diff options
author | Jean-Baptiste Queru <jbq@google.com> | 2009-11-12 18:45:14 -0800 |
---|---|---|
committer | Jean-Baptiste Queru <jbq@google.com> | 2009-11-12 18:45:14 -0800 |
commit | 194d3fa048cf909ca592dd56fa538dc9cd3f5ddb (patch) | |
tree | 3a00bb9e267cf952d7d1140ff9a39ca07ee6c994 /linker | |
parent | fde8642fc43bdd224e43e5ee9583a49a758fb03c (diff) | |
download | bionic-194d3fa048cf909ca592dd56fa538dc9cd3f5ddb.zip bionic-194d3fa048cf909ca592dd56fa538dc9cd3f5ddb.tar.gz bionic-194d3fa048cf909ca592dd56fa538dc9cd3f5ddb.tar.bz2 |
eclair snapshot
Diffstat (limited to 'linker')
-rw-r--r-- | linker/ba.c | 76 | ||||
-rw-r--r-- | linker/ba.h | 31 | ||||
-rw-r--r-- | linker/dlfcn.c | 12 | ||||
-rw-r--r-- | linker/linker.c | 166 | ||||
-rw-r--r-- | linker/linker.h | 2 |
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 |