How does ldd knows it's depending on libc.so.6 ,not libc.so.5 or libc.so.7?
libc.so.6 => /lib64/libc.so.6 (0x00000034f4000000)
/lib64/ld-linux-x86-64.so.2 (0x00000034f3c00000)
It is recorded inside application binary itself (specified at compile time, more exactly at link step, done with ld):
$ readelf -d /bin/echo
Dynamic section at offset 0x5f1c contains 21 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libc.so.6]
...
(there are some additional columns for how elf does store information in dynamic section. but you can see that libc.so.6 is hardcoded with .6 suffix because of SONAME)
or even without any knowledge of ELF file format:
$ strings /bin/echo |grep libc.so
libc.so.6
To find, how does linker find a library (it is done at final step of compilation), use gcc option -Wl,--verbose (this asks gcc to pass option --verbose to ld):
$ gcc a.c -Wl,--verbose
...
attempt to open /usr/lib/gcc/i686-pc-linux-gnu/4.4.4/libc.so failed
attempt to open /usr/lib/gcc/i686-pc-linux-gnu/4.4.4/libc.a failed
attempt to open /usr/lib/gcc/i686-pc-linux-gnu/4.4.4/libc.so failed
attempt to open /usr/lib/gcc/i686-pc-linux-gnu/4.4.4/libc.a failed
attempt to open /usr/lib/gcc/i686-pc-linux-gnu/4.4.4/../../../libc.so succeeded
opened script file /usr/lib/gcc/i686-pc-linux-gnu/4.4.4/../../../libc.so
opened script file /usr/lib/gcc/i686-pc-linux-gnu/4.4.4/../../../libc.so
attempt to open /lib/libc.so.6 succeeded
/lib/libc.so.6
Linker doesn't know anything about .digit suffix, it just iterate over all library search directories trying to open libLIBNAME.so and libLIBNAME.a, where LIBNAME is a string after -l option. ( -lc option is added by default).
First success is /usr/lib/libc.so which itself is not a library, but a linker script (text file). Here is content from typical libc.so script:
$ cat /usr/lib/libc.so
/* GNU ld script
Use the shared library, but some functions are only in
the static library, so try that secondarily. */
OUTPUT_FORMAT(elf32-i386)
GROUP ( /lib/libc.so.6 /usr/lib/libc_nonshared.a AS_NEEDED ( /lib/ld-linux.so.2 ) )
So, script /usr/lib/libc.so is found earlier than actual library, and this script says, what file will be linked, libc.so.6 in this case.
In more common case, lib___.so is symlink to some version like lib___.so.3.4.5 and there is SONAME field filled in lib___.so.3.4.5 which says to ld link not to lib___.so but to lib___.so.3.4 which is another symlink to lib___.so.3.4.5. The .3.4 name will be recorded in NEEDED field of binary.
http://www.sco.com/developers/gabi/latest/ch5.dynamic.html#dynamic_section
Has the meaning of each dynamic tags. The 1 indicates it is a DT_NEEDED tag meaning in this case the
typedef struct {
Elf32_Sword d_tag;
union {
Elf32_Word d_val;
Elf32_Addr d_ptr;
} d_un;
} Elf32_Dyn;
structure has d_val union valid and look up at an offset specified by thi union member in DT_STRTAB table to find the name of library that this binary/SO depends on.
Related
On our project at work, where I use a build system managed by another team, I am running into an issue where I am able to compile and link the binary, but then when executing it, it complains about being unable to load a shared object file, it should never have been linked against:
/home/me/build/bin/executable: error while loading shared libraries: libicui18n.so.52.1: cannot open shared object file: No such file or directory
The curious part is, that the only instance of the file name libicui18n.so.52.1 I can even find on my machine are /opt/onlyoffice/desktopeditors/libicui18n.so.52.1 and /home/me/.local/opt/foxitsoftware/foxitreader/lib/libicui18n.so.52.1, which I definitely don't link against, at least according to linker command executed by the build system:
It does not occur as any of the explicit /absolute/path/to/libsomething.so.42.3 files.
Nothing related is given as -L option.
Nothing related is given as /absolute/path/to/libsomething.a file.
Nothing related is in $LD_LIBRARY_PATH or $LIBRARY_PATH.
The Intel Fortran compiler has no reason to include those in the default paths.
So now I am wondering, how else the libicui18n.so.52.1 may be linked against, and how I may go about debugging such an issue.
how else the libicui18n.so.52.1 may be linked against
ELF libraries can specify "what name should be used at runtime" independently of the name of the library itself. Example:
gcc -fPIC -shared -o libt.so t.c -Wl,--soname=libfoo.so.1.2.3
gcc main.c -L. -lt
./a.out
./a.out: error while loading shared libraries: libfoo.so.1.2.3: cannot open shared object file: No such file or directory
Here, the library is built in a way which requires libfoo.so.1.2.3 at runtime, although no such library exists. It is up to the packaging system to provide such library (or a symlink).
You can examine a library to see what name it expects to be used at runtime like so:
readelf -d libt.so | grep SONAME
0x000000000000000e (SONAME) Library soname: [libfoo.so.1.2.3]
You can examine the list of libraries a.out requires at runtime like so:
readelf -d ./a.out | grep NEEDED
0x0000000000000001 (NEEDED) Shared library: [libfoo.so.1.2.3]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
how I may go about debugging such an issue.
As above. Your next question is probably "how can I fix this issue?".
Since libicui18n.so.52.1 is installed, but not installed into a directory which the dynamic linker will search by default, all you have to do is tell your binary to add the non-standard /opt/onlyoffice/desktopeditors directory the list of directories to search.
You do that by adding -Wl,-rpath=/opt/onlyoffice/desktopeditors to your executable's link line.
You can also add /opt/onlyoffice/desktopeditors to LD_LIBRARY_PATH, but using -Wl,-rpath is preferred.
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.
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.
Here is a Hello World code in C:
// a.c
#include <stdio.h>
int main() {
printf("Hello world\n");
return 0;
}
I compile it as gcc a.c, which produces a.out as expected and ./a.out prints Hello world... as expected.
Now if I do the compile and link separately:
gcc -c a.c; ld -lc a.o, it run the a.out produced as ./a.out I get the message:
bash: ./a.out: No such file or directory
I Googled that error and it seems that happens when the executable produced is a 32-bit ELF and the machine architecture is 64-bit.
I'm running a 64-bit machine and running file a.out gives:
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), not stripped
Why does this happen?
EDIT:
Output of uname -m
$ uname -m
x86_64
Output of ldd a.out
$ ldd a.out
linux-vdso.so.1 => (0x00007ffeeedfb000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa13a7b8000)
/lib64/ld-linux-x86-64.so.2 (0x00007fa13abab000)
gcc a.c produces a.out which runs correctly.
ld -lc a.o
There are several things wrong with this command line:
In general, user-level code should never use ld directly, and always use appropriate compiler front end (gcc here) to perform the link.
As you have discovered, the link command line that gcc constructs is quite complicated, and the command line that you've accepted in Joan Esteban's answer is wrong.
If you want to see the actual link command, examine output from gcc -v a.o.
Also note that link command changes significantly when you change gcc command only slightly (e.g. some OSes require different crt1.o depending on whether you are linking multi-threaded executable or not), and the command line is always OS-specific (which is one more reason to never use ld directly).
Libraries should follow object files on command line. So ld -lc a.o is never correct, and should always be (a variant of) ld a.o -lc. Explanation.
Link dynamic executables with gcc foo.o (to use the right paths for CRT and libc, and the dynamic linker / ELF interpreter ld-linux-x86-64.so.2).
Or gcc -nostartfiles foo.o for libc but not CRT _start, if you have a hand-written _start
(For static executables without libc or CRT, you can use ld directly or gcc -nostdlib -static.)
gcc -v foo.o will show you the actual paths GCC used on your system.
The other answers only address how to avoid this1, not the actual question of what happened.
The gcc -c a.c; ld -lc a.o commands you gave produce a pretty obvious warning:
ld: warning: cannot find entry symbol _start; defaulting to 0000000000400260
So even if this file could be executed, it will probably crash right away. See #EmployedRussian's answer for an explanation of what you should have done.
The question of why it can't even be executed is still interesting:
$ strace ./a.out
execve("./a.out", ["./a.out"], [/* 72 vars */]) = -1 ENOENT (No such file or directory)
execve(2) returns ENOENT because it can't find the interpreter (which I figured out from file and so on, see below). You'd get the same error from trying to run a file that started with
#!/usr/non-existant-path/bin/bash
As you discovered, the usual reason for this error message is when running an ELF binary on a system without the right dynamic linker and dynamic libraries installed (e.g. a 64bit system without 32bit support installed). In your case, it's because you used a bad link command and made a dynamic executable with a bad interpreter path.
I'm on Ubuntu 15.10, where GNU file version 5.22 reports:
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib/ld64.so.1, not stripped
There is no /lib/ld64.so.1 on my system. ldd output is confusing, because ldd uses its default ELF interpreter, not the one specified by the binary.
$ ldd a.out
linux-vdso.so.1 => (0x00007ffc18d2b000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0e0a79f000)
/lib/ld64.so.1 => /lib64/ld-linux-x86-64.so.2 (0x0000559dbc9d2000)
So it assumes that the runtime interpreter in the binary resolved to the one ldd used itself, I guess.
Your ldd output is probably from an old version too, since it just shows /lib64/ld-linux-x86-64.so.2 for that line. Not taking a bad guess is probably better behaviour, for a weird case like this, but doesn't help you see that your binary has a weird interpreter path.
readelf -l a.out
will decode the ELF headers for you, including the interpreter path. (Thanks to #EmployedRussian's comment for pointing this out.)
Use that:
ld -o a.out -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o -lc c.o /usr/lib/crtn.o
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?