summaryrefslogtreecommitdiff
path: root/elf
diff options
context:
space:
mode:
Diffstat (limited to 'elf')
-rw-r--r--elf/Makefile7
-rw-r--r--elf/dl-close.c107
-rw-r--r--elf/dl-deps.c4
-rw-r--r--elf/dl-load.c314
-rw-r--r--elf/dl-lookup.c125
-rw-r--r--elf/dl-object.c12
-rw-r--r--elf/dl-open.c81
-rw-r--r--elf/dl-reloc.c33
-rw-r--r--elf/dl-runtime.c58
-rw-r--r--elf/dlclose.c70
-rw-r--r--elf/dlopen.c2
-rw-r--r--elf/link.h68
-rw-r--r--elf/rtld.c83
13 files changed, 574 insertions, 390 deletions
diff --git a/elf/Makefile b/elf/Makefile
index 06aeb7590a..c2c8f7c5b2 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -21,7 +21,8 @@
subdir := elf
headers = elf.h elfclass.h link.h dlfcn.h
-routines = init-first $(dl-routines) dl-open dl-symbol dl-support
+routines = init-first $(dl-routines) \
+ dl-open dl-close dl-symbol dl-support
# The core dynamic linking functions are in libc for the static and
# profiled libraries.
@@ -54,7 +55,7 @@ install-bin = ldd
# to run programs during the `make others' pass.
lib-noranlib: $(objpfx)ld.so $(addprefix $(objpfx),$(extra-objs))
-ifneq (,$(filter linux%,$(config-os)))
+ifneq (,$(filter linux% linux,$(config-os)))
extra-objs += linux-compat.so
install-others += $(slibdir)/ld-linux.so.1
lib-noranlib: $(objpfx)ld-linux.so.1
@@ -75,7 +76,7 @@ $(objpfx)dl-allobjs.so: $(rtld-routines:%=$(objpfx)%.so)
# dynamic linker shared objects below.
$(objpfx)librtld.so: $(objpfx)dl-allobjs.so \
$(patsubst %,$(common-objpfx)lib%_pic.a,\
- elf c $(LDLIBS-c.so:-l%=%))
+ c $(LDLIBS-c.so:-l%=%))
$(reloc-link) '-Wl,-(' $^ -lgcc '-Wl,-)'
$(objpfx)ld.so: $(objpfx)librtld.so
diff --git a/elf/dl-close.c b/elf/dl-close.c
new file mode 100644
index 0000000000..69fdefac95
--- /dev/null
+++ b/elf/dl-close.c
@@ -0,0 +1,107 @@
+/* _dl_close -- Close a shared object opened by `_dl_open'.
+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 <dlfcn.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+
+
+#define LOSE(s) _dl_signal_error (0, map->l_name, s)
+
+void
+_dl_close (struct link_map *map)
+{
+ struct link_map **list;
+ unsigned int i;
+
+ if (map->l_opencount == 0)
+ LOSE ("shared object not open");
+
+ /* Decrement the reference count. */
+ if (--map->l_opencount > 0 || map->l_type != lt_loaded)
+ /* There are still references to this object. Do nothing more. */
+ return;
+
+ list = map->l_searchlist;
+
+ /* The search list contains a counted reference to each object it
+ points to, the 0th elt being MAP itself. Decrement the reference
+ counts on all the objects MAP depends on. */
+ for (i = 1; i < map->l_nsearchlist; ++i)
+ --list[i]->l_opencount;
+
+ /* Clear the search list so it doesn't get freed while we are still
+ using it. We have cached it in LIST and will free it when
+ finished. */
+ map->l_searchlist = NULL;
+
+ /* Check each element of the search list to see if all references to
+ it are gone. */
+ for (i = 0; i < map->l_nsearchlist; ++i)
+ {
+ struct link_map *map = list[i];
+ if (map->l_opencount == 0 && map->l_type == lt_loaded)
+ {
+ /* That was the last reference, and this was a dlopen-loaded
+ object. We can unmap it. */
+ const ElfW(Phdr) *ph;
+
+ if (map->l_info[DT_FINI])
+ /* Call its termination function. */
+ (*(void (*) (void)) ((void *) map->l_addr +
+ map->l_info[DT_FINI]->d_un.d_ptr)) ();
+
+ if (map->l_global)
+ {
+ /* This object is in the global scope list. Remove it. */
+ struct link_map **tail = _dl_global_scope_end;
+ do
+ --tail;
+ while (*tail != map);
+ --_dl_global_scope_end;
+ memcpy (tail, tail + 1, _dl_global_scope_end - tail);
+ _dl_global_scope_end[0] = NULL;
+ _dl_global_scope_end[1] = NULL;
+ }
+
+ /* Unmap the segments. */
+ for (ph = map->l_phdr; ph < &map->l_phdr[map->l_phnum]; ++ph)
+ if (ph->p_type == PT_LOAD)
+ {
+ ElfW(Addr) mapstart = ph->p_vaddr & ~(ph->p_align - 1);
+ ElfW(Addr) mapend = ((ph->p_vaddr + ph->p_memsz
+ + ph->p_align - 1)
+ & ~(ph->p_align - 1));
+ __munmap ((caddr_t) mapstart, mapend - mapstart);
+ }
+
+ /* Finally, unlink the data structure and free it. */
+ map->l_prev->l_next = map->l_next;
+ if (map->l_next)
+ map->l_next->l_prev = map->l_prev;
+ if (map->l_searchlist)
+ free (map->l_searchlist);
+ free (map);
+ }
+ }
+
+ free (list);
+}
diff --git a/elf/dl-deps.c b/elf/dl-deps.c
index 7e3b259362..3e49fcfe01 100644
--- a/elf/dl-deps.c
+++ b/elf/dl-deps.c
@@ -62,7 +62,9 @@ _dl_map_object_deps (struct link_map *map)
{
/* Map in the needed object. */
struct link_map *dep
- = _dl_map_object (l, strtab + d->d_un.d_val);
+ = _dl_map_object (l, strtab + d->d_un.d_val,
+ l->l_type == lt_executable ? lt_library :
+ l->l_type);
if (dep->l_reserved)
/* This object is already in the search list we are
diff --git a/elf/dl-load.c b/elf/dl-load.c
index c6acc8c222..b9f4aa184b 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -63,152 +63,14 @@ int _dl_zerofd = -1;
size_t _dl_pagesize;
-/* Try to open NAME in one of the directories in DIRPATH.
- Return the fd, or -1. If successful, fill in *REALNAME
- with the malloc'd full directory name. */
-
-static int
-open_path (const char *name, size_t namelen,
- const char *dirpath,
- char **realname)
-{
- char *buf;
- const char *p;
- int fd;
-
- p = dirpath;
- if (p == NULL || *p == '\0')
- {
- errno = ENOENT;
- return -1;
- }
-
- buf = __alloca (strlen (dirpath) + 1 + namelen);
- do
- {
- size_t buflen;
-
- dirpath = p;
- p = strpbrk (dirpath, ":;");
- if (p == NULL)
- p = strchr (dirpath, '\0');
-
- if (p == dirpath)
- {
- /* Two adjacent colons, or a colon at the beginning or the end of
- the path means to search the current directory. */
- (void) memcpy (buf, name, namelen);
- buflen = namelen;
- }
- else
- {
- /* Construct the pathname to try. */
- (void) memcpy (buf, dirpath, p - dirpath);
- buf[p - dirpath] = '/';
- (void) memcpy (&buf[(p - dirpath) + 1], name, namelen);
- buflen = p - dirpath + 1 + namelen;
- }
-
- fd = __open (buf, O_RDONLY);
- if (fd != -1)
- {
- *realname = malloc (buflen);
- if (*realname)
- {
- memcpy (*realname, buf, buflen);
- return fd;
- }
- else
- {
- /* No memory for the name, we certainly won't be able
- to load and link it. */
- __close (fd);
- return -1;
- }
- }
- if (errno != ENOENT && errno != EACCES)
- /* The file exists and is readable, but something went wrong. */
- return -1;
- }
- while (*p++ != '\0');
-
- return -1;
-}
-
-/* Map in the shared object file NAME. */
-
-struct link_map *
-_dl_map_object (struct link_map *loader, const char *name)
-{
- int fd;
- char *realname;
- struct link_map *l;
-
- /* Look for this name among those already loaded. */
- for (l = _dl_loaded; l; l = l->l_next)
- if (! strcmp (name, l->l_libname))
- {
- /* The object is already loaded.
- Just bump its reference count and return it. */
- ++l->l_opencount;
- return l;
- }
-
- if (strchr (name, '/') == NULL)
- {
- /* Search for NAME in several places. */
-
- size_t namelen = strlen (name) + 1;
-
- inline void trypath (const char *dirpath)
- {
- fd = open_path (name, namelen, dirpath, &realname);
- }
-
- fd = -1;
- if (loader && loader->l_info[DT_RPATH])
- trypath ((const char *) (loader->l_addr +
- loader->l_info[DT_STRTAB]->d_un.d_ptr +
- loader->l_info[DT_RPATH]->d_un.d_val));
- if (fd == -1 && ! _dl_secure)
- trypath (getenv ("LD_LIBRARY_PATH"));
- if (fd == -1)
- {
- extern const char *_dl_rpath; /* Set in rtld.c. */
- trypath (_dl_rpath);
- }
- }
- else
- {
- fd = __open (name, O_RDONLY);
- if (fd != -1)
- {
- size_t len = strlen (name) + 1;
- realname = malloc (len);
- if (realname)
- memcpy (realname, name, len);
- else
- {
- __close (fd);
- fd = -1;
- }
- }
- }
-
- if (fd == -1)
- _dl_signal_error (errno, name, "cannot open shared object file");
-
- return _dl_map_object_from_fd (name, fd, realname);
-}
-
-
/* Map in the shared object NAME, actually located in REALNAME, and already
opened on FD. */
struct link_map *
-_dl_map_object_from_fd (const char *name, int fd, char *realname)
+_dl_map_object_from_fd (const char *name, int fd, char *realname,
+ struct link_map *loader, int l_type)
{
- struct link_map *l = NULL;
+ struct link_map *l;
void *file_mapping = NULL;
size_t mapping_size = 0;
@@ -218,7 +80,17 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname)
(void) __close (fd);
if (file_mapping)
__munmap (file_mapping, mapping_size);
- _dl_signal_error (code, l ? l->l_name : name, msg);
+ if (l)
+ {
+ /* Remove the stillborn object from the list and free it. */
+ if (l->l_prev)
+ l->l_prev->l_next = l->l_next;
+ if (l->l_next)
+ l->l_next->l_prev = l->l_prev;
+ free (l);
+ }
+ free (realname);
+ _dl_signal_error (code, name, msg);
}
inline caddr_t map_segment (ElfW(Addr) mapstart, size_t len,
@@ -304,17 +176,23 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname)
if (header->e_phentsize != sizeof (ElfW(Phdr)))
LOSE ("ELF file's phentsize not the expected size");
- /* Enter the new object in the list of loaded objects. */
- l = _dl_new_object (realname, name, lt_loaded);
- l->l_opencount = 1;
-
if (_dl_zerofd == -1)
{
_dl_zerofd = _dl_sysdep_open_zero_fill ();
if (_dl_zerofd == -1)
- _dl_signal_error (errno, NULL, "cannot open zero fill device");
+ {
+ __close (fd);
+ _dl_signal_error (errno, NULL, "cannot open zero fill device");
+ }
}
+ /* Enter the new object in the list of loaded objects. */
+ l = _dl_new_object (realname, name, l_type);
+ if (! l)
+ lose (ENOMEM, "cannot create shared object descriptor");
+ l->l_opencount = 1;
+ l->l_loader = loader;
+
/* Extract the remaining details we need from the ELF header
and then map in the program header table. */
l->l_entry = header->e_entry;
@@ -464,7 +342,8 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname)
/* We are done mapping in the file. We no longer need the descriptor. */
__close (fd);
- l->l_type = type == ET_EXEC ? lt_executable : lt_library;
+ if (l->l_type == lt_library && type == ET_EXEC)
+ l->l_type = lt_executable;
if (l->l_ld == 0)
{
@@ -484,3 +363,142 @@ _dl_map_object_from_fd (const char *name, int fd, char *realname)
return l;
}
+
+/* Try to open NAME in one of the directories in DIRPATH.
+ Return the fd, or -1. If successful, fill in *REALNAME
+ with the malloc'd full directory name. */
+
+static int
+open_path (const char *name, size_t namelen,
+ const char *dirpath,
+ char **realname)
+{
+ char *buf;
+ const char *p;
+ int fd;
+
+ p = dirpath;
+ if (p == NULL || *p == '\0')
+ {
+ errno = ENOENT;
+ return -1;
+ }
+
+ buf = __alloca (strlen (dirpath) + 1 + namelen);
+ do
+ {
+ size_t buflen;
+
+ dirpath = p;
+ p = strpbrk (dirpath, ":;");
+ if (p == NULL)
+ p = strchr (dirpath, '\0');
+
+ if (p == dirpath)
+ {
+ /* Two adjacent colons, or a colon at the beginning or the end of
+ the path means to search the current directory. */
+ (void) memcpy (buf, name, namelen);
+ buflen = namelen;
+ }
+ else
+ {
+ /* Construct the pathname to try. */
+ (void) memcpy (buf, dirpath, p - dirpath);
+ buf[p - dirpath] = '/';
+ (void) memcpy (&buf[(p - dirpath) + 1], name, namelen);
+ buflen = p - dirpath + 1 + namelen;
+ }
+
+ fd = __open (buf, O_RDONLY);
+ if (fd != -1)
+ {
+ *realname = malloc (buflen);
+ if (*realname)
+ {
+ memcpy (*realname, buf, buflen);
+ return fd;
+ }
+ else
+ {
+ /* No memory for the name, we certainly won't be able
+ to load and link it. */
+ __close (fd);
+ return -1;
+ }
+ }
+ if (errno != ENOENT && errno != EACCES)
+ /* The file exists and is readable, but something went wrong. */
+ return -1;
+ }
+ while (*p++ != '\0');
+
+ return -1;
+}
+
+/* Map in the shared object file NAME. */
+
+struct link_map *
+_dl_map_object (struct link_map *loader, const char *name, int type)
+{
+ int fd;
+ char *realname;
+ struct link_map *l;
+
+ /* Look for this name among those already loaded. */
+ for (l = _dl_loaded; l; l = l->l_next)
+ if (! strcmp (name, l->l_libname))
+ {
+ /* The object is already loaded.
+ Just bump its reference count and return it. */
+ ++l->l_opencount;
+ return l;
+ }
+
+ if (strchr (name, '/') == NULL)
+ {
+ /* Search for NAME in several places. */
+
+ size_t namelen = strlen (name) + 1;
+
+ inline void trypath (const char *dirpath)
+ {
+ fd = open_path (name, namelen, dirpath, &realname);
+ }
+
+ fd = -1;
+ for (l = loader; l; l = l->l_loader)
+ if (l && l->l_info[DT_RPATH])
+ trypath ((const char *) (l->l_addr +
+ l->l_info[DT_STRTAB]->d_un.d_ptr +
+ l->l_info[DT_RPATH]->d_un.d_val));
+ if (fd == -1 && ! _dl_secure)
+ trypath (getenv ("LD_LIBRARY_PATH"));
+ if (fd == -1)
+ {
+ extern const char *_dl_rpath; /* Set in rtld.c. */
+ trypath (_dl_rpath);
+ }
+ }
+ else
+ {
+ fd = __open (name, O_RDONLY);
+ if (fd != -1)
+ {
+ size_t len = strlen (name) + 1;
+ realname = malloc (len);
+ if (realname)
+ memcpy (realname, name, len);
+ else
+ {
+ __close (fd);
+ fd = -1;
+ }
+ }
+ }
+
+ if (fd == -1)
+ _dl_signal_error (errno, name, "cannot open shared object file");
+
+ return _dl_map_object_from_fd (name, fd, realname, loader, type);
+}
diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
index 0674253041..5f1e6d03d9 100644
--- a/elf/dl-lookup.c
+++ b/elf/dl-lookup.c
@@ -50,7 +50,7 @@ _dl_elf_hash (const char *name)
ElfW(Addr)
_dl_lookup_symbol (const char *undef_name, const ElfW(Sym) **ref,
- struct link_map *symbol_scope[2],
+ struct link_map *symbol_scope[],
const char *reference_name,
ElfW(Addr) reloc_addr,
int noplt)
@@ -65,69 +65,68 @@ _dl_lookup_symbol (const char *undef_name, const ElfW(Sym) **ref,
struct link_map **scope, *map;
/* Search the relevant loaded objects for a definition. */
- for (scope = symbol_scope; scope < &symbol_scope[2]; ++scope)
- if (*scope)
- for (i = 0; i < (*scope)->l_nsearchlist; ++i)
- {
- const ElfW(Sym) *symtab;
- const char *strtab;
- ElfW(Word) symidx;
-
- map = (*scope)->l_searchlist[i];
-
- 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])
- {
- const ElfW(Sym) *sym = &symtab[symidx];
-
- 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 (ELFW(ST_TYPE) (sym->st_info))
- {
- 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. */
+ for (scope = symbol_scope; *scope; ++scope)
+ for (i = 0; i < (*scope)->l_nsearchlist; ++i)
+ {
+ const ElfW(Sym) *symtab;
+ const char *strtab;
+ ElfW(Word) symidx;
+
+ map = (*scope)->l_searchlist[i];
+
+ 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])
+ {
+ const ElfW(Sym) *sym = &symtab[symidx];
+
+ 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 (ELFW(ST_TYPE) (sym->st_info))
+ {
+ case STT_NOTYPE:
+ case STT_FUNC:
+ case STT_OBJECT:
+ break;
+ default:
+ /* Not a code/data definition. */
continue;
-
- switch (ELFW(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;
- }
- }
- }
+ }
+
+ if (sym != *ref && strcmp (strtab + sym->st_name, undef_name))
+ /* Not the symbol we are looking for. */
+ continue;
+
+ switch (ELFW(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;
+ }
+ }
+ }
if (weak_value.s == NULL && ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK)
{
diff --git a/elf/dl-object.c b/elf/dl-object.c
index 11e9e082d2..851d133df3 100644
--- a/elf/dl-object.c
+++ b/elf/dl-object.c
@@ -1,5 +1,5 @@
/* Storage management for the chain of loaded shared objects.
-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
@@ -24,11 +24,8 @@ Cambridge, MA 02139, USA. */
#include <errno.h>
-/* List of objects currently loaded. */
-struct link_map *_dl_loaded;
-
-/* Tail of that list which were loaded at startup. */
-struct link_map *_dl_startup_loaded;
+/* List of objects currently loaded is [2] of this, aka _dl_loaded. */
+struct link_map *_dl_default_scope[5];
/* Allocate a `struct link_map' for a new object being loaded,
and enter it into the _dl_loaded list. */
@@ -38,8 +35,7 @@ _dl_new_object (char *realname, const char *libname, int type)
{
struct link_map *new = malloc (sizeof *new);
if (! new)
- _dl_signal_error (ENOMEM, libname,
- "cannot allocate shared object descriptor");
+ return NULL;
memset (new, 0, sizeof *new);
new->l_name = realname;
diff --git a/elf/dl-open.c b/elf/dl-open.c
index 221abbd35e..9389303b90 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -19,6 +19,10 @@ Cambridge, MA 02139, USA. */
#include <link.h>
#include <dlfcn.h>
+#include <stdlib.h>
+#include <errno.h>
+
+size_t _dl_global_scope_alloc;
struct link_map *
_dl_open (struct link_map *parent, const char *file, int mode)
@@ -26,16 +30,83 @@ _dl_open (struct link_map *parent, const char *file, int mode)
struct link_map *new, *l;
ElfW(Addr) init;
+
/* Load the named object. */
- new = _dl_map_object (parent, file);
+ new = _dl_map_object (parent, file, lt_loaded);
+ if (new->l_searchlist)
+ /* It was already open. */
+ return new;
/* 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);
+
+ /* Relocate the objects loaded. We do this in reverse order so that copy
+ relocs of earlier objects overwrite the data written by later objects. */
+
+ l = new;
+ while (l->l_next)
+ l = l->l_next;
+ do
+ {
+ if (! l->l_relocated)
+ {
+ _dl_relocate_object (l, _dl_object_relocation_scope (l),
+ (mode & RTLD_BINDING_MASK) == RTLD_LAZY);
+ *_dl_global_scope_end = NULL;
+ }
+
+ l = l->l_prev;
+ } while (l != new);
+
+ new->l_global = (mode & RTLD_GLOBAL);
+ if (new->l_global)
+ {
+ /* The symbols of the new object and its dependencies are to be
+ introduced into the global scope that will be used to resolve
+ references from other dynamically-loaded objects. */
+
+ if (_dl_global_scope_alloc == 0)
+ {
+ /* This is the first dynamic object given global scope. */
+ _dl_global_scope_alloc = 8;
+ _dl_global_scope = malloc (8 * sizeof (struct link_map *));
+ if (! _dl_global_scope)
+ {
+ _dl_global_scope = _dl_default_scope;
+ nomem:
+ _dl_close (new);
+ _dl_signal_error (ENOMEM, file, "cannot extend global scope");
+ }
+ _dl_global_scope[2] = _dl_default_scope[2];
+ _dl_global_scope[3] = new;
+ _dl_global_scope[4] = NULL;
+ _dl_global_scope[5] = NULL;
+ }
+ else
+ {
+ if (_dl_global_scope_alloc <
+ _dl_global_scope_end - _dl_global_scope + 2)
+ {
+ /* Must extend the list. */
+ struct link_map **new = realloc (_dl_global_scope,
+ _dl_global_scope_alloc * 2);
+ if (! new)
+ goto nomem;
+ _dl_global_scope_end = new + (_dl_global_scope_end -
+ _dl_global_scope);
+ _dl_global_scope = new;
+ _dl_global_scope_alloc *= 2;
+ }
+
+ /* Append the new object and re-terminate the list. */
+ *_dl_global_scope_end++ = new;
+ /* We keep the list double-terminated so the last element
+ can be filled in for symbol lookups. */
+ _dl_global_scope_end[0] = NULL;
+ _dl_global_scope_end[1] = NULL;
+ }
+ }
/* Run the initializer functions of new objects. */
while (init = _dl_init_next (new))
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index 5da8575da5..e6778e7de3 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -26,7 +26,7 @@ Cambridge, MA 02139, USA. */
void
-_dl_relocate_object (struct link_map *l, int lazy)
+_dl_relocate_object (struct link_map *l, struct link_map *scope[], int lazy)
{
if (l->l_relocated)
return;
@@ -52,44 +52,26 @@ _dl_relocate_object (struct link_map *l, int lazy)
}
{
- struct link_map *scope[2];
+ /* Do the actual relocation of the object's GOT and other data. */
- const char *strtab
+ const char *strtab /* String table object symbols. */
= ((void *) l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr);
-
ElfW(Addr) resolve (const ElfW(Sym) **ref,
ElfW(Addr) reloc_addr, int noplt)
{
+ /* Look up the referenced symbol in the specified scope. */
return _dl_lookup_symbol (strtab + (*ref)->st_name, ref, scope,
l->l_name, reloc_addr, noplt);
}
- if (l->l_info[DT_SYMBOLIC])
- {
- scope[0] = l;
- scope[1] = _dl_loaded;
- }
- else
- {
- scope[0] = _dl_loaded;
- scope[1] = l;
- }
-
- if (l->l_type == lt_interpreter)
- /* We cannot be lazy when relocating the dynamic linker itself. It
- was previously relocated eagerly (allowing us to be running now),
- and needs always to be fully relocated so it can run without the
- aid of run-time fixups (because it's the one to do them), so we
- must always re-relocate its PLT eagerly. */
- lazy = 0;
-
ELF_DYNAMIC_RELOCATE (l, lazy, resolve);
}
- /* Set up the PLT so its unrelocated entries will
- jump to _dl_runtime_resolve, which will relocate them. */
+ /* Set up the PLT so its unrelocated entries will jump to
+ _dl_runtime_resolve (dl-runtime.c), which will relocate them. */
elf_machine_runtime_setup (l, lazy);
+ /* Mark the object so we know ths work has been done. */
l->l_relocated = 1;
if (l->l_info[DT_TEXTREL])
@@ -114,5 +96,4 @@ _dl_relocate_object (struct link_map *l, int lazy)
"can't restore segment prot after reloc");
}
}
-
}
diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c
index c306543155..8ad2c0ffa4 100644
--- a/elf/dl-runtime.c
+++ b/elf/dl-runtime.c
@@ -20,6 +20,54 @@ Cambridge, MA 02139, USA. */
#include <link.h>
#include "dynamic-link.h"
+
+/* The global scope we will use for symbol lookups.
+ This will be modified by _dl_open if RTLD_GLOBAL is used. */
+struct link_map **_dl_global_scope = _dl_default_scope;
+struct link_map **_dl_global_scope_end = &_dl_default_scope[3];
+
+
+/* Hack _dl_global_scope[0] and [1] as necessary, and return a pointer into
+ _dl_global_scope that should be passed to _dl_lookup_symbol for symbol
+ references made in the object L's relocations. */
+inline struct link_map **
+_dl_object_relocation_scope (struct link_map *l)
+{
+ if (l->l_info[DT_SYMBOLIC])
+ {
+ /* This object's global references are to be resolved first
+ in the object itself, and only secondarily in more global
+ scopes. */
+
+ if (! l->l_searchlist)
+ /* We must construct the searchlist for this object. */
+ _dl_map_object_deps (l);
+
+ /* The primary scope is this object itself and its
+ dependencies. */
+ _dl_global_scope[0] = l;
+
+ /* Secondary is the dependency tree that reached L; the object
+ requested directly by the user is at the root of that tree. */
+ while (l->l_loader)
+ l = l->l_loader;
+ _dl_global_scope[1] = l;
+
+ /* Finally, the global scope follows. */
+
+ return _dl_global_scope;
+ }
+ else
+ {
+ /* Use first the global scope, and then the scope of the root of the
+ dependency tree that first caused this object to be loaded. */
+ while (l->l_loader)
+ l = l->l_loader;
+ *_dl_global_scope_end = l;
+ return &_dl_global_scope[2];
+ }
+}
+
/* Figure out the right type, Rel or Rela. */
#define elf_machine_rel 1
#define elf_machine_rela 2
@@ -67,17 +115,21 @@ fixup (
= (const void *) (l->l_addr + l->l_info[DT_JMPREL]->d_un.d_ptr +
reloc_offset);
+ /* Set up the scope to find symbols referenced by this object. */
+ struct link_map **scope = _dl_object_relocation_scope (l);
+
+ /* Perform the specified relocation. */
ElfW(Addr) resolve (const ElfW(Sym) **ref,
ElfW(Addr) reloc_addr, int 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. */
elf_machine_relplt (l, reloc, &symtab[ELFW(R_SYM) (reloc->r_info)], resolve);
+ *_dl_global_scope_end = NULL;
+
+ /* Return the address that was written by the relocation. */
return *(ElfW(Addr) *) (l->l_addr + reloc->r_offset);
}
diff --git a/elf/dlclose.c b/elf/dlclose.c
index 0d2689e01c..a5c8e7c17e 100644
--- a/elf/dlclose.c
+++ b/elf/dlclose.c
@@ -19,81 +19,13 @@ Cambridge, MA 02139, USA. */
#include <link.h>
#include <dlfcn.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/mman.h>
-
-
-#define LOSE(s) _dl_signal_error (0, map->l_name, s)
int
dlclose (void *handle)
{
void doit (void)
{
- struct link_map *map = handle;
- struct link_map **list;
- unsigned int i;
-
- if (map->l_opencount == 0)
- LOSE ("shared object not open");
-
- /* Decrement the reference count. */
- if (--map->l_opencount > 0 || map->l_type != lt_loaded)
- /* There are still references to this object. Do nothing more. */
- return;
-
- list = map->l_searchlist;
-
- /* The search list contains a counted reference to each object it
- points to, the 0th elt being MAP itself. Decrement the reference
- counts on all the objects MAP depends on. */
- for (i = 1; i < map->l_nsearchlist; ++i)
- --list[i]->l_opencount;
-
- /* Clear the search list so it doesn't get freed while we are still
- using it. We have cached it in LIST and will free it when
- finished. */
- map->l_searchlist = NULL;
-
- /* Check each element of the search list to see if all references to
- it are gone. */
- for (i = 0; i < map->l_nsearchlist; ++i)
- {
- struct link_map *map = list[i];
- if (map->l_opencount == 0 && map->l_type == lt_loaded)
- {
- /* That was the last reference, and this was a dlopen-loaded
- object. We can unmap it. */
- const ElfW(Phdr) *ph;
-
- if (map->l_info[DT_FINI])
- /* Call its termination function. */
- (*(void (*) (void)) ((void *) map->l_addr +
- map->l_info[DT_FINI]->d_un.d_ptr)) ();
-
- /* Unmap the segments. */
- for (ph = map->l_phdr; ph < &map->l_phdr[map->l_phnum]; ++ph)
- if (ph->p_type == PT_LOAD)
- {
- ElfW(Addr) mapstart = ph->p_vaddr & ~(ph->p_align - 1);
- ElfW(Addr) mapend = ((ph->p_vaddr + ph->p_memsz
- + ph->p_align - 1)
- & ~(ph->p_align - 1));
- munmap ((caddr_t) mapstart, mapend - mapstart);
- }
-
- /* Finally, unlink the data structure and free it. */
- map->l_prev->l_next = map->l_next;
- if (map->l_next)
- map->l_next->l_prev = map->l_prev;
- if (map->l_searchlist)
- free (map->l_searchlist);
- free (map);
- }
- }
-
- free (list);
+ _dl_close (handle);
}
return _dlerror_run (doit) ? -1 : 0;
diff --git a/elf/dlopen.c b/elf/dlopen.c
index 74ab8bb715..e261fcadd9 100644
--- a/elf/dlopen.c
+++ b/elf/dlopen.c
@@ -28,7 +28,7 @@ dlopen (const char *file, int mode)
void doit (void)
{
- new = _dl_open (_dl_loaded, file, mode);
+ new = _dl_open (_dl_loaded, file ?: "", mode);
}
return _dlerror_run (doit) ? NULL : new;
diff --git a/elf/link.h b/elf/link.h
index 6d284cbbde..89dfa2087f 100644
--- a/elf/link.h
+++ b/elf/link.h
@@ -106,6 +106,9 @@ struct link_map
struct link_map **l_searchlist;
unsigned int l_nsearchlist;
+ /* Dependent object that first caused this object to be loaded. */
+ struct link_map *l_loader;
+
/* Symbol hash table. */
ElfW(Word) l_nbuckets;
const ElfW(Word) *l_buckets, *l_chain;
@@ -114,14 +117,14 @@ struct link_map
enum /* Where this object came from. */
{
lt_executable, /* The main executable program. */
- lt_interpreter, /* The interpreter: the dynamic linker. */
lt_library, /* Library needed by main executable. */
lt_loaded, /* Extra run-time loaded shared object. */
} l_type:2;
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. */
- unsigned int l_reserved:3; /* Reserved for internal use. */
+ unsigned int l_global:1; /* Nonzero if object in _dl_global_scope. */
+ unsigned int l_reserved:2; /* Reserved for internal use. */
};
/* Internal functions of the run-time dynamic linker.
@@ -188,12 +191,7 @@ extern int _dlerror_run (void (*operate) (void));
LOADER's DT_RPATH is used in searching for NAME.
If the object is already opened, returns its existing map. */
extern struct link_map *_dl_map_object (struct link_map *loader,
- const char *name);
-
-/* Similar, but file found at REALNAME and opened on FD.
- REALNAME must malloc'd storage and is used in internal data structures. */
-extern struct link_map *_dl_map_object_from_fd (const char *name,
- int fd, char *realname);
+ const char *name, int type);
/* Call _dl_map_object on the dependencies of MAP, and
set up MAP->l_searchlist. */
@@ -210,21 +208,24 @@ extern void _dl_setup_hash (struct link_map *map);
extern struct link_map *_dl_open (struct link_map *loader,
const char *name, int mode);
+/* Close an object previously opened by _dl_open. */
+extern void _dl_close (struct link_map *map);
/* 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. 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. */
+ address of the defining object is returned. SYMBOL_SCOPE is a
+ null-terminated list of object scopes to search; each object's
+ l_searchlist (i.e. the segment of the dependency tree starting at that
+ object) is searched in turn. 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 ElfW(Addr) _dl_lookup_symbol (const char *undef,
const ElfW(Sym) **sym,
- struct link_map *symbol_scope[2],
+ struct link_map *symbol_scope[],
const char *reference_name,
ElfW(Addr) reloc_addr,
int noplt);
@@ -236,11 +237,33 @@ extern ElfW(Addr) _dl_symbol_value (struct link_map *map, const char *name);
/* Structure describing the dynamic linker itself. */
extern struct link_map _dl_rtld_map;
-/* List of objects currently loaded. */
-extern struct link_map *_dl_loaded;
+/* The list of objects currently loaded is the third element of the
+ `_dl_default_scope' array, and the fourth element is always null.
+ This leaves two slots before it that are used when resolving
+ DT_SYMBOLIC objects' references one after it for normal references
+ (see below). */
+#define _dl_loaded (_dl_default_scope[2])
+extern struct link_map *_dl_default_scope[5];
+
+/* Null-terminated list of objects in the dynamic `global scope'. The
+ list starts at [2]; i.e. &_dl_global_scope[2] is the argument
+ passed to _dl_lookup_symbol to search the global scope. To search
+ a specific object and its dependencies in preference to the global
+ scope, fill in the [1] slot and pass its address; for two specific
+ object scopes, fill [0] and [1]. The list is double-terminated; to
+ search the global scope and then a specific object and its
+ dependencies, set *_dl_global_scope_end. This variable initially
+ points to _dl_default_scope, and _dl_loaded is always kept in [2]
+ of this list. A new list is malloc'd when new objects are loaded
+ with RTLD_GLOBAL. */
+extern struct link_map **_dl_global_scope, **_dl_global_scope_end;
+extern size_t _dl_global_scope_alloc; /* Number of slots malloc'd. */
+
+/* Hack _dl_global_scope[0] and [1] as necessary, and return a pointer into
+ _dl_global_scope that should be passed to _dl_lookup_symbol for symbol
+ references made in the object MAP's relocations. */
+extern struct link_map **_dl_object_relocation_scope (struct link_map *map);
-/* Tail of that list which were loaded at startup. */
-extern struct link_map *_dl_startup_loaded;
/* Allocate a `struct link_map' for a new object being loaded,
and enter it into the _dl_loaded list. */
@@ -248,8 +271,11 @@ extern struct link_map *_dl_new_object (char *realname, const char *libname,
int type);
/* Relocate the given object (if it hasn't already been).
+ SCOPE is passed to _dl_lookup_symbol in symbol lookups.
If LAZY is nonzero, don't relocate its PLT. */
-extern void _dl_relocate_object (struct link_map *map, int lazy);
+extern void _dl_relocate_object (struct link_map *map,
+ struct link_map *scope[],
+ int lazy);
/* Return the address of the next initializer function for MAP or one of
its dependencies that has not yet been run. When there are no more
diff --git a/elf/rtld.c b/elf/rtld.c
index bc1f71bd4e..66477274e5 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -75,13 +75,6 @@ _dl_start (void *arg)
/* Relocate ourselves so we can do normal function calls and
data access using the global offset table. */
- /* We must initialize `l_type' to make sure it is not `lt_interpreter'.
- That is the type to describe us, but not during bootstrapping--it
- indicates to elf_machine_rel{,a} that we were already relocated during
- bootstrapping, so it must anti-perform each bootstrapping relocation
- before applying the final relocation when ld.so is linked in as
- normal a shared library. */
- bootstrap_map.l_type = lt_library;
ELF_DYNAMIC_RELOCATE (&bootstrap_map, 0, NULL);
@@ -178,7 +171,7 @@ of this helper program; chances are you did not intend to run this program.\n",
--_dl_argc;
++_dl_argv;
- l = _dl_map_object (NULL, _dl_argv[0]);
+ l = _dl_map_object (NULL, _dl_argv[0], lt_library);
phdr = l->l_phdr;
phent = l->l_phnum;
l->l_name = (char *) "";
@@ -188,7 +181,7 @@ of this helper program; chances are you did not intend to run this program.\n",
{
/* Create a link_map for the executable itself.
This will be what dlopen on "" returns. */
- l = _dl_new_object ((char *) "", "", lt_executable);
+ l = _dl_new_object ((char *) "", "", lt_library);
l->l_phdr = phdr;
l->l_phnum = phent;
interpreter_name = 0;
@@ -245,7 +238,7 @@ of this helper program; chances are you did not intend to run this program.\n",
/* Put the link_map for ourselves on the chain so it can be found by
name. */
_dl_rtld_map.l_name = (char *) _dl_rtld_map.l_libname = interpreter_name;
- _dl_rtld_map.l_type = lt_interpreter;
+ _dl_rtld_map.l_type = lt_library;
while (l->l_next)
l = l->l_next;
l->l_next = &_dl_rtld_map;
@@ -293,9 +286,9 @@ of this helper program; chances are you did not intend to run this program.\n",
for (i = 1; i < _dl_argc; ++i)
{
const ElfW(Sym) *ref = NULL;
- struct link_map *scope[2] ={ _dl_loaded, NULL };
- ElfW(Addr) loadbase
- = _dl_lookup_symbol (_dl_argv[i], &ref, scope, "argument", 0, 0);
+ ElfW(Addr) loadbase = _dl_lookup_symbol (_dl_argv[i], &ref,
+ &_dl_default_scope[2],
+ "argument", 0, 0);
char buf[20], *bp;
buf[sizeof buf - 1] = '\0';
bp = _itoa (ref->st_value, &buf[sizeof buf - 1], 16, 0);
@@ -314,35 +307,41 @@ of this helper program; chances are you did not intend to run this program.\n",
lazy = !_dl_secure && *(getenv ("LD_BIND_NOW") ?: "") == '\0';
- /* Now we have all the objects loaded. Relocate them all except for
- the dynamic linker itself. We do this in reverse order so that
- copy relocs of earlier objects overwrite the data written by later
- objects. We do not re-relocate the dynamic linker itself in this
- loop because that could result in the GOT entries for functions we
- call being changed, and that would break us. It is safe to
- relocate the dynamic linker out of order because it has no copy
- relocs (we know that because it is self-contained). */
- l = _dl_loaded;
- while (l->l_next)
- l = l->l_next;
- do
- {
- if (l != &_dl_rtld_map)
- _dl_relocate_object (l, lazy);
- l = l->l_prev;
- } while (l);
-
- /* Do any necessary cleanups for the startup OS interface code.
- We do these now so that no calls are made after rtld re-relocation
- which might be resolved to different functions than we expect.
- We cannot do this before relocating the other objects because
- _dl_relocate_object might need to call `mprotect' for DT_TEXTREL. */
- _dl_sysdep_start_cleanup ();
-
- if (_dl_rtld_map.l_opencount > 0)
- /* There was an explicit ref to the dynamic linker as a shared lib.
- Re-relocate ourselves with user-controlled symbol definitions. */
- _dl_relocate_object (&_dl_rtld_map, lazy);
+ {
+ /* Now we have all the objects loaded. Relocate them all except for
+ the dynamic linker itself. We do this in reverse order so that copy
+ relocs of earlier objects overwrite the data written by later
+ objects. We do not re-relocate the dynamic linker itself in this
+ loop because that could result in the GOT entries for functions we
+ call being changed, and that would break us. It is safe to relocate
+ the dynamic linker out of order because it has no copy relocs (we
+ know that because it is self-contained). */
+
+ l = _dl_loaded;
+ while (l->l_next)
+ l = l->l_next;
+ do
+ {
+ if (l != &_dl_rtld_map)
+ {
+ _dl_relocate_object (l, _dl_object_relocation_scope (l), lazy);
+ *_dl_global_scope_end = NULL;
+ }
+ l = l->l_prev;
+ } while (l);
+
+ /* Do any necessary cleanups for the startup OS interface code.
+ We do these now so that no calls are made after rtld re-relocation
+ which might be resolved to different functions than we expect.
+ We cannot do this before relocating the other objects because
+ _dl_relocate_object might need to call `mprotect' for DT_TEXTREL. */
+ _dl_sysdep_start_cleanup ();
+
+ if (_dl_rtld_map.l_opencount > 0)
+ /* There was an explicit ref to the dynamic linker as a shared lib.
+ Re-relocate ourselves with user-controlled symbol definitions. */
+ _dl_relocate_object (&_dl_rtld_map, &_dl_default_scope[2], 0);
+ }
/* Tell the debugger where to find the map of loaded objects. */
_dl_r_debug.r_version = 1 /* R_DEBUG_VERSION XXX */;