[v3] elf: Canonicalize $ORIGIN in an explicit ld.so invocation [BZ 25263]
Checks
Context |
Check |
Description |
redhat-pt-bot/TryBot-apply_patch |
success
|
Patch applied to master at the time it was sent
|
redhat-pt-bot/TryBot-32bit |
success
|
Build for i686
|
linaro-tcwg-bot/tcwg_glibc_build--master-arm |
success
|
Build passed
|
linaro-tcwg-bot/tcwg_glibc_check--master-arm |
fail
|
Test failed
|
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 |
success
|
Build passed
|
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 |
success
|
Test passed
|
Commit Message
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>
---
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
Comments
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
@@ -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)
@@ -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
@@ -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)
new file mode 100644
@@ -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;
+}
+
@@ -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)
@@ -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. */
@@ -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;
+}