summaryrefslogtreecommitdiff
path: root/src/ldso/dynlink.c
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2013-08-08 16:10:35 -0400
committerRich Felker <dalias@aerifal.cx>2013-08-08 16:10:35 -0400
commit72482f9020e4362b8d35849df6dca49f26d24c54 (patch)
tree7a635929d2df888adfcf47d98e91ff0ed06aa332 /src/ldso/dynlink.c
parente28c2ecae43468e2abf92957d3622dc830d90ada (diff)
downloadmusl-72482f9020e4362b8d35849df6dca49f26d24c54.tar.gz
work around libraries with versioned symbols in dynamic linker
this commit does not add versioning support; it merely fixes incorrect lookups of symbols in libraries that contain versioned symbols. previously, the version information was completely ignored, and empirically this seems to have resulted in the oldest version being chosen, but I am uncertain if that behavior was even reliable. the new behavior being introduced is to completely ignore symbols which are marked "hidden" (this seems to be the confusing nomenclature for non-current-version) when versioning is present. this should solve all problems related to libraries with symbol versioning as long as all binaries involved are up-to-date (compatible with the latest-version symbols), and it's the needed behavior for dlsym under all circumstances.
Diffstat (limited to 'src/ldso/dynlink.c')
-rw-r--r--src/ldso/dynlink.c25
1 files changed, 14 insertions, 11 deletions
diff --git a/src/ldso/dynlink.c b/src/ldso/dynlink.c
index fa5ad004..ad987b8f 100644
--- a/src/ldso/dynlink.c
+++ b/src/ldso/dynlink.c
@@ -63,6 +63,7 @@ struct dso {
Sym *syms;
uint32_t *hashtab;
uint32_t *ghashtab;
+ int16_t *versym;
char *strings;
unsigned char *map;
size_t map_len;
@@ -156,7 +157,8 @@ static Sym *sysv_lookup(const char *s, uint32_t h, struct dso *dso)
uint32_t *hashtab = dso->hashtab;
char *strings = dso->strings;
for (i=hashtab[2+h%hashtab[0]]; i; i=hashtab[2+hashtab[0]+i]) {
- if (!strcmp(s, strings+syms[i].st_name))
+ if ((!dso->versym || dso->versym[i] >= 0)
+ && (!strcmp(s, strings+syms[i].st_name)))
return syms+i;
}
return 0;
@@ -164,25 +166,24 @@ static Sym *sysv_lookup(const char *s, uint32_t h, struct dso *dso)
static Sym *gnu_lookup(const char *s, uint32_t h1, struct dso *dso)
{
- Sym *sym;
- char *strings;
+ Sym *syms = dso->syms;
+ char *strings = dso->strings;
uint32_t *hashtab = dso->ghashtab;
uint32_t nbuckets = hashtab[0];
uint32_t *buckets = hashtab + 4 + hashtab[2]*(sizeof(size_t)/4);
uint32_t h2;
uint32_t *hashval;
- uint32_t n = buckets[h1 % nbuckets];
+ uint32_t i = buckets[h1 % nbuckets];
- if (!n) return 0;
+ if (!i) return 0;
- strings = dso->strings;
- sym = dso->syms + n;
- hashval = buckets + nbuckets + (n - hashtab[1]);
+ hashval = buckets + nbuckets + (i - hashtab[1]);
- for (h1 |= 1; ; sym++) {
+ for (h1 |= 1; ; i++) {
h2 = *hashval++;
- if ((h1 == (h2|1)) && !strcmp(s, strings + sym->st_name))
- return sym;
+ if ((!dso->versym || dso->versym[i] >= 0)
+ && (h1 == (h2|1)) && !strcmp(s, strings + syms[i].st_name))
+ return syms+i;
if (h2 & 1) break;
}
@@ -456,6 +457,8 @@ static void decode_dyn(struct dso *p)
p->hashtab = (void *)(p->base + dyn[DT_HASH]);
if (search_vec(p->dynv, dyn, DT_GNU_HASH))
p->ghashtab = (void *)(p->base + *dyn);
+ if (search_vec(p->dynv, dyn, DT_VERSYM))
+ p->versym = (void *)(p->base + *dyn);
}
static struct dso *load_library(const char *name)