Message ID | 20250311201720.2794859-1-adhemerval.zanella@linaro.org |
---|---|
State | Accepted |
Commit | 9b646f5dc933dfa019f2ed7f80b6198b43e31f62 |
Headers | show |
Series | [v3] elf: Canonicalize $ORIGIN in an explicit ld.so invocation [BZ 25263] | expand |
On Tue, Mar 11, 2025, at 4:17 PM, Adhemerval Zanella wrote: > When an executable is invoked directly, we calculate $ORIGIN by calling > readlink on /proc/self/exe, which the Linux kernel resolves to the > target of any symlinks. However, if an executable is run through ld.so, > we cannot use /proc/self/exe and instead use the path given as an > argument. This leads to a different calculation of $ORIGIN, which is > most notable in that it causes ldd to behave differently (e.g., by not > finding a library) from directly running the program. > > To make the behavior consistent, take advantage of the fact that the > kernel also resolves /proc/self/fd/ symlinks to the target of any > symlinks in the same manner, so once we have opened the main executable > in order to load it, replace the user-provided path with the result of > calling readlink("/proc/self/fd/N"). > > (On non-Linux platforms this resolution does not happen and so no > behavior change is needed.) > > The __fd_to_filename usage on loader (through dl-origin.c) pulls > _itoa.c, which in turn defines a lot of > > The __fd_to_filename requires _fitoa_word and _itoa_word, which for duplicate paragraph > 32-bits pulls a lot of definitions from _itoa.c (due _ITOA_NEEDED > debing defined). To simplify the build move the required function "due to _ITOA_NEEDED being defined" > to a new file, _fitoa_word.c. > > Checked on x86_64-linux-gnu and i686-linux-gnu. I have also just tested this version on aarch64-linux-gnu and it works. Reviewed-by: Geoffrey Thomas <geofft@ldpreload.com> (and Tested-by and Signed-off-by if you need them) Thanks again for your help in getting this through! > > Co-authored-by: Geoffrey Thomas <geofft@ldpreload.com> > --- > elf/Makefile | 23 +++++++++++ > elf/dl-load.c | 6 +++ > elf/dl-origin.c | 6 +++ > elf/liborigin-mod.c | 1 + > elf/tst-origin.c | 26 +++++++++++++ > elf/tst-origin.sh | 60 +++++++++++++++++++++++++++++ > stdio-common/Makefile | 3 ++ > stdio-common/_fitoa_word.c | 60 +++++++++++++++++++++++++++++ > stdio-common/_itoa.c | 43 --------------------- > sysdeps/generic/_itoa.h | 31 --------------- > sysdeps/generic/ldsodefs.h | 4 ++ > sysdeps/mach/hurd/Makefile | 2 + > sysdeps/unix/sysv/linux/dl-origin.c | 23 +++++++++++ > 13 files changed, 214 insertions(+), 74 deletions(-) > create mode 100644 elf/liborigin-mod.c > create mode 100644 elf/tst-origin.c > create mode 100755 elf/tst-origin.sh > create mode 100644 stdio-common/_fitoa_word.c > > diff --git a/elf/Makefile b/elf/Makefile > index 77a76f2142..8055d9ffbb 100644 > --- a/elf/Makefile > +++ b/elf/Makefile > @@ -456,6 +456,7 @@ tests += \ > tst-noload \ > tst-non-directory-path \ > tst-null-argv \ > + tst-origin \ > tst-p_align1 \ > tst-p_align2 \ > tst-p_align3 \ > @@ -763,6 +764,7 @@ modules-names += \ > libmarkermod5-3 \ > libmarkermod5-4 \ > libmarkermod5-5 \ > + liborigin-mod \ > libtracemod1-1 \ > libtracemod2-1 \ > libtracemod3-1 \ > @@ -3442,3 +3444,24 @@ $(objpfx)tst-dlopen-constructor-null: \ > $(objpfx)tst-dlopen-constructor-null-mod2.so > $(objpfx)tst-dlopen-constructor-null-mod2.so: \ > $(objpfx)tst-dlopen-constructor-null-mod1.so > + > +CFLAGS-tst-origin.c += $(no-stack-protector) > +$(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) > diff --git a/elf/dl-load.c b/elf/dl-load.c > index 4998652adf..6b7e9799f3 100644 > --- a/elf/dl-load.c > +++ b/elf/dl-load.c > @@ -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 > { > diff --git a/elf/dl-origin.c b/elf/dl-origin.c > index 9f6b921b01..812f5dbb28 100644 > --- a/elf/dl-origin.c > +++ b/elf/dl-origin.c > @@ -47,3 +47,9 @@ _dl_get_origin (void) > > return result; > } > + > +char * > +_dl_canonicalize (int fd) > +{ > + return NULL; > +} > diff --git a/elf/liborigin-mod.c b/elf/liborigin-mod.c > new file mode 100644 > index 0000000000..aa6d4c27df > --- /dev/null > +++ b/elf/liborigin-mod.c > @@ -0,0 +1 @@ > +void foo (void) {} > diff --git a/elf/tst-origin.c b/elf/tst-origin.c > new file mode 100644 > index 0000000000..734b2e81f6 > --- /dev/null > +++ b/elf/tst-origin.c > @@ -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; > +} > diff --git a/elf/tst-origin.sh b/elf/tst-origin.sh > new file mode 100755 > index 0000000000..2555d3ed9e > --- /dev/null > +++ b/elf/tst-origin.sh > @@ -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 > diff --git a/stdio-common/Makefile b/stdio-common/Makefile > index b68e9223c6..d3733d0c3d 100644 > --- a/stdio-common/Makefile > +++ b/stdio-common/Makefile > @@ -59,6 +59,7 @@ headers := \ > # headers > > routines := \ > + _fitoa_word \ > _itoa \ > _itowa \ > asprintf \ > @@ -663,6 +664,8 @@ CFLAGS-dprintf.c += $(config-cflags-wno-ignored-attributes) > # off for non-shared builds. > CFLAGS-_itoa.o = $(no-stack-protector) > CFLAGS-_itoa.op = $(no-stack-protector) > +CFLAGS-_fitoa_word.o = $(no-stack-protector) > +CFLAGS-_fitoa_word.op = $(no-stack-protector) > > CFLAGS-scanf13.c += $(test-config-cflags-wno-fortify-source) > > diff --git a/stdio-common/_fitoa_word.c b/stdio-common/_fitoa_word.c > new file mode 100644 > index 0000000000..f0b2707173 > --- /dev/null > +++ b/stdio-common/_fitoa_word.c > @@ -0,0 +1,60 @@ > +/* Internal function for converting integers to ASCII. > + Copyright (C) 1994-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/>. */ > + > +#include <_itoa.h> > + > +char * > +_itoa_word (_ITOA_WORD_TYPE value, char *buflim, > + unsigned int base, int upper_case) > +{ > + const char *digits = (upper_case > + ? _itoa_upper_digits > + : _itoa_lower_digits); > + > + switch (base) > + { > +#define SPECIAL(Base) \ > + case Base: \ > + do \ > + *--buflim = digits[value % Base]; \ > + while ((value /= Base) != 0); \ > + break > + > + SPECIAL (10); > + SPECIAL (16); > + SPECIAL (8); > + default: > + do > + *--buflim = digits[value % base]; > + while ((value /= base) != 0); > + } > + return buflim; > +} > +#undef SPECIAL > + > +char * > +_fitoa_word (_ITOA_WORD_TYPE value, char *buf, unsigned int base, > + int upper_case) > +{ > + char tmpbuf[sizeof (value) * 4]; /* Worst case length: base 2. > */ > + char *cp = _itoa_word (value, tmpbuf + sizeof (value) * 4, base, > upper_case); > + while (cp < tmpbuf + sizeof (value) * 4) > + *buf++ = *cp++; > + return buf; > +} > + > diff --git a/stdio-common/_itoa.c b/stdio-common/_itoa.c > index 51c3ab9c14..08859f0dd0 100644 > --- a/stdio-common/_itoa.c > +++ b/stdio-common/_itoa.c > @@ -162,38 +162,6 @@ const struct base_table_t _itoa_base_table[] > attribute_hidden = > }; > #endif > > -#if IS_IN (libc) > -char * > -_itoa_word (_ITOA_WORD_TYPE value, char *buflim, > - unsigned int base, int upper_case) > -{ > - const char *digits = (upper_case > - ? _itoa_upper_digits > - : _itoa_lower_digits); > - > - switch (base) > - { > -#define SPECIAL(Base) \ > - case Base: \ > - do \ > - *--buflim = digits[value % Base]; \ > - while ((value /= Base) != 0); \ > - break > - > - SPECIAL (10); > - SPECIAL (16); > - SPECIAL (8); > - default: > - do > - *--buflim = digits[value % base]; > - while ((value /= base) != 0); > - } > - return buflim; > -} > -#undef SPECIAL > -#endif /* IS_IN (libc) */ > - > - > #if _ITOA_NEEDED > char * > _itoa (unsigned long long int value, char *buflim, unsigned int base, > @@ -460,17 +428,6 @@ _itoa (unsigned long long int value, char *buflim, > unsigned int base, > } > #endif > > -char * > -_fitoa_word (_ITOA_WORD_TYPE value, char *buf, unsigned int base, > - int upper_case) > -{ > - char tmpbuf[sizeof (value) * 4]; /* Worst case length: base 2. */ > - char *cp = _itoa_word (value, tmpbuf + sizeof (value) * 4, base, upper_case); > - while (cp < tmpbuf + sizeof (value) * 4) > - *buf++ = *cp++; > - return buf; > -} > - > #if _ITOA_NEEDED > char * > _fitoa (unsigned long long value, char *buf, unsigned int base, int upper_case) > diff --git a/sysdeps/generic/_itoa.h b/sysdeps/generic/_itoa.h > index d7e3007389..2f170d3bf2 100644 > --- a/sysdeps/generic/_itoa.h > +++ b/sysdeps/generic/_itoa.h > @@ -51,40 +51,9 @@ hidden_proto (_itoa_upper_digits) > hidden_proto (_itoa_lower_digits) > #endif > > -#if IS_IN (libc) > extern char *_itoa_word (_ITOA_WORD_TYPE value, char *buflim, > unsigned int base, > int upper_case) attribute_hidden; > -#else > -static inline char * __attribute__ ((unused, always_inline)) > -_itoa_word (_ITOA_WORD_TYPE value, char *buflim, > - unsigned int base, int upper_case) > -{ > - const char *digits = (upper_case > - ? _itoa_upper_digits > - : _itoa_lower_digits); > - > - switch (base) > - { > -# define SPECIAL(Base) \ > - case Base: \ > - do \ > - *--buflim = digits[value % Base]; \ > - while ((value /= Base) != 0); \ > - break > - > - SPECIAL (10); > - SPECIAL (16); > - SPECIAL (8); > - default: > - do > - *--buflim = digits[value % base]; > - while ((value /= base) != 0); > - } > - return buflim; > -} > -# undef SPECIAL > -#endif > > /* Similar to the _itoa functions, but output starts at buf and pointer > after the last written character is returned. */ > diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h > index 8465cbaa9b..19494b82ee 100644 > --- a/sysdeps/generic/ldsodefs.h > +++ b/sysdeps/generic/ldsodefs.h > @@ -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; > > diff --git a/sysdeps/mach/hurd/Makefile b/sysdeps/mach/hurd/Makefile > index 13e5cea4c2..4b69b40065 100644 > --- a/sysdeps/mach/hurd/Makefile > +++ b/sysdeps/mach/hurd/Makefile > @@ -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 > diff --git a/sysdeps/unix/sysv/linux/dl-origin.c > b/sysdeps/unix/sysv/linux/dl-origin.c > index decdd8ae9e..3c52ba51a6 100644 > --- a/sysdeps/unix/sysv/linux/dl-origin.c > +++ b/sysdeps/unix/sysv/linux/dl-origin.c > @@ -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; > +} > -- > 2.43.0
* Adhemerval Zanella: > When an executable is invoked directly, we calculate $ORIGIN by calling > readlink on /proc/self/exe, which the Linux kernel resolves to the > target of any symlinks. However, if an executable is run through ld.so, > we cannot use /proc/self/exe and instead use the path given as an > argument. This leads to a different calculation of $ORIGIN, which is > most notable in that it causes ldd to behave differently (e.g., by not > finding a library) from directly running the program. > > To make the behavior consistent, take advantage of the fact that the > kernel also resolves /proc/self/fd/ symlinks to the target of any > symlinks in the same manner, so once we have opened the main executable > in order to load it, replace the user-provided path with the result of > calling readlink("/proc/self/fd/N"). > > (On non-Linux platforms this resolution does not happen and so no > behavior change is needed.) > > The __fd_to_filename usage on loader (through dl-origin.c) pulls > _itoa.c, which in turn defines a lot of > > The __fd_to_filename requires _fitoa_word and _itoa_word, which for > 32-bits pulls a lot of definitions from _itoa.c (due _ITOA_NEEDED > debing defined). To simplify the build move the required function > to a new file, _fitoa_word.c. > > Checked on x86_64-linux-gnu and i686-linux-gnu. > > Co-authored-by: Geoffrey Thomas <geofft@ldpreload.com> This or commit 997f49fbadf892136c77115edd537c832fb8074d Author: Adhemerval Zanella <adhemerval.zanella@linaro.org> Date: Mon Mar 17 19:08:22 2025 +0000 elf: Fix tst-origin make rules Add tests-special before include Rules and compile liborigin.os with MODULE_NAME set to testsuite instead of libc. caused: /usr/bin/ld: /home/fweimer/src/gnu/glibc/build/libc.so: undefined reference to `__tunable_is_initialized@GLIBC_PRIVATE' Failing the “make check” build. I think this happens because the linker invocation links against system ld.so. Is there a reason why we can't use the usual test DSO linker invocation? Thanks, Florian
On 18/03/25 08:34, Florian Weimer wrote: > * Adhemerval Zanella: > >> When an executable is invoked directly, we calculate $ORIGIN by calling >> readlink on /proc/self/exe, which the Linux kernel resolves to the >> target of any symlinks. However, if an executable is run through ld.so, >> we cannot use /proc/self/exe and instead use the path given as an >> argument. This leads to a different calculation of $ORIGIN, which is >> most notable in that it causes ldd to behave differently (e.g., by not >> finding a library) from directly running the program. >> >> To make the behavior consistent, take advantage of the fact that the >> kernel also resolves /proc/self/fd/ symlinks to the target of any >> symlinks in the same manner, so once we have opened the main executable >> in order to load it, replace the user-provided path with the result of >> calling readlink("/proc/self/fd/N"). >> >> (On non-Linux platforms this resolution does not happen and so no >> behavior change is needed.) >> >> The __fd_to_filename usage on loader (through dl-origin.c) pulls >> _itoa.c, which in turn defines a lot of >> >> The __fd_to_filename requires _fitoa_word and _itoa_word, which for >> 32-bits pulls a lot of definitions from _itoa.c (due _ITOA_NEEDED >> debing defined). To simplify the build move the required function >> to a new file, _fitoa_word.c. >> >> Checked on x86_64-linux-gnu and i686-linux-gnu. >> >> Co-authored-by: Geoffrey Thomas <geofft@ldpreload.com> > > This > > or > > commit 997f49fbadf892136c77115edd537c832fb8074d > Author: Adhemerval Zanella <adhemerval.zanella@linaro.org> > Date: Mon Mar 17 19:08:22 2025 +0000 > > elf: Fix tst-origin make rules > > Add tests-special before include Rules and compile liborigin.os with > MODULE_NAME set to testsuite instead of libc. > > caused: > > /usr/bin/ld: /home/fweimer/src/gnu/glibc/build/libc.so: undefined reference to `__tunable_is_initialized@GLIBC_PRIVATE' > > Failing the “make check” build. I think this happens because the linker > invocation links against system ld.so. > > Is there a reason why we can't use the usual test DSO linker invocation? The only requirement is to avoid a the liborigin.so without a full path, which is the default for --enable-hardcoded-path-in-tests. I also tried to mimic the original provided testcase and avoid the extra RPATH entries, it is not really required. I only tested on system with recent glibc, so I think that's why I have not see the this build issue. Does the following fix the build issue? diff --git a/elf/Makefile b/elf/Makefile index 5a50c7d50c..3d60000ec9 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -456,6 +456,7 @@ tests += \ tst-noload \ tst-non-directory-path \ tst-null-argv \ + tst-origin \ tst-p_align1 \ tst-p_align2 \ tst-p_align3 \ @@ -1195,7 +1196,6 @@ extra-test-objs += $(addsuffix .os,$(strip $(modules-names))) # filtmod1.so, tst-big-note-lib.so, tst-ro-dynamic-mod.so have special # rules. modules-names-nobuild += \ - liborigin-mod \ filtmod1 \ tst-audit24bmod1 \ tst-audit24bmod2 \ @@ -3451,15 +3451,8 @@ $(objpfx)tst-dlopen-constructor-null-mod2.so: \ CFLAGS-tst-origin.c += $(no-stack-protector) CFLAGS-liborigin-mod.c += $(no-stack-protector) -$(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 +LDFLAGS-tst-origin += -Wl,-rpath,\$$ORIGIN -L$(subst :, -L,$(rpath-link)) -lorigin-mod +$(objpfx)tst-origin.out: tst-origin.sh $(objpfx)liborigin-mod.so $(objpfx)tst-origin $(SHELL) \ $< \ '$(common-objpfx)' \
* Adhemerval Zanella Netto: > The only requirement is to avoid a the liborigin.so without a full path, > which is the default for --enable-hardcoded-path-in-tests. I also tried > to mimic the original provided testcase and avoid the extra RPATH entries, > it is not really required. > > I only tested on system with recent glibc, so I think that's why I have > not see the this build issue. Does the following fix the build issue? > > diff --git a/elf/Makefile b/elf/Makefile > index 5a50c7d50c..3d60000ec9 100644 > --- a/elf/Makefile > +++ b/elf/Makefile > @@ -456,6 +456,7 @@ tests += \ > tst-noload \ > tst-non-directory-path \ > tst-null-argv \ > + tst-origin \ > tst-p_align1 \ > tst-p_align2 \ > tst-p_align3 \ > @@ -1195,7 +1196,6 @@ extra-test-objs += $(addsuffix .os,$(strip $(modules-names))) > # filtmod1.so, tst-big-note-lib.so, tst-ro-dynamic-mod.so have special > # rules. > modules-names-nobuild += \ > - liborigin-mod \ > filtmod1 \ > tst-audit24bmod1 \ > tst-audit24bmod2 \ > @@ -3451,15 +3451,8 @@ $(objpfx)tst-dlopen-constructor-null-mod2.so: \ > > CFLAGS-tst-origin.c += $(no-stack-protector) > CFLAGS-liborigin-mod.c += $(no-stack-protector) > -$(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 > +LDFLAGS-tst-origin += -Wl,-rpath,\$$ORIGIN -L$(subst :, -L,$(rpath-link)) -lorigin-mod > +$(objpfx)tst-origin.out: tst-origin.sh $(objpfx)liborigin-mod.so $(objpfx)tst-origin > $(SHELL) \ > $< \ > '$(common-objpfx)' \ The patch did not apply to me. This is how I fixed it up: diff --git a/elf/Makefile b/elf/Makefile index 5a50c7d50c..3d60000ec9 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -456,6 +456,7 @@ tests += \ tst-noload \ tst-non-directory-path \ tst-null-argv \ + tst-origin \ tst-p_align1 \ tst-p_align2 \ tst-p_align3 \ @@ -1195,7 +1196,6 @@ extra-test-objs += $(addsuffix .os,$(strip $(modules-names))) # filtmod1.so, tst-big-note-lib.so, tst-ro-dynamic-mod.so have special # rules. modules-names-nobuild += \ - liborigin-mod \ filtmod1 \ tst-audit24bmod1 \ tst-audit24bmod2 \ @@ -3451,15 +3451,8 @@ $(objpfx)tst-dlopen-constructor-null-mod2.so: \ CFLAGS-tst-origin.c += $(no-stack-protector) CFLAGS-liborigin-mod.c += $(no-stack-protector) -$(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 +LDFLAGS-tst-origin += -Wl,-rpath,\$$ORIGIN -L$(subst :, -L,$(rpath-link)) -lorigin-mod +$(objpfx)tst-origin.out: tst-origin.sh $(objpfx)liborigin-mod.so $(objpfx)tst-origin $(SHELL) \ $< \ '$(common-objpfx)' \ This variant worked for me. Thanks, Florian
On 18/03/25 15:34, Florian Weimer wrote: > * Adhemerval Zanella Netto: > >> The only requirement is to avoid a the liborigin.so without a full path, >> which is the default for --enable-hardcoded-path-in-tests. I also tried >> to mimic the original provided testcase and avoid the extra RPATH entries, >> it is not really required. >> >> I only tested on system with recent glibc, so I think that's why I have >> not see the this build issue. Does the following fix the build issue? >> >> diff --git a/elf/Makefile b/elf/Makefile >> index 5a50c7d50c..3d60000ec9 100644 >> --- a/elf/Makefile >> +++ b/elf/Makefile >> @@ -456,6 +456,7 @@ tests += \ >> tst-noload \ >> tst-non-directory-path \ >> tst-null-argv \ >> + tst-origin \ >> tst-p_align1 \ >> tst-p_align2 \ >> tst-p_align3 \ >> @@ -1195,7 +1196,6 @@ extra-test-objs += $(addsuffix .os,$(strip $(modules-names))) >> # filtmod1.so, tst-big-note-lib.so, tst-ro-dynamic-mod.so have special >> # rules. >> modules-names-nobuild += \ >> - liborigin-mod \ >> filtmod1 \ >> tst-audit24bmod1 \ >> tst-audit24bmod2 \ >> @@ -3451,15 +3451,8 @@ $(objpfx)tst-dlopen-constructor-null-mod2.so: \ >> >> CFLAGS-tst-origin.c += $(no-stack-protector) >> CFLAGS-liborigin-mod.c += $(no-stack-protector) >> -$(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 >> +LDFLAGS-tst-origin += -Wl,-rpath,\$$ORIGIN -L$(subst :, -L,$(rpath-link)) -lorigin-mod >> +$(objpfx)tst-origin.out: tst-origin.sh $(objpfx)liborigin-mod.so $(objpfx)tst-origin >> $(SHELL) \ >> $< \ >> '$(common-objpfx)' \ > > The patch did not apply to me. This is how I fixed it up: > > diff --git a/elf/Makefile b/elf/Makefile > index 5a50c7d50c..3d60000ec9 100644 > --- a/elf/Makefile > +++ b/elf/Makefile > @@ -456,6 +456,7 @@ tests += \ > tst-noload \ > tst-non-directory-path \ > tst-null-argv \ > + tst-origin \ > tst-p_align1 \ > tst-p_align2 \ > tst-p_align3 \ > @@ -1195,7 +1196,6 @@ extra-test-objs += $(addsuffix .os,$(strip $(modules-names))) > # filtmod1.so, tst-big-note-lib.so, tst-ro-dynamic-mod.so have special > # rules. > modules-names-nobuild += \ > - liborigin-mod \ > filtmod1 \ > tst-audit24bmod1 \ > tst-audit24bmod2 \ > @@ -3451,15 +3451,8 @@ $(objpfx)tst-dlopen-constructor-null-mod2.so: \ > > CFLAGS-tst-origin.c += $(no-stack-protector) > CFLAGS-liborigin-mod.c += $(no-stack-protector) > -$(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 > +LDFLAGS-tst-origin += -Wl,-rpath,\$$ORIGIN -L$(subst :, -L,$(rpath-link)) -lorigin-mod > +$(objpfx)tst-origin.out: tst-origin.sh $(objpfx)liborigin-mod.so $(objpfx)tst-origin > $(SHELL) \ > $< \ > '$(common-objpfx)' \ > > This variant worked for me. Thanks, I will fix this upstream.
This still isn't working for me; the build fails with "cannot find -lorigin-mod". (Before 5291d9f1e274dd869bc0b3d044fd4cbae486893d it was also broken with a lint-makefiles failure.)
On 19/03/25 09:42, Joseph Myers wrote: > This still isn't working for me; the build fails with "cannot find > -lorigin-mod". (Before 5291d9f1e274dd869bc0b3d044fd4cbae486893d it was > also broken with a lint-makefiles failure.) > I forgot that rules dependencies are evaluated in no defined order. I am trying to come up a fix, the main issue is making the build with --enable-hardcoded-path-in-tests.
On 19/03/25 11:02, Adhemerval Zanella Netto wrote: > > > On 19/03/25 09:42, Joseph Myers wrote: >> This still isn't working for me; the build fails with "cannot find >> -lorigin-mod". (Before 5291d9f1e274dd869bc0b3d044fd4cbae486893d it was >> also broken with a lint-makefiles failure.) >> > > I forgot that rules dependencies are evaluated in no defined order. I am > trying to come up a fix, the main issue is making the build with > --enable-hardcoded-path-in-tests. I had to reinstate a custom rule to avoid the DT_NEEDED with a full path and also link tst-origin with built libc: diff --git a/elf/Makefile b/elf/Makefile index 3d60000ec9..fd73b68308 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -456,7 +456,6 @@ tests += \ tst-noload \ tst-non-directory-path \ tst-null-argv \ - tst-origin \ tst-p_align1 \ tst-p_align2 \ tst-p_align3 \ @@ -3451,8 +3450,15 @@ $(objpfx)tst-dlopen-constructor-null-mod2.so: \ CFLAGS-tst-origin.c += $(no-stack-protector) CFLAGS-liborigin-mod.c += $(no-stack-protector) -LDFLAGS-tst-origin += -Wl,-rpath,\$$ORIGIN -L$(subst :, -L,$(rpath-link)) -lorigin-mod -$(objpfx)tst-origin.out: tst-origin.sh $(objpfx)liborigin-mod.so $(objpfx)tst-origin +# The test requires liborigin-mod.so as dependency without a path, which is +# added by default. +$(objpfx)tst-origin: $(objpfx)tst-origin.o $(objpfx)liborigin-mod.so + $(LINK.o) $(sysdep-LDFLAGS) $(rtld-LDFLAGS) -B$(csu-objpfx) \ + $(link-test-modules-rpath-link) \ + $(objpfx)tst-origin.o \ + -o $@ \ + -Wl,-rpath,\$$ORIGIN -L$(subst :, -L,$(rpath-link)) -lorigin-mod +$(objpfx)tst-origin.out: tst-origin.sh $(objpfx)tst-origin $(SHELL) \ $< \ '$(common-objpfx)' \
* Adhemerval Zanella Netto: > On 19/03/25 11:02, Adhemerval Zanella Netto wrote: >> >> >> On 19/03/25 09:42, Joseph Myers wrote: >>> This still isn't working for me; the build fails with "cannot find >>> -lorigin-mod". (Before 5291d9f1e274dd869bc0b3d044fd4cbae486893d it was >>> also broken with a lint-makefiles failure.) >>> >> >> I forgot that rules dependencies are evaluated in no defined order. I am >> trying to come up a fix, the main issue is making the build with >> --enable-hardcoded-path-in-tests. > > I had to reinstate a custom rule to avoid the DT_NEEDED with a full path > and also link tst-origin with built libc: This looks like a job for .EXTRA_PREREQS, I think: <https://www.gnu.org/software/make/manual/html_node/Special-Variables.html#index-_002eEXTRA_005fPREREQS-_0028prerequisites-not-added-to-automatic-variables_0029> Unfortunately that was only added in make 4.3, which is quite recent. I think we could add this: # sofini.os must be placed last since it terminates .eh_frame section. build-module-helper-objlist = \ $(patsubst %_pic.a,$(whole-archive) %_pic.a $(no-whole-archive),\ $(filter-out %.lds $(map-file) $(+preinit) $(+postinit) \ + $(build-module-nolink) \ $(elf-objpfx)sofini.os \ $(link-libc-deps),$^)) and then use: LDFLAGS-tst-origin += -Wl,-rpath,\$$ORIGIN -L$(subst :, -L,$(rpath-link)) -lorigin-mod $(objpfx)tst-origin: $(objpfx)liborigin-mod.so $(objpfx)tst-origin: build-module-nolink = $(objpfx)liborigin-mod.so But I haven't tested it. Thanks, Florian
On 19/03/25 14:28, Florian Weimer wrote: > * Adhemerval Zanella Netto: > >> On 19/03/25 11:02, Adhemerval Zanella Netto wrote: >>> >>> >>> On 19/03/25 09:42, Joseph Myers wrote: >>>> This still isn't working for me; the build fails with "cannot find >>>> -lorigin-mod". (Before 5291d9f1e274dd869bc0b3d044fd4cbae486893d it was >>>> also broken with a lint-makefiles failure.) >>>> >>> >>> I forgot that rules dependencies are evaluated in no defined order. I am >>> trying to come up a fix, the main issue is making the build with >>> --enable-hardcoded-path-in-tests. >> >> I had to reinstate a custom rule to avoid the DT_NEEDED with a full path >> and also link tst-origin with built libc: > > This looks like a job for .EXTRA_PREREQS, I think: > > <https://www.gnu.org/software/make/manual/html_node/Special-Variables.html#index-_002eEXTRA_005fPREREQS-_0028prerequisites-not-added-to-automatic-variables_0029> > > Unfortunately that was only added in make 4.3, which is quite recent. > > I think we could add this: > > # sofini.os must be placed last since it terminates .eh_frame section. > build-module-helper-objlist = \ > $(patsubst %_pic.a,$(whole-archive) %_pic.a $(no-whole-archive),\ > $(filter-out %.lds $(map-file) $(+preinit) $(+postinit) \ > + $(build-module-nolink) \ > $(elf-objpfx)sofini.os \ > $(link-libc-deps),$^)) > > > and then use: > > LDFLAGS-tst-origin += -Wl,-rpath,\$$ORIGIN -L$(subst :, -L,$(rpath-link)) -lorigin-mod > $(objpfx)tst-origin: $(objpfx)liborigin-mod.so > $(objpfx)tst-origin: build-module-nolink = $(objpfx)liborigin-mod.so > > But I haven't tested it. It did not work, the $(objpfx)liborigin-mod.so still adds the full path DT_NEEDED. It works if I just use the build-module-nolink trick, but it does back to prerequisite order issue (even if I add $(objpfx)liborigin-mod.so in the same rule).
On 19/03/25 11:47, Adhemerval Zanella Netto wrote: > > > On 19/03/25 11:02, Adhemerval Zanella Netto wrote: >> >> >> On 19/03/25 09:42, Joseph Myers wrote: >>> This still isn't working for me; the build fails with "cannot find >>> -lorigin-mod". (Before 5291d9f1e274dd869bc0b3d044fd4cbae486893d it was >>> also broken with a lint-makefiles failure.) >>> >> >> I forgot that rules dependencies are evaluated in no defined order. I am >> trying to come up a fix, the main issue is making the build with >> --enable-hardcoded-path-in-tests. > > I had to reinstate a custom rule to avoid the DT_NEEDED with a full path > and also link tst-origin with built libc: > > diff --git a/elf/Makefile b/elf/Makefile > index 3d60000ec9..fd73b68308 100644 > --- a/elf/Makefile > +++ b/elf/Makefile > @@ -456,7 +456,6 @@ tests += \ > tst-noload \ > tst-non-directory-path \ > tst-null-argv \ > - tst-origin \ > tst-p_align1 \ > tst-p_align2 \ > tst-p_align3 \ > @@ -3451,8 +3450,15 @@ $(objpfx)tst-dlopen-constructor-null-mod2.so: \ > > CFLAGS-tst-origin.c += $(no-stack-protector) > CFLAGS-liborigin-mod.c += $(no-stack-protector) > -LDFLAGS-tst-origin += -Wl,-rpath,\$$ORIGIN -L$(subst :, -L,$(rpath-link)) -lorigin-mod > -$(objpfx)tst-origin.out: tst-origin.sh $(objpfx)liborigin-mod.so $(objpfx)tst-origin > +# The test requires liborigin-mod.so as dependency without a path, which is > +# added by default. > +$(objpfx)tst-origin: $(objpfx)tst-origin.o $(objpfx)liborigin-mod.so > + $(LINK.o) $(sysdep-LDFLAGS) $(rtld-LDFLAGS) -B$(csu-objpfx) \ > + $(link-test-modules-rpath-link) \ > + $(objpfx)tst-origin.o \ > + -o $@ \ > + -Wl,-rpath,\$$ORIGIN -L$(subst :, -L,$(rpath-link)) -lorigin-mod > +$(objpfx)tst-origin.out: tst-origin.sh $(objpfx)tst-origin > $(SHELL) \ > $< \ > '$(common-objpfx)' \ Is this ok to fix this issue Florian? I have tested with and without --enable-hardcoded-path-in-tests and it seems to work as expected by fixing the prerequisite rule and creating a dependency without the full path.
On 20/03/25 11:11, Adhemerval Zanella Netto wrote: > > > On 19/03/25 11:47, Adhemerval Zanella Netto wrote: >> >> >> On 19/03/25 11:02, Adhemerval Zanella Netto wrote: >>> >>> >>> On 19/03/25 09:42, Joseph Myers wrote: >>>> This still isn't working for me; the build fails with "cannot find >>>> -lorigin-mod". (Before 5291d9f1e274dd869bc0b3d044fd4cbae486893d it was >>>> also broken with a lint-makefiles failure.) >>>> >>> >>> I forgot that rules dependencies are evaluated in no defined order. I am >>> trying to come up a fix, the main issue is making the build with >>> --enable-hardcoded-path-in-tests. >> >> I had to reinstate a custom rule to avoid the DT_NEEDED with a full path >> and also link tst-origin with built libc: >> >> diff --git a/elf/Makefile b/elf/Makefile >> index 3d60000ec9..fd73b68308 100644 >> --- a/elf/Makefile >> +++ b/elf/Makefile >> @@ -456,7 +456,6 @@ tests += \ >> tst-noload \ >> tst-non-directory-path \ >> tst-null-argv \ >> - tst-origin \ >> tst-p_align1 \ >> tst-p_align2 \ >> tst-p_align3 \ >> @@ -3451,8 +3450,15 @@ $(objpfx)tst-dlopen-constructor-null-mod2.so: \ >> >> CFLAGS-tst-origin.c += $(no-stack-protector) >> CFLAGS-liborigin-mod.c += $(no-stack-protector) >> -LDFLAGS-tst-origin += -Wl,-rpath,\$$ORIGIN -L$(subst :, -L,$(rpath-link)) -lorigin-mod >> -$(objpfx)tst-origin.out: tst-origin.sh $(objpfx)liborigin-mod.so $(objpfx)tst-origin >> +# The test requires liborigin-mod.so as dependency without a path, which is >> +# added by default. >> +$(objpfx)tst-origin: $(objpfx)tst-origin.o $(objpfx)liborigin-mod.so >> + $(LINK.o) $(sysdep-LDFLAGS) $(rtld-LDFLAGS) -B$(csu-objpfx) \ It is missing a -nostdlib -nostartfiles here to avoid linking with the toochain sysroot one. >> + $(link-test-modules-rpath-link) \ >> + $(objpfx)tst-origin.o \ >> + -o $@ \ >> + -Wl,-rpath,\$$ORIGIN -L$(subst :, -L,$(rpath-link)) -lorigin-mod >> +$(objpfx)tst-origin.out: tst-origin.sh $(objpfx)tst-origin >> $(SHELL) \ >> $< \ >> '$(common-objpfx)' \ > > Is this ok to fix this issue Florian? I have tested with and without > --enable-hardcoded-path-in-tests and it seems to work as expected > by fixing the prerequisite rule and creating a dependency without > the full path. I have tested it with different gcc (9, 10, 11, 12, 13) built with different glibcs (2.32, 2.33, 2.34, 2.35, 2.38) and I saw no issue. I will install this patch to avoid the breakage of tst-origin.
* Adhemerval Zanella Netto: >> Is this ok to fix this issue Florian? I have tested with and without >> --enable-hardcoded-path-in-tests and it seems to work as expected >> by fixing the prerequisite rule and creating a dependency without >> the full path. > > I have tested it with different gcc (9, 10, 11, 12, 13) built with > different glibcs (2.32, 2.33, 2.34, 2.35, 2.38) and I saw no issue. > > I will install this patch to avoid the breakage of tst-origin. Yes, please go ahead. We'll see how it works in my environment. Thanks, Florian
* Florian Weimer: > * Adhemerval Zanella Netto: > >> On 19/03/25 11:02, Adhemerval Zanella Netto wrote: >>> >>> >>> On 19/03/25 09:42, Joseph Myers wrote: >>>> This still isn't working for me; the build fails with "cannot find >>>> -lorigin-mod". (Before 5291d9f1e274dd869bc0b3d044fd4cbae486893d it was >>>> also broken with a lint-makefiles failure.) >>>> >>> >>> I forgot that rules dependencies are evaluated in no defined order. I am >>> trying to come up a fix, the main issue is making the build with >>> --enable-hardcoded-path-in-tests. >> >> I had to reinstate a custom rule to avoid the DT_NEEDED with a full path >> and also link tst-origin with built libc: > > This looks like a job for .EXTRA_PREREQS, I think: > > <https://www.gnu.org/software/make/manual/html_node/Special-Variables.html#index-_002eEXTRA_005fPREREQS-_0028prerequisites-not-added-to-automatic-variables_0029> > > Unfortunately that was only added in make 4.3, which is quite recent. > > I think we could add this: > > # sofini.os must be placed last since it terminates .eh_frame section. > build-module-helper-objlist = \ > $(patsubst %_pic.a,$(whole-archive) %_pic.a $(no-whole-archive),\ > $(filter-out %.lds $(map-file) $(+preinit) $(+postinit) \ > + $(build-module-nolink) \ > $(elf-objpfx)sofini.os \ > $(link-libc-deps),$^)) > > > and then use: > > LDFLAGS-tst-origin += -Wl,-rpath,\$$ORIGIN -L$(subst :, -L,$(rpath-link)) -lorigin-mod > $(objpfx)tst-origin: $(objpfx)liborigin-mod.so > $(objpfx)tst-origin: build-module-nolink = $(objpfx)liborigin-mod.so > > But I haven't tested it. I found out why this didn't work. The link is for the main program, so build-module-helper-objlist is unused. I'm testing a more complete approach. Thanks, Florian
diff --git a/elf/Makefile b/elf/Makefile index 77a76f2142..8055d9ffbb 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -456,6 +456,7 @@ tests += \ tst-noload \ tst-non-directory-path \ tst-null-argv \ + tst-origin \ tst-p_align1 \ tst-p_align2 \ tst-p_align3 \ @@ -763,6 +764,7 @@ modules-names += \ libmarkermod5-3 \ libmarkermod5-4 \ libmarkermod5-5 \ + liborigin-mod \ libtracemod1-1 \ libtracemod2-1 \ libtracemod3-1 \ @@ -3442,3 +3444,24 @@ $(objpfx)tst-dlopen-constructor-null: \ $(objpfx)tst-dlopen-constructor-null-mod2.so $(objpfx)tst-dlopen-constructor-null-mod2.so: \ $(objpfx)tst-dlopen-constructor-null-mod1.so + +CFLAGS-tst-origin.c += $(no-stack-protector) +$(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) diff --git a/elf/dl-load.c b/elf/dl-load.c index 4998652adf..6b7e9799f3 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -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 { diff --git a/elf/dl-origin.c b/elf/dl-origin.c index 9f6b921b01..812f5dbb28 100644 --- a/elf/dl-origin.c +++ b/elf/dl-origin.c @@ -47,3 +47,9 @@ _dl_get_origin (void) return result; } + +char * +_dl_canonicalize (int fd) +{ + return NULL; +} diff --git a/elf/liborigin-mod.c b/elf/liborigin-mod.c new file mode 100644 index 0000000000..aa6d4c27df --- /dev/null +++ b/elf/liborigin-mod.c @@ -0,0 +1 @@ +void foo (void) {} diff --git a/elf/tst-origin.c b/elf/tst-origin.c new file mode 100644 index 0000000000..734b2e81f6 --- /dev/null +++ b/elf/tst-origin.c @@ -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; +} diff --git a/elf/tst-origin.sh b/elf/tst-origin.sh new file mode 100755 index 0000000000..2555d3ed9e --- /dev/null +++ b/elf/tst-origin.sh @@ -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 diff --git a/stdio-common/Makefile b/stdio-common/Makefile index b68e9223c6..d3733d0c3d 100644 --- a/stdio-common/Makefile +++ b/stdio-common/Makefile @@ -59,6 +59,7 @@ headers := \ # headers routines := \ + _fitoa_word \ _itoa \ _itowa \ asprintf \ @@ -663,6 +664,8 @@ CFLAGS-dprintf.c += $(config-cflags-wno-ignored-attributes) # off for non-shared builds. CFLAGS-_itoa.o = $(no-stack-protector) CFLAGS-_itoa.op = $(no-stack-protector) +CFLAGS-_fitoa_word.o = $(no-stack-protector) +CFLAGS-_fitoa_word.op = $(no-stack-protector) CFLAGS-scanf13.c += $(test-config-cflags-wno-fortify-source) diff --git a/stdio-common/_fitoa_word.c b/stdio-common/_fitoa_word.c new file mode 100644 index 0000000000..f0b2707173 --- /dev/null +++ b/stdio-common/_fitoa_word.c @@ -0,0 +1,60 @@ +/* Internal function for converting integers to ASCII. + Copyright (C) 1994-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/>. */ + +#include <_itoa.h> + +char * +_itoa_word (_ITOA_WORD_TYPE value, char *buflim, + unsigned int base, int upper_case) +{ + const char *digits = (upper_case + ? _itoa_upper_digits + : _itoa_lower_digits); + + switch (base) + { +#define SPECIAL(Base) \ + case Base: \ + do \ + *--buflim = digits[value % Base]; \ + while ((value /= Base) != 0); \ + break + + SPECIAL (10); + SPECIAL (16); + SPECIAL (8); + default: + do + *--buflim = digits[value % base]; + while ((value /= base) != 0); + } + return buflim; +} +#undef SPECIAL + +char * +_fitoa_word (_ITOA_WORD_TYPE value, char *buf, unsigned int base, + int upper_case) +{ + char tmpbuf[sizeof (value) * 4]; /* Worst case length: base 2. */ + char *cp = _itoa_word (value, tmpbuf + sizeof (value) * 4, base, upper_case); + while (cp < tmpbuf + sizeof (value) * 4) + *buf++ = *cp++; + return buf; +} + diff --git a/stdio-common/_itoa.c b/stdio-common/_itoa.c index 51c3ab9c14..08859f0dd0 100644 --- a/stdio-common/_itoa.c +++ b/stdio-common/_itoa.c @@ -162,38 +162,6 @@ const struct base_table_t _itoa_base_table[] attribute_hidden = }; #endif -#if IS_IN (libc) -char * -_itoa_word (_ITOA_WORD_TYPE value, char *buflim, - unsigned int base, int upper_case) -{ - const char *digits = (upper_case - ? _itoa_upper_digits - : _itoa_lower_digits); - - switch (base) - { -#define SPECIAL(Base) \ - case Base: \ - do \ - *--buflim = digits[value % Base]; \ - while ((value /= Base) != 0); \ - break - - SPECIAL (10); - SPECIAL (16); - SPECIAL (8); - default: - do - *--buflim = digits[value % base]; - while ((value /= base) != 0); - } - return buflim; -} -#undef SPECIAL -#endif /* IS_IN (libc) */ - - #if _ITOA_NEEDED char * _itoa (unsigned long long int value, char *buflim, unsigned int base, @@ -460,17 +428,6 @@ _itoa (unsigned long long int value, char *buflim, unsigned int base, } #endif -char * -_fitoa_word (_ITOA_WORD_TYPE value, char *buf, unsigned int base, - int upper_case) -{ - char tmpbuf[sizeof (value) * 4]; /* Worst case length: base 2. */ - char *cp = _itoa_word (value, tmpbuf + sizeof (value) * 4, base, upper_case); - while (cp < tmpbuf + sizeof (value) * 4) - *buf++ = *cp++; - return buf; -} - #if _ITOA_NEEDED char * _fitoa (unsigned long long value, char *buf, unsigned int base, int upper_case) diff --git a/sysdeps/generic/_itoa.h b/sysdeps/generic/_itoa.h index d7e3007389..2f170d3bf2 100644 --- a/sysdeps/generic/_itoa.h +++ b/sysdeps/generic/_itoa.h @@ -51,40 +51,9 @@ hidden_proto (_itoa_upper_digits) hidden_proto (_itoa_lower_digits) #endif -#if IS_IN (libc) extern char *_itoa_word (_ITOA_WORD_TYPE value, char *buflim, unsigned int base, int upper_case) attribute_hidden; -#else -static inline char * __attribute__ ((unused, always_inline)) -_itoa_word (_ITOA_WORD_TYPE value, char *buflim, - unsigned int base, int upper_case) -{ - const char *digits = (upper_case - ? _itoa_upper_digits - : _itoa_lower_digits); - - switch (base) - { -# define SPECIAL(Base) \ - case Base: \ - do \ - *--buflim = digits[value % Base]; \ - while ((value /= Base) != 0); \ - break - - SPECIAL (10); - SPECIAL (16); - SPECIAL (8); - default: - do - *--buflim = digits[value % base]; - while ((value /= base) != 0); - } - return buflim; -} -# undef SPECIAL -#endif /* Similar to the _itoa functions, but output starts at buf and pointer after the last written character is returned. */ diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h index 8465cbaa9b..19494b82ee 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -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; diff --git a/sysdeps/mach/hurd/Makefile b/sysdeps/mach/hurd/Makefile index 13e5cea4c2..4b69b40065 100644 --- a/sysdeps/mach/hurd/Makefile +++ b/sysdeps/mach/hurd/Makefile @@ -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 diff --git a/sysdeps/unix/sysv/linux/dl-origin.c b/sysdeps/unix/sysv/linux/dl-origin.c index decdd8ae9e..3c52ba51a6 100644 --- a/sysdeps/unix/sysv/linux/dl-origin.c +++ b/sysdeps/unix/sysv/linux/dl-origin.c @@ -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; +}