diff options
author | Florian Mayer <fmayer@google.com> | 2018-11-23 16:56:17 +0000 |
---|---|---|
committer | Florian Mayer <fmayer@google.com> | 2018-12-05 14:13:30 +0000 |
commit | 7cc81a4b75e409e468dfaab817f00c68135f5d3e (patch) | |
tree | 7949d7a620093ece53b0fa8d705f98bcf60cca51 | |
parent | 55d83530f1cbda48e15857c92892b5b2377462d4 (diff) | |
download | unwinding-7cc81a4b75e409e468dfaab817f00c68135f5d3e.tar.gz |
Read .note.gnu.build-id.
This will be used by heapprofd to allow us to correlate build ids with
memory leaks in libraries and binaries.
Test: m
Test: host libunwindstack_test
Test: run unwind_info against my phone's libc.so (32/64) and compare
to readelf Build ID output.
Bug: 120186412
Change-Id: I3cefd6cce9a8733509bf35b7175eb0f967783477
-rw-r--r-- | libunwindstack/Elf.cpp | 4 | ||||
-rw-r--r-- | libunwindstack/ElfInterface.cpp | 62 | ||||
-rw-r--r-- | libunwindstack/include/unwindstack/Elf.h | 2 | ||||
-rw-r--r-- | libunwindstack/include/unwindstack/ElfInterface.h | 18 | ||||
-rw-r--r-- | libunwindstack/tests/ElfFake.h | 3 | ||||
-rw-r--r-- | libunwindstack/tests/ElfInterfaceTest.cpp | 345 | ||||
-rw-r--r-- | libunwindstack/tests/ElfTest.cpp | 1 | ||||
-rw-r--r-- | libunwindstack/tools/unwind_info.cpp | 9 |
8 files changed, 443 insertions, 1 deletions
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp index 4d72ead..5b586a2 100644 --- a/libunwindstack/Elf.cpp +++ b/libunwindstack/Elf.cpp @@ -140,6 +140,10 @@ bool Elf::GetGlobalVariable(const std::string& name, uint64_t* memory_address) { return true; } +bool Elf::GetBuildID(std::string* build_id) { + return valid_ && interface_->GetBuildID(build_id); +} + void Elf::GetLastError(ErrorData* data) { if (valid_) { *data = interface_->last_error(); diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp index f59a472..d0af94a 100644 --- a/libunwindstack/ElfInterface.cpp +++ b/libunwindstack/ElfInterface.cpp @@ -237,6 +237,56 @@ void ElfInterface::ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias) } } +template <typename NhdrType> +bool ElfInterface::ReadBuildID(std::string* build_id) { + // Ensure there is no overflow in any of the calulations below. + uint64_t tmp; + if (__builtin_add_overflow(gnu_build_id_offset_, gnu_build_id_size_, &tmp)) { + return false; + } + + uint64_t offset = 0; + while (offset < gnu_build_id_size_) { + if (gnu_build_id_size_ - offset < sizeof(NhdrType)) { + return false; + } + NhdrType hdr; + if (!memory_->ReadFully(gnu_build_id_offset_ + offset, &hdr, sizeof(hdr))) { + return false; + } + offset += sizeof(hdr); + + if (gnu_build_id_size_ - offset < hdr.n_namesz) { + return false; + } + if (hdr.n_namesz > 0) { + std::string name(hdr.n_namesz, '\0'); + if (!memory_->ReadFully(gnu_build_id_offset_ + offset, &(name[0]), hdr.n_namesz)) { + return false; + } + + // Trim trailing \0 as GNU is stored as a C string in the ELF file. + if (name.back() == '\0') + name.resize(name.size() - 1); + + // Align hdr.n_namesz to next power multiple of 4. See man 5 elf. + offset += (hdr.n_namesz + 3) & ~3; + + if (name == "GNU" && hdr.n_type == NT_GNU_BUILD_ID) { + if (gnu_build_id_size_ - offset < hdr.n_descsz) { + return false; + } + build_id->resize(hdr.n_descsz); + return memory_->ReadFully(gnu_build_id_offset_ + offset, &(*build_id)[0], + hdr.n_descsz); + } + } + // Align hdr.n_descsz to next power multiple of 4. See man 5 elf. + offset += (hdr.n_descsz + 3) & ~3; + } + return false; +} + template <typename EhdrType, typename ShdrType> void ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) { uint64_t offset = ehdr.e_shoff; @@ -308,6 +358,15 @@ void ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) { // In order to read soname, keep track of address to offset mapping. strtabs_.push_back(std::make_pair<uint64_t, uint64_t>(static_cast<uint64_t>(shdr.sh_addr), static_cast<uint64_t>(shdr.sh_offset))); + } else if (shdr.sh_type == SHT_NOTE) { + if (shdr.sh_name < sec_size) { + std::string name; + if (memory_->ReadString(sec_offset + shdr.sh_name, &name) && + name == ".note.gnu.build-id") { + gnu_build_id_offset_ = shdr.sh_offset; + gnu_build_id_size_ = shdr.sh_size; + } + } } } } @@ -492,6 +551,9 @@ template void ElfInterface::ReadProgramHeaders<Elf64_Ehdr, Elf64_Phdr>(const Elf template void ElfInterface::ReadSectionHeaders<Elf32_Ehdr, Elf32_Shdr>(const Elf32_Ehdr&); template void ElfInterface::ReadSectionHeaders<Elf64_Ehdr, Elf64_Shdr>(const Elf64_Ehdr&); +template bool ElfInterface::ReadBuildID<Elf32_Nhdr>(std::string*); +template bool ElfInterface::ReadBuildID<Elf64_Nhdr>(std::string*); + template bool ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(std::string*); template bool ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(std::string*); diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h index e5b0a89..27f7201 100644 --- a/libunwindstack/include/unwindstack/Elf.h +++ b/libunwindstack/include/unwindstack/Elf.h @@ -65,6 +65,8 @@ class Elf { bool GetGlobalVariable(const std::string& name, uint64_t* memory_address); + bool GetBuildID(std::string* build_id); + uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info); bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, Regs* regs, Memory* process_memory, diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h index a45eba8..52992d9 100644 --- a/libunwindstack/include/unwindstack/ElfInterface.h +++ b/libunwindstack/include/unwindstack/ElfInterface.h @@ -62,6 +62,8 @@ class ElfInterface { virtual bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) = 0; + virtual bool GetBuildID(std::string* build_id) = 0; + virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished); virtual bool IsValidPc(uint64_t pc); @@ -85,6 +87,8 @@ class ElfInterface { uint64_t debug_frame_size() { return debug_frame_size_; } uint64_t gnu_debugdata_offset() { return gnu_debugdata_offset_; } uint64_t gnu_debugdata_size() { return gnu_debugdata_size_; } + uint64_t gnu_build_id_offset() { return gnu_build_id_offset_; } + uint64_t gnu_build_id_size() { return gnu_build_id_size_; } DwarfSection* eh_frame() { return eh_frame_.get(); } DwarfSection* debug_frame() { return debug_frame_.get(); } @@ -123,6 +127,9 @@ class ElfInterface { template <typename EhdrType> static void GetMaxSizeWithTemplate(Memory* memory, uint64_t* size); + template <typename NhdrType> + bool ReadBuildID(std::string* build_id); + Memory* memory_; std::unordered_map<uint64_t, LoadInfo> pt_loads_; @@ -143,6 +150,9 @@ class ElfInterface { uint64_t gnu_debugdata_offset_ = 0; uint64_t gnu_debugdata_size_ = 0; + uint64_t gnu_build_id_offset_ = 0; + uint64_t gnu_build_id_size_ = 0; + uint8_t soname_type_ = SONAME_UNKNOWN; std::string soname_; @@ -182,6 +192,10 @@ class ElfInterface32 : public ElfInterface { return ElfInterface::GetGlobalVariableWithTemplate<Elf32_Sym>(name, memory_address); } + bool GetBuildID(std::string* build_id) { + return ElfInterface::ReadBuildID<Elf32_Nhdr>(build_id); + } + static void GetMaxSize(Memory* memory, uint64_t* size) { GetMaxSizeWithTemplate<Elf32_Ehdr>(memory, size); } @@ -212,6 +226,10 @@ class ElfInterface64 : public ElfInterface { return ElfInterface::GetGlobalVariableWithTemplate<Elf64_Sym>(name, memory_address); } + bool GetBuildID(std::string* build_id) { + return ElfInterface::ReadBuildID<Elf64_Nhdr>(build_id); + } + static void GetMaxSize(Memory* memory, uint64_t* size) { GetMaxSizeWithTemplate<Elf64_Ehdr>(memory, size); } diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h index a3bf5ce..c2bd0f6 100644 --- a/libunwindstack/tests/ElfFake.h +++ b/libunwindstack/tests/ElfFake.h @@ -72,6 +72,9 @@ class ElfInterfaceFake : public ElfInterface { bool GetFunctionName(uint64_t, std::string*, uint64_t*) override; bool GetGlobalVariable(const std::string&, uint64_t*) override; + bool GetBuildID(std::string*) override { + return false; + } bool Step(uint64_t, Regs*, Memory*, bool*) override; diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp index 9326bff..6023dc4 100644 --- a/libunwindstack/tests/ElfInterfaceTest.cpp +++ b/libunwindstack/tests/ElfInterfaceTest.cpp @@ -116,6 +116,21 @@ class ElfInterfaceTest : public ::testing::Test { void InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset, uint64_t sym_offset, const char* name); + template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType> + void BuildID(); + + template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType> + void BuildIDTwoNotes(); + + template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType> + void BuildIDSectionTooSmallForName(); + + template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType> + void BuildIDSectionTooSmallForDesc(); + + template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType> + void BuildIDSectionTooSmallForHeader(); + MemoryFake memory_; }; @@ -898,7 +913,7 @@ void ElfInterfaceTest::InitSectionHeadersOffsets() { Ehdr ehdr = {}; ehdr.e_shoff = offset; - ehdr.e_shnum = 6; + ehdr.e_shnum = 7; ehdr.e_shentsize = sizeof(Shdr); ehdr.e_shstrndx = 2; memory_.SetMemory(0, &ehdr, sizeof(ehdr)); @@ -958,10 +973,19 @@ void ElfInterfaceTest::InitSectionHeadersOffsets() { memory_.SetMemory(offset, &shdr, sizeof(shdr)); offset += ehdr.e_shentsize; + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_NOTE; + shdr.sh_name = 0x500; + shdr.sh_offset = 0xb000; + shdr.sh_size = 0xf00; + 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")); memory_.SetMemory(0xf300, ".eh_frame", sizeof(".eh_frame")); memory_.SetMemory(0xf400, ".eh_frame_hdr", sizeof(".eh_frame_hdr")); + memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id")); uint64_t load_bias = 0; ASSERT_TRUE(elf->Init(&load_bias)); @@ -974,6 +998,8 @@ void ElfInterfaceTest::InitSectionHeadersOffsets() { EXPECT_EQ(0x800U, elf->eh_frame_size()); EXPECT_EQ(0xa000U, elf->eh_frame_hdr_offset()); EXPECT_EQ(0xf00U, elf->eh_frame_hdr_size()); + EXPECT_EQ(0xb000U, elf->gnu_build_id_offset()); + EXPECT_EQ(0xf00U, elf->gnu_build_id_size()); } TEST_F(ElfInterfaceTest, init_section_headers_offsets32) { @@ -1153,4 +1179,321 @@ TEST_F(ElfInterfaceTest, is_valid_pc_from_eh_frame) { EXPECT_FALSE(elf->IsValidPc(0x2a00)); } +template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType> +void ElfInterfaceTest::BuildID() { + std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_)); + + uint64_t offset = 0x2000; + + Ehdr ehdr = {}; + ehdr.e_shoff = offset; + ehdr.e_shnum = 3; + ehdr.e_shentsize = sizeof(Shdr); + ehdr.e_shstrndx = 2; + memory_.SetMemory(0, &ehdr, sizeof(ehdr)); + + offset += ehdr.e_shentsize; + + char note_section[128]; + Nhdr note_header = {}; + note_header.n_namesz = 4; // "GNU" + note_header.n_descsz = 8; // "BUILDID" + note_header.n_type = NT_GNU_BUILD_ID; + memcpy(¬e_section, ¬e_header, sizeof(note_header)); + size_t note_offset = sizeof(note_header); + memcpy(¬e_section[note_offset], "GNU", sizeof("GNU")); + note_offset += sizeof("GNU"); + memcpy(¬e_section[note_offset], "BUILDID", sizeof("BUILDID")); + note_offset += sizeof("BUILDID"); + + Shdr shdr = {}; + shdr.sh_type = SHT_NOTE; + shdr.sh_name = 0x500; + shdr.sh_offset = 0xb000; + shdr.sh_size = sizeof(note_section); + 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; + + memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id")); + memory_.SetMemory(0xb000, note_section, sizeof(note_section)); + + uint64_t load_bias = 0; + ASSERT_TRUE(elf->Init(&load_bias)); + std::string build_id; + ASSERT_TRUE(elf->GetBuildID(&build_id)); + EXPECT_STREQ(build_id.c_str(), "BUILDID"); +} + +template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType> +void ElfInterfaceTest::BuildIDTwoNotes() { + std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_)); + + uint64_t offset = 0x2000; + + Ehdr ehdr = {}; + ehdr.e_shoff = offset; + ehdr.e_shnum = 3; + ehdr.e_shentsize = sizeof(Shdr); + ehdr.e_shstrndx = 2; + memory_.SetMemory(0, &ehdr, sizeof(ehdr)); + + offset += ehdr.e_shentsize; + + char note_section[128]; + Nhdr note_header = {}; + note_header.n_namesz = 8; // "WRONG" aligned to 4 + note_header.n_descsz = 8; // "BUILDID" + note_header.n_type = NT_GNU_BUILD_ID; + memcpy(¬e_section, ¬e_header, sizeof(note_header)); + size_t note_offset = sizeof(note_header); + memcpy(¬e_section[note_offset], "WRONG", sizeof("WRONG")); + note_offset += 8; + memcpy(¬e_section[note_offset], "BUILDID", sizeof("BUILDID")); + note_offset += sizeof("BUILDID"); + + note_header.n_namesz = 4; // "GNU" + note_header.n_descsz = 8; // "BUILDID" + note_header.n_type = NT_GNU_BUILD_ID; + memcpy(¬e_section[note_offset], ¬e_header, sizeof(note_header)); + note_offset += sizeof(note_header); + memcpy(¬e_section[note_offset], "GNU", sizeof("GNU")); + note_offset += sizeof("GNU"); + memcpy(¬e_section[note_offset], "BUILDID", sizeof("BUILDID")); + note_offset += sizeof("BUILDID"); + + Shdr shdr = {}; + shdr.sh_type = SHT_NOTE; + shdr.sh_name = 0x500; + shdr.sh_offset = 0xb000; + shdr.sh_size = sizeof(note_section); + 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; + + memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id")); + memory_.SetMemory(0xb000, note_section, sizeof(note_section)); + + uint64_t load_bias = 0; + ASSERT_TRUE(elf->Init(&load_bias)); + std::string build_id; + ASSERT_TRUE(elf->GetBuildID(&build_id)); + EXPECT_STREQ(build_id.c_str(), "BUILDID"); +} + +template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType> +void ElfInterfaceTest::BuildIDSectionTooSmallForName () { + std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_)); + + uint64_t offset = 0x2000; + + Ehdr ehdr = {}; + ehdr.e_shoff = offset; + ehdr.e_shnum = 3; + ehdr.e_shentsize = sizeof(Shdr); + ehdr.e_shstrndx = 2; + memory_.SetMemory(0, &ehdr, sizeof(ehdr)); + + offset += ehdr.e_shentsize; + + char note_section[128]; + Nhdr note_header = {}; + note_header.n_namesz = 4; // "GNU" + note_header.n_descsz = 8; // "BUILDID" + note_header.n_type = NT_GNU_BUILD_ID; + memcpy(¬e_section, ¬e_header, sizeof(note_header)); + size_t note_offset = sizeof(note_header); + memcpy(¬e_section[note_offset], "GNU", sizeof("GNU")); + note_offset += sizeof("GNU"); + memcpy(¬e_section[note_offset], "BUILDID", sizeof("BUILDID")); + note_offset += sizeof("BUILDID"); + + Shdr shdr = {}; + shdr.sh_type = SHT_NOTE; + shdr.sh_name = 0x500; + shdr.sh_offset = 0xb000; + shdr.sh_size = sizeof(note_header) + 1; + 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; + + memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id")); + memory_.SetMemory(0xb000, note_section, sizeof(note_section)); + + uint64_t load_bias = 0; + ASSERT_TRUE(elf->Init(&load_bias)); + std::string build_id; + ASSERT_FALSE(elf->GetBuildID(&build_id)); +} + +template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType> +void ElfInterfaceTest::BuildIDSectionTooSmallForDesc () { + std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_)); + + uint64_t offset = 0x2000; + + Ehdr ehdr = {}; + ehdr.e_shoff = offset; + ehdr.e_shnum = 3; + ehdr.e_shentsize = sizeof(Shdr); + ehdr.e_shstrndx = 2; + memory_.SetMemory(0, &ehdr, sizeof(ehdr)); + + offset += ehdr.e_shentsize; + + char note_section[128]; + Nhdr note_header = {}; + note_header.n_namesz = 4; // "GNU" + note_header.n_descsz = 8; // "BUILDID" + note_header.n_type = NT_GNU_BUILD_ID; + memcpy(¬e_section, ¬e_header, sizeof(note_header)); + size_t note_offset = sizeof(note_header); + memcpy(¬e_section[note_offset], "GNU", sizeof("GNU")); + note_offset += sizeof("GNU"); + memcpy(¬e_section[note_offset], "BUILDID", sizeof("BUILDID")); + note_offset += sizeof("BUILDID"); + + Shdr shdr = {}; + shdr.sh_type = SHT_NOTE; + shdr.sh_name = 0x500; + shdr.sh_offset = 0xb000; + shdr.sh_size = sizeof(note_header) + sizeof("GNU") + 1; + 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; + + memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id")); + memory_.SetMemory(0xb000, note_section, sizeof(note_section)); + + uint64_t load_bias = 0; + ASSERT_TRUE(elf->Init(&load_bias)); + std::string build_id; + ASSERT_FALSE(elf->GetBuildID(&build_id)); +} + +template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType> +void ElfInterfaceTest::BuildIDSectionTooSmallForHeader () { + std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_)); + + uint64_t offset = 0x2000; + + Ehdr ehdr = {}; + ehdr.e_shoff = offset; + ehdr.e_shnum = 3; + ehdr.e_shentsize = sizeof(Shdr); + ehdr.e_shstrndx = 2; + memory_.SetMemory(0, &ehdr, sizeof(ehdr)); + + offset += ehdr.e_shentsize; + + char note_section[128]; + Nhdr note_header = {}; + note_header.n_namesz = 4; // "GNU" + note_header.n_descsz = 8; // "BUILDID" + note_header.n_type = NT_GNU_BUILD_ID; + memcpy(¬e_section, ¬e_header, sizeof(note_header)); + size_t note_offset = sizeof(note_header); + memcpy(¬e_section[note_offset], "GNU", sizeof("GNU")); + note_offset += sizeof("GNU"); + memcpy(¬e_section[note_offset], "BUILDID", sizeof("BUILDID")); + note_offset += sizeof("BUILDID"); + + Shdr shdr = {}; + shdr.sh_type = SHT_NOTE; + shdr.sh_name = 0x500; + shdr.sh_offset = 0xb000; + shdr.sh_size = sizeof(note_header) - 1; + 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; + + memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id")); + memory_.SetMemory(0xb000, note_section, sizeof(note_section)); + + uint64_t load_bias = 0; + ASSERT_TRUE(elf->Init(&load_bias)); + std::string build_id; + ASSERT_FALSE(elf->GetBuildID(&build_id)); +} + +TEST_F(ElfInterfaceTest, build_id32) { + BuildID<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>(); +} + +TEST_F(ElfInterfaceTest, build_id64) { + BuildID<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>(); +} + +TEST_F(ElfInterfaceTest, build_id_two_notes32) { + BuildIDTwoNotes<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>(); +} + +TEST_F(ElfInterfaceTest, build_id_two_notes64) { + BuildIDTwoNotes<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>(); +} + +TEST_F(ElfInterfaceTest, build_id_section_too_small_for_name32) { + BuildIDSectionTooSmallForName<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>(); +} + +TEST_F(ElfInterfaceTest, build_id_section_too_small_for_name64) { + BuildIDSectionTooSmallForName<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>(); +} + +TEST_F(ElfInterfaceTest, build_id_section_too_small_for_desc32) { + BuildIDSectionTooSmallForDesc<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>(); +} + +TEST_F(ElfInterfaceTest, build_id_section_too_small_for_desc64) { + BuildIDSectionTooSmallForDesc<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>(); +} + +TEST_F(ElfInterfaceTest, build_id_section_too_small_for_header32) { + BuildIDSectionTooSmallForHeader<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>(); +} + +TEST_F(ElfInterfaceTest, build_id_section_too_small_for_header64) { + BuildIDSectionTooSmallForHeader<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>(); +} + } // namespace unwindstack diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp index ccf8927..7766218 100644 --- a/libunwindstack/tests/ElfTest.cpp +++ b/libunwindstack/tests/ElfTest.cpp @@ -311,6 +311,7 @@ class ElfInterfaceMock : public ElfInterface { void InitHeaders(uint64_t) override {} bool GetSoname(std::string*) override { return false; } bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; } + bool GetBuildID(std::string*) override { return false; } MOCK_METHOD4(Step, bool(uint64_t, Regs*, Memory*, bool*)); MOCK_METHOD2(GetGlobalVariable, bool(const std::string&, uint64_t*)); diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp index aebeb95..3f2dfb0 100644 --- a/libunwindstack/tools/unwind_info.cpp +++ b/libunwindstack/tools/unwind_info.cpp @@ -123,6 +123,15 @@ int GetElfInfo(const char* file, uint64_t offset) { printf("Soname: %s\n", soname.c_str()); } + std::string build_id; + if (elf.GetBuildID(&build_id)) { + printf("Build ID: "); + for (size_t i = 0; i < build_id.size(); ++i) { + printf("%02hhx", build_id[i]); + } + printf("\n"); + } + ElfInterface* interface = elf.interface(); if (elf.machine_type() == EM_ARM) { DumpArm(&elf, reinterpret_cast<ElfInterfaceArm*>(interface)); |