Extract the elf image from a compressed kernel image.

If the --decompress_kernel flag is given to the launcher, the kernel
image will be decompressed and stored in ${runtime_dir}/vmlinux. This
is the default (and required) behavior for crosvm.

Bug: 122978436
Test: build & run locally
Change-Id: I47e70d4cae20a398bc46772c3f5cb0dd9e3b5dcc
diff --git a/host/commands/launch/flags.cc b/host/commands/launch/flags.cc
index 5b67f2b..76cb1c8 100644
--- a/host/commands/launch/flags.cc
+++ b/host/commands/launch/flags.cc
@@ -43,6 +43,11 @@
 DEFINE_int32(num_screen_buffers, 3, "The number of screen buffers");
 DEFINE_string(kernel_path, "",
               "Path to the kernel. Overrides the one from the boot image");
+DEFINE_bool(decompress_kernel, false,
+            "Whether to decompress the kernel image. Required for crosvm.");
+DEFINE_string(kernel_decompresser_executable,
+              vsoc::DefaultHostArtifactsPath("bin/extract-vmlinux"),
+             "Path to the extract-vmlinux executable.");
 DEFINE_string(extra_kernel_cmdline, "",
               "Additional flags to put on the kernel command line");
 DEFINE_int32(loop_max_part, 7, "Maximum number of loop partitions");
@@ -245,6 +250,11 @@
         tmp_config_obj.PerInstancePath("kernel"));
     tmp_config_obj.set_use_unpacked_kernel(true);
   }
+  tmp_config_obj.set_decompress_kernel(FLAGS_decompress_kernel);
+  if (FLAGS_decompress_kernel) {
+    tmp_config_obj.set_decompressed_kernel_image_path(
+        tmp_config_obj.PerInstancePath("vmlinux"));
+  }
 
   auto ramdisk_path = tmp_config_obj.PerInstancePath("ramdisk.img");
   bool use_ramdisk = boot_image_unpacker.HasRamdiskImage();
@@ -430,6 +440,8 @@
                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
   SetCommandLineOptionWithMode("adb_mode", "tunnel",
                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  SetCommandLineOptionWithMode("decompress_kernel", "false",
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
 }
 
 void SetDefaultFlagsForCrosvm() {
@@ -459,6 +471,8 @@
                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
   SetCommandLineOptionWithMode("adb_mode", "vsock_tunnel",
                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  SetCommandLineOptionWithMode("decompress_kernel", "true",
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
 }
 
 bool ParseCommandLineFlags(int* argc, char*** argv) {
@@ -513,6 +527,20 @@
   }
   return true;
 }
+
+bool DecompressKernel(const std::string& src, const std::string& dst) {
+  cvd::Command decomp_cmd(FLAGS_kernel_decompresser_executable);
+  decomp_cmd.AddParameter(src);
+  auto output_file = cvd::SharedFD::Creat(dst.c_str(), 0666);
+  if (!output_file->IsOpen()) {
+    LOG(ERROR) << "Unable to create decompressed image file: "
+               << output_file->StrError();
+    return false;
+  }
+  decomp_cmd.RedirectStdIO(cvd::Subprocess::StdIOChannel::kStdOut, output_file);
+  auto decomp_proc = decomp_cmd.Start(false);
+  return decomp_proc.Started() && decomp_proc.Wait() == 0;
+}
 } // namespace
 
 vsoc::CuttlefishConfig* InitFilesystemAndCreateConfig(int* argc, char*** argv) {
@@ -563,6 +591,14 @@
     exit(LauncherExitCodes::kBootImageUnpackError);
   }
 
+  if (config->decompress_kernel()) {
+    if (!DecompressKernel(config->kernel_image_path(),
+        config->decompressed_kernel_image_path())) {
+      LOG(ERROR) << "Failed to decompress kernel";
+      exit(LauncherExitCodes::kKernelDecompressError);
+    }
+  }
+
   ValidateAdbModeFlag(*config);
 
   // Create data if necessary