Runtime linker ignores RPATH in executable on solaris - linker

I'm trying to compile samtools on a Solaris server where I do not have root. Samtools depends on zlib. The system zlib on this machine is not compiled with large file support, so compiling samtools against this version has the expected effect: samtools only handle small files. I need it to be able to handle large files. Luckily, there is a version of zlib compiled by the admin in /usr/local/apps/zlib-1.2.5/ with large file support. I can compile against this by adding -R /usr/local/apps/zlib-1.2.5/lib to CFLAGS, but this seems not to work. The symptoms are as follows:
When I try to run samtools, it crashes with this error:
ld.so.1: samtools: fatal: relocation error: file samtools: symbol gzopen64: referenced symbol not found
If I add /usr/local/apps/zlib-1.2.5/ to LD_LIBRARY_PATH, then samtools works fine.
Analyzing samtools with ldd and readelf yields the following:
$ ldd -r samtools
libnsl.so.1 => /usr/lib/libnsl.so.1
libsocket.so.1 => /usr/lib/libsocket.so.1
libresolv.so.2 => /usr/lib/libresolv.so.2
libm.so.2 => /usr/lib/libm.so.2
libcurses.so.1 => /usr/lib/libcurses.so.1
libz.so => /usr/lib/libz.so
libc.so.1 => /usr/lib/libc.so.1
libmp.so.2 => /usr/lib/libmp.so.2
libmd.so.1 => /usr/lib/libmd.so.1
libscf.so.1 => /usr/lib/libscf.so.1
libdoor.so.1 => /usr/lib/libdoor.so.1
libuutil.so.1 => /usr/lib/libuutil.so.1
libgen.so.1 => /usr/lib/libgen.so.1
symbol not found: gzopen64 (samtools)
$ ldd -s samtools
...(snip)...
find object=libz.so; required by samtools
search path=/usr/lib:/usr/openwin/lib:/usr/dt/lib:/usr/local/lib (LD_LIBRARY_PATH)
trying path=/usr/lib/libz.so
libz.so => /usr/lib/libz.so
...(snip)...
$ readelf -d samtools | grep RPATH
0x0000000f (RPATH) Library rpath: [/usr/local/apps/zlib-1.2.5/lib:/usr/local/apps/gcc-4.5.1/lib]
So /usr/local/apps/zlib-1.2.5/lib is clearly in the binary's RPATH, which I understand is supposed to be searched at runtime for shared libraries. However, ldd -s shows that this directory is never searched. Adding this path to LD_LIBRARY_PATH and re-running the ldd commands has the expected effect: the directory is searched and the correct version of libz is found.
So how can I force samtools to search in /usr/local/apps/zlib-1.2.5/lib at runtime without using LD_LIBRARY_PATH?
Edit: The documentation here would seem to indicate that the -R option is the correct thing to do. But it doesn't work.

I'm by no means a Solaris expert, but this line:
find object=libz.so; required by samtools
search path=/usr/lib:/usr/openwin/lib:/usr/dt/lib:/usr/local/lib (LD_LIBRARY_PATH)
seems to indicate to me that LD_LIBRARY_PATH is already set, and the /usr/lib path in it is taking precedence over any runtime linker paths. Can you unset LD_LIBRARY_PATH if it is in fact present and see if that resolves it?

Related

Getting "cannot open shared object file" even though ldd shows it can find it

I looked for similar post on this topic but none the solutions work for me. I am trying to build a small program using openssl.
/mnt/sda1/openssl$ gcc tstsvr.c -I/mnt/sda1/openssl/include -L/mnt/sda1/openssl -lcrypto -lssl
When I try to run it:
$ sudo ./a.out
./a.out: error while loading shared libraries: libcrypto.so.81.1.1: cannot open shared object file: No such file or directory
But:
$ ldd a.out
linux-vdso.so.1 (0x00007fff23fe6000)
libcrypto.so.81.1.1 => /mnt/sda1/openssl/libcrypto.so.81.1.1 (0x00007f82f1db9000)
libssl.so.81.1.1 => /mnt/sda1/openssl/libssl.so.81.1.1 (0x00007f82f1d1e000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f82f1b1a000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f82f1b14000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f82f1af1000)
/lib64/ld-linux-x86-64.so.2 (0x00007f82f20ad000)
Format looks fine (inside /mnt/sda1/openssl):
$ file a.out
a.out: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=760dc5f7be4f51f598bab38a0b1eab1a42ef8a68, for GNU/Linux 3.2.0, not stripped
$ file libcrypto.so.81.1.1
libcrypto.so.81.1.1: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=404d1e7ed143383801efbb10ed7914f2cd0858d4, not stripped
$ ldd libcrypto.so.81.1.1
linux-vdso.so.1 (0x00007ffc44b5a000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f1083a8c000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f1083a69000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f1083877000)
/lib64/ld-linux-x86-64.so.2 (0x00007f1083d93000)
Just to make sure, I added path to LD_LIBRARY_PATH as well. Even ran sudo ldconfig. Neither made any difference.
What else can I try?
Per sudoers(5):
By default, the env_reset flag is enabled. This causes commands to be executed with a new, minimal environment.
...
Note that the dynamic linker on most operating systems will remove variables that can control dynamic linking from the environment of set-user-ID executables, including sudo. Depending on the operating system this may include RLD*, DYLD, LD_, LDR_*, LIBPATH, SHLIB_PATH, and others. These type of variables are removed from the environment before sudo even begins execution and, as such, it is not possible for sudo to preserve them.
Your easiest option is probably to do:
sudo LD_LIBRARY_PATH=/mnt/sda1/openssl ./a.out

cannot open shared object file: No such file or directory | including libbpf with userspace program

So in my userspace program I am calling some functions like bpf_object__open_file which are part of libbpf library installed with PKG_CONFIG_PATH=/build/root/lib64/pkgconfig DESTDIR=/build/root make install
So when I compile the it compiles just fine, no error with this command
clang -L /build/root/usr/lib64/ -I /usr/include/ -Wall -o user u.c -lbpf
so these files exists in my /build/root/usr/lib64 directory
libbpf.a libbpf.so libbpf.so.0 libbpf.so.0.7.0 pkgconfig
But when I run the program like
sudo ./user
It throws message that
./user: error while loading shared libraries: libbpf.so.0: cannot open shared object file: No such file or directory
So basically I am creating shared library, giving the path but why running the program not able to find my libbpf.so.0 shared library
can anyone tell why is that the case I am getting message can't find library
As Qeole mentioned in comment
So I did this
root#/dir/# ldd ./user
and it gives me this output without any location where did it tried to find path directory
linux-vdso.so.1 (0x00007ffcd77e7000)
libbpf.so.0 => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9b3943c000)
/lib64/ld-linux-x86-64.so.2 (0x00007f9b39642000)
You should add the libbpf library directory to your LD_LIBRARY_PATH variable.
$ LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/build/root/usr/lib64
$ export LD_LIBRARY_PATH
Then go ahead an run the program. Note that if you run it with sudo, you may also need to set root's LD_LIBRARY_PATH
$ sudo su
# LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/build/root/usr/lib64
# export LD_LIBRARY_PATH
# ./user
You can verify that libbfp was found with the same ldd command.

LD_LIBRARY_PATH doesn't take effect for LibXML.so

XML::LibXML is a Perl library. LibXML.so is a part of the library. I'm trying to make XML::LibXML use custom built libxml2. But providing LD_LIBRARY_PATH doesn't make any difference:
$ ldd ./blib/arch/auto/XML/LibXML/LibXML.so | grep libxml2
libxml2.so.2 => /usr/lib/libxml2.so.2 (0x00007f66af5e9000)
$ LD_LIBRARY_PATH=/home/yuri/_/libxml2/.libs ldd ./blib/arch/auto/XML/LibXML/LibXML.so | grep libxml2
libxml2.so.2 => /usr/lib/libxml2.so.2 (0x00007f2d26ae3000)
$ ldd /usr/lib/python3.7/site-packages/libxml2mod.so | grep libxml2
libxml2.so.2 => /usr/lib/libxml2.so.2 (0x00007f878cbc8000)
$ LD_LIBRARY_PATH=/home/yuri/_/libxml2/.libs ldd /usr/lib/python3.7/site-packages/libxml2mod.so | grep libxml2
libxml2.so.2 => /home/yuri/_/libxml2/.libs/libxml2.so.2 (0x00007f6f8f5d8000)
What am I doing wrong? Is there a way to handle it? The goal is to make Perl script use custom built libxml2 (to investigate some issue).
UPD
$ find /usr/lib -name libxml2.so.2
/usr/lib/libxml2.so.2
/usr/lib/vmware-installer/2.1.0/lib/lib/libxml2.so.2
/usr/lib/vmware-installer/2.1.0/lib/lib/libxml2.so.2/libxml2.so.2
/usr/lib/vmware-vmrc/5.5/lib/libxml2.so.2
/usr/lib/vmware-vmrc/5.5/lib/libxml2.so.2/libxml2.so.2
$ find /usr/local/lib -name libxml2.so.2
tl;dr
ExtUtils::MakeMaker hardcodes paths of the shared libraries in the binaries by means of RPATH entry in .dynamic section. But you can preload the needed library,
$ LD_PRELOAD=path/to/custom/libxml2/.libs/libxml2.so.2 ldd ./blib/arch/auto/XML/LibXML/LibXML.so
or make the loader ignore RPATH entry,
$ LD_LIBRARY_PATH=path/to/custom/libxml2/.libs /usr/lib/ld-linux-x86-64.so.2 --inhibit-rpath /abs/path/to/LibXML.so /abs/path/to/perl ./1.pl
or convert RPATH entry into RUNPATH one,
$ chrpath --convert blib/arch/auto/XML/LibXML/LibXML.so
or delete RPATH entry,
$ chrpath --delete blib/arch/auto/XML/LibXML/LibXML.so
or specify XMLPREFIX variable,
$ perl Makefile.PL XMLPREFIX=path/to/custom/libxml2/build
Under the hood
ExtUtils::Liblist::ext() takes a list of libraries to be linked with, like -lxml2 -lz -llzma -licui18n -licuuc -licudata -lm -ldl, and turns them it into four or five variables, that make their way into the generated Makefile. One of those is LD_RUN_PATH. It contains all the paths where the libraries where found.
$is_dyna is true if the library is dynamic (not ends with .a). $is_perl is true if Perl was not built with it, and %ld_run_path_seen is to not duplicate values in LD_RUN_PATH.
The part that calculates LD_RUN_PATH, that generates part of the Makefile with the variable, and the part that passes it to the linker.
Consequences
LD_RUN_PATH and in its turn RPATH entry in the binary make the loader search for libraries (at runtime) in the directories where they where found (at compile time).
-rpath=dir
Add a directory to the runtime library search path. This is used when linking an ELF executable with shared objects. All -rpath arguments are concatenated and passed to the runtime linker, which uses them to locate shared objects at runtime. The -rpath option is also used when locating shared objects which are needed by shared objects explicitly included in the link; see the description of the -rpath-link option. If -rpath is not used when linking an ELF executable, the contents of the environment variable "LD_RUN_PATH" will be used if it is defined.
https://jlk.fjfi.cvut.cz/arch/manpages/man/core/binutils/ld.1.en
If a shared object dependency does not contain a slash, then it is searched for in the following order:
o Using the directories specified in the DT_RPATH dynamic section attribute of the binary if present and DT_RUNPATH attribute does not exist. Use of DT_RPATH is deprecated.
o Using the environment variable LD_LIBRARY_PATH, unless the executable is being run in secure-execution mode (see below), in which case this variable is ignored.
https://jlk.fjfi.cvut.cz/arch/manpages/man/core/man-pages/ld.so.8.en
$ perl Makefile.PL
$ make
Result,
$ readelf --dynamic ./blib/arch/auto/XML/LibXML/LibXML.so | grep RPATH
0x000000000000000f (RPATH) Library rpath: [/usr/lib]
$ objdump -x ./blib/arch/auto/XML/LibXML/LibXML.so | egrep RPATH
RPATH /usr/lib
$ LD_LIBRARY_PATH=path/to/custom/libxml2/.libs ldd ./blib/arch/auto/XML/LibXML/LibXML.so | grep libxml2
libxml2.so.2 => /usr/lib/libxml2.so.2 (0x00007f6cfabb2000)
--dynamic - display contents of the .dynamic section, -x - display all headers.
Solution
One way to remedy this is to preload needed instance of libxml2,
$ LD_PRELOAD=path/to/custom/libxml2/.libs/libxml2.so.2 ldd ./blib/arch/auto/XML/LibXML/LibXML.so | grep libxml2
path/to/custom/libxml2/build/lib/libxml2.so.2 (0x00007fe183aeb000)
Another is to tell Makefile.PL search for libxml2 in the desired location (at compile time).
$ perl Makefile.PL XMLPREFIX=path/to/custom/libxml2/build
$ make
Result,
$ readelf --dynamic ./blib/arch/auto/XML/LibXML/LibXML.so | grep RPATH
0x000000000000000f (RPATH) Library rpath: [path/to/custom/libxml2/build/lib:/usr/lib]
$ objdump -x ./blib/arch/auto/XML/LibXML/LibXML.so | egrep RPATH
RPATH path/to/custom/libxml2/build/lib:/usr/lib
$ ldd ./blib/arch/auto/XML/LibXML/LibXML.so | grep libxml2
libxml2.so.2 => path/to/custom/libxml2/build/lib/libxml2.so.2 (0x00007fe183aeb000)
On a side note
Should you care to debug XML::LibXML, you can run make with OPTIMIZE variable (add -B to make make rebuild everything in case the library has already been built),
$ make OPTIMIZE='-g3 -O0'
There's also -O2, and no -g in LDDLFLAGS, but not sure if that matters much.
Alternatively, WriteMakeFile takes OPTIMIZE parameter, so you can add a line here,
'OPTIMIZE' => '-g3 -O0'
Or you can add the variable here, and pass it to Makefile.PL,
$ perl Makefile.PL OPTIMIZE='-g3 -O0'
If you install your custom built version of libxml in /usr/local/lib, I think the XML::LibXML module should load the custom one rather than the one in /usr/lib (Assuming you have sudoers or root permission to install it).
I'm not sure why Python seems to use a different lookup path for libraries than Perl does, though. Maybe the full path to libxml library is hardcoded in XML::LibXML's LibXML.so? If the XML::LibXML module was linked with -l/usr/lib/libxml2.so instead of -lxml2 that would be the problem.

why does the linker change shared library name?

Using linker command line options passed via 'node-gyp' I specify that the library path and library name that I want the program to link with. But the resulting executable does not reference the file I specified, it references a different name in /usr/lib.
I'm using the libraries section in binding.gyp to reference a local lib directory.
'libraries': [
'-lao-oboe',
'-L<(module_root_dir)/lib/',
'-Wl,-rpath-link,<(module_root_dir)/lib/',
'-Wl,-rpath,<(module_root_dir)/lib/'
],
node-gyp seems to be passing the options correctly because the linker returns /usr/bin/ld: cannot find -la-oboe if I change the -L path to one that doesn't contain libao-oboe.so. The linker also returns an error if I change the name of the requested library to be different than the one in lib.
The problem is that the local library will not be loaded at runtime. ldd shows that the node-gyp output file is not referencing the file that was specified - it is referencing a library with a different name altogether - /usr/lib/liboboe-1.0.so.1. See the second line of ldd output:
linux-vdso.so.1 => (0x00007ffee20f5000)
liboboe-1.0.so.1 => /usr/lib/liboboe-1.0.so.1 (0x00007fa476377000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fa475ff5000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa475c2b000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fa475a27000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fa47580a000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fa475501000)
/lib64/ld-linux-x86-64.so.2 (0x00007fa476922000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fa4752eb000)
The local library directory contains:
lrwxrwxrwx 1 bruce bruce 15 Sep 8 02:50 libao-oboe.so -> libao-oboe.so.1`
-rw-r--r-- 2 bruce bruce 1640848 Aug 31 15:01 libao-oboe.so.1
It is the case that the local library file, libao-oboe.so.1 is the same as the system library file being referenced in the executable (as shown by ldd): /usr/lib/liboboe-1.0.so.1.
Does the linker somehow notice that the local file is the same (via hashing or some signature) and substituting the library file from the standard location?
Why does node-gyp's output file reference a library file that was never requested as part of the build process?
According to wikipedia - soname the SONAME field in the .so file is the "base compatible version". It's clear from the behavior I found in the problem above that ld inserts the SONAME into the file being linked to the shared library - not the filename specified in the ld command.
Renaming a .so file doesn't change the SONAME so an executable linked to a renamed file will try to load a library named by the SONAME field, not the filename.
My solution was to not rename the file.

what besides object files and shared dlls is required to create an executable

First I describe what I have done.I am using cygwin on windows 7 64 bit.
I first just created simple program in C say famous hello program.Then I compiled using 'gcc' directly to generate 'hello.exe' .Then I checked the dynamically linked libraries using 'ldd' command and checked their paths.
$ ldd hello.exe
ntdll.dll => /cygdrive/c/Windows/SYSTEM32/ntdll.dll (0x77a70000)
kernel32.dll => /cygdrive/c/Windows/system32/kernel32.dll (0x77850000)
KERNELBASE.dll => /cygdrive/c/Windows/system32/KERNELBASE.dll (0x7fefdb40000)
cygwin1.dll => /cygdrive/c/Users/vishu/cygwin1.dll (0x180040000)
Then I created object file 'hello.o' instead of generating executable and then used linker to link object files and the shared libraries to create 'hello1.exe'.But when I tried to run it it showed no output .What I am missing?
Note : When I checked using 'file' command for both the files then output are
$ file hello.exe
hello.exe: PE32+ executable (console) x86-64, for MS Windows
$ file hello1.exe
hello1.exe: PE32+ executable (console) x86-64 (stripped to external PDB), for MS Windows
Edit for comments : I do not think that I have written wrong commands as the shared libraries for two executables is the same.I think the type of two executables is different but I do not know what 'stripped to external PDB' means.But as you are asking I will post them below.I copied all dll to same directory as object file hello.o (It is same object file for hello.exe and hello1.exe).
$ ld -o hello1.exe hello.o ntdll.dll kernel32.dll KERNELBASE.dll cygwin1.dll
Sorry if this is too silly question.

Resources