Update.
authorUlrich Drepper <drepper@redhat.com>
Thu, 27 Aug 1998 20:08:32 +0000 (20:08 +0000)
committerUlrich Drepper <drepper@redhat.com>
Thu, 27 Aug 1998 20:08:32 +0000 (20:08 +0000)
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.

12 files changed:
ChangeLog
elf/Makefile
elf/dl-close.c
elf/dl-load.c
elf/dl-object.c
elf/dl-support.c
elf/link.h
elf/rtld.c
linuxthreads/sysdeps/i386/useldt.h
sysdeps/generic/dl-origin.h [new file with mode: 0644]
sysdeps/mach/hurd/Makefile
sysdeps/unix/sysv/linux/dl-origin.h [new file with mode: 0644]

index 9e1d523..3605250 100644 (file)
--- 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
index d452ee7..4fcb997 100644 (file)
@@ -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)
index 8e613eb..0d4b176 100644 (file)
@@ -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)
index b14e52f..8e6a853 100644 (file)
@@ -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);
        }
     }
 
index ed4b059..f2ef816 100644 (file)
@@ -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;
 }
index 418088a..9b94907 100644 (file)
@@ -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));
 
index 30efa0e..40f7435 100644 (file)
@@ -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 */
index 13c4f26..3b3dfbf 100644 (file)
@@ -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)
index 2fdc0ce..53cf522 100644 (file)
@@ -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 (file)
index 0000000..6d5a8da
--- /dev/null
@@ -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;
+}
index afd8473..42e9a79 100644 (file)
@@ -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 (file)
index 0000000..eef0678
--- /dev/null
@@ -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;
+}