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.
This commit is contained in:
Rich Felker 2012-07-11 01:41:20 -04:00
parent bd1cf09c37
commit 0420b87446
1 changed files with 21 additions and 14 deletions

View File

@ -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;
}