aboutsummaryrefslogtreecommitdiff
path: root/elf
diff options
context:
space:
mode:
Diffstat (limited to 'elf')
-rw-r--r--elf/Makefile2
-rw-r--r--elf/dl-deps.c93
-rw-r--r--elf/dl-lookup.c105
-rw-r--r--elf/dl-minimal.c13
-rw-r--r--elf/dl-reloc.c20
-rw-r--r--elf/dl-runtime.c5
-rw-r--r--elf/dlfcn.h1
-rw-r--r--elf/dlopen.c31
-rw-r--r--elf/dlsym.c3
-rw-r--r--elf/link.h35
-rw-r--r--elf/linux-compat.c7
-rw-r--r--elf/rtld.c55
12 files changed, 221 insertions, 149 deletions
diff --git a/elf/Makefile b/elf/Makefile
index 6124fa95de..8f34a9cfd2 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -29,7 +29,7 @@ libdl-routines := dlopen dlclose dlsym dlerror
libdl-inhibit-o = $(filter-out .so,$(object-suffixes)) # Build only shared.
rtld-routines := rtld $(addprefix dl-,minimal load lookup object reloc \
- runtime sysdep error init fini)
+ deps runtime sysdep error init fini)
distribute = $(rtld-routines:=.c) dynamic-link.h do-rel.h \
soinit.c sofini.c ldd.sh.in linux-compat.c
diff --git a/elf/dl-deps.c b/elf/dl-deps.c
new file mode 100644
index 0000000000..f87475a670
--- /dev/null
+++ b/elf/dl-deps.c
@@ -0,0 +1,93 @@
+/* Load the dependencies of a mapped object.
+Copyright (C) 1996 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <link.h>
+#include <errno.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+
+void
+_dl_map_object_deps (struct link_map *map)
+{
+ unsigned int nlist = 1;
+ struct link_map **list = malloc (sizeof *list);
+ unsigned int done;
+
+ /* Start the search list with one element: MAP itself. */
+ list[0] = map;
+
+ /* Process each element of the search list, loading each of its immediate
+ dependencies and appending them to the list as we step through it.
+ This produces a flat, ordered list that represents a breadth-first
+ search of the dependency tree. */
+ for (done = 0; done < nlist; ++done)
+ {
+ struct link_map *l = list[done];
+ if (l->l_info[DT_NEEDED])
+ {
+ const char *strtab
+ = ((void *) l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr);
+ const Elf32_Dyn *d;
+ for (d = l->l_ld; d->d_tag != DT_NULL; ++d)
+ if (d->d_tag == DT_NEEDED)
+ {
+ /* Extend the list and put this object on the end. */
+ struct link_map **n
+ = realloc (list, (nlist + 1) * sizeof *list);
+ if (n)
+ list = n;
+ else
+ {
+ free (list);
+ _dl_signal_error (ENOMEM, map->l_name,
+ "finding dependencies");
+ }
+ list[nlist++] = _dl_map_object (l, strtab + d->d_un.d_val);
+ }
+ }
+ }
+
+ map->l_searchlist = list;
+ map->l_nsearchlist = nlist;
+}
+
+
+struct link_map *
+_dl_open (struct link_map *parent, const char *file, int mode)
+{
+ struct link_map *new, *l;
+ Elf32_Addr init;
+
+ /* Load the named object. */
+ new = _dl_map_object (parent, file);
+
+ /* Load that object's dependencies. */
+ _dl_map_object_deps (new);
+
+ /* Relocate the objects loaded. */
+ for (l = new; l; l = l->l_next)
+ if (! l->l_relocated)
+ _dl_relocate_object (l, (mode & RTLD_BINDING_MASK) == RTLD_LAZY);
+
+ /* Run the initializer functions of new objects. */
+ while (init = _dl_init_next ())
+ (*(void (*) (void)) init) ();
+
+ return new;
+}
diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
index b72a6a27d5..26357c2484 100644
--- a/elf/dl-lookup.c
+++ b/elf/dl-lookup.c
@@ -29,77 +29,84 @@ Cambridge, MA 02139, USA. */
Elf32_Addr
_dl_lookup_symbol (const char *undef_name, const Elf32_Sym **ref,
- struct link_map *symbol_scope,
+ struct link_map *symbol_scope[2],
const char *reference_name,
Elf32_Addr reloc_addr,
int noplt)
{
unsigned long int hash = elf_hash (undef_name);
- struct link_map *map;
struct
{
Elf32_Addr a;
const Elf32_Sym *s;
} weak_value = { 0, NULL };
+ size_t i;
+ struct link_map **scope, *map;
/* Search the relevant loaded objects for a definition. */
- for (map = symbol_scope; map; map = map->l_next)
- {
- const Elf32_Sym *symtab;
- const char *strtab;
- Elf32_Word symidx;
-
- symtab = ((void *) map->l_addr + map->l_info[DT_SYMTAB]->d_un.d_ptr);
- strtab = ((void *) map->l_addr + map->l_info[DT_STRTAB]->d_un.d_ptr);
-
- /* Search the appropriate hash bucket in this object's symbol table
- for a definition for the same symbol name. */
- for (symidx = map->l_buckets[hash % map->l_nbuckets];
- symidx != STN_UNDEF;
- symidx = map->l_chain[symidx])
+ for (scope = symbol_scope; scope < &symbol_scope[2]; ++scope)
+ if (*scope)
+ for (i = 0; i < (*scope)->l_nsearchlist; ++i)
{
- const Elf32_Sym *sym = &symtab[symidx];
+ const Elf32_Sym *symtab;
+ const char *strtab;
+ Elf32_Word symidx;
+
+ map = (*scope)->l_searchlist[i];
- if (sym->st_value == 0 || /* No value. */
- reloc_addr == map->l_addr + sym->st_value || /* Self ref. */
- (noplt && sym->st_shndx == SHN_UNDEF)) /* Unwanted PLT entry. */
- continue;
+ symtab = ((void *) map->l_addr + map->l_info[DT_SYMTAB]->d_un.d_ptr);
+ strtab = ((void *) map->l_addr + map->l_info[DT_STRTAB]->d_un.d_ptr);
- switch (ELF32_ST_TYPE (sym->st_info))
+ /* Search the appropriate hash bucket in this object's symbol table
+ for a definition for the same symbol name. */
+ for (symidx = map->l_buckets[hash % map->l_nbuckets];
+ symidx != STN_UNDEF;
+ symidx = map->l_chain[symidx])
{
- case STT_NOTYPE:
- case STT_FUNC:
- case STT_OBJECT:
- break;
- default:
- /* Not a code/data definition. */
- continue;
- }
+ const Elf32_Sym *sym = &symtab[symidx];
- if (sym != *ref && strcmp (strtab + sym->st_name, undef_name))
- /* Not the symbol we are looking for. */
- continue;
+ if (sym->st_value == 0 || /* No value. */
+ /* Cannot resolve to the location being filled in. */
+ reloc_addr == map->l_addr + sym->st_value ||
+ (noplt && sym->st_shndx == SHN_UNDEF)) /* Reject PLT. */
+ continue;
- switch (ELF32_ST_BIND (sym->st_info))
- {
- case STB_GLOBAL:
- /* Global definition. Just what we need. */
- *ref = sym;
- return map->l_addr;
- case STB_WEAK:
- /* Weak definition. Use this value if we don't find another. */
- if (! weak_value.s)
+ switch (ELF32_ST_TYPE (sym->st_info))
{
- weak_value.s = sym;
- weak_value.a = map->l_addr;
+ case STT_NOTYPE:
+ case STT_FUNC:
+ case STT_OBJECT:
+ break;
+ default:
+ /* Not a code/data definition. */
+ continue;
+ }
+
+ if (sym != *ref && strcmp (strtab + sym->st_name, undef_name))
+ /* Not the symbol we are looking for. */
+ continue;
+
+ switch (ELF32_ST_BIND (sym->st_info))
+ {
+ case STB_GLOBAL:
+ /* Global definition. Just what we need. */
+ *ref = sym;
+ return map->l_addr;
+ case STB_WEAK:
+ /* Weak definition. Use this value if we don't find
+ another. */
+ if (! weak_value.s)
+ {
+ weak_value.s = sym;
+ weak_value.a = map->l_addr;
+ }
+ break;
+ default:
+ /* Local symbols are ignored. */
+ break;
}
- break;
- default:
- /* Local symbols are ignored. */
- break;
}
}
- }
if (weak_value.s == NULL && ELF32_ST_BIND ((*ref)->st_info) != STB_WEAK)
{
diff --git a/elf/dl-minimal.c b/elf/dl-minimal.c
index 9e0a77032d..c61b51532b 100644
--- a/elf/dl-minimal.c
+++ b/elf/dl-minimal.c
@@ -1,5 +1,5 @@
/* Minimal replacements for basic facilities used in the dynamic linker.
-Copyright (C) 1995 Free Software Foundation, Inc.
+Copyright (C) 1995, 1996 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
@@ -84,10 +84,17 @@ free (void *ptr)
}
weak_symbol (free)
-/* This is never called. */
+/* This is only called with the most recent block returned by malloc. */
void *
realloc (void *ptr, size_t n)
-{ ptr += n; abort (); }
+{
+ void *new;
+ assert (ptr == alloc_last_block);
+ alloc_ptr = alloc_last_block;
+ new = malloc (n);
+ assert (new == ptr);
+ return new;
+}
weak_symbol (realloc)
/* Avoid signal frobnication in setjmp/longjmp. Keeps things smaller. */
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index 424d303800..bfa0174444 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -54,12 +54,11 @@ _dl_relocate_object (struct link_map *l, int lazy)
}
{
- struct link_map *real_next, *scope;
+ struct link_map *scope[2];
const char *strtab
= ((void *) l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr);
-
Elf32_Addr resolve (const Elf32_Sym **ref,
Elf32_Addr reloc_addr, int noplt)
{
@@ -67,16 +66,16 @@ _dl_relocate_object (struct link_map *l, int lazy)
l->l_name, reloc_addr, noplt);
}
- real_next = l->l_next;
if (l->l_info[DT_SYMBOLIC])
{
- if (l->l_prev)
- l->l_prev->l_next = real_next;
- l->l_next = _dl_loaded;
- scope = l;
+ scope[0] = l;
+ scope[1] = _dl_loaded;
}
else
- scope = _dl_loaded;
+ {
+ scope[0] = _dl_loaded;
+ scope[1] = l;
+ }
if (l->l_type == lt_interpreter)
/* We cannot be lazy when relocating the dynamic linker itself. It
@@ -87,11 +86,6 @@ _dl_relocate_object (struct link_map *l, int lazy)
lazy = 0;
ELF_DYNAMIC_RELOCATE (l, lazy, resolve);
-
- /* Restore list frobnication done above for DT_SYMBOLIC. */
- l->l_next = real_next;
- if (l->l_prev)
- l->l_prev->l_next = l;
}
/* Set up the PLT so its unrelocated entries will
diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c
index 0beba769fa..dce34f8a89 100644
--- a/elf/dl-runtime.c
+++ b/elf/dl-runtime.c
@@ -70,8 +70,9 @@ fixup (
Elf32_Addr resolve (const Elf32_Sym **ref,
Elf32_Addr reloc_addr, int noplt)
{
- return _dl_lookup_symbol (strtab + (*ref)->st_name, ref, _dl_loaded,
- l->l_name, reloc_addr, noplt);
+ struct link_map *scope[2] = { _dl_loaded, NULL };
+ return _dl_lookup_symbol (strtab + (*ref)->st_name, ref,
+ scope, l->l_name, reloc_addr, noplt);
}
/* Perform the specified relocation. */
diff --git a/elf/dlfcn.h b/elf/dlfcn.h
index a405baa473..8d19b8b847 100644
--- a/elf/dlfcn.h
+++ b/elf/dlfcn.h
@@ -24,6 +24,7 @@ Cambridge, MA 02139, USA. */
/* The MODE argument to `dlopen' contains one of the following: */
#define RTLD_LAZY 0x001 /* Lazy function call binding. */
#define RTLD_NOW 0x002 /* Immediate function call binding. */
+#define RTLD_BINDING_MASK 0x3 /* Mask of binding time value. */
/* If the following bit is set in the MODE argument to `dlopen',
the symbols of the loaded object and its dependencies are made
diff --git a/elf/dlopen.c b/elf/dlopen.c
index 26ed35ace6..74ab8bb715 100644
--- a/elf/dlopen.c
+++ b/elf/dlopen.c
@@ -24,38 +24,11 @@ Cambridge, MA 02139, USA. */
void *
dlopen (const char *file, int mode)
{
- struct link_map *new, *l;
+ struct link_map *new;
void doit (void)
{
- Elf32_Addr init;
-
- new = _dl_map_object (_dl_loaded, file);
-
- /* Map in any dependencies. */
- for (l = new; l; l = l->l_next)
- if (! l->l_deps_loaded)
- {
- if (l->l_info[DT_NEEDED])
- {
- const char *strtab
- = (void *) l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr;
- const Elf32_Dyn *d;
- for (d = l->l_ld; d->d_tag != DT_NULL; ++d)
- if (d->d_tag == DT_NEEDED)
- _dl_map_object (l, strtab + d->d_un.d_val);
- }
- l->l_deps_loaded = 1;
- }
-
- /* Relocate the objects loaded. */
- for (l = new; l; l = l->l_next)
- if (! l->l_relocated)
- _dl_relocate_object (l, mode == RTLD_LAZY);
-
- /* Run the initializer functions of new objects. */
- while (init = _dl_init_next ())
- (*(void (*) (void)) init) ();
+ new = _dl_open (_dl_loaded, file, mode);
}
return _dlerror_run (doit) ? NULL : new;
diff --git a/elf/dlsym.c b/elf/dlsym.c
index cc50650fc9..804d404bb3 100644
--- a/elf/dlsym.c
+++ b/elf/dlsym.c
@@ -33,7 +33,8 @@ dlsym (void *handle, const char *name)
int lose;
void doit (void)
{
- loadbase = _dl_lookup_symbol (name, &ref, map, map->l_name, 0);
+ struct link_map *scope[2] = { map, NULL };
+ loadbase = _dl_lookup_symbol (name, &ref, scope, map->l_name, 0, 0);
}
/* Confine the symbol scope to just this map. */
diff --git a/elf/link.h b/elf/link.h
index a19a2ce0b3..73782d8ac3 100644
--- a/elf/link.h
+++ b/elf/link.h
@@ -90,6 +90,12 @@ struct link_map
Elf32_Word l_phnum; /* Number of program header entries. */
Elf32_Addr l_entry; /* Entry point location. */
+ /* Array of DT_NEEDED dependencies and their dependencies, in
+ dependency order for symbol lookup. This is null before the
+ dependencies have been loaded. */
+ struct link_map **l_searchlist;
+ unsigned int l_nsearchlist;
+
/* Symbol hash table. */
Elf32_Word l_nbuckets;
const Elf32_Word *l_buckets, *l_chain;
@@ -102,7 +108,6 @@ struct link_map
lt_library, /* Library needed by main executable. */
lt_loaded, /* Extra run-time loaded shared object. */
} l_type:2;
- unsigned int l_deps_loaded:1; /* Nonzero if DT_NEEDED items loaded. */
unsigned int l_relocated:1; /* Nonzero if object's relocations done. */
unsigned int l_init_called:1; /* Nonzero if DT_INIT function called. */
unsigned int l_init_running:1; /* Nonzero while DT_INIT function runs. */
@@ -176,22 +181,36 @@ extern struct link_map *_dl_map_object (struct link_map *loader,
extern struct link_map *_dl_map_object_from_fd (const char *name,
int fd, char *realname);
+/* Call _dl_map_object on the dependencies of MAP, and
+ set up MAP->l_searchlist. */
+extern void _dl_map_object_deps (struct link_map *map);
+
/* Cache the locations of MAP's hash table. */
extern void _dl_setup_hash (struct link_map *map);
+/* Open the shared object NAME, relocate it, and run its initializer if it
+ hasn't already been run. LOADER's DT_RPATH is used in searching for
+ NAME. MODE is as for `dlopen' (see <dlfcn.h>). If the object is
+ already opened, returns its existing map. */
+extern struct link_map *_dl_open (struct link_map *loader,
+ const char *name, int mode);
+
+
+
/* Search loaded objects' symbol tables for a definition of the symbol
referred to by UNDEF. *SYM is the symbol table entry containing the
reference; it is replaced with the defining symbol, and the base load
- address of the defining object is returned. SYMBOL_SCOPE is the head of
- the chain used for searching. REFERENCE_NAME should name the object
- containing the reference; it is used in error messages. RELOC_ADDR is
- the address being fixed up and the chosen symbol cannot be one with this
- value. If NOPLT is nonzero, then the reference must not be resolved to
- a PLT entry. */
+ address of the defining object is returned. Each of SYMBOL_SCOPE[0] and
+ SYMBOL_SCOPE[1] that is not null and their dependencies are searched to
+ resolve the name. REFERENCE_NAME should name the object containing the
+ reference; it is used in error messages. RELOC_ADDR is the address
+ being fixed up and the chosen symbol cannot be one with this value. If
+ NOPLT is nonzero, then the reference must not be resolved to a PLT
+ entry. */
extern Elf32_Addr _dl_lookup_symbol (const char *undef,
const Elf32_Sym **sym,
- struct link_map *symbol_scope,
+ struct link_map *symbol_scope[2],
const char *reference_name,
Elf32_Addr reloc_addr,
int noplt);
diff --git a/elf/linux-compat.c b/elf/linux-compat.c
index ed1595e247..8821a140e0 100644
--- a/elf/linux-compat.c
+++ b/elf/linux-compat.c
@@ -1,5 +1,5 @@
/* Initializer for Linux-compatible dynamic linker `/lib/ld-linux.so.1'.
-Copyright (C) 1995 Free Software Foundation, Inc.
+Copyright (C) 1995, 1996 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
@@ -33,8 +33,9 @@ void
_init (void)
{
const Elf32_Sym *ref = NULL;
- Elf32_Addr loadbase = _dl_lookup_symbol ("atexit", &ref, _dl_loaded,
+ struct link_map *scope[2] = { _dl_loaded, NULL };
+ Elf32_Addr loadbase = _dl_lookup_symbol ("atexit", &ref, scope,
"<ld-linux.so.1 initialization>",
- 1);
+ 0, 1);
(*(__typeof (atexit) *) (loadbase + ref->st_value)) (&_dl_fini);
}
diff --git a/elf/rtld.c b/elf/rtld.c
index 511d19cb15..6dc68226db 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -124,7 +124,7 @@ dl_main (const Elf32_Phdr *phdr,
Elf32_Addr *user_entry)
{
const Elf32_Phdr *ph;
- struct link_map *l, *last, *before_rtld;
+ struct link_map *l;
const char *interpreter_name;
int lazy;
int list_only = 0;
@@ -250,44 +250,19 @@ of this helper program; chances are you did not intend to run this program.\n",
l->l_next = &rtld_map;
rtld_map.l_prev = l;
- /* Now process all the DT_NEEDED entries and map in the objects.
- Each new link_map will go on the end of the chain, so we will
- come across it later in the loop to map in its dependencies. */
- before_rtld = NULL;
- for (l = _dl_loaded; l; l = l->l_next)
- {
- if (l->l_info[DT_NEEDED])
- {
- const char *strtab
- = (void *) l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr;
- const Elf32_Dyn *d;
- last = l;
- for (d = l->l_ld; d->d_tag != DT_NULL; ++d)
- if (d->d_tag == DT_NEEDED)
- {
- struct link_map *new;
- new = _dl_map_object (l, strtab + d->d_un.d_val);
- if (!before_rtld && new == &rtld_map)
- before_rtld = last;
- last = new;
- }
- }
- l->l_deps_loaded = 1;
- }
+ /* Load all the libraries specified by DT_NEEDED entries. */
+ _dl_map_object_deps (l);
- /* If any DT_NEEDED entry referred to the interpreter object itself,
- reorder the list so it appears after its dependent. If not,
- remove it from the maps we will use for symbol resolution. */
- rtld_map.l_prev->l_next = rtld_map.l_next;
- if (rtld_map.l_next)
- rtld_map.l_next->l_prev = rtld_map.l_prev;
- if (before_rtld)
+ /* XXX if kept, move it so l_next list is in dep order because
+ it will determine gdb's search order.
+ Perhaps do this always, so later dlopen by name finds it?
+ XXX But then gdb always considers it present. */
+ if (rtld_map.l_opencount == 0)
{
- rtld_map.l_prev = before_rtld;
- rtld_map.l_next = before_rtld->l_next;
- before_rtld->l_next = &rtld_map;
- if (rtld_map.l_next)
- rtld_map.l_next->l_prev = &rtld_map;
+ /* No DT_NEEDED entry referred to the interpreter object itself,
+ so remove it from the list of visible objects. */
+ rtld_map.l_prev->l_next = rtld_map.l_next;
+ rtld_map.l_next->l_prev = rtld_map.l_prev;
}
if (list_only)
@@ -316,9 +291,9 @@ of this helper program; chances are you did not intend to run this program.\n",
for (i = 1; i < _dl_argc; ++i)
{
const Elf32_Sym *ref = NULL;
- Elf32_Addr loadbase = _dl_lookup_symbol (_dl_argv[i], &ref,
- _dl_loaded, "argument",
- 0, 0);
+ struct link_map *scope[2] ={ _dl_loaded, NULL };
+ Elf32_Addr loadbase
+ = _dl_lookup_symbol (_dl_argv[i], &ref, scope, "argument", 0, 0);
char buf[20], *bp;
buf[sizeof buf - 1] = '\0';
bp = _itoa (ref->st_value, &buf[sizeof buf - 1], 16, 0);