Relocate DWARF using .oat_patches.
The current solution is to hard-code knowledge of DWARF in the linker.
This works for simple use of DWARF, but breaks as soon as I try to do
anything more complex. Making the linker fully support DWARF would be
non-trivial task and would be essentially rewrite. Using .oat_patches
is much easier solution.
Relocating .debug_* sections required extending .oat_patches to support
more sections than just .text. I have encoded each section as
null-terminated section name followed by ULEB128 deltas.
The ULEB128 encoding shrinks .oat_patches for .text by factor of
about 6 with 64-bit compiler, and factor of 3 with 32-bit compiler.
On the other hand, it grows by the extra .oat_patches for DWARF which
were not present before (if debug symbols are included).
Overall, it is still a clear improvement even with the DWARF patches.
Change-Id: I78ffeda0f8a3da03341995a3b5ef15c954e16e9f
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index 411ec43..f2b013f 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -25,7 +25,6 @@
#include "base/stringprintf.h"
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
-#include "dwarf.h"
#include "elf_file_impl.h"
#include "elf_utils.h"
#include "leb128.h"
@@ -1587,487 +1586,6 @@
return nullptr;
}
-struct PACKED(1) FDE32 {
- uint32_t raw_length_;
- uint32_t GetLength() {
- return raw_length_ + sizeof(raw_length_);
- }
- uint32_t CIE_pointer;
- uint32_t initial_location;
- uint32_t address_range;
- uint8_t instructions[0];
-};
-
-static FDE32* NextFDE(FDE32* frame) {
- uint8_t* fde_bytes = reinterpret_cast<uint8_t*>(frame);
- fde_bytes += frame->GetLength();
- return reinterpret_cast<FDE32*>(fde_bytes);
-}
-
-static bool IsFDE(FDE32* frame) {
- return frame->CIE_pointer != 0;
-}
-
-struct PACKED(1) FDE64 {
- uint32_t raw_length_;
- uint64_t extended_length_;
- uint64_t GetLength() {
- return extended_length_ + sizeof(raw_length_) + sizeof(extended_length_);
- }
- uint64_t CIE_pointer;
- uint64_t initial_location;
- uint64_t address_range;
- uint8_t instructions[0];
-};
-
-static FDE64* NextFDE(FDE64* frame) {
- uint8_t* fde_bytes = reinterpret_cast<uint8_t*>(frame);
- fde_bytes += frame->GetLength();
- return reinterpret_cast<FDE64*>(fde_bytes);
-}
-
-static bool IsFDE(FDE64* frame) {
- return frame->CIE_pointer != 0;
-}
-
-template <typename Elf_SOff>
-static bool FixupEHFrame(Elf_SOff base_address_delta, uint8_t* eh_frame, size_t eh_frame_size) {
- // TODO: Check the spec whether this is really data-dependent, or whether it's clear from the
- // ELF file whether we should expect 32-bit or 64-bit.
- if (*(reinterpret_cast<uint32_t*>(eh_frame)) == 0xffffffff) {
- FDE64* last_frame = reinterpret_cast<FDE64*>(eh_frame + eh_frame_size);
- FDE64* frame = NextFDE(reinterpret_cast<FDE64*>(eh_frame));
- for (; frame < last_frame; frame = NextFDE(frame)) {
- if (!IsFDE(frame)) {
- return false;
- }
- frame->initial_location += base_address_delta;
- }
- return true;
- } else {
- CHECK(IsInt<32>(base_address_delta));
- FDE32* last_frame = reinterpret_cast<FDE32*>(eh_frame + eh_frame_size);
- FDE32* frame = NextFDE(reinterpret_cast<FDE32*>(eh_frame));
- for (; frame < last_frame; frame = NextFDE(frame)) {
- if (!IsFDE(frame)) {
- return false;
- }
- frame->initial_location += base_address_delta;
- }
- return true;
- }
-}
-
-static uint8_t* NextLeb128(uint8_t* current) {
- DecodeUnsignedLeb128(const_cast<const uint8_t**>(¤t));
- return current;
-}
-
-struct PACKED(1) DebugLineHeader {
- uint32_t unit_length_; // TODO 32-bit specific size
- uint16_t version_;
- uint32_t header_length_; // TODO 32-bit specific size
- uint8_t minimum_instruction_lenght_;
- uint8_t default_is_stmt_;
- int8_t line_base_;
- uint8_t line_range_;
- uint8_t opcode_base_;
- uint8_t remaining_[0];
-
- bool IsStandardOpcode(const uint8_t* op) const {
- return *op != 0 && *op < opcode_base_;
- }
-
- bool IsExtendedOpcode(const uint8_t* op) const {
- return *op == 0;
- }
-
- const uint8_t* GetStandardOpcodeLengths() const {
- return remaining_;
- }
-
- uint8_t* GetNextOpcode(uint8_t* op) const {
- if (IsExtendedOpcode(op)) {
- uint8_t* length_field = op + 1;
- uint32_t length = DecodeUnsignedLeb128(const_cast<const uint8_t**>(&length_field));
- return length_field + length;
- } else if (!IsStandardOpcode(op)) {
- return op + 1;
- } else if (*op == dwarf::DW_LNS_fixed_advance_pc) {
- return op + 1 + sizeof(uint16_t);
- } else {
- uint8_t num_args = GetStandardOpcodeLengths()[*op - 1];
- op += 1;
- for (int i = 0; i < num_args; i++) {
- op = NextLeb128(op);
- }
- return op;
- }
- }
-
- uint8_t* GetDebugLineData() const {
- const uint8_t* hdr_start =
- reinterpret_cast<const uint8_t*>(&header_length_) + sizeof(header_length_);
- return const_cast<uint8_t*>(hdr_start + header_length_);
- }
-};
-
-class DebugLineInstructionIterator FINAL {
- public:
- static DebugLineInstructionIterator* Create(DebugLineHeader* header, size_t section_size) {
- std::unique_ptr<DebugLineInstructionIterator> line_iter(
- new DebugLineInstructionIterator(header, section_size));
- if (line_iter.get() == nullptr) {
- return nullptr;
- } else {
- return line_iter.release();
- }
- }
-
- ~DebugLineInstructionIterator() {}
-
- bool Next() {
- if (current_instruction_ == nullptr) {
- return false;
- }
- current_instruction_ = header_->GetNextOpcode(current_instruction_);
- if (current_instruction_ >= last_instruction_) {
- current_instruction_ = nullptr;
- return false;
- } else {
- return true;
- }
- }
-
- uint8_t* GetInstruction() const {
- return current_instruction_;
- }
-
- bool IsExtendedOpcode() const {
- return header_->IsExtendedOpcode(current_instruction_);
- }
-
- uint8_t GetOpcode() {
- if (!IsExtendedOpcode()) {
- return *current_instruction_;
- } else {
- uint8_t* len_ptr = current_instruction_ + 1;
- return *NextLeb128(len_ptr);
- }
- }
-
- uint8_t* GetArguments() {
- if (!IsExtendedOpcode()) {
- return current_instruction_ + 1;
- } else {
- uint8_t* len_ptr = current_instruction_ + 1;
- return NextLeb128(len_ptr) + 1;
- }
- }
-
- private:
- DebugLineInstructionIterator(DebugLineHeader* header, size_t size)
- : header_(header), last_instruction_(reinterpret_cast<uint8_t*>(header) + size),
- current_instruction_(header->GetDebugLineData()) {}
-
- DebugLineHeader* const header_;
- uint8_t* const last_instruction_;
- uint8_t* current_instruction_;
-};
-
-template <typename Elf_SOff>
-static bool FixupDebugLine(Elf_SOff base_offset_delta, DebugLineInstructionIterator* iter) {
- CHECK(IsInt<32>(base_offset_delta));
- for (; iter->GetInstruction(); iter->Next()) {
- if (iter->IsExtendedOpcode() && iter->GetOpcode() == dwarf::DW_LNE_set_address) {
- *reinterpret_cast<uint32_t*>(iter->GetArguments()) += base_offset_delta;
- }
- }
- return true;
-}
-
-struct PACKED(1) DebugInfoHeader {
- uint32_t unit_length; // TODO 32-bit specific size
- uint16_t version;
- uint32_t debug_abbrev_offset; // TODO 32-bit specific size
- uint8_t address_size;
-};
-
-// Returns -1 if it is variable length, which we will just disallow for now.
-static int32_t FormLength(uint32_t att) {
- switch (att) {
- case dwarf::DW_FORM_data1:
- case dwarf::DW_FORM_flag:
- case dwarf::DW_FORM_flag_present:
- case dwarf::DW_FORM_ref1:
- return 1;
-
- case dwarf::DW_FORM_data2:
- case dwarf::DW_FORM_ref2:
- return 2;
-
- case dwarf::DW_FORM_addr: // TODO 32-bit only
- case dwarf::DW_FORM_ref_addr: // TODO 32-bit only
- case dwarf::DW_FORM_sec_offset: // TODO 32-bit only
- case dwarf::DW_FORM_strp: // TODO 32-bit only
- case dwarf::DW_FORM_data4:
- case dwarf::DW_FORM_ref4:
- return 4;
-
- case dwarf::DW_FORM_data8:
- case dwarf::DW_FORM_ref8:
- case dwarf::DW_FORM_ref_sig8:
- return 8;
-
- case dwarf::DW_FORM_block:
- case dwarf::DW_FORM_block1:
- case dwarf::DW_FORM_block2:
- case dwarf::DW_FORM_block4:
- case dwarf::DW_FORM_exprloc:
- case dwarf::DW_FORM_indirect:
- case dwarf::DW_FORM_ref_udata:
- case dwarf::DW_FORM_sdata:
- case dwarf::DW_FORM_string:
- case dwarf::DW_FORM_udata:
- default:
- return -1;
- }
-}
-
-class DebugTag FINAL {
- public:
- ~DebugTag() {}
- // Creates a new tag and moves data pointer up to the start of the next one.
- // nullptr means error.
- static DebugTag* Create(const uint8_t** data_pointer) {
- const uint8_t* data = *data_pointer;
- uint32_t index = DecodeUnsignedLeb128(&data);
- std::unique_ptr<DebugTag> tag(new DebugTag(index));
- tag->size_ = static_cast<uint32_t>(
- reinterpret_cast<uintptr_t>(data) - reinterpret_cast<uintptr_t>(*data_pointer));
- // skip the abbrev
- tag->tag_ = DecodeUnsignedLeb128(&data);
- tag->has_child_ = (*data == 0);
- data++;
- while (true) {
- uint32_t attr = DecodeUnsignedLeb128(&data);
- uint32_t form = DecodeUnsignedLeb128(&data);
- if (attr == 0 && form == 0) {
- break;
- } else if (attr == 0 || form == 0) {
- // Bad abbrev.
- return nullptr;
- }
- int32_t size = FormLength(form);
- if (size == -1) {
- return nullptr;
- }
- tag->AddAttribute(attr, static_cast<uint32_t>(size));
- }
- *data_pointer = data;
- return tag.release();
- }
-
- uint32_t GetSize() const {
- return size_;
- }
-
- bool HasChild() const {
- return has_child_;
- }
-
- uint32_t GetTagNumber() const {
- return tag_;
- }
-
- uint32_t GetIndex() const {
- return index_;
- }
-
- // Gets the offset of a particular attribute in this tag structure.
- // Interpretation of the data is left to the consumer. 0 is returned if the
- // tag does not contain the attribute.
- uint32_t GetOffsetOf(uint32_t dwarf_attribute) const {
- auto it = off_map_.find(dwarf_attribute);
- if (it == off_map_.end()) {
- return 0;
- } else {
- return it->second;
- }
- }
-
- // Gets the size of attribute
- uint32_t GetAttrSize(uint32_t dwarf_attribute) const {
- auto it = size_map_.find(dwarf_attribute);
- if (it == size_map_.end()) {
- return 0;
- } else {
- return it->second;
- }
- }
-
- private:
- explicit DebugTag(uint32_t index) : index_(index), size_(0), tag_(0), has_child_(false) {}
- void AddAttribute(uint32_t type, uint32_t attr_size) {
- off_map_.insert(std::pair<uint32_t, uint32_t>(type, size_));
- size_map_.insert(std::pair<uint32_t, uint32_t>(type, attr_size));
- size_ += attr_size;
- }
-
- const uint32_t index_;
- std::map<uint32_t, uint32_t> off_map_;
- std::map<uint32_t, uint32_t> size_map_;
- uint32_t size_;
- uint32_t tag_;
- bool has_child_;
-};
-
-class DebugAbbrev {
- public:
- ~DebugAbbrev() {}
- static DebugAbbrev* Create(const uint8_t* dbg_abbrev, size_t dbg_abbrev_size) {
- std::unique_ptr<DebugAbbrev> abbrev(new DebugAbbrev(dbg_abbrev, dbg_abbrev + dbg_abbrev_size));
- if (!abbrev->ReadAtOffset(0)) {
- return nullptr;
- }
- return abbrev.release();
- }
-
- bool ReadAtOffset(uint32_t abbrev_offset) {
- tags_.clear();
- tag_list_.clear();
- const uint8_t* dbg_abbrev = begin_ + abbrev_offset;
- while (dbg_abbrev < end_ && *dbg_abbrev != 0) {
- std::unique_ptr<DebugTag> tag(DebugTag::Create(&dbg_abbrev));
- if (tag.get() == nullptr) {
- return false;
- } else {
- tags_.insert(std::pair<uint32_t, uint32_t>(tag->GetIndex(), tag_list_.size()));
- tag_list_.push_back(std::move(tag));
- }
- }
- return true;
- }
-
- DebugTag* ReadTag(const uint8_t* entry) {
- uint32_t tag_num = DecodeUnsignedLeb128(&entry);
- auto it = tags_.find(tag_num);
- if (it == tags_.end()) {
- return nullptr;
- } else {
- CHECK_GT(tag_list_.size(), it->second);
- return tag_list_.at(it->second).get();
- }
- }
-
- private:
- DebugAbbrev(const uint8_t* begin, const uint8_t* end) : begin_(begin), end_(end) {}
- const uint8_t* const begin_;
- const uint8_t* const end_;
- std::map<uint32_t, uint32_t> tags_;
- std::vector<std::unique_ptr<DebugTag>> tag_list_;
-};
-
-class DebugInfoIterator {
- public:
- static DebugInfoIterator* Create(DebugInfoHeader* header, size_t frame_size,
- DebugAbbrev* abbrev) {
- std::unique_ptr<DebugInfoIterator> iter(new DebugInfoIterator(header, frame_size, abbrev));
- if (iter->GetCurrentTag() == nullptr) {
- return nullptr;
- } else {
- return iter.release();
- }
- }
- ~DebugInfoIterator() {}
-
- // Moves to the next DIE. Returns false if at last entry.
- // TODO Handle variable length attributes.
- bool next() {
- if (current_entry_ == nullptr || current_tag_ == nullptr) {
- return false;
- }
- bool reread_abbrev = false;
- current_entry_ += current_tag_->GetSize();
- if (reinterpret_cast<DebugInfoHeader*>(current_entry_) >= next_cu_) {
- current_cu_ = next_cu_;
- next_cu_ = GetNextCu(current_cu_);
- current_entry_ = reinterpret_cast<uint8_t*>(current_cu_) + sizeof(DebugInfoHeader);
- reread_abbrev = true;
- }
- if (current_entry_ >= last_entry_) {
- current_entry_ = nullptr;
- return false;
- }
- if (reread_abbrev) {
- abbrev_->ReadAtOffset(current_cu_->debug_abbrev_offset);
- }
- current_tag_ = abbrev_->ReadTag(current_entry_);
- if (current_tag_ == nullptr) {
- current_entry_ = nullptr;
- return false;
- } else {
- return true;
- }
- }
-
- const DebugTag* GetCurrentTag() {
- return const_cast<DebugTag*>(current_tag_);
- }
- uint8_t* GetPointerToField(uint8_t dwarf_field) {
- if (current_tag_ == nullptr || current_entry_ == nullptr || current_entry_ >= last_entry_) {
- return nullptr;
- }
- uint32_t off = current_tag_->GetOffsetOf(dwarf_field);
- if (off == 0) {
- // tag does not have that field.
- return nullptr;
- } else {
- DCHECK_LT(off, current_tag_->GetSize());
- return current_entry_ + off;
- }
- }
-
- private:
- static DebugInfoHeader* GetNextCu(DebugInfoHeader* hdr) {
- uint8_t* hdr_byte = reinterpret_cast<uint8_t*>(hdr);
- return reinterpret_cast<DebugInfoHeader*>(hdr_byte + sizeof(uint32_t) + hdr->unit_length);
- }
-
- DebugInfoIterator(DebugInfoHeader* header, size_t frame_size, DebugAbbrev* abbrev)
- : abbrev_(abbrev),
- current_cu_(header),
- next_cu_(GetNextCu(header)),
- last_entry_(reinterpret_cast<uint8_t*>(header) + frame_size),
- current_entry_(reinterpret_cast<uint8_t*>(header) + sizeof(DebugInfoHeader)),
- current_tag_(abbrev_->ReadTag(current_entry_)) {}
- DebugAbbrev* const abbrev_;
- DebugInfoHeader* current_cu_;
- DebugInfoHeader* next_cu_;
- uint8_t* const last_entry_;
- uint8_t* current_entry_;
- DebugTag* current_tag_;
-};
-
-template <typename Elf_SOff>
-static bool FixupDebugInfo(Elf_SOff base_address_delta, DebugInfoIterator* iter) {
- CHECK(IsInt<32>(base_address_delta));
- do {
- if (iter->GetCurrentTag()->GetAttrSize(dwarf::DW_AT_low_pc) != sizeof(int32_t) ||
- iter->GetCurrentTag()->GetAttrSize(dwarf::DW_AT_high_pc) != sizeof(int32_t)) {
- LOG(ERROR) << "DWARF information with 64 bit pointers is not supported yet.";
- return false;
- }
- uint32_t* PC_low = reinterpret_cast<uint32_t*>(iter->GetPointerToField(dwarf::DW_AT_low_pc));
- uint32_t* PC_high = reinterpret_cast<uint32_t*>(iter->GetPointerToField(dwarf::DW_AT_high_pc));
- if (PC_low != nullptr && PC_high != nullptr) {
- *PC_low += base_address_delta;
- *PC_high += base_address_delta;
- }
- } while (iter->next());
- return true;
-}
-
template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
@@ -2076,9 +1594,7 @@
::FixupDebugSections(typename std::make_signed<Elf_Off>::type base_address_delta) {
const Elf_Shdr* debug_info = FindSectionByName(".debug_info");
const Elf_Shdr* debug_abbrev = FindSectionByName(".debug_abbrev");
- const Elf_Shdr* eh_frame = FindSectionByName(".eh_frame");
const Elf_Shdr* debug_str = FindSectionByName(".debug_str");
- const Elf_Shdr* debug_line = FindSectionByName(".debug_line");
const Elf_Shdr* strtab_sec = FindSectionByName(".strtab");
const Elf_Shdr* symtab_sec = FindSectionByName(".symtab");
@@ -2090,37 +1606,82 @@
if (base_address_delta == 0) {
return true;
}
- if (eh_frame != nullptr &&
- !FixupEHFrame(base_address_delta, Begin() + eh_frame->sh_offset, eh_frame->sh_size)) {
+ if (!ApplyOatPatchesTo(".eh_frame", base_address_delta)) {
return false;
}
+ if (!ApplyOatPatchesTo(".debug_info", base_address_delta)) {
+ return false;
+ }
+ if (!ApplyOatPatchesTo(".debug_line", base_address_delta)) {
+ return false;
+ }
+ return true;
+}
- std::unique_ptr<DebugAbbrev> abbrev(DebugAbbrev::Create(Begin() + debug_abbrev->sh_offset,
- debug_abbrev->sh_size));
- if (abbrev.get() == nullptr) {
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+ typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+ typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+ Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+ ::ApplyOatPatchesTo(const char* target_section_name,
+ typename std::make_signed<Elf_Off>::type delta) {
+ auto patches_section = FindSectionByName(".oat_patches");
+ if (patches_section == nullptr) {
+ LOG(ERROR) << ".oat_patches section not found.";
return false;
}
- DebugInfoHeader* info_header =
- reinterpret_cast<DebugInfoHeader*>(Begin() + debug_info->sh_offset);
- std::unique_ptr<DebugInfoIterator> info_iter(DebugInfoIterator::Create(info_header,
- debug_info->sh_size,
- abbrev.get()));
- if (info_iter.get() == nullptr) {
+ if (patches_section->sh_type != SHT_OAT_PATCH) {
+ LOG(ERROR) << "Unexpected type of .oat_patches.";
return false;
}
- if (debug_line != nullptr) {
- DebugLineHeader* line_header =
- reinterpret_cast<DebugLineHeader*>(Begin() + debug_line->sh_offset);
- std::unique_ptr<DebugLineInstructionIterator> line_iter(
- DebugLineInstructionIterator::Create(line_header, debug_line->sh_size));
- if (line_iter.get() == nullptr) {
- return false;
- }
- if (!FixupDebugLine(base_address_delta, line_iter.get())) {
- return false;
- }
+ auto target_section = FindSectionByName(target_section_name);
+ if (target_section == nullptr) {
+ LOG(ERROR) << target_section_name << " section not found.";
+ return false;
}
- return FixupDebugInfo(base_address_delta, info_iter.get());
+ if (!ApplyOatPatches(
+ Begin() + patches_section->sh_offset,
+ Begin() + patches_section->sh_offset + patches_section->sh_size,
+ target_section_name, delta,
+ Begin() + target_section->sh_offset,
+ Begin() + target_section->sh_offset + target_section->sh_size)) {
+ LOG(ERROR) << target_section_name << " section not found in .oat_patches.";
+ }
+ return true;
+}
+
+// Apply .oat_patches to given section.
+template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,
+ typename Elf_Sword, typename Elf_Addr, typename Elf_Sym, typename Elf_Rel,
+ typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
+bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
+ Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
+ ::ApplyOatPatches(const uint8_t* patches, const uint8_t* patches_end,
+ const char* target_section_name,
+ typename std::make_signed<Elf_Off>::type delta,
+ uint8_t* to_patch, const uint8_t* to_patch_end) {
+ // Read null-terminated section name.
+ const char* section_name;
+ while ((section_name = reinterpret_cast<const char*>(patches))[0] != '\0') {
+ patches += strlen(section_name) + 1;
+ uint32_t length = DecodeUnsignedLeb128(&patches);
+ const uint8_t* next_section = patches + length;
+ // Is it the section we want to patch?
+ if (strcmp(section_name, target_section_name) == 0) {
+ // Read LEB128 encoded list of advances.
+ while (patches < next_section) {
+ DCHECK_LT(patches, patches_end) << "Unexpected end of .oat_patches.";
+ to_patch += DecodeUnsignedLeb128(&patches);
+ DCHECK_LT(to_patch, to_patch_end) << "Patch past the end of " << section_name;
+ // TODO: 32-bit vs 64-bit. What is the right type to use here?
+ auto* patch_loc = reinterpret_cast<typename std::make_signed<Elf_Off>::type*>(to_patch);
+ *patch_loc += delta;
+ }
+ return true;
+ }
+ patches = next_section;
+ }
+ return false;
}
template <typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr, typename Elf_Word,