ART: Refactor elf_writer_quick for elf section order
This writes ELF sections in approximate order (debug sections are
currently out-of-order) to avoid incompletely written files
appearing OK to ART loading code.
Bug: 17622827
(cherry picked from commit 3c5ec99e156632a734c2359642ae928102850597)
Change-Id: I812fd42fcf2823dbfaf5891bf101d3b59406f263
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
index 35320f5..e535b6d 100644
--- a/compiler/elf_builder.h
+++ b/compiler/elf_builder.h
@@ -17,6 +17,7 @@
#ifndef ART_COMPILER_ELF_BUILDER_H_
#define ART_COMPILER_ELF_BUILDER_H_
+#include "base/stl_util.h"
#include "buffered_output_stream.h"
#include "elf_utils.h"
#include "file_output_stream.h"
@@ -354,12 +355,124 @@
ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> strtab_;
};
+template <typename Elf_Word>
+class ElfFilePiece {
+ public:
+ virtual ~ElfFilePiece() {}
+
+ virtual bool Write(File* elf_file) {
+ if (static_cast<off_t>(offset_) != lseek(elf_file->Fd(), offset_, SEEK_SET)) {
+ PLOG(ERROR) << "Failed to seek to " << GetDescription() << " offset " << offset_ << " for "
+ << elf_file->GetPath();
+ return false;
+ }
+
+ return DoActualWrite(elf_file);
+ }
+
+ static bool Compare(ElfFilePiece* a, ElfFilePiece* b) {
+ return a->offset_ < b->offset_;
+ }
+
+ protected:
+ explicit ElfFilePiece(Elf_Word offset) : offset_(offset) {}
+
+ virtual std::string GetDescription() = 0;
+ virtual bool DoActualWrite(File* elf_file) = 0;
+
+ Elf_Word offset_;
+};
+
+template <typename Elf_Word>
+class ElfFileMemoryPiece : public ElfFilePiece<Elf_Word> {
+ public:
+ ElfFileMemoryPiece(const std::string& name, Elf_Word offset, const void* data, Elf_Word size)
+ : ElfFilePiece<Elf_Word>(offset), dbg_name_(name), data_(data), size_(size) {}
+
+ bool DoActualWrite(File* elf_file) OVERRIDE {
+ DCHECK(data_ != nullptr || size_ == 0U) << dbg_name_ << " " << size_;
+
+ if (!elf_file->WriteFully(data_, size_)) {
+ PLOG(ERROR) << "Failed to write " << dbg_name_ << " for " << elf_file->GetPath();
+ return false;
+ }
+
+ return true;
+ }
+
+ std::string GetDescription() OVERRIDE {
+ return dbg_name_;
+ }
+
+ private:
+ const std::string& dbg_name_;
+ const void *data_;
+ Elf_Word size_;
+};
+
class CodeOutput {
public:
virtual bool Write(OutputStream* out) = 0;
virtual ~CodeOutput() {}
};
+template <typename Elf_Word>
+class ElfFileRodataPiece : public ElfFilePiece<Elf_Word> {
+ public:
+ ElfFileRodataPiece(Elf_Word offset, CodeOutput* output) : ElfFilePiece<Elf_Word>(offset),
+ output_(output) {}
+
+ bool DoActualWrite(File* elf_file) OVERRIDE {
+ std::unique_ptr<BufferedOutputStream> output_stream(
+ new BufferedOutputStream(new FileOutputStream(elf_file)));
+ if (!output_->Write(output_stream.get())) {
+ PLOG(ERROR) << "Failed to write .rodata and .text for " << elf_file->GetPath();
+ return false;
+ }
+
+ return true;
+ }
+
+ std::string GetDescription() OVERRIDE {
+ return ".rodata";
+ }
+
+ private:
+ CodeOutput* output_;
+};
+
+template <typename Elf_Word>
+class ElfFileOatTextPiece : public ElfFilePiece<Elf_Word> {
+ public:
+ ElfFileOatTextPiece(Elf_Word offset, CodeOutput* output) : ElfFilePiece<Elf_Word>(offset),
+ output_(output) {}
+
+ bool DoActualWrite(File* elf_file) OVERRIDE {
+ // All data is written by the ElfFileRodataPiece right now, as the oat writer writes in one
+ // piece. This is for future flexibility.
+ UNUSED(output_);
+ return true;
+ }
+
+ std::string GetDescription() OVERRIDE {
+ return ".text";
+ }
+
+ private:
+ CodeOutput* output_;
+};
+
+template <typename Elf_Word>
+static bool WriteOutFile(const std::vector<ElfFilePiece<Elf_Word>*>& pieces, File* elf_file) {
+ // TODO It would be nice if this checked for overlap.
+ for (auto it = pieces.begin(); it != pieces.end(); ++it) {
+ if (!(*it)->Write(elf_file)) {
+ return false;
+ }
+ }
+ return true;
+}
+
template <typename Elf_Word, typename Elf_Shdr>
static inline constexpr Elf_Word NextOffset(const Elf_Shdr& cur, const Elf_Shdr& prev) {
return RoundUp(prev.sh_size + prev.sh_offset, cur.sh_addralign);
@@ -667,7 +780,7 @@
}
bool Write() {
- std::vector<ElfFilePiece> pieces;
+ std::vector<ElfFilePiece<Elf_Word>*> pieces;
Elf_Shdr prev = dynamic_builder_.section_;
std::string strtab;
@@ -746,8 +859,9 @@
it->section_.sh_addr = 0;
it->section_.sh_size = it->GetBuffer()->size();
it->section_.sh_link = it->GetLink();
- pieces.push_back(ElfFilePiece(it->name_, it->section_.sh_offset,
- it->GetBuffer()->data(), it->GetBuffer()->size()));
+
+ // We postpone adding an ElfFilePiece to keep the order in "pieces."
+
prev = it->section_;
if (debug_logging_) {
LOG(INFO) << it->name_ << " off=" << it->section_.sh_offset
@@ -824,55 +938,62 @@
elf_header_.e_shstrndx = shstrtab_builder_.section_index_;
// Add the rest of the pieces to the list.
- pieces.push_back(ElfFilePiece("Elf Header", 0, &elf_header_, sizeof(elf_header_)));
- pieces.push_back(ElfFilePiece("Program headers", PHDR_OFFSET,
- &program_headers_, sizeof(program_headers_)));
- pieces.push_back(ElfFilePiece(".dynamic", dynamic_builder_.section_.sh_offset,
- dynamic.data(), dynamic_builder_.section_.sh_size));
- pieces.push_back(ElfFilePiece(".dynsym", dynsym_builder_.section_.sh_offset,
- dynsym.data(), dynsym.size() * sizeof(Elf_Sym)));
- pieces.push_back(ElfFilePiece(".dynstr", dynsym_builder_.GetStrTab()->section_.sh_offset,
- dynstr_.c_str(), dynstr_.size()));
- pieces.push_back(ElfFilePiece(".hash", hash_builder_.section_.sh_offset,
- hash_.data(), hash_.size() * sizeof(Elf_Word)));
- pieces.push_back(ElfFilePiece(".rodata", rodata_builder_.section_.sh_offset,
- nullptr, rodata_builder_.section_.sh_size));
- pieces.push_back(ElfFilePiece(".text", text_builder_.section_.sh_offset,
- nullptr, text_builder_.section_.sh_size));
+ pieces.push_back(new ElfFileMemoryPiece<Elf_Word>("Elf Header", 0, &elf_header_,
+ sizeof(elf_header_)));
+ pieces.push_back(new ElfFileMemoryPiece<Elf_Word>("Program headers", PHDR_OFFSET,
+ &program_headers_, sizeof(program_headers_)));
+ pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".dynamic",
+ dynamic_builder_.section_.sh_offset,
+ dynamic.data(),
+ dynamic_builder_.section_.sh_size));
+ pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".dynsym", dynsym_builder_.section_.sh_offset,
+ dynsym.data(),
+ dynsym.size() * sizeof(Elf_Sym)));
+ pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".dynstr",
+ dynsym_builder_.GetStrTab()->section_.sh_offset,
+ dynstr_.c_str(), dynstr_.size()));
+ pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".hash", hash_builder_.section_.sh_offset,
+ hash_.data(),
+ hash_.size() * sizeof(Elf_Word)));
+ pieces.push_back(new ElfFileRodataPiece<Elf_Word>(rodata_builder_.section_.sh_offset,
+ oat_writer_));
+ pieces.push_back(new ElfFileOatTextPiece<Elf_Word>(text_builder_.section_.sh_offset,
+ oat_writer_));
if (IncludingDebugSymbols()) {
- pieces.push_back(ElfFilePiece(".symtab", symtab_builder_.section_.sh_offset,
- symtab.data(), symtab.size() * sizeof(Elf_Sym)));
- pieces.push_back(ElfFilePiece(".strtab", symtab_builder_.GetStrTab()->section_.sh_offset,
- strtab.c_str(), strtab.size()));
+ pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".symtab",
+ symtab_builder_.section_.sh_offset,
+ symtab.data(),
+ symtab.size() * sizeof(Elf_Sym)));
+ pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".strtab",
+ symtab_builder_.GetStrTab()->section_.sh_offset,
+ strtab.c_str(), strtab.size()));
}
- pieces.push_back(ElfFilePiece(".shstrtab", shstrtab_builder_.section_.sh_offset,
- &shstrtab_[0], shstrtab_.size()));
+ pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".shstrtab",
+ shstrtab_builder_.section_.sh_offset,
+ &shstrtab_[0], shstrtab_.size()));
for (uint32_t i = 0; i < section_ptrs_.size(); ++i) {
// Just add all the sections in induvidually since they are all over the
// place on the heap/stack.
Elf_Word cur_off = sections_offset + i * sizeof(Elf_Shdr);
- pieces.push_back(ElfFilePiece("section table piece", cur_off,
- section_ptrs_[i], sizeof(Elf_Shdr)));
+ pieces.push_back(new ElfFileMemoryPiece<Elf_Word>("section table piece", cur_off,
+ section_ptrs_[i], sizeof(Elf_Shdr)));
+ }
+
+ // Postponed debug info.
+ for (auto it = other_builders_.begin(); it != other_builders_.end(); ++it) {
+ pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(it->name_, it->section_.sh_offset,
+ it->GetBuffer()->data(),
+ it->GetBuffer()->size()));
}
if (!WriteOutFile(pieces)) {
LOG(ERROR) << "Unable to write to file " << elf_file_->GetPath();
- return false;
- }
- // write out the actual oat file data.
- Elf_Word oat_data_offset = rodata_builder_.section_.sh_offset;
- if (static_cast<off_t>(oat_data_offset) != lseek(elf_file_->Fd(), oat_data_offset, SEEK_SET)) {
- PLOG(ERROR) << "Failed to seek to .rodata offset " << oat_data_offset
- << " for " << elf_file_->GetPath();
- return false;
- }
- std::unique_ptr<BufferedOutputStream> output_stream(
- new BufferedOutputStream(new FileOutputStream(elf_file_)));
- if (!oat_writer_->Write(output_stream.get())) {
- PLOG(ERROR) << "Failed to write .rodata and .text for " << elf_file_->GetPath();
+
+ STLDeleteElements(&pieces); // Have to manually clean pieces.
return false;
}
+ STLDeleteElements(&pieces); // Have to manually clean pieces.
return true;
}
@@ -1028,34 +1149,12 @@
}
}
- struct ElfFilePiece {
- ElfFilePiece(const std::string& name, Elf_Word offset, const void* data, Elf_Word size)
- : dbg_name_(name), offset_(offset), data_(data), size_(size) {}
- ~ElfFilePiece() {}
-
- const std::string& dbg_name_;
- Elf_Word offset_;
- const void *data_;
- Elf_Word size_;
- static bool Compare(ElfFilePiece a, ElfFilePiece b) {
- return a.offset_ < b.offset_;
- }
- };
// Write each of the pieces out to the file.
- bool WriteOutFile(const std::vector<ElfFilePiece>& pieces) {
- // TODO It would be nice if this checked for overlap.
+ bool WriteOutFile(const std::vector<ElfFilePiece<Elf_Word>*>& pieces) {
for (auto it = pieces.begin(); it != pieces.end(); ++it) {
- if (it->data_) {
- if (static_cast<off_t>(it->offset_) != lseek(elf_file_->Fd(), it->offset_, SEEK_SET)) {
- PLOG(ERROR) << "Failed to seek to " << it->dbg_name_ << " offset location "
- << it->offset_ << " for " << elf_file_->GetPath();
- return false;
- }
- if (!elf_file_->WriteFully(it->data_, it->size_)) {
- PLOG(ERROR) << "Failed to write " << it->dbg_name_ << " for " << elf_file_->GetPath();
- return false;
- }
+ if (!(*it)->Write(elf_file_)) {
+ return false;
}
}
return true;