diff --git a/headers/os/kernel/elf.h b/headers/os/kernel/elf.h index ca9dacc118..3e071bc8b8 100644 --- a/headers/os/kernel/elf.h +++ b/headers/os/kernel/elf.h @@ -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 */ diff --git a/headers/private/runtime_loader/runtime_loader.h b/headers/private/runtime_loader/runtime_loader.h index 2cf0a271e8..39785ed18c 100644 --- a/headers/private/runtime_loader/runtime_loader.h +++ b/headers/private/runtime_loader/runtime_loader.h @@ -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, diff --git a/src/system/runtime_loader/elf_load_image.cpp b/src/system/runtime_loader/elf_load_image.cpp index 63a7db8a43..ab0d51141e 100644 --- a/src/system/runtime_loader/elf_load_image.cpp +++ b/src/system/runtime_loader/elf_load_image.cpp @@ -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); diff --git a/src/system/runtime_loader/elf_symbol_lookup.cpp b/src/system/runtime_loader/elf_symbol_lookup.cpp index c919e91d77..7e1b1f161f 100644 --- a/src/system/runtime_loader/elf_symbol_lookup.cpp +++ b/src/system/runtime_loader/elf_symbol_lookup.cpp @@ -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(lookupInfo.gnuhash) = elf_gnuhash(lookupInfo.name); + return find_symbol_gnuhash(image, lookupInfo); + } + + if (lookupInfo.hash == 0) + const_cast(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) diff --git a/src/system/runtime_loader/elf_symbol_lookup.h b/src/system/runtime_loader/elf_symbol_lookup.h index b21fd655f2..b80100e32e 100644 --- a/src/system/runtime_loader/elf_symbol_lookup.h +++ b/src/system/runtime_loader/elf_symbol_lookup.h @@ -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)