fix lots of breakage on dlopen, mostly with explicit pathnames
authorRich Felker <dalias@aerifal.cx>
Wed, 11 Jul 2012 05:41:20 +0000 (01:41 -0400)
committerRich Felker <dalias@aerifal.cx>
Wed, 11 Jul 2012 05:41:20 +0000 (01:41 -0400)
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.

src/ldso/dynlink.c

index 263593ab75531e45e9d3f04f5f5740f68c611220..0a64ef8ae2decafba3cf592da87ed34319cf615f 100644 (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;
        }