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()) {