diff --git a/ChangeLog b/ChangeLog index 9e1d5235aa..3605250234 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,33 @@ +1998-08-27 19:42 Ulrich Drepper + + * elf/Makefile (distribute): Add dl-origin.h. + * sysdeps/generic/dl-origin.h: New file. + * sysdeps/unix/sysv/linux/dl-origin.h: New file. + * elf/link.h (struct link_map): Add l_origin field. + * elf/dl-load.c (expand_dynamic_string_token): New function. + (decompose_path): Remove WHERE argument, take link map pointer instead. + Call expand_dynamic_string_token instead of local_strdup to make copy + of rpath. + (_dl_init_paths): Call decompose_path with correct argument. + (_dl_map_object_from_fd): Define static is EXTERNAL_MAP_FROM_FD is + not defined. + Check EI_OSABI and EI_ABIVERSION fields in header. + (_dl_map_object): Call decompose_path with correct argument. + Call expand_dynamic_string_token instead of local_strdup to also + expand DST. + * elf/dl-object.c (_dl_new_object): Determine l_origin for all maps + but the main one. + * elf/dl-support.c: Define _dl_origin_path. + * elf/rtld.c: Likewise. Set _dl_origin_path based on LD_ORIGIN_PATH. + + * elf/dl-close (_dl_close): Free l_name and l_origin. + + * sysdeps/i386/useldt.h (THREAD_GETMEM, THREAD_SETMEM): Use P + modifier in asm, not c. + + * sysdeps/mach/hurd/Makefile [subdirs==elf]: Define CFLAGS-dl-load.c + to -DEXTERNAL_MAP_FROM_FD to make _dl_map_object_from_fd extern. + 1998-08-26 17:48 Ulrich Drepper * elf/dl-close.c (_dl_close): Move map->l_nsearchlist value into local 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 +#include + /* 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 #include #include +#include #include #include @@ -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) diff --git a/linuxthreads/sysdeps/i386/useldt.h b/linuxthreads/sysdeps/i386/useldt.h index 2fdc0ce767..53cf522951 100644 --- a/linuxthreads/sysdeps/i386/useldt.h +++ b/linuxthreads/sysdeps/i386/useldt.h @@ -82,7 +82,7 @@ extern int __modify_ldt (int, struct modify_ldt_ldt_s *, size_t); ({ \ __typeof__ (descr->member) __value; \ if (sizeof (__value) == 1) \ - __asm__ __volatile__ ("movb %%gs:%c1,%b0" \ + __asm__ __volatile__ ("movb %%gs:%P1,%b0" \ : "=r" (__value) \ : "0" (0), \ "i" (offsetof (struct _pthread_descr_struct, \ @@ -93,7 +93,7 @@ extern int __modify_ldt (int, struct modify_ldt_ldt_s *, size_t); /* There should not be any value with a size other than 1 or 4. */ \ abort (); \ \ - __asm__ __volatile__ ("movl %%gs:%c1,%0" \ + __asm__ __volatile__ ("movl %%gs:%P1,%0" \ : "=r" (__value) \ : "i" (offsetof (struct _pthread_descr_struct, \ member))); \ @@ -106,7 +106,7 @@ extern int __modify_ldt (int, struct modify_ldt_ldt_s *, size_t); ({ \ __typeof__ (descr->member) __value = (value); \ if (sizeof (__value) == 1) \ - __asm__ __volatile__ ("movb %0,%%gs:%c1" : \ + __asm__ __volatile__ ("movb %0,%%gs:%P1" : \ : "r" (__value), \ "i" (offsetof (struct _pthread_descr_struct, \ member))); \ @@ -116,7 +116,7 @@ extern int __modify_ldt (int, struct modify_ldt_ldt_s *, size_t); /* There should not be any value with a size other than 1 or 4. */ \ abort (); \ \ - __asm__ __volatile__ ("movl %0,%%gs:%c1" : \ + __asm__ __volatile__ ("movl %0,%%gs:%P1" : \ : "r" (__value), \ "i" (offsetof (struct _pthread_descr_struct, \ member))); \ diff --git a/sysdeps/generic/dl-origin.h b/sysdeps/generic/dl-origin.h new file mode 100644 index 0000000000..6d5a8da52e --- /dev/null +++ b/sysdeps/generic/dl-origin.h @@ -0,0 +1,47 @@ +/* Find path of executable. + Copyright (C) 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper , 1998. + + 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. */ + +/* Generally it is not possible to implement this. We have to fall + back on a solution where the user provides the information. */ +extern const char *_dl_origin_path; + +static inline const char * +get_origin (void) +{ + char *result = (char *) -1; + /* We use te environment variable LD_ORIGIN_PATH. If it is set make + a copy and strip out trailing slashes. */ + if (_dl_origin_path != NULL) + { + size_t len = strlen (_dl_origin_path); + result = malloc (len + 1); + if (result == NULL) + result = (char *) -1; + else + { + char *cp = __mempcpy (result, _dl_origin_path, len); + while (cp > result && cp[-1] == '/') + --cp; + *cp = '\0'; + } + } + + return result; +} diff --git a/sysdeps/mach/hurd/Makefile b/sysdeps/mach/hurd/Makefile index afd8473540..42e9a79e7b 100644 --- a/sysdeps/mach/hurd/Makefile +++ b/sysdeps/mach/hurd/Makefile @@ -124,6 +124,8 @@ $(inst_libdir)/libc.so: $(rpcuserlibs) # objects directly into the shared object. ifeq (elf,$(subdir)) $(objpfx)librtld.os: $(rpcuserlibs:.so=_pic.a) + +CFLAGS-dl-load.c = -DEXTERNAL_MAP_FROM_FD endif # We need these libs to link static programs in the libc source tree, too. diff --git a/sysdeps/unix/sysv/linux/dl-origin.h b/sysdeps/unix/sysv/linux/dl-origin.h new file mode 100644 index 0000000000..eef067814a --- /dev/null +++ b/sysdeps/unix/sysv/linux/dl-origin.h @@ -0,0 +1,67 @@ +/* Find path of executable. + Copyright (C) 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper , 1998. + + 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. */ + +/* On Linux >= 2.1 systems which have the dcache implementation we can get + the path of the application from the /proc/self/exe symlink. Try this + first and fall back on the generic method if necessary. */ +extern const char *_dl_origin_path; + +static inline const char * +get_origin (void) +{ + char linkval[PATH_MAX]; + char *result; + + if (readlink ("/proc/self/exe", linkval, PATH_MAX) != -1 + && result[0] != '[') + { + /* We can use this value. */ + char *last_slash = strrchr (linkval, '/'); + result = (char *) malloc (linkval - last_slash + 1); + if (result == NULL) + result = (char *) -1; + else + *((char *) __mempcpy (result, linkval, linkval - last_slash)) = '\0'; + } + else + { + size_t len = 0; + + result = (char *) -1; + /* We use te environment variable LD_ORIGIN_PATH. If it is set make + a copy and strip out trailing slashes. */ + if (_dl_origin_path != NULL) + { + size_t len = strlen (_dl_origin_path); + result = malloc (len + 1); + if (result == NULL) + result = (char *) -1; + else + { + char *cp = __mempcpy (result, _dl_origin_path, len); + while (cp > result && cp[-1] == '/') + --cp; + *cp = '\0'; + } + } + } + + return result; +}