FreeBSD Bugzilla – Attachment 145127 Details for
Bug 192253
[INCOMPLETE] Add GNU hash support to kernel link_elf, kldxref
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
(patch -p1 in src/ ; applies against recent-ish CURRENT)
a.patch (text/plain), 16.17 KB, created by
Conrad Meyer
on 2014-07-29 20:09:35 UTC
(
hide
)
Description:
(patch -p1 in src/ ; applies against recent-ish CURRENT)
Filename:
MIME Type:
Creator:
Conrad Meyer
Created:
2014-07-29 20:09:35 UTC
Size:
16.17 KB
patch
obsolete
>diff --git a/sys/conf/kern.pre.mk b/sys/conf/kern.pre.mk >index b880f18..2d12a89 100644 >--- a/sys/conf/kern.pre.mk >+++ b/sys/conf/kern.pre.mk >@@ -23,10 +23,11 @@ _srcconf_included_: > KERNEL_KO?= kernel > KERNEL?= kernel > KODIR?= /boot/${KERNEL} > LDSCRIPT_NAME?= ldscript.$M > LDSCRIPT?= $S/conf/${LDSCRIPT_NAME} >+LDFLAGS+= --hash-style=gnu > > M= ${MACHINE_CPUARCH} > > AWK?= awk > CP?= cp >diff --git a/sys/conf/kmod.mk b/sys/conf/kmod.mk >index 06b95e5..6ce8079 100644 >--- a/sys/conf/kmod.mk >+++ b/sys/conf/kmod.mk >@@ -109,11 +109,11 @@ CFLAGS.gcc+= --param inline-unit-growth=100 > CFLAGS.gcc+= --param large-function-growth=1000 > > # Disallow common variables, and if we end up with commons from > # somewhere unexpected, allocate storage for them in the module itself. > CFLAGS+= -fno-common >-LDFLAGS+= -d -warn-common >+LDFLAGS+= -d -warn-common --hash-style=gnu > > CFLAGS+= ${DEBUG_FLAGS} > .if ${MACHINE_CPUARCH} == amd64 > CFLAGS+= -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer > .endif >diff --git a/sys/kern/link_elf.c b/sys/kern/link_elf.c >index ecee5a7..0823e1d 100644 >--- a/sys/kern/link_elf.c >+++ b/sys/kern/link_elf.c >@@ -79,14 +79,28 @@ typedef struct elf_file { > caddr_t address; /* Relocation address */ > #ifdef SPARSE_MAPPING > vm_object_t object; /* VM object to hold file pages */ > #endif > Elf_Dyn *dynamic; /* Symbol table etc. */ >+ >+ /* Adaptation of r234841 (DT_GNU_HASH for rtld-elf) to kernel linker */ >+ bool valid_hash_sysv : 1; >+ bool valid_hash_gnu : 1; >+ > Elf_Hashelt nbuckets; /* DT_HASH info */ > Elf_Hashelt nchains; > const Elf_Hashelt *buckets; > const Elf_Hashelt *chains; >+ >+ Elf_Hashelt nbuckets_gnu; /* DT_GNU_HASH info */ >+ Elf_Word symndx_gnu; /* 1st accessible symbol on dynsym table */ >+ Elf_Word maskwords_bm_gnu; /* Bloom filter words - 1 (bitmask) */ >+ Elf_Word shift2_gnu; /* Bloom filter shift count */ >+ Elf_Addr *bloom_gnu; /* Bloom filter used by GNU hash func */ >+ const Elf_Hashelt *buckets_gnu; >+ const Elf_Hashelt *chain_zero_gnu; >+ > caddr_t hash; > caddr_t strtab; /* DT_STRTAB */ > int strsz; /* DT_STRSZ */ > const Elf_Sym *symtab; /* DT_SYMTAB */ > Elf_Addr *got; /* DT_PLTGOT */ >@@ -483,22 +497,51 @@ link_elf_preload_parse_symbols(elf_file_t ef) > static int > parse_dynamic(elf_file_t ef) > { > Elf_Dyn *dp; > int plttype = DT_REL; >+ const Elf_Hashelt *hashtab; > > for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { > switch (dp->d_tag) { > case DT_HASH: > { > /* From src/libexec/rtld-elf/rtld.c */ >- const Elf_Hashelt *hashtab = (const Elf_Hashelt *) >- (ef->address + dp->d_un.d_ptr); >+ hashtab = (const Elf_Hashelt *)(ef->address + >+ dp->d_un.d_ptr); > ef->nbuckets = hashtab[0]; > ef->nchains = hashtab[1]; > ef->buckets = hashtab + 2; > ef->chains = ef->buckets + ef->nbuckets; >+ >+ ef->valid_hash_sysv = (ef->nbuckets > 0 && >+ ef->nchains > 0); >+ break; >+ } >+ case DT_GNU_HASH: >+ { >+ Elf_Hashelt nmaskwords; >+ size_t bloom_size32; >+ bool nmw_power2; >+ >+ hashtab = (const Elf_Hashelt *)(ef->address + >+ dp->d_un.d_ptr); >+ >+ ef->nbuckets_gnu = hashtab[0]; >+ ef->symndx_gnu = hashtab[1]; >+ nmaskwords = hashtab[2]; >+ bloom_size32 = (__ELF_WORD_SIZE / 32) * nmaskwords; >+ /* Number of bitmask words is required to be power of 2 */ >+ nmw_power2 = powerof2(nmaskwords); >+ ef->maskwords_bm_gnu = nmaskwords - 1; >+ ef->shift2_gnu = hashtab[3]; >+ ef->bloom_gnu = (Elf_Addr *)(hashtab + 4); >+ ef->buckets_gnu = hashtab + 4 + bloom_size32; >+ ef->chain_zero_gnu = ef->buckets_gnu + ef->nbuckets_gnu >+ - ef->symndx_gnu; >+ ef->valid_hash_gnu = (nmw_power2 && >+ ef->nbuckets_gnu > 0 && nmaskwords > 0); > break; > } > case DT_STRTAB: > ef->strtab = (caddr_t) (ef->address + dp->d_un.d_ptr); > break; >@@ -560,14 +603,32 @@ parse_dynamic(elf_file_t ef) > ef->pltrelasize = ef->pltrelsize; > ef->pltrelsize = 0; > } > > ef->ddbsymtab = ef->symtab; >- ef->ddbsymcnt = ef->nchains; > ef->ddbstrtab = ef->strtab; > ef->ddbstrcnt = ef->strsz; > >+ /* Determine size of dynsym table (equal to nchains of sysv hash) */ >+ if (ef->valid_hash_sysv) >+ ef->ddbsymcnt = ef->nchains; >+ else if (ef->valid_hash_gnu) { >+ const Elf_Word *hashval; >+ Elf_Word bkt; >+ >+ ef->ddbsymcnt = 0; >+ for (bkt = 0; bkt < ef->nbuckets_gnu; bkt++) { >+ if (ef->buckets_gnu[bkt] == 0) >+ continue; >+ hashval = &ef->chain_zero_gnu[ef->buckets_gnu[bkt]]; >+ do >+ ef->ddbsymcnt++; >+ while ((*hashval++ & 1u) == 0); >+ } >+ ef->ddbsymcnt += ef->symndx_gnu; >+ } >+ > return (0); > } > > static int > parse_dpcpu(elf_file_t ef) >@@ -1220,25 +1281,20 @@ elf_hash(const char *name) > } > return (h); > } > > static int >-link_elf_lookup_symbol(linker_file_t lf, const char* name, c_linker_sym_t* sym) >+link_elf_lookup_symbol_sysv(linker_file_t lf, const char* name, >+ c_linker_sym_t* sym) > { > elf_file_t ef = (elf_file_t) lf; > unsigned long symnum; > const Elf_Sym* symp; > const char *strp; > unsigned long hash; > int i; > >- /* If we don't have a hash, bail. */ >- if (ef->buckets == NULL || ef->nbuckets == 0) { >- printf("link_elf_lookup_symbol: missing symbol hash table\n"); >- return (ENOENT); >- } >- > /* First, search hashed global symbols */ > hash = elf_hash(name); > symnum = ef->buckets[hash % ef->nbuckets]; > > while (symnum != STN_UNDEF) { >@@ -1287,18 +1343,113 @@ link_elf_lookup_symbol(linker_file_t lf, const char* name, c_linker_sym_t* sym) > } > > return (ENOENT); > } > >+/* >+ * The GNU hash function is the Daniel J. Bernstein hash clipped to 32 bits >+ * unsigned in case it's implemented with a wider type. >+ */ >+static uint32_t >+gnu_hash(const char *s) >+{ >+ uint32_t h; >+ unsigned char c; >+ >+ h = 5381; >+ for (c = *s; c != '\0'; c = *++s) >+ h = h * 33 + c; >+ return (h & 0xffffffff); >+} >+ >+static int >+link_elf_lookup_symbol_gnu(linker_file_t lf, const char* name, >+ c_linker_sym_t* sym) >+{ >+ elf_file_t ef = (elf_file_t)lf; >+ const Elf_Word *hashval; >+ unsigned long symnum; >+ const Elf_Sym *symp; >+ Elf_Addr bloom_word; >+ const char *strp; >+ Elf_Word bucket; >+ unsigned h1, h2; >+ uint32_t hash; >+ >+ hash = gnu_hash(name); >+ >+ /* >+ * Bloom filter: compute bloom hashes; check for possible membership in >+ * filter. There are no false negatives, so return ENOENT early. >+ */ >+ h1 = hash & (__ELF_WORD_SIZE - 1); >+ h2 = ((hash >> ef->shift2_gnu) & (__ELF_WORD_SIZE - 1)); >+ >+ bloom_word = ef->bloom_gnu[(hash / __ELF_WORD_SIZE) & >+ ef->maskwords_bm_gnu]; >+ >+ if (((bloom_word >> h1) & (bloom_word >> h2) & 1) == 0) >+ return (ENOENT); >+ >+ /* Ok, it's (possibly) present? Look up in the hash table. */ >+ bucket = ef->buckets_gnu[hash % ef->nbuckets_gnu]; >+ if (bucket == 0) >+ return (ENOENT); >+ hashval = &ef->chain_zero_gnu[bucket]; >+ do { >+ if (((*hashval ^ hash) >> 1) != 0) >+ continue; >+ >+ symnum = hashval - ef->chain_zero_gnu; >+ if (symnum >= ef->ddbsymcnt) { >+ printf("%s: corrupt symbol table\n", __func__); >+ return (ENOENT); >+ } >+ >+ symp = ef->symtab + symnum; >+ if (symp->st_name == 0) { >+ printf("%s: corrupt symbol table\n", __func__); >+ return (ENOENT); >+ } >+ >+ strp = ef->strtab + symp->st_name; >+ if (strcmp(name, strp) != 0) >+ continue; >+ >+ if (symp->st_shndx == SHN_UNDEF && (symp->st_value == 0 || >+ ELF_ST_TYPE(symp->st_info) != STT_FUNC)) >+ return (ENOENT); >+ >+ *sym = (c_linker_sym_t)symp; >+ return (0); >+ } while ((*hashval++ & 1) == 0); >+ return (ENOENT); >+} >+ >+static int >+link_elf_lookup_symbol(linker_file_t lf, const char* name, c_linker_sym_t* sym) >+{ >+ elf_file_t ef = (elf_file_t)lf; >+ >+ if (ef->valid_hash_gnu) >+ return (link_elf_lookup_symbol_gnu(lf, name, sym)); >+ else if (ef->valid_hash_sysv) >+ return (link_elf_lookup_symbol_sysv(lf, name, sym)); >+ else { >+ printf("link_elf_lookup_symbol: missing symbol hash table\n"); >+ return (ENOENT); >+ } >+} >+ > static int > link_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym, > linker_symval_t *symval) > { > elf_file_t ef = (elf_file_t) lf; > const Elf_Sym* es = (const Elf_Sym*) sym; > >- if (es >= ef->symtab && es < (ef->symtab + ef->nchains)) { >+ if (es >= ef->symtab && es < (ef->symtab + ef->ddbsymcnt)) { > symval->name = ef->strtab + es->st_name; > symval->value = (caddr_t) ef->address + es->st_value; > symval->size = es->st_size; > return (0); > } >@@ -1453,22 +1604,22 @@ link_elf_each_function_nameval(linker_file_t file, > const Elf_Sym * > elf_get_sym(linker_file_t lf, Elf_Size symidx) > { > elf_file_t ef = (elf_file_t)lf; > >- if (symidx >= ef->nchains) >+ if (symidx >= ef->ddbsymcnt) > return (NULL); > return (ef->symtab + symidx); > } > > const char * > elf_get_symname(linker_file_t lf, Elf_Size symidx) > { > elf_file_t ef = (elf_file_t)lf; > const Elf_Sym *sym; > >- if (symidx >= ef->nchains) >+ if (symidx >= ef->ddbsymcnt) > return (NULL); > sym = ef->symtab + symidx; > return (ef->strtab + sym->st_name); > } > >@@ -1486,11 +1637,11 @@ elf_lookup(linker_file_t lf, Elf_Size symidx, int deps) > const Elf_Sym *sym; > const char *symbol; > Elf_Addr addr, start, base; > > /* Don't even try to lookup the symbol if the index is bogus. */ >- if (symidx >= ef->nchains) >+ if (symidx >= ef->ddbsymcnt) > return (0); > > sym = ef->symtab + symidx; > > /* >diff --git a/usr.sbin/kldxref/ef.c b/usr.sbin/kldxref/ef.c >index 88fbc34..e9efba8 100644 >--- a/usr.sbin/kldxref/ef.c >+++ b/usr.sbin/kldxref/ef.c >@@ -32,11 +32,13 @@ > * $FreeBSD$ > */ > > #include <sys/param.h> > #include <sys/linker.h> >+#include <stdbool.h> > #include <string.h> >+#include <stdint.h> > #include <stdio.h> > #include <stdlib.h> > #include <unistd.h> > #include <errno.h> > #include <fcntl.h> >@@ -73,10 +75,22 @@ struct ef_file { > int ef_verbose; > Elf_Rel * ef_rel; /* relocation table */ > int ef_relsz; /* number of entries */ > Elf_Rela * ef_rela; /* relocation table */ > int ef_relasz; /* number of entries */ >+ >+ long ef_symcnt; /* Number of symbols */ >+ >+ bool ef_valid_hash_sysv : 1; >+ bool ef_valid_hash_gnu : 1; >+ Elf_Hashelt ef_nbuckets_gnu; /* DT_GNU_HASH info */ >+ Elf_Word ef_symndx_gnu; /* 1st accessible symbol on dynsym table */ >+ Elf_Word ef_maskwords_bm_gnu; /* Bloom filter words - 1 (bitmask) */ >+ Elf_Word ef_shift2_gnu; /* Bloom filter shift count */ >+ Elf_Addr * ef_bloom_gnu; /* Bloom filter used by GNU hash func */ >+ Elf_Hashelt * ef_buckets_gnu; >+ Elf_Hashelt * ef_chain_zero_gnu; > }; > > static void ef_print_phdr(Elf_Phdr *); > static u_long ef_get_offset(elf_file_t, Elf_Off); > static int ef_parse_dynamic(elf_file_t); >@@ -165,11 +179,11 @@ elf_hash(const char *name) > } > return h; > } > > static int >-ef_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym) >+ef_lookup_symbol_sysv(elf_file_t ef, const char* name, Elf_Sym** sym) > { > unsigned long symnum; > Elf_Sym* symp; > char *strp; > unsigned long hash; >@@ -208,10 +222,105 @@ ef_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym) > } > > return ENOENT; > } > >+/* >+ * The GNU hash function is the Daniel J. Bernstein hash clipped to 32 bits >+ * unsigned in case it's implemented with a wider type. >+ */ >+static uint32_t >+gnu_hash(const char *s) >+{ >+ uint32_t h; >+ unsigned char c; >+ >+ h = 5381; >+ for (c = *s; c != '\0'; c = *++s) >+ h = h * 33 + c; >+ return (h & 0xffffffff); >+} >+ >+static int >+ef_lookup_symbol_gnu(elf_file_t ef, const char* name, Elf_Sym** sym) >+{ >+ const Elf_Word *hashval; >+ unsigned long symnum; >+ Elf_Sym *symp; >+ Elf_Addr bloom_word; >+ const char *strp; >+ Elf_Word bucket; >+ unsigned h1, h2; >+ uint32_t hash; >+ >+ hash = gnu_hash(name); >+ >+ /* >+ * Bloom filter: compute bloom hashes; check for possible membership in >+ * filter. There are no false negatives, so return ENOENT early. >+ */ >+ h1 = hash & (__ELF_WORD_SIZE - 1); >+ h2 = ((hash >> ef->ef_shift2_gnu) & (__ELF_WORD_SIZE - 1)); >+ >+ bloom_word = ef->ef_bloom_gnu[(hash / __ELF_WORD_SIZE) & >+ ef->ef_maskwords_bm_gnu]; >+ >+ if (((bloom_word >> h1) & (bloom_word >> h2) & 1) == 0) >+ return (ENOENT); >+ >+ /* Ok, it's (possibly) present? Look up in the hash table. */ >+ bucket = ef->ef_buckets_gnu[hash % ef->ef_nbuckets_gnu]; >+ if (bucket == 0) >+ return (ENOENT); >+ hashval = &ef->ef_chain_zero_gnu[bucket]; >+ do { >+ if (((*hashval ^ hash) >> 1) != 0) >+ continue; >+ >+ symnum = hashval - ef->ef_chain_zero_gnu; >+ if (symnum >= ef->ef_symcnt) { >+ warnx("%s: file %s has corrupted symbol table\n", >+ __func__, ef->ef_name); >+ return (ENOENT); >+ } >+ >+ symp = ef->ef_symtab + symnum; >+ if (symp->st_name == 0) { >+ warnx("%s: file %s has corrupted symbol table\n", >+ __func__, ef->ef_name); >+ return (ENOENT); >+ } >+ >+ strp = ef->ef_strtab + symp->st_name; >+ if (strcmp(name, strp) != 0) >+ continue; >+ >+ if (symp->st_shndx == SHN_UNDEF && (symp->st_value == 0 || >+ ELF_ST_TYPE(symp->st_info) != STT_FUNC)) >+ return (ENOENT); >+ >+ *sym = symp; >+ return (0); >+ } while ((*hashval++ & 1) == 0); >+ return (ENOENT); >+} >+ >+static int >+ef_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym) >+{ >+ >+ if (ef->ef_valid_hash_gnu) >+ return (ef_lookup_symbol_gnu(ef, name, sym)); >+ else if (ef->ef_valid_hash_sysv) >+ return (ef_lookup_symbol_sysv(ef, name, sym)); >+ else { >+ warnx("ef_lookup_symbol: file %s missing symbol hash table\n", >+ ef->ef_name); >+ return (ENOENT); >+ } >+} >+ > static int > ef_lookup_set(elf_file_t ef, const char *name, long *startp, long *stopp, > long *countp) > { > Elf_Sym *sym; >@@ -248,11 +357,11 @@ out: > static Elf_Addr > ef_symaddr(elf_file_t ef, Elf_Size symidx) > { > const Elf_Sym *sym; > >- if (symidx >= ef->ef_nchains) >+ if (symidx >= ef->ef_symcnt) > return (0); > sym = ef->ef_symtab + symidx; > > if (ELF_ST_BIND(sym->st_info) == STB_LOCAL && > sym->st_shndx != SHN_UNDEF && sym->st_value != 0) >@@ -263,10 +372,11 @@ ef_symaddr(elf_file_t ef, Elf_Size symidx) > static int > ef_parse_dynamic(elf_file_t ef) > { > Elf_Dyn *dp; > Elf_Hashelt hashhdr[2]; >+ Elf_Hashelt gnuhashhdr[4]; > /* int plttype = DT_REL;*/ > int error; > Elf_Off rel_off; > Elf_Off rela_off; > int rel_sz; >@@ -296,11 +406,58 @@ ef_parse_dynamic(elf_file_t ef) > warnx("can't read hash table"); > return error; > } > ef->ef_buckets = ef->ef_hashtab; > ef->ef_chains = ef->ef_buckets + ef->ef_nbuckets; >+ >+ ef->ef_valid_hash_sysv = (ef->ef_nbuckets > 0 && >+ ef->ef_nchains > 0); > break; >+ case DT_GNU_HASH: >+ { >+ Elf_Hashelt nmaskwords; >+ size_t bloom_size32; >+ bool nmw_power2; >+ >+ error = ef_read(ef, ef_get_offset(ef, dp->d_un.d_ptr), >+ sizeof(gnuhashhdr), gnuhashhdr); >+ if (error) { >+ warnx("can't read GNU hash header (%lx)", >+ ef_get_offset(ef, dp->d_un.d_ptr)); >+ return (error); >+ } >+ >+ ef->ef_nbuckets_gnu = gnuhashhdr[0]; >+ ef->ef_symndx_gnu = gnuhashhdr[1]; >+ nmaskwords = gnuhashhdr[2]; >+ bloom_size32 = (__ELF_WORD_SIZE / 32) * nmaskwords; >+ /* Number of bitmask words is required to be power of 2 */ >+ nmw_power2 = powerof2(nmaskwords); >+ ef->ef_maskwords_bm_gnu = nmaskwords - 1; >+ ef->ef_shift2_gnu = gnuhashhdr[3]; >+ >+ error = ef_read_entry(ef, -1, bloom_size32 * 4, >+ (void**)&ef->ef_bloom_gnu); >+ if (error) { >+ warnx("can't read GNU bloom filter"); >+ return error; >+ } >+ >+ error = ef_read_entry(ef, -1, >+ ef->ef_nbuckets_gnu * sizeof(Elf_Hashelt), >+ (void**)&ef->ef_buckets_gnu); >+ if (error) { >+ warnx("can't read GNU hash table"); >+ return error; >+ } >+ >+ ef->ef_chain_zero_gnu = ef->ef_buckets_gnu + >+ ef->ef_nbuckets_gnu - ef->ef_symndx_gnu; >+ ef->ef_valid_hash_gnu = (nmw_power2 && >+ ef->ef_nbuckets_gnu > 0 && nmaskwords > 0); >+ break; >+ } > case DT_STRTAB: > ef->ef_stroff = dp->d_un.d_ptr; > break; > case DT_STRSZ: > ef->ef_strsz = dp->d_un.d_val; >@@ -350,12 +507,29 @@ ef_parse_dynamic(elf_file_t ef) > } > if (ef->ef_stroff == 0) { > warnx("%s: no .dynstr section found\n", ef->ef_name); > return EFTYPE; > } >+ if (ef->ef_valid_hash_sysv) >+ ef->ef_symcnt = ef->ef_nchains; >+ else if (ef->ef_valid_hash_gnu) { >+ const Elf_Word *hashval; >+ Elf_Word bkt; >+ >+ ef->ef_symcnt = 0; >+ for (bkt = 0; bkt < ef->ef_nbuckets_gnu; bkt++) { >+ if (ef->ef_buckets_gnu[bkt] == 0) >+ continue; >+ hashval = &ef->ef_chain_zero_gnu[ef->ef_buckets_gnu[bkt]]; >+ do >+ ef->ef_symcnt++; >+ while ((*hashval++ & 1u) == 0); >+ } >+ ef->ef_symcnt += ef->ef_symndx_gnu; >+ } > if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_symoff), >- ef->ef_nchains * sizeof(Elf_Sym), >+ ef->ef_symcnt * sizeof(Elf_Sym), > (void**)&ef->ef_symtab) != 0) { > if (ef->ef_verbose) > warnx("%s: can't load .dynsym section (0x%lx)", > ef->ef_name, (long)ef->ef_symoff); > return EIO;
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 192253
: 145127