diff options
author | Iliyan Malchev <malchev@google.com> | 2009-09-24 17:14:33 -0700 |
---|---|---|
committer | Iliyan Malchev <malchev@google.com> | 2009-09-27 17:18:06 -0700 |
commit | 8d0c0334f1106d36f2fd5c1cf6d5dc75a4b88850 (patch) | |
tree | 4f0b57fa3743ec253faac9ced5e4d9586920a633 /linker | |
parent | bc10cd2900cdb7fed077163b6a33e0f8572b2b19 (diff) | |
download | bionic-8d0c0334f1106d36f2fd5c1cf6d5dc75a4b88850.zip bionic-8d0c0334f1106d36f2fd5c1cf6d5dc75a4b88850.tar.gz bionic-8d0c0334f1106d36f2fd5c1cf6d5dc75a4b88850.tar.bz2 |
bionic/linker: fix symbol lookup during relocations
When resolving relocations while loading a library, the linker used to find
symbols by looking them up in the list of all linked libraries for the current
process, as opposed to following just the library's DT_NEEDED entries. This
can cause a problem where the symbol is picked up from the wrong library.
Signed-off-by: Iliyan Malchev <malchev@google.com>
Diffstat (limited to 'linker')
-rw-r--r-- | linker/linker.c | 100 |
1 files changed, 74 insertions, 26 deletions
diff --git a/linker/linker.c b/linker/linker.c index eed9738..6f09837 100644 --- a/linker/linker.c +++ b/linker/linker.c @@ -87,6 +87,12 @@ static soinfo *freelist = NULL; static soinfo *solist = &libdl_info; static soinfo *sonext = &libdl_info; +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,32 +427,70 @@ _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; + } + } + +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, unsigned *base) { 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) { @@ -455,7 +499,7 @@ _do_lookup(soinfo *user_si, const char *name, unsigned *base) } } - 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 +508,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) { @@ -1116,14 +1154,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); } } @@ -1641,6 +1681,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++; } } |