summaryrefslogtreecommitdiff
path: root/src/ldso/dynlink.c
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2012-07-11 01:41:20 -0400
committerRich Felker <dalias@aerifal.cx>2012-07-11 01:41:20 -0400
commit0420b874465db7544a9e1f320969b4920c9405d8 (patch)
treea6685f74c0ae8dd478a300533c3054516d7f121a /src/ldso/dynlink.c
parentbd1cf09c375e56c7fba6b7a1036b33b9591b5dba (diff)
downloadmusl-0420b874465db7544a9e1f320969b4920c9405d8.tar.gz
fix lots of breakage on dlopen, mostly with explicit pathnames
most importantly, the name for such libs was being set from an uninitialized buffer. also, shortname always had an initial '/' character, making it useless for looking up already-loaded libraries by name, and thus causing repeated searches through the library path. major changes now: - shortname is the base name for library lookups with no explicit pathname. it's initially clear for libraries loaded with an explicit pathname (and for the main program), but will be set if the same library (detected via inodes match) is later found by a search. - exact name match is never used to identify libraries loaded with an explicit pathname. in this case, there's no explicit search, so we can just stat the file and check for inode match.
Diffstat (limited to 'src/ldso/dynlink.c')
-rw-r--r--src/ldso/dynlink.c35
1 files changed, 21 insertions, 14 deletions
diff --git a/src/ldso/dynlink.c b/src/ldso/dynlink.c
index 263593ab..0a64ef8a 100644
--- a/src/ldso/dynlink.c
+++ b/src/ldso/dynlink.c
@@ -322,6 +322,7 @@ static void decode_dyn(struct dso *p)
static struct dso *load_library(const char *name)
{
char buf[2*NAME_MAX+2];
+ const char *pathname;
unsigned char *base, *map;
size_t dyno, map_len;
struct dso *p;
@@ -346,16 +347,17 @@ static struct dso *load_library(const char *name)
}
}
}
- /* Search for the name to see if it's already loaded */
- for (p=head->next; p; p=p->next) {
- if (!strcmp(p->shortname, name)) {
- p->refcnt++;
- return p;
- }
- }
if (strchr(name, '/')) {
+ pathname = name;
fd = open(name, O_RDONLY);
} else {
+ /* Search for the name to see if it's already loaded */
+ for (p=head->next; p; p=p->next) {
+ if (p->shortname && !strcmp(p->shortname, name)) {
+ p->refcnt++;
+ return p;
+ }
+ }
if (strlen(name) > NAME_MAX) return 0;
fd = -1;
if (r_path) fd = path_open(name, r_path, buf, sizeof buf);
@@ -372,6 +374,7 @@ static struct dso *load_library(const char *name)
if (sys_path) fd = path_open(name, sys_path, buf, sizeof buf);
else fd = path_open(name, "/lib:/usr/local/lib:/usr/lib", buf, sizeof buf);
}
+ pathname = buf;
}
if (fd < 0) return 0;
if (fstat(fd, &st) < 0) {
@@ -380,6 +383,10 @@ static struct dso *load_library(const char *name)
}
for (p=head->next; p; p=p->next) {
if (p->dev == st.st_dev && p->ino == st.st_ino) {
+ /* If this library was previously loaded with a
+ * pathname but a search found the same inode,
+ * setup its shortname so it can be found by name. */
+ if (!p->shortname) p->shortname = strrchr(p->name, '/')+1;
close(fd);
p->refcnt++;
return p;
@@ -388,7 +395,7 @@ static struct dso *load_library(const char *name)
map = map_library(fd, &map_len, &base, &dyno);
close(fd);
if (!map) return 0;
- p = calloc(1, sizeof *p + strlen(buf) + 1);
+ p = calloc(1, sizeof *p + strlen(pathname) + 1);
if (!p) {
munmap(map, map_len);
return 0;
@@ -404,15 +411,15 @@ static struct dso *load_library(const char *name)
p->ino = st.st_ino;
p->refcnt = 1;
p->name = p->buf;
- strcpy(p->name, buf);
- if (!strchr(name, '/')) p->shortname = strrchr(p->name, '/');
- if (!p->shortname) p->shortname = p->name;
+ strcpy(p->name, pathname);
+ /* Add a shortname only if name arg was not an explicit pathname. */
+ if (pathname != name) p->shortname = strrchr(p->name, '/')+1;
tail->next = p;
p->prev = tail;
tail = p;
- if (ldd_mode) dprintf(1, "\t%s => %s (%p)\n", name, buf, base);
+ if (ldd_mode) dprintf(1, "\t%s => %s (%p)\n", name, pathname, base);
return p;
}
@@ -576,7 +583,7 @@ void *__dynlink(int argc, char **argv)
if (phdr->p_type == PT_PHDR)
app->base = (void *)(aux[AT_PHDR] - phdr->p_vaddr);
}
- app->name = app->shortname = argv[0];
+ app->name = argv[0];
app->dynv = (void *)(app->base + find_dyn(
(void *)aux[AT_PHDR], aux[AT_PHNUM], aux[AT_PHENT]));
} else {
@@ -605,7 +612,7 @@ void *__dynlink(int argc, char **argv)
}
runtime = 0;
close(fd);
- app->name = app->shortname = argv[0];
+ app->name = argv[0];
app->dynv = (void *)(app->base + dyno);
aux[AT_ENTRY] = ehdr->e_entry;
}