mirror of
https://review.haiku-os.org/haiku
synced 2024-11-23 07:18:40 +01:00
runtime_loader: Add support for DT_GNU_HASH.
This is an alternative to DT_HASH (SystemV/SVR4 hash tables.) Notably, it uses a Bloom filter to allow an entire image to be skipped at once rather than searching the actual symbol hash. We don't currently build anything with DT_GNU_HASH support. You can test this by adding -Wl,--hash-style=both to HAIKU_LINKFLAGS. (It seems to increase image sizes by not too much: libroot goes from 1347139 to 1367691 bytes (20.55 KB) in my build.) Change-Id: I4a91276490fcd136db175833ee48b36e06ceed47 Reviewed-on: https://review.haiku-os.org/c/haiku/+/7855 Reviewed-by: waddlesplash <waddlesplash@gmail.com>
This commit is contained in:
parent
90a0982dcb
commit
ffc1a5219d
@ -544,6 +544,8 @@ typedef struct {
|
||||
#define DT_PREINIT_ARRAY 32 /* preinitialization array */
|
||||
#define DT_PREINIT_ARRAYSZ 33 /* preinitialization array size */
|
||||
|
||||
#define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table */
|
||||
|
||||
#define DT_VERSYM 0x6ffffff0 /* symbol version table */
|
||||
#define DT_VERDEF 0x6ffffffc /* version definition table */
|
||||
#define DT_VERDEFNUM 0x6ffffffd /* number of version definitions */
|
||||
|
@ -107,16 +107,32 @@ typedef struct image_t {
|
||||
addr_t term_routine;
|
||||
addr_t dynamic_ptr; // pointer to the dynamic section
|
||||
|
||||
// pointer to symbol participation data structures
|
||||
// needed images
|
||||
uint32 num_needed;
|
||||
struct image_t **needed;
|
||||
|
||||
// symbol participation data structures
|
||||
uint32 *symhash;
|
||||
elf_sym *syms;
|
||||
char *strtab;
|
||||
struct {
|
||||
uint32 mask_words_count_mask;
|
||||
uint32 shift2;
|
||||
uint32 bucket_count;
|
||||
elf_addr *bloom;
|
||||
uint32 *buckets;
|
||||
uint32 *chain0;
|
||||
} gnuhash;
|
||||
|
||||
// relocation information
|
||||
elf_rel *rel;
|
||||
int rel_len;
|
||||
elf_rela *rela;
|
||||
int rela_len;
|
||||
elf_rel *pltrel;
|
||||
int pltrel_len;
|
||||
|
||||
// init/term functions
|
||||
addr_t *init_array;
|
||||
int init_array_len;
|
||||
addr_t *preinit_array;
|
||||
@ -124,11 +140,6 @@ typedef struct image_t {
|
||||
addr_t *term_array;
|
||||
int term_array_len;
|
||||
|
||||
unsigned dso_tls_id;
|
||||
|
||||
uint32 num_needed;
|
||||
struct image_t **needed;
|
||||
|
||||
// versioning related structures
|
||||
uint32 num_version_definitions;
|
||||
elf_verdef *version_definitions;
|
||||
@ -138,6 +149,9 @@ typedef struct image_t {
|
||||
elf_version_info *versions;
|
||||
uint32 num_versions;
|
||||
|
||||
// thread-local storage
|
||||
unsigned dso_tls_id;
|
||||
|
||||
#ifdef __cplusplus
|
||||
elf_sym* (*find_undefined_symbol)(struct image_t* rootImage,
|
||||
struct image_t* image,
|
||||
|
@ -321,6 +321,23 @@ parse_dynamic_segment(image_t* image)
|
||||
case DT_SONAME:
|
||||
sonameOffset = d[i].d_un.d_val;
|
||||
break;
|
||||
case DT_GNU_HASH:
|
||||
{
|
||||
uint32* gnuhash = (uint32*)
|
||||
(d[i].d_un.d_ptr + image->regions[0].delta);
|
||||
const uint32 bucketCount = gnuhash[0];
|
||||
const uint32 symIndex = gnuhash[1];
|
||||
const uint32 maskWordsCount = gnuhash[2];
|
||||
const uint32 bloomSize = maskWordsCount * (sizeof(elf_addr) / 4);
|
||||
|
||||
image->gnuhash.mask_words_count_mask = maskWordsCount - 1;
|
||||
image->gnuhash.shift2 = gnuhash[3];
|
||||
image->gnuhash.bucket_count = bucketCount;
|
||||
image->gnuhash.bloom = (elf_addr*)(gnuhash + 4);
|
||||
image->gnuhash.buckets = gnuhash + 4 + bloomSize;
|
||||
image->gnuhash.chain0 = image->gnuhash.buckets + bucketCount - symIndex;
|
||||
break;
|
||||
}
|
||||
case DT_VERSYM:
|
||||
image->symbol_versions = (elf_versym*)
|
||||
(d[i].d_un.d_ptr + image->regions[0].delta);
|
||||
|
@ -31,7 +31,7 @@
|
||||
\return \c true, iff \a name is non-NULL and matches the name of \a image.
|
||||
*/
|
||||
static bool
|
||||
equals_image_name(image_t* image, const char* name)
|
||||
equals_image_name(const image_t* image, const char* name)
|
||||
{
|
||||
if (name == NULL)
|
||||
return false;
|
||||
@ -62,6 +62,19 @@ elf_hash(const char* _name)
|
||||
}
|
||||
|
||||
|
||||
uint32
|
||||
elf_gnuhash(const char* _name)
|
||||
{
|
||||
const uint8* name = (const uint8*)_name;
|
||||
|
||||
uint32 h = 5381;
|
||||
for (uint8 c = *name; c != '\0'; c = *++name)
|
||||
h = (h * 33) + c;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
struct match_result {
|
||||
elf_sym* symbol;
|
||||
elf_sym* versioned_symbol;
|
||||
@ -72,7 +85,7 @@ struct match_result {
|
||||
|
||||
|
||||
static bool
|
||||
match_symbol(image_t* image, const SymbolLookupInfo& lookupInfo, uint32 symIdx,
|
||||
match_symbol(const image_t* image, const SymbolLookupInfo& lookupInfo, uint32 symIdx,
|
||||
match_result& result)
|
||||
{
|
||||
elf_sym* symbol = &image->syms[symIdx];
|
||||
@ -188,8 +201,43 @@ match_symbol(image_t* image, const SymbolLookupInfo& lookupInfo, uint32 symIdx,
|
||||
}
|
||||
|
||||
|
||||
elf_sym*
|
||||
find_symbol(image_t* image, const SymbolLookupInfo& lookupInfo)
|
||||
static elf_sym*
|
||||
find_symbol_gnuhash(const image_t* image, const SymbolLookupInfo& lookupInfo)
|
||||
{
|
||||
// Test against the Bloom filter.
|
||||
const uint32 wordSize = sizeof(elf_addr) * 8;
|
||||
const uint32 firstHash = lookupInfo.gnuhash & (wordSize - 1);
|
||||
const uint32 secondHash = lookupInfo.gnuhash >> image->gnuhash.shift2;
|
||||
const uint32 index = (lookupInfo.gnuhash / wordSize) & image->gnuhash.mask_words_count_mask;
|
||||
const elf_addr bloomWord = image->gnuhash.bloom[index];
|
||||
if (((bloomWord >> firstHash) & (bloomWord >> secondHash) & 1) == 0)
|
||||
return NULL;
|
||||
|
||||
// Locate hash chain and corresponding value element.
|
||||
const uint32 bucket = image->gnuhash.buckets[lookupInfo.gnuhash % image->gnuhash.bucket_count];
|
||||
if (bucket == 0)
|
||||
return NULL;
|
||||
|
||||
match_result result;
|
||||
const uint32* chain0 = image->gnuhash.chain0;
|
||||
const uint32* hashValue = &chain0[bucket];
|
||||
do {
|
||||
if (((*hashValue ^ lookupInfo.gnuhash) >> 1) != 0)
|
||||
continue;
|
||||
|
||||
uint32 symIndex = hashValue - chain0;
|
||||
if (match_symbol(image, lookupInfo, symIndex, result))
|
||||
return result.symbol;
|
||||
} while ((*hashValue++ & 1) == 0);
|
||||
|
||||
if (result.versioned_symbol_count == 1)
|
||||
return result.versioned_symbol;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static elf_sym*
|
||||
find_symbol_sysv(const image_t* image, const SymbolLookupInfo& lookupInfo)
|
||||
{
|
||||
if (image->dynamic_ptr == 0)
|
||||
return NULL;
|
||||
@ -209,6 +257,21 @@ find_symbol(image_t* image, const SymbolLookupInfo& lookupInfo)
|
||||
}
|
||||
|
||||
|
||||
elf_sym*
|
||||
find_symbol(image_t* image, const SymbolLookupInfo& lookupInfo)
|
||||
{
|
||||
if (image->gnuhash.buckets != NULL) {
|
||||
if (lookupInfo.gnuhash == 0)
|
||||
const_cast<uint32&>(lookupInfo.gnuhash) = elf_gnuhash(lookupInfo.name);
|
||||
return find_symbol_gnuhash(image, lookupInfo);
|
||||
}
|
||||
|
||||
if (lookupInfo.hash == 0)
|
||||
const_cast<uint32&>(lookupInfo.hash) = elf_hash(lookupInfo.name);
|
||||
return find_symbol_sysv(image, lookupInfo);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
patch_defined_symbol(image_t* image, const char* name, void** symbol,
|
||||
int32* type)
|
||||
|
@ -17,12 +17,13 @@
|
||||
|
||||
|
||||
uint32 elf_hash(const char* name);
|
||||
uint32 elf_gnuhash(const char* name);
|
||||
|
||||
|
||||
struct SymbolLookupInfo {
|
||||
const char* name;
|
||||
int32 type;
|
||||
uint32 hash;
|
||||
uint32 hash, gnuhash;
|
||||
uint32 flags;
|
||||
const elf_version_info* version;
|
||||
elf_sym* requestingSymbol;
|
||||
@ -33,7 +34,8 @@ struct SymbolLookupInfo {
|
||||
:
|
||||
name(name),
|
||||
type(type),
|
||||
hash(elf_hash(name)),
|
||||
hash(0),
|
||||
gnuhash(0),
|
||||
flags(flags),
|
||||
version(version),
|
||||
requestingSymbol(requestingSymbol)
|
||||
|
Loading…
Reference in New Issue
Block a user