aboutsummaryrefslogtreecommitdiffstats
path: root/elf/elf_module.c
diff options
context:
space:
mode:
authorStefan Bucur <stefanb@zytor.com>2008-06-15 14:48:36 +0300
committerStefan Bucur <stefan@stefan-ubumac.(none)>2009-03-15 10:02:14 +0200
commit2c85080419f8a44a35f319304745f9dd94d5839e (patch)
tree25dee6ad31a6398e15aaa1114c3a1052d528b680 /elf/elf_module.c
parentc452160ab1e3f591f50dde0a7168d5275e17efb8 (diff)
downloadsyslinux-elf-2c85080419f8a44a35f319304745f9dd94d5839e.tar.gz
syslinux-elf-2c85080419f8a44a35f319304745f9dd94d5839e.tar.xz
syslinux-elf-2c85080419f8a44a35f319304745f9dd94d5839e.zip
Implemented support for GNU hash tables.
The implementation is an adaptation from the uClibc dynamic ELF loading and linking.
Diffstat (limited to 'elf/elf_module.c')
-rw-r--r--elf/elf_module.c80
1 files changed, 77 insertions, 3 deletions
diff --git a/elf/elf_module.c b/elf/elf_module.c
index a91b1f1b..cadcf8a4 100644
--- a/elf/elf_module.c
+++ b/elf/elf_module.c
@@ -622,11 +622,15 @@ int module_unload(struct elf_module *module) {
}
-Elf32_Sym *module_find_symbol(const char *name, struct elf_module *module) {
+static Elf32_Sym *module_find_symbol_sysv(const char *name, struct elf_module *module) {
unsigned long h = elf_hash((const unsigned char*)name);
+ Elf32_Word *cr_word = module->hash_table;
- Elf32_Word *bkt = module->hash_table + 2;
- Elf32_Word *chn = module->hash_table + 2 + module->hash_table[0];
+ Elf32_Word nbucket = *cr_word++;
+ cr_word++; // Skip nchain
+
+ Elf32_Word *bkt = cr_word;
+ Elf32_Word *chn = cr_word + nbucket;
Elf32_Word crt_index = bkt[h % module->hash_table[0]];
Elf32_Sym *crt_sym;
@@ -644,6 +648,76 @@ Elf32_Sym *module_find_symbol(const char *name, struct elf_module *module) {
return NULL;
}
+static Elf32_Sym *module_find_symbol_gnu(const char *name, struct elf_module *module) {
+ unsigned long h = elf_gnu_hash((const unsigned char*)name);
+
+ // Setup code (TODO: Optimize this by computing only once)
+ Elf32_Word *cr_word = module->ghash_table;
+ Elf32_Word nbucket = *cr_word++;
+ Elf32_Word symbias = *cr_word++;
+ Elf32_Word bitmask_nwords = *cr_word++;
+
+ if ((bitmask_nwords & (bitmask_nwords - 1)) != 0) {
+ fprintf(stderr, "Warning: Invalid GNU Hash structure\n");
+ return NULL;
+ }
+
+ Elf32_Word gnu_shift = *cr_word++;
+
+ Elf32_Addr *gnu_bitmask = (Elf32_Addr*)cr_word;
+ cr_word += MODULE_ELF_CLASS_SIZE / 32 * bitmask_nwords;
+
+ Elf32_Word *gnu_buckets = cr_word;
+ cr_word += nbucket;
+
+ Elf32_Word *gnu_chain_zero = cr_word - symbias;
+
+ // Computations
+ Elf32_Word bitmask_word = gnu_bitmask[(h / MODULE_ELF_CLASS_SIZE) &
+ (bitmask_nwords - 1)];
+
+ unsigned int hashbit1 = h & (MODULE_ELF_CLASS_SIZE - 1);
+ unsigned int hashbit2 = (h >> gnu_shift) & (MODULE_ELF_CLASS_SIZE - 1);
+
+ if ((bitmask_word >> hashbit1) & (bitmask_word >> hashbit2) & 1) {
+ unsigned long rem;
+ Elf32_Word bucket;
+
+ rem = h % nbucket;
+
+ bucket = gnu_buckets[rem];
+
+ if (bucket != 0) {
+ const Elf32_Word* hasharr = &gnu_chain_zero[bucket];
+
+ do {
+ if (((*hasharr ^ h ) >> 1) == 0) {
+ Elf32_Sym *crt_sym = (Elf32_Sym*)(module->sym_table +
+ (hasharr - gnu_chain_zero) * module->syment_size);
+
+ if (strcmp(name, module->str_table + crt_sym->st_name) == 0) {
+ return crt_sym;
+ }
+ }
+ } while ((*hasharr++ & 1u) == 0);
+ }
+ }
+
+ return NULL;
+}
+
+Elf32_Sym *module_find_symbol(const char *name, struct elf_module *module) {
+ Elf32_Sym *result = NULL;
+
+ if (module->ghash_table != NULL)
+ result = module_find_symbol_gnu(name, module);
+
+ if (result == NULL)
+ result = module_find_symbol_sysv(name, module);
+
+ return result;
+}
+
Elf32_Sym *global_find_symbol(const char *name, struct elf_module **module) {
struct elf_module *crt_module;
Elf32_Sym *crt_sym = NULL;