aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog30
-rw-r--r--elf/Makefile2
-rw-r--r--elf/dl-close.c5
-rw-r--r--elf/dl-load.c153
-rw-r--r--elf/dl-object.c64
-rw-r--r--elf/dl-support.c3
-rw-r--r--elf/link.h3
-rw-r--r--elf/rtld.c7
-rw-r--r--linuxthreads/sysdeps/i386/useldt.h8
-rw-r--r--sysdeps/generic/dl-origin.h47
-rw-r--r--sysdeps/mach/hurd/Makefile2
-rw-r--r--sysdeps/unix/sysv/linux/dl-origin.h67
12 files changed, 374 insertions, 17 deletions
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 <drepper@cygnus.com>
+
+ * 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 <drepper@cygnus.com>
* 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 <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)
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 <drepper@cygnus.com>, 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 <drepper@cygnus.com>, 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;
+}