From beae42bc7f8fb6494138ff8a6d17ed6a65b2cf9a Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Thu, 15 Feb 2018 17:36:33 -0800 Subject: [PATCH] Fix soname reading code. The dynamic section contained an address, not an offset into the elf file to indicate where the soname exists. Changed to use the strtab entries in the section headers to map this address to the actual offset. Refactor the soname test a bit to make it easier to verify the code. Bug: 73499044 Test: Passes new unit tests. Test: Ran unwind_info on the failing shared elf and verified the soinfo Test: is correct. Change-Id: I16ba148389bcb9aadd3566fb442dac27f89fe894 --- libunwindstack/ElfInterface.cpp | 71 +++---- .../include/unwindstack/ElfInterface.h | 1 + libunwindstack/tests/ElfInterfaceTest.cpp | 179 +++++++++--------- libunwindstack/tools/unwind_info.cpp | 5 + libunwindstack/tools/unwind_reg_info.cpp | 5 + libunwindstack/tools/unwind_symbols.cpp | 5 + 6 files changed, 130 insertions(+), 136 deletions(-) diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp index e41308171..10afe33be 100644 --- a/libunwindstack/ElfInterface.cpp +++ b/libunwindstack/ElfInterface.cpp @@ -19,6 +19,7 @@ #include #include +#include #include <7zCrc.h> #include @@ -322,19 +323,13 @@ 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))) { + if (!memory_->Read(offset, &shdr, sizeof(shdr))) { last_error_.code = ERROR_MEMORY_INVALID; - last_error_.address = - offset + reinterpret_cast(&shdr.sh_type) - reinterpret_cast(&shdr); + last_error_.address = offset; return false; } if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) { - if (!memory_->ReadFully(offset, &shdr, sizeof(shdr))) { - last_error_.code = ERROR_MEMORY_INVALID; - last_error_.address = offset; - return false; - } // Need to go get the information about the section that contains // the string terminated names. ShdrType str_shdr; @@ -343,39 +338,19 @@ bool ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) { 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))) { + if (!memory_->Read(str_offset, &str_shdr, sizeof(str_shdr))) { last_error_.code = ERROR_MEMORY_INVALID; - last_error_.address = str_offset + reinterpret_cast(&str_shdr.sh_type) - - reinterpret_cast(&str_shdr); + last_error_.address = str_offset; return false; } if (str_shdr.sh_type != SHT_STRTAB) { last_error_.code = ERROR_UNWIND_INFO; return false; } - if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_offset, - sizeof(str_shdr.sh_offset))) { - last_error_.code = ERROR_MEMORY_INVALID; - last_error_.address = str_offset + reinterpret_cast(&str_shdr.sh_offset) - - reinterpret_cast(&str_shdr); - return false; - } - if (!memory_->ReadField(str_offset, &str_shdr, &str_shdr.sh_size, sizeof(str_shdr.sh_size))) { - last_error_.code = ERROR_MEMORY_INVALID; - last_error_.address = str_offset + reinterpret_cast(&str_shdr.sh_size) - - reinterpret_cast(&str_shdr); - 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))) { - last_error_.code = ERROR_MEMORY_INVALID; - last_error_.address = offset + reinterpret_cast(&shdr.sh_name) - - reinterpret_cast(&shdr); - return false; - } if (shdr.sh_name < sec_size) { std::string name; if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) { @@ -394,14 +369,16 @@ bool ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) { offset_ptr = &eh_frame_hdr_offset_; size_ptr = &eh_frame_hdr_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))) { + if (offset_ptr != nullptr) { *offset_ptr = shdr.sh_offset; *size_ptr = shdr.sh_size; } } } + } else if (shdr.sh_type == SHT_STRTAB) { + // In order to read soname, keep track of address to offset mapping. + strtabs_.push_back(std::make_pair(static_cast(shdr.sh_addr), + static_cast(shdr.sh_offset))); } } return true; @@ -420,7 +397,7 @@ bool ElfInterface::GetSonameWithTemplate(std::string* soname) { soname_type_ = SONAME_INVALID; uint64_t soname_offset = 0; - uint64_t strtab_offset = 0; + uint64_t strtab_addr = 0; uint64_t strtab_size = 0; // Find the soname location from the dynamic headers section. @@ -435,7 +412,7 @@ bool ElfInterface::GetSonameWithTemplate(std::string* soname) { } if (dyn.d_tag == DT_STRTAB) { - strtab_offset = dyn.d_un.d_ptr; + strtab_addr = dyn.d_un.d_ptr; } else if (dyn.d_tag == DT_STRSZ) { strtab_size = dyn.d_un.d_val; } else if (dyn.d_tag == DT_SONAME) { @@ -445,16 +422,22 @@ bool ElfInterface::GetSonameWithTemplate(std::string* soname) { } } - soname_offset += strtab_offset; - if (soname_offset >= strtab_offset + strtab_size) { - return false; + // Need to map the strtab address to the real offset. + for (const auto& entry : strtabs_) { + if (entry.first == strtab_addr) { + soname_offset = entry.second + soname_offset; + if (soname_offset >= entry.second + strtab_size) { + return false; + } + if (!memory_->ReadString(soname_offset, &soname_)) { + return false; + } + soname_type_ = SONAME_VALID; + *soname = soname_; + return true; + } } - if (!memory_->ReadString(soname_offset, &soname_)) { - return false; - } - soname_type_ = SONAME_VALID; - *soname = soname_; - return true; + return false; } template diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h index ea9ec9dac..3a221bc2e 100644 --- a/libunwindstack/include/unwindstack/ElfInterface.h +++ b/libunwindstack/include/unwindstack/ElfInterface.h @@ -157,6 +157,7 @@ class ElfInterface { ElfInterface* gnu_debugdata_interface_ = nullptr; std::vector symbols_; + std::vector> strtabs_; }; class ElfInterface32 : public ElfInterface { diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp index 042c5fb3b..bf97e30bf 100644 --- a/libunwindstack/tests/ElfInterfaceTest.cpp +++ b/libunwindstack/tests/ElfInterfaceTest.cpp @@ -63,15 +63,28 @@ class ElfInterfaceTest : public ::testing::Test { template void ManyPhdrs(); - template + enum SonameTestEnum : uint8_t { + SONAME_NORMAL, + SONAME_DTNULL_AFTER, + SONAME_DTSIZE_SMALL, + SONAME_MISSING_MAP, + }; + + template + void SonameInit(SonameTestEnum test_type = SONAME_NORMAL); + + template void Soname(); - template + template void SonameAfterDtNull(); - template + template void SonameSize(); + template + void SonameMissingMap(); + template void InitHeadersEhFrameTest(); @@ -465,17 +478,29 @@ TEST_F(ElfInterfaceTest, elf32_arm) { ASSERT_EQ(2U, elf_arm.total_entries()); } -template -void ElfInterfaceTest::Soname() { - std::unique_ptr elf(new ElfInterfaceType(&memory_)); - +template +void ElfInterfaceTest::SonameInit(SonameTestEnum test_type) { Ehdr ehdr; memset(&ehdr, 0, sizeof(ehdr)); + ehdr.e_shoff = 0x200; + ehdr.e_shnum = 2; + ehdr.e_shentsize = sizeof(Shdr); ehdr.e_phoff = 0x100; ehdr.e_phnum = 1; ehdr.e_phentsize = sizeof(Phdr); memory_.SetMemory(0, &ehdr, sizeof(ehdr)); + Shdr shdr; + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_STRTAB; + if (test_type == SONAME_MISSING_MAP) { + shdr.sh_addr = 0x20100; + } else { + shdr.sh_addr = 0x10100; + } + shdr.sh_offset = 0x10000; + memory_.SetMemory(0x200 + sizeof(shdr), &shdr, sizeof(shdr)); + Phdr phdr; memset(&phdr, 0, sizeof(phdr)); phdr.p_type = PT_DYNAMIC; @@ -487,15 +512,25 @@ void ElfInterfaceTest::Soname() { Dyn dyn; dyn.d_tag = DT_STRTAB; - dyn.d_un.d_ptr = 0x10000; + dyn.d_un.d_ptr = 0x10100; memory_.SetMemory(offset, &dyn, sizeof(dyn)); offset += sizeof(dyn); dyn.d_tag = DT_STRSZ; - dyn.d_un.d_val = 0x1000; + if (test_type == SONAME_DTSIZE_SMALL) { + dyn.d_un.d_val = 0x10; + } else { + dyn.d_un.d_val = 0x1000; + } memory_.SetMemory(offset, &dyn, sizeof(dyn)); offset += sizeof(dyn); + if (test_type == SONAME_DTNULL_AFTER) { + dyn.d_tag = DT_NULL; + memory_.SetMemory(offset, &dyn, sizeof(dyn)); + offset += sizeof(dyn); + } + dyn.d_tag = DT_SONAME; dyn.d_un.d_val = 0x10; memory_.SetMemory(offset, &dyn, sizeof(dyn)); @@ -505,6 +540,11 @@ void ElfInterfaceTest::Soname() { memory_.SetMemory(offset, &dyn, sizeof(dyn)); SetStringMemory(0x10010, "fake_soname.so"); +} + +template +void ElfInterfaceTest::Soname() { + std::unique_ptr elf(new ElfInterfaceType(&memory_)); uint64_t load_bias = 0; ASSERT_TRUE(elf->Init(&load_bias)); @@ -516,55 +556,19 @@ void ElfInterfaceTest::Soname() { } TEST_F(ElfInterfaceTest, elf32_soname) { - Soname(); + SonameInit(); + Soname(); } TEST_F(ElfInterfaceTest, elf64_soname) { - Soname(); + SonameInit(); + Soname(); } -template +template void ElfInterfaceTest::SonameAfterDtNull() { std::unique_ptr elf(new ElfInterfaceType(&memory_)); - Ehdr ehdr; - memset(&ehdr, 0, sizeof(ehdr)); - ehdr.e_phoff = 0x100; - ehdr.e_phnum = 1; - ehdr.e_phentsize = sizeof(Phdr); - memory_.SetMemory(0, &ehdr, sizeof(ehdr)); - - Phdr phdr; - memset(&phdr, 0, sizeof(phdr)); - phdr.p_type = PT_DYNAMIC; - phdr.p_offset = 0x2000; - phdr.p_memsz = sizeof(Dyn) * 3; - memory_.SetMemory(0x100, &phdr, sizeof(phdr)); - - Dyn dyn; - uint64_t offset = 0x2000; - - dyn.d_tag = DT_STRTAB; - dyn.d_un.d_ptr = 0x10000; - memory_.SetMemory(offset, &dyn, sizeof(dyn)); - offset += sizeof(dyn); - - dyn.d_tag = DT_STRSZ; - dyn.d_un.d_val = 0x1000; - memory_.SetMemory(offset, &dyn, sizeof(dyn)); - offset += sizeof(dyn); - - dyn.d_tag = DT_NULL; - memory_.SetMemory(offset, &dyn, sizeof(dyn)); - offset += sizeof(dyn); - - dyn.d_tag = DT_SONAME; - dyn.d_un.d_val = 0x10; - memory_.SetMemory(offset, &dyn, sizeof(dyn)); - offset += sizeof(dyn); - - SetStringMemory(0x10010, "fake_soname.so"); - uint64_t load_bias = 0; ASSERT_TRUE(elf->Init(&load_bias)); EXPECT_EQ(0U, load_bias); @@ -574,54 +578,19 @@ void ElfInterfaceTest::SonameAfterDtNull() { } TEST_F(ElfInterfaceTest, elf32_soname_after_dt_null) { - SonameAfterDtNull(); + SonameInit(SONAME_DTNULL_AFTER); + SonameAfterDtNull(); } TEST_F(ElfInterfaceTest, elf64_soname_after_dt_null) { - SonameAfterDtNull(); + SonameInit(SONAME_DTNULL_AFTER); + SonameAfterDtNull(); } -template +template void ElfInterfaceTest::SonameSize() { std::unique_ptr elf(new ElfInterfaceType(&memory_)); - Ehdr ehdr; - memset(&ehdr, 0, sizeof(ehdr)); - ehdr.e_phoff = 0x100; - ehdr.e_phnum = 1; - ehdr.e_phentsize = sizeof(Phdr); - memory_.SetMemory(0, &ehdr, sizeof(ehdr)); - - Phdr phdr; - memset(&phdr, 0, sizeof(phdr)); - phdr.p_type = PT_DYNAMIC; - phdr.p_offset = 0x2000; - phdr.p_memsz = sizeof(Dyn); - memory_.SetMemory(0x100, &phdr, sizeof(phdr)); - - Dyn dyn; - uint64_t offset = 0x2000; - - dyn.d_tag = DT_STRTAB; - dyn.d_un.d_ptr = 0x10000; - memory_.SetMemory(offset, &dyn, sizeof(dyn)); - offset += sizeof(dyn); - - dyn.d_tag = DT_STRSZ; - dyn.d_un.d_val = 0x10; - memory_.SetMemory(offset, &dyn, sizeof(dyn)); - offset += sizeof(dyn); - - dyn.d_tag = DT_SONAME; - dyn.d_un.d_val = 0x10; - memory_.SetMemory(offset, &dyn, sizeof(dyn)); - offset += sizeof(dyn); - - dyn.d_tag = DT_NULL; - memory_.SetMemory(offset, &dyn, sizeof(dyn)); - - SetStringMemory(0x10010, "fake_soname.so"); - uint64_t load_bias = 0; ASSERT_TRUE(elf->Init(&load_bias)); EXPECT_EQ(0U, load_bias); @@ -631,11 +600,37 @@ void ElfInterfaceTest::SonameSize() { } TEST_F(ElfInterfaceTest, elf32_soname_size) { - SonameSize(); + SonameInit(SONAME_DTSIZE_SMALL); + SonameSize(); } TEST_F(ElfInterfaceTest, elf64_soname_size) { - SonameSize(); + SonameInit(SONAME_DTSIZE_SMALL); + SonameSize(); +} + +// Verify that there is no map from STRTAB in the dynamic section to a +// STRTAB entry in the section headers. +template +void ElfInterfaceTest::SonameMissingMap() { + std::unique_ptr elf(new ElfInterfaceType(&memory_)); + + uint64_t load_bias = 0; + ASSERT_TRUE(elf->Init(&load_bias)); + EXPECT_EQ(0U, load_bias); + + std::string name; + ASSERT_FALSE(elf->GetSoname(&name)); +} + +TEST_F(ElfInterfaceTest, elf32_soname_missing_map) { + SonameInit(SONAME_MISSING_MAP); + SonameMissingMap(); +} + +TEST_F(ElfInterfaceTest, elf64_soname_missing_map) { + SonameInit(SONAME_MISSING_MAP); + SonameMissingMap(); } template diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp index 7f2d11d67..a0abccae2 100644 --- a/libunwindstack/tools/unwind_info.cpp +++ b/libunwindstack/tools/unwind_info.cpp @@ -120,6 +120,11 @@ int GetElfInfo(const char* file, uint64_t offset) { return 1; } + std::string soname; + if (elf.GetSoname(&soname)) { + printf("Soname: %s\n", soname.c_str()); + } + ElfInterface* interface = elf.interface(); if (elf.machine_type() == EM_ARM) { DumpArm(reinterpret_cast(interface)); diff --git a/libunwindstack/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp index 4d890879f..47a4f912d 100644 --- a/libunwindstack/tools/unwind_reg_info.cpp +++ b/libunwindstack/tools/unwind_reg_info.cpp @@ -157,6 +157,11 @@ int GetInfo(const char* file, uint64_t pc) { return 1; } + std::string soname; + if (elf.GetSoname(&soname)) { + printf("Soname: %s\n\n", soname.c_str()); + } + printf("PC 0x%" PRIx64 ":\n", pc); DwarfSection* section = interface->eh_frame(); diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp index 697e4cd9e..086dffef3 100644 --- a/libunwindstack/tools/unwind_symbols.cpp +++ b/libunwindstack/tools/unwind_symbols.cpp @@ -71,6 +71,11 @@ int main(int argc, char** argv) { return 1; } + std::string soname; + if (elf.GetSoname(&soname)) { + printf("Soname: %s\n\n", soname.c_str()); + } + switch (elf.machine_type()) { case EM_ARM: printf("ABI: arm\n");