Merge "ART: Fix critical edge splitting under try/catch"
diff --git a/compiler/buffered_output_stream.cc b/compiler/buffered_output_stream.cc
index 0940a80..3ca518b 100644
--- a/compiler/buffered_output_stream.cc
+++ b/compiler/buffered_output_stream.cc
@@ -25,12 +25,13 @@
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 15fc033..b447f41 100644
--- a/compiler/buffered_output_stream.h
+++ b/compiler/buffered_output_stream.h
@@ -36,11 +36,11 @@
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 6fd4575..508b04a 100644
--- a/compiler/cfi_test.h
+++ b/compiler/cfi_test.h
@@ -48,11 +48,11 @@
// 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 3ba380e..a412a99 100644
--- a/compiler/dwarf/dwarf_test.cc
+++ b/compiler/dwarf/dwarf_test.cc
@@ -122,12 +122,12 @@
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, 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 @@
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 f819c49..5464ed9 100644
--- a/compiler/dwarf/dwarf_test.h
+++ b/compiler/dwarf/dwarf_test.h
@@ -59,38 +59,27 @@
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 f3fba4b..883d756 100644
--- a/compiler/dwarf/headers.h
+++ b/compiler/dwarf/headers.h
@@ -38,15 +38,14 @@
// 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 @@
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 @@
// 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 (is64bit) {
- writer.PushUint64(initial_address);
- writer.PushUint64(address_range);
+ if (format == DW_EH_FRAME_FORMAT) {
+ // .eh_frame encodes the location as relative address.
+ code_address -= buffer_address + buffer->size();
} else {
- writer.PushUint32(initial_address);
- writer.PushUint32(address_range);
+ 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(code_address);
+ writer.PushUint64(code_size);
+ } else {
+ 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 e977798..895dfcc 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 @@
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;
+ virtual ~Section() {
+ if (started_) {
+ CHECK(finished_);
+ }
}
- const Elf_Shdr* GetHeader() const {
- return &header_;
+ // 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;
+ }
}
- Elf_Shdr* GetHeader() {
- return &header_;
+ // 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;
+ }
+ }
+
+ // Get the location of this section in virtual memory.
+ Elf_Addr GetAddress() const {
+ CHECK(started_);
+ return header_.sh_addr;
+ }
+
+ // Returns the size of the content of this section.
+ Elf_Word GetSize() const {
+ CHECK(finished_);
+ return header_.sh_size;
+ }
+
+ // 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;
+ }
+
+ // 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;
+ }
+
+ // 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);
}
Elf_Word GetSectionIndex() const {
+ DCHECK(started_);
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:
+ 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_;
+
+ friend class ElfBuilder;
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_}});
- }
- }
- 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();
- }
-
- 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;
- }
- return WriteArray(elf_file, buffer_.data(), buffer_.size());
- }
-
- bool IsEmpty() const {
- return buffer_.size() == 0;
- }
-
- std::vector<uint8_t>* GetBuffer() {
- return &buffer_;
- }
-
- void SetBuffer(const std::vector<uint8_t>& buffer) {
- buffer_ = buffer;
- }
-
- std::vector<uintptr_t>* GetPatchLocations() {
- return &patch_locations_;
- }
-
- 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_;
- }
-
- 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());
- }
-
- 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_;
- }
-
- bool Write(File* elf_file ATTRIBUTE_UNUSED) OVERRIDE {
- LOG(ERROR) << "This section should not be written to the ELF file";
- return false;
- }
-
- private:
- Elf_Word size_;
- };
-
// 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) {
+ 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)) {
+ }
+
+ // 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_Word name_idx = strtab_->AddName(name);
- symbols_.push_back({ name, section, addr, size, is_relative,
- MakeStInfo(binding, type), other, name_idx });
+ 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);
}
- 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);
- }
-
- 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;
-
- while (*name) {
- h = (h << 4) + *name++;
- g = h & 0xf0000000;
- h ^= g;
- h ^= g >> 24;
- }
- return h;
- }
-
- SymtabSection* symtab_;
-
- DISALLOW_COPY_AND_ASSIGN(HashSection);
- };
-
- ElfBuilder(InstructionSet isa,
- Elf_Word rodata_size, CodeOutput* rodata_writer,
- Elf_Word text_size, CodeOutput* text_writer,
- Elf_Word bss_size)
+ ElfBuilder(InstructionSet isa, OutputStream* output)
: 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) {
+ 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() {}
- OatSection* GetText() { return &text_; }
- SymtabSection* GetSymtab() { return &symtab_; }
+ 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_; }
- 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;
+ // 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;
+ }
+ }
- // 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 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));
+ }
- // 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());
+ 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));
+ }
- // 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);
+ 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;
+ }
- // 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;
- }
- 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));
+ 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();
}
}
- 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);
+ shstrtab_.End();
- // Create the main ELF header.
+ // 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_);
+ }
+ 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]));
+
+ // 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();
-
- // 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;
- }
- return true;
+ Seek(0, kSeekSet);
+ WriteFully(&elf_header, sizeof(elf_header));
+ WriteFully(phdrs.data(), phdrs.size() * sizeof(phdrs[0]));
}
- // Adds the given section to the builder. It does not take ownership.
- void RegisterSection(Section* section) {
- other_sections_.push_back(section);
+ // 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);
+ }
+
+ 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;
- }
- return true;
- }
-
- 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.
+ 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;
}
- // 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;
+ // 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);
+ }
+ output_offset_ = new_offset;
+ return new_offset;
}
static Elf_Ehdr MakeElfHeader(InstructionSet isa) {
@@ -869,6 +544,10 @@
}
case kNone: {
LOG(FATAL) << "No instruction set";
+ break;
+ }
+ default: {
+ LOG(FATAL) << "Unknown instruction set " << isa;
}
}
@@ -892,56 +571,110 @@
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);
+ // 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);
}
- // NB: We must add the name before adding DT_STRSZ.
- Elf_Word soname_offset = dynstr_.AddName(soname);
+ // 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);
+ }
+ }
+ }
+ 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);
- 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);
- }
- 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);
- }
+ 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 3a9e312..90db7eb 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 @@
}
}
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 @@
}
}
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 @@
}
}
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 @@
}
}
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 @@
}
}
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 @@
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 @@
// 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 @@
}
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 69f7e0d..e58fd0a 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);
+template<typename ElfTypes>
+void WriteCFISection(ElfBuilder<ElfTypes>* builder,
+ const std::vector<OatWriter::DebugInfo>& method_infos,
+ CFIFormat format);
-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 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 dce1e86..5c059e1 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -70,190 +70,78 @@
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);
- }
-
- private:
- OatWriter* oat_writer_;
-};
-
-class TextWriter FINAL : public CodeOutput {
- public:
- explicit TextWriter(OatWriter* oat_writer) : oat_writer_(oat_writer) {}
-
- bool Write(OutputStream* out) OVERRIDE {
- return oat_writer_->WriteCode(out);
- }
-
- 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;
- }
- }
-}
-
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();
+ 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()));
- // 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->Start();
- // 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());
- }
+ auto* rodata = builder->GetRoData();
+ auto* text = builder->GetText();
+ auto* bss = builder->GetBss();
+
+ rodata->Start();
+ if (!oat_writer->WriteRodata(rodata)) {
+ return false;
+ }
+ rodata->End();
+
+ text->Start();
+ if (!oat_writer->WriteCode(text)) {
+ return false;
+ }
+ text->End();
+
+ if (oat_writer->GetBssSize() != 0) {
+ bss->Start();
+ bss->SetSize(oat_writer->GetBssSize());
+ bss->End();
+ }
+
+ builder->WriteDynamicSection(elf_file_->GetPath());
+
+ 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 @@
}
}
- 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 @@
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 @@
// 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 ccf34b8..b413a9e 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 @@
// 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/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 676e564..167c35d 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -998,7 +998,9 @@
return false;
}
- if (invoke->IsInvokeStaticOrDirect()) {
+ if (invoke->IsInvokeStaticOrDirect() &&
+ HInvokeStaticOrDirect::NeedsCurrentMethodInput(
+ invoke->AsInvokeStaticOrDirect()->GetMethodLoadKind())) {
invoke->SetArgumentAt(*argument_index, graph_->GetCurrentMethod());
(*argument_index)++;
}
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index f77576d..0d7c796 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -257,10 +257,11 @@
}
size_t use_index = use_it.Current()->GetIndex();
if ((use_index >= use->InputCount()) || (use->InputAt(use_index) != instruction)) {
- AddError(StringPrintf("User %s:%d of instruction %d has a wrong "
+ AddError(StringPrintf("User %s:%d of instruction %s:%d has a wrong "
"UseListNode index.",
use->DebugName(),
use->GetId(),
+ instruction->DebugName(),
instruction->GetId()));
}
}
@@ -559,10 +560,14 @@
!use_it.Done(); use_it.Advance()) {
HInstruction* use = use_it.Current()->GetUser();
if (!use->IsPhi() && !instruction->StrictlyDominates(use)) {
- AddError(StringPrintf("Instruction %d in block %d does not dominate "
- "use %d in block %d.",
- instruction->GetId(), current_block_->GetBlockId(),
- use->GetId(), use->GetBlock()->GetBlockId()));
+ AddError(StringPrintf("Instruction %s:%d in block %d does not dominate "
+ "use %s:%d in block %d.",
+ instruction->DebugName(),
+ instruction->GetId(),
+ current_block_->GetBlockId(),
+ use->DebugName(),
+ use->GetId(),
+ use->GetBlock()->GetBlockId()));
}
}
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 3e137ff..7a8b463 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -1986,6 +1986,16 @@
return !opt.GetDoesNotNeedDexCache();
}
+void HInvokeStaticOrDirect::RemoveInputAt(size_t index) {
+ RemoveAsUserOfInput(index);
+ inputs_.erase(inputs_.begin() + index);
+ // Update indexes in use nodes of inputs that have been pulled forward by the erase().
+ for (size_t i = index, e = InputCount(); i < e; ++i) {
+ DCHECK_EQ(InputRecordAt(i).GetUseNode()->GetIndex(), i + 1u);
+ InputRecordAt(i).GetUseNode()->SetIndex(i);
+ }
+}
+
void HInstruction::RemoveEnvironmentUsers() {
for (HUseIterator<HEnvironment*> use_it(GetEnvUses()); !use_it.Done(); use_it.Advance()) {
HUseListNode<HEnvironment*>* user_node = use_it.Current();
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index ddd39a3..4e81248 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -3408,11 +3408,12 @@
ClinitCheckRequirement clinit_check_requirement)
: HInvoke(arena,
number_of_arguments,
- // There is one extra argument for the HCurrentMethod node, and
- // potentially one other if the clinit check is explicit, and one other
- // if the method is a string factory.
- 1u + (clinit_check_requirement == ClinitCheckRequirement::kExplicit ? 1u : 0u)
- + (dispatch_info.method_load_kind == MethodLoadKind::kStringInit ? 1u : 0u),
+ // There is potentially one extra argument for the HCurrentMethod node, and
+ // potentially one other if the clinit check is explicit, and potentially
+ // one other if the method is a string factory.
+ (NeedsCurrentMethodInput(dispatch_info.method_load_kind) ? 1u : 0u) +
+ (clinit_check_requirement == ClinitCheckRequirement::kExplicit ? 1u : 0u) +
+ (dispatch_info.method_load_kind == MethodLoadKind::kStringInit ? 1u : 0u),
return_type,
dex_pc,
method_index,
@@ -3420,12 +3421,25 @@
invoke_type_(invoke_type),
clinit_check_requirement_(clinit_check_requirement),
target_method_(target_method),
- dispatch_info_(dispatch_info) {}
+ dispatch_info_(dispatch_info) { }
void SetDispatchInfo(const DispatchInfo& dispatch_info) {
+ bool had_current_method_input = HasCurrentMethodInput();
+ bool needs_current_method_input = NeedsCurrentMethodInput(dispatch_info.method_load_kind);
+
+ // Using the current method is the default and once we find a better
+ // method load kind, we should not go back to using the current method.
+ DCHECK(had_current_method_input || !needs_current_method_input);
+
+ if (had_current_method_input && !needs_current_method_input) {
+ DCHECK_EQ(InputAt(GetCurrentMethodInputIndex()), GetBlock()->GetGraph()->GetCurrentMethod());
+ RemoveInputAt(GetCurrentMethodInputIndex());
+ }
dispatch_info_ = dispatch_info;
}
+ void RemoveInputAt(size_t index);
+
bool CanDoImplicitNullCheckOn(HInstruction* obj ATTRIBUTE_UNUSED) const OVERRIDE {
// We access the method via the dex cache so we can't do an implicit null check.
// TODO: for intrinsics we can generate implicit null checks.
@@ -3447,6 +3461,17 @@
bool HasPcRelDexCache() const {
return GetMethodLoadKind() == MethodLoadKind::kDexCachePcRelative;
}
+ bool HasCurrentMethodInput() const {
+ // This function can be called only after the invoke has been fully initialized by the builder.
+ if (NeedsCurrentMethodInput(GetMethodLoadKind())) {
+ DCHECK(InputAt(GetCurrentMethodInputIndex())->IsCurrentMethod());
+ return true;
+ } else {
+ DCHECK(InputCount() == GetCurrentMethodInputIndex() ||
+ !InputAt(GetCurrentMethodInputIndex())->IsCurrentMethod());
+ return false;
+ }
+ }
bool HasDirectCodePtr() const { return GetCodePtrLocation() == CodePtrLocation::kCallDirect; }
MethodReference GetTargetMethod() const { return target_method_; }
@@ -3495,8 +3520,8 @@
bool IsStringFactoryFor(HFakeString* str) const {
if (!IsStringInit()) return false;
- // +1 for the current method.
- if (InputCount() == (number_of_arguments_ + 1)) return false;
+ DCHECK(!HasCurrentMethodInput());
+ if (InputCount() == (number_of_arguments_)) return false;
return InputAt(InputCount() - 1)->AsFakeString() == str;
}
@@ -3522,6 +3547,11 @@
return IsStatic() && (clinit_check_requirement_ == ClinitCheckRequirement::kImplicit);
}
+ // Does this method load kind need the current method as an input?
+ static bool NeedsCurrentMethodInput(MethodLoadKind kind) {
+ return kind == MethodLoadKind::kRecursive || kind == MethodLoadKind::kDexCacheViaMethod;
+ }
+
DECLARE_INSTRUCTION(InvokeStaticOrDirect);
protected:
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index ea61b43..a163380 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -98,33 +98,6 @@
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 @@
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 @@
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);
}
diff --git a/test/441-checker-inliner/src/Main.java b/test/441-checker-inliner/src/Main.java
index 96302fb..6d6a4f2 100644
--- a/test/441-checker-inliner/src/Main.java
+++ b/test/441-checker-inliner/src/Main.java
@@ -19,7 +19,7 @@
/// CHECK-START: void Main.InlineVoid() inliner (before)
/// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
/// CHECK-DAG: InvokeStaticOrDirect
- /// CHECK-DAG: InvokeStaticOrDirect [<<Const42>>,{{[ij]\d+}}]
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Const42>>{{(,[ij]\d+)?}}]
/// CHECK-START: void Main.InlineVoid() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect
@@ -31,7 +31,7 @@
/// CHECK-START: int Main.InlineParameter(int) inliner (before)
/// CHECK-DAG: <<Param:i\d+>> ParameterValue
- /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect [<<Param>>,{{[ij]\d+}}]
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect [<<Param>>{{(,[ij]\d+)?}}]
/// CHECK-DAG: Return [<<Result>>]
/// CHECK-START: int Main.InlineParameter(int) inliner (after)
@@ -44,7 +44,7 @@
/// CHECK-START: long Main.InlineWideParameter(long) inliner (before)
/// CHECK-DAG: <<Param:j\d+>> ParameterValue
- /// CHECK-DAG: <<Result:j\d+>> InvokeStaticOrDirect [<<Param>>,{{[ij]\d+}}]
+ /// CHECK-DAG: <<Result:j\d+>> InvokeStaticOrDirect [<<Param>>{{(,[ij]\d+)?}}]
/// CHECK-DAG: Return [<<Result>>]
/// CHECK-START: long Main.InlineWideParameter(long) inliner (after)
@@ -57,7 +57,7 @@
/// CHECK-START: java.lang.Object Main.InlineReferenceParameter(java.lang.Object) inliner (before)
/// CHECK-DAG: <<Param:l\d+>> ParameterValue
- /// CHECK-DAG: <<Result:l\d+>> InvokeStaticOrDirect [<<Param>>,{{[ij]\d+}}]
+ /// CHECK-DAG: <<Result:l\d+>> InvokeStaticOrDirect [<<Param>>{{(,[ij]\d+)?}}]
/// CHECK-DAG: Return [<<Result>>]
/// CHECK-START: java.lang.Object Main.InlineReferenceParameter(java.lang.Object) inliner (after)
@@ -128,8 +128,8 @@
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
/// CHECK-DAG: <<Const5:i\d+>> IntConstant 5
- /// CHECK-DAG: <<Add:i\d+>> InvokeStaticOrDirect [<<Const1>>,<<Const3>>,{{[ij]\d+}}]
- /// CHECK-DAG: <<Sub:i\d+>> InvokeStaticOrDirect [<<Const5>>,<<Const3>>,{{[ij]\d+}}]
+ /// CHECK-DAG: <<Add:i\d+>> InvokeStaticOrDirect [<<Const1>>,<<Const3>>{{(,[ij]\d+)?}}]
+ /// CHECK-DAG: <<Sub:i\d+>> InvokeStaticOrDirect [<<Const5>>,<<Const3>>{{(,[ij]\d+)?}}]
/// CHECK-DAG: <<Phi:i\d+>> Phi [<<Add>>,<<Sub>>]
/// CHECK-DAG: Return [<<Phi>>]
diff --git a/test/478-checker-clinit-check-pruning/src/Main.java b/test/478-checker-clinit-check-pruning/src/Main.java
index e6aab63..cff6273 100644
--- a/test/478-checker-clinit-check-pruning/src/Main.java
+++ b/test/478-checker-clinit-check-pruning/src/Main.java
@@ -67,14 +67,14 @@
*/
/// CHECK-START: void Main.invokeStaticNotInlined() builder (after)
- /// CHECK-DAG: <<LoadClass:l\d+>> LoadClass gen_clinit_check:false
- /// CHECK-DAG: <<ClinitCheck:l\d+>> ClinitCheck [<<LoadClass>>]
- /// CHECK-DAG: InvokeStaticOrDirect [{{[ij]\d+}},<<ClinitCheck>>]
+ /// CHECK: <<LoadClass:l\d+>> LoadClass gen_clinit_check:false
+ /// CHECK: <<ClinitCheck:l\d+>> ClinitCheck [<<LoadClass>>]
+ /// CHECK: InvokeStaticOrDirect [{{[ij]\d+}},<<ClinitCheck>>]
/// CHECK-START: void Main.invokeStaticNotInlined() inliner (after)
- /// CHECK-DAG: <<LoadClass:l\d+>> LoadClass gen_clinit_check:false
- /// CHECK-DAG: <<ClinitCheck:l\d+>> ClinitCheck [<<LoadClass>>]
- /// CHECK-DAG: InvokeStaticOrDirect [{{[ij]\d+}},<<ClinitCheck>>]
+ /// CHECK: <<LoadClass:l\d+>> LoadClass gen_clinit_check:false
+ /// CHECK: <<ClinitCheck:l\d+>> ClinitCheck [<<LoadClass>>]
+ /// CHECK: InvokeStaticOrDirect [{{([ij]\d+,)?}}<<ClinitCheck>>]
// The following checks ensure the clinit check and load class
// instructions added by the builder are pruned by the
@@ -83,7 +83,7 @@
// before the next pass (liveness analysis) instead.
/// CHECK-START: void Main.invokeStaticNotInlined() liveness (before)
- /// CHECK-DAG: InvokeStaticOrDirect
+ /// CHECK: InvokeStaticOrDirect
/// CHECK-START: void Main.invokeStaticNotInlined() liveness (before)
/// CHECK-NOT: LoadClass
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 8744674..ab1e34e 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -71,6 +71,7 @@
JACK=$(abspath $(JACK)) \
JACK_CLASSPATH=$(TARGET_JACK_CLASSPATH) \
JILL_JAR=$(abspath $(JILL_JAR)) \
+ JACK_VERSION=$(JACK_LANG_DEV_VERSION) \
$(LOCAL_PATH)/run-test $$(PRIVATE_RUN_TEST_OPTIONS) --output-path $$(abspath $$(dir $$@)) $(1)
$(hide) touch $$@
@@ -875,6 +876,7 @@
JACK=$(abspath $(JACK)) \
JACK_CLASSPATH=$$(PRIVATE_JACK_CLASSPATH) \
JILL_JAR=$(abspath $(JILL_JAR)) \
+ JACK_VERSION=$(JACK_LANG_DEV_VERSION) \
art/test/run-test $$(PRIVATE_RUN_TEST_OPTIONS) $(12) \
&& $$(call ART_TEST_PASSED,$$@) || $$(call ART_TEST_FAILED,$$@)
$$(hide) (echo $(MAKECMDGOALS) | grep -q $$@ && \