diff options
Diffstat (limited to 'elf')
-rw-r--r-- | elf/Makefile | 2 | ||||
-rw-r--r-- | elf/dl-close.c | 5 | ||||
-rw-r--r-- | elf/dl-load.c | 153 | ||||
-rw-r--r-- | elf/dl-object.c | 64 | ||||
-rw-r--r-- | elf/dl-support.c | 3 | ||||
-rw-r--r-- | elf/link.h | 3 | ||||
-rw-r--r-- | elf/rtld.c | 7 |
7 files changed, 224 insertions, 13 deletions
diff --git a/elf/Makefile b/elf/Makefile index d452ee7a06..4fcb997b3e 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -38,7 +38,7 @@ rtld-routines := rtld $(dl-routines) dl-sysdep dl-environ dl-minimal distribute = $(rtld-routines:=.c) dynamic-link.h do-rel.h dl-machine.h \ dl-hash.h soinit.c sofini.c ldd.sh.in ldd.bash.in eval.c \ genrtldtbl.awk atomicity.h dl-procinfo.h ldsodefs.h \ - dl-librecon.h interp.c sln.c + dl-librecon.h interp.c sln.c dl-origin.h extra-libs = libdl extra-libs-others = $(extra-libs) diff --git a/elf/dl-close.c b/elf/dl-close.c index 8e613eb3eb..0d4b176b1e 100644 --- a/elf/dl-close.c +++ b/elf/dl-close.c @@ -88,6 +88,11 @@ _dl_close (struct link_map *map) for (i = 0; i < nsearchlist; ++i) --list[i]->l_opencount; + if (map->l_origin != NULL) + free ((char *) map->l_origin); + /* The name always is allocated. */ + free (map->l_name); + /* Check each element of the search list to see if all references to it are gone. */ for (i = 0; i < nsearchlist; ++i) diff --git a/elf/dl-load.c b/elf/dl-load.c index b14e52f101..8e6a8537a3 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -31,6 +31,8 @@ #include "dynamic-link.h" #include <stdio-common/_itoa.h> +#include <dl-origin.h> + /* On some systems, no flag bits are given to specify file mapping. */ #ifndef MAP_FILE @@ -124,6 +126,116 @@ 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) +{ + /* 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 *st, *sf; + size_t cnt = 0; + size_t origin_len; + size_t total; + char *result, *last_elem, *wp; + + st = s; + sf = strchr (s, '$'); + while (sf != NULL) + { + size_t len = 1; + + if (((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] == ':')) + ++cnt; + + st = sf + len; + sf = strchr (st, '$'); + } + + /* 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"). + + 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 ? 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; + + /* 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 == '$') + { + 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] == ':')) + { + if ((repl = len == 7 ? l->l_origin : _dl_platform) != NULL + && repl != (const char *) -1) + { + wp = __stpcpy (wp, repl); + s += 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; + } + } + else + /* No SDK we recognize. */ + *wp++ = *s++; + } + else if (*s == ':') + { + *wp++ = *s++; + last_elem = wp; + } + else + *wp++ = *s++; + } + while (*s != '\0'); + + *wp = '\0'; + + return result; +} + /* 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. @@ -286,9 +398,10 @@ fillin_rpath (char *rpath, struct r_search_path_elem **result, const char *sep, static struct r_search_path_elem ** internal_function -decompose_rpath (const char *rpath, size_t additional_room, const char *where) +decompose_rpath (const char *rpath, size_t additional_room, struct link_map *l) { /* Make a copy we can work with. */ + const char *where = l->l_name; char *copy; char *cp; struct r_search_path_elem **result; @@ -318,8 +431,13 @@ decompose_rpath (const char *rpath, size_t additional_room, const char *where) } } + /* Make a writable copy. At the same time expand possible dynamic + string tokens. */ + copy = expand_dynamic_string_token (l, rpath); + if (copy == NULL) + _dl_signal_error (ENOMEM, NULL, "cannot create RPATH copy"); + /* Count the number of necessary elements in the result array. */ - copy = local_strdup (rpath); nelems = 0; for (cp = copy; *cp != '\0'; ++cp) if (*cp == ':') @@ -429,7 +547,7 @@ _dl_init_paths (const char *llp) decompose_rpath ((const char *) (l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr + l->l_info[DT_RPATH]->d_un.d_val), - nllp, l->l_name); + nllp, l); } else { @@ -497,6 +615,9 @@ _dl_init_paths (const char *llp) /* Map in the shared object NAME, actually located in REALNAME, and already opened on FD. */ +#ifndef EXTERNAL_MAP_FROM_FD +static +#endif struct link_map * _dl_map_object_from_fd (char *name, int fd, char *realname, struct link_map *loader, int l_type) @@ -591,6 +712,12 @@ _dl_map_object_from_fd (char *name, int fd, char *realname, LOSE ("ELF file data encoding not " byteorder_name); if (header->e_ident[EI_VERSION] != EV_CURRENT) LOSE ("ELF file version ident not " STRING(EV_CURRENT)); + /* XXX We should be able so set system specific versions which are + allowed here. */ + if (header->e_ident[EI_OSABI] != ELFOSABI_SYSV) + LOSE ("ELF file OS ABI not " STRING(ELFOSABI_SYSV)); + if (header->e_ident[EI_ABIVERSION] != 0) + LOSE ("ELF file ABI version not 0"); if (header->e_version != EV_CURRENT) LOSE ("ELF file version not " STRING(EV_CURRENT)); if (! elf_machine_matches_host (header->e_machine)) @@ -1076,7 +1203,7 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, + l->l_info[DT_STRTAB]->d_un.d_ptr + l->l_info[DT_RPATH]->d_un.d_val); l->l_rpath_dirs = - decompose_rpath ((const char *) ptrval, 0, l->l_name); + decompose_rpath ((const char *) ptrval, 0, l); } if (l->l_rpath_dirs != (struct r_search_path_elem **) -1l) @@ -1127,15 +1254,17 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, } else { - fd = __open (name, O_RDONLY); - if (fd != -1) + /* The path may contain dynamic string tokens. */ + realname = (loader + ? expand_dynamic_string_token (loader, name) + : local_strdup (name)); + if (realname == NULL) + fd = -1; + else { - realname = local_strdup (name); - if (realname == NULL) - { - __close (fd); - fd = -1; - } + fd = __open (realname, O_RDONLY); + if (fd == -1) + free (realname); } } diff --git a/elf/dl-object.c b/elf/dl-object.c index ed4b059754..f2ef8163ce 100644 --- a/elf/dl-object.c +++ b/elf/dl-object.c @@ -20,6 +20,7 @@ #include <errno.h> #include <string.h> #include <stdlib.h> +#include <unistd.h> #include <elf/ldsodefs.h> #include <assert.h> @@ -61,5 +62,68 @@ _dl_new_object (char *realname, const char *libname, int type) l->l_next = new; } + /* The REALNAME is "" for the main link map. This name must be determined + specially. */ + if (realname[0] == '\0') + new->l_origin = NULL; + else + { + char *origin; + + if (realname[0] == '/') + { + /* It an absolute path. Use it. But we have to make a copy since + we strip out the trailing slash. */ + size_t len = strlen (realname) + 1; + origin = malloc (len); + if (origin == NULL) + origin = (char *) -1; + else + memcpy (origin, realname, len); + } + else + { + size_t realname_len = strlen (realname) + 1; + size_t len = 128 + realname_len; + char *result = NULL; + + /* Get the current directory name. */ + origin = malloc (len); + + while (origin != NULL + && (result = __getcwd (origin, len - realname_len)) == NULL + && errno == ERANGE) + { + len += 128; + origin = (char *) realloc (origin, len); + } + + if (result == NULL) + { + /* We were not able to determine the current directory. */ + if (origin != NULL) + free (origin); + origin = (char *) -1; + } + else + { + /* Now append the filename. */ + char *cp = strchr (origin, '\0'); + + if (cp [-1] != '/') + *cp++ = '/'; + + memcpy (cp, realname, realname_len); + } + } + + if (origin != (char *) -1) + /* Now remove the filename and the slash. Do this even if the + string is something like "/foo" which leaves an empty string. */ + *strrchr (origin, '/') = '\0'; + + new->l_origin = origin; + } + return new; } diff --git a/elf/dl-support.c b/elf/dl-support.c index 418088acfa..9b94907940 100644 --- a/elf/dl-support.c +++ b/elf/dl-support.c @@ -63,6 +63,9 @@ struct link_map *_dl_profile_map; /* This is the address of the last stack address ever used. */ void *__libc_stack_end; +/* Path where the binary is found. */ +const char *_dl_origin_path; + static void non_dynamic_init (void) __attribute__ ((unused)); diff --git a/elf/link.h b/elf/link.h index 30efa0ed25..40f7435a84 100644 --- a/elf/link.h +++ b/elf/link.h @@ -161,6 +161,9 @@ struct link_map /* Pointer to the version information if available. */ ElfW(Half) *l_versyms; + + /* String specifying the path where this object was found. */ + const char *l_origin; }; #endif /* link.h */ diff --git a/elf/rtld.c b/elf/rtld.c index 13c4f2698e..3b3dfbf669 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -87,6 +87,7 @@ int _dl_debug_reloc; int _dl_debug_files; const char *_dl_inhibit_rpath; /* RPATH values which should be ignored. */ +const char *_dl_origin_path; /* Set nonzero during loading and initialization of executable and libraries, cleared before the executable's entry point runs. This @@ -1164,6 +1165,12 @@ process_envvars (enum mode *modep, int *lazyp) _dl_hwcap_mask = strtoul (&envline[14], NULL, 0); break; + case 11: + /* Path where the binary is found. */ + if (memcmp (&envline[3], "ORIGIN_PATH", 11) == 0) + _dl_hwcap_mask = strtoul (&envline[15], NULL, 0); + break; + case 12: /* Where to place the profiling data file. */ if (memcmp (&envline[3], "DEBUG_OUTPUT", 12) == 0) |