aboutsummaryrefslogtreecommitdiff
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/