diff options
-rw-r--r-- | compiler/buffered_output_stream.cc | 7 | ||||
-rw-r--r-- | compiler/buffered_output_stream.h | 4 | ||||
-rw-r--r-- | compiler/cfi_test.h | 8 | ||||
-rw-r--r-- | compiler/dwarf/dwarf_test.cc | 28 | ||||
-rw-r--r-- | compiler/dwarf/dwarf_test.h | 33 | ||||
-rw-r--r-- | compiler/dwarf/headers.h | 65 | ||||
-rw-r--r-- | compiler/elf_builder.h | 1221 | ||||
-rw-r--r-- | compiler/elf_writer_debug.cc | 218 | ||||
-rw-r--r-- | compiler/elf_writer_debug.h | 26 | ||||
-rw-r--r-- | compiler/elf_writer_quick.cc | 220 | ||||
-rw-r--r-- | compiler/elf_writer_test.cc | 3 | ||||
-rw-r--r-- | oatdump/oatdump.cc | 90 |
12 files changed, 793 insertions, 1130 deletions
diff --git a/compiler/buffered_output_stream.cc b/compiler/buffered_output_stream.cc index 0940a80cc1..3ca518b686 100644 --- a/compiler/buffered_output_stream.cc +++ b/compiler/buffered_output_stream.cc @@ -25,12 +25,13 @@ BufferedOutputStream::BufferedOutputStream(OutputStream* out) bool BufferedOutputStream::WriteFully(const void* buffer, size_t byte_count) { if (byte_count > kBufferSize) { - Flush(); + if (!Flush()) { + return false; + } return out_->WriteFully(buffer, byte_count); } if (used_ + byte_count > kBufferSize) { - bool success = Flush(); - if (!success) { + if (!Flush()) { return false; } } diff --git a/compiler/buffered_output_stream.h b/compiler/buffered_output_stream.h index 15fc0335a9..b447f41e21 100644 --- a/compiler/buffered_output_stream.h +++ b/compiler/buffered_output_stream.h @@ -36,11 +36,11 @@ class BufferedOutputStream FINAL : public OutputStream { virtual off_t Seek(off_t offset, Whence whence); + bool Flush(); + private: static const size_t kBufferSize = 8 * KB; - bool Flush(); - OutputStream* const out_; uint8_t buffer_[kBufferSize]; diff --git a/compiler/cfi_test.h b/compiler/cfi_test.h index 6fd457599f..508b04a16f 100644 --- a/compiler/cfi_test.h +++ b/compiler/cfi_test.h @@ -48,11 +48,11 @@ class CFITest : public dwarf::DwarfTest { // Pretty-print CFI opcodes. constexpr bool is64bit = false; dwarf::DebugFrameOpCodeWriter<> initial_opcodes; - dwarf::WriteDebugFrameCIE(is64bit, dwarf::DW_EH_PE_absptr, dwarf::Reg(8), - initial_opcodes, kCFIFormat, &debug_frame_data_); + dwarf::WriteCIE(is64bit, dwarf::Reg(8), + initial_opcodes, kCFIFormat, &debug_frame_data_); std::vector<uintptr_t> debug_frame_patches; - dwarf::WriteDebugFrameFDE(is64bit, 0, 0, actual_asm.size(), ArrayRef<const uint8_t>(actual_cfi), - kCFIFormat, &debug_frame_data_, &debug_frame_patches); + dwarf::WriteFDE(is64bit, 0, 0, 0, actual_asm.size(), ArrayRef<const uint8_t>(actual_cfi), + kCFIFormat, 0, &debug_frame_data_, &debug_frame_patches); ReformatCfi(Objdump(false, "-W"), &lines); // Pretty-print assembly. auto* opts = new DisassemblerOptions(false, actual_asm.data(), true); diff --git a/compiler/dwarf/dwarf_test.cc b/compiler/dwarf/dwarf_test.cc index 3ba380e9db..a412a99d98 100644 --- a/compiler/dwarf/dwarf_test.cc +++ b/compiler/dwarf/dwarf_test.cc @@ -122,12 +122,12 @@ TEST_F(DwarfTest, DebugFrame) { DW_CHECK_NEXT("DW_CFA_restore: r5 (ebp)"); DebugFrameOpCodeWriter<> initial_opcodes; - WriteDebugFrameCIE(is64bit, DW_EH_PE_absptr, Reg(is64bit ? 16 : 8), - initial_opcodes, kCFIFormat, &debug_frame_data_); + WriteCIE(is64bit, Reg(is64bit ? 16 : 8), + initial_opcodes, kCFIFormat, &debug_frame_data_); std::vector<uintptr_t> debug_frame_patches; std::vector<uintptr_t> expected_patches { 28 }; // NOLINT - WriteDebugFrameFDE(is64bit, 0, 0x01000000, 0x01000000, ArrayRef<const uint8_t>(*opcodes.data()), - kCFIFormat, &debug_frame_data_, &debug_frame_patches); + WriteFDE(is64bit, 0, 0, 0x01000000, 0x01000000, ArrayRef<const uint8_t>(*opcodes.data()), + kCFIFormat, 0, &debug_frame_data_, &debug_frame_patches); EXPECT_EQ(expected_patches, debug_frame_patches); CheckObjdumpOutput(is64bit, "-W"); @@ -136,14 +136,14 @@ TEST_F(DwarfTest, DebugFrame) { TEST_F(DwarfTest, DebugFrame64) { constexpr bool is64bit = true; DebugFrameOpCodeWriter<> initial_opcodes; - WriteDebugFrameCIE(is64bit, DW_EH_PE_absptr, Reg(16), - initial_opcodes, kCFIFormat, &debug_frame_data_); + WriteCIE(is64bit, Reg(16), + initial_opcodes, kCFIFormat, &debug_frame_data_); DebugFrameOpCodeWriter<> opcodes; std::vector<uintptr_t> debug_frame_patches; std::vector<uintptr_t> expected_patches { 32 }; // NOLINT - WriteDebugFrameFDE(is64bit, 0, 0x0100000000000000, 0x0200000000000000, - ArrayRef<const uint8_t>(*opcodes.data()), - kCFIFormat, &debug_frame_data_, &debug_frame_patches); + WriteFDE(is64bit, 0, 0, 0x0100000000000000, 0x0200000000000000, + ArrayRef<const uint8_t>(*opcodes.data()), + kCFIFormat, 0, &debug_frame_data_, &debug_frame_patches); DW_CHECK("FDE cie=00000000 pc=100000000000000..300000000000000"); EXPECT_EQ(expected_patches, debug_frame_patches); @@ -176,12 +176,12 @@ TEST_F(DwarfTest, x86_64_RegisterMapping) { DW_CHECK_NEXT("DW_CFA_offset: r14 (r14)"); DW_CHECK_NEXT("DW_CFA_offset: r15 (r15)"); DebugFrameOpCodeWriter<> initial_opcodes; - WriteDebugFrameCIE(is64bit, DW_EH_PE_absptr, Reg(16), - initial_opcodes, kCFIFormat, &debug_frame_data_); + WriteCIE(is64bit, Reg(16), + initial_opcodes, kCFIFormat, &debug_frame_data_); std::vector<uintptr_t> debug_frame_patches; - WriteDebugFrameFDE(is64bit, 0, 0x0100000000000000, 0x0200000000000000, - ArrayRef<const uint8_t>(*opcodes.data()), - kCFIFormat, &debug_frame_data_, &debug_frame_patches); + WriteFDE(is64bit, 0, 0, 0x0100000000000000, 0x0200000000000000, + ArrayRef<const uint8_t>(*opcodes.data()), + kCFIFormat, 0, &debug_frame_data_, &debug_frame_patches); CheckObjdumpOutput(is64bit, "-W"); } diff --git a/compiler/dwarf/dwarf_test.h b/compiler/dwarf/dwarf_test.h index f819c49cee..5464ed9c49 100644 --- a/compiler/dwarf/dwarf_test.h +++ b/compiler/dwarf/dwarf_test.h @@ -59,38 +59,27 @@ class DwarfTest : public CommonRuntimeTest { std::vector<std::string> Objdump(const char* args) { // Write simple elf file with just the DWARF sections. InstructionSet isa = (sizeof(typename ElfTypes::Addr) == 8) ? kX86_64 : kX86; - class NoCode : public CodeOutput { - bool Write(OutputStream*) OVERRIDE { return true; } // NOLINT - } no_code; - ElfBuilder<ElfTypes> builder(isa, 0, &no_code, 0, &no_code, 0); - typedef typename ElfBuilder<ElfTypes>::RawSection RawSection; - RawSection debug_info(".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0); - RawSection debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0, nullptr, 0, 1, 0); - RawSection debug_str(".debug_str", SHT_PROGBITS, 0, nullptr, 0, 1, 0); - RawSection debug_line(".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0); - RawSection debug_frame(".debug_frame", SHT_PROGBITS, 0, nullptr, 0, 8, 0); + ScratchFile file; + FileOutputStream output_stream(file.GetFile()); + ElfBuilder<ElfTypes> builder(isa, &output_stream); + builder.Start(); if (!debug_info_data_.empty()) { - debug_info.SetBuffer(debug_info_data_); - builder.RegisterSection(&debug_info); + builder.WriteSection(".debug_info", &debug_info_data_); } if (!debug_abbrev_data_.empty()) { - debug_abbrev.SetBuffer(debug_abbrev_data_); - builder.RegisterSection(&debug_abbrev); + builder.WriteSection(".debug_abbrev", &debug_abbrev_data_); } if (!debug_str_data_.empty()) { - debug_str.SetBuffer(debug_str_data_); - builder.RegisterSection(&debug_str); + builder.WriteSection(".debug_str", &debug_str_data_); } if (!debug_line_data_.empty()) { - debug_line.SetBuffer(debug_line_data_); - builder.RegisterSection(&debug_line); + builder.WriteSection(".debug_line", &debug_line_data_); } if (!debug_frame_data_.empty()) { - debug_frame.SetBuffer(debug_frame_data_); - builder.RegisterSection(&debug_frame); + builder.WriteSection(".debug_frame", &debug_frame_data_); } - ScratchFile file; - builder.Write(file.GetFile()); + builder.End(); + EXPECT_TRUE(builder.Good()); // Read the elf file back using objdump. std::vector<std::string> lines; diff --git a/compiler/dwarf/headers.h b/compiler/dwarf/headers.h index f3fba4b1fa..883d756885 100644 --- a/compiler/dwarf/headers.h +++ b/compiler/dwarf/headers.h @@ -38,15 +38,14 @@ namespace dwarf { // Write common information entry (CIE) to .debug_frame or .eh_frame section. template<typename Vector> -void WriteDebugFrameCIE(bool is64bit, - ExceptionHeaderValueApplication address_type, - Reg return_address_register, - const DebugFrameOpCodeWriter<Vector>& opcodes, - CFIFormat format, - std::vector<uint8_t>* debug_frame) { +void WriteCIE(bool is64bit, + Reg return_address_register, + const DebugFrameOpCodeWriter<Vector>& opcodes, + CFIFormat format, + std::vector<uint8_t>* buffer) { static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type"); - Writer<> writer(debug_frame); + Writer<> writer(buffer); size_t cie_header_start_ = writer.data()->size(); writer.PushUint32(0); // Length placeholder. writer.PushUint32((format == DW_EH_FRAME_FORMAT) ? 0 : 0xFFFFFFFF); // CIE id. @@ -57,17 +56,17 @@ void WriteDebugFrameCIE(bool is64bit, writer.PushUleb128(return_address_register.num()); // ubyte in DWARF2. writer.PushUleb128(1); // z: Augmentation data size. if (is64bit) { - if (address_type == DW_EH_PE_pcrel) { + if (format == DW_EH_FRAME_FORMAT) { writer.PushUint8(DW_EH_PE_pcrel | DW_EH_PE_sdata8); // R: Pointer encoding. } else { - DCHECK(address_type == DW_EH_PE_absptr); + DCHECK(format == DW_DEBUG_FRAME_FORMAT); writer.PushUint8(DW_EH_PE_absptr | DW_EH_PE_udata8); // R: Pointer encoding. } } else { - if (address_type == DW_EH_PE_pcrel) { + if (format == DW_EH_FRAME_FORMAT) { writer.PushUint8(DW_EH_PE_pcrel | DW_EH_PE_sdata4); // R: Pointer encoding. } else { - DCHECK(address_type == DW_EH_PE_absptr); + DCHECK(format == DW_DEBUG_FRAME_FORMAT); writer.PushUint8(DW_EH_PE_absptr | DW_EH_PE_udata4); // R: Pointer encoding. } } @@ -78,30 +77,44 @@ void WriteDebugFrameCIE(bool is64bit, // Write frame description entry (FDE) to .debug_frame or .eh_frame section. inline -void WriteDebugFrameFDE(bool is64bit, size_t cie_offset, - uint64_t initial_address, uint64_t address_range, - const ArrayRef<const uint8_t>& opcodes, - CFIFormat format, - std::vector<uint8_t>* debug_frame, - std::vector<uintptr_t>* debug_frame_patches) { - Writer<> writer(debug_frame); +void WriteFDE(bool is64bit, + uint64_t section_address, // Absolute address of the section. + uint64_t cie_address, // Absolute address of last CIE. + uint64_t code_address, + uint64_t code_size, + const ArrayRef<const uint8_t>& opcodes, + CFIFormat format, + uint64_t buffer_address, // Address of buffer in linked application. + std::vector<uint8_t>* buffer, + std::vector<uintptr_t>* patch_locations) { + CHECK_GE(cie_address, section_address); + CHECK_GE(buffer_address, section_address); + + Writer<> writer(buffer); size_t fde_header_start = writer.data()->size(); writer.PushUint32(0); // Length placeholder. if (format == DW_EH_FRAME_FORMAT) { - uint32_t cie_pointer = writer.data()->size() - cie_offset; + uint32_t cie_pointer = (buffer_address + buffer->size()) - cie_address; writer.PushUint32(cie_pointer); } else { - uint32_t cie_pointer = cie_offset; + DCHECK(format == DW_DEBUG_FRAME_FORMAT); + uint32_t cie_pointer = cie_address - section_address; writer.PushUint32(cie_pointer); } - // Relocate initial_address, but not address_range (it is size). - debug_frame_patches->push_back(writer.data()->size()); + if (format == DW_EH_FRAME_FORMAT) { + // .eh_frame encodes the location as relative address. + code_address -= buffer_address + buffer->size(); + } else { + DCHECK(format == DW_DEBUG_FRAME_FORMAT); + // Relocate code_address if it has absolute value. + patch_locations->push_back(buffer_address + buffer->size() - section_address); + } if (is64bit) { - writer.PushUint64(initial_address); - writer.PushUint64(address_range); + writer.PushUint64(code_address); + writer.PushUint64(code_size); } else { - writer.PushUint32(initial_address); - writer.PushUint32(address_range); + writer.PushUint32(code_address); + writer.PushUint32(code_size); } writer.PushUleb128(0); // Augmentation data size. writer.PushData(opcodes); diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h index e977798ab1..78d1693f8c 100644 --- a/compiler/elf_builder.h +++ b/compiler/elf_builder.h @@ -21,27 +21,58 @@ #include "arch/instruction_set.h" #include "base/bit_utils.h" +#include "base/casts.h" #include "base/unix_file/fd_file.h" #include "buffered_output_stream.h" #include "elf_utils.h" #include "file_output_stream.h" +#include "leb128.h" namespace art { -class CodeOutput { - public: - virtual bool Write(OutputStream* out) = 0; - virtual ~CodeOutput() {} -}; - // Writes ELF file. -// The main complication is that the sections often want to reference -// each other. We solve this by writing the ELF file in two stages: -// * Sections are asked about their size, and overall layout is calculated. -// * Sections do the actual writes which may use offsets of other sections. +// +// The basic layout of the elf file: +// Elf_Ehdr - The ELF header. +// Elf_Phdr[] - Program headers for the linker. +// .rodata - DEX files and oat metadata. +// .text - Compiled code. +// .bss - Zero-initialized writeable section. +// .dynstr - Names for .dynsym. +// .dynsym - A few oat-specific dynamic symbols. +// .hash - Hash-table for .dynsym. +// .dynamic - Tags which let the linker locate .dynsym. +// .strtab - Names for .symtab. +// .symtab - Debug symbols. +// .eh_frame - Unwind information (CFI). +// .eh_frame_hdr - Index of .eh_frame. +// .debug_frame - Unwind information (CFI). +// .debug_frame.oat_patches - Addresses for relocation. +// .debug_info - Debug information. +// .debug_info.oat_patches - Addresses for relocation. +// .debug_abbrev - Decoding information for .debug_info. +// .debug_str - Strings for .debug_info. +// .debug_line - Line number tables. +// .debug_line.oat_patches - Addresses for relocation. +// .text.oat_patches - Addresses for relocation. +// .shstrtab - Names of ELF sections. +// Elf_Shdr[] - Section headers. +// +// Some section are optional (the debug sections in particular). +// +// We try write the section data directly into the file without much +// in-memory buffering. This means we generally write sections based on the +// dependency order (e.g. .dynamic points to .dynsym which points to .text). +// +// In the cases where we need to buffer, we write the larger section first +// and buffer the smaller one (e.g. .strtab is bigger than .symtab). +// +// The debug sections are written last for easier stripping. +// template <typename ElfTypes> class ElfBuilder FINAL { public: + static constexpr size_t kMaxProgramHeaders = 16; using Elf_Addr = typename ElfTypes::Addr; using Elf_Off = typename ElfTypes::Off; using Elf_Word = typename ElfTypes::Word; @@ -53,776 +84,420 @@ class ElfBuilder FINAL { using Elf_Dyn = typename ElfTypes::Dyn; // Base class of all sections. - class Section { + class Section : public OutputStream { public: - Section(const std::string& name, Elf_Word type, Elf_Word flags, - const Section* link, Elf_Word info, Elf_Word align, Elf_Word entsize) - : header_(), section_index_(0), name_(name), link_(link) { + Section(ElfBuilder<ElfTypes>* owner, const std::string& name, + Elf_Word type, Elf_Word flags, const Section* link, + Elf_Word info, Elf_Word align, Elf_Word entsize) + : OutputStream(name), owner_(owner), header_(), + section_index_(0), name_(name), link_(link), + started_(false), finished_(false), phdr_flags_(PF_R), phdr_type_(0) { + DCHECK_GE(align, 1u); header_.sh_type = type; header_.sh_flags = flags; header_.sh_info = info; header_.sh_addralign = align; header_.sh_entsize = entsize; } - virtual ~Section() {} - - // Returns the size of the content of this section. It is used to - // calculate file offsets of all sections before doing any writes. - virtual Elf_Word GetSize() const = 0; - - // Write the content of this section to the given file. - // This must write exactly the number of bytes returned by GetSize(). - // Offsets of all sections are known when this method is called. - virtual bool Write(File* elf_file) = 0; - - Elf_Word GetLink() const { - return (link_ != nullptr) ? link_->GetSectionIndex() : 0; - } - - const Elf_Shdr* GetHeader() const { - return &header_; - } - - Elf_Shdr* GetHeader() { - return &header_; - } - - Elf_Word GetSectionIndex() const { - DCHECK_NE(section_index_, 0u); - return section_index_; - } - - void SetSectionIndex(Elf_Word section_index) { - section_index_ = section_index; - } - - const std::string& GetName() const { - return name_; - } - - private: - Elf_Shdr header_; - Elf_Word section_index_; - const std::string name_; - const Section* const link_; - - DISALLOW_COPY_AND_ASSIGN(Section); - }; - - // Writer of .dynamic section. - class DynamicSection FINAL : public Section { - public: - void AddDynamicTag(Elf_Sword tag, Elf_Word value, const Section* section) { - DCHECK_NE(tag, static_cast<Elf_Sword>(DT_NULL)); - dynamics_.push_back({tag, value, section}); - } - DynamicSection(const std::string& name, Section* link) - : Section(name, SHT_DYNAMIC, SHF_ALLOC, - link, 0, kPageSize, sizeof(Elf_Dyn)) {} - - Elf_Word GetSize() const OVERRIDE { - return (dynamics_.size() + 1 /* DT_NULL */) * sizeof(Elf_Dyn); - } - - bool Write(File* elf_file) OVERRIDE { - std::vector<Elf_Dyn> buffer; - buffer.reserve(dynamics_.size() + 1u); - for (const ElfDynamicState& it : dynamics_) { - if (it.section_ != nullptr) { - // We are adding an address relative to a section. - buffer.push_back( - {it.tag_, {it.value_ + it.section_->GetHeader()->sh_addr}}); - } else { - buffer.push_back({it.tag_, {it.value_}}); - } + virtual ~Section() { + if (started_) { + CHECK(finished_); } - buffer.push_back({DT_NULL, {0}}); - return WriteArray(elf_file, buffer.data(), buffer.size()); - } - - private: - struct ElfDynamicState { - Elf_Sword tag_; - Elf_Word value_; - const Section* section_; - }; - std::vector<ElfDynamicState> dynamics_; - }; - - using PatchFn = void (*)(const std::vector<uintptr_t>& patch_locations, - Elf_Addr buffer_address, - Elf_Addr base_address, - std::vector<uint8_t>* buffer); - - // Section with content based on simple memory buffer. - // The buffer can be optionally patched before writing. - class RawSection FINAL : public Section { - public: - RawSection(const std::string& name, Elf_Word type, Elf_Word flags, - const Section* link, Elf_Word info, Elf_Word align, Elf_Word entsize, - PatchFn patch = nullptr, const Section* patch_base_section = nullptr) - : Section(name, type, flags, link, info, align, entsize), - patched_(false), patch_(patch), patch_base_section_(patch_base_section) { } - RawSection(const std::string& name, Elf_Word type) - : RawSection(name, type, 0, nullptr, 0, 1, 0, nullptr, nullptr) { - } - - Elf_Word GetSize() const OVERRIDE { - return buffer_.size(); + // Start writing of this section. + void Start() { + CHECK(!started_); + CHECK(!finished_); + started_ = true; + auto& sections = owner_->sections_; + // Check that the previous section is complete. + CHECK(sections.empty() || sections.back()->finished_); + // The first ELF section index is 1. Index 0 is reserved for NULL. + section_index_ = sections.size() + 1; + // Push this section on the list of written sections. + sections.push_back(this); + // Align file position. + if (header_.sh_type != SHT_NOBITS) { + header_.sh_offset = RoundUp(owner_->Seek(0, kSeekCurrent), header_.sh_addralign); + owner_->Seek(header_.sh_offset, kSeekSet); + } + // Align virtual memory address. + if ((header_.sh_flags & SHF_ALLOC) != 0) { + header_.sh_addr = RoundUp(owner_->virtual_address_, header_.sh_addralign); + owner_->virtual_address_ = header_.sh_addr; + } } - bool Write(File* elf_file) OVERRIDE { - if (!patch_locations_.empty()) { - DCHECK(!patched_); // Do not patch twice. - DCHECK(patch_ != nullptr); - DCHECK(patch_base_section_ != nullptr); - patch_(patch_locations_, - this->GetHeader()->sh_addr, - patch_base_section_->GetHeader()->sh_addr, - &buffer_); - patched_ = true; + // Finish writing of this section. + void End() { + CHECK(started_); + CHECK(!finished_); + finished_ = true; + if (header_.sh_type == SHT_NOBITS) { + CHECK_GT(header_.sh_size, 0u); + } else { + // Use the current file position to determine section size. + off_t file_offset = owner_->Seek(0, kSeekCurrent); + CHECK_GE(file_offset, (off_t)header_.sh_offset); + header_.sh_size = file_offset - header_.sh_offset; + } + if ((header_.sh_flags & SHF_ALLOC) != 0) { + owner_->virtual_address_ += header_.sh_size; } - return WriteArray(elf_file, buffer_.data(), buffer_.size()); } - bool IsEmpty() const { - return buffer_.size() == 0; + // Get the location of this section in virtual memory. + Elf_Addr GetAddress() const { + CHECK(started_); + return header_.sh_addr; } - std::vector<uint8_t>* GetBuffer() { - return &buffer_; + // Returns the size of the content of this section. + Elf_Word GetSize() const { + CHECK(finished_); + return header_.sh_size; } - void SetBuffer(const std::vector<uint8_t>& buffer) { - buffer_ = buffer; + // Set desired allocation size for .bss section. + void SetSize(Elf_Word size) { + CHECK_EQ(header_.sh_type, (Elf_Word)SHT_NOBITS); + header_.sh_size = size; } - std::vector<uintptr_t>* GetPatchLocations() { - return &patch_locations_; + // This function always succeeds to simplify code. + // Use builder's Good() to check the actual status. + bool WriteFully(const void* buffer, size_t byte_count) OVERRIDE { + CHECK(started_); + CHECK(!finished_); + owner_->WriteFully(buffer, byte_count); + return true; } - private: - std::vector<uint8_t> buffer_; - std::vector<uintptr_t> patch_locations_; - bool patched_; - // User-provided function to do the actual patching. - PatchFn patch_; - // The section that we patch against (usually .text). - const Section* patch_base_section_; - }; - - // Writer of .rodata section or .text section. - // The write is done lazily using the provided CodeOutput. - class OatSection FINAL : public Section { - public: - OatSection(const std::string& name, Elf_Word type, Elf_Word flags, - const Section* link, Elf_Word info, Elf_Word align, - Elf_Word entsize, Elf_Word size, CodeOutput* code_output) - : Section(name, type, flags, link, info, align, entsize), - size_(size), code_output_(code_output) { - } - - Elf_Word GetSize() const OVERRIDE { - return size_; + // This function always succeeds to simplify code. + // Use builder's Good() to check the actual status. + off_t Seek(off_t offset, Whence whence) OVERRIDE { + // Forward the seek as-is and trust the caller to use it reasonably. + return owner_->Seek(offset, whence); } - bool Write(File* elf_file) OVERRIDE { - // The BufferedOutputStream class contains the buffer as field, - // therefore it is too big to allocate on the stack. - std::unique_ptr<BufferedOutputStream> output_stream( - new BufferedOutputStream(new FileOutputStream(elf_file))); - return code_output_->Write(output_stream.get()); + Elf_Word GetSectionIndex() const { + DCHECK(started_); + DCHECK_NE(section_index_, 0u); + return section_index_; } private: - Elf_Word size_; - CodeOutput* code_output_; - }; - - // Writer of .bss section. - class NoBitsSection FINAL : public Section { - public: - NoBitsSection(const std::string& name, Elf_Word size) - : Section(name, SHT_NOBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0), - size_(size) { - } - - Elf_Word GetSize() const OVERRIDE { - return size_; - } + ElfBuilder<ElfTypes>* owner_; + Elf_Shdr header_; + Elf_Word section_index_; + const std::string name_; + const Section* const link_; + bool started_; + bool finished_; + Elf_Word phdr_flags_; + Elf_Word phdr_type_; - bool Write(File* elf_file ATTRIBUTE_UNUSED) OVERRIDE { - LOG(ERROR) << "This section should not be written to the ELF file"; - return false; - } + DISALLOW_COPY_AND_ASSIGN(Section); - private: - Elf_Word size_; + friend class ElfBuilder; }; // Writer of .dynstr .strtab and .shstrtab sections. - class StrtabSection FINAL : public Section { + class StringSection FINAL : public Section { public: - StrtabSection(const std::string& name, Elf_Word flags, Elf_Word align) - : Section(name, SHT_STRTAB, flags, nullptr, 0, align, 0) { - buffer_.reserve(4 * KB); - // The first entry of strtab must be empty string. - buffer_ += '\0'; + StringSection(ElfBuilder<ElfTypes>* owner, const std::string& name, + Elf_Word flags, Elf_Word align) + : Section(owner, name, SHT_STRTAB, flags, nullptr, 0, align, 0), + current_offset_(0) { } - Elf_Word AddName(const std::string& name) { - Elf_Word offset = buffer_.size(); - buffer_ += name; - buffer_ += '\0'; + Elf_Word Write(const std::string& name) { + if (current_offset_ == 0) { + DCHECK(name.empty()); + } + Elf_Word offset = current_offset_; + this->WriteFully(name.c_str(), name.length() + 1); + current_offset_ += name.length() + 1; return offset; } - Elf_Word GetSize() const OVERRIDE { - return buffer_.size(); - } - - bool Write(File* elf_file) OVERRIDE { - return WriteArray(elf_file, buffer_.data(), buffer_.size()); - } - private: - std::string buffer_; + Elf_Word current_offset_; }; - class HashSection; - // Writer of .dynsym and .symtab sections. - class SymtabSection FINAL : public Section { + class SymbolSection FINAL : public Section { public: - // Add a symbol with given name to this symtab. The symbol refers to - // 'relative_addr' within the given section and has the given attributes. - void AddSymbol(const std::string& name, const Section* section, - Elf_Addr addr, bool is_relative, Elf_Word size, - uint8_t binding, uint8_t type, uint8_t other = 0) { - CHECK(section != nullptr); - Elf_Word name_idx = strtab_->AddName(name); - symbols_.push_back({ name, section, addr, size, is_relative, - MakeStInfo(binding, type), other, name_idx }); + SymbolSection(ElfBuilder<ElfTypes>* owner, const std::string& name, + Elf_Word type, Elf_Word flags, StringSection* strtab) + : Section(owner, name, type, flags, strtab, 0, + sizeof(Elf_Off), sizeof(Elf_Sym)) { } - SymtabSection(const std::string& name, Elf_Word type, Elf_Word flags, - StrtabSection* strtab) - : Section(name, type, flags, strtab, 0, sizeof(Elf_Off), sizeof(Elf_Sym)), - strtab_(strtab) { - } - - bool IsEmpty() const { - return symbols_.empty(); - } - - Elf_Word GetSize() const OVERRIDE { - return (1 /* NULL */ + symbols_.size()) * sizeof(Elf_Sym); + // Buffer symbol for this section. It will be written later. + void Add(Elf_Word name, const Section* section, + Elf_Addr addr, bool is_relative, Elf_Word size, + uint8_t binding, uint8_t type, uint8_t other = 0) { + CHECK(section != nullptr); + Elf_Sym sym = Elf_Sym(); + sym.st_name = name; + sym.st_value = addr + (is_relative ? section->GetAddress() : 0); + sym.st_size = size; + sym.st_other = other; + sym.st_shndx = section->GetSectionIndex(); + sym.st_info = (binding << 4) + (type & 0xf); + symbols_.push_back(sym); } - bool Write(File* elf_file) OVERRIDE { - std::vector<Elf_Sym> buffer; - buffer.reserve(1u + symbols_.size()); - buffer.push_back(Elf_Sym()); // NULL. - for (const ElfSymbolState& it : symbols_) { - Elf_Sym sym = Elf_Sym(); - sym.st_name = it.name_idx_; - if (it.is_relative_) { - sym.st_value = it.addr_ + it.section_->GetHeader()->sh_addr; - } else { - sym.st_value = it.addr_; - } - sym.st_size = it.size_; - sym.st_other = it.other_; - sym.st_shndx = it.section_->GetSectionIndex(); - sym.st_info = it.info_; - buffer.push_back(sym); - } - return WriteArray(elf_file, buffer.data(), buffer.size()); + void Write() { + // The symbol table always has to start with NULL symbol. + Elf_Sym null_symbol = Elf_Sym(); + this->WriteFully(&null_symbol, sizeof(null_symbol)); + this->WriteFully(symbols_.data(), symbols_.size() * sizeof(symbols_[0])); + symbols_.clear(); + symbols_.shrink_to_fit(); } private: - struct ElfSymbolState { - const std::string name_; - const Section* section_; - Elf_Addr addr_; - Elf_Word size_; - bool is_relative_; - uint8_t info_; - uint8_t other_; - Elf_Word name_idx_; // index in the strtab. - }; - - static inline constexpr uint8_t MakeStInfo(uint8_t binding, uint8_t type) { - return ((binding) << 4) + ((type) & 0xf); - } - - // The symbols in the same order they will be in the symbol table. - std::vector<ElfSymbolState> symbols_; - StrtabSection* strtab_; - - friend class HashSection; + std::vector<Elf_Sym> symbols_; }; - // TODO: Consider removing. - // We use it only for the dynsym section which has only 5 symbols. - // We do not use it for symtab, and we probably do not have to - // since we use those symbols only to print backtraces. - class HashSection FINAL : public Section { - public: - HashSection(const std::string& name, Elf_Word flags, SymtabSection* symtab) - : Section(name, SHT_HASH, flags, symtab, - 0, sizeof(Elf_Word), sizeof(Elf_Word)), - symtab_(symtab) { - } - - Elf_Word GetSize() const OVERRIDE { - Elf_Word nbuckets = GetNumBuckets(); - Elf_Word chain_size = symtab_->symbols_.size() + 1 /* NULL */; - return (2 /* header */ + nbuckets + chain_size) * sizeof(Elf_Word); - } - - bool Write(File* const elf_file) OVERRIDE { - // Here is how The ELF hash table works. - // There are 3 arrays to worry about. - // * The symbol table where the symbol information is. - // * The bucket array which is an array of indexes into the symtab and chain. - // * The chain array which is also an array of indexes into the symtab and chain. - // - // Lets say the state is something like this. - // +--------+ +--------+ +-----------+ - // | symtab | | bucket | | chain | - // | null | | 1 | | STN_UNDEF | - // | <sym1> | | 4 | | 2 | - // | <sym2> | | | | 5 | - // | <sym3> | | | | STN_UNDEF | - // | <sym4> | | | | 3 | - // | <sym5> | | | | STN_UNDEF | - // +--------+ +--------+ +-----------+ - // - // The lookup process (in python psudocode) is - // - // def GetSym(name): - // # NB STN_UNDEF == 0 - // indx = bucket[elfhash(name) % num_buckets] - // while indx != STN_UNDEF: - // if GetSymbolName(symtab[indx]) == name: - // return symtab[indx] - // indx = chain[indx] - // return SYMBOL_NOT_FOUND - // - // Between bucket and chain arrays every symtab index must be present exactly - // once (except for STN_UNDEF, which must be present 1 + num_bucket times). - const auto& symbols = symtab_->symbols_; - // Select number of buckets. - // This is essentially arbitrary. - Elf_Word nbuckets = GetNumBuckets(); - // 1 is for the implicit NULL symbol. - Elf_Word chain_size = (symbols.size() + 1); - std::vector<Elf_Word> hash; - hash.push_back(nbuckets); - hash.push_back(chain_size); - uint32_t bucket_offset = hash.size(); - uint32_t chain_offset = bucket_offset + nbuckets; - hash.resize(hash.size() + nbuckets + chain_size, 0); - - Elf_Word* buckets = hash.data() + bucket_offset; - Elf_Word* chain = hash.data() + chain_offset; - - // Set up the actual hash table. - for (Elf_Word i = 0; i < symbols.size(); i++) { - // Add 1 since we need to have the null symbol that is not in the symbols - // list. - Elf_Word index = i + 1; - Elf_Word hash_val = static_cast<Elf_Word>(elfhash(symbols[i].name_.c_str())) % nbuckets; - if (buckets[hash_val] == 0) { - buckets[hash_val] = index; - } else { - hash_val = buckets[hash_val]; - CHECK_LT(hash_val, chain_size); - while (chain[hash_val] != 0) { - hash_val = chain[hash_val]; - CHECK_LT(hash_val, chain_size); - } - chain[hash_val] = index; - // Check for loops. Works because if this is non-empty then there must be - // another cell which already contains the same symbol index as this one, - // which means some symbol has more then one name, which isn't allowed. - CHECK_EQ(chain[index], static_cast<Elf_Word>(0)); - } - } - return WriteArray(elf_file, hash.data(), hash.size()); - } - - private: - Elf_Word GetNumBuckets() const { - const auto& symbols = symtab_->symbols_; - // Have about 32 ids per bucket. - return 1 + symbols.size()/32; - } - - // from bionic - static inline unsigned elfhash(const char *_name) { - const unsigned char *name = (const unsigned char *) _name; - unsigned h = 0, g; + ElfBuilder(InstructionSet isa, OutputStream* output) + : isa_(isa), + output_(output), + output_good_(true), + output_offset_(0), + rodata_(this, ".rodata", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0), + text_(this, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, nullptr, 0, kPageSize, 0), + bss_(this, ".bss", SHT_NOBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0), + dynstr_(this, ".dynstr", SHF_ALLOC, kPageSize), + dynsym_(this, ".dynsym", SHT_DYNSYM, SHF_ALLOC, &dynstr_), + hash_(this, ".hash", SHT_HASH, SHF_ALLOC, &dynsym_, 0, sizeof(Elf_Word), sizeof(Elf_Word)), + dynamic_(this, ".dynamic", SHT_DYNAMIC, SHF_ALLOC, &dynstr_, 0, kPageSize, sizeof(Elf_Dyn)), + eh_frame_(this, ".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0), + eh_frame_hdr_(this, ".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0), + strtab_(this, ".strtab", 0, kPageSize), + symtab_(this, ".symtab", SHT_SYMTAB, 0, &strtab_), + debug_frame_(this, ".debug_frame", SHT_PROGBITS, 0, nullptr, 0, sizeof(Elf_Addr), 0), + shstrtab_(this, ".shstrtab", 0, 1), + virtual_address_(0) { + text_.phdr_flags_ = PF_R | PF_X; + bss_.phdr_flags_ = PF_R | PF_W; + dynamic_.phdr_flags_ = PF_R | PF_W; + dynamic_.phdr_type_ = PT_DYNAMIC; + eh_frame_hdr_.phdr_type_ = PT_GNU_EH_FRAME; + } + ~ElfBuilder() {} - while (*name) { - h = (h << 4) + *name++; - g = h & 0xf0000000; - h ^= g; - h ^= g >> 24; - } - return h; + InstructionSet GetIsa() { return isa_; } + Section* GetRoData() { return &rodata_; } + Section* GetText() { return &text_; } + Section* GetBss() { return &bss_; } + StringSection* GetStrTab() { return &strtab_; } + SymbolSection* GetSymTab() { return &symtab_; } + Section* GetEhFrame() { return &eh_frame_; } + Section* GetEhFrameHdr() { return &eh_frame_hdr_; } + Section* GetDebugFrame() { return &debug_frame_; } + + // Encode patch locations as LEB128 list of deltas between consecutive addresses. + // (exposed publicly for tests) + static void EncodeOatPatches(const std::vector<uintptr_t>& locations, + std::vector<uint8_t>* buffer) { + buffer->reserve(buffer->size() + locations.size() * 2); // guess 2 bytes per ULEB128. + uintptr_t address = 0; // relative to start of section. + for (uintptr_t location : locations) { + DCHECK_GE(location, address) << "Patch locations are not in sorted order"; + EncodeUnsignedLeb128(buffer, dchecked_integral_cast<uint32_t>(location - address)); + address = location; } + } - SymtabSection* symtab_; - - DISALLOW_COPY_AND_ASSIGN(HashSection); - }; + void WritePatches(const char* name, const std::vector<uintptr_t>* patch_locations) { + std::vector<uint8_t> buffer; + EncodeOatPatches(*patch_locations, &buffer); + std::unique_ptr<Section> s(new Section(this, name, SHT_OAT_PATCH, 0, nullptr, 0, 1, 0)); + s->Start(); + s->WriteFully(buffer.data(), buffer.size()); + s->End(); + other_sections_.push_back(std::move(s)); + } - ElfBuilder(InstructionSet isa, - Elf_Word rodata_size, CodeOutput* rodata_writer, - Elf_Word text_size, CodeOutput* text_writer, - Elf_Word bss_size) - : isa_(isa), - dynstr_(".dynstr", SHF_ALLOC, kPageSize), - dynsym_(".dynsym", SHT_DYNSYM, SHF_ALLOC, &dynstr_), - hash_(".hash", SHF_ALLOC, &dynsym_), - rodata_(".rodata", SHT_PROGBITS, SHF_ALLOC, - nullptr, 0, kPageSize, 0, rodata_size, rodata_writer), - text_(".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, - nullptr, 0, kPageSize, 0, text_size, text_writer), - bss_(".bss", bss_size), - dynamic_(".dynamic", &dynstr_), - strtab_(".strtab", 0, kPageSize), - symtab_(".symtab", SHT_SYMTAB, 0, &strtab_), - shstrtab_(".shstrtab", 0, 1) { + void WriteSection(const char* name, const std::vector<uint8_t>* buffer) { + std::unique_ptr<Section> s(new Section(this, name, SHT_PROGBITS, 0, nullptr, 0, 1, 0)); + s->Start(); + s->WriteFully(buffer->data(), buffer->size()); + s->End(); + other_sections_.push_back(std::move(s)); } - ~ElfBuilder() {} - OatSection* GetText() { return &text_; } - SymtabSection* GetSymtab() { return &symtab_; } - - bool Write(File* elf_file) { - // Since the .text section of an oat file contains relative references to .rodata - // and (optionally) .bss, we keep these 2 or 3 sections together. This creates - // a non-traditional layout where the .bss section is mapped independently of the - // .dynamic section and needs its own program header with LOAD RW. - // - // The basic layout of the elf file. Order may be different in final output. - // +-------------------------+ - // | Elf_Ehdr | - // +-------------------------+ - // | Elf_Phdr PHDR | - // | Elf_Phdr LOAD R | .dynsym .dynstr .hash .rodata - // | Elf_Phdr LOAD R X | .text - // | Elf_Phdr LOAD RW | .bss (Optional) - // | Elf_Phdr LOAD RW | .dynamic - // | Elf_Phdr DYNAMIC | .dynamic - // | Elf_Phdr LOAD R | .eh_frame .eh_frame_hdr - // | Elf_Phdr EH_FRAME R | .eh_frame_hdr - // +-------------------------+ - // | .dynsym | - // | Elf_Sym STN_UNDEF | - // | Elf_Sym oatdata | - // | Elf_Sym oatexec | - // | Elf_Sym oatlastword | - // | Elf_Sym oatbss | (Optional) - // | Elf_Sym oatbsslastword | (Optional) - // +-------------------------+ - // | .dynstr | - // | names for .dynsym | - // +-------------------------+ - // | .hash | - // | hashtable for dynsym | - // +-------------------------+ - // | .rodata | - // | oatdata..oatexec-4 | - // +-------------------------+ - // | .text | - // | oatexec..oatlastword | - // +-------------------------+ - // | .dynamic | - // | Elf_Dyn DT_HASH | - // | Elf_Dyn DT_STRTAB | - // | Elf_Dyn DT_SYMTAB | - // | Elf_Dyn DT_SYMENT | - // | Elf_Dyn DT_STRSZ | - // | Elf_Dyn DT_SONAME | - // | Elf_Dyn DT_NULL | - // +-------------------------+ (Optional) - // | .symtab | (Optional) - // | program symbols | (Optional) - // +-------------------------+ (Optional) - // | .strtab | (Optional) - // | names for .symtab | (Optional) - // +-------------------------+ (Optional) - // | .eh_frame | (Optional) - // +-------------------------+ (Optional) - // | .eh_frame_hdr | (Optional) - // +-------------------------+ (Optional) - // | .debug_info | (Optional) - // +-------------------------+ (Optional) - // | .debug_abbrev | (Optional) - // +-------------------------+ (Optional) - // | .debug_str | (Optional) - // +-------------------------+ (Optional) - // | .debug_line | (Optional) - // +-------------------------+ - // | .shstrtab | - // | names of sections | - // +-------------------------+ - // | Elf_Shdr null | - // | Elf_Shdr .dynsym | - // | Elf_Shdr .dynstr | - // | Elf_Shdr .hash | - // | Elf_Shdr .rodata | - // | Elf_Shdr .text | - // | Elf_Shdr .bss | (Optional) - // | Elf_Shdr .dynamic | - // | Elf_Shdr .symtab | (Optional) - // | Elf_Shdr .strtab | (Optional) - // | Elf_Shdr .eh_frame | (Optional) - // | Elf_Shdr .eh_frame_hdr | (Optional) - // | Elf_Shdr .debug_info | (Optional) - // | Elf_Shdr .debug_abbrev | (Optional) - // | Elf_Shdr .debug_str | (Optional) - // | Elf_Shdr .debug_line | (Optional) - // | Elf_Shdr .oat_patches | (Optional) - // | Elf_Shdr .shstrtab | - // +-------------------------+ - constexpr bool debug_logging_ = false; - - // Create a list of all section which we want to write. - // This is the order in which they will be written. - std::vector<Section*> sections; - sections.push_back(&rodata_); - // Need to write text to update checksum of header even if it is empty. - sections.push_back(&text_); - if (bss_.GetSize() != 0u) { - sections.push_back(&bss_); - } - sections.push_back(&dynstr_); - sections.push_back(&dynsym_); - sections.push_back(&hash_); - sections.push_back(&dynamic_); - if (!symtab_.IsEmpty()) { - sections.push_back(&strtab_); - sections.push_back(&symtab_); - } - for (Section* section : other_sections_) { - sections.push_back(section); - } - sections.push_back(&shstrtab_); - for (size_t i = 0; i < sections.size(); i++) { - // The first section index is 1. Index 0 is reserved for NULL. - // Section index is used for relative symbols and for section links. - sections[i]->SetSectionIndex(i + 1); - // Add section name to .shstrtab. - Elf_Word name_offset = shstrtab_.AddName(sections[i]->GetName()); - sections[i]->GetHeader()->sh_name = name_offset; - } + void Start() { + // Reserve space for ELF header and program headers. + // We do not know the number of headers until later, so + // it is easiest to just reserve a fixed amount of space. + int size = sizeof(Elf_Ehdr) + sizeof(Elf_Phdr) * kMaxProgramHeaders; + Seek(size, kSeekSet); + virtual_address_ += size; + } - // The running program does not have access to section headers - // and the loader is not supposed to use them either. - // The dynamic sections therefore replicates some of the layout - // information like the address and size of .rodata and .text. - // It also contains other metadata like the SONAME. - // The .dynamic section is found using the PT_DYNAMIC program header. - BuildDynsymSection(); - BuildDynamicSection(elf_file->GetPath()); - - // We do not know the number of headers until the final stages of write. - // It is easiest to just reserve a fixed amount of space for them. - constexpr size_t kMaxProgramHeaders = 16; - constexpr size_t kProgramHeadersOffset = sizeof(Elf_Ehdr); - - // Layout of all sections - determine the final file offsets and addresses. - // This must be done after we have built all sections and know their size. - Elf_Off file_offset = kProgramHeadersOffset + sizeof(Elf_Phdr) * kMaxProgramHeaders; - Elf_Addr load_address = file_offset; - std::vector<Elf_Shdr> section_headers; - section_headers.reserve(1u + sections.size()); - section_headers.push_back(Elf_Shdr()); // NULL at index 0. - for (auto* section : sections) { - Elf_Shdr* header = section->GetHeader(); - Elf_Off alignment = header->sh_addralign > 0 ? header->sh_addralign : 1; - header->sh_size = section->GetSize(); - header->sh_link = section->GetLink(); - // Allocate memory for the section in the file. - if (header->sh_type != SHT_NOBITS) { - header->sh_offset = RoundUp(file_offset, alignment); - file_offset = header->sh_offset + header->sh_size; - } - // Allocate memory for the section during program execution. - if ((header->sh_flags & SHF_ALLOC) != 0) { - header->sh_addr = RoundUp(load_address, alignment); - load_address = header->sh_addr + header->sh_size; + void End() { + // Write section names and finish the section headers. + shstrtab_.Start(); + shstrtab_.Write(""); + for (auto* section : sections_) { + section->header_.sh_name = shstrtab_.Write(section->name_); + if (section->link_ != nullptr) { + section->header_.sh_link = section->link_->GetSectionIndex(); } - if (debug_logging_) { - LOG(INFO) << "Section " << section->GetName() << ":" << std::hex - << " offset=0x" << header->sh_offset - << " addr=0x" << header->sh_addr - << " size=0x" << header->sh_size; - } - // Collect section headers into continuous array for convenience. - section_headers.push_back(*header); - } - Elf_Off section_headers_offset = RoundUp(file_offset, sizeof(Elf_Off)); - - // Create program headers now that we know the layout of the whole file. - // Each segment contains one or more sections which are mapped together. - // Not all sections are mapped during the execution of the program. - // PT_LOAD does the mapping. Other PT_* types allow the program to locate - // interesting parts of memory and their addresses overlap with PT_LOAD. - std::vector<Elf_Phdr> program_headers; - program_headers.push_back(Elf_Phdr()); // Placeholder for PT_PHDR. - // Create the main LOAD R segment which spans all sections up to .rodata. - const Elf_Shdr* rodata = rodata_.GetHeader(); - program_headers.push_back(MakeProgramHeader(PT_LOAD, PF_R, - 0, rodata->sh_offset + rodata->sh_size, rodata->sh_addralign)); - if (text_.GetHeader()->sh_size != 0u) { - program_headers.push_back(MakeProgramHeader(PT_LOAD, PF_R | PF_X, text_)); - } - if (bss_.GetHeader()->sh_size != 0u) { - program_headers.push_back(MakeProgramHeader(PT_LOAD, PF_R | PF_W, bss_)); } - program_headers.push_back(MakeProgramHeader(PT_LOAD, PF_R, dynstr_)); - int dynstr_dynsym_hash_size = hash_.GetHeader()->sh_offset + - hash_.GetHeader()->sh_size - dynstr_.GetHeader()->sh_offset; - program_headers.back().p_filesz = dynstr_dynsym_hash_size; - program_headers.back().p_memsz = dynstr_dynsym_hash_size; - program_headers.push_back(MakeProgramHeader(PT_LOAD, PF_R | PF_W, dynamic_)); - program_headers.push_back(MakeProgramHeader(PT_DYNAMIC, PF_R | PF_W, dynamic_)); - const Section* eh_frame = FindSection(".eh_frame"); - if (eh_frame != nullptr) { - program_headers.push_back(MakeProgramHeader(PT_LOAD, PF_R, *eh_frame)); - const Section* eh_frame_hdr = FindSection(".eh_frame_hdr"); - if (eh_frame_hdr != nullptr) { - // Check layout: eh_frame is before eh_frame_hdr and there is no gap. - CHECK_LE(eh_frame->GetHeader()->sh_offset, eh_frame_hdr->GetHeader()->sh_offset); - CHECK_EQ(eh_frame->GetHeader()->sh_offset + eh_frame->GetHeader()->sh_size, - eh_frame_hdr->GetHeader()->sh_offset); - // Extend the PT_LOAD of .eh_frame to include the .eh_frame_hdr as well. - program_headers.back().p_filesz += eh_frame_hdr->GetHeader()->sh_size; - program_headers.back().p_memsz += eh_frame_hdr->GetHeader()->sh_size; - program_headers.push_back(MakeProgramHeader(PT_GNU_EH_FRAME, PF_R, *eh_frame_hdr)); - } + shstrtab_.End(); + + // Write section headers at the end of the ELF file. + std::vector<Elf_Shdr> shdrs; + shdrs.reserve(1u + sections_.size()); + shdrs.push_back(Elf_Shdr()); // NULL at index 0. + for (auto* section : sections_) { + shdrs.push_back(section->header_); } - DCHECK_EQ(program_headers[0].p_type, 0u); // Check placeholder. - program_headers[0] = MakeProgramHeader(PT_PHDR, PF_R, - kProgramHeadersOffset, program_headers.size() * sizeof(Elf_Phdr), sizeof(Elf_Off)); - CHECK_LE(program_headers.size(), kMaxProgramHeaders); + Elf_Off section_headers_offset; + section_headers_offset = RoundUp(Seek(0, kSeekCurrent), sizeof(Elf_Off)); + Seek(section_headers_offset, kSeekSet); + WriteFully(shdrs.data(), shdrs.size() * sizeof(shdrs[0])); - // Create the main ELF header. + // Write the initial file headers. + std::vector<Elf_Phdr> phdrs = MakeProgramHeaders(); Elf_Ehdr elf_header = MakeElfHeader(isa_); - elf_header.e_phoff = kProgramHeadersOffset; + elf_header.e_phoff = sizeof(Elf_Ehdr); elf_header.e_shoff = section_headers_offset; - elf_header.e_phnum = program_headers.size(); - elf_header.e_shnum = section_headers.size(); + elf_header.e_phnum = phdrs.size(); + elf_header.e_shnum = shdrs.size(); elf_header.e_shstrndx = shstrtab_.GetSectionIndex(); + Seek(0, kSeekSet); + WriteFully(&elf_header, sizeof(elf_header)); + WriteFully(phdrs.data(), phdrs.size() * sizeof(phdrs[0])); + } - // Write all headers and section content to the file. - // Depending on the implementations of Section::Write, this - // might be just memory copies or some more elaborate operations. - if (!WriteArray(elf_file, &elf_header, 1)) { - LOG(INFO) << "Failed to write the ELF header"; - return false; - } - if (!WriteArray(elf_file, program_headers.data(), program_headers.size())) { - LOG(INFO) << "Failed to write the program headers"; - return false; - } - for (Section* section : sections) { - const Elf_Shdr* header = section->GetHeader(); - if (header->sh_type != SHT_NOBITS) { - if (!SeekTo(elf_file, header->sh_offset) || !section->Write(elf_file)) { - LOG(INFO) << "Failed to write section " << section->GetName(); - return false; - } - Elf_Word current_offset = lseek(elf_file->Fd(), 0, SEEK_CUR); - CHECK_EQ(current_offset, header->sh_offset + header->sh_size) - << "The number of bytes written does not match GetSize()"; - } - } - if (!SeekTo(elf_file, section_headers_offset) || - !WriteArray(elf_file, section_headers.data(), section_headers.size())) { - LOG(INFO) << "Failed to write the section headers"; - return false; + // The running program does not have access to section headers + // and the loader is not supposed to use them either. + // The dynamic sections therefore replicates some of the layout + // information like the address and size of .rodata and .text. + // It also contains other metadata like the SONAME. + // The .dynamic section is found using the PT_DYNAMIC program header. + void WriteDynamicSection(const std::string& elf_file_path) { + std::string soname(elf_file_path); + size_t directory_separator_pos = soname.rfind('/'); + if (directory_separator_pos != std::string::npos) { + soname = soname.substr(directory_separator_pos + 1); } - return true; - } - // Adds the given section to the builder. It does not take ownership. - void RegisterSection(Section* section) { - other_sections_.push_back(section); + dynstr_.Start(); + dynstr_.Write(""); // dynstr should start with empty string. + dynsym_.Add(dynstr_.Write("oatdata"), &rodata_, 0, true, + rodata_.GetSize(), STB_GLOBAL, STT_OBJECT); + if (text_.GetSize() != 0u) { + dynsym_.Add(dynstr_.Write("oatexec"), &text_, 0, true, + text_.GetSize(), STB_GLOBAL, STT_OBJECT); + dynsym_.Add(dynstr_.Write("oatlastword"), &text_, text_.GetSize() - 4, + true, 4, STB_GLOBAL, STT_OBJECT); + } else if (rodata_.GetSize() != 0) { + // rodata_ can be size 0 for dwarf_test. + dynsym_.Add(dynstr_.Write("oatlastword"), &rodata_, rodata_.GetSize() - 4, + true, 4, STB_GLOBAL, STT_OBJECT); + } + if (bss_.finished_) { + dynsym_.Add(dynstr_.Write("oatbss"), &bss_, + 0, true, bss_.GetSize(), STB_GLOBAL, STT_OBJECT); + dynsym_.Add(dynstr_.Write("oatbsslastword"), &bss_, + bss_.GetSize() - 4, true, 4, STB_GLOBAL, STT_OBJECT); + } + Elf_Word soname_offset = dynstr_.Write(soname); + dynstr_.End(); + + dynsym_.Start(); + dynsym_.Write(); + dynsym_.End(); + + // We do not really need a hash-table since there is so few entries. + // However, the hash-table is the only way the linker can actually + // determine the number of symbols in .dynsym so it is required. + hash_.Start(); + int count = dynsym_.GetSize() / sizeof(Elf_Sym); // Includes NULL. + std::vector<Elf_Word> hash; + hash.push_back(1); // Number of buckets. + hash.push_back(count); // Number of chains. + // Buckets. Having just one makes it linear search. + hash.push_back(1); // Point to first non-NULL symbol. + // Chains. This creates linked list of symbols. + hash.push_back(0); // Dummy entry for the NULL symbol. + for (int i = 1; i < count - 1; i++) { + hash.push_back(i + 1); // Each symbol points to the next one. + } + hash.push_back(0); // Last symbol terminates the chain. + hash_.WriteFully(hash.data(), hash.size() * sizeof(hash[0])); + hash_.End(); + + dynamic_.Start(); + Elf_Dyn dyns[] = { + { DT_HASH, { hash_.GetAddress() } }, + { DT_STRTAB, { dynstr_.GetAddress() } }, + { DT_SYMTAB, { dynsym_.GetAddress() } }, + { DT_SYMENT, { sizeof(Elf_Sym) } }, + { DT_STRSZ, { dynstr_.GetSize() } }, + { DT_SONAME, { soname_offset } }, + { DT_NULL, { 0 } }, + }; + dynamic_.WriteFully(&dyns, sizeof(dyns)); + dynamic_.End(); } - const Section* FindSection(const char* name) { - for (const auto* section : other_sections_) { - if (section->GetName() == name) { - return section; - } - } - return nullptr; + // Returns true if all writes and seeks on the output stream succeeded. + bool Good() { + return output_good_; } private: - static bool SeekTo(File* elf_file, Elf_Word offset) { - DCHECK_LE(lseek(elf_file->Fd(), 0, SEEK_CUR), static_cast<off_t>(offset)) - << "Seeking backwards"; - if (static_cast<off_t>(offset) != lseek(elf_file->Fd(), offset, SEEK_SET)) { - PLOG(ERROR) << "Failed to seek in file " << elf_file->GetPath(); - return false; + // This function always succeeds to simplify code. + // Use Good() to check the actual status of the output stream. + void WriteFully(const void* buffer, size_t byte_count) { + if (output_good_) { + if (!output_->WriteFully(buffer, byte_count)) { + PLOG(ERROR) << "Failed to write " << byte_count + << " bytes to ELF file at offset " << output_offset_; + output_good_ = false; + } } - return true; + output_offset_ += byte_count; } - template<typename T> - static bool WriteArray(File* elf_file, const T* data, size_t count) { - if (count != 0) { - DCHECK(data != nullptr); - if (!elf_file->WriteFully(data, count * sizeof(T))) { - PLOG(ERROR) << "Failed to write to file " << elf_file->GetPath(); - return false; + // This function always succeeds to simplify code. + // Use Good() to check the actual status of the output stream. + off_t Seek(off_t offset, Whence whence) { + // We keep shadow copy of the offset so that we return + // the expected value even if the output stream failed. + off_t new_offset; + switch (whence) { + case kSeekSet: + new_offset = offset; + break; + case kSeekCurrent: + new_offset = output_offset_ + offset; + break; + default: + LOG(FATAL) << "Unsupported seek type: " << whence; + UNREACHABLE(); + } + if (output_good_) { + off_t actual_offset = output_->Seek(offset, whence); + if (actual_offset == (off_t)-1) { + PLOG(ERROR) << "Failed to seek in ELF file. Offset=" << offset + << " whence=" << whence << " new_offset=" << new_offset; + output_good_ = false; } + DCHECK_EQ(actual_offset, new_offset); } - return true; - } - - // Helper - create segment header based on memory range. - static Elf_Phdr MakeProgramHeader(Elf_Word type, Elf_Word flags, - Elf_Off offset, Elf_Word size, Elf_Word align) { - Elf_Phdr phdr = Elf_Phdr(); - phdr.p_type = type; - phdr.p_flags = flags; - phdr.p_offset = offset; - phdr.p_vaddr = offset; - phdr.p_paddr = offset; - phdr.p_filesz = size; - phdr.p_memsz = size; - phdr.p_align = align; - return phdr; - } - - // Helper - create segment header based on section header. - static Elf_Phdr MakeProgramHeader(Elf_Word type, Elf_Word flags, - const Section& section) { - const Elf_Shdr* shdr = section.GetHeader(); - // Only run-time allocated sections should be in segment headers. - CHECK_NE(shdr->sh_flags & SHF_ALLOC, 0u); - Elf_Phdr phdr = Elf_Phdr(); - phdr.p_type = type; - phdr.p_flags = flags; - phdr.p_offset = shdr->sh_offset; - phdr.p_vaddr = shdr->sh_addr; - phdr.p_paddr = shdr->sh_addr; - phdr.p_filesz = shdr->sh_type != SHT_NOBITS ? shdr->sh_size : 0u; - phdr.p_memsz = shdr->sh_size; - phdr.p_align = shdr->sh_addralign; - return phdr; + output_offset_ = new_offset; + return new_offset; } static Elf_Ehdr MakeElfHeader(InstructionSet isa) { @@ -869,6 +544,10 @@ class ElfBuilder FINAL { } case kNone: { LOG(FATAL) << "No instruction set"; + break; + } + default: { + LOG(FATAL) << "Unknown instruction set " << isa; } } @@ -892,56 +571,110 @@ class ElfBuilder FINAL { return elf_header; } - void BuildDynamicSection(const std::string& elf_file_path) { - std::string soname(elf_file_path); - size_t directory_separator_pos = soname.rfind('/'); - if (directory_separator_pos != std::string::npos) { - soname = soname.substr(directory_separator_pos + 1); - } - // NB: We must add the name before adding DT_STRSZ. - Elf_Word soname_offset = dynstr_.AddName(soname); - - dynamic_.AddDynamicTag(DT_HASH, 0, &hash_); - dynamic_.AddDynamicTag(DT_STRTAB, 0, &dynstr_); - dynamic_.AddDynamicTag(DT_SYMTAB, 0, &dynsym_); - dynamic_.AddDynamicTag(DT_SYMENT, sizeof(Elf_Sym), nullptr); - dynamic_.AddDynamicTag(DT_STRSZ, dynstr_.GetSize(), nullptr); - dynamic_.AddDynamicTag(DT_SONAME, soname_offset, nullptr); - } - - void BuildDynsymSection() { - dynsym_.AddSymbol("oatdata", &rodata_, 0, true, - rodata_.GetSize(), STB_GLOBAL, STT_OBJECT); - if (text_.GetSize() != 0u) { - dynsym_.AddSymbol("oatexec", &text_, 0, true, - text_.GetSize(), STB_GLOBAL, STT_OBJECT); - dynsym_.AddSymbol("oatlastword", &text_, text_.GetSize() - 4, - true, 4, STB_GLOBAL, STT_OBJECT); - } else if (rodata_.GetSize() != 0) { - // rodata_ be size 0 for dwarf_test. - dynsym_.AddSymbol("oatlastword", &rodata_, rodata_.GetSize() - 4, - true, 4, STB_GLOBAL, STT_OBJECT); + // Create program headers based on written sections. + std::vector<Elf_Phdr> MakeProgramHeaders() { + CHECK(!sections_.empty()); + std::vector<Elf_Phdr> phdrs; + { + // The program headers must start with PT_PHDR which is used in + // loaded process to determine the number of program headers. + Elf_Phdr phdr = Elf_Phdr(); + phdr.p_type = PT_PHDR; + phdr.p_flags = PF_R; + phdr.p_offset = phdr.p_vaddr = phdr.p_paddr = sizeof(Elf_Ehdr); + phdr.p_filesz = phdr.p_memsz = 0; // We need to fill this later. + phdr.p_align = sizeof(Elf_Off); + phdrs.push_back(phdr); + // Tell the linker to mmap the start of file to memory. + Elf_Phdr load = Elf_Phdr(); + load.p_type = PT_LOAD; + load.p_flags = PF_R; + load.p_offset = load.p_vaddr = load.p_paddr = 0; + load.p_filesz = load.p_memsz = sections_[0]->header_.sh_offset; + load.p_align = kPageSize; + phdrs.push_back(load); + } + // Create program headers for sections. + for (auto* section : sections_) { + const Elf_Shdr& shdr = section->header_; + if ((shdr.sh_flags & SHF_ALLOC) != 0 && shdr.sh_size != 0) { + // PT_LOAD tells the linker to mmap part of the file. + // The linker can only mmap page-aligned sections. + // Single PT_LOAD may contain several ELF sections. + Elf_Phdr& prev = phdrs.back(); + Elf_Phdr load = Elf_Phdr(); + load.p_type = PT_LOAD; + load.p_flags = section->phdr_flags_; + load.p_offset = shdr.sh_offset; + load.p_vaddr = load.p_paddr = shdr.sh_addr; + load.p_filesz = (shdr.sh_type != SHT_NOBITS ? shdr.sh_size : 0u); + load.p_memsz = shdr.sh_size; + load.p_align = shdr.sh_addralign; + if (prev.p_type == load.p_type && + prev.p_flags == load.p_flags && + prev.p_filesz == prev.p_memsz && // Do not merge .bss + load.p_filesz == load.p_memsz) { // Do not merge .bss + // Merge this PT_LOAD with the previous one. + Elf_Word size = shdr.sh_offset + shdr.sh_size - prev.p_offset; + prev.p_filesz = size; + prev.p_memsz = size; + } else { + // If we are adding new load, it must be aligned. + CHECK_EQ(shdr.sh_addralign, (Elf_Word)kPageSize); + phdrs.push_back(load); + } + } } - if (bss_.GetSize() != 0u) { - dynsym_.AddSymbol("oatbss", &bss_, 0, true, - bss_.GetSize(), STB_GLOBAL, STT_OBJECT); - dynsym_.AddSymbol("oatbsslastword", &bss_, bss_.GetSize() - 4, - true, 4, STB_GLOBAL, STT_OBJECT); + for (auto* section : sections_) { + const Elf_Shdr& shdr = section->header_; + if ((shdr.sh_flags & SHF_ALLOC) != 0 && shdr.sh_size != 0) { + // Other PT_* types allow the program to locate interesting + // parts of memory at runtime. They must overlap with PT_LOAD. + if (section->phdr_type_ != 0) { + Elf_Phdr phdr = Elf_Phdr(); + phdr.p_type = section->phdr_type_; + phdr.p_flags = section->phdr_flags_; + phdr.p_offset = shdr.sh_offset; + phdr.p_vaddr = phdr.p_paddr = shdr.sh_addr; + phdr.p_filesz = phdr.p_memsz = shdr.sh_size; + phdr.p_align = shdr.sh_addralign; + phdrs.push_back(phdr); + } + } } + // Set the size of the initial PT_PHDR. + CHECK_EQ(phdrs[0].p_type, (Elf_Word)PT_PHDR); + phdrs[0].p_filesz = phdrs[0].p_memsz = phdrs.size() * sizeof(Elf_Phdr); + + return phdrs; } InstructionSet isa_; - StrtabSection dynstr_; - SymtabSection dynsym_; - HashSection hash_; - OatSection rodata_; - OatSection text_; - NoBitsSection bss_; - DynamicSection dynamic_; - StrtabSection strtab_; - SymtabSection symtab_; - std::vector<Section*> other_sections_; - StrtabSection shstrtab_; + + OutputStream* output_; + bool output_good_; // True if all writes to output succeeded. + off_t output_offset_; // Keep track of the current position in the stream. + + Section rodata_; + Section text_; + Section bss_; + StringSection dynstr_; + SymbolSection dynsym_; + Section hash_; + Section dynamic_; + Section eh_frame_; + Section eh_frame_hdr_; + StringSection strtab_; + SymbolSection symtab_; + Section debug_frame_; + StringSection shstrtab_; + std::vector<std::unique_ptr<Section>> other_sections_; + + // List of used section in the order in which they were written. + std::vector<Section*> sections_; + + // Used for allocation of virtual address space. + Elf_Addr virtual_address_; DISALLOW_COPY_AND_ASSIGN(ElfBuilder); }; diff --git a/compiler/elf_writer_debug.cc b/compiler/elf_writer_debug.cc index 3a9e312225..90db7eb41a 100644 --- a/compiler/elf_writer_debug.cc +++ b/compiler/elf_writer_debug.cc @@ -24,16 +24,16 @@ #include "dex_file-inl.h" #include "dwarf/headers.h" #include "dwarf/register.h" +#include "elf_builder.h" #include "oat_writer.h" #include "utils.h" namespace art { namespace dwarf { -static void WriteDebugFrameCIE(InstructionSet isa, - ExceptionHeaderValueApplication addr_type, - CFIFormat format, - std::vector<uint8_t>* eh_frame) { +static void WriteCIE(InstructionSet isa, + CFIFormat format, + std::vector<uint8_t>* buffer) { // Scratch registers should be marked as undefined. This tells the // debugger that its value in the previous frame is not recoverable. bool is64bit = Is64BitInstructionSet(isa); @@ -59,8 +59,7 @@ static void WriteDebugFrameCIE(InstructionSet isa, } } auto return_reg = Reg::ArmCore(14); // R14(LR). - WriteDebugFrameCIE(is64bit, addr_type, return_reg, - opcodes, format, eh_frame); + WriteCIE(is64bit, return_reg, opcodes, format, buffer); return; } case kArm64: { @@ -83,8 +82,7 @@ static void WriteDebugFrameCIE(InstructionSet isa, } } auto return_reg = Reg::Arm64Core(30); // R30(LR). - WriteDebugFrameCIE(is64bit, addr_type, return_reg, - opcodes, format, eh_frame); + WriteCIE(is64bit, return_reg, opcodes, format, buffer); return; } case kMips: @@ -100,8 +98,7 @@ static void WriteDebugFrameCIE(InstructionSet isa, } } auto return_reg = Reg::MipsCore(31); // R31(RA). - WriteDebugFrameCIE(is64bit, addr_type, return_reg, - opcodes, format, eh_frame); + WriteCIE(is64bit, return_reg, opcodes, format, buffer); return; } case kX86: { @@ -127,8 +124,7 @@ static void WriteDebugFrameCIE(InstructionSet isa, } } auto return_reg = Reg::X86Core(8); // R8(EIP). - WriteDebugFrameCIE(is64bit, addr_type, return_reg, - opcodes, format, eh_frame); + WriteCIE(is64bit, return_reg, opcodes, format, buffer); return; } case kX86_64: { @@ -154,8 +150,7 @@ static void WriteDebugFrameCIE(InstructionSet isa, } } auto return_reg = Reg::X86_64Core(16); // R16(RIP). - WriteDebugFrameCIE(is64bit, addr_type, return_reg, - opcodes, format, eh_frame); + WriteCIE(is64bit, return_reg, opcodes, format, buffer); return; } case kNone: @@ -165,36 +160,69 @@ static void WriteDebugFrameCIE(InstructionSet isa, UNREACHABLE(); } -void WriteCFISection(const CompilerDriver* compiler, - const OatWriter* oat_writer, - ExceptionHeaderValueApplication address_type, - CFIFormat format, - std::vector<uint8_t>* debug_frame, - std::vector<uintptr_t>* debug_frame_patches, - std::vector<uint8_t>* eh_frame_hdr, - std::vector<uintptr_t>* eh_frame_hdr_patches) { - const auto& method_infos = oat_writer->GetMethodDebugInfo(); - const InstructionSet isa = compiler->GetInstructionSet(); +template<typename ElfTypes> +void WriteCFISection(ElfBuilder<ElfTypes>* builder, + const std::vector<OatWriter::DebugInfo>& method_infos, + CFIFormat format) { + CHECK(format == dwarf::DW_DEBUG_FRAME_FORMAT || + format == dwarf::DW_EH_FRAME_FORMAT); + typedef typename ElfTypes::Addr Elf_Addr; + + std::vector<uint32_t> binary_search_table; + std::vector<uintptr_t> patch_locations; + if (format == DW_EH_FRAME_FORMAT) { + binary_search_table.reserve(2 * method_infos.size()); + } else { + patch_locations.reserve(method_infos.size()); + } // Write .eh_frame/.debug_frame section. - std::map<uint32_t, size_t> address_to_fde_offset_map; - size_t cie_offset = debug_frame->size(); - WriteDebugFrameCIE(isa, address_type, format, debug_frame); - for (const OatWriter::DebugInfo& mi : method_infos) { - if (!mi.deduped_) { // Only one FDE per unique address. - ArrayRef<const uint8_t> opcodes = mi.compiled_method_->GetCFIInfo(); - if (!opcodes.empty()) { - address_to_fde_offset_map.emplace(mi.low_pc_, debug_frame->size()); - WriteDebugFrameFDE(Is64BitInstructionSet(isa), cie_offset, - mi.low_pc_, mi.high_pc_ - mi.low_pc_, - opcodes, format, debug_frame, debug_frame_patches); + auto* cfi_section = (format == dwarf::DW_DEBUG_FRAME_FORMAT + ? builder->GetDebugFrame() + : builder->GetEhFrame()); + { + cfi_section->Start(); + const bool is64bit = Is64BitInstructionSet(builder->GetIsa()); + const Elf_Addr text_address = builder->GetText()->GetAddress(); + const Elf_Addr cfi_address = cfi_section->GetAddress(); + const Elf_Addr cie_address = cfi_address; + Elf_Addr buffer_address = cfi_address; + std::vector<uint8_t> buffer; // Small temporary buffer. + WriteCIE(builder->GetIsa(), format, &buffer); + cfi_section->WriteFully(buffer.data(), buffer.size()); + buffer_address += buffer.size(); + buffer.clear(); + for (const OatWriter::DebugInfo& mi : method_infos) { + if (!mi.deduped_) { // Only one FDE per unique address. + ArrayRef<const uint8_t> opcodes = mi.compiled_method_->GetCFIInfo(); + if (!opcodes.empty()) { + const Elf_Addr code_address = text_address + mi.low_pc_; + if (format == DW_EH_FRAME_FORMAT) { + binary_search_table.push_back( + dchecked_integral_cast<uint32_t>(code_address)); + binary_search_table.push_back( + dchecked_integral_cast<uint32_t>(buffer_address)); + } + WriteFDE(is64bit, cfi_address, cie_address, + code_address, mi.high_pc_ - mi.low_pc_, + opcodes, format, buffer_address, &buffer, + &patch_locations); + cfi_section->WriteFully(buffer.data(), buffer.size()); + buffer_address += buffer.size(); + buffer.clear(); + } } } + cfi_section->End(); } if (format == DW_EH_FRAME_FORMAT) { + auto* header_section = builder->GetEhFrameHdr(); + header_section->Start(); + uint32_t header_address = dchecked_integral_cast<int32_t>(header_section->GetAddress()); // Write .eh_frame_hdr section. - Writer<> header(eh_frame_hdr); + std::vector<uint8_t> buffer; + Writer<> header(&buffer); header.PushUint8(1); // Version. // Encoding of .eh_frame pointer - libunwind does not honor datarel here, // so we have to use pcrel which means relative to the pointer's location. @@ -204,47 +232,29 @@ void WriteCFISection(const CompilerDriver* compiler, // Encoding of binary search table addresses - libunwind supports only this // specific combination, which means relative to the start of .eh_frame_hdr. header.PushUint8(DW_EH_PE_datarel | DW_EH_PE_sdata4); - // .eh_frame pointer - .eh_frame_hdr section is after .eh_frame section - const int32_t relative_eh_frame_begin = -static_cast<int32_t>(debug_frame->size()); - header.PushInt32(relative_eh_frame_begin - 4U); + // .eh_frame pointer + header.PushInt32(cfi_section->GetAddress() - (header_address + 4u)); // Binary search table size (number of entries). - header.PushUint32(dchecked_integral_cast<uint32_t>(address_to_fde_offset_map.size())); + header.PushUint32(dchecked_integral_cast<uint32_t>(binary_search_table.size()/2)); + header_section->WriteFully(buffer.data(), buffer.size()); // Binary search table. - for (const auto& address_to_fde_offset : address_to_fde_offset_map) { - u_int32_t code_address = address_to_fde_offset.first; - int32_t fde_address = dchecked_integral_cast<int32_t>(address_to_fde_offset.second); - eh_frame_hdr_patches->push_back(header.data()->size()); - header.PushUint32(code_address); - // We know the exact layout (eh_frame is immediately before eh_frame_hdr) - // and the data is relative to the start of the eh_frame_hdr, - // so patching isn't necessary (in contrast to the code address above). - header.PushInt32(relative_eh_frame_begin + fde_address); + for (size_t i = 0; i < binary_search_table.size(); i++) { + // Make addresses section-relative since we know the header address now. + binary_search_table[i] -= header_address; } + header_section->WriteFully(binary_search_table.data(), binary_search_table.size()); + header_section->End(); + } else { + builder->WritePatches(".debug_frame.oat_patches", &patch_locations); } } -/* - * @brief Generate the DWARF sections. - * @param oat_writer The Oat file Writer. - * @param eh_frame Call Frame Information. - * @param debug_info Compilation unit information. - * @param debug_info_patches Address locations to be patched. - * @param debug_abbrev Abbreviations used to generate dbg_info. - * @param debug_str Debug strings. - * @param debug_line Line number table. - * @param debug_line_patches Address locations to be patched. - */ -void WriteDebugSections(const CompilerDriver* compiler, - const OatWriter* oat_writer, - std::vector<uint8_t>* debug_info, - std::vector<uintptr_t>* debug_info_patches, - std::vector<uint8_t>* debug_abbrev, - std::vector<uint8_t>* debug_str, - std::vector<uint8_t>* debug_line, - std::vector<uintptr_t>* debug_line_patches) { - const std::vector<OatWriter::DebugInfo>& method_infos = oat_writer->GetMethodDebugInfo(); - const InstructionSet isa = compiler->GetInstructionSet(); - const bool is64bit = Is64BitInstructionSet(isa); +template<typename ElfTypes> +void WriteDebugSections(ElfBuilder<ElfTypes>* builder, + const std::vector<OatWriter::DebugInfo>& method_infos) { + typedef typename ElfTypes::Addr Elf_Addr; + const bool is64bit = Is64BitInstructionSet(builder->GetIsa()); + Elf_Addr text_address = builder->GetText()->GetAddress(); // Find all addresses (low_pc) which contain deduped methods. // The first instance of method is not marked deduped_, but the rest is. @@ -273,6 +283,12 @@ void WriteDebugSections(const CompilerDriver* compiler, } // Write .debug_info section. + std::vector<uint8_t> debug_info; + std::vector<uintptr_t> debug_info_patches; + std::vector<uint8_t> debug_abbrev; + std::vector<uint8_t> debug_str; + std::vector<uint8_t> debug_line; + std::vector<uintptr_t> debug_line_patches; for (const auto& compilation_unit : compilation_units) { uint32_t cunit_low_pc = 0xFFFFFFFFU; uint32_t cunit_high_pc = 0; @@ -281,14 +297,14 @@ void WriteDebugSections(const CompilerDriver* compiler, cunit_high_pc = std::max(cunit_high_pc, method_info->high_pc_); } - size_t debug_abbrev_offset = debug_abbrev->size(); - DebugInfoEntryWriter<> info(is64bit, debug_abbrev); + size_t debug_abbrev_offset = debug_abbrev.size(); + DebugInfoEntryWriter<> info(is64bit, &debug_abbrev); info.StartTag(DW_TAG_compile_unit, DW_CHILDREN_yes); - info.WriteStrp(DW_AT_producer, "Android dex2oat", debug_str); + info.WriteStrp(DW_AT_producer, "Android dex2oat", &debug_str); info.WriteData1(DW_AT_language, DW_LANG_Java); - info.WriteAddr(DW_AT_low_pc, cunit_low_pc); - info.WriteAddr(DW_AT_high_pc, cunit_high_pc); - info.WriteData4(DW_AT_stmt_list, debug_line->size()); + info.WriteAddr(DW_AT_low_pc, text_address + cunit_low_pc); + info.WriteAddr(DW_AT_high_pc, text_address + cunit_high_pc); + info.WriteData4(DW_AT_stmt_list, debug_line.size()); for (auto method_info : compilation_unit) { std::string method_name = PrettyMethod(method_info->dex_method_index_, *method_info->dex_file_, true); @@ -296,13 +312,13 @@ void WriteDebugSections(const CompilerDriver* compiler, method_name += " [DEDUPED]"; } info.StartTag(DW_TAG_subprogram, DW_CHILDREN_no); - info.WriteStrp(DW_AT_name, method_name.data(), debug_str); - info.WriteAddr(DW_AT_low_pc, method_info->low_pc_); - info.WriteAddr(DW_AT_high_pc, method_info->high_pc_); + info.WriteStrp(DW_AT_name, method_name.data(), &debug_str); + info.WriteAddr(DW_AT_low_pc, text_address + method_info->low_pc_); + info.WriteAddr(DW_AT_high_pc, text_address + method_info->high_pc_); info.EndTag(); // DW_TAG_subprogram } info.EndTag(); // DW_TAG_compile_unit - WriteDebugInfoCU(debug_abbrev_offset, info, debug_info, debug_info_patches); + WriteDebugInfoCU(debug_abbrev_offset, info, &debug_info, &debug_info_patches); // Write .debug_line section. std::vector<FileEntry> files; @@ -311,7 +327,7 @@ void WriteDebugSections(const CompilerDriver* compiler, std::unordered_map<std::string, size_t> directories_map; int code_factor_bits_ = 0; int dwarf_isa = -1; - switch (isa) { + switch (builder->GetIsa()) { case kArm: // arm actually means thumb2. case kThumb2: code_factor_bits_ = 1; // 16-bit instuctions @@ -328,7 +344,7 @@ void WriteDebugSections(const CompilerDriver* compiler, break; } DebugLineOpCodeWriter<> opcodes(is64bit, code_factor_bits_); - opcodes.SetAddress(cunit_low_pc); + opcodes.SetAddress(text_address + cunit_low_pc); if (dwarf_isa != -1) { opcodes.SetISA(dwarf_isa); } @@ -342,6 +358,8 @@ void WriteDebugSections(const CompilerDriver* compiler, DefaultSrcMap dex2line_; } debug_info_callbacks; + Elf_Addr method_address = text_address + mi->low_pc_; + const DexFile* dex = mi->dex_file_; if (mi->code_item_ != nullptr) { dex->DecodeDebugInfo(mi->code_item_, @@ -414,26 +432,48 @@ void WriteDebugSections(const CompilerDriver* compiler, int first_line = dex2line_map.front().to_; // Prologue is not a sensible place for a breakpoint. opcodes.NegateStmt(); - opcodes.AddRow(mi->low_pc_, first_line); + opcodes.AddRow(method_address, first_line); opcodes.NegateStmt(); opcodes.SetPrologueEnd(); } - opcodes.AddRow(mi->low_pc_ + pc, line); + opcodes.AddRow(method_address + pc, line); } else if (line != opcodes.CurrentLine()) { - opcodes.AddRow(mi->low_pc_ + pc, line); + opcodes.AddRow(method_address + pc, line); } } } } else { // line 0 - instruction cannot be attributed to any source line. - opcodes.AddRow(mi->low_pc_, 0); + opcodes.AddRow(method_address, 0); } } - opcodes.AdvancePC(cunit_high_pc); + opcodes.AdvancePC(text_address + cunit_high_pc); opcodes.EndSequence(); - WriteDebugLineTable(directories, files, opcodes, debug_line, debug_line_patches); + WriteDebugLineTable(directories, files, opcodes, &debug_line, &debug_line_patches); } + builder->WriteSection(".debug_info", &debug_info); + builder->WritePatches(".debug_info.oat_patches", &debug_info_patches); + builder->WriteSection(".debug_abbrev", &debug_abbrev); + builder->WriteSection(".debug_str", &debug_str); + builder->WriteSection(".debug_line", &debug_line); + builder->WritePatches(".debug_line.oat_patches", &debug_line_patches); } +// Explicit instantiations +template void WriteCFISection<ElfTypes32>( + ElfBuilder<ElfTypes32>* builder, + const std::vector<OatWriter::DebugInfo>& method_infos, + CFIFormat format); +template void WriteCFISection<ElfTypes64>( + ElfBuilder<ElfTypes64>* builder, + const std::vector<OatWriter::DebugInfo>& method_infos, + CFIFormat format); +template void WriteDebugSections<ElfTypes32>( + ElfBuilder<ElfTypes32>* builder, + const std::vector<OatWriter::DebugInfo>& method_infos); +template void WriteDebugSections<ElfTypes64>( + ElfBuilder<ElfTypes64>* builder, + const std::vector<OatWriter::DebugInfo>& method_infos); + } // namespace dwarf } // namespace art diff --git a/compiler/elf_writer_debug.h b/compiler/elf_writer_debug.h index 69f7e0d811..e58fd0a390 100644 --- a/compiler/elf_writer_debug.h +++ b/compiler/elf_writer_debug.h @@ -19,29 +19,21 @@ #include <vector> +#include "elf_builder.h" #include "dwarf/dwarf_constants.h" #include "oat_writer.h" namespace art { namespace dwarf { -void WriteCFISection(const CompilerDriver* compiler, - const OatWriter* oat_writer, - ExceptionHeaderValueApplication address_type, - CFIFormat format, - std::vector<uint8_t>* debug_frame, - std::vector<uintptr_t>* debug_frame_patches, - std::vector<uint8_t>* eh_frame_hdr, - std::vector<uintptr_t>* eh_frame_hdr_patches); - -void WriteDebugSections(const CompilerDriver* compiler, - const OatWriter* oat_writer, - std::vector<uint8_t>* debug_info, - std::vector<uintptr_t>* debug_info_patches, - std::vector<uint8_t>* debug_abbrev, - std::vector<uint8_t>* debug_str, - std::vector<uint8_t>* debug_line, - std::vector<uintptr_t>* debug_line_patches); +template<typename ElfTypes> +void WriteCFISection(ElfBuilder<ElfTypes>* builder, + const std::vector<OatWriter::DebugInfo>& method_infos, + CFIFormat format); + +template<typename ElfTypes> +void WriteDebugSections(ElfBuilder<ElfTypes>* builder, + const std::vector<OatWriter::DebugInfo>& method_infos); } // namespace dwarf } // namespace art diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc index dce1e861b4..5c059e1e82 100644 --- a/compiler/elf_writer_quick.cc +++ b/compiler/elf_writer_quick.cc @@ -70,190 +70,78 @@ bool ElfWriterQuick<ElfTypes>::Create(File* elf_file, template <typename ElfTypes> static void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder, OatWriter* oat_writer); -// Encode patch locations as LEB128 list of deltas between consecutive addresses. template <typename ElfTypes> -void ElfWriterQuick<ElfTypes>::EncodeOatPatches(const std::vector<uintptr_t>& locations, - std::vector<uint8_t>* buffer) { - buffer->reserve(buffer->size() + locations.size() * 2); // guess 2 bytes per ULEB128. - uintptr_t address = 0; // relative to start of section. - for (uintptr_t location : locations) { - DCHECK_GE(location, address) << "Patch locations are not in sorted order"; - EncodeUnsignedLeb128(buffer, dchecked_integral_cast<uint32_t>(location - address)); - address = location; - } -} - -class RodataWriter FINAL : public CodeOutput { - public: - explicit RodataWriter(OatWriter* oat_writer) : oat_writer_(oat_writer) {} - - bool Write(OutputStream* out) OVERRIDE { - return oat_writer_->WriteRodata(out); - } +bool ElfWriterQuick<ElfTypes>::Write( + OatWriter* oat_writer, + const std::vector<const DexFile*>& dex_files_unused ATTRIBUTE_UNUSED, + const std::string& android_root_unused ATTRIBUTE_UNUSED, + bool is_host_unused ATTRIBUTE_UNUSED) { + const InstructionSet isa = compiler_driver_->GetInstructionSet(); + std::unique_ptr<BufferedOutputStream> output_stream( + new BufferedOutputStream(new FileOutputStream(elf_file_))); + std::unique_ptr<ElfBuilder<ElfTypes>> builder( + new ElfBuilder<ElfTypes>(isa, output_stream.get())); - private: - OatWriter* oat_writer_; -}; + builder->Start(); -class TextWriter FINAL : public CodeOutput { - public: - explicit TextWriter(OatWriter* oat_writer) : oat_writer_(oat_writer) {} + auto* rodata = builder->GetRoData(); + auto* text = builder->GetText(); + auto* bss = builder->GetBss(); - bool Write(OutputStream* out) OVERRIDE { - return oat_writer_->WriteCode(out); + rodata->Start(); + if (!oat_writer->WriteRodata(rodata)) { + return false; } + rodata->End(); - private: - OatWriter* oat_writer_; -}; - -enum PatchResult { - kAbsoluteAddress, // Absolute memory location. - kPointerRelativeAddress, // Offset relative to the location of the pointer. - kSectionRelativeAddress, // Offset relative to start of containing section. -}; - -// Patch memory addresses within a buffer. -// It assumes that the unpatched addresses are offsets relative to base_address. -// (which generally means method's low_pc relative to the start of .text) -template <typename Elf_Addr, typename Address, PatchResult kPatchResult> -static void Patch(const std::vector<uintptr_t>& patch_locations, - Elf_Addr buffer_address, Elf_Addr base_address, - std::vector<uint8_t>* buffer) { - for (uintptr_t location : patch_locations) { - typedef __attribute__((__aligned__(1))) Address UnalignedAddress; - auto* to_patch = reinterpret_cast<UnalignedAddress*>(buffer->data() + location); - switch (kPatchResult) { - case kAbsoluteAddress: - *to_patch = (base_address + *to_patch); - break; - case kPointerRelativeAddress: - *to_patch = (base_address + *to_patch) - (buffer_address + location); - break; - case kSectionRelativeAddress: - *to_patch = (base_address + *to_patch) - buffer_address; - break; - } + text->Start(); + if (!oat_writer->WriteCode(text)) { + return false; } -} + text->End(); -template <typename ElfTypes> -bool ElfWriterQuick<ElfTypes>::Write( - OatWriter* oat_writer, - const std::vector<const DexFile*>& dex_files_unused ATTRIBUTE_UNUSED, - const std::string& android_root_unused ATTRIBUTE_UNUSED, - bool is_host_unused ATTRIBUTE_UNUSED) { - using Elf_Addr = typename ElfTypes::Addr; - const InstructionSet isa = compiler_driver_->GetInstructionSet(); + if (oat_writer->GetBssSize() != 0) { + bss->Start(); + bss->SetSize(oat_writer->GetBssSize()); + bss->End(); + } - // Setup the builder with the main OAT sections (.rodata .text .bss). - const size_t rodata_size = oat_writer->GetOatHeader().GetExecutableOffset(); - const size_t text_size = oat_writer->GetSize() - rodata_size; - const size_t bss_size = oat_writer->GetBssSize(); - RodataWriter rodata_writer(oat_writer); - TextWriter text_writer(oat_writer); - std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>( - isa, rodata_size, &rodata_writer, text_size, &text_writer, bss_size)); + builder->WriteDynamicSection(elf_file_->GetPath()); - // Add debug sections. - // They are allocated here (in the same scope as the builder), - // but they are registered with the builder only if they are used. - using RawSection = typename ElfBuilder<ElfTypes>::RawSection; - const auto* text = builder->GetText(); - const bool is64bit = Is64BitInstructionSet(isa); - const int pointer_size = GetInstructionSetPointerSize(isa); - std::unique_ptr<RawSection> eh_frame(new RawSection( - ".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0, - is64bit ? Patch<Elf_Addr, uint64_t, kPointerRelativeAddress> : - Patch<Elf_Addr, uint32_t, kPointerRelativeAddress>, - text)); - std::unique_ptr<RawSection> eh_frame_hdr(new RawSection( - ".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0, - Patch<Elf_Addr, uint32_t, kSectionRelativeAddress>, text)); - std::unique_ptr<RawSection> debug_frame(new RawSection( - ".debug_frame", SHT_PROGBITS, 0, nullptr, 0, pointer_size, 0, - is64bit ? Patch<Elf_Addr, uint64_t, kAbsoluteAddress> : - Patch<Elf_Addr, uint32_t, kAbsoluteAddress>, - text)); - std::unique_ptr<RawSection> debug_frame_oat_patches(new RawSection( - ".debug_frame.oat_patches", SHT_OAT_PATCH)); - std::unique_ptr<RawSection> debug_info(new RawSection( - ".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0, - Patch<Elf_Addr, uint32_t, kAbsoluteAddress>, text)); - std::unique_ptr<RawSection> debug_info_oat_patches(new RawSection( - ".debug_info.oat_patches", SHT_OAT_PATCH)); - std::unique_ptr<RawSection> debug_abbrev(new RawSection( - ".debug_abbrev", SHT_PROGBITS)); - std::unique_ptr<RawSection> debug_str(new RawSection( - ".debug_str", SHT_PROGBITS)); - std::unique_ptr<RawSection> debug_line(new RawSection( - ".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0, - Patch<Elf_Addr, uint32_t, kAbsoluteAddress>, text)); - std::unique_ptr<RawSection> debug_line_oat_patches(new RawSection( - ".debug_line.oat_patches", SHT_OAT_PATCH)); - if (!oat_writer->GetMethodDebugInfo().empty()) { - if (compiler_driver_->GetCompilerOptions().GetGenerateDebugInfo()) { - // Generate CFI (stack unwinding information). - if (kCFIFormat == dwarf::DW_EH_FRAME_FORMAT) { - dwarf::WriteCFISection( - compiler_driver_, oat_writer, - dwarf::DW_EH_PE_pcrel, kCFIFormat, - eh_frame->GetBuffer(), eh_frame->GetPatchLocations(), - eh_frame_hdr->GetBuffer(), eh_frame_hdr->GetPatchLocations()); - builder->RegisterSection(eh_frame.get()); - builder->RegisterSection(eh_frame_hdr.get()); - } else { - DCHECK(kCFIFormat == dwarf::DW_DEBUG_FRAME_FORMAT); - dwarf::WriteCFISection( - compiler_driver_, oat_writer, - dwarf::DW_EH_PE_absptr, kCFIFormat, - debug_frame->GetBuffer(), debug_frame->GetPatchLocations(), - nullptr, nullptr); - builder->RegisterSection(debug_frame.get()); - EncodeOatPatches(*debug_frame->GetPatchLocations(), - debug_frame_oat_patches->GetBuffer()); - builder->RegisterSection(debug_frame_oat_patches.get()); - } + if (compiler_driver_->GetCompilerOptions().GetGenerateDebugInfo()) { + const auto& method_infos = oat_writer->GetMethodDebugInfo(); + if (!method_infos.empty()) { // Add methods to .symtab. WriteDebugSymbols(builder.get(), oat_writer); - // Generate DWARF .debug_* sections. - dwarf::WriteDebugSections( - compiler_driver_, oat_writer, - debug_info->GetBuffer(), debug_info->GetPatchLocations(), - debug_abbrev->GetBuffer(), - debug_str->GetBuffer(), - debug_line->GetBuffer(), debug_line->GetPatchLocations()); - builder->RegisterSection(debug_info.get()); - EncodeOatPatches(*debug_info->GetPatchLocations(), - debug_info_oat_patches->GetBuffer()); - builder->RegisterSection(debug_info_oat_patches.get()); - builder->RegisterSection(debug_abbrev.get()); - builder->RegisterSection(debug_str.get()); - builder->RegisterSection(debug_line.get()); - EncodeOatPatches(*debug_line->GetPatchLocations(), - debug_line_oat_patches->GetBuffer()); - builder->RegisterSection(debug_line_oat_patches.get()); + // Generate CFI (stack unwinding information). + dwarf::WriteCFISection(builder.get(), method_infos, kCFIFormat); + // Write DWARF .debug_* sections. + dwarf::WriteDebugSections(builder.get(), method_infos); } } // Add relocation section for .text. - std::unique_ptr<RawSection> text_oat_patches(new RawSection( - ".text.oat_patches", SHT_OAT_PATCH)); if (compiler_driver_->GetCompilerOptions().GetIncludePatchInformation()) { // Note that ElfWriter::Fixup will be called regardless and therefore // we need to include oat_patches for debug sections unconditionally. - EncodeOatPatches(oat_writer->GetAbsolutePatchLocations(), - text_oat_patches->GetBuffer()); - builder->RegisterSection(text_oat_patches.get()); + builder->WritePatches(".text.oat_patches", &oat_writer->GetAbsolutePatchLocations()); } - return builder->Write(elf_file_); + builder->End(); + + return builder->Good() && output_stream->Flush(); } template <typename ElfTypes> static void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder, OatWriter* oat_writer) { const std::vector<OatWriter::DebugInfo>& method_info = oat_writer->GetMethodDebugInfo(); bool generated_mapping_symbol = false; + auto* strtab = builder->GetStrTab(); + auto* symtab = builder->GetSymTab(); + + if (method_info.empty()) { + return; + } // Find all addresses (low_pc) which contain deduped methods. // The first instance of method is not marked deduped_, but the rest is. @@ -264,7 +152,8 @@ static void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder, OatWriter* oat_writ } } - auto* symtab = builder->GetSymtab(); + strtab->Start(); + strtab->Write(""); // strtab should start with empty string. for (auto it = method_info.begin(); it != method_info.end(); ++it) { if (it->deduped_) { continue; // Add symbol only for the first instance. @@ -277,8 +166,8 @@ static void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder, OatWriter* oat_writ uint32_t low_pc = it->low_pc_; // Add in code delta, e.g., thumb bit 0 for Thumb2 code. low_pc += it->compiled_method_->CodeDelta(); - symtab->AddSymbol(name, builder->GetText(), low_pc, - true, it->high_pc_ - it->low_pc_, STB_GLOBAL, STT_FUNC); + symtab->Add(strtab->Write(name), builder->GetText(), low_pc, + true, it->high_pc_ - it->low_pc_, STB_GLOBAL, STT_FUNC); // Conforming to aaelf, add $t mapping symbol to indicate start of a sequence of thumb2 // instructions, so that disassembler tools can correctly disassemble. @@ -286,12 +175,19 @@ static void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder, OatWriter* oat_writ // requires it to match function symbol. Just address 0 does not work. if (it->compiled_method_->GetInstructionSet() == kThumb2) { if (!generated_mapping_symbol || !kGenerateSingleArmMappingSymbol) { - symtab->AddSymbol("$t", builder->GetText(), it->low_pc_ & ~1, true, - 0, STB_LOCAL, STT_NOTYPE); + symtab->Add(strtab->Write("$t"), builder->GetText(), it->low_pc_ & ~1, + true, 0, STB_LOCAL, STT_NOTYPE); generated_mapping_symbol = true; } } } + strtab->End(); + + // Symbols are buffered and written after names (because they are smaller). + // We could also do two passes in this function to avoid the buffering. + symtab->Start(); + symtab->Write(); + symtab->End(); } // Explicit instantiations diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc index ccf34b816b..b413a9eb7b 100644 --- a/compiler/elf_writer_test.cc +++ b/compiler/elf_writer_test.cc @@ -21,6 +21,7 @@ #include "common_compiler_test.h" #include "elf_file.h" #include "elf_file_impl.h" +#include "elf_builder.h" #include "elf_writer_quick.h" #include "oat.h" #include "utils.h" @@ -100,7 +101,7 @@ TEST_F(ElfWriterTest, EncodeDecodeOatPatches) { // Encode patch locations. std::vector<uint8_t> oat_patches; - ElfWriterQuick32::EncodeOatPatches(patch_locations, &oat_patches); + ElfBuilder<ElfTypes32>::EncodeOatPatches(patch_locations, &oat_patches); // Create buffer to be patched. std::vector<uint8_t> initial_data(256); diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index ea61b43627..a163380b25 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -98,33 +98,6 @@ const DexFile* OpenDexFile(const OatFile::OatDexFile* oat_dex_file, std::string* class OatSymbolizer FINAL { public: - class RodataWriter FINAL : public CodeOutput { - public: - explicit RodataWriter(const OatFile* oat_file) : oat_file_(oat_file) {} - - bool Write(OutputStream* out) OVERRIDE { - const size_t rodata_size = oat_file_->GetOatHeader().GetExecutableOffset(); - return out->WriteFully(oat_file_->Begin(), rodata_size); - } - - private: - const OatFile* oat_file_; - }; - - class TextWriter FINAL : public CodeOutput { - public: - explicit TextWriter(const OatFile* oat_file) : oat_file_(oat_file) {} - - bool Write(OutputStream* out) OVERRIDE { - const size_t rodata_size = oat_file_->GetOatHeader().GetExecutableOffset(); - const uint8_t* text_begin = oat_file_->Begin() + rodata_size; - return out->WriteFully(text_begin, oat_file_->End() - text_begin); - } - - private: - const OatFile* oat_file_; - }; - OatSymbolizer(const OatFile* oat_file, const std::string& output_name) : oat_file_(oat_file), builder_(nullptr), output_name_(output_name.empty() ? "symbolized.oat" : output_name) { @@ -139,31 +112,57 @@ class OatSymbolizer FINAL { uint32_t); bool Symbolize() { - Elf32_Word rodata_size = oat_file_->GetOatHeader().GetExecutableOffset(); - uint32_t size = static_cast<uint32_t>(oat_file_->End() - oat_file_->Begin()); - uint32_t text_size = size - rodata_size; - uint32_t bss_size = oat_file_->BssSize(); - RodataWriter rodata_writer(oat_file_); - TextWriter text_writer(oat_file_); - builder_.reset(new ElfBuilder<ElfTypes32>( - oat_file_->GetOatHeader().GetInstructionSet(), - rodata_size, &rodata_writer, - text_size, &text_writer, - bss_size)); + const InstructionSet isa = oat_file_->GetOatHeader().GetInstructionSet(); + + File* elf_file = OS::CreateEmptyFile(output_name_.c_str()); + std::unique_ptr<BufferedOutputStream> output_stream( + new BufferedOutputStream(new FileOutputStream(elf_file))); + builder_.reset(new ElfBuilder<ElfTypes32>(isa, output_stream.get())); + + builder_->Start(); + + auto* rodata = builder_->GetRoData(); + auto* text = builder_->GetText(); + auto* bss = builder_->GetBss(); + auto* strtab = builder_->GetStrTab(); + auto* symtab = builder_->GetSymTab(); + + rodata->Start(); + const uint8_t* rodata_begin = oat_file_->Begin(); + const size_t rodata_size = oat_file_->GetOatHeader().GetExecutableOffset(); + rodata->WriteFully(rodata_begin, rodata_size); + rodata->End(); + + text->Start(); + const uint8_t* text_begin = oat_file_->Begin() + rodata_size; + const size_t text_size = oat_file_->End() - text_begin; + text->WriteFully(text_begin, text_size); + text->End(); + + if (oat_file_->BssSize() != 0) { + bss->Start(); + bss->SetSize(oat_file_->BssSize()); + bss->End(); + } + + builder_->WriteDynamicSection(elf_file->GetPath()); Walk(&art::OatSymbolizer::RegisterForDedup); NormalizeState(); + strtab->Start(); + strtab->Write(""); // strtab should start with empty string. Walk(&art::OatSymbolizer::AddSymbol); + strtab->End(); - File* elf_output = OS::CreateEmptyFile(output_name_.c_str()); - bool result = builder_->Write(elf_output); + symtab->Start(); + symtab->Write(); + symtab->End(); - // Ignore I/O errors. - UNUSED(elf_output->FlushClose()); + builder_->End(); - return result; + return builder_->Good() && output_stream->Flush(); } void Walk(Callback callback) { @@ -295,9 +294,8 @@ class OatSymbolizer FINAL { pretty_name = "[Dedup]" + pretty_name; } - auto* symtab = builder_->GetSymtab(); - - symtab->AddSymbol(pretty_name, builder_->GetText(), + int name_offset = builder_->GetStrTab()->Write(pretty_name); + builder_->GetSymTab()->Add(name_offset, builder_->GetText(), oat_method.GetCodeOffset() - oat_file_->GetOatHeader().GetExecutableOffset(), true, oat_method.GetQuickCodeSize(), STB_GLOBAL, STT_FUNC); } |