diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2017-06-25 07:41:31 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2017-06-25 07:41:31 +0000 |
commit | 53d6bf442e0e59a48445ef6d6572b29ea1de816d (patch) | |
tree | c4e13b762355ac9a356c0563f07ccca07b8f4ac0 | |
parent | fbf622a98cd343908f8303314dafba70534eebfb (diff) | |
parent | 002a64c374e8f9298189cc53a28e5492f0bd90ba (diff) | |
download | unwinding-53d6bf442e0e59a48445ef6d6572b29ea1de816d.tar.gz |
release-request-7bfcab52-d1c0-4256-9d6b-5b5092bc78ca-for-git_oc-mr1-release-4133428 snap-temp-L95800000077479875
Change-Id: Ie188da50603fd5ed5f59236c5164bf49c18f76be
-rw-r--r-- | libunwindstack/Elf.h | 4 | ||||
-rw-r--r-- | libunwindstack/ElfInterface.cpp | 108 | ||||
-rw-r--r-- | libunwindstack/ElfInterface.h | 16 | ||||
-rw-r--r-- | libunwindstack/tests/ElfInterfaceTest.cpp | 203 |
4 files changed, 311 insertions, 20 deletions
diff --git a/libunwindstack/Elf.h b/libunwindstack/Elf.h index 7bf45b8..f9db541 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 bfa7944..3a7f7cb 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 <typename AddressType> void ElfInterface::InitHeadersWithTemplate() { @@ -57,7 +66,13 @@ bool ElfInterface::ReadAllHeaders() { if (!ReadProgramHeaders<EhdrType, PhdrType>(ehdr)) { return false; } - return ReadSectionHeaders<EhdrType, ShdrType>(ehdr); + + // We could still potentially unwind without the section header + // information, so ignore any errors. + if (!ReadSectionHeaders<EhdrType, ShdrType>(ehdr)) { + log(0, "Malformed section header found, ignoring..."); + } + return true; } template <typename EhdrType, typename PhdrType> @@ -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 <typename SymType> +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<SymType>(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<Elf64_Ehdr, Elf64_Shdr>(const Elf template bool ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(std::string*); template bool ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(std::string*); + +template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, std::string*, + uint64_t*); +template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, std::string*, + uint64_t*); diff --git a/libunwindstack/ElfInterface.h b/libunwindstack/ElfInterface.h index 1cc8aa0..d0d0d28 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 <typename DynType> bool GetSonameWithTemplate(std::string* soname); + template <typename SymType> + 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<DwarfSection> eh_frame_; std::unique_ptr<DwarfSection> debug_frame_; + + std::vector<Symbols*> symbols_; }; class ElfInterface32 : public ElfInterface { @@ -135,8 +141,8 @@ class ElfInterface32 : public ElfInterface { return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(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<Elf32_Sym>(addr, name, func_offset); } }; @@ -155,8 +161,8 @@ class ElfInterface64 : public ElfInterface { return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(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<Elf64_Sym>(addr, name, func_offset); } }; diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp index 81cdaf5..0f56ba8 100644 --- a/libunwindstack/tests/ElfInterfaceTest.cpp +++ b/libunwindstack/tests/ElfInterfaceTest.cpp @@ -79,9 +79,37 @@ class ElfInterfaceTest : public ::testing::Test { template <typename ElfType> void InitHeadersDebugFrameFail(); + template <typename Ehdr, typename Shdr, typename ElfInterfaceType> + void InitSectionHeadersMalformed(); + + template <typename Ehdr, typename Shdr, typename Sym, typename ElfInterfaceType> + void InitSectionHeaders(uint64_t entry_size); + + template <typename Ehdr, typename Shdr, typename ElfInterfaceType> + void InitSectionHeadersOffsets(); + + template <typename Sym> + 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 <typename Sym> +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 <typename Ehdr, typename Phdr, typename Dyn, typename ElfInterfaceType> void ElfInterfaceTest::SinglePtLoad() { std::unique_ptr<ElfInterface> 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<MockElfInterface64>(); } + +template <typename Ehdr, typename Shdr, typename ElfInterfaceType> +void ElfInterfaceTest::InitSectionHeadersMalformed() { + std::unique_ptr<ElfInterfaceType> 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<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(); +} + +TEST_F(ElfInterfaceTest, init_section_headers_malformed64) { + InitSectionHeadersMalformed<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(); +} + +template <typename Ehdr, typename Shdr, typename Sym, typename ElfInterfaceType> +void ElfInterfaceTest::InitSectionHeaders(uint64_t entry_size) { + std::unique_ptr<ElfInterfaceType> 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<Sym>(0x5000, 0x90000, 0x1000, 0x100, 0xf000, "function_one"); + InitSym<Sym>(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<Elf32_Ehdr, Elf32_Shdr, Elf32_Sym, ElfInterface32>(sizeof(Elf32_Shdr)); +} + +TEST_F(ElfInterfaceTest, init_section_headers64) { + InitSectionHeaders<Elf64_Ehdr, Elf64_Shdr, Elf64_Sym, ElfInterface64>(sizeof(Elf64_Shdr)); +} + +TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size32) { + InitSectionHeaders<Elf32_Ehdr, Elf32_Shdr, Elf32_Sym, ElfInterface32>(0x100); +} + +TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size64) { + InitSectionHeaders<Elf64_Ehdr, Elf64_Shdr, Elf64_Sym, ElfInterface64>(0x100); +} + +template <typename Ehdr, typename Shdr, typename ElfInterfaceType> +void ElfInterfaceTest::InitSectionHeadersOffsets() { + std::unique_ptr<ElfInterfaceType> 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<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(); +} + +TEST_F(ElfInterfaceTest, init_section_headers_offsets64) { + InitSectionHeadersOffsets<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(); +} |