Add caching of build id in MapInfo object.
Change the GetBuildID function to return a std::string.
Added benchmark to check how long it takes to get the build id from
a file versus an elf object.
Added a way to get an elf without passing in a valid process_memory and
added tests for this.
Test: New unit tests.
Change-Id: I3029019767e0181c758d611fe635bc1bf72d6e8e
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 709e6c9..c1b98d9 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -238,31 +238,31 @@
}
template <typename NhdrType>
-bool ElfInterface::ReadBuildID(std::string* build_id) {
+std::string ElfInterface::ReadBuildID() {
// 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;
+ return "";
}
uint64_t offset = 0;
while (offset < gnu_build_id_size_) {
if (gnu_build_id_size_ - offset < sizeof(NhdrType)) {
- return false;
+ return "";
}
NhdrType hdr;
if (!memory_->ReadFully(gnu_build_id_offset_ + offset, &hdr, sizeof(hdr))) {
- return false;
+ return "";
}
offset += sizeof(hdr);
if (gnu_build_id_size_ - offset < hdr.n_namesz) {
- return false;
+ return "";
}
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;
+ return "";
}
// Trim trailing \0 as GNU is stored as a C string in the ELF file.
@@ -273,18 +273,20 @@
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;
+ if (gnu_build_id_size_ - offset < hdr.n_descsz || hdr.n_descsz == 0) {
+ return "";
}
- build_id->resize(hdr.n_descsz);
- return memory_->ReadFully(gnu_build_id_offset_ + offset, &(*build_id)[0],
- hdr.n_descsz);
+ std::string build_id(hdr.n_descsz - 1, '\0');
+ if (memory_->ReadFully(gnu_build_id_offset_ + offset, &build_id[0], hdr.n_descsz)) {
+ return build_id;
+ }
+ return "";
}
}
// Align hdr.n_descsz to next power multiple of 4. See man 5 elf.
offset += (hdr.n_descsz + 3) & ~3;
}
- return false;
+ return "";
}
template <typename EhdrType, typename ShdrType>
@@ -536,6 +538,103 @@
*size = ehdr.e_shoff + ehdr.e_shentsize * ehdr.e_shnum;
}
+template <typename EhdrType, typename ShdrType>
+bool GetBuildIDInfo(Memory* memory, uint64_t* build_id_offset, uint64_t* build_id_size) {
+ EhdrType ehdr;
+ if (!memory->ReadFully(0, &ehdr, sizeof(ehdr))) {
+ return false;
+ }
+
+ uint64_t offset = ehdr.e_shoff;
+ uint64_t sec_offset;
+ uint64_t sec_size;
+ ShdrType shdr;
+ if (ehdr.e_shstrndx >= ehdr.e_shnum) {
+ return false;
+ }
+
+ uint64_t sh_offset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
+ if (!memory->ReadFully(sh_offset, &shdr, sizeof(shdr))) {
+ return false;
+ }
+ sec_offset = shdr.sh_offset;
+ sec_size = shdr.sh_size;
+
+ // 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->ReadFully(offset, &shdr, sizeof(shdr))) {
+ return false;
+ }
+ std::string name;
+ if (shdr.sh_type == SHT_NOTE && shdr.sh_name < sec_size &&
+ memory->ReadString(sec_offset + shdr.sh_name, &name) && name == ".note.gnu.build-id") {
+ *build_id_offset = shdr.sh_offset;
+ *build_id_size = shdr.sh_size;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+template <typename EhdrType, typename ShdrType, typename NhdrType>
+std::string ElfInterface::ReadBuildIDFromMemory(Memory* memory) {
+ uint64_t note_offset;
+ uint64_t note_size;
+ if (!GetBuildIDInfo<EhdrType, ShdrType>(memory, ¬e_offset, ¬e_size)) {
+ return "";
+ }
+
+ // Ensure there is no overflow in any of the calculations below.
+ uint64_t tmp;
+ if (__builtin_add_overflow(note_offset, note_size, &tmp)) {
+ return "";
+ }
+
+ uint64_t offset = 0;
+ while (offset < note_size) {
+ if (note_size - offset < sizeof(NhdrType)) {
+ return "";
+ }
+ NhdrType hdr;
+ if (!memory->ReadFully(note_offset + offset, &hdr, sizeof(hdr))) {
+ return "";
+ }
+ offset += sizeof(hdr);
+
+ if (note_size - offset < hdr.n_namesz) {
+ return "";
+ }
+ if (hdr.n_namesz > 0) {
+ std::string name(hdr.n_namesz, '\0');
+ if (!memory->ReadFully(note_offset + offset, &(name[0]), hdr.n_namesz)) {
+ return "";
+ }
+
+ // 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 (note_size - offset < hdr.n_descsz || hdr.n_descsz == 0) {
+ return "";
+ }
+ std::string build_id(hdr.n_descsz - 1, '\0');
+ if (memory->ReadFully(note_offset + offset, &build_id[0], hdr.n_descsz)) {
+ return build_id;
+ }
+ return "";
+ }
+ }
+ // Align hdr.n_descsz to next power multiple of 4. See man 5 elf.
+ offset += (hdr.n_descsz + 3) & ~3;
+ }
+ return "";
+}
+
// Instantiate all of the needed template functions.
template void ElfInterface::InitHeadersWithTemplate<uint32_t>(uint64_t);
template void ElfInterface::InitHeadersWithTemplate<uint64_t>(uint64_t);
@@ -551,8 +650,8 @@
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 std::string ElfInterface::ReadBuildID<Elf32_Nhdr>();
+template std::string ElfInterface::ReadBuildID<Elf64_Nhdr>();
template bool ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(std::string*);
template bool ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(std::string*);
@@ -571,4 +670,9 @@
template uint64_t ElfInterface::GetLoadBias<Elf32_Ehdr, Elf32_Phdr>(Memory*);
template uint64_t ElfInterface::GetLoadBias<Elf64_Ehdr, Elf64_Phdr>(Memory*);
+template std::string ElfInterface::ReadBuildIDFromMemory<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr>(
+ Memory*);
+template std::string ElfInterface::ReadBuildIDFromMemory<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr>(
+ Memory*);
+
} // namespace unwindstack