Generate unstripped .oat files in the symbols directory.

Test: Check the generated files with readelf.
Bug: 70512966
Change-Id: Id31232f8b750281bdc170f356833a8d71e1b5796
diff --git a/compiler/linker/elf_builder.h b/compiler/linker/elf_builder.h
index 3da7a43..974c590 100644
--- a/compiler/linker/elf_builder.h
+++ b/compiler/linker/elf_builder.h
@@ -308,9 +308,14 @@
                   /* link */ nullptr,
                   /* info */ 0,
                   align,
-                  /* entsize */ 0),
-          current_offset_(0),
-          last_offset_(0) {
+                  /* entsize */ 0) {
+      Reset();
+    }
+
+    void Reset() {
+      current_offset_ = 0;
+      last_name_ = "";
+      last_offset_ = 0;
     }
 
     Elf_Word Write(const std::string& name) {
@@ -550,6 +555,7 @@
         build_id_(this, ".note.gnu.build-id", SHT_NOTE, SHF_ALLOC, nullptr, 0, 4, 0),
         current_section_(nullptr),
         started_(false),
+        finished_(false),
         write_program_headers_(false),
         loaded_size_(0u),
         virtual_address_(0) {
@@ -627,8 +633,10 @@
     write_program_headers_ = write_program_headers;
   }
 
-  void End() {
+  off_t End() {
     DCHECK(started_);
+    DCHECK(!finished_);
+    finished_ = true;
 
     // Note: loaded_size_ == 0 for tests that don't write .rodata, .text, .bss,
     // .dynstr, dynsym, .hash and .dynamic. These tests should not read loaded_size_.
@@ -662,6 +670,7 @@
     Elf_Off section_headers_offset;
     section_headers_offset = AlignFileOffset(sizeof(Elf_Off));
     stream_.WriteFully(shdrs.data(), shdrs.size() * sizeof(shdrs[0]));
+    off_t file_size = stream_.Seek(0, kSeekCurrent);
 
     // Flush everything else before writing the program headers. This should prevent
     // the OS from reordering writes, so that we don't end up with valid headers
@@ -687,6 +696,39 @@
     stream_.WriteFully(&elf_header, sizeof(elf_header));
     stream_.WriteFully(phdrs.data(), phdrs.size() * sizeof(phdrs[0]));
     stream_.Flush();
+
+    return file_size;
+  }
+
+  // This has the same effect as running the "strip" command line tool.
+  // It removes all debugging sections (but it keeps mini-debug-info).
+  // It returns the ELF file size (as the caller needs to truncate it).
+  off_t Strip() {
+    DCHECK(finished_);
+    finished_ = false;
+    Elf_Off end = 0;
+    std::vector<Section*> non_debug_sections;
+    for (Section* section : sections_) {
+      if (section == &shstrtab_ ||  // Section names will be recreated.
+          section == &symtab_ ||
+          section == &strtab_ ||
+          section->name_.find(".debug_") == 0) {
+        section->header_.sh_offset = 0;
+        section->header_.sh_size = 0;
+        section->section_index_ = 0;
+      } else {
+        if (section->header_.sh_type != SHT_NOBITS) {
+          DCHECK_LE(section->header_.sh_offset, end + kPageSize) << "Large gap between sections";
+          end = std::max<off_t>(end, section->header_.sh_offset + section->header_.sh_size);
+        }
+        non_debug_sections.push_back(section);
+      }
+    }
+    shstrtab_.Reset();
+    // Write the non-debug section headers, program headers, and ELF header again.
+    sections_ = std::move(non_debug_sections);
+    stream_.Seek(end, kSeekSet);
+    return End();
   }
 
   // The running program does not have access to section headers
@@ -861,6 +903,7 @@
   void WriteBuildId(uint8_t build_id[kBuildIdLen]) {
     stream_.Seek(build_id_.GetDigestStart(), kSeekSet);
     stream_.WriteFully(build_id, kBuildIdLen);
+    stream_.Flush();
   }
 
   // Returns true if all writes and seeks on the output stream succeeded.
@@ -1060,6 +1103,7 @@
   Section* current_section_;  // The section which is currently being written.
 
   bool started_;
+  bool finished_;
   bool write_program_headers_;
 
   // The size of the memory taken by the ELF file when loaded.
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index cbc6424..0b68620 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -253,7 +253,14 @@
   UsageError("      to the file descriptor specified by --oat-fd.");
   UsageError("      Example: --oat-location=/data/dalvik-cache/system@app@Calculator.apk.oat");
   UsageError("");
-  UsageError("  --oat-symbols=<file.oat>: specifies an oat output destination with full symbols.");
+  UsageError("  --oat-symbols=<file.oat>: specifies a destination where the oat file is copied.");
+  UsageError("      This is equivalent to file copy as build post-processing step.");
+  UsageError("      It is intended to be used with --strip and it happens before it.");
+  UsageError("      Example: --oat-symbols=/symbols/system/framework/boot.oat");
+  UsageError("");
+  UsageError("  --strip: remove all debugging sections at the end (but keep mini-debug-info).");
+  UsageError("      This is equivalent to the \"strip\" command as build post-processing step.");
+  UsageError("      It is intended to be used with --oat-symbols and it happens after it.");
   UsageError("      Example: --oat-symbols=/symbols/system/framework/boot.oat");
   UsageError("");
   UsageError("  --image=<file.art>: specifies an output image filename.");
@@ -1180,6 +1187,7 @@
     AssignIfExists(args, M::DexLocations, &dex_locations_);
     AssignIfExists(args, M::OatFiles, &oat_filenames_);
     AssignIfExists(args, M::OatSymbols, &parser_options->oat_symbols);
+    AssignTrueIfExists(args, M::Strip, &strip_);
     AssignIfExists(args, M::ImageFilenames, &image_filenames_);
     AssignIfExists(args, M::ZipFd, &zip_fd_);
     AssignIfExists(args, M::ZipLocation, &zip_location_);
@@ -2175,7 +2183,7 @@
         VLOG(compiler) << "Oat file written successfully: " << oat_filenames_[i];
 
         oat_writer.reset();
-        elf_writer.reset();
+        // We may still need the ELF writer later for stripping.
       }
     }
 
@@ -2194,21 +2202,16 @@
     return true;
   }
 
-  // Create a copy from stripped to unstripped.
-  bool CopyStrippedToUnstripped() {
+  // Copy the full oat files to symbols directory and then strip the originals.
+  bool CopyOatFilesToSymbolsDirectoryAndStrip() {
     for (size_t i = 0; i < oat_unstripped_.size(); ++i) {
       // If we don't want to strip in place, copy from stripped location to unstripped location.
       // We need to strip after image creation because FixupElf needs to use .strtab.
       if (strcmp(oat_unstripped_[i], oat_filenames_[i]) != 0) {
-        // If the oat file is still open, flush it.
-        if (oat_files_[i].get() != nullptr && oat_files_[i]->IsOpened()) {
-          if (!FlushCloseOutputFile(&oat_files_[i])) {
-            return false;
-          }
-        }
+        DCHECK(oat_files_[i].get() != nullptr && oat_files_[i]->IsOpened());
 
         TimingLogger::ScopedTiming t("dex2oat OatFile copy", timings_);
-        std::unique_ptr<File> in(OS::OpenFileForReading(oat_filenames_[i]));
+        std::unique_ptr<File>& in = oat_files_[i];
         std::unique_ptr<File> out(OS::CreateEmptyFile(oat_unstripped_[i]));
         int64_t in_length = in->GetLength();
         if (in_length < 0) {
@@ -2224,6 +2227,14 @@
           return false;
         }
         VLOG(compiler) << "Oat file copied successfully (unstripped): " << oat_unstripped_[i];
+
+        if (strip_) {
+          TimingLogger::ScopedTiming t2("dex2oat OatFile strip", timings_);
+          if (!elf_writers_[i]->StripDebugInfo()) {
+            PLOG(ERROR) << "Failed strip oat file: " << in->GetPath();
+            return false;
+          }
+        }
       }
     }
     return true;
@@ -2239,11 +2250,10 @@
     return true;
   }
 
-  bool FlushCloseOutputFile(std::unique_ptr<File>* file) {
-    if (file->get() != nullptr) {
-      std::unique_ptr<File> tmp(file->release());
-      if (tmp->FlushCloseOrErase() != 0) {
-        PLOG(ERROR) << "Failed to flush and close output file: " << tmp->GetPath();
+  bool FlushCloseOutputFile(File* file) {
+    if (file != nullptr) {
+      if (file->FlushCloseOrErase() != 0) {
+        PLOG(ERROR) << "Failed to flush and close output file: " << file->GetPath();
         return false;
       }
     }
@@ -2266,7 +2276,7 @@
     bool result = true;
     for (auto& files : { &vdex_files_, &oat_files_ }) {
       for (size_t i = 0; i < files->size(); ++i) {
-        result &= FlushCloseOutputFile(&(*files)[i]);
+        result &= FlushCloseOutputFile((*files)[i].get());
       }
     }
     return result;
@@ -2825,6 +2835,7 @@
   std::string oat_location_;
   std::vector<const char*> oat_filenames_;
   std::vector<const char*> oat_unstripped_;
+  bool strip_;
   int oat_fd_;
   int input_vdex_fd_;
   int output_vdex_fd_;
@@ -2947,15 +2958,9 @@
     return dex2oat::ReturnCode::kOther;
   }
 
-  // Flush boot.oat. We always expect the output file by name, and it will be re-opened from the
-  // unstripped name. Do not close the file if we are compiling the image with an oat fd since the
-  // image writer will require this fd to generate the image.
-  if (dex2oat.ShouldKeepOatFileOpen()) {
-    if (!dex2oat.FlushOutputFiles()) {
-      dex2oat.EraseOutputFiles();
-      return dex2oat::ReturnCode::kOther;
-    }
-  } else if (!dex2oat.FlushCloseOutputFiles()) {
+  // Flush boot.oat.  Keep it open as we might still modify it later (strip it).
+  if (!dex2oat.FlushOutputFiles()) {
+    dex2oat.EraseOutputFiles();
     return dex2oat::ReturnCode::kOther;
   }
 
@@ -2974,7 +2979,7 @@
   }
 
   // Copy stripped to unstripped location, if necessary.
-  if (!dex2oat.CopyStrippedToUnstripped()) {
+  if (!dex2oat.CopyOatFilesToSymbolsDirectoryAndStrip()) {
     return dex2oat::ReturnCode::kOther;
   }
 
@@ -3012,7 +3017,7 @@
 
   // Copy stripped to unstripped location, if necessary. This will implicitly flush & close the
   // stripped versions. If this is given, we expect to be able to open writable files by name.
-  if (!dex2oat.CopyStrippedToUnstripped()) {
+  if (!dex2oat.CopyOatFilesToSymbolsDirectoryAndStrip()) {
     return dex2oat::ReturnCode::kOther;
   }
 
diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc
index bf9edf7..710f14c 100644
--- a/dex2oat/dex2oat_options.cc
+++ b/dex2oat/dex2oat_options.cc
@@ -98,6 +98,8 @@
       .Define("--oat-symbols=_")
           .WithType<std::vector<std::string>>().AppendValues()
           .IntoKey(M::OatSymbols)
+      .Define("--strip")
+          .IntoKey(M::Strip)
       .Define("--oat-fd=_")
           .WithType<int>()
           .IntoKey(M::OatFd)
diff --git a/dex2oat/dex2oat_options.def b/dex2oat/dex2oat_options.def
index fe5c4e6..c8cb7e7 100644
--- a/dex2oat/dex2oat_options.def
+++ b/dex2oat/dex2oat_options.def
@@ -47,6 +47,7 @@
 DEX2OAT_OPTIONS_KEY (std::string,                    DmFile)
 DEX2OAT_OPTIONS_KEY (std::vector<std::string>,       OatFiles)
 DEX2OAT_OPTIONS_KEY (std::vector<std::string>,       OatSymbols)
+DEX2OAT_OPTIONS_KEY (Unit,                           Strip)
 DEX2OAT_OPTIONS_KEY (int,                            OatFd)
 DEX2OAT_OPTIONS_KEY (std::string,                    OatLocation)
 DEX2OAT_OPTIONS_KEY (bool,                           Watchdog)
diff --git a/dex2oat/linker/elf_writer.h b/dex2oat/linker/elf_writer.h
index cd8cf4c..637330c 100644
--- a/dex2oat/linker/elf_writer.h
+++ b/dex2oat/linker/elf_writer.h
@@ -77,6 +77,7 @@
   virtual void EndDataBimgRelRo(OutputStream* data_bimg_rel_ro) = 0;
   virtual void WriteDynamicSection() = 0;
   virtual void WriteDebugInfo(const debug::DebugInfo& debug_info) = 0;
+  virtual bool StripDebugInfo() = 0;
   virtual bool End() = 0;
 
   // Get the ELF writer's stream. This stream can be used for writing data directly
diff --git a/dex2oat/linker/elf_writer_quick.cc b/dex2oat/linker/elf_writer_quick.cc
index 8f6ff70..4e7d636 100644
--- a/dex2oat/linker/elf_writer_quick.cc
+++ b/dex2oat/linker/elf_writer_quick.cc
@@ -117,6 +117,7 @@
   void EndDataBimgRelRo(OutputStream* data_bimg_rel_ro) OVERRIDE;
   void WriteDynamicSection() OVERRIDE;
   void WriteDebugInfo(const debug::DebugInfo& debug_info) OVERRIDE;
+  bool StripDebugInfo() OVERRIDE;
   bool End() OVERRIDE;
 
   virtual OutputStream* GetStream() OVERRIDE;
@@ -280,10 +281,6 @@
 template <typename ElfTypes>
 void ElfWriterQuick<ElfTypes>::WriteDebugInfo(const debug::DebugInfo& debug_info) {
   if (!debug_info.Empty()) {
-    if (compiler_options_.GetGenerateDebugInfo()) {
-      // Generate all the debug information we can.
-      debug::WriteDebugInfo(builder_.get(), debug_info, kCFIFormat, true /* write_oat_patches */);
-    }
     if (compiler_options_.GetGenerateMiniDebugInfo()) {
       // Wait for the mini-debug-info generation to finish and write it to disk.
       Thread* self = Thread::Current();
@@ -291,10 +288,21 @@
       debug_info_thread_pool_->Wait(self, true, false);
       builder_->WriteSection(".gnu_debugdata", debug_info_task_->GetResult());
     }
+    // The Strip method expects debug info to be last (mini-debug-info is not stripped).
+    if (compiler_options_.GetGenerateDebugInfo()) {
+      // Generate all the debug information we can.
+      debug::WriteDebugInfo(builder_.get(), debug_info, kCFIFormat, true /* write_oat_patches */);
+    }
   }
 }
 
 template <typename ElfTypes>
+bool ElfWriterQuick<ElfTypes>::StripDebugInfo() {
+  off_t file_size = builder_->Strip();
+  return elf_file_->SetLength(file_size) == 0;
+}
+
+template <typename ElfTypes>
 bool ElfWriterQuick<ElfTypes>::End() {
   builder_->End();
   if (compiler_options_.GetGenerateBuildId()) {