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 <[email protected]>
This commit is contained in:
committed by
waddlesplash
parent
90a0982dcb
commit
ffc1a5219d
@@ -544,6 +544,8 @@ typedef struct {
|
|||||||
#define DT_PREINIT_ARRAY 32 /* preinitialization array */
|
#define DT_PREINIT_ARRAY 32 /* preinitialization array */
|
||||||
#define DT_PREINIT_ARRAYSZ 33 /* preinitialization array size */
|
#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_VERSYM 0x6ffffff0 /* symbol version table */
|
||||||
#define DT_VERDEF 0x6ffffffc /* version definition table */
|
#define DT_VERDEF 0x6ffffffc /* version definition table */
|
||||||
#define DT_VERDEFNUM 0x6ffffffd /* number of version definitions */
|
#define DT_VERDEFNUM 0x6ffffffd /* number of version definitions */
|
||||||
|
|||||||
@@ -107,16 +107,32 @@ typedef struct image_t {
|
|||||||
addr_t term_routine;
|
addr_t term_routine;
|
||||||
addr_t dynamic_ptr; // pointer to the dynamic section
|
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;
|
uint32 *symhash;
|
||||||
elf_sym *syms;
|
elf_sym *syms;
|
||||||
char *strtab;
|
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;
|
elf_rel *rel;
|
||||||
int rel_len;
|
int rel_len;
|
||||||
elf_rela *rela;
|
elf_rela *rela;
|
||||||
int rela_len;
|
int rela_len;
|
||||||
elf_rel *pltrel;
|
elf_rel *pltrel;
|
||||||
int pltrel_len;
|
int pltrel_len;
|
||||||
|
|
||||||
|
// init/term functions
|
||||||
addr_t *init_array;
|
addr_t *init_array;
|
||||||
int init_array_len;
|
int init_array_len;
|
||||||
addr_t *preinit_array;
|
addr_t *preinit_array;
|
||||||
@@ -124,11 +140,6 @@ typedef struct image_t {
|
|||||||
addr_t *term_array;
|
addr_t *term_array;
|
||||||
int term_array_len;
|
int term_array_len;
|
||||||
|
|
||||||
unsigned dso_tls_id;
|
|
||||||
|
|
||||||
uint32 num_needed;
|
|
||||||
struct image_t **needed;
|
|
||||||
|
|
||||||
// versioning related structures
|
// versioning related structures
|
||||||
uint32 num_version_definitions;
|
uint32 num_version_definitions;
|
||||||
elf_verdef *version_definitions;
|
elf_verdef *version_definitions;
|
||||||
@@ -138,6 +149,9 @@ typedef struct image_t {
|
|||||||
elf_version_info *versions;
|
elf_version_info *versions;
|
||||||
uint32 num_versions;
|
uint32 num_versions;
|
||||||
|
|
||||||
|
// thread-local storage
|
||||||
|
unsigned dso_tls_id;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
elf_sym* (*find_undefined_symbol)(struct image_t* rootImage,
|
elf_sym* (*find_undefined_symbol)(struct image_t* rootImage,
|
||||||
struct image_t* image,
|
struct image_t* image,
|
||||||
|
|||||||
@@ -321,6 +321,23 @@ parse_dynamic_segment(image_t* image)
|
|||||||
case DT_SONAME:
|
case DT_SONAME:
|
||||||
sonameOffset = d[i].d_un.d_val;
|
sonameOffset = d[i].d_un.d_val;
|
||||||
break;
|
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:
|
case DT_VERSYM:
|
||||||
image->symbol_versions = (elf_versym*)
|
image->symbol_versions = (elf_versym*)
|
||||||
(d[i].d_un.d_ptr + image->regions[0].delta);
|
(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.
|
\return \c true, iff \a name is non-NULL and matches the name of \a image.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
equals_image_name(image_t* image, const char* name)
|
equals_image_name(const image_t* image, const char* name)
|
||||||
{
|
{
|
||||||
if (name == NULL)
|
if (name == NULL)
|
||||||
return false;
|
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 {
|
struct match_result {
|
||||||
elf_sym* symbol;
|
elf_sym* symbol;
|
||||||
elf_sym* versioned_symbol;
|
elf_sym* versioned_symbol;
|
||||||
@@ -72,7 +85,7 @@ struct match_result {
|
|||||||
|
|
||||||
|
|
||||||
static bool
|
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)
|
match_result& result)
|
||||||
{
|
{
|
||||||
elf_sym* symbol = &image->syms[symIdx];
|
elf_sym* symbol = &image->syms[symIdx];
|
||||||
@@ -188,8 +201,43 @@ match_symbol(image_t* image, const SymbolLookupInfo& lookupInfo, uint32 symIdx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
elf_sym*
|
static elf_sym*
|
||||||
find_symbol(image_t* image, const SymbolLookupInfo& lookupInfo)
|
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)
|
if (image->dynamic_ptr == 0)
|
||||||
return NULL;
|
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
|
void
|
||||||
patch_defined_symbol(image_t* image, const char* name, void** symbol,
|
patch_defined_symbol(image_t* image, const char* name, void** symbol,
|
||||||
int32* type)
|
int32* type)
|
||||||
|
|||||||
@@ -17,12 +17,13 @@
|
|||||||
|
|
||||||
|
|
||||||
uint32 elf_hash(const char* name);
|
uint32 elf_hash(const char* name);
|
||||||
|
uint32 elf_gnuhash(const char* name);
|
||||||
|
|
||||||
|
|
||||||
struct SymbolLookupInfo {
|
struct SymbolLookupInfo {
|
||||||
const char* name;
|
const char* name;
|
||||||
int32 type;
|
int32 type;
|
||||||
uint32 hash;
|
uint32 hash, gnuhash;
|
||||||
uint32 flags;
|
uint32 flags;
|
||||||
const elf_version_info* version;
|
const elf_version_info* version;
|
||||||
elf_sym* requestingSymbol;
|
elf_sym* requestingSymbol;
|
||||||
@@ -33,7 +34,8 @@ struct SymbolLookupInfo {
|
|||||||
:
|
:
|
||||||
name(name),
|
name(name),
|
||||||
type(type),
|
type(type),
|
||||||
hash(elf_hash(name)),
|
hash(0),
|
||||||
|
gnuhash(0),
|
||||||
flags(flags),
|
flags(flags),
|
||||||
version(version),
|
version(version),
|
||||||
requestingSymbol(requestingSymbol)
|
requestingSymbol(requestingSymbol)
|
||||||
|
|||||||
Reference in New Issue
Block a user