[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