Refactor ElfDebugReader.
Make the code more flexible, which I will need for
future mini-debug-info work.
Bug: 110133331
Test: ./art/test.py -b -r -t 137
Change-Id: I8b0fe3c43537f546f2ff103bff3c63a59a0f940a
diff --git a/compiler/debug/dwarf/headers.h b/compiler/debug/dwarf/headers.h
index 869a2c2..3cc8ad8 100644
--- a/compiler/debug/dwarf/headers.h
+++ b/compiler/debug/dwarf/headers.h
@@ -90,30 +90,6 @@
writer.UpdateUint32(fde_header_start, writer.data()->size() - fde_header_start - 4);
}
-// Read singe FDE entry from 'data' (which is advanced).
-template<typename Addr>
-bool ReadFDE(const uint8_t** data, Addr* addr, Addr* size, ArrayRef<const uint8_t>* opcodes) {
- struct Header {
- uint32_t length;
- int32_t cie_pointer;
- Addr addr;
- Addr size;
- uint8_t augmentaion;
- uint8_t opcodes[];
- } PACKED(1);
- const Header* header = reinterpret_cast<const Header*>(*data);
- const size_t length = 4 + header->length;
- *data += length;
- if (header->cie_pointer == -1) {
- return false; // Not an FDE entry.
- }
- DCHECK_EQ(header->cie_pointer, 0); // Expects single CIE. Assumes DW_DEBUG_FRAME_FORMAT.
- *addr = header->addr;
- *size = header->size;
- *opcodes = ArrayRef<const uint8_t>(header->opcodes, length - offsetof(Header, opcodes));
- return true;
-}
-
// Write compilation unit (CU) to .debug_info section.
template<typename Vector>
void WriteDebugInfoCU(uint32_t debug_abbrev_offset,
diff --git a/compiler/debug/elf_debug_reader.h b/compiler/debug/elf_debug_reader.h
index 91b1b3e..1820e7c 100644
--- a/compiler/debug/elf_debug_reader.h
+++ b/compiler/debug/elf_debug_reader.h
@@ -22,6 +22,9 @@
#include "elf.h"
#include "xz_utils.h"
+#include <map>
+#include <string_view>
+
namespace art {
namespace debug {
@@ -29,75 +32,141 @@
//
// It is the bare minimum needed to read mini-debug-info symbols for unwinding.
// We use it to merge JIT mini-debug-infos together or to prune them after GC.
-// The consumed ELF file comes from ART JIT.
-template <typename ElfTypes, typename VisitSym, typename VisitFde>
-static void ReadElfSymbols(const uint8_t* elf, VisitSym visit_sym, VisitFde visit_fde) {
+template <typename ElfTypes>
+class ElfDebugReader {
+ public:
// Note that the input buffer might be misaligned.
typedef typename ElfTypes::Ehdr ALIGNED(1) Elf_Ehdr;
typedef typename ElfTypes::Shdr ALIGNED(1) Elf_Shdr;
typedef typename ElfTypes::Sym ALIGNED(1) Elf_Sym;
typedef typename ElfTypes::Addr ALIGNED(1) Elf_Addr;
- // Read and check the elf header.
- const Elf_Ehdr* header = reinterpret_cast<const Elf_Ehdr*>(elf);
- CHECK(header->checkMagic());
+ // Call Frame Information.
+ struct CFI {
+ uint32_t length; // Length excluding the size of this field.
+ int32_t cie_pointer; // Offset in the section or -1 for CIE.
- // Find sections that we are interested in.
- const Elf_Shdr* sections = reinterpret_cast<const Elf_Shdr*>(elf + header->e_shoff);
- const Elf_Shdr* strtab = nullptr;
- const Elf_Shdr* symtab = nullptr;
- const Elf_Shdr* debug_frame = nullptr;
- const Elf_Shdr* gnu_debugdata = nullptr;
- for (size_t i = 1 /* skip null section */; i < header->e_shnum; i++) {
- const Elf_Shdr* section = sections + i;
- const char* name = reinterpret_cast<const char*>(
- elf + sections[header->e_shstrndx].sh_offset + section->sh_name);
- if (strcmp(name, ".strtab") == 0) {
- strtab = section;
- } else if (strcmp(name, ".symtab") == 0) {
- symtab = section;
- } else if (strcmp(name, ".debug_frame") == 0) {
- debug_frame = section;
- } else if (strcmp(name, ".gnu_debugdata") == 0) {
- gnu_debugdata = section;
+ const uint8_t* data() const { return reinterpret_cast<const uint8_t*>(this); }
+ size_t size() const { return sizeof(uint32_t) + length; }
+ } PACKED(1);
+
+ // Common Information Entry.
+ struct CIE : public CFI {
+ } PACKED(1);
+
+ // Frame Description Entry.
+ struct FDE : public CFI {
+ Elf_Addr sym_addr;
+ Elf_Addr sym_size;
+ } PACKED(1);
+
+ explicit ElfDebugReader(ArrayRef<const uint8_t> file) : file_(file) {
+ header_ = Read<Elf_Ehdr>(/*offset=*/ 0);
+ CHECK(header_->checkMagic());
+ CHECK_EQ(header_->e_ehsize, sizeof(Elf_Ehdr));
+ CHECK_EQ(header_->e_shentsize, sizeof(Elf_Shdr));
+
+ // Find all ELF sections.
+ sections_ = Read<Elf_Shdr>(header_->e_shoff, header_->e_shnum);
+ for (const Elf_Shdr& section : sections_) {
+ const char* name = Read<char>(sections_[header_->e_shstrndx].sh_offset + section.sh_name);
+ section_map_[std::string_view(name)] = §ion;
+ }
+
+ // Decompressed embedded debug symbols, if any.
+ const Elf_Shdr* gnu_debugdata = section_map_[".gnu_debugdata"];
+ if (gnu_debugdata != nullptr) {
+ auto compressed = Read<uint8_t>(gnu_debugdata->sh_offset, gnu_debugdata->sh_size);
+ XzDecompress(compressed, &decompressed_gnu_debugdata_);
+ gnu_debugdata_reader_.reset(new ElfDebugReader(decompressed_gnu_debugdata_));
}
}
- // Visit symbols.
- if (symtab != nullptr && strtab != nullptr) {
- const Elf_Sym* symbols = reinterpret_cast<const Elf_Sym*>(elf + symtab->sh_offset);
- DCHECK_EQ(symtab->sh_entsize, sizeof(Elf_Sym));
- size_t count = symtab->sh_size / sizeof(Elf_Sym);
- for (size_t i = 1 /* skip null symbol */; i < count; i++) {
- Elf_Sym symbol = symbols[i];
- if (symbol.getBinding() != STB_LOCAL) { // Ignore local symbols (e.g. "$t").
- const uint8_t* name = elf + strtab->sh_offset + symbol.st_name;
- visit_sym(symbol, reinterpret_cast<const char*>(name));
+ explicit ElfDebugReader(std::vector<uint8_t>& file)
+ : ElfDebugReader(ArrayRef<const uint8_t>(file)) {
+ }
+
+ const Elf_Ehdr* GetHeader() { return header_; }
+
+ ArrayRef<Elf_Shdr> GetSections() { return sections_; }
+
+ const Elf_Shdr* GetSection(const char* name) { return section_map_[name]; }
+
+ template <typename VisitSym>
+ void VisitFunctionSymbols(VisitSym visit_sym) {
+ const Elf_Shdr* symtab = GetSection(".symtab");
+ const Elf_Shdr* strtab = GetSection(".strtab");
+ const Elf_Shdr* text = GetSection(".text");
+ if (symtab != nullptr && strtab != nullptr) {
+ CHECK_EQ(symtab->sh_entsize, sizeof(Elf_Sym));
+ size_t count = symtab->sh_size / sizeof(Elf_Sym);
+ for (const Elf_Sym& symbol : Read<Elf_Sym>(symtab->sh_offset, count)) {
+ if (symbol.getType() == STT_FUNC && §ions_[symbol.st_shndx] == text) {
+ visit_sym(symbol, Read<char>(strtab->sh_offset + symbol.st_name));
+ }
+ }
+ }
+ if (gnu_debugdata_reader_ != nullptr) {
+ gnu_debugdata_reader_->VisitFunctionSymbols(visit_sym);
+ }
+ }
+
+ template <typename VisitSym>
+ void VisitDynamicSymbols(VisitSym visit_sym) {
+ const Elf_Shdr* dynsym = GetSection(".dynsym");
+ const Elf_Shdr* dynstr = GetSection(".dynstr");
+ if (dynsym != nullptr && dynstr != nullptr) {
+ CHECK_EQ(dynsym->sh_entsize, sizeof(Elf_Sym));
+ size_t count = dynsym->sh_size / sizeof(Elf_Sym);
+ for (const Elf_Sym& symbol : Read<Elf_Sym>(dynsym->sh_offset, count)) {
+ visit_sym(symbol, Read<char>(dynstr->sh_offset + symbol.st_name));
}
}
}
- // Visit CFI (unwind) data.
- if (debug_frame != nullptr) {
- const uint8_t* data = elf + debug_frame->sh_offset;
- const uint8_t* end = data + debug_frame->sh_size;
- while (data < end) {
- Elf_Addr addr, size;
- ArrayRef<const uint8_t> opcodes;
- if (dwarf::ReadFDE<Elf_Addr>(&data, &addr, &size, &opcodes)) {
- visit_fde(addr, size, opcodes);
+ template <typename VisitCIE, typename VisitFDE>
+ void VisitDebugFrame(VisitCIE visit_cie, VisitFDE visit_fde) {
+ const Elf_Shdr* debug_frame = GetSection(".debug_frame");
+ if (debug_frame != nullptr) {
+ for (size_t offset = 0; offset < debug_frame->sh_size;) {
+ const CFI* entry = Read<CFI>(debug_frame->sh_offset + offset);
+ DCHECK_LE(entry->size(), debug_frame->sh_size - offset);
+ if (entry->cie_pointer == -1) {
+ visit_cie(Read<CIE>(debug_frame->sh_offset + offset));
+ } else {
+ const FDE* fde = Read<FDE>(debug_frame->sh_offset + offset);
+ visit_fde(fde, Read<CIE>(debug_frame->sh_offset + fde->cie_pointer));
+ }
+ offset += entry->size();
}
}
+ if (gnu_debugdata_reader_ != nullptr) {
+ gnu_debugdata_reader_->VisitDebugFrame(visit_cie, visit_fde);
+ }
}
- // Process embedded compressed ELF file.
- if (gnu_debugdata != nullptr) {
- ArrayRef<const uint8_t> compressed(elf + gnu_debugdata->sh_offset, gnu_debugdata->sh_size);
- std::vector<uint8_t> decompressed;
- XzDecompress(compressed, &decompressed);
- ReadElfSymbols<ElfTypes>(decompressed.data(), visit_sym, visit_fde);
+ private:
+ template<typename T>
+ const T* Read(size_t offset) {
+ DCHECK_LE(offset + sizeof(T), file_.size());
+ return reinterpret_cast<const T*>(file_.data() + offset);
}
-}
+
+ template<typename T>
+ ArrayRef<const T> Read(size_t offset, size_t count) {
+ DCHECK_LE(offset + count * sizeof(T), file_.size());
+ return ArrayRef<const T>(Read<T>(offset), count);
+ }
+
+ ArrayRef<const uint8_t> const file_;
+ const Elf_Ehdr* header_;
+ ArrayRef<const Elf_Shdr> sections_;
+ std::unordered_map<std::string_view, const Elf_Shdr*> section_map_;
+ std::vector<uint8_t> decompressed_gnu_debugdata_;
+ std::unique_ptr<ElfDebugReader> gnu_debugdata_reader_;
+
+ DISALLOW_COPY_AND_ASSIGN(ElfDebugReader);
+};
} // namespace debug
} // namespace art
diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc
index fd638b7..3b7363b 100644
--- a/compiler/debug/elf_debug_writer.cc
+++ b/compiler/debug/elf_debug_writer.cc
@@ -203,25 +203,26 @@
// Verify the ELF file by reading it back using the trivial reader.
if (kIsDebugBuild) {
using Elf_Sym = typename ElfTypes::Sym;
- using Elf_Addr = typename ElfTypes::Addr;
size_t num_syms = 0;
- size_t num_cfis = 0;
- ReadElfSymbols<ElfTypes>(
- buffer.data(),
- [&](Elf_Sym sym, const char*) {
- DCHECK_EQ(sym.st_value, method_info.code_address + CompiledMethod::CodeDelta(isa));
- DCHECK_EQ(sym.st_size, method_info.code_size);
- num_syms++;
- },
- [&](Elf_Addr addr, Elf_Addr size, ArrayRef<const uint8_t> opcodes) {
- DCHECK_EQ(addr, method_info.code_address);
- DCHECK_EQ(size, method_info.code_size);
- DCHECK_GE(opcodes.size(), method_info.cfi.size());
- DCHECK_EQ(memcmp(opcodes.data(), method_info.cfi.data(), method_info.cfi.size()), 0);
- num_cfis++;
- });
+ size_t num_cies = 0;
+ size_t num_fdes = 0;
+ using Reader = ElfDebugReader<ElfTypes>;
+ Reader reader(buffer);
+ reader.VisitFunctionSymbols([&](Elf_Sym sym, const char*) {
+ DCHECK_EQ(sym.st_value, method_info.code_address + CompiledMethod::CodeDelta(isa));
+ DCHECK_EQ(sym.st_size, method_info.code_size);
+ num_syms++;
+ });
+ reader.VisitDebugFrame([&](const Reader::CIE* cie ATTRIBUTE_UNUSED) {
+ num_cies++;
+ }, [&](const Reader::FDE* fde, const Reader::CIE* cie ATTRIBUTE_UNUSED) {
+ DCHECK_EQ(fde->sym_addr, method_info.code_address);
+ DCHECK_EQ(fde->sym_size, method_info.code_size);
+ num_fdes++;
+ });
DCHECK_EQ(num_syms, 1u);
- DCHECK_EQ(num_cfis, 1u);
+ DCHECK_LE(num_cies, 1u);
+ DCHECK_LE(num_fdes, 1u);
}
return buffer;
}
@@ -230,14 +231,13 @@
std::vector<uint8_t> PackElfFileForJIT(
InstructionSet isa,
const InstructionSetFeatures* features,
- std::vector<const uint8_t*>& added_elf_files,
+ std::vector<ArrayRef<const uint8_t>>& added_elf_files,
std::vector<const void*>& removed_symbols,
/*out*/ size_t* num_symbols) {
using ElfTypes = ElfRuntimeTypes;
using Elf_Addr = typename ElfTypes::Addr;
using Elf_Sym = typename ElfTypes::Sym;
CHECK_EQ(sizeof(Elf_Addr), static_cast<size_t>(GetInstructionSetPointerSize(isa)));
- const bool is64bit = Is64BitInstructionSet(isa);
auto is_removed_symbol = [&removed_symbols](Elf_Addr addr) {
const void* code_ptr = reinterpret_cast<const void*>(addr);
return std::binary_search(removed_symbols.begin(), removed_symbols.end(), code_ptr);
@@ -259,35 +259,26 @@
auto* symtab = builder->GetSymTab();
auto* debug_frame = builder->GetDebugFrame();
std::deque<Elf_Sym> symbols;
- std::vector<uint8_t> debug_frame_buffer;
- WriteCIE(isa, &debug_frame_buffer);
+
+ using Reader = ElfDebugReader<ElfTypes>;
+ std::deque<Reader> readers;
+ for (ArrayRef<const uint8_t> added_elf_file : added_elf_files) {
+ readers.emplace_back(added_elf_file);
+ }
// Write symbols names. All other data is buffered.
strtab->Start();
strtab->Write(""); // strtab should start with empty string.
- for (const uint8_t* added_elf_file : added_elf_files) {
- ReadElfSymbols<ElfTypes>(
- added_elf_file,
- [&](Elf_Sym sym, const char* name) {
- if (is_removed_symbol(sym.st_value)) {
- return;
- }
- sym.st_name = strtab->Write(name);
- symbols.push_back(sym);
- min_address = std::min<uint64_t>(min_address, sym.st_value);
- max_address = std::max<uint64_t>(max_address, sym.st_value + sym.st_size);
- },
- [&](Elf_Addr addr, Elf_Addr size, ArrayRef<const uint8_t> opcodes) {
- if (is_removed_symbol(addr)) {
- return;
- }
- dwarf::WriteFDE(is64bit,
- /* cie_pointer= */ 0,
- addr,
- size,
- opcodes,
- &debug_frame_buffer);
- });
+ for (Reader& reader : readers) {
+ reader.VisitFunctionSymbols([&](Elf_Sym sym, const char* name) {
+ if (is_removed_symbol(sym.st_value)) {
+ return;
+ }
+ sym.st_name = strtab->Write(name);
+ symbols.push_back(sym);
+ min_address = std::min<uint64_t>(min_address, sym.st_value);
+ max_address = std::max<uint64_t>(max_address, sym.st_value + sym.st_size);
+ });
}
strtab->End();
@@ -305,7 +296,22 @@
// Add the CFI/unwind section.
debug_frame->Start();
- debug_frame->WriteFully(debug_frame_buffer.data(), debug_frame_buffer.size());
+ // ART always produces the same CIE, so we copy the first one and ignore the rest.
+ bool copied_cie = false;
+ for (Reader& reader : readers) {
+ reader.VisitDebugFrame([&](const Reader::CIE* cie) {
+ if (!copied_cie) {
+ debug_frame->WriteFully(cie->data(), cie->size());
+ copied_cie = true;
+ }
+ }, [&](const Reader::FDE* fde, const Reader::CIE* cie ATTRIBUTE_UNUSED) {
+ DCHECK(copied_cie);
+ DCHECK_EQ(fde->cie_pointer, 0);
+ if (!is_removed_symbol(fde->sym_addr)) {
+ debug_frame->WriteFully(fde->data(), fde->size());
+ }
+ });
+ }
debug_frame->End();
builder->End();
diff --git a/compiler/debug/elf_debug_writer.h b/compiler/debug/elf_debug_writer.h
index 3cc38a2..90580b4 100644
--- a/compiler/debug/elf_debug_writer.h
+++ b/compiler/debug/elf_debug_writer.h
@@ -57,7 +57,7 @@
std::vector<uint8_t> PackElfFileForJIT(
InstructionSet isa,
const InstructionSetFeatures* features,
- std::vector<const uint8_t*>& added_elf_files,
+ std::vector<ArrayRef<const uint8_t>>& added_elf_files,
std::vector<const void*>& removed_symbols,
/*out*/ size_t* num_symbols);
diff --git a/runtime/jit/debugger_interface.cc b/runtime/jit/debugger_interface.cc
index 99f9387..a69429f 100644
--- a/runtime/jit/debugger_interface.cc
+++ b/runtime/jit/debugger_interface.cc
@@ -301,7 +301,7 @@
return; // Nothing to do.
}
- std::vector<const uint8_t*> added_elf_files;
+ std::vector<ArrayRef<const uint8_t>> added_elf_files;
std::vector<const void*> removed_symbols;
auto added_it = g_jit_debug_entries.begin();
auto removed_it = removed_entries.begin();
@@ -312,7 +312,8 @@
auto added_begin = added_it;
while (added_it != g_jit_debug_entries.end() &&
AlignDown(added_it->first, kGroupSize) == group_ptr) {
- added_elf_files.push_back((added_it++)->second->symfile_addr_);
+ JITCodeEntry* entry = (added_it++)->second;
+ added_elf_files.emplace_back(entry->symfile_addr_, entry->symfile_size_);
}
removed_symbols.clear();
while (removed_it != removed_entries.end() &&
diff --git a/runtime/jit/debugger_interface.h b/runtime/jit/debugger_interface.h
index 17beb4b..51b7041 100644
--- a/runtime/jit/debugger_interface.h
+++ b/runtime/jit/debugger_interface.h
@@ -21,6 +21,7 @@
#include <vector>
#include "arch/instruction_set_features.h"
+#include "base/array_ref.h"
#include "base/locks.h"
namespace art {
@@ -33,7 +34,7 @@
typedef std::vector<uint8_t> PackElfFileForJITFunction(
InstructionSet isa,
const InstructionSetFeatures* features,
- std::vector<const uint8_t*>& added_elf_files,
+ std::vector<ArrayRef<const uint8_t>>& added_elf_files,
std::vector<const void*>& removed_symbols,
/*out*/ size_t* num_symbols);