diff options
author | Fangrui Song <maskray@google.com> | 2024-07-01 12:58:38 -0700 |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2024-07-01 12:59:40 -0700 |
commit | 1278ee9bd9bd4916181521fac96d6fa1100e38e6 (patch) | |
tree | b86d867fa96f2d23c89efb8da277cf8cef92b483 | |
parent | 37ebde53cfcf400ef01b59c80ae3f72039cd90f2 (diff) |
vdso_support: support DT_GNU_HASH
This library provides `LookupSymbol` and `LookupSymbolByAddress`. The latter
needs `GetNumSymbols` support. An object file needs either .hash (DT_HASH) or
.gnu.hash (DT_GNU_HASH). This patch adds DT_GNU_HASH support.
Note: glibc has been supporting DT_GNU_HASH since 2006 and .hash has been quite
obsoleted in the Linux communities.
PiperOrigin-RevId: 648459622
Change-Id: I3aa1274cd4617990844258175715e3be2343afd2
-rw-r--r-- | absl/debugging/internal/elf_mem_image.cc | 59 | ||||
-rw-r--r-- | absl/debugging/internal/elf_mem_image.h | 15 |
2 files changed, 51 insertions, 23 deletions
diff --git a/absl/debugging/internal/elf_mem_image.cc b/absl/debugging/internal/elf_mem_image.cc index 42dcd3cd..2c168309 100644 --- a/absl/debugging/internal/elf_mem_image.cc +++ b/absl/debugging/internal/elf_mem_image.cc @@ -20,8 +20,11 @@ #ifdef ABSL_HAVE_ELF_MEM_IMAGE // defined in elf_mem_image.h #include <string.h> + #include <cassert> #include <cstddef> +#include <cstdint> + #include "absl/base/config.h" #include "absl/base/internal/raw_logging.h" @@ -86,20 +89,14 @@ ElfMemImage::ElfMemImage(const void *base) { Init(base); } -int ElfMemImage::GetNumSymbols() const { - if (!hash_) { - return 0; - } - // See http://www.caldera.com/developers/gabi/latest/ch5.dynamic.html#hash - return static_cast<int>(hash_[1]); -} +uint32_t ElfMemImage::GetNumSymbols() const { return num_syms_; } -const ElfW(Sym) *ElfMemImage::GetDynsym(int index) const { +const ElfW(Sym) * ElfMemImage::GetDynsym(uint32_t index) const { ABSL_RAW_CHECK(index < GetNumSymbols(), "index out of range"); return dynsym_ + index; } -const ElfW(Versym) *ElfMemImage::GetVersym(int index) const { +const ElfW(Versym) *ElfMemImage::GetVersym(uint32_t index) const { ABSL_RAW_CHECK(index < GetNumSymbols(), "index out of range"); return versym_ + index; } @@ -154,7 +151,7 @@ void ElfMemImage::Init(const void *base) { dynstr_ = nullptr; versym_ = nullptr; verdef_ = nullptr; - hash_ = nullptr; + num_syms_ = 0; strsize_ = 0; verdefnum_ = 0; // Sentinel: PT_LOAD .p_vaddr can't possibly be this. @@ -219,12 +216,17 @@ void ElfMemImage::Init(const void *base) { base_as_char - reinterpret_cast<const char *>(link_base_); ElfW(Dyn)* dynamic_entry = reinterpret_cast<ElfW(Dyn)*>( static_cast<intptr_t>(dynamic_program_header->p_vaddr) + relocation); + uint32_t *sysv_hash = nullptr; + uint32_t *gnu_hash = nullptr; for (; dynamic_entry->d_tag != DT_NULL; ++dynamic_entry) { const auto value = static_cast<intptr_t>(dynamic_entry->d_un.d_val) + relocation; switch (dynamic_entry->d_tag) { case DT_HASH: - hash_ = reinterpret_cast<ElfW(Word) *>(value); + sysv_hash = reinterpret_cast<uint32_t *>(value); + break; + case DT_GNU_HASH: + gnu_hash = reinterpret_cast<uint32_t *>(value); break; case DT_SYMTAB: dynsym_ = reinterpret_cast<ElfW(Sym) *>(value); @@ -249,13 +251,38 @@ void ElfMemImage::Init(const void *base) { break; } } - if (!hash_ || !dynsym_ || !dynstr_ || !versym_ || + if ((!sysv_hash && !gnu_hash) || !dynsym_ || !dynstr_ || !versym_ || !verdef_ || !verdefnum_ || !strsize_) { assert(false); // invalid VDSO // Mark this image as not present. Can not recur infinitely. Init(nullptr); return; } + if (sysv_hash) { + num_syms_ = sysv_hash[1]; + } else { + assert(gnu_hash); + // Compute the number of symbols for DT_GNU_HASH, which is specified by + // https://sourceware.org/gnu-gabi/program-loading-and-dynamic-linking.txt + uint32_t nbuckets = gnu_hash[0]; + // The buckets array is located after the header (4 uint32) and the bloom + // filter (size_t array of gnu_hash[2] elements). + uint32_t *buckets = gnu_hash + 4 + sizeof(size_t) / 4 * gnu_hash[2]; + // Find the chain of the last non-empty bucket. + uint32_t idx = 0; + for (uint32_t i = nbuckets; i > 0;) { + idx = buckets[--i]; + if (idx != 0) break; + } + if (idx != 0) { + // Find the last element of the chain, which has an odd value. + // Add one to get the number of symbols. + uint32_t *chain = buckets + nbuckets - gnu_hash[1]; + while (chain[idx++] % 2 == 0) { + } + } + num_syms_ = idx; + } } bool ElfMemImage::LookupSymbol(const char *name, @@ -300,9 +327,9 @@ bool ElfMemImage::LookupSymbolByAddress(const void *address, return false; } -ElfMemImage::SymbolIterator::SymbolIterator(const void *const image, int index) - : index_(index), image_(image) { -} +ElfMemImage::SymbolIterator::SymbolIterator(const void *const image, + uint32_t index) + : index_(index), image_(image) {} const ElfMemImage::SymbolInfo *ElfMemImage::SymbolIterator::operator->() const { return &info_; @@ -335,7 +362,7 @@ ElfMemImage::SymbolIterator ElfMemImage::end() const { return SymbolIterator(this, GetNumSymbols()); } -void ElfMemImage::SymbolIterator::Update(int increment) { +void ElfMemImage::SymbolIterator::Update(uint32_t increment) { const ElfMemImage *image = reinterpret_cast<const ElfMemImage *>(image_); ABSL_RAW_CHECK(image->IsPresent() || increment == 0, ""); if (!image->IsPresent()) { diff --git a/absl/debugging/internal/elf_mem_image.h b/absl/debugging/internal/elf_mem_image.h index e7fe6ab0..19c4952e 100644 --- a/absl/debugging/internal/elf_mem_image.h +++ b/absl/debugging/internal/elf_mem_image.h @@ -22,6 +22,7 @@ // Including this will define the __GLIBC__ macro if glibc is being // used. #include <climits> +#include <cstdint> #include "absl/base/config.h" @@ -82,10 +83,10 @@ class ElfMemImage { bool operator!=(const SymbolIterator &rhs) const; bool operator==(const SymbolIterator &rhs) const; private: - SymbolIterator(const void *const image, int index); - void Update(int incr); + SymbolIterator(const void *const image, uint32_t index); + void Update(uint32_t incr); SymbolInfo info_; - int index_; + uint32_t index_; const void *const image_; }; @@ -94,14 +95,14 @@ class ElfMemImage { void Init(const void *base); bool IsPresent() const { return ehdr_ != nullptr; } const ElfW(Phdr)* GetPhdr(int index) const; - const ElfW(Sym)* GetDynsym(int index) const; - const ElfW(Versym)* GetVersym(int index) const; + const ElfW(Sym) * GetDynsym(uint32_t index) const; + const ElfW(Versym)* GetVersym(uint32_t index) const; const ElfW(Verdef)* GetVerdef(int index) const; const ElfW(Verdaux)* GetVerdefAux(const ElfW(Verdef) *verdef) const; const char* GetDynstr(ElfW(Word) offset) const; const void* GetSymAddr(const ElfW(Sym) *sym) const; const char* GetVerstr(ElfW(Word) offset) const; - int GetNumSymbols() const; + uint32_t GetNumSymbols() const; SymbolIterator begin() const; SymbolIterator end() const; @@ -124,8 +125,8 @@ class ElfMemImage { const ElfW(Sym) *dynsym_; const ElfW(Versym) *versym_; const ElfW(Verdef) *verdef_; - const ElfW(Word) *hash_; const char *dynstr_; + uint32_t num_syms_; size_t strsize_; size_t verdefnum_; ElfW(Addr) link_base_; // Link-time base (p_vaddr of first PT_LOAD). |