From 8098b1c378cf999fda76c2dc0dfbdfaafc25adb9 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Tue, 20 Jun 2017 13:54:08 -0700 Subject: [PATCH] Add section parsing and function name handling. Add the code to parse the Elf section headers. Add the plumbing through of all the symbol handling code. Add tests for all of this new functionality. Bug: 23762183 Test: Pass new unit tests. Change-Id: Ie2d90cbb3d7653c53251dbcf34d9e5d241278377 --- libunwindstack/Elf.h | 4 +- libunwindstack/ElfInterface.cpp | 108 ++++++++++-- libunwindstack/ElfInterface.h | 16 +- libunwindstack/tests/ElfInterfaceTest.cpp | 203 ++++++++++++++++++++++ 4 files changed, 311 insertions(+), 20 deletions(-) diff --git a/libunwindstack/Elf.h b/libunwindstack/Elf.h index 7bf45b842..f9db54124 100644 --- a/libunwindstack/Elf.h +++ b/libunwindstack/Elf.h @@ -45,8 +45,8 @@ class Elf { return valid_ && interface_->GetSoname(name); } - bool GetFunctionName(uint64_t, std::string*, uint64_t*) { - return false; + bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) { + return valid_ && interface_->GetFunctionName(addr, name, func_offset); } bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory) { diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp index bfa794432..3a7f7cb7b 100644 --- a/libunwindstack/ElfInterface.cpp +++ b/libunwindstack/ElfInterface.cpp @@ -22,9 +22,18 @@ #include "DwarfDebugFrame.h" #include "DwarfEhFrame.h" +#include "DwarfSection.h" #include "ElfInterface.h" +#include "Log.h" #include "Memory.h" #include "Regs.h" +#include "Symbols.h" + +ElfInterface::~ElfInterface() { + for (auto symbol : symbols_) { + delete symbol; + } +} template void ElfInterface::InitHeadersWithTemplate() { @@ -57,7 +66,13 @@ bool ElfInterface::ReadAllHeaders() { if (!ReadProgramHeaders(ehdr)) { return false; } - return ReadSectionHeaders(ehdr); + + // We could still potentially unwind without the section header + // information, so ignore any errors. + if (!ReadSectionHeaders(ehdr)) { + log(0, "Malformed section header found, ignoring..."); + } + return true; } template @@ -147,12 +162,39 @@ bool ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) { } // Skip the first header, it's always going to be NULL. + offset += ehdr.e_shentsize; for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) { if (!memory_->ReadField(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) { return false; } - if (shdr.sh_type == SHT_PROGBITS) { + if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) { + if (!memory_->Read(offset, &shdr, sizeof(shdr))) { + return false; + } + // Need to go get the information about the section that contains + // the string terminated names. + ShdrType str_shdr; + if (shdr.sh_link >= ehdr.e_shnum) { + return false; + } + uint64_t str_offset = ehdr.e_shoff + shdr.sh_link * ehdr.e_shentsize; + if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_type, sizeof(str_shdr.sh_type))) { + return false; + } + if (str_shdr.sh_type != SHT_STRTAB) { + return false; + } + if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_offset, + sizeof(str_shdr.sh_offset))) { + return false; + } + if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_size, sizeof(str_shdr.sh_size))) { + return false; + } + symbols_.push_back(new Symbols(shdr.sh_offset, shdr.sh_size, shdr.sh_entsize, + str_shdr.sh_offset, str_shdr.sh_size)); + } else if (shdr.sh_type == SHT_PROGBITS && sec_size != 0) { // Look for the .debug_frame and .gnu_debugdata. if (!memory_->ReadField(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) { return false; @@ -160,18 +202,20 @@ bool ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) { if (shdr.sh_name < sec_size) { std::string name; if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) { + uint64_t* offset_ptr = nullptr; + uint64_t* size_ptr = nullptr; if (name == ".debug_frame") { - if (memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) && - memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) { - debug_frame_offset_ = shdr.sh_offset; - debug_frame_size_ = shdr.sh_size; - } + offset_ptr = &debug_frame_offset_; + size_ptr = &debug_frame_size_; } else if (name == ".gnu_debugdata") { - if (memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) && - memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) { - gnu_debugdata_offset_ = shdr.sh_offset; - gnu_debugdata_size_ = shdr.sh_size; - } + offset_ptr = &gnu_debugdata_offset_; + size_ptr = &gnu_debugdata_size_; + } + if (offset_ptr != nullptr && + memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) && + memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) { + *offset_ptr = shdr.sh_offset; + *size_ptr = shdr.sh_size; } } } @@ -228,7 +272,40 @@ bool ElfInterface::GetSonameWithTemplate(std::string* soname) { return true; } -bool ElfInterface::Step(uint64_t, Regs*, Memory*) { +template +bool ElfInterface::GetFunctionNameWithTemplate(uint64_t addr, std::string* name, + uint64_t* func_offset) { + if (symbols_.empty()) { + return false; + } + + for (const auto symbol : symbols_) { + if (symbol->GetName(addr, load_bias_, memory_, name, func_offset)) { + return true; + } + } + return false; +} + +bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory) { + // Need to subtract off the load_bias to get the correct pc. + if (pc < load_bias_) { + return false; + } + pc -= load_bias_; + + // Try the eh_frame first. + DwarfSection* eh_frame = eh_frame_.get(); + if (eh_frame != nullptr && eh_frame->Step(pc, regs, process_memory)) { + return true; + } + + // Try the debug_frame next. + DwarfSection* debug_frame = debug_frame_.get(); + if (debug_frame != nullptr && debug_frame->Step(pc, regs, process_memory)) { + return true; + } + return false; } @@ -247,3 +324,8 @@ template bool ElfInterface::ReadSectionHeaders(const Elf template bool ElfInterface::GetSonameWithTemplate(std::string*); template bool ElfInterface::GetSonameWithTemplate(std::string*); + +template bool ElfInterface::GetFunctionNameWithTemplate(uint64_t, std::string*, + uint64_t*); +template bool ElfInterface::GetFunctionNameWithTemplate(uint64_t, std::string*, + uint64_t*); diff --git a/libunwindstack/ElfInterface.h b/libunwindstack/ElfInterface.h index 1cc8aa0ee..d0d0d2839 100644 --- a/libunwindstack/ElfInterface.h +++ b/libunwindstack/ElfInterface.h @@ -30,6 +30,7 @@ // Forward declarations. class Memory; class Regs; +class Symbols; struct LoadInfo { uint64_t offset; @@ -46,7 +47,7 @@ enum : uint8_t { class ElfInterface { public: ElfInterface(Memory* memory) : memory_(memory) {} - virtual ~ElfInterface() = default; + virtual ~ElfInterface(); virtual bool Init() = 0; @@ -94,6 +95,9 @@ class ElfInterface { template bool GetSonameWithTemplate(std::string* soname); + template + bool GetFunctionNameWithTemplate(uint64_t addr, std::string* name, uint64_t* func_offset); + virtual bool HandleType(uint64_t, uint32_t) { return false; } Memory* memory_; @@ -118,6 +122,8 @@ class ElfInterface { std::unique_ptr eh_frame_; std::unique_ptr debug_frame_; + + std::vector symbols_; }; class ElfInterface32 : public ElfInterface { @@ -135,8 +141,8 @@ class ElfInterface32 : public ElfInterface { return ElfInterface::GetSonameWithTemplate(soname); } - bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { - return false; + bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override { + return ElfInterface::GetFunctionNameWithTemplate(addr, name, func_offset); } }; @@ -155,8 +161,8 @@ class ElfInterface64 : public ElfInterface { return ElfInterface::GetSonameWithTemplate(soname); } - bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { - return false; + bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override { + return ElfInterface::GetFunctionNameWithTemplate(addr, name, func_offset); } }; diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp index 81cdaf5f0..0f56ba897 100644 --- a/libunwindstack/tests/ElfInterfaceTest.cpp +++ b/libunwindstack/tests/ElfInterfaceTest.cpp @@ -79,9 +79,37 @@ class ElfInterfaceTest : public ::testing::Test { template void InitHeadersDebugFrameFail(); + template + void InitSectionHeadersMalformed(); + + template + void InitSectionHeaders(uint64_t entry_size); + + template + void InitSectionHeadersOffsets(); + + template + void InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset, + uint64_t sym_offset, const char* name); + MemoryFake memory_; }; +template +void ElfInterfaceTest::InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset, + uint64_t sym_offset, const char* name) { + Sym sym; + memset(&sym, 0, sizeof(sym)); + sym.st_info = STT_FUNC; + sym.st_value = value; + sym.st_size = size; + sym.st_name = name_offset; + sym.st_shndx = SHN_COMMON; + + memory_.SetMemory(offset, &sym, sizeof(sym)); + memory_.SetMemory(sym_offset + name_offset, name, strlen(name) + 1); +} + template void ElfInterfaceTest::SinglePtLoad() { std::unique_ptr elf(new ElfInterfaceType(&memory_)); @@ -718,3 +746,178 @@ TEST_F(ElfInterfaceTest, init_headers_debug_frame32_fail) { TEST_F(ElfInterfaceTest, init_headers_debug_frame64_fail) { InitHeadersDebugFrameFail(); } + +template +void ElfInterfaceTest::InitSectionHeadersMalformed() { + std::unique_ptr elf(new ElfInterfaceType(&memory_)); + + Ehdr ehdr; + memset(&ehdr, 0, sizeof(ehdr)); + ehdr.e_shoff = 0x1000; + ehdr.e_shnum = 10; + ehdr.e_shentsize = sizeof(Shdr); + memory_.SetMemory(0, &ehdr, sizeof(ehdr)); + + ASSERT_TRUE(elf->Init()); +} + +TEST_F(ElfInterfaceTest, init_section_headers_malformed32) { + InitSectionHeadersMalformed(); +} + +TEST_F(ElfInterfaceTest, init_section_headers_malformed64) { + InitSectionHeadersMalformed(); +} + +template +void ElfInterfaceTest::InitSectionHeaders(uint64_t entry_size) { + std::unique_ptr elf(new ElfInterfaceType(&memory_)); + + uint64_t offset = 0x1000; + + Ehdr ehdr; + memset(&ehdr, 0, sizeof(ehdr)); + ehdr.e_shoff = offset; + ehdr.e_shnum = 10; + ehdr.e_shentsize = entry_size; + memory_.SetMemory(0, &ehdr, sizeof(ehdr)); + + offset += ehdr.e_shentsize; + + Shdr shdr; + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_SYMTAB; + shdr.sh_link = 4; + shdr.sh_addr = 0x5000; + shdr.sh_offset = 0x5000; + shdr.sh_entsize = sizeof(Sym); + shdr.sh_size = shdr.sh_entsize * 10; + memory_.SetMemory(offset, &shdr, sizeof(shdr)); + offset += ehdr.e_shentsize; + + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_DYNSYM; + shdr.sh_link = 4; + shdr.sh_addr = 0x6000; + shdr.sh_offset = 0x6000; + shdr.sh_entsize = sizeof(Sym); + shdr.sh_size = shdr.sh_entsize * 10; + memory_.SetMemory(offset, &shdr, sizeof(shdr)); + offset += ehdr.e_shentsize; + + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_PROGBITS; + shdr.sh_name = 0xa000; + memory_.SetMemory(offset, &shdr, sizeof(shdr)); + offset += ehdr.e_shentsize; + + // The string data for the entries. + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_STRTAB; + shdr.sh_name = 0x20000; + shdr.sh_offset = 0xf000; + shdr.sh_size = 0x1000; + memory_.SetMemory(offset, &shdr, sizeof(shdr)); + offset += ehdr.e_shentsize; + + InitSym(0x5000, 0x90000, 0x1000, 0x100, 0xf000, "function_one"); + InitSym(0x6000, 0xd0000, 0x1000, 0x300, 0xf000, "function_two"); + + ASSERT_TRUE(elf->Init()); + EXPECT_EQ(0U, elf->debug_frame_offset()); + EXPECT_EQ(0U, elf->debug_frame_size()); + EXPECT_EQ(0U, elf->gnu_debugdata_offset()); + EXPECT_EQ(0U, elf->gnu_debugdata_size()); + + // Look in the first symbol table. + std::string name; + uint64_t name_offset; + ASSERT_TRUE(elf->GetFunctionName(0x90010, &name, &name_offset)); + EXPECT_EQ("function_one", name); + EXPECT_EQ(16U, name_offset); + ASSERT_TRUE(elf->GetFunctionName(0xd0020, &name, &name_offset)); + EXPECT_EQ("function_two", name); + EXPECT_EQ(32U, name_offset); +} + +TEST_F(ElfInterfaceTest, init_section_headers32) { + InitSectionHeaders(sizeof(Elf32_Shdr)); +} + +TEST_F(ElfInterfaceTest, init_section_headers64) { + InitSectionHeaders(sizeof(Elf64_Shdr)); +} + +TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size32) { + InitSectionHeaders(0x100); +} + +TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size64) { + InitSectionHeaders(0x100); +} + +template +void ElfInterfaceTest::InitSectionHeadersOffsets() { + std::unique_ptr elf(new ElfInterfaceType(&memory_)); + + uint64_t offset = 0x2000; + + Ehdr ehdr; + memset(&ehdr, 0, sizeof(ehdr)); + ehdr.e_shoff = offset; + ehdr.e_shnum = 10; + ehdr.e_shentsize = sizeof(Shdr); + ehdr.e_shstrndx = 2; + memory_.SetMemory(0, &ehdr, sizeof(ehdr)); + + offset += ehdr.e_shentsize; + + Shdr shdr; + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_PROGBITS; + shdr.sh_link = 2; + shdr.sh_name = 0x200; + shdr.sh_addr = 0x5000; + shdr.sh_offset = 0x5000; + shdr.sh_entsize = 0x100; + shdr.sh_size = 0x800; + memory_.SetMemory(offset, &shdr, sizeof(shdr)); + offset += ehdr.e_shentsize; + + // The string data for section header names. + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_STRTAB; + shdr.sh_name = 0x20000; + shdr.sh_offset = 0xf000; + shdr.sh_size = 0x1000; + memory_.SetMemory(offset, &shdr, sizeof(shdr)); + offset += ehdr.e_shentsize; + + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_PROGBITS; + shdr.sh_link = 2; + shdr.sh_name = 0x100; + shdr.sh_addr = 0x6000; + shdr.sh_offset = 0x6000; + shdr.sh_entsize = 0x100; + shdr.sh_size = 0x500; + memory_.SetMemory(offset, &shdr, sizeof(shdr)); + offset += ehdr.e_shentsize; + + memory_.SetMemory(0xf100, ".debug_frame", sizeof(".debug_frame")); + memory_.SetMemory(0xf200, ".gnu_debugdata", sizeof(".gnu_debugdata")); + + ASSERT_TRUE(elf->Init()); + EXPECT_EQ(0x6000U, elf->debug_frame_offset()); + EXPECT_EQ(0x500U, elf->debug_frame_size()); + EXPECT_EQ(0x5000U, elf->gnu_debugdata_offset()); + EXPECT_EQ(0x800U, elf->gnu_debugdata_size()); +} + +TEST_F(ElfInterfaceTest, init_section_headers_offsets32) { + InitSectionHeadersOffsets(); +} + +TEST_F(ElfInterfaceTest, init_section_headers_offsets64) { + InitSectionHeadersOffsets(); +}