summaryrefslogtreecommitdiffstats
path: root/linker
diff options
context:
space:
mode:
Diffstat (limited to 'linker')
-rw-r--r--linker/ba.c76
-rw-r--r--linker/ba.h31
-rw-r--r--linker/dlfcn.c12
-rw-r--r--linker/linker.c166
-rw-r--r--linker/linker.h2
5 files changed, 188 insertions, 99 deletions
diff --git a/linker/ba.c b/linker/ba.c
index bea6f84..db49c4b 100644
--- a/linker/ba.c
+++ b/linker/ba.c
@@ -30,65 +30,41 @@
#include "linker_debug.h"
#include "ba.h"
-struct ba_bits {
- unsigned allocated:1; /* 1 if allocated, 0 if free */
- unsigned order:7; /* size of the region in ba space */
-};
-
-struct ba_info {
- /* start address of the ba space */
- unsigned long base;
- /* total size of the ba space */
- unsigned long size;
- /* number of entries in the ba space */
- int num_entries;
- /* the bitmap for the region indicating which entries are allocated
- * and which are free */
- struct ba_bits *bitmap;
-};
-
#undef min
#define min(a,b) ((a)<(b)?(a):(b))
-#define BA_MIN_ALLOC LIBINC
-#define BA_MAX_ORDER 128
-#define BA_START LIBBASE
-#define BA_SIZE (LIBLAST - LIBBASE)
-
-#define BA_IS_FREE(index) (!(ba.bitmap[index].allocated))
-#define BA_ORDER(index) ba.bitmap[index].order
+#define BA_IS_FREE(index) (!(ba->bitmap[index].allocated))
+#define BA_ORDER(index) ba->bitmap[index].order
#define BA_BUDDY_INDEX(index) ((index) ^ (1 << BA_ORDER(index)))
#define BA_NEXT_INDEX(index) ((index) + (1 << BA_ORDER(index)))
-#define BA_OFFSET(index) ((index) * BA_MIN_ALLOC)
-#define BA_START_ADDR(index) (BA_OFFSET(index) + ba.base)
-#define BA_LEN(index) ((1 << BA_ORDER(index)) * BA_MIN_ALLOC)
-
-static struct ba_bits ba_bitmap[BA_SIZE / BA_MIN_ALLOC];
+#define BA_OFFSET(index) ((index) * ba->min_alloc)
+#define BA_START_ADDR(index) (BA_OFFSET(index) + ba->base)
+#define BA_LEN(index) ((1 << BA_ORDER(index)) * ba->min_alloc)
-static struct ba_info ba = {
- .base = BA_START,
- .size = BA_SIZE,
- .bitmap = ba_bitmap,
- .num_entries = sizeof(ba_bitmap)/sizeof(ba_bitmap[0]),
-};
+static unsigned long ba_order(struct ba *ba, unsigned long len);
-void ba_init(void)
+void ba_init(struct ba *ba)
{
int i, index = 0;
- for (i = sizeof(ba.num_entries) * 8 - 1; i >= 0; i--) {
- if (ba.num_entries & 1<<i) {
+
+ unsigned long max_order = ba_order(ba, ba->size);
+ if (ba->max_order == 0 || ba->max_order > max_order)
+ ba->max_order = max_order;
+
+ for (i = sizeof(ba->num_entries) * 8 - 1; i >= 0; i--) {
+ if (ba->num_entries & 1<<i) {
BA_ORDER(index) = i;
index = BA_NEXT_INDEX(index);
}
}
}
-int ba_free(int index)
+int ba_free(struct ba *ba, int index)
{
int buddy, curr = index;
/* clean up the bitmap, merging any buddies */
- ba.bitmap[curr].allocated = 0;
+ ba->bitmap[curr].allocated = 0;
/* find a slots buddy Buddy# = Slot# ^ (1 << order)
* if the buddy is also free merge them
* repeat until the buddy is not free or end of the bitmap is reached
@@ -103,16 +79,16 @@ int ba_free(int index)
} else {
break;
}
- } while (curr < ba.num_entries);
+ } while (curr < ba->num_entries);
return 0;
}
-static unsigned long ba_order(unsigned long len)
+static unsigned long ba_order(struct ba *ba, unsigned long len)
{
unsigned long i;
- len = (len + BA_MIN_ALLOC - 1) / BA_MIN_ALLOC;
+ len = (len + ba->min_alloc - 1) / ba->min_alloc;
len--;
for (i = 0; i < sizeof(len)*8; i++)
if (len >> i == 0)
@@ -120,14 +96,14 @@ static unsigned long ba_order(unsigned long len)
return i;
}
-int ba_allocate(unsigned long len)
+int ba_allocate(struct ba *ba, unsigned long len)
{
int curr = 0;
- int end = ba.num_entries;
+ int end = ba->num_entries;
int best_fit = -1;
- unsigned long order = ba_order(len);
+ unsigned long order = ba_order(ba, len);
- if (order > BA_MAX_ORDER)
+ if (order > ba->max_order)
return -1;
/* look through the bitmap:
@@ -165,16 +141,16 @@ int ba_allocate(unsigned long len)
buddy = BA_BUDDY_INDEX(best_fit);
BA_ORDER(buddy) = BA_ORDER(best_fit);
}
- ba.bitmap[best_fit].allocated = 1;
+ ba->bitmap[best_fit].allocated = 1;
return best_fit;
}
-unsigned long ba_start_addr(int index)
+unsigned long ba_start_addr(struct ba *ba, int index)
{
return BA_START_ADDR(index);
}
-unsigned long ba_len(int index)
+unsigned long ba_len(struct ba *ba, int index)
{
return BA_LEN(index);
}
diff --git a/linker/ba.h b/linker/ba.h
index 78f4626..c11017b 100644
--- a/linker/ba.h
+++ b/linker/ba.h
@@ -29,10 +29,31 @@
#ifndef __LINKER_BA_H
#define __LINKER_BA_H
-extern void ba_init(void);
-extern int ba_allocate(unsigned long len);
-extern int ba_free(int index);
-extern unsigned long ba_start_addr(int index);
-extern unsigned long ba_len(int index);
+struct ba_bits {
+ unsigned allocated:1; /* 1 if allocated, 0 if free */
+ unsigned order:7; /* size of the region in ba space */
+};
+
+struct ba {
+ /* start address of the ba space */
+ unsigned long base;
+ /* total size of the ba space */
+ unsigned long size;
+ /* the smaller allocation that can be made */
+ unsigned long min_alloc;
+ /* the order of the largest allocation that can be made */
+ unsigned long max_order;
+ /* number of entries in the ba space */
+ int num_entries;
+ /* the bitmap for the region indicating which entries are allocated
+ * and which are free */
+ struct ba_bits *bitmap;
+};
+
+extern void ba_init(struct ba *ba);
+extern int ba_allocate(struct ba *ba, unsigned long len);
+extern int ba_free(struct ba *ba, int index);
+extern unsigned long ba_start_addr(struct ba *ba, int index);
+extern unsigned long ba_len(struct ba *ba, int index);
#endif
diff --git a/linker/dlfcn.c b/linker/dlfcn.c
index b54674f..053713c 100644
--- a/linker/dlfcn.c
+++ b/linker/dlfcn.c
@@ -74,7 +74,7 @@ const char *dlerror(void)
void *dlsym(void *handle, const char *symbol)
{
- unsigned base;
+ soinfo *found;
Elf32_Sym *sym;
unsigned bind;
@@ -90,19 +90,19 @@ void *dlsym(void *handle, const char *symbol)
}
if(handle == RTLD_DEFAULT) {
- sym = lookup(symbol, &base);
+ sym = lookup(symbol, &found);
} else if(handle == RTLD_NEXT) {
- sym = lookup(symbol, &base);
+ sym = lookup(symbol, &found);
} else {
- sym = lookup_in_library((soinfo*) handle, symbol);
- base = ((soinfo*) handle)->base;
+ found = (soinfo*)handle;
+ sym = lookup_in_library(found, symbol);
}
if(likely(sym != 0)) {
bind = ELF32_ST_BIND(sym->st_info);
if(likely((bind == STB_GLOBAL) && (sym->st_shndx != 0))) {
- unsigned ret = sym->st_value + base;
+ unsigned ret = sym->st_value + found->base;
pthread_mutex_unlock(&dl_lock);
return (void*)ret;
}
diff --git a/linker/linker.c b/linker/linker.c
index 789a828..5090b11 100644
--- a/linker/linker.c
+++ b/linker/linker.c
@@ -51,6 +51,7 @@
#include "ba.h"
+#define ALLOW_SYMBOLS_FROM_MAIN 1
#define SO_MAX 96
/* Assume average path length of 64 and max 8 paths */
@@ -86,6 +87,27 @@ static soinfo sopool[SO_MAX];
static soinfo *freelist = NULL;
static soinfo *solist = &libdl_info;
static soinfo *sonext = &libdl_info;
+#if ALLOW_SYMBOLS_FROM_MAIN
+static soinfo *somain; /* main process, always the one after libdl_info */
+#endif
+
+
+/* Set up for the buddy allocator managing the prelinked libraries. */
+static struct ba_bits ba_prelink_bitmap[(LIBLAST - LIBBASE) / LIBINC];
+static struct ba ba_prelink = {
+ .base = LIBBASE,
+ .size = LIBLAST - LIBBASE,
+ .min_alloc = LIBINC,
+ /* max_order will be determined automatically */
+ .bitmap = ba_prelink_bitmap,
+ .num_entries = sizeof(ba_prelink_bitmap)/sizeof(ba_prelink_bitmap[0]),
+};
+
+static inline int validate_soinfo(soinfo *si)
+{
+ return (si >= sopool && si < sopool + SO_MAX) ||
+ si == &libdl_info;
+}
static char ldpaths_buf[LDPATH_BUFSIZE];
static const char *ldpaths[LDPATH_MAX + 1];
@@ -421,41 +443,92 @@ _do_lookup_in_so(soinfo *si, const char *name, unsigned *elf_hash)
return _elf_lookup (si, *elf_hash, name);
}
-/* This is used by dl_sym() */
+static Elf32_Sym *
+_do_lookup(soinfo *si, const char *name, unsigned *base)
+{
+ unsigned elf_hash = 0;
+ 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);
+ if(s != NULL)
+ goto done;
+
+ for(d = si->dynamic; *d; d += 2) {
+ if(d[0] == DT_NEEDED){
+ lsi = (soinfo *)d[1];
+ if (!validate_soinfo(lsi)) {
+ DL_ERR("%5d bad DT_NEEDED pointer in %s",
+ pid, si->name);
+ return 0;
+ }
+
+ DEBUG("%5d %s: looking up %s in %s\n",
+ pid, si->name, name, lsi->name);
+ s = _do_lookup_in_so(lsi, name, &elf_hash);
+ if(s != NULL)
+ goto done;
+ }
+ }
+
+#if ALLOW_SYMBOLS_FROM_MAIN
+ /* If we are resolving relocations while dlopen()ing a library, it's OK for
+ * the library to resolve a symbol that's defined in the executable itself,
+ * although this is rare and is generally a bad idea.
+ */
+ if (somain) {
+ 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);
+ }
+#endif
+
+done:
+ if(s != NULL) {
+ TRACE_TYPE(LOOKUP, "%5d si %s sym %s s->st_value = 0x%08x, "
+ "found in %s, base = 0x%08x\n",
+ pid, si->name, name, s->st_value, lsi->name, lsi->base);
+ *base = lsi->base;
+ return s;
+ }
+
+ return 0;
+}
+
+/* This is used by dl_sym(). It performs symbol lookup only within the
+ specified soinfo object and not in any of its dependencies.
+ */
Elf32_Sym *lookup_in_library(soinfo *si, const char *name)
{
unsigned unused = 0;
return _do_lookup_in_so(si, name, &unused);
}
-static Elf32_Sym *
-_do_lookup(soinfo *user_si, const char *name, unsigned *base)
+/* This is used by dl_sym(). It performs a global symbol lookup.
+ */
+Elf32_Sym *lookup(const char *name, soinfo **found)
{
unsigned elf_hash = 0;
Elf32_Sym *s = NULL;
soinfo *si;
- /* Look for symbols in the local scope first (the object who is
- * searching). This happens with C++ templates on i386 for some
- * reason. */
- if (user_si) {
- s = _do_lookup_in_so(user_si, name, &elf_hash);
- if (s != NULL)
- *base = user_si->base;
- }
-
for(si = solist; (s == NULL) && (si != NULL); si = si->next)
{
- if((si->flags & FLAG_ERROR) || (si == user_si))
+ if(si->flags & FLAG_ERROR)
continue;
s = _do_lookup_in_so(si, name, &elf_hash);
if (s != NULL) {
- *base = si->base;
+ *found = si;
break;
}
}
- if (s != NULL) {
+ if(s != NULL) {
TRACE_TYPE(LOOKUP, "%5d %s s->st_value = 0x%08x, "
"si->base = 0x%08x\n", pid, name, s->st_value, si->base);
return s;
@@ -464,12 +537,6 @@ _do_lookup(soinfo *user_si, const char *name, unsigned *base)
return 0;
}
-/* This is used by dl_sym() */
-Elf32_Sym *lookup(const char *name, unsigned *base)
-{
- return _do_lookup(NULL, name, base);
-}
-
#if 0
static void dump(soinfo *si)
{
@@ -728,14 +795,14 @@ alloc_mem_region(soinfo *si)
for it from the buddy allocator, which manages the area between
LIBBASE and LIBLAST.
*/
- si->ba_index = ba_allocate(si->size);
+ si->ba_index = ba_allocate(&ba_prelink, si->size);
if(si->ba_index >= 0) {
- si->base = ba_start_addr(si->ba_index);
+ si->base = ba_start_addr(&ba_prelink, si->ba_index);
PRINT("%5d mapping library '%s' at %08x (index %d) " \
"through buddy allocator.\n",
pid, si->name, si->base, si->ba_index);
if (reserve_mem_region(si) < 0) {
- ba_free(si->ba_index);
+ ba_free(&ba_prelink, si->ba_index);
si->ba_index = -1;
si->base = 0;
goto err;
@@ -1031,7 +1098,7 @@ load_library(const char *name)
/* Now actually load the library's segments into right places in memory */
if (load_segments(fd, &__header[0], si) < 0) {
if (si->ba_index >= 0) {
- ba_free(si->ba_index);
+ ba_free(&ba_prelink, si->ba_index);
si->ba_index = -1;
}
goto fail;
@@ -1085,7 +1152,10 @@ soinfo *find_library(const char *name)
for(si = solist; si != 0; si = si->next){
if(!strcmp(bname, si->name)) {
- if(si->flags & FLAG_ERROR) return 0;
+ if(si->flags & FLAG_ERROR) {
+ DL_ERR("%5d '%s' failed to load previously", pid, bname);
+ return NULL;
+ }
if(si->flags & FLAG_LINKED) return si;
DL_ERR("OOPS: %5d recursive link to '%s'", pid, si->name);
return NULL;
@@ -1113,14 +1183,16 @@ unsigned unload_library(soinfo *si)
for(d = si->dynamic; *d; d += 2) {
if(d[0] == DT_NEEDED){
- TRACE("%5d %s needs to unload %s\n", pid,
- si->name, si->strtab + d[1]);
- soinfo *lsi = find_library(si->strtab + d[1]);
- if(lsi)
+ soinfo *lsi = (soinfo *)d[1];
+ d[1] = 0;
+ if (validate_soinfo(lsi)) {
+ TRACE("%5d %s needs to unload %s\n", pid,
+ si->name, lsi->name);
unload_library(lsi);
+ }
else
- DL_ERR("%5d could not unload '%s'",
- pid, si->strtab + d[1]);
+ DL_ERR("%5d %s: could not unload dependent library",
+ pid, si->name);
}
}
@@ -1129,7 +1201,7 @@ unsigned unload_library(soinfo *si)
PRINT("%5d releasing library '%s' address space at %08x "\
"through buddy allocator.\n",
pid, si->name, si->base);
- ba_free(si->ba_index);
+ ba_free(&ba_prelink, si->ba_index);
}
notify_gdb_of_unload(si);
free_info(si);
@@ -1180,9 +1252,13 @@ static int reloc_library(soinfo *si, Elf32_Rel *rel, unsigned count)
return -1;
}
#endif
- if ((s->st_shndx == SHN_UNDEF) && (s->st_value != 0)) {
- DL_ERR("%5d In '%s', shndx=%d && value=0x%08x. We do not "
- "handle this yet", pid, si->name, s->st_shndx,
+ // 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;
}
@@ -1638,6 +1714,14 @@ static int link_image(soinfo *si, unsigned wr_offset)
pid, si->strtab + d[1], si->name, tmp_err_buf);
goto fail;
}
+ /* Save the soinfo of the loaded DT_NEEDED library in the payload
+ of the DT_NEEDED entry itself, so that we can retrieve the
+ soinfo directly later from the dynamic segment. This is a hack,
+ but it allows us to map from DT_NEEDED to soinfo efficiently
+ later on when we resolve relocations, trying to look up a symgol
+ with dlsym().
+ */
+ d[1] = (unsigned)lsi;
lsi->refcount++;
}
}
@@ -1825,7 +1909,7 @@ unsigned __linker_init(unsigned **elfdata)
vecs += 2;
}
- ba_init();
+ ba_init(&ba_prelink);
si->base = 0;
si->dynamic = (unsigned *)-1;
@@ -1843,6 +1927,14 @@ unsigned __linker_init(unsigned **elfdata)
exit(-1);
}
+#if ALLOW_SYMBOLS_FROM_MAIN
+ /* Set somain after we've loaded all the libraries in order to prevent
+ * linking of symbols back to the main image, which is not set up at that
+ * point yet.
+ */
+ somain = si;
+#endif
+
#if TIMING
gettimeofday(&t1,NULL);
PRINT("LINKER TIME: %s: %d microseconds\n", argv[0], (int) (
diff --git a/linker/linker.h b/linker/linker.h
index 69042c0..d289c81 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -203,7 +203,7 @@ extern soinfo libdl_info;
soinfo *find_library(const char *name);
unsigned unload_library(soinfo *si);
Elf32_Sym *lookup_in_library(soinfo *si, const char *name);
-Elf32_Sym *lookup(const char *name, unsigned *base);
+Elf32_Sym *lookup(const char *name, soinfo **found);
const char *linker_get_error(void);
#ifdef ANDROID_ARM_LINKER