2011/8/22 Bernhard Rosenkranzer <bernhard.rosenkran...@linaro.org>: > On 21 August 2011 15:00, Michael Hope <michael.h...@linaro.org> wrote: >> Sorry, silly question, but does Android use the glibc dynamic linker? > > No, they really like reinventing the wheel. Bionic comes with its own > dynamic linker.
hi Bernhard and Michael, The dynamic linker is file "/system/bin/linker". >> If not, does its linker support other hash styles? > > It looks like it supports both the sysv and gnu styles. It uses libelf > to do most of its ELF parsing, and libelf seems to do the right thing. I don't think so. Since Android uses the libelfcopy[2], derived from some really old parts in binutils, providing the facilities about DWARF/ELF, we would need extra modifications like the attached patch.[*] Also, bionic linker changes are required. Another attachment contains the 0xlab internal modifications. Sincerely, -jserv [*] The related patches are expected to be submitted to AOSP Gerrit recently.
From 71f965b55ffaaa0853e60be2e8b97daf95544712 Mon Sep 17 00:00:00 2001 From: Kito Cheng <k...@0xlab.org> Date: Fri, 12 Aug 2011 00:55:37 +0800 Subject: [PATCH] Basic GNU-style hash support for elfcopy --- elfcopy.c | 17 +++++++++++++++-- 1 files changed, 15 insertions(+), 2 deletions(-) diff --git a/elfcopy.c b/elfcopy.c index ed1e6ce..cac10ea 100644 --- a/elfcopy.c +++ b/elfcopy.c @@ -34,6 +34,16 @@ */ #define ELF_STRPTR_IS_BROKEN (1) +/* Compatible for older elfutils. + elfutils support gnu-style hash since version 0.122 +*/ +#ifndef DT_GNU_HASH +#define DT_GNU_HASH 0x6ffffef5 +#endif +#ifndef SHT_GNU_HASH +#define SHT_GNU_HASH 0x6ffffff6 +#endif + static void update_relocations_section_symbol_references(Elf *newelf, Elf *elf, shdr_info_t *info, int info_len, shdr_info_t *relsect_info, @@ -744,7 +754,6 @@ void adjust_elf(Elf *elf, const char *elf_name, FAILIF(shdr_info[cnt].shdr.sh_entsize != sizeof (Elf32_Word), "Can't handle 64-bit ELF files!\n"); - update_hash_table(newelf, /* new ELF */ elf, /* old ELF */ shdr_info[cnt].idx, /* hash table index */ @@ -846,7 +855,9 @@ void adjust_elf(Elf *elf, const char *elf_name, } /* for each symbol... */ } } - + /* We don't support update gnu-style hash table now! */ + FAILIF(shdr_info[cnt].shdr.sh_type == SHT_GNU_HASH, + "Can't handle SHT_GNU_HASH!\n"); FAILIF(shdr_info[cnt].shdr.sh_type == SHT_GNU_versym, "Can't handle SHT_GNU_versym!\n"); FAILIF(shdr_info[cnt].shdr.sh_type == SHT_GROUP, @@ -1933,6 +1944,7 @@ static void adjust_dynamic_segment_offsets(Elf *elf, Ebl *oldebl, case DT_PLTGOT: case DT_HASH: + case DT_GNU_HASH: case DT_SYMTAB: (void)update_dyn_entry_address(elf, dyn, shdr_info, shdr_info_len); break; @@ -2582,6 +2594,7 @@ static int get_end_of_range(shdr_info_t *shdr_info, (shdr_info[end].shdr.sh_type == SHT_INIT_ARRAY) || (shdr_info[end].shdr.sh_type == SHT_FINI_ARRAY) || (shdr_info[end].shdr.sh_type == SHT_PREINIT_ARRAY) || + (shdr_info[end].shdr.sh_type == SHT_GNU_HASH) || /* (shdr_info[end].shdr.sh_type == SHT_NOBITS) || */ #ifdef ARM_SPECIFIC_HACKS /* SHF_ALLOC sections with with names starting with ".ARM." are -- 1.7.5.4
From 5bf2ca212750d3dadac96de0bfd4d621abbb0dea Mon Sep 17 00:00:00 2001 From: Kito Cheng <k...@0xlab.org> Date: Fri, 12 Aug 2011 00:49:57 +0800 Subject: [PATCH] GNU-style hash support for linker --- linker/linker.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++--- linker/linker.h | 7 +++ linker/linker_debug.h | 17 +++++++ 3 files changed, 133 insertions(+), 7 deletions(-) diff --git a/linker/linker.c b/linker/linker.c index bb31703..d5fc459 100644 --- a/linker/linker.c +++ b/linker/linker.c @@ -62,6 +62,13 @@ #define LDPRELOAD_BUFSIZE 512 #define LDPRELOAD_MAX 8 +/* Compatible for older elfutils. + elfutils support gnu-style hash since version 0.122 +*/ +#ifndef DT_GNU_HASH +#define DT_GNU_HASH 0x6ffffef5 /* address of gnu-style symbol hash table */ +#endif + /* >>> IMPORTANT NOTE - READ ME BEFORE MODIFYING <<< * * Do NOT use malloc() and friends or pthread_*() code here. @@ -399,6 +406,54 @@ dl_iterate_phdr(int (*cb)(struct dl_phdr_info *info, size_t size, void *data), } #endif +static Elf32_Sym *_gnu_lookup(soinfo *si, unsigned gnu_hash, const char *name) +{ + Elf32_Sym *s; + Elf32_Sym *symtab = si->symtab; + const char *strtab = si->strtab; + int elfclass = 32; + Elf32_Word bitmask_word = si->gnu_bitmask[(gnu_hash / elfclass) + & (si->gnu_bitmask_words - 1)]; + unsigned hashbit1 = gnu_hash & (elfclass - 1); + unsigned hashbit2 = ((gnu_hash >> si->gnu_shift) + & (elfclass - 1)); + Elf32_Word bucket; + Elf32_Word index; + /* Bloom Filter */ + if (( (bitmask_word >> hashbit1) + & (bitmask_word >> hashbit2) & 1) == 0){ + COUNT_LOOKUP(LOOKUP_BLOOM); + return NULL; + } + TRACE_TYPE(LOOKUP, "%5d SEARCH %s in %s@0x%08x %08x %d\n", pid, + name, si->name, si->base, gnu_hash, gnu_hash % si->gnu_nbucket); + bucket = si->gnu_bucket[gnu_hash % si->gnu_nbucket]; + if (bucket != 0) { + Elf32_Word *hasharr = &si->gnu_chain[bucket]; + do { + if (((*hasharr ^ gnu_hash) >> 1) == 0){ + index = hasharr - si->gnu_chain; + s = symtab + index; + if(strcmp(strtab + s->st_name, name)) continue; + + + switch(ELF32_ST_BIND(s->st_info)){ + case STB_GLOBAL: + case STB_WEAK: + /* no section == undefined */ + if(s->st_shndx == 0) continue; + + TRACE_TYPE(LOOKUP, "%5d FOUND %s in %s (%08x) %d\n", pid, + name, si->name, s->st_value, s->st_size); + return s; + } + } + } while ((*hasharr++ & 1) == 0); + } + + return NULL; +} + static Elf32_Sym *_elf_lookup(soinfo *si, unsigned hash, const char *name) { Elf32_Sym *s; @@ -430,6 +485,32 @@ static Elf32_Sym *_elf_lookup(soinfo *si, unsigned hash, const char *name) return NULL; } +static Elf32_Sym *_symbol_lookup(soinfo *si, unsigned hash, unsigned gnu_hash, const char *name) +{ + Elf32_Sym *sym; + COUNT_LOOKUP(LOOKUP_NUM); + if ( si->gnu_bitmask != NULL ){ + COUNT_LOOKUP(LOOKUP_GNU); + sym = _gnu_lookup(si, gnu_hash, name); + } else { + COUNT_LOOKUP(LOOKUP_ELF); + sym = _elf_lookup(si, hash, name); + } + if (!sym) COUNT_LOOKUP(LOOKUP_FAIL); + return sym; +} + + +static unsigned gnuhash(const char *_name) +{ + const unsigned char *name = (const unsigned char *) _name; + unsigned h = 5381; + unsigned char ch; + while ((ch = *name++) != '\0') + h = (h << 5) + h + ch; + return h & 0xffffffff; +} + static unsigned elfhash(const char *_name) { const unsigned char *name = (const unsigned char *) _name; @@ -448,6 +529,7 @@ static Elf32_Sym * _do_lookup(soinfo *si, const char *name, unsigned *base) { unsigned elf_hash = elfhash(name); + unsigned gnu_hash = gnuhash(name); Elf32_Sym *s; unsigned *d; soinfo *lsi = si; @@ -462,14 +544,14 @@ _do_lookup(soinfo *si, const char *name, unsigned *base) * dynamic linking. Some systems return the first definition found * and some the first non-weak definition. This is system dependent. * Here we return the first definition found for simplicity. */ - s = _elf_lookup(si, elf_hash, name); + s = _symbol_lookup(si, elf_hash, gnu_hash, name); if(s != NULL) goto done; /* Next, look for it in the preloads list */ for(i = 0; preloads[i] != NULL; i++) { lsi = preloads[i]; - s = _elf_lookup(lsi, elf_hash, name); + s = _symbol_lookup(lsi, elf_hash, gnu_hash, name); if(s != NULL) goto done; } @@ -485,7 +567,7 @@ _do_lookup(soinfo *si, const char *name, unsigned *base) DEBUG("%5d %s: looking up %s in %s\n", pid, si->name, name, lsi->name); - s = _elf_lookup(lsi, elf_hash, name); + s = _symbol_lookup(lsi, elf_hash, gnu_hash, name); if ((s != NULL) && (s->st_shndx != SHN_UNDEF)) goto done; } @@ -500,7 +582,7 @@ _do_lookup(soinfo *si, const char *name, unsigned *base) lsi = somain; DEBUG("%5d %s: looking up %s in executable %s\n", pid, si->name, name, lsi->name); - s = _elf_lookup(lsi, elf_hash, name); + s = _symbol_lookup(lsi, elf_hash, gnu_hash, name); } #endif @@ -521,7 +603,7 @@ done: */ Elf32_Sym *lookup_in_library(soinfo *si, const char *name) { - return _elf_lookup(si, elfhash(name), name); + return _symbol_lookup(si, elfhash(name), gnuhash(name), name); } /* This is used by dl_sym(). It performs a global symbol lookup. @@ -529,6 +611,7 @@ Elf32_Sym *lookup_in_library(soinfo *si, const char *name) Elf32_Sym *lookup(const char *name, soinfo **found, soinfo *start) { unsigned elf_hash = elfhash(name); + unsigned gnu_hash = gnuhash(name); Elf32_Sym *s = NULL; soinfo *si; @@ -540,7 +623,7 @@ Elf32_Sym *lookup(const char *name, soinfo **found, soinfo *start) { if(si->flags & FLAG_ERROR) continue; - s = _elf_lookup(si, elf_hash, name); + s = _symbol_lookup(si, elf_hash, gnu_hash, name); if (s != NULL) { *found = si; break; @@ -1824,6 +1907,19 @@ static int link_image(soinfo *si, unsigned wr_offset) si->bucket = (unsigned *) (si->base + *d + 8); si->chain = (unsigned *) (si->base + *d + 8 + si->nbucket * 4); break; + case DT_GNU_HASH: + { + unsigned symbias; + si->gnu_nbucket = ((unsigned *) (si->base + *d))[0]; + symbias = ((unsigned *) (si->base + *d))[1]; + si->gnu_bitmask_words = ((unsigned *) (si->base + *d))[2]; + si->gnu_shift = ((unsigned *) (si->base + *d))[3]; + si->gnu_bitmask = (unsigned *) (si->base + *d + 16); + si->gnu_bucket = (unsigned *) (si->base + *d + 16 + + 4 * si->gnu_bitmask_words); + si->gnu_chain = (si->gnu_bucket + si->gnu_nbucket - symbias); + } + break; case DT_STRTAB: si->strtab = (const char *) (si->base + *d); break; @@ -2149,7 +2245,6 @@ unsigned __linker_init(unsigned **elfdata) vecs++; } vecs++; - INFO("[ android linker & debugger ]\n"); DEBUG("%5d elfdata @ 0x%08x\n", pid, (unsigned)elfdata); @@ -2241,6 +2336,13 @@ unsigned __linker_init(unsigned **elfdata) linker_stats.reloc[RELOC_RELATIVE], linker_stats.reloc[RELOC_COPY], linker_stats.reloc[RELOC_SYMBOL]); + PRINT("LOOKUP STATS: %s: %d lookup, %d fail, %d elf, %d gnu," + " %d filter by bloom\n", argv[0], + linker_stats.lookup[LOOKUP_NUM], + linker_stats.lookup[LOOKUP_FAIL], + linker_stats.lookup[LOOKUP_ELF], + linker_stats.lookup[LOOKUP_GNU], + linker_stats.lookup[LOOKUP_BLOOM]); #endif #if COUNT_PAGES { diff --git a/linker/linker.h b/linker/linker.h index 8596973..c5adc36 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -114,6 +114,13 @@ struct soinfo unsigned *bucket; unsigned *chain; + unsigned gnu_nbucket; + unsigned gnu_shift; + unsigned gnu_bitmask_words; + unsigned *gnu_bitmask; + unsigned *gnu_bucket; + unsigned *gnu_chain; + unsigned *plt_got; Elf32_Rel *plt_rel; diff --git a/linker/linker_debug.h b/linker/linker_debug.h index 3f08303..3cd7ae3 100644 --- a/linker/linker_debug.h +++ b/linker/linker_debug.h @@ -111,8 +111,16 @@ extern int format_fd(int, const char *, ...); #define RELOC_SYMBOL 3 #define NUM_RELOC_STATS 4 +#define LOOKUP_NUM 0 +#define LOOKUP_FAIL 1 +#define LOOKUP_ELF 2 +#define LOOKUP_GNU 3 +#define LOOKUP_BLOOM 4 +#define NUM_LOOKUP_STATS 5 + struct _link_stats { int reloc[NUM_RELOC_STATS]; + int lookup[NUM_LOOKUP_STATS]; }; extern struct _link_stats linker_stats; @@ -123,8 +131,17 @@ extern struct _link_stats linker_stats; PRINT("Unknown reloc stat requested\n"); \ } \ } while(0) +#define COUNT_LOOKUP(type) \ + do { if (type >= 0 && type < NUM_LOOKUP_STATS) { \ + linker_stats.lookup[type] += 1; \ + } else { \ + PRINT("Unknown lookup stat requested\n"); \ + } \ + } while(0) + #else /* !STATS */ #define COUNT_RELOC(type) do {} while(0) +#define COUNT_LOOKUP(type) do {} while(0) #endif /* STATS */ #if TIMING -- 1.7.5.4
_______________________________________________ linaro-dev mailing list linaro-dev@lists.linaro.org http://lists.linaro.org/mailman/listinfo/linaro-dev