aboutsummaryrefslogtreecommitdiff
path: root/elf
diff options
context:
space:
mode:
Diffstat (limited to 'elf')
-rw-r--r--elf/Makefile4
-rw-r--r--elf/dl-error.c4
-rw-r--r--elf/dl-load.c34
-rw-r--r--elf/dl-lookup.c159
-rw-r--r--elf/dl-object.c9
-rw-r--r--elf/dl-open.c4
-rw-r--r--elf/dl-reloc.c11
-rw-r--r--elf/dl-runtime.c35
-rw-r--r--elf/dl-version.c337
-rw-r--r--elf/dlerror.c4
-rw-r--r--elf/dlfcn.h13
-rw-r--r--elf/dlopen.c4
-rw-r--r--elf/dlsym.c4
-rw-r--r--elf/dlvsym.c72
-rw-r--r--elf/do-rel.h36
-rw-r--r--elf/elf.h48
-rw-r--r--elf/ldd.bash.in14
-rw-r--r--elf/ldd.sh.in14
-rw-r--r--elf/link.h66
-rw-r--r--elf/rtld.c77
20 files changed, 851 insertions, 98 deletions
diff --git a/elf/Makefile b/elf/Makefile
index 4666919478..b69b8a0754 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -22,7 +22,7 @@ subdir := elf
headers = elf.h elfclass.h link.h dlfcn.h
routines = $(dl-routines) dl-open dl-close dl-symbol dl-support \
- enbl-secure
+ dl-version enbl-secure
# The core dynamic linking functions are in libc for the static and
# profiled libraries.
@@ -39,7 +39,7 @@ distribute = $(rtld-routines:=.c) dynamic-link.h do-rel.h dl-machine.h \
extra-libs = libdl
extra-libs-others = $(extra-libs)
-libdl-routines := dlopen dlclose dlsym dlerror dladdr
+libdl-routines := dlopen dlclose dlsym dlvsym dlerror dladdr
before-compile = $(objpfx)trusted-dirs.h
diff --git a/elf/dl-error.c b/elf/dl-error.c
index a19ccff626..263bd65eb0 100644
--- a/elf/dl-error.c
+++ b/elf/dl-error.c
@@ -66,9 +66,9 @@ _dl_signal_error (int errcode,
else if (receiver)
{
/* We are inside _dl_receive_error. Call the user supplied
- handler and resume the work. The receiver will still
+ handler and resume the work. The receiver will still be
installed. */
- (*receiver) (errstring, objname);
+ (*receiver) (errcode, objname, errstring);
}
else
{
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 7368858f64..e3de0764ec 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -154,11 +154,34 @@ _dl_map_object_from_fd (char *name, int fd, char *realname,
for (l = _dl_loaded; l; l = l->l_next)
if (! strcmp (realname, l->l_name))
{
+ struct libname_list *lnp, *lastp;
/* The object is already loaded.
Just bump its reference count and return it. */
__close (fd);
- free (name);
+
+ /* If the name is not in the list of names for this object add
+ it. */
free (realname);
+ lastp = NULL;
+ for (lnp = l->l_libname; lnp != NULL; lastp = lnp, lnp = lnp->next)
+ if (strcmp (name, lnp->name) == 0)
+ {
+ free (name);
+ break;
+ }
+ if (lnp == NULL)
+ {
+ struct libname_list *newname = malloc (sizeof *newname);
+ if (newname == NULL)
+ /* No more memory. */
+ lose (ENOMEM, "cannot allocate name record");
+ /* The object should have a libname set. */
+ assert (lastp != NULL);
+
+ newname->name = name;
+ newname->next = NULL;
+ lastp->next = newname;
+ }
++l->l_opencount;
return l;
}
@@ -517,8 +540,7 @@ _dl_map_object (struct link_map *loader, const char *name, int type,
/* Look for this name among those already loaded. */
for (l = _dl_loaded; l; l = l->l_next)
- if (! strcmp (name, l->l_libname) || /* NAME was requested before. */
- ! strcmp (name, l->l_name) || /* NAME was found before. */
+ if (_dl_does_name_match_p (name, l) ||
/* If the requested name matches the soname of a loaded object,
use that object. */
(l->l_info[DT_SONAME] &&
@@ -559,15 +581,17 @@ _dl_map_object (struct link_map *loader, const char *name, int type,
l->l_info[DT_STRTAB]->d_un.d_ptr +
l->l_info[DT_RPATH]->d_un.d_val), NULL);
/* Try an environment variable (unless setuid). */
- if (fd == -1 && ! __libc_enable_secure)
+ if (fd == -1)
{
static const char *trusted_dirs[] =
{
#include "trusted-dirs.h"
NULL
};
+ const char *ld_library_path = getenv ("LD_LIBRARY_PATH");
- trypath (getenv ("LD_LIBRARY_PATH"), trusted_dirs);
+ if (ld_library_path != NULL && *ld_library_path != '\0')
+ trypath (ld_library_path, trusted_dirs);
}
if (fd == -1)
{
diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
index 1000a52e0d..06d1d09b90 100644
--- a/elf/dl-lookup.c
+++ b/elf/dl-lookup.c
@@ -1,5 +1,5 @@
/* Look up a symbol in the loaded objects.
- Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Copyright (C) 1995, 1996, 1997 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
@@ -22,6 +22,9 @@
#include <assert.h>
#include <string.h>
+#include "../stdio-common/_itoa.h"
+
+#define VERSTAG(tag) (DT_NUM + DT_PROCNUM + DT_VERSIONTAGIDX (tag))
struct sym_val
{
@@ -30,22 +33,46 @@ struct sym_val
};
-/* This is the hashing function specified by the ELF ABI. */
+/* This is the hashing function specified by the ELF ABI. In the
+ first five operations now overflow is possible so we optimized it a
+ bit. */
static inline unsigned
_dl_elf_hash (const char *name)
{
unsigned long int hash = 0;
- while (*name != '\0')
+ if (*name != '\0')
{
- unsigned long int hi;
hash = (hash << 4) + *name++;
- hi = hash & 0xf0000000;
- if (hi != 0)
+ if (*name != '\0')
{
- hash ^= hi >> 24;
- /* The ELF ABI says `hash &= ~hi', but this is equivalent
- in this case and on some machines one insn instead of two. */
- hash ^= hi;
+ hash = (hash << 4) + *name++;
+ if (*name != '\0')
+ {
+ hash = (hash << 4) + *name++;
+ if (*name != '\0')
+ {
+ hash = (hash << 4) + *name++;
+ if (*name != '\0')
+ {
+ hash = (hash << 4) + *name++;
+ while (*name != '\0')
+ {
+ unsigned long int hi;
+ hash = (hash << 4) + *name++;
+ hi = hash & 0xf0000000;
+ if (hi != 0)
+ {
+ hash ^= hi >> 24;
+ /* The ELF ABI says `hash &= ~hi', but
+ this is equivalent in this case and
+ on some machines one insn instead of
+ two. */
+ hash ^= hi;
+ }
+ }
+ }
+ }
+ }
}
}
return hash;
@@ -57,7 +84,8 @@ static inline ElfW(Addr)
do_lookup (const char *undef_name, unsigned long int hash,
const ElfW(Sym) **ref, struct sym_val *result,
struct link_map *list[], size_t i, size_t n,
- const char *reference_name, struct link_map *skip, int flags)
+ const char *reference_name, const hash_name_pair *version,
+ struct link_map *skip, int flags)
{
struct link_map *map;
@@ -65,6 +93,7 @@ do_lookup (const char *undef_name, unsigned long int hash,
{
const ElfW(Sym) *symtab;
const char *strtab;
+ const ElfW(Half) *verstab;
ElfW(Symndx) symidx;
map = list[i];
@@ -79,6 +108,12 @@ do_lookup (const char *undef_name, unsigned long int hash,
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);
+ if (version != NULL
+ && map->l_nversions > 0 && map->l_info[VERSTAG (DT_VERSYM)] != NULL)
+ verstab = ((void *) map->l_addr
+ + map->l_info[VERSTAG (DT_VERSYM)]->d_un.d_ptr);
+ else
+ verstab = NULL;
/* Search the appropriate hash bucket in this object's symbol table
for a definition for the same symbol name. */
@@ -108,6 +143,15 @@ do_lookup (const char *undef_name, unsigned long int hash,
/* Not the symbol we are looking for. */
continue;
+ if (verstab != NULL)
+ {
+ /* We can match the version information. */
+ ElfW(Half) ndx = verstab[symidx] & 0x7fff;
+ if (map->l_versions[ndx].hash != version->hash
+ || strcmp (map->l_versions[ndx].name, version->name))
+ continue;
+ }
+
switch (ELFW(ST_BIND) (sym->st_info))
{
case STB_GLOBAL:
@@ -155,14 +199,14 @@ _dl_lookup_symbol (const char *undef_name, const ElfW(Sym) **ref,
for (scope = symbol_scope; *scope; ++scope)
if (do_lookup (undef_name, hash, ref, &current_value,
(*scope)->l_searchlist, 0, (*scope)->l_nsearchlist,
- reference_name, NULL, flags))
+ reference_name, NULL, NULL, flags))
break;
if (current_value.s == NULL &&
(*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK))
{
/* We could find no value for a strong reference. */
- const char msg[] = "undefined symbol: ";
+ static const char msg[] = "undefined symbol: ";
const size_t len = strlen (undef_name);
char buf[sizeof msg + len];
memcpy (buf, msg, sizeof msg - 1);
@@ -199,11 +243,96 @@ _dl_lookup_symbol_skip (const char *undef_name, const ElfW(Sym) **ref,
if (! do_lookup (undef_name, hash, ref, &current_value,
(*scope)->l_dupsearchlist, i, (*scope)->l_ndupsearchlist,
- reference_name, skip_map, flags))
+ reference_name, NULL, skip_map, flags))
+ while (*++scope)
+ if (do_lookup (undef_name, hash, ref, &current_value,
+ (*scope)->l_dupsearchlist, 0, (*scope)->l_ndupsearchlist,
+ reference_name, NULL, skip_map, flags))
+ break;
+
+ *ref = current_value.s;
+ return current_value.a;
+}
+
+
+/* This function works like _dl_lookup_symbol but it takes an
+ additional arguement with the version number of the requested
+ symbol.
+
+ XXX We'll see whether we need this separate function. */
+ElfW(Addr)
+_dl_lookup_versioned_symbol (const char *undef_name, const ElfW(Sym) **ref,
+ struct link_map *symbol_scope[],
+ const char *reference_name,
+ const hash_name_pair *version, int flags)
+{
+ const unsigned long int hash = _dl_elf_hash (undef_name);
+ struct sym_val current_value = { 0, NULL };
+ struct link_map **scope;
+
+ /* Search the relevant loaded objects for a definition. */
+ for (scope = symbol_scope; *scope; ++scope)
+ if (do_lookup (undef_name, hash, ref, &current_value,
+ (*scope)->l_searchlist, 0, (*scope)->l_nsearchlist,
+ reference_name, version, NULL, flags))
+ break;
+
+ if (current_value.s == NULL &&
+ (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK))
+ {
+ /* We could find no value for a strong reference. */
+ static const char msg1[] = "undefined symbol: ";
+ const size_t len = strlen (undef_name);
+ static const char msg2[] = ", version ";
+ const size_t verslen = strlen (version->name ?: "") + 1;
+ char buf[sizeof msg1 - 1 + len + sizeof msg2 - 1 + verslen];
+
+ memcpy (buf, msg1, sizeof msg1 - 1);
+ memcpy (&buf[sizeof msg1 - 1], undef_name, len + 1);
+ memcpy (&buf[sizeof msg1 - 1 + len], msg2, sizeof msg2 - 1);
+ memcpy (&buf[sizeof msg1 - 1 + len + sizeof msg2 - 1], version->name,
+ verslen);
+ _dl_signal_error (0, reference_name, buf);
+ }
+
+ *ref = current_value.s;
+ return current_value.a;
+}
+
+
+/* Similar to _dl_lookup_symbol_skip but takes an additional argument
+ with the version we are looking for. */
+ElfW(Addr)
+_dl_lookup_versioned_symbol_skip (const char *undef_name,
+ const ElfW(Sym) **ref,
+ struct link_map *symbol_scope[],
+ const char *reference_name,
+ const char *version_name,
+ struct link_map *skip_map,
+ int flags)
+{
+ const unsigned long int hash = _dl_elf_hash (undef_name);
+ struct sym_val current_value = { 0, NULL };
+ struct link_map **scope;
+ hash_name_pair version;
+ size_t i;
+
+ /* First convert the VERSION_NAME into a `hash_name_pair' value. */
+ version.hash = _dl_elf_hash (version_name);
+ version.name = version_name;
+
+ /* Search the relevant loaded objects for a definition. */
+ scope = symbol_scope;
+ for (i = 0; (*scope)->l_dupsearchlist[i] != skip_map; ++i)
+ assert (i < (*scope)->l_ndupsearchlist);
+
+ if (! do_lookup (undef_name, hash, ref, &current_value,
+ (*scope)->l_dupsearchlist, i, (*scope)->l_ndupsearchlist,
+ reference_name, &version, skip_map, flags))
while (*++scope)
if (do_lookup (undef_name, hash, ref, &current_value,
(*scope)->l_dupsearchlist, 0, (*scope)->l_ndupsearchlist,
- reference_name, skip_map, flags))
+ reference_name, &version, skip_map, flags))
break;
*ref = current_value.s;
diff --git a/elf/dl-object.c b/elf/dl-object.c
index 6986dc34d3..941bfa34cc 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, 1996 Free Software Foundation, Inc.
+ Copyright (C) 1995, 1996, 1997 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
@@ -34,12 +34,15 @@ struct link_map *
_dl_new_object (char *realname, const char *libname, int type)
{
struct link_map *new = malloc (sizeof *new);
- if (! new)
+ struct libname_list *newname = malloc (sizeof *newname);
+ if (! new || ! newname)
return NULL;
memset (new, 0, sizeof *new);
new->l_name = realname;
- new->l_libname = libname;
+ newname->name = libname;
+ newname->next = NULL;
+ new->l_libname = newname;
new->l_type = type;
if (_dl_loaded == NULL)
diff --git a/elf/dl-open.c b/elf/dl-open.c
index fe9f8ff017..5915b7f2a2 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -1,5 +1,5 @@
/* Load a shared object at runtime, relocate it, and run its initializer.
- Copyright (C) 1996 Free Software Foundation, Inc.
+ Copyright (C) 1996, 1997 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
@@ -54,6 +54,8 @@ _dl_open (const char *file, int mode)
/* Load that object's dependencies. */
_dl_map_object_deps (new, NULL, 0, 0);
+ /* So far, so good. Now check the versions. */
+ (void) _dl_check_map_versions (new, 0);
/* Relocate the objects loaded. We do this in reverse order so that copy
relocs of earlier objects overwrite the data written by later objects. */
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index e299e523ee..f1c43ea174 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -1,5 +1,5 @@
/* Relocate a shared object and resolve its references to other loaded objects.
- Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Copyright (C) 1995, 1996, 1997 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
@@ -57,9 +57,12 @@ _dl_relocate_object (struct link_map *l, struct link_map *scope[], int lazy)
= ((void *) l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr);
/* This macro is used as a callback from the ELF_DYNAMIC_RELOCATE code. */
-#define RESOLVE(ref, flags) \
- (_dl_lookup_symbol (strtab + (*ref)->st_name, ref, scope, \
- l->l_name, flags))
+#define RESOLVE(ref, version, flags) \
+ ((version) != NULL && (version)->hash != 0 \
+ ? _dl_lookup_versioned_symbol (strtab + (*ref)->st_name, (ref), scope, \
+ l->l_name, (version), (flags)) \
+ : _dl_lookup_symbol (strtab + (*ref)->st_name, (ref), scope, \
+ l->l_name, (flags)))
#include "dynamic-link.h"
ELF_DYNAMIC_RELOCATE (l, lazy);
diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c
index 088ff64c6e..08c605cd0f 100644
--- a/elf/dl-runtime.c
+++ b/elf/dl-runtime.c
@@ -1,5 +1,5 @@
/* On-demand PLT fixup for shared objects.
- Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Copyright (C) 1995, 1996, 1997 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
@@ -74,15 +74,19 @@ _dl_object_relocation_scope (struct link_map *l)
#define elf_machine_rel 1
#define elf_machine_rela 2
#if elf_machine_relplt == elf_machine_rel
-#define PLTREL ElfW(Rel)
+# define PLTREL ElfW(Rel)
#elif elf_machine_relplt == elf_machine_rela
-#define PLTREL ElfW(Rela)
+# define PLTREL ElfW(Rela)
#else
-#error "dl-machine.h bug: elf_machine_relplt not rel or rela"
+# error "dl-machine.h bug: elf_machine_relplt not rel or rela"
#endif
#undef elf_machine_rel
#undef elf_machine_rela
+#ifndef VERSYMIDX
+# define VERSYMIDX(sym) (DT_NUM + DT_PROCNUM + DT_VERSIONTAGIDX (sym))
+#endif
+
/* We need to define the function as a local symbol so that the reference
in the trampoline code will be a local PC-relative call. Tell the
compiler not to worry that the function appears not to be called. */
@@ -122,13 +126,28 @@ fixup (
{
/* This macro is used as a callback from the elf_machine_relplt code. */
-#define RESOLVE(ref, flags) \
- (_dl_lookup_symbol (strtab + (*ref)->st_name, ref, scope, \
- l->l_name, flags))
+#define RESOLVE(ref, version, flags) \
+ ((version) != NULL && (version)->hash != 0 \
+ ? _dl_lookup_versioned_symbol (strtab + (*ref)->st_name, (ref), scope, \
+ l->l_name, (version), (flags)) \
+ : _dl_lookup_symbol (strtab + (*ref)->st_name, (ref), scope, \
+ l->l_name, (flags)))
#include "dynamic-link.h"
/* Perform the specified relocation. */
- elf_machine_relplt (l, reloc, &symtab[ELFW(R_SYM) (reloc->r_info)]);
+ if (l->l_info[VERSYMIDX (DT_VERNEEDNUM)])
+ {
+ const ElfW(Half) * version =
+ (const ElfW(Half) *) (l->l_addr +
+ l->l_info[VERSYMIDX (DT_VERSYM)]->d_un.d_ptr);
+ ElfW(Half) ndx = version[ELFW(R_SYM) (reloc->r_info)];
+
+ elf_machine_relplt (l, reloc, &symtab[ELFW(R_SYM) (reloc->r_info)],
+ &l->l_versions[ndx]);
+ }
+ else
+ elf_machine_relplt (l, reloc, &symtab[ELFW(R_SYM) (reloc->r_info)],
+ NULL);
}
*_dl_global_scope_end = NULL;
diff --git a/elf/dl-version.c b/elf/dl-version.c
new file mode 100644
index 0000000000..4382a4c0ae
--- /dev/null
+++ b/elf/dl-version.c
@@ -0,0 +1,337 @@
+/* Handle symbol and library versioning.
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
+
+ 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <elf.h>
+#include <errno.h>
+#include <link.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../stdio-common/_itoa.h"
+
+
+/* Set in rtld.c at startup. */
+extern char **_dl_argv;
+
+#define VERSTAG(tag) (DT_NUM + DT_PROCNUM + DT_VERSIONTAGIDX (tag))
+
+
+#define make_string(string, rest...) \
+ ({ \
+ const char *all[] = { string, ## rest }; \
+ size_t len, cnt; \
+ char *result, *cp; \
+ \
+ len = 1; \
+ for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt) \
+ len += strlen (all[cnt]); \
+ \
+ cp = result = alloca (len); \
+ for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt) \
+ cp = stpcpy (cp, all[cnt]); \
+ \
+ result; \
+ })
+
+
+static inline struct link_map *
+find_needed (struct link_map *map, const char *name)
+{
+ unsigned int n;
+
+ for (n = 0; n < map->l_nsearchlist; ++n)
+ if (_dl_does_name_match_p (name, map->l_searchlist[n]))
+ return map->l_searchlist[n];
+
+ /* Should never happen. */
+ return NULL;
+}
+
+
+static int
+match_symbol (const char *name, ElfW(Word) hash, const char *string,
+ struct link_map *map, int verbose, int weak)
+{
+ const char *strtab = (const char *) (map->l_addr
+ + map->l_info[DT_STRTAB]->d_un.d_ptr);
+ ElfW(Addr) def_offset = map->l_info[VERSTAG (DT_VERDEF)]->d_un.d_ptr;