@@ -455,6 +455,7 @@ tests += \
tst-noload \
tst-non-directory-path \
tst-null-argv \
+ tst-origin \
tst-p_align1 \
tst-p_align2 \
tst-p_align3 \
@@ -762,6 +763,7 @@ modules-names += \
libmarkermod5-3 \
libmarkermod5-4 \
libmarkermod5-5 \
+ liborigin-mod \
libtracemod1-1 \
libtracemod2-1 \
libtracemod3-1 \
@@ -3433,3 +3435,23 @@ LDFLAGS-tst-version-hash-zero-linkmod.so = \
$(objpfx)tst-version-hash-zero-refmod.so: \
$(objpfx)tst-version-hash-zero-linkmod.so
tst-version-hash-zero-refmod.so-no-z-defs = yes
+
+$(objpfx)tst-origin: $(objpfx)tst-origin.o $(objpfx)liborigin-mod.so
+ $(LINK.o) -o $@ -B$(csu-objpfx) $(LDFLAGS.so) $< \
+ -Wl,-rpath,\$$ORIGIN \
+ -L$(subst :, -L,$(rpath-link)) -Wl,--no-as-needed -lorigin-mod
+$(objpfx)liborigin-mod.so: $(objpfx)liborigin-mod.os
+ $(LINK.o) -shared -o $@ -B$(csu-objpfx) $(LDFLAGS.so) \
+ $(LDFLAGS-soname-fname) \
+ $<
+$(objpfx)tst-origin.out: tst-origin.sh $(objpfx)tst-origin
+ $(SHELL) \
+ $< \
+ '$(common-objpfx)' \
+ '$(test-wrapper-env)' \
+ '$(run-program-env)' \
+ '$(rpath-link)' \
+ tst-origin \
+ liborigin-mod.so \
+ > $@; \
+ $(evaluate-test)
@@ -965,6 +965,12 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
{
assert (nsid == LM_ID_BASE);
memset (&id, 0, sizeof (id));
+ char *realname_can = _dl_canonicalize (fd);
+ if (realname_can != NULL)
+ {
+ free (realname);
+ realname = realname_can;
+ }
}
else
{
@@ -47,3 +47,9 @@ _dl_get_origin (void)
return result;
}
+
+char *
+_dl_canonicalize (int fd)
+{
+ return NULL;
+}
new file mode 100644
@@ -0,0 +1 @@
+void foo (void) {}
new file mode 100644
@@ -0,0 +1,26 @@
+/* Test if $ORIGIN works correctly with symlinks (BZ 25263)
+ Copyright (C) 2025 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
+ <https://www.gnu.org/licenses/>. */
+
+extern void foo (void);
+
+int
+main (int argc, char *argv[])
+{
+ foo ();
+ return 0;
+}
new file mode 100755
@@ -0,0 +1,60 @@
+#!/bin/sh
+# Test if $ORIGIN works correctly with symlinks (BZ 25263)
+# Copyright (C) 2025 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
+# <https://www.gnu.org/licenses/>.
+
+set -e
+
+objpfx=$1
+test_wrapper_env=$2
+run_program_env=$3
+library_path=$4
+test_program=$5
+test_library=$6
+
+cleanup()
+{
+ # Move the binary and library back to build directory
+ mv $tmpdir/sub/$test_program ${objpfx}elf
+ mv $tmpdir/sub/$test_library ${objpfx}elf
+
+ rm -rf $tmpdir
+}
+
+tmpdir=$(mktemp -d "${objpfx}elf/tst-origin.XXXXXXXXXX")
+#trap cleanup 0
+
+mkdir ${tmpdir}/sub
+
+# Remove the dependency from $library_path
+mv ${objpfx}elf/$test_program $tmpdir/sub
+mv ${objpfx}elf/$test_library $tmpdir/sub
+
+cd ${tmpdir}
+ln -s sub/$test_program $test_program
+
+${test_wrapper_env} \
+${run_program_env} \
+${objpfx}elf/ld.so --library-path "$library_path" \
+ ./$test_program 2>&1 && rc=0 || rc=$?
+
+# Also check if ldd resolves the dependency
+LD_TRACE_LOADED_OBJECTS=1 \
+${objpfx}elf/ld.so --library-path "$library_path" \
+ ./$test_program 2>&1 | grep 'not found' && rc=1 || rc=0
+
+exit $rc
@@ -1223,6 +1223,10 @@ extern struct link_map * _dl_get_dl_main_map (void) attribute_hidden;
/* Find origin of the executable. */
extern const char *_dl_get_origin (void) attribute_hidden;
+/* Return the canonalized path name from the opened file descriptor FD,
+ or NULL otherwise. */
+extern char * _dl_canonicalize (int fd) attribute_hidden;
+
/* Count DSTs. */
extern size_t _dl_dst_count (const char *name) attribute_hidden;
@@ -300,6 +300,8 @@ ifeq ($(subdir),elf)
check-execstack-xfail += ld.so libc.so libpthread.so
# We always create a thread for signals
test-xfail-tst-single_threaded-pthread-static = yes
+# Bug 25263
+test-xfail-tst-origin = yes
CFLAGS-tst-execstack.c += -DDEFAULT_RWX_STACK=1
endif
@@ -21,6 +21,7 @@
#include <fcntl.h>
#include <ldsodefs.h>
#include <sysdep.h>
+#include <fd_to_filename.h>
/* 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
@@ -72,3 +73,25 @@ _dl_get_origin (void)
return result;
}
+
+/* On Linux, readlink on the magic symlinks in /proc/self/fd also has
+ the same behavior of returning the canonical path from the dcache.
+ If it does not work, we do not bother to canonicalize. */
+
+char *
+_dl_canonicalize (int fd)
+{
+ struct fd_to_filename fdfilename;
+ char canonical[PATH_MAX];
+ char *path = __fd_to_filename (fd, &fdfilename);
+ int size = INTERNAL_SYSCALL_CALL (readlinkat, AT_FDCWD, path,
+ canonical, PATH_MAX - 1);
+
+ /* Check if the path was truncated. */
+ if (size >= 0 && size < PATH_MAX - 1)
+ {
+ canonical[size] = '\0';
+ return __strdup (canonical);
+ }
+ return NULL;
+}