During attempts to build top-of-tree libssh2 on a dinosaur (SCO OpenServer 5.0.7), an issue with loading indirect dependencies of libssh2.so is occurring. They can be worked around by setting LD_LIBRARY_PATH at run-time, however it seems much simpler to depend on DT_RPATH so the run-time environment does not require management.
The linker behaves as defined at the link following (ironically, more completely documented on this list than in the official documentation): https://lists.gnu.org/archive/html/bug-libtool/2002-08/msg00026.html In essence, the libssh2 project is set up in a way so that DT_RUNPATH is set in the build ELF objects and DT_RPATH is omitted. I need to keep the original vendor files and configuration, so have chosen to place the alternate ELF binaries under /usr/local. With a (slightly patched for this platform) copy of libssh2-1.10.1_DEV, and a configure like: CONFIG_SHELL=/usr/bin/bash \ ./configure \ CONFIG_SHELL=/usr/bin/bash \ --prefix=/usr/local \ --with-libssl-prefix=/usr/local The binary that uses the shared object has DT_RUNPATH: $ readelf -d example/.libs/ssh2 Dynamic segment at offset 0x1adc contains 23 entries: Tag Type Name/Value 0x00000001 (NEEDED) Shared library: [libssh2.so.1] 0x00000001 (NEEDED) Shared library: [libcrypto.so.3] 0x00000001 (NEEDED) Shared library: [/usr/lib/libz.so.1] 0x00000001 (NEEDED) Shared library: [libssl.so.3] 0x00000001 (NEEDED) Shared library: [/usr/lib/libcrypt.so] 0x00000001 (NEEDED) Shared library: [/usr/lib/libc.so.1] program interpreter 0x0000000c (INIT) 0x8049ac0 0x0000000d (FINI) 0x8049ad0 0x0000001d (RUNPATH) Library runpath: [/usr/local/lib] ... The shared object has DT_RUNPATH: $ readelf -d /usr/local/lib/libssh2.so Dynamic segment at offset 0xfb30 contains 21 entries: Tag Type Name/Value 0x00000001 (NEEDED) Shared library: [libcrypto.so.3] 0x00000001 (NEEDED) Shared library: [/usr/lib/libz.so.1] 0x00000001 (NEEDED) Shared library: [libssl.so.3] 0x0000000c (INIT) 0x4dae0 0x0000000d (FINI) 0x4daf0 0x0000000e (SONAME) Library soname: [libssh2.so.1] 0x0000001d (RUNPATH) Library runpath: [/usr/local/lib] ... Though the direct dependency loads, the indirect does not when LD_LIBRARY_PATH is unset. $ echo "|$LD_LIBRARY_PATH|" || $ export LD_DEBUG=16 $ example/.libs/ssh2 Object: Search Path DT_RUNPATH /usr/local/lib Object: example/.libs/ssh2 Search Path LD_LIBRARY_PATH Object: example/.libs/ssh2 Search Path DT_RPATH Object: example/.libs/ssh2 Search Path DEFAULT /usr/lib search_dir_list: trying: /usr/local/lib/libssh2.so.1 Object: /usr/local/lib/libssh2.so.1 Search Path DT_RUNPATH /usr/local/lib search_dir_list: trying: /usr/local/lib/libcrypto.so.3 _rt_so_find: trying: /usr/lib/libz.so.1 search_dir_list: trying: /usr/local/lib/libssl.so.3 _rt_so_find: trying: /usr/lib/libcrypt.so search_dir_list: trying: /usr/local/lib/libcrypto.so.3 _rt_so_find: trying: /usr/lib/libz.so.1 search_dir_list: trying: /usr/local/lib/libssl.so.3 _rt_so_find: trying: /usr/lib/libsocket.so.2 search_dir_list: trying: /usr/lib/libnsl.so search_dir_list: trying: /usr/lib/libcrypto.so.3 dynamic linker : example/.libs/ssh2 : could not open libcrypto.so.3 Killed $ ls -l /usr/local/lib/libcrypto.so.3 /opt/K/CSDI/openssl/3.0.3/usr/local/lib/libcrypto.so.3 -rwxr-xr-x 1 root sys 3731468 Dec 14 13:29 /opt/K/CSDI/openssl/3.0.3/usr/local/lib/libcrypto.so.3 lrwxrwxrwx 1 root sys 54 Dec 14 13:29 /usr/local/lib/libcrypto.so.3 -> /opt/K/CSDI/openssl/3.0.3/usr/local/lib/libcrypto.so.3 Not being intimately familiar with autotools/libtool makes it somewhat difficult to know where to start. I have made some attempts at looking into config.rpath or various m4 edits along with autoreconf -fi, but have not been successful at getting DT_RPATH written. The attempts were likely completely bogus, but it occurred to me to try to see if somewhere changing -R to -rpath would tell libtool to do something differently. It was hard to sort out which switches were for libtool and which were for ld. I managed only to block DT_RUNPATH without producing an ELF with DT_RPATH. I cannot build patchelf on this platform, but I have proven that setting DT_RPATH works by using the readelf reported "Dynamic segment at offset 0x1adc" message. For example: $ cat example/.libs/ssh2 | xxd >ssh2.xxd.orig $ cp ssh2.xxd.orig ssh2.xxd.edit $ vim ssh2.xxd.edit $ diff -U1 ssh2.xxd.orig ssh2.xxd.edit --- ssh2.xxd.orig 2022-12-14 21:35:09.000000000 -0600 +++ ssh2.xxd.edit 2022-12-14 21:39:32.000000000 -0600 @@ -433,3 +433,3 @@ 0001b00: b403 0000 0100 0000 c903 0000 0c00 0000 ................ -0001b10: c09a 0408 0d00 0000 d09a 0408 1d00 0000 ................ +0001b10: c09a 0408 0d00 0000 d09a 0408 0f00 0000 ................ 0001b20: dc03 0000 0400 0000 1481 0408 0500 0000 ................ $ cat ssh2.xxd.edit | xxd -r >ssh2 $ readelf -d ./ssh2 Dynamic segment at offset 0x1adc contains 23 entries: Tag Type Name/Value 0x00000001 (NEEDED) Shared library: [libssh2.so.1] 0x00000001 (NEEDED) Shared library: [libcrypto.so.3] 0x00000001 (NEEDED) Shared library: [/usr/lib/libz.so.1] 0x00000001 (NEEDED) Shared library: [libssl.so.3] 0x00000001 (NEEDED) Shared library: [/usr/lib/libcrypt.so] 0x00000001 (NEEDED) Shared library: [/usr/lib/libc.so.1] program interpreter 0x0000000c (INIT) 0x8049ac0 0x0000000d (FINI) 0x8049ad0 0x0000000f (RPATH) Library rpath: [/usr/local/lib] ... Now all the indirect dependencies load without having to set LD_LIBRARY_PATH at runtime: $ echo "|$LD_LIBRARY_PATH|" || $ export LD_DEBUG=16 $ ./ssh2 Object: ./ssh2 Search Path LD_LIBRARY_PATH Object: ./ssh2 Search Path DT_RPATH /usr/local/lib Object: ./ssh2 Search Path DEFAULT /usr/lib search_dir_list: trying: /usr/local/lib/libssh2.so.1 search_dir_list: trying: /usr/local/lib/libcrypto.so.3 _rt_so_find: trying: /usr/lib/libz.so.1 search_dir_list: trying: /usr/local/lib/libssl.so.3 _rt_so_find: trying: /usr/lib/libcrypt.so search_dir_list: trying: /usr/local/lib/libcrypto.so.3 _rt_so_find: trying: /usr/lib/libz.so.1 search_dir_list: trying: /usr/local/lib/libssl.so.3 _rt_so_find: trying: /usr/lib/libsocket.so.2 search_dir_list: trying: /usr/local/lib/libnsl.so search_dir_list: trying: /usr/lib/libnsl.so search_dir_list: trying: /usr/local/lib/libcrypto.so.3 _rt_so_find: trying: /usr/lib/libsocket.so.2 search_dir_list: trying: /usr/local/lib/libnsl.so search_dir_list: trying: /usr/lib/libnsl.so Fingerprint: 88 EC C8 1B 18 E8 09 D6 1E A4 B3 5D E1 FE 74 30 8B 4D D7 CA Authentication methods: publickey,password,keyboard-interactive Authentication by password failed! all done! On this platform, ld does not have a switch that acts like -rpath but will write DT_RPATH when LD_RUN_PATH is defined in the environment and ld -R will cause DT_RUNPATH creation in the linked binary. Not having stumbled on a way to modify the project in a way that leaves DT_RPATH defined and DT_RUNPATH not defined, it would be appreciated to have some comment as to whether libtool actually could still cause ld to see LD_RUN_PATH so it writes DT_RPATH. I have tried --disable-rpath on a whim but sensibly, neither DT_RPATH or DT_RUNPATH are placed in the ELF. I realize I have not shared any libssh2 resources or posted to libssh2 lists, but I wonder if this is even a possibility with libtool. A lot of searches turn up folk using patchelf or comments saying that DT_RPATH is deprecated, and such. Can anyone offer a pointer on where to look in autotools/libtool structure to get DT_RPATH set in the linked binaries or comment on whether this is even an option with libtool? I'm finding difficulty differentiating between -R and -rpath for libtool versus similar switches for ld. -- Kevin R. Bulgrien