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
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 @@
}
}
+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 @@
// 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::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*);