diff options
Diffstat (limited to 'elf')
-rw-r--r-- | elf/Makefile | 7 | ||||
-rw-r--r-- | elf/Versions | 7 | ||||
-rw-r--r-- | elf/dl-deps.c | 79 | ||||
-rw-r--r-- | elf/dl-dst.h | 72 | ||||
-rw-r--r-- | elf/dl-load.c | 143 | ||||
-rw-r--r-- | elf/dl-open.c | 53 | ||||
-rw-r--r-- | elf/dlopen.c | 7 | ||||
-rw-r--r-- | elf/dlopenold.c | 5 | ||||
-rw-r--r-- | elf/ldsodefs.h | 3 | ||||
-rw-r--r-- | elf/origtest.c | 38 |
10 files changed, 326 insertions, 88 deletions
diff --git a/elf/Makefile b/elf/Makefile index 26a08322df..d6c37c2bb8 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -22,7 +22,7 @@ subdir := elf headers = elf.h bits/elfclass.h bits/dlfcn.h link.h dlfcn.h routines = $(dl-routines) dl-open dl-close dl-symbol dl-support \ - dl-addr enbl-secure dl-profstub + dl-addr enbl-secure dl-profstub dl-origin # The core dynamic linking functions are in libc for the static and # profiled libraries. @@ -78,7 +78,7 @@ install-rootsbin += ldconfig endif ifeq (yes,$(build-shared)) -tests = loadtest restest1 preloadtest loadfail multiload +tests = loadtest restest1 preloadtest loadfail multiload origtest endif modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \ testobj1_1 failobj @@ -253,6 +253,9 @@ LDFLAGS-multiload = -rdynamic CFLAGS-multiload.c = -DOBJDIR=\"$(elf-objpfx)\" $(objpfx)multiload.out: $(objpfx)testobj1.so + +$(objpfx)origtest: $(libdl) +$(objpfx)origtest.out: $(objpfx)testobj1.so # muwahaha diff --git a/elf/Versions b/elf/Versions index 8e6dca878b..e26e096469 100644 --- a/elf/Versions +++ b/elf/Versions @@ -37,6 +37,13 @@ ld.so { # functions used in other libraries _dl_mcount; _dl_unload_cache; } + GLIBC_2.1.1 { + # global variables + _dl_origin_path; _dl_platformlen; + + # functions used in other libraries + _dl_dst_count; _dl_dst_substitute; + } } libdl { diff --git a/elf/dl-deps.c b/elf/dl-deps.c index 7291cde787..56f91f6dd0 100644 --- a/elf/dl-deps.c +++ b/elf/dl-deps.c @@ -17,13 +17,15 @@ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include <assert.h> #include <dlfcn.h> #include <errno.h> #include <stdlib.h> #include <string.h> +#include <sys/param.h> #include <elf/ldsodefs.h> -#include <assert.h> +#include <dl-dst.h> /* Whether an shared object references one or more auxiliary objects is signaled by the AUXTAG entry in l_info. */ @@ -39,6 +41,7 @@ reset if in _dl_close if the last global object is removed. */ size_t _dl_global_scope_alloc; +extern size_t _dl_platformlen; /* When loading auxiliary objects we must ignore errors. It's ok if an object is missing. */ @@ -48,7 +51,7 @@ struct openaux_args struct link_map *map; int trace_mode; const char *strtab; - const ElfW(Dyn) *d; + const char *name; /* The return value of openaux. */ struct link_map *aux; @@ -59,7 +62,7 @@ openaux (void *a) { struct openaux_args *args = (struct openaux_args *) a; - args->aux = _dl_map_object (args->map, args->strtab + args->d->d_un.d_val, 0, + args->aux = _dl_map_object (args->map, args->name, 0, (args->map->l_type == lt_executable ? lt_library : args->map->l_type), args->trace_mode); @@ -84,6 +87,43 @@ struct list }; +/* Macro to expand DST. It is an macro since we use `alloca'. */ +#define expand_dst(l, str, fatal) \ + ({ \ + const char *__str = (str); \ + const char *__result = __str; \ + size_t __cnt = DL_DST_COUNT(__str, 0); \ + \ + if (__cnt != 0) \ + { \ + char *__newp = (char *) alloca (DL_DST_REQUIRED (l, __str, \ + strlen (__str), \ + __cnt)); \ + \ + __result = DL_DST_SUBSTITUTE (l, __str, __newp, 0); \ + \ + if (*__result == '\0') \ + { \ + /* The replacement for the DST is not known. We can't \ + processed. */ \ + if (fatal) \ + _dl_signal_error (0, __str, \ + "empty dynamics string token substitution"); \ + else \ + { \ + /* This is for DT_AUXILIARY. */ \ + if (_dl_debug_libs) \ + _dl_debug_message (1, "cannot load auxiliary `", __str, \ + "' because of empty dynamic string" \ + " token substitution\n", NULL); \ + continue; \ + } \ + } \ + } \ + \ + __result; }) + + unsigned int internal_function _dl_map_object_deps (struct link_map *map, @@ -166,14 +206,21 @@ _dl_map_object_deps (struct link_map *map, if (__builtin_expect (d->d_tag, DT_NEEDED) == DT_NEEDED) { /* Map in the needed object. */ - struct link_map *dep - = _dl_map_object (l, strtab + d->d_un.d_val, 0, - l->l_type == lt_executable ? lt_library : - l->l_type, trace_mode); + struct link_map *dep; /* Allocate new entry. */ - struct list *newp = alloca (sizeof (struct list)); + struct list *newp; + /* Object name. */ + const char *name; + + /* Recognize DSTs. */ + name = expand_dst (l, strtab + d->d_un.d_val, 0); + + dep = _dl_map_object (l, name, 0, + l->l_type == lt_executable ? lt_library : + l->l_type, trace_mode); /* Add it in any case to the duplicate list. */ + newp = alloca (sizeof (struct list)); newp->map = dep; newp->dup = NULL; dtail->dup = newp; @@ -202,17 +249,22 @@ _dl_map_object_deps (struct link_map *map, { char *errstring; struct list *newp; + /* Object name. */ + const char *name; + + /* Recognize DSTs. */ + name = expand_dst (l, strtab + d->d_un.d_val, + d->d_tag == DT_AUXILIARY); if (d->d_tag == DT_AUXILIARY) { /* Store the tag in the argument structure. */ - args.d = d; + args.name = name; /* Say that we are about to load an auxiliary library. */ if (_dl_debug_libs) _dl_debug_message (1, "load auxiliary object=", - strtab + d->d_un.d_val, - " requested by file=", + name, " requested by file=", l->l_name[0] ? l->l_name : _dl_argv[0], "\n", NULL); @@ -233,15 +285,14 @@ _dl_map_object_deps (struct link_map *map, { /* Say that we are about to load an auxiliary library. */ if (_dl_debug_libs) - _dl_debug_message (1, "load filtered object=", - strtab + d->d_un.d_val, + _dl_debug_message (1, "load filtered object=", name, " requested by file=", l->l_name[0] ? l->l_name : _dl_argv[0], "\n", NULL); /* For filter objects the dependency must be available. */ - args.aux = _dl_map_object (l, strtab + d->d_un.d_val, 0, + args.aux = _dl_map_object (l, name, 0, (l->l_type == lt_executable ? lt_library : l->l_type), trace_mode); diff --git a/elf/dl-dst.h b/elf/dl-dst.h new file mode 100644 index 0000000000..4b014d6ed5 --- /dev/null +++ b/elf/dl-dst.h @@ -0,0 +1,72 @@ +/* Handling of dynamic sring tokens. + Copyright (C) 1999 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* Determine the number of DST elements in the name. Only if IS_PATH is + nonzero paths are recognized (i.e., multiple, ':' separated filenames). */ +#define DL_DST_COUNT(name, is_path) \ + ({ \ + size_t __cnt = 0; \ + const char *__sf = strchr (name, '$'); \ + \ + if (__sf != NULL) \ + __cnt = _dl_dst_count (__sf, is_path); \ + \ + __cnt; }) + +/* Prototype for used function. */ +extern size_t _dl_dst_count (const char *name, int is_path); + + +/* Guess from the number of DSTs the length of the restul string. */ +#define DL_DST_REQUIRED(l, name, len, cnt) \ + ({ \ + size_t origin_len; \ + size_t __len = (len); \ + \ + /* Now we make a guess how many extra characters on top of the length \ + of S we need to represent the result. We know that we have CNT \ + replacements. Each at most can use \ + MAX (strlen (ORIGIN), strlen (_dl_platform)) \ + minus 7 (which is the length of "$ORIGIN"). \ + \ + First get the origin string if it is not available yet. This can \ + only happen for the map of the executable. */ \ + if ((l)->l_origin == NULL) \ + { \ + assert ((l)->l_name[0] == '\0'); \ + (l)->l_origin = _dl_get_origin (); \ + origin_len = ((l)->l_origin && (l)->l_origin != (char *) -1 \ + ? strlen ((l)->l_origin) : 0); \ + } \ + else \ + origin_len = (l)->l_origin == (char *) -1 ? 0 : strlen ((l)->l_origin); \ + \ + __len + cnt * (MAX (origin_len, _dl_platformlen) - 7); }) + +/* Find origin of the executable. */ +extern const char *_dl_get_origin (void); + + +/* Perform the DST substitution. */ +#define DL_DST_SUBSTITUTE(l, name, res, is_path) \ + _dl_dst_substitute (l, name, res, is_path) + +/* Prototype for used function. */ +extern char *_dl_dst_substitute (struct link_map *l, const char *name, + char *result, int is_path); diff --git a/elf/dl-load.c b/elf/dl-load.c index 46f0b67567..6e8b977195 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -31,8 +31,7 @@ #include "dynamic-link.h" #include <stdio-common/_itoa.h> -#include <dl-origin.h> - +#include <dl-dst.h> /* On some systems, no flag bits are given to specify file mapping. */ #ifndef MAP_FILE @@ -146,129 +145,139 @@ local_strdup (const char *s) return (char *) memcpy (new, s, len); } -/* Return copy of argument with all recognized dynamic string tokens - ($ORIGIN and $PLATFORM for now) replaced. On some platforms it - might not be possible to determine the path from which the object - belonging to the map is loaded. In this case the path element - containing $ORIGIN is left out. */ -static char * -expand_dynamic_string_token (struct link_map *l, const char *s) + +size_t +_dl_dst_count (const char *name, int is_path) { - /* We make two runs over the string. First we determine how large the - resulting string is and then we copy it over. Since this is now - frequently executed operation we are looking here not for performance - but rather for code size. */ - const char *sf; size_t cnt = 0; - size_t origin_len; - size_t total; - char *result, *last_elem, *wp; - sf = strchr (s, '$'); - while (sf != NULL) + do { size_t len = 1; /* $ORIGIN is not expanded for SUID/GUID programs. */ if ((((!__libc_enable_secure - && strncmp (&sf[1], "ORIGIN", 6) == 0 && (len = 7) != 0) - || (strncmp (&sf[1], "PLATFORM", 8) == 0 && (len = 9) != 0)) - && (s[len] == '\0' || s[len] == '/' || s[len] == ':')) - || (s[1] == '{' + && strncmp (&name[1], "ORIGIN", 6) == 0 && (len = 7) != 0) + || (strncmp (&name[1], "PLATFORM", 8) == 0 && (len = 9) != 0)) + && (name[len] == '\0' || name[len] == '/' + || (is_path && name[len] == ':'))) + || (name[1] == '{' && ((!__libc_enable_secure - && strncmp (&sf[2], "ORIGIN}", 7) == 0 && (len = 9) != 0) - || (strncmp (&sf[2], "PLATFORM}", 9) == 0 + && strncmp (&name[2], "ORIGIN}", 7) == 0 && (len = 9) != 0) + || (strncmp (&name[2], "PLATFORM}", 9) == 0 && (len = 11) != 0)))) ++cnt; - sf = strchr (sf + len, '$'); + name = strchr (name + len, '$'); } + while (name != NULL); - /* If we do not have to replace anything simply copy the string. */ - if (cnt == 0) - return local_strdup (s); - - /* Now we make a guess how many extra characters on top of the length - of S we need to represent the result. We know that we have CNT - replacements. Each at most can use - MAX (strlen (ORIGIN), strlen (_dl_platform)) - minus 7 (which is the length of "$ORIGIN"). + return cnt; +} - First get the origin string if it is not available yet. This can - only happen for the map of the executable. */ - if (l->l_origin == NULL) - { - assert (l->l_name[0] == '\0'); - l->l_origin = get_origin (); - origin_len = (l->l_origin && l->l_origin != (char *) -1 - ? strlen (l->l_origin) : 0); - } - else - origin_len = l->l_origin == (char *) -1 ? 0 : strlen (l->l_origin); - total = strlen (s) + cnt * (MAX (origin_len, _dl_platformlen) - 7); - result = (char *) malloc (total + 1); - if (result == NULL) - return NULL; +char * +_dl_dst_substitute (struct link_map *l, const char *name, char *result, + int is_path) +{ + char *last_elem, *wp; /* Now fill the result path. While copying over the string we keep track of the start of the last path element. When we come accross a DST we copy over the value or (if the value is not available) leave the entire path element out. */ last_elem = wp = result; + do { - if (*s == '$') + if (*name == '$') { const char *repl; size_t len; - if ((((strncmp (&s[1], "ORIGIN", 6) == 0 && (len = 7) != 0) - || (strncmp (&s[1], "PLATFORM", 8) == 0 && (len = 9) != 0)) - && (s[len] == '\0' || s[len] == '/' || s[len] == ':')) - || (s[1] == '{' - && ((strncmp (&s[2], "ORIGIN}", 7) == 0 && (len = 9) != 0) - || (strncmp (&s[2], "PLATFORM}", 9) == 0 + if ((((strncmp (&name[1], "ORIGIN", 6) == 0 && (len = 7) != 0) + || (strncmp (&name[1], "PLATFORM", 8) == 0 && (len = 9) != 0)) + && (name[len] == '\0' || name[len] == '/' + || (is_path && name[len] == ':'))) + || (name[1] == '{' + && ((strncmp (&name[2], "ORIGIN}", 7) == 0 && (len = 9) != 0) + || (strncmp (&name[2], "PLATFORM}", 9) == 0 && (len = 11) != 0)))) { - repl = ((len == 7 || s[2] == 'O') + repl = ((len == 7 || name[2] == 'O') ? (__libc_enable_secure ? NULL : l->l_origin) : _dl_platform); if (repl != NULL && repl != (const char *) -1) { wp = __stpcpy (wp, repl); - s += len; + name += len; } else { /* We cannot use this path element, the value of the replacement is unknown. */ wp = last_elem; - s += len; - while (*s != '\0' && *s != ':') - ++s; + name += len; + while (*name != '\0' && (!is_path || *name != ':')) + ++name; } } else /* No DST we recognize. */ - *wp++ = *s++; + *wp++ = *name++; } - else if (*s == ':') + else if (is_path && *name == ':') { - *wp++ = *s++; + *wp++ = *name++; last_elem = wp; } else - *wp++ = *s++; + *wp++ = *name++; } - while (*s != '\0'); + while (*name != '\0'); *wp = '\0'; return result; } + +/* Return copy of argument with all recognized dynamic string tokens + ($ORIGIN and $PLATFORM for now) replaced. On some platforms it + might not be possible to determine the path from which the object + belonging to the map is loaded. In this case the path element + containing $ORIGIN is left out. */ +static char * +expand_dynamic_string_token (struct link_map *l, const char *s) +{ + /* We make two runs over the string. First we determine how large the + resulting string is and then we copy it over. Since this is now + frequently executed operation we are looking here not for performance + but rather for code size. */ + size_t cnt; + size_t total; + char *result; + + /* Determine the nubmer of DST elements. */ + cnt = DL_DST_COUNT (s, 1); + + /* If we do not have to replace anything simply copy the string. */ + if (cnt == 0) + return local_strdup (s); + + /* Determine the length of the substituted string. */ + total = DL_DST_REQUIRED (l, s, strlen (s), cnt); + + /* Allocate the necessary memory. */ + result = (char *) malloc (total + 1); + if (result == NULL) + return NULL; + + return DL_DST_SUBSTITUTE (l, s, result, 1); +} + + /* Add `name' to the list of names for a particular shared object. `name' is expected to have been allocated with malloc and will be freed if the shared object already has this name. diff --git a/elf/dl-open.c b/elf/dl-open.c index 2b3352b674..9a3c0939d8 100644 --- a/elf/dl-open.c +++ b/elf/dl-open.c @@ -17,14 +17,18 @@ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include <assert.h> #include <dlfcn.h> #include <errno.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> /* Check whether MAP_COPY is defined. */ +#include <sys/param.h> #include <bits/libc-lock.h> #include <elf/ldsodefs.h> +#include <dl-dst.h> + extern ElfW(Addr) _dl_sysdep_start (void **start_argptr, void (*dl_main) (const ElfW(Phdr) *phdr, @@ -57,6 +61,7 @@ static void show_scope (struct link_map *new); At this time it is not anymore a problem to modify the tables. */ __libc_lock_define_initialized_recursive (, _dl_load_lock) +extern size_t _dl_platformlen; /* We must be carefull not to leave us in an inconsistent state. Thus we catch any error and re-raise it after cleaning up. */ @@ -65,6 +70,7 @@ struct dl_open_args { const char *file; int mode; + const void *caller; struct link_map *map; }; @@ -78,6 +84,50 @@ dl_open_worker (void *a) ElfW(Addr) init; struct r_debug *r; unsigned int global_add; + const char *dst; + + /* Maybe we have to expand a DST. */ + dst = strchr (file, '$'); + if (dst != NULL) + { + const void *caller = args->caller; + size_t len = strlen (file); + size_t required; + struct link_map *call_map; + char *new_file; + + /* We have to find out from which object the caller is calling. + Find the highest-addressed object that ADDRESS is not below. */ + call_map = NULL; + for (l = _dl_loaded; l; l = l->l_next) + if (l->l_addr != 0 /* Make sure we do not currently set this map up + in this moment. */ + && caller >= (const void *) l->l_addr + && (call_map == NULL || call_map->l_addr < l->l_addr)) + call_map = l; + + if (call_map == NULL) + /* In this case we assume this is the main application. */ + call_map = _dl_loaded; + + /* Determine how much space we need. We have to allocate the + memory locally. */ + required = DL_DST_REQUIRED (call_map, file, len, _dl_dst_count (dst, 0)); + + /* Get space for the new file name. */ + new_file = (char *) alloca (required + 1); + + /* Generate the new file name. */ + DL_DST_SUBSTITUTE (call_map, file, new_file, 0); + + /* If the substitution failed don't try to load. */ + if (*new_file == '\0') + _dl_signal_error (0, "dlopen", + "empty dynamics string token substitution"); + + /* Now we have a new file name. */ + file = new_file; + } /* Load the named object. */ args->map = new = _dl_map_object (NULL, file, 0, lt_loaded, 0); @@ -157,7 +207,7 @@ dl_open_worker (void *a) struct link_map * internal_function -_dl_open (const char *file, int mode) +_dl_open (const char *file, int mode, const void *caller) { struct dl_open_args args; char *errstring; @@ -172,6 +222,7 @@ _dl_open (const char *file, int mode) args.file = file; args.mode = mode; + args.caller = caller; args.map = NULL; errcode = _dl_catch_error (&errstring, dl_open_worker, &args); diff --git a/elf/dlopen.c b/elf/dlopen.c index 496fc7d386..b72300df5c 100644 --- a/elf/dlopen.c +++ b/elf/dlopen.c @@ -1,5 +1,5 @@ /* Load a shared object at run time. - Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc. + Copyright (C) 1995, 1996, 1997, 1998, 1999 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 @@ -28,6 +28,8 @@ struct dlopen_args int mode; /* The return value of dlopen_doit. */ struct link_map *new; + /* Address of the caller. */ + const void *caller; }; @@ -36,7 +38,7 @@ dlopen_doit (void *a) { struct dlopen_args *args = (struct dlopen_args *) a; - args->new = _dl_open (args->file ?: "", args->mode); + args->new = _dl_open (args->file ?: "", args->mode, args->caller); } @@ -46,6 +48,7 @@ __dlopen_check (const char *file, int mode) struct dlopen_args args; args.file = file; args.mode = mode; + args.caller = __builtin_return_address (0); return _dlerror_run (dlopen_doit, &args) ? NULL : args.new; } diff --git a/elf/dlopenold.c b/elf/dlopenold.c index 7e7b462e1a..661a40579a 100644 --- a/elf/dlopenold.c +++ b/elf/dlopenold.c @@ -32,6 +32,8 @@ struct dlopen_args int mode; /* The return value of dlopen_doit. */ struct link_map *new; + /* Address of the caller. */ + const void *caller; }; @@ -40,7 +42,7 @@ dlopen_doit (void *a) { struct dlopen_args *args = (struct dlopen_args *) a; - args->new = _dl_open (args->file ?: "", args->mode); + args->new = _dl_open (args->file ?: "", args->mode, args->caller); } @@ -49,6 +51,7 @@ __dlopen_nocheck (const char *file, int mode) { struct dlopen_args args; args.file = file; + args.caller = __builtin_return_address (0); if ((mode & RTLD_BINDING_MASK) == 0) /* By default assume RTLD_LAZY. */ diff --git a/elf/ldsodefs.h b/elf/ldsodefs.h index 392ea1787f..f8db76c32c 100644 --- a/elf/ldsodefs.h +++ b/elf/ldsodefs.h @@ -270,7 +270,8 @@ extern void _dl_setup_hash (struct link_map *map) internal_function; /* Open the shared object NAME, relocate it, and run its initializer if it hasn't already been run. MODE is as for `dlopen' (see <dlfcn.h>). If the object is already opened, returns its existing map. */ -extern struct link_map *_dl_open (const char *name, int mode) +extern struct link_map *_dl_open (const char *name, int mode, + const void *caller) internal_function; /* Close an object previously opened by _dl_open. */ diff --git a/elf/origtest.c b/elf/origtest.c new file mode 100644 index 0000000000..11bd0f76cf --- /dev/null +++ b/elf/origtest.c @@ -0,0 +1,38 @@ +#include <dlfcn.h> +#include <error.h> +#include <stdio.h> +#include <stdlib.h> + +int +main (void) +{ + void *h; + int (*fp) (int); + int res; + + h = dlopen ("${ORIGIN}/testobj1.so", RTLD_LAZY); + if (h == NULL) + error (EXIT_FAILURE, 0, "while loading `%s': %s", "testobj1.so", + dlerror ()); + + fp = dlsym (h, "obj1func1"); + if (fp == NULL) + error (EXIT_FAILURE, 0, "getting `obj1func1' in `%s': %s", + "testobj1.so", dlerror ()); + + res = fp (10); + printf ("fp(10) = %d\n", res); + + if (dlclose (h) != 0) + error (EXIT_FAILURE, 0, "while close `%s': %s", + "testobj1.so", dlerror ()); + + return res != 42; +} + + +int +foo (int a) +{ + return a + 10; +} |