Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle symlinks to executables using $ORIGIN in rpath/runpath better #49

Open
ekacoei opened this issue Dec 8, 2021 · 6 comments
Open

Comments

@ekacoei
Copy link

ekacoei commented Dec 8, 2021

What is the expected output if a dependency cannot be found? I cannot resolve my Java dependencies:

$ ./libtree /usr/bin/java 
java
└── libjli.so not found: RPATH (empty) LD_LIBRARY_PATH (empty) RUNPATH "/usr/bin//../lib/jli":"/usr/bin//../lib": /etc/ld.so.conf "/usr/lib/x86_64-linux-gnu/libfakeroot":"/usr/local/lib/i386-linux-gnu":"/lib/i386-linux-gnu":"/usr/lib/i386-linux-gnu":"/usr/local/lib/i686-linux-gnu":"/lib/i686-linux-gnu":"/usr/lib/i686-linux-gnu":"/usr/local/lib":"/usr/local/lib/x86_64-linux-gnu":"/lib/x86_64-linux-gnu":"/usr/lib/x86_64-linux-gnu":"/lib32":"/usr/lib32":"/libx32":"/usr/libx32":

$ ldd /usr/bin/java
	linux-vdso.so.1 (0x00007ffc30d7b000)
	libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f541bf8f000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f541bf6e000)
	libjli.so => not found
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f541bf69000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f541bda8000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f541c1f0000)

$ /usr/bin/java --version
openjdk 11.0.9.1 2020-11-04
OpenJDK Runtime Environment (build 11.0.9.1+1-post-Debian-1deb10u2)
OpenJDK 64-Bit Server VM (build 11.0.9.1+1-post-Debian-1deb10u2, mixed mode, sharing)
@haampie
Copy link
Owner

haampie commented Dec 8, 2021

See also #46, libtree skips some glibc libraries because output tends to be very verbose otherwise. With libtree -v you get the other libs too.

@haampie
Copy link
Owner

haampie commented Dec 9, 2021

Okay, Java is very odd, I can't explain it yet. Using the C rewrite:

$ ./libtree /usr/bin/java 
lib.so 
└── libjli.so not found
    ┊ Paths considered in this order:
    ┊ 1. rpath is skipped because runpath was set
    ┊ 2. LD_LIBRARY_PATH was not set
    ┊ 3. runpath:
    ┊    /usr/bin//../lib/amd64/jli
    ┊    /usr/bin//../lib/amd64
    ┊ 4. ld.so.conf:
...

So, you're reading this right, java has a soname lib.so 😅, and a needed library libjli.so, and also runpaths, but the library can't be found from there. Also ldd chokes:

$ ldd /usr/bin/java 
	linux-vdso.so.1 (0x00007ffef8fac000)
	libjli.so => not found
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa54d940000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fa54db55000)

It also has a default interpreter:

$ readelf -l /usr/bin/java | grep interpret
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]

If I pass it to the interpreter:

$ /lib64/ld-linux-x86-64.so.2 /usr/bin/java 
/usr/bin/java: error while loading shared libraries: libjli.so: cannot open shared object file: No such file or directory

So, apparently that's not how java executes.

@haampie
Copy link
Owner

haampie commented Dec 9, 2021

Okay, it turns out that ldd output is completely unreliable for symlinks.

Note that this works:

$ ./libtree -v $(realpath /usr/bin/java)
lib.so 
├── libjli.so [runpath]
│   ├── libz.so.1 [ld.so.conf]
│   │   └── libc.so.6 [ld.so.conf]
│   ├── libpthread.so.0 [ld.so.conf]
│   ├── libc.so.6 [ld.so.conf]
│   └── libdl.so.2 [ld.so.conf]
└── libc.so.6 [ld.so.conf]

$ ldd $(realpath /usr/bin/java)
	linux-vdso.so.1 (0x00007ffd4b90b000)
	libjli.so => /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/../lib/amd64/jli/libjli.so (0x00007f3db0e03000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f3db0bf5000)
	libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f3db0bd9000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f3db0bd3000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f3db0bb0000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f3db0e1c000)

If you execute /usr/bin/java the executable path is resolved before ld.so is invoked (from strace output), and then $ORIGIN runpaths are relative to the real path. If you invoke ld.so by hand and pass the symlink, $ORIGIN is relative to the symlink and it fails.

It seems better (but note the man ldd security warning) to do

$ LD_TRACE_LOADED_OBJECTS=1 /usr/bin/java
	linux-vdso.so.1 (0x00007fffb7764000)
	libjli.so => /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/../lib/amd64/jli/libjli.so (0x00007f1353f70000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f1353d62000)
	libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f1353d46000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f1353d40000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f1353d1d000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f1353f84000)

Not sure how to handle this best in libtree... warn if the user passes a symlink? Resolve the symlink?

@haampie haampie changed the title Dependency not found, output incomplete Handle symlinks to executables using $ORIGIN in rpath/runpath better Dec 9, 2021
@ekacoei
Copy link
Author

ekacoei commented Dec 9, 2021

Thank you for your detailed writedown, that provided me a lot of insigths.
Concerning whether you shall resolve symlinks - as far as I know it is very common that libraries symlink one another to cover version compatibility

$  ls -l /usr/lib/libgimp-2.0.so.0
lrwxrwxrwx 1 root root 23 Dec 24  2018 /usr/lib/libgimp-2.0.so.0 -> libgimp-2.0.so.0.1000.8

but I could not find libgimp being loaded

$ ./libtree $(realpath $(which gimp))
gimp-2.10
├── libgimpwidgets-2.0.so.0 [default paths]
│   ├── libgimpbase-2.0.so.0 [default paths]
│   │   └── libgexiv2.so.2 [ld.so.conf]
│   │       └── libexiv2.so.14 [ld.so.conf]
│   ├── libgimpcolor-2.0.so.0 [default paths]
│   │   ├── libgimpbase-2.0.so.0 (collapsed) [default paths]
│   │   ├── libgegl-0.4.so.0 [ld.so.conf]
│   │   │   ├── libgmodule-2.0.so.0 [ld.so.conf]
│   │   │   └── libbabl-0.1.so.0 [ld.so.conf]
│   │   │       └── liblcms2.so.2 [ld.so.conf]
│   │   ├── libbabl-0.1.so.0 (collapsed) [ld.so.conf]
│   │   ├── libcairo.so.2 [ld.so.conf]
│   │   │   ├── libpixman-1.so.0 [ld.so.conf]
│   │   │   ├── libpng16.so.16 [ld.so.conf]
│   │   │   ├── libxcb-shm.so.0 [ld.so.conf]
│   │   │   ├── libxcb-render.so.0 [ld.so.conf]
│   │   │   ├── libXrender.so.1 [ld.so.conf]
│   │   │   └── libXext.so.6 [ld.so.conf]
│   │   └── liblcms2.so.2 (collapsed) [ld.so.conf]
│   ├── libgimpconfig-2.0.so.0 [default paths]
│   │   ├── libgimpbase-2.0.so.0 (collapsed) [default paths]
│   │   ├── libgimpcolor-2.0.so.0 (collapsed) [default paths]
│   │   └── libgimpmath-2.0.so.0 [default paths]
│   ├── libgegl-0.4.so.0 (collapsed) [ld.so.conf]
│   ├── libbabl-0.1.so.0 (collapsed) [ld.so.conf]
│   ├── libgtk-x11-2.0.so.0 [ld.so.conf]
│   │   ├── libgdk-x11-2.0.so.0 [ld.so.conf]
│   │   │   ├── libXrender.so.1 (collapsed) [ld.so.conf]
│   │   │   ├── libXinerama.so.1 [ld.so.conf]
│   │   │   │   └── libXext.so.6 (collapsed) [ld.so.conf]
│   │   │   ├── libXi.so.6 [ld.so.conf]
│   │   │   │   └── libXext.so.6 (collapsed) [ld.so.conf]
│   │   │   ├── libXrandr.so.2 [ld.so.conf]
│   │   │   │   ├── libXext.so.6 (collapsed) [ld.so.conf]
│   │   │   │   └── libXrender.so.1 (collapsed) [ld.so.conf]
│   │   │   ├── libXcursor.so.1 [ld.so.conf]
│   │   │   │   ├── libXrender.so.1 (collapsed) [ld.so.conf]
│   │   │   │   └── libXfixes.so.3 [ld.so.conf]
│   │   │   ├── libXcomposite.so.1 [ld.so.conf]
│   │   │   ├── libXdamage.so.1 [ld.so.conf]
│   │   │   │   └── libXfixes.so.3 (collapsed) [ld.so.conf]
│   │   │   ├── libXfixes.so.3 (collapsed) [ld.so.conf]
│   │   │   ├── libcairo.so.2 (collapsed) [ld.so.conf]
│   │   │   └── libXext.so.6 (collapsed) [ld.so.conf]
│   │   ├── libgmodule-2.0.so.0 (collapsed) [ld.so.conf]
│   │   ├── libXcomposite.so.1 (collapsed) [ld.so.conf]
│   │   ├── libXdamage.so.1 (collapsed) [ld.so.conf]
│   │   ├── libXfixes.so.3 (collapsed) [ld.so.conf]
│   │   ├── libatk-1.0.so.0 [ld.so.conf]
│   │   └── libcairo.so.2 (collapsed) [ld.so.conf]
│   ├── libgdk-x11-2.0.so.0 (collapsed) [ld.so.conf]
│   ├── libcairo.so.2 (collapsed) [ld.so.conf]
│   └── liblcms2.so.2 (collapsed) [ld.so.conf]
├── libgtk-x11-2.0.so.0 (collapsed) [ld.so.conf]
├── libgdk-x11-2.0.so.0 (collapsed) [ld.so.conf]
├── libgimpconfig-2.0.so.0 (collapsed) [default paths]
├── libgimpmath-2.0.so.0 (collapsed) [default paths]
├── libgimpthumb-2.0.so.0 [default paths]
│   └── libgimpbase-2.0.so.0 (collapsed) [default paths]
├── libgimpcolor-2.0.so.0 (collapsed) [default paths]
├── libgimpmodule-2.0.so.0 [default paths]
│   ├── libgimpbase-2.0.so.0 (collapsed) [default paths]
│   ├── libgimpconfig-2.0.so.0 (collapsed) [default paths]
│   └── libgmodule-2.0.so.0 (collapsed) [ld.so.conf]
├── libgimpbase-2.0.so.0 (collapsed) [default paths]
├── libcairo.so.2 (collapsed) [ld.so.conf]
├── libgegl-0.4.so.0 (collapsed) [ld.so.conf]
├── libgegl-npd-0.4.so [ld.so.conf]
│   ├── libgegl-0.4.so.0 (collapsed) [ld.so.conf]
│   └── libbabl-0.1.so.0 (collapsed) [ld.so.conf]
├── libbabl-0.1.so.0 (collapsed) [ld.so.conf]
├── liblcms2.so.2 (collapsed) [ld.so.conf]
├── libgexiv2.so.2 (collapsed) [ld.so.conf]
└── libmypaint-1.3.so.0 [ld.so.conf]
    └── libjson-c.so.3 [ld.so.conf]

So I am lost at the moment if libtree has to handle symlinks on real world examples, including libraries as symlinks.

@haampie
Copy link
Owner

haampie commented Dec 9, 2021

It only applies to executables, not to libraries, because ld.so gets the resolved executable path (I think).

$ORIGIN is relative to the path in which the library was found, even if it is a symlink. For example if you have exe depends on libb.so depends on liba.so and the libs are in the same dir with $ORIGIN rpath, but you symlink libb.so from another dir, and set exe's rpath to that other dir, liba.so is not found.

 $ mkdir -p a b
 $ echo 'int f(){return 3;}' | gcc -shared -o a/liba.so -Wl,-soname,liba.so -nostdlib -x c -
 $ echo 'extern int f(); int g(){return f();}' | gcc -shared -o a/libb.so -Wl,-soname,libb.so '-Wl,-rpath,$ORIGIN' -nostdlib -x c - -La -la 
 $ ln -s ../a/libb.so b/libb.so
 $ echo 'extern int g(); int main(){return g();}' | gcc -o exe '-Wl,-rpath,$ORIGIN/b' -x c - -La -lb 
 $ ./exe 
./exe: error while loading shared libraries: liba.so: cannot open shared object file: No such file or directory

 $ echo 'extern int g(); int main(){return g();}' | gcc -o exe '-Wl,-rpath,$ORIGIN/a' -x c - -La -lb 
 $ ./exe
[works]

@haampie
Copy link
Owner

haampie commented Dec 9, 2021

Another thing to note is that the linker likes to copy the soname into DT_NEEDED instead of the filename you provide in -l<libname>. So if linking works & rpaths are set correctly, it may still fail at runtime if there is no file/symlink matching the soname.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants