diff options
Diffstat (limited to 'linker')
-rw-r--r-- | linker/linker.c | 127 |
1 files changed, 84 insertions, 43 deletions
diff --git a/linker/linker.c b/linker/linker.c index 9779290..1c2a28c 100644 --- a/linker/linker.c +++ b/linker/linker.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2008, 2009 The Android Open Source Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -68,7 +68,6 @@ * * open issues / todo: * - * - should we do anything special for STB_WEAK symbols? * - are we doing everything we should for ARM_COPY relocations? * - cleaner error reporting * - after linking, set as much stuff as possible to READONLY @@ -258,7 +257,7 @@ static soinfo *alloc_info(const char *name) if(strlen(name) >= SOINFO_NAME_LEN) { DL_ERR("%5d library name %s too long", pid, name); - return 0; + return NULL; } /* The freelist is populated when we call free_info(), which in turn is @@ -406,20 +405,20 @@ static Elf32_Sym *_elf_lookup(soinfo *si, unsigned hash, const char *name) s = symtab + n; if(strcmp(strtab + s->st_name, name)) continue; - /* only concern ourselves with global symbols */ + /* only concern ourselves with global and weak symbol definitions */ switch(ELF32_ST_BIND(s->st_info)){ case STB_GLOBAL: + case STB_WEAK: /* no section == undefined */ if(s->st_shndx == 0) continue; - case STB_WEAK: TRACE_TYPE(LOOKUP, "%5d FOUND %s in %s (%08x) %d\n", pid, name, si->name, s->st_value, s->st_size); return s; } } - return 0; + return NULL; } static unsigned elfhash(const char *_name) @@ -437,25 +436,23 @@ static unsigned elfhash(const char *_name) } static Elf32_Sym * -_do_lookup_in_so(soinfo *si, const char *name, unsigned *elf_hash) -{ - if (*elf_hash == 0) - *elf_hash = elfhash(name); - return _elf_lookup (si, *elf_hash, name); -} - -static Elf32_Sym * _do_lookup(soinfo *si, const char *name, unsigned *base) { - unsigned elf_hash = 0; + unsigned elf_hash = elfhash(name); Elf32_Sym *s; unsigned *d; soinfo *lsi = si; /* Look for symbols in the local scope first (the object who is * searching). This happens with C++ templates on i386 for some - * reason. */ - s = _do_lookup_in_so(si, name, &elf_hash); + * reason. + * + * Notes on weak symbols: + * The ELF specs are ambigious about treatment of weak definitions in + * 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); if(s != NULL) goto done; @@ -465,12 +462,12 @@ _do_lookup(soinfo *si, const char *name, unsigned *base) if (!validate_soinfo(lsi)) { DL_ERR("%5d bad DT_NEEDED pointer in %s", pid, si->name); - return 0; + return NULL; } DEBUG("%5d %s: looking up %s in %s\n", pid, si->name, name, lsi->name); - s = _do_lookup_in_so(lsi, name, &elf_hash); + s = _elf_lookup(lsi, elf_hash, name); if(s != NULL) goto done; } @@ -485,7 +482,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 = _do_lookup_in_so(lsi, name, &elf_hash); + s = _elf_lookup(lsi, elf_hash, name); } #endif @@ -498,7 +495,7 @@ done: return s; } - return 0; + return NULL; } /* This is used by dl_sym(). It performs symbol lookup only within the @@ -506,15 +503,14 @@ done: */ Elf32_Sym *lookup_in_library(soinfo *si, const char *name) { - unsigned unused = 0; - return _do_lookup_in_so(si, name, &unused); + return _elf_lookup(si, elfhash(name), name); } /* This is used by dl_sym(). It performs a global symbol lookup. */ Elf32_Sym *lookup(const char *name, soinfo **found) { - unsigned elf_hash = 0; + unsigned elf_hash = elfhash(name); Elf32_Sym *s = NULL; soinfo *si; @@ -522,7 +518,7 @@ Elf32_Sym *lookup(const char *name, soinfo **found) { if(si->flags & FLAG_ERROR) continue; - s = _do_lookup_in_so(si, name, &elf_hash); + s = _elf_lookup(si, elf_hash, name); if (s != NULL) { *found = si; break; @@ -535,7 +531,7 @@ Elf32_Sym *lookup(const char *name, soinfo **found) return s; } - return 0; + return NULL; } #if 0 @@ -1241,10 +1237,64 @@ static int reloc_library(soinfo *si, Elf32_Rel *rel, unsigned count) if(sym != 0) { sym_name = (char *)(strtab + symtab[sym].st_name); s = _do_lookup(si, sym_name, &base); - if(s == 0) { - DL_ERR("%5d cannot locate '%s'...", pid, sym_name); - return -1; - } + if(s == NULL) { + /* We only allow an undefined symbol if this is a weak + reference.. */ + s = &symtab[sym]; + if (ELF32_ST_BIND(s->st_info) != STB_WEAK) { + DL_ERR("%5d cannot locate '%s'...\n", pid, sym_name); + return -1; + } + + /* IHI0044C AAELF 4.5.1.1: + + Libraries are not searched to resolve weak references. + It is not an error for a weak reference to remain + unsatisfied. + + During linking, the value of an undefined weak reference is: + - Zero if the relocation type is absolute + - The address of the place if the relocation is pc-relative + - The address of nominial base address if the relocation + type is base-relative. + */ + + switch (type) { +#if defined(ANDROID_ARM_LINKER) + case R_ARM_JUMP_SLOT: + case R_ARM_GLOB_DAT: + case R_ARM_ABS32: + case R_ARM_RELATIVE: /* Don't care. */ + case R_ARM_NONE: /* Don't care. */ +#elif defined(ANDROID_X86_LINKER) + case R_386_JUMP_SLOT: + case R_386_GLOB_DAT: + case R_386_32: + case R_386_RELATIVE: /* Dont' care. */ +#endif /* ANDROID_*_LINKER */ + /* sym_addr was initialized to be zero above or relocation + code below does not care about value of sym_addr. + No need to do anything. */ + break; + +#if defined(ANDROID_X86_LINKER) + case R_386_PC32: + sym_addr = reloc; + break; +#endif /* ANDROID_X86_LINKER */ + +#if defined(ANDROID_ARM_LINKER) + case R_ARM_COPY: + /* Fall through. Can't really copy if weak symbol is + not found in run-time. */ +#endif /* ANDROID_ARM_LINKER */ + default: + DL_ERR("%5d unknown weak reloc type %d @ %p (%d)\n", + pid, type, rel, (int) (rel - start)); + return -1; + } + } else { + /* We got a definition. */ #if 0 if((base == 0) && (si->base != 0)){ /* linking from libraries to main image is bad */ @@ -1253,20 +1303,11 @@ static int reloc_library(soinfo *si, Elf32_Rel *rel, unsigned count) return -1; } #endif - // st_shndx==SHN_UNDEF means an undefined symbol. - // st_value should be 0 then, except that the low bit of st_value is - // used to indicate whether the symbol points to an ARM or thumb function, - // and should be ignored in the following check. - if ((s->st_shndx == SHN_UNDEF) && ((s->st_value & ~1) != 0)) { - DL_ERR("%5d In '%s', symbol=%s shndx=%d && value=0x%08x. We do not " - "handle this yet", pid, si->name, sym_name, s->st_shndx, - s->st_value); - return -1; - } - sym_addr = (unsigned)(s->st_value + base); + sym_addr = (unsigned)(s->st_value + base); + } COUNT_RELOC(RELOC_SYMBOL); } else { - s = 0; + s = NULL; } /* TODO: This is ugly. Split up the relocations by arch into @@ -1909,7 +1950,7 @@ static int link_image(soinfo *si, unsigned wr_offset) return 0; fail: - ERROR("failed to link %s\n", si->name); + DL_ERR("failed to link %s\n", si->name); si->flags |= FLAG_ERROR; return -1; } |