[PATCH 1/1] elf: Canonicalize $ORIGIN in an explicit ld.so invocation [BZ #25263]
Adhemerval Zanella Netto
adhemerval.zanella@linaro.org
Sat Mar 1 01:27:16 GMT 2025
On 28/02/25 16:02, Geoffrey Thomas wrote:
> On Thu, Feb 27, 2025, at 3:26 PM, Adhemerval Zanella Netto wrote:
>> Although this change solves the LD_TRACE_LOADED_OBJECTS=1 (ldd) issue, I
>> think it still misses the underlying issue for the case user want to also
>> run the binary with a direct loader invocation (as Florian has hinted on
>> comment #4).
>>
>> For instance, with this testcase (similar to what electron is doing from
>> the bug report):
>>
>> $ cat test/main.c
>> int main (int argc, char *argv[]) { return 0; }
>> $ cat test/lib1.c
>> int foo (void) { return 42; }
>> tst-bz25263 $ mkdir test && cd test
>> test $ gcc -Wall -shared -Wl,-soname,lib1.so lib1.c -o lib1.so
>> test $ gcc -no-pie -Wall main.c -o main -Wl,-rpath,\$ORIGIN -L`pwd`
>> -Wl,-no-as-needed -l1
>> test $ gcc -no-pie -Wall main.c -o main-hcp
>> -Wl,-rpath,\$ORIGIN:$GLIBCBUILDDIR
>> -Wl,-dynamic-linker,$GLIBCBUILDDIR/elf/ld.so -L`pwd` -Wl,-no-as-needed
>> -l1
>> main $ cd ..
>> $ ln -s test/main main
>> $ ln -s test/main-hcp main-hcp
>>
>> So 'main' is a binary without just one RUNPATH with $ORIGIN, while 'main-hcp'
>> is binary that will use a built glibc (along with the RUNPATH).
>>
>> Checking the library path resolution for both cases:
>>
>> $ LD_DEBUG=libs ./main-hcp
>> 3151277: find library=lib1.so [0]; searching
>> 3151277: search
>> path=$GLIBCBUILDDIR/tst-bz25263/test/glibc-hwcaps/x86-64-v3:/home/azanella/Projects/glibc/build/
>> x86_64-linux-gnu-work/tst-bz25263/test/glibc-hwcaps/x86-64-v2:$GLIBCBUILDDIR/tst-bz25263/test:$GLIBCBUILDDIR/glibc-hwcaps/x86-64-v3:$GLIBCBUILDDIR/glibc-hwcaps/x86-64-v2:$GLIBCBUILDDIR
>> (RUNPATH from file ./main-hcp)
>> 3151277: trying
>> file=$GLIBCBUILDDIR/tst-bz25263/test/glibc-hwcaps/x86-64-v3/lib1.so
>> 3151277: trying
>> file=$GLIBCBUILDDIR/tst-bz25263/test/glibc-hwcaps/x86-64-v2/lib1.so
>> 3151277: trying file=$GLIBCBUILDDIR/tst-bz25263/test/lib1.so
>>
>> LD_DEBUG=libs $GLIBCBUILDDIR/elf/ld.so --library-path $GLIBCBUILDDIR
>> ./main
>> 3151286: find library=lib1.so [0]; searching
>> 3151286: search
>> path=$GLIBCBUILDDIR/glibc-hwcaps/x86-64-v3:$GLIBCBUILDDIR/glibc-hwcaps/x86-64-v2:$GLIBCBUILDDIR (--library-path)
>> 3151286: trying file=$GLIBCBUILDDIR/glibc-hwcaps/x86-64-v3/lib1.so
>> 3151286: trying file=$GLIBCBUILDDIR/glibc-hwcaps/x86-64-v2/lib1.so
>> 3151286: trying file=$GLIBCBUILDDIR/lib1.so
>> 3151286: search
>> path=$GLIBCBUILDDIR/tst-bz25263/./glibc-hwcaps/x86-64-v3:$GLIBCBUILDDIR/tst-bz25263/./glibc-hwcaps/x86-64-v2:$GLIBCBUILDDIR/tst-bz25263/. (RUNPATH
>> from file ./main)
>> 3151286: trying
>> file=$GLIBCBUILDDIR/tst-bz25263/./glibc-hwcaps/x86-64-v3/lib1.so
>> 3151286: trying
>> file=$GLIBCBUILDDIR/tst-bz25263/./glibc-hwcaps/x86-64-v2/lib1.so
>> 3151286: trying file=$GLIBCBUILDDIR/tst-bz25263/./lib1.so
>> 3151286: search cache=/etc/ld.so.cache
>> 3151286: search
>> path=/lib64/glibc-hwcaps/x86-64-v3:/lib64/glibc-hwcaps/x86-64-v2:/lib64:/usr/lib64/glibc-hwcaps/x86-64-v3:/usr/lib64/glibc-hwcaps/x86-64-v2:/usr/lib64 (system
>> search path)
>> 3151286: trying file=/lib64/glibc-hwcaps/x86-64-v3/lib1.so
>> 3151286: trying file=/lib64/glibc-hwcaps/x86-64-v2/lib1.so
>> 3151286: trying file=/lib64/lib1.so
>> 3151286: trying file=/usr/lib64/glibc-hwcaps/x86-64-v3/lib1.so
>> 3151286: trying file=/usr/lib64/glibc-hwcaps/x86-64-v2/lib1.so
>> 3151286: trying file=/usr/lib64/lib1.so
>>
>> You can see that if the binary is not issued directly by the loader
>> (main-hcp),
>> it will have the full realpath on the search path
>> (GLIBCBUILDDIR/tst-bz25263/test);
>> different that binary issued by the loader which is has the current
>> directory
>> (GLIBCBUILDDIR/tst-bz25263).
>
> This is with my patch? Directly on top of glibc-2.41 (commit db71c940
> from github.com/geofft/glibc), with a checkout at /glibc and so
> GLIBCBUILDDIR=/glibc/build, I get
Yes, this your patch on top of 9e51ae3cd0c7f65bdeba93b7f1d780cdb21fc269
>
> [root@nixos:/glibc/build/tst-bz25263]# ls -l
> total 0
> lrwxr-xr-x 1 root root 9 Feb 28 13:37 main -> test/main
> lrwxr-xr-x 1 root root 13 Feb 28 13:37 main-hcp -> test/main-hcp
> drwxr-xr-x 7 root root 224 Feb 28 13:37 test
> [root@nixos:/glibc/build/tst-bz25263]# readelf -d main | grep NEEDED
> 0x0000000000000001 (NEEDED) Shared library: [lib1.so]
> 0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
> [root@nixos:/glibc/build/tst-bz25263]# LD_DEBUG=libs /glibc/build/elf/ld.so --library-path /glibc/build ./main
> 49044: find library=lib1.so [0]; searching
> 49044: search path=/glibc/build (--library-path)
> 49044: trying file=/glibc/build/lib1.so
> 49044: search path=/glibc/build/tst-bz25263/test:/nix/store/6j2qcdi8axcl91v08blc3vjn39y117z3-shell/lib:/nix/store/q6kvdfhlp251n8rx183lg1n245kwg6m8-glibc-2.39-52/lib:/nix/store/fxh2cg0sfgygfya6qjr11gzj4sbjx0h9-gcc-13.2.0-lib/lib (RUNPATH from file ./main)
> 49044: trying file=/glibc/build/tst-bz25263/test/lib1.so
> 49044:
> 49044: find library=libc.so.6 [0]; searching
> 49044: search path=/glibc/build (--library-path)
> 49044: trying file=/glibc/build/libc.so.6
> [...]
>
> which, if I'm following correctly, is the fixed behavior.
Sigh, for some reason I did not use git-pw on your patch and applied
directly from mailbox it was malformated with the wrong path for the
procfs. With 'git-pw patch apply 106751' it now seems to work.
You might want to use this instead (we have a helper to access the
procfs).
struct fd_to_filename fdfilename;
char *canonical = (char *) malloc (PATH_MAX + 1);
char *path = __fd_to_filename (fd, &fdfilename);
int size = INTERNAL_SYSCALL_CALL (readlinkat, AT_FDCWD, path,
canonical, PATH_MAX);
It does seems to work.
>
> As expected it does not work with a copy instead of a symlink:
>
> [root@nixos:/glibc/build/tst-bz25263]# cp test/main maincopy
> [root@nixos:/glibc/build/tst-bz25263]# LD_DEBUG=libs /glibc/build/elf/ld.so --library-path /glibc/build ./maincopy
> 49073: find library=lib1.so [0]; searching
> 49073: search path=/glibc/build (--library-path)
> 49073: trying file=/glibc/build/lib1.so
> 49073: search path=/glibc/build/tst-bz25263:/nix/store/6j2qcdi8axcl91v08blc3vjn39y117z3-shell/lib:/nix/store/q6kvdfhlp251n8rx183lg1n245kwg6m8-glibc-2.39-52/lib:/nix/store/fxh2cg0sfgygfya6qjr11gzj4sbjx0h9-gcc-13.2.0-lib/lib (RUNPATH from file ./maincopy)
> 49073: trying file=/glibc/build/tst-bz25263/lib1.so
> 49073: trying file=/nix/store/6j2qcdi8axcl91v08blc3vjn39y117z3-shell/lib/lib1.so
> 49073: trying file=/nix/store/q6kvdfhlp251n8rx183lg1n245kwg6m8-glibc-2.39-52/lib/lib1.so
> 49073: trying file=/nix/store/fxh2cg0sfgygfya6qjr11gzj4sbjx0h9-gcc-13.2.0-lib/lib/lib1.so
> 49073: search cache=/root/etc/ld.so.cache
> 49073: search path=/root/lib (system search path)
> 49073: trying file=/root/lib/lib1.so
> 49073:
> ./maincopy: error while loading shared libraries: lib1.so: cannot open shared object file: No such file or directory
>
> What commit and OS and arch are you on? (It's possible I'm confusing
> myself by using NixOS, maybe.)
>
So I don't have a strong preference, I started to work on a similar fix [1]
with my proposed approach. It has the slight advantage that realpath will
allocate just the required size and work with path larger than PATH_MAX
(which we try to impose on most interfaces, but I think this will be
unlikely to be a problem), although it will require some more syscall to
canonicalize.
https://github.com/zatrazz/glibc/tree/azanella/bz25263-loader-realpath
More information about the Libc-alpha
mailing list