merge from eclair
This commit is contained in:
182
linker/linker.c
182
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];
|
||||
@@ -422,53 +444,98 @@ _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() */
|
||||
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)
|
||||
_do_lookup(soinfo *si, const char *name, unsigned *base)
|
||||
{
|
||||
unsigned elf_hash = 0;
|
||||
Elf32_Sym *s = NULL;
|
||||
soinfo *si;
|
||||
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. */
|
||||
if (user_si) {
|
||||
s = _do_lookup_in_so(user_si, name, &elf_hash);
|
||||
if (s != NULL)
|
||||
*base = user_si->base;
|
||||
}
|
||||
s = _do_lookup_in_so(si, name, &elf_hash);
|
||||
if(s != NULL)
|
||||
goto done;
|
||||
|
||||
for(si = solist; (s == NULL) && (si != NULL); si = si->next)
|
||||
{
|
||||
if((si->flags & FLAG_ERROR) || (si == user_si))
|
||||
continue;
|
||||
s = _do_lookup_in_so(si, name, &elf_hash);
|
||||
if (s != NULL) {
|
||||
*base = si->base;
|
||||
break;
|
||||
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 (s != NULL) {
|
||||
TRACE_TYPE(LOOKUP, "%5d %s s->st_value = 0x%08x, "
|
||||
"si->base = 0x%08x\n", pid, name, s->st_value, si->base);
|
||||
#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() */
|
||||
Elf32_Sym *lookup(const char *name, unsigned *base)
|
||||
/* 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)
|
||||
{
|
||||
return _do_lookup(NULL, name, base);
|
||||
unsigned unused = 0;
|
||||
return _do_lookup_in_so(si, name, &unused);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
for(si = solist; (s == NULL) && (si != NULL); si = si->next)
|
||||
{
|
||||
if(si->flags & FLAG_ERROR)
|
||||
continue;
|
||||
s = _do_lookup_in_so(si, name, &elf_hash);
|
||||
if (s != NULL) {
|
||||
*found = si;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
@@ -729,14 +796,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;
|
||||
@@ -1032,7 +1099,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;
|
||||
@@ -1086,7 +1153,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;
|
||||
@@ -1114,14 +1184,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1130,7 +1202,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);
|
||||
@@ -1181,9 +1253,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;
|
||||
}
|
||||
@@ -1755,6 +1831,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++;
|
||||
}
|
||||
}
|
||||
@@ -1955,7 +2039,7 @@ unsigned __linker_init(unsigned **elfdata)
|
||||
vecs += 2;
|
||||
}
|
||||
|
||||
ba_init();
|
||||
ba_init(&ba_prelink);
|
||||
|
||||
si->base = 0;
|
||||
si->dynamic = (unsigned *)-1;
|
||||
@@ -1973,6 +2057,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) (
|
||||
|
||||
Reference in New Issue
Block a user