diff --git a/elf/Makefile b/elf/Makefile
index 25012cc..c130f21 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -43,7 +43,8 @@ shared-only-routines += dl-caller
# ld.so uses those routines, plus some special stuff for being the program
# interpreter and operating independent of libc.
-rtld-routines := rtld $(dl-routines) dl-sysdep dl-environ dl-minimal
+rtld-routines := rtld $(dl-routines) dl-sysdep dl-environ dl-minimal \
+ dl-realpath
all-rtld-routines = $(rtld-routines) $(sysdep-rtld-routines)
CFLAGS-dl-runtime.c = -fexceptions -fasynchronous-unwind-tables
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 016a99c..7353837 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -891,6 +891,7 @@ _dl_map_object_from_fd (const char *name, int fd, struct filebuf *fbp,
int errval = 0;
struct r_debug *r = _dl_debug_initialize (0, nsid);
bool make_consistent = false;
+ char *absolutename = NULL;
/* Get file information. */
if (__glibc_unlikely (__fxstat64 (_STAT_VER, fd, &st) < 0))
@@ -902,6 +903,23 @@ _dl_map_object_from_fd (const char *name, int fd, struct filebuf *fbp,
lose (errval, fd, name, realname, l, errstring,
make_consistent ? r : NULL, nsid);
}
+ /* Find absolute pathname for object */
+ absolutename = (char *) malloc (PATH_MAX);
+ if (!absolutename)
+ {
+ errstring = N_("cannot allocate memory for absolute path");
+ goto call_lose_errno;
+ }
+
+ if (!__realpath (realname, absolutename))
+ {
+ free (absolutename);
+ errstring = N_("cannot find absolute path of shared object");
+ goto call_lose_errno;
+ }
+
+ free (realname);
+ realname = absolutename;
/* Look again to see if the real name matched another already loaded. */
for (l = GL(dl_ns)[nsid]._ns_loaded; l; l = l->l_next)
diff --git a/elf/dl-object.c b/elf/dl-object.c
index afd80a6..d93935b 100644
--- a/elf/dl-object.c
+++ b/elf/dl-object.c
@@ -158,55 +158,15 @@ _dl_new_object (char *realname, const char *libname, int type,
char *origin;
char *cp;
- if (realname[0] == '/')
+ /* It is an absolute path, calculated by realpath. Use it.
+ * But we have to make a copy since we strip out the trailing slash. */
+ assert (realname[0] == '/');
+ cp = origin = (char *) malloc (realname_len);
+ if (origin == NULL)
{
- /* It is an absolute path. Use it. But we have to make a
- copy since we strip out the trailing slash. */
- cp = origin = (char *) malloc (realname_len);
- if (origin == NULL)
- {
- origin = (char *) -1;
- goto out;
- }
+ origin = (char *) -1;
+ goto out;
}
- else
- {
- size_t len = realname_len;
- char *result = NULL;
-
- /* Get the current directory name. */
- origin = NULL;
- do
- {
- char *new_origin;
-
- len += 128;
- new_origin = (char *) realloc (origin, len);
- if (new_origin == NULL)
- /* We exit the loop. Note that result == NULL. */
- break;
- origin = new_origin;
- }
- while ((result = __getcwd (origin, len - realname_len)) == NULL
- && errno == ERANGE);
-
- if (result == NULL)
- {
- /* We were not able to determine the current directory.
- Note that free(origin) is OK if origin == NULL. */
- free (origin);
- origin = (char *) -1;
- goto out;
- }
-
- /* Find the end of the path and see whether we have to add a
- slash. We could use rawmemchr but this need not be
- fast. */
- cp = (strchr) (origin, '\0');
- if (cp[-1] != '/')
- *cp++ = '/';
- }
-
/* Add the real file name. */
cp = __mempcpy (cp, realname, realname_len);
diff --git a/elf/dl-realpath.c b/elf/dl-realpath.c
new file mode 100644
index 0000000..2549bb3
--- /dev/null
+++ b/elf/dl-realpath.c
@@ -0,0 +1,85 @@
+/* Dynamic loader version of realpath.
+ Copyright (C) 2014 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 Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ . */
+
+#include
+#include
+#include
+#include
+
+/* Simplified implementation of realpath(): no dynamic memory use, no lstat(),
+ no set_errno(), no valid "rpath" on error, etc. This simplifies cases
+ involving relative paths, specifically where $ORIGIN needs to be
+ calculated. For this relatively rare case, one could also imagine using
+ link_map.l_origin to avoid the getcwd() here, but the simpler code
+ here seems like a better solution. */
+char * internal_function
+__realpath (const char *name, char *rpath)
+{
+ char *dest;
+ const char *start, *end;
+
+ if (name[0] != '/')
+ {
+ if (!__getcwd (rpath, PATH_MAX))
+ return NULL;
+ dest = __rawmemchr (rpath, '\0');
+ }
+ else
+ {
+ rpath[0] = '/';
+ dest = rpath + 1;
+ }
+
+ for (start = end = name; *start; start = end)
+ {
+ /* Skip sequence of multiple path-separators. */
+ while (*start == '/')
+ ++start;
+
+ /* Find end of path component. */
+ for (end = start; *end && *end != '/'; ++end)
+ /* Nothing. */;
+
+ if (end - start == 0)
+ break;
+ else if (end - start == 1 && start[0] == '.')
+ /* nothing */;
+ else if (end - start == 2 && start[0] == '.' && start[1] == '.')
+ {
+ /* Back up to previous component, ignore if at root already. */
+ if (dest > rpath + 1)
+ while ((--dest)[-1] != '/');
+ }
+ else
+ {
+ if (dest[-1] != '/')
+ *dest++ = '/';
+
+ if (dest + (end - start) >= rpath + PATH_MAX)
+ return NULL;
+
+ dest = __mempcpy (dest, start, end - start);
+ *dest = '\0';
+ }
+ }
+ if (dest > rpath + 1 && dest[-1] == '/')
+ --dest;
+ *dest = '\0';
+
+ return rpath;
+}
diff --git a/sysdeps/tile/dl-runtime.c b/sysdeps/tile/dl-runtime.c
index bcc00bc..45251d0 100644
--- a/sysdeps/tile/dl-runtime.c
+++ b/sysdeps/tile/dl-runtime.c
@@ -29,70 +29,6 @@
#include
#include
-/* Like realpath(), but simplified: no dynamic memory use, no lstat(),
- no set_errno(), no valid "rpath" on error, etc. This handles some
- simple cases where the simulator might not have a valid entry for
- a loaded Elf object, in particular dlopen() with a relative path.
- For this relatively rare case, one could also imagine using
- link_map.l_origin to avoid the getcwd() here, but the simpler code
- here seems like a better solution. */
-static char *
-dl_realpath (const char *name, char *rpath)
-{
- char *dest;
- const char *start, *end;
-
- if (name[0] != '/')
- {
- if (!__getcwd (rpath, PATH_MAX))
- return NULL;
- dest = __rawmemchr (rpath, '\0');
- }
- else
- {
- rpath[0] = '/';
- dest = rpath + 1;
- }
-
- for (start = end = name; *start; start = end)
- {
- /* Skip sequence of multiple path-separators. */
- while (*start == '/')
- ++start;
-
- /* Find end of path component. */
- for (end = start; *end && *end != '/'; ++end)
- /* Nothing. */;
-
- if (end - start == 0)
- break;
- else if (end - start == 1 && start[0] == '.')
- /* nothing */;
- else if (end - start == 2 && start[0] == '.' && start[1] == '.')
- {
- /* Back up to previous component, ignore if at root already. */
- if (dest > rpath + 1)
- while ((--dest)[-1] != '/');
- }
- else
- {
- if (dest[-1] != '/')
- *dest++ = '/';
-
- if (dest + (end - start) >= rpath + PATH_MAX)
- return NULL;
-
- dest = __mempcpy (dest, start, end - start);
- *dest = '\0';
- }
- }
- if (dest > rpath + 1 && dest[-1] == '/')
- --dest;
- *dest = '\0';
-
- return rpath;
-}
-
/* Support notifying the simulator about new objects. */
void internal_function
_dl_after_load (struct link_map *l)
@@ -117,7 +53,7 @@ _dl_after_load (struct link_map *l)
DLPUTC (':');
/* Write the library path, including the terminating '\0'. */
- path = dl_realpath (l->l_name, pathbuf) ?: l->l_name;
+ path = __realpath (l->l_name, pathbuf) ?: l->l_name;
for (size_t i = 0;; i++)
{
DLPUTC (path[i]);
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index 9ad6d22..4525429 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -176,7 +176,7 @@ endif
ifeq ($(subdir),elf)
sysdep-rtld-routines += dl-brk dl-sbrk dl-getcwd dl-openat64 dl-opendir \
- dl-fxstatat64
+ dl-fxstatat64 dl-lxstat64
CPPFLAGS-lddlibc4 += -DNOT_IN_libc
diff --git a/sysdeps/unix/sysv/linux/dl-lxstat64.c b/sysdeps/unix/sysv/linux/dl-lxstat64.c
new file mode 100644
index 0000000..63e6800
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/dl-lxstat64.c
@@ -0,0 +1 @@
+#include
diff --git a/sysdeps/unix/sysv/linux/dl-realpath.c b/sysdeps/unix/sysv/linux/dl-realpath.c
new file mode 100644
index 0000000..91652dc
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/dl-realpath.c
@@ -0,0 +1,121 @@
+/* Dynamic loader version of realpath for linux.
+ Copyright (C) 2014 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 Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ . */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* Simplified version of realpath which extends elf/dl-realpath.c to use
+ * linux syscalls for handling symlinks */
+char * internal_function
+__realpath (const char *name, char *rpath)
+{
+ char *dest;
+ char extra_buf[PATH_MAX];
+ const char *start, *end;
+ int num_links = 0;
+
+ if (name[0] != '/')
+ {
+ if (!__getcwd (rpath, PATH_MAX))
+ return NULL;
+ dest = __rawmemchr (rpath, '\0');
+ }
+ else
+ {
+ rpath[0] = '/';
+ dest = rpath + 1;
+ }
+
+ for (start = end = name; *start; start = end)
+ {
+ struct stat64 st;
+ int n;
+ /* Skip sequence of multiple path-separators. */
+ while (*start == '/')
+ ++start;
+
+ /* Find end of path component. */
+ for (end = start; *end && *end != '/'; ++end)
+ /* Nothing. */;
+
+ if (end - start == 0)
+ break;
+ else if (end - start == 1 && start[0] == '.')
+ /* nothing */;
+ else if (end - start == 2 && start[0] == '.' && start[1] == '.')
+ {
+ /* Back up to previous component, ignore if at root already. */
+ if (dest > rpath + 1)
+ while ((--dest)[-1] != '/');
+ }
+ else
+ {
+ if (dest[-1] != '/')
+ *dest++ = '/';
+
+ if (dest + (end - start) >= rpath + PATH_MAX)
+ return NULL;
+
+ dest = __mempcpy (dest, start, end - start);
+ *dest = '\0';
+ if (__lxstat64 (_STAT_VER, rpath, &st) < 0)
+ return NULL;
+
+ if (S_ISLNK (st.st_mode))
+ {
+ char buf[PATH_MAX];
+ size_t len;
+
+ INTERNAL_SYSCALL_DECL (err);
+
+ n = INTERNAL_SYSCALL (readlink, err, 3, rpath, buf,
+ PATH_MAX - 1);
+
+ if (n <= 0 || buf[0] == '[')
+ return NULL;
+ buf[n] = '\0';
+
+ len = strlen (end);
+ if ((long int) (n + len) >= PATH_MAX)
+ return NULL;
+
+ /* Careful here, end may be a pointer into extra_buf... */
+ memmove (&extra_buf[n], end, len + 1);
+ name = end = memcpy (extra_buf, buf, n);
+
+ if (buf[0] == '/')
+ dest = rpath + 1; /* Absolute symlink */
+ else
+ /* Back up to previous component, ignore if at root already: */
+ if (dest > rpath + 1)
+ while ((--dest)[-1] != '/');
+ }
+ }
+ }
+ if (dest > rpath + 1 && dest[-1] == '/')
+ --dest;
+ *dest = '\0';
+
+ return rpath;
+
+}