Commit 1278ee9b by Fangrui Song Committed by Copybara-Service

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
parent 37ebde53
...@@ -20,8 +20,11 @@ ...@@ -20,8 +20,11 @@
#ifdef ABSL_HAVE_ELF_MEM_IMAGE // defined in elf_mem_image.h #ifdef ABSL_HAVE_ELF_MEM_IMAGE // defined in elf_mem_image.h
#include <string.h> #include <string.h>
#include <cassert> #include <cassert>
#include <cstddef> #include <cstddef>
#include <cstdint>
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/base/internal/raw_logging.h" #include "absl/base/internal/raw_logging.h"
...@@ -86,20 +89,14 @@ ElfMemImage::ElfMemImage(const void *base) { ...@@ -86,20 +89,14 @@ ElfMemImage::ElfMemImage(const void *base) {
Init(base); Init(base);
} }
int ElfMemImage::GetNumSymbols() const { uint32_t ElfMemImage::GetNumSymbols() const { return num_syms_; }
if (!hash_) {
return 0;
}
// See http://www.caldera.com/developers/gabi/latest/ch5.dynamic.html#hash
return static_cast<int>(hash_[1]);
}
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"); ABSL_RAW_CHECK(index < GetNumSymbols(), "index out of range");
return dynsym_ + index; 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"); ABSL_RAW_CHECK(index < GetNumSymbols(), "index out of range");
return versym_ + index; return versym_ + index;
} }
...@@ -154,7 +151,7 @@ void ElfMemImage::Init(const void *base) { ...@@ -154,7 +151,7 @@ void ElfMemImage::Init(const void *base) {
dynstr_ = nullptr; dynstr_ = nullptr;
versym_ = nullptr; versym_ = nullptr;
verdef_ = nullptr; verdef_ = nullptr;
hash_ = nullptr; num_syms_ = 0;
strsize_ = 0; strsize_ = 0;
verdefnum_ = 0; verdefnum_ = 0;
// Sentinel: PT_LOAD .p_vaddr can't possibly be this. // Sentinel: PT_LOAD .p_vaddr can't possibly be this.
...@@ -219,12 +216,17 @@ void ElfMemImage::Init(const void *base) { ...@@ -219,12 +216,17 @@ void ElfMemImage::Init(const void *base) {
base_as_char - reinterpret_cast<const char *>(link_base_); base_as_char - reinterpret_cast<const char *>(link_base_);
ElfW(Dyn)* dynamic_entry = reinterpret_cast<ElfW(Dyn)*>( ElfW(Dyn)* dynamic_entry = reinterpret_cast<ElfW(Dyn)*>(
static_cast<intptr_t>(dynamic_program_header->p_vaddr) + relocation); 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) { for (; dynamic_entry->d_tag != DT_NULL; ++dynamic_entry) {
const auto value = const auto value =
static_cast<intptr_t>(dynamic_entry->d_un.d_val) + relocation; static_cast<intptr_t>(dynamic_entry->d_un.d_val) + relocation;
switch (dynamic_entry->d_tag) { switch (dynamic_entry->d_tag) {
case DT_HASH: 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; break;
case DT_SYMTAB: case DT_SYMTAB:
dynsym_ = reinterpret_cast<ElfW(Sym) *>(value); dynsym_ = reinterpret_cast<ElfW(Sym) *>(value);
...@@ -249,13 +251,38 @@ void ElfMemImage::Init(const void *base) { ...@@ -249,13 +251,38 @@ void ElfMemImage::Init(const void *base) {
break; break;
} }
} }
if (!hash_ || !dynsym_ || !dynstr_ || !versym_ || if ((!sysv_hash && !gnu_hash) || !dynsym_ || !dynstr_ || !versym_ ||
!verdef_ || !verdefnum_ || !strsize_) { !verdef_ || !verdefnum_ || !strsize_) {
assert(false); // invalid VDSO assert(false); // invalid VDSO
// Mark this image as not present. Can not recur infinitely. // Mark this image as not present. Can not recur infinitely.
Init(nullptr); Init(nullptr);
return; 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, bool ElfMemImage::LookupSymbol(const char *name,
...@@ -300,9 +327,9 @@ bool ElfMemImage::LookupSymbolByAddress(const void *address, ...@@ -300,9 +327,9 @@ bool ElfMemImage::LookupSymbolByAddress(const void *address,
return false; return false;
} }
ElfMemImage::SymbolIterator::SymbolIterator(const void *const image, int index) ElfMemImage::SymbolIterator::SymbolIterator(const void *const image,
: index_(index), image_(image) { uint32_t index)
} : index_(index), image_(image) {}
const ElfMemImage::SymbolInfo *ElfMemImage::SymbolIterator::operator->() const { const ElfMemImage::SymbolInfo *ElfMemImage::SymbolIterator::operator->() const {
return &info_; return &info_;
...@@ -335,7 +362,7 @@ ElfMemImage::SymbolIterator ElfMemImage::end() const { ...@@ -335,7 +362,7 @@ ElfMemImage::SymbolIterator ElfMemImage::end() const {
return SymbolIterator(this, GetNumSymbols()); 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_); const ElfMemImage *image = reinterpret_cast<const ElfMemImage *>(image_);
ABSL_RAW_CHECK(image->IsPresent() || increment == 0, ""); ABSL_RAW_CHECK(image->IsPresent() || increment == 0, "");
if (!image->IsPresent()) { if (!image->IsPresent()) {
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
// Including this will define the __GLIBC__ macro if glibc is being // Including this will define the __GLIBC__ macro if glibc is being
// used. // used.
#include <climits> #include <climits>
#include <cstdint>
#include "absl/base/config.h" #include "absl/base/config.h"
...@@ -82,10 +83,10 @@ class ElfMemImage { ...@@ -82,10 +83,10 @@ class ElfMemImage {
bool operator!=(const SymbolIterator &rhs) const; bool operator!=(const SymbolIterator &rhs) const;
bool operator==(const SymbolIterator &rhs) const; bool operator==(const SymbolIterator &rhs) const;
private: private:
SymbolIterator(const void *const image, int index); SymbolIterator(const void *const image, uint32_t index);
void Update(int incr); void Update(uint32_t incr);
SymbolInfo info_; SymbolInfo info_;
int index_; uint32_t index_;
const void *const image_; const void *const image_;
}; };
...@@ -94,14 +95,14 @@ class ElfMemImage { ...@@ -94,14 +95,14 @@ class ElfMemImage {
void Init(const void *base); void Init(const void *base);
bool IsPresent() const { return ehdr_ != nullptr; } bool IsPresent() const { return ehdr_ != nullptr; }
const ElfW(Phdr)* GetPhdr(int index) const; const ElfW(Phdr)* GetPhdr(int index) const;
const ElfW(Sym)* GetDynsym(int index) const; const ElfW(Sym) * GetDynsym(uint32_t index) const;
const ElfW(Versym)* GetVersym(int index) const; const ElfW(Versym)* GetVersym(uint32_t index) const;
const ElfW(Verdef)* GetVerdef(int index) const; const ElfW(Verdef)* GetVerdef(int index) const;
const ElfW(Verdaux)* GetVerdefAux(const ElfW(Verdef) *verdef) const; const ElfW(Verdaux)* GetVerdefAux(const ElfW(Verdef) *verdef) const;
const char* GetDynstr(ElfW(Word) offset) const; const char* GetDynstr(ElfW(Word) offset) const;
const void* GetSymAddr(const ElfW(Sym) *sym) const; const void* GetSymAddr(const ElfW(Sym) *sym) const;
const char* GetVerstr(ElfW(Word) offset) const; const char* GetVerstr(ElfW(Word) offset) const;
int GetNumSymbols() const; uint32_t GetNumSymbols() const;
SymbolIterator begin() const; SymbolIterator begin() const;
SymbolIterator end() const; SymbolIterator end() const;
...@@ -124,8 +125,8 @@ class ElfMemImage { ...@@ -124,8 +125,8 @@ class ElfMemImage {
const ElfW(Sym) *dynsym_; const ElfW(Sym) *dynsym_;
const ElfW(Versym) *versym_; const ElfW(Versym) *versym_;
const ElfW(Verdef) *verdef_; const ElfW(Verdef) *verdef_;
const ElfW(Word) *hash_;
const char *dynstr_; const char *dynstr_;
uint32_t num_syms_;
size_t strsize_; size_t strsize_;
size_t verdefnum_; size_t verdefnum_;
ElfW(Addr) link_base_; // Link-time base (p_vaddr of first PT_LOAD). ElfW(Addr) link_base_; // Link-time base (p_vaddr of first PT_LOAD).
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment