summaryrefslogtreecommitdiffstats
path: root/linker
diff options
context:
space:
mode:
Diffstat (limited to 'linker')
-rw-r--r--linker/linker.c127
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;
}