How to get library path from SONAME? - linker

I'm developing a ldd-python script.
As far as I know, ELF binary itself has information only about library's SONAME.
How can I get library's full path by this SONAME?
I want to print like this original ldd result:
$ ldd test
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7d28000)

ldd prints this information by running ld.so in special mode (LD_TRACE_LOADED_OBJECTS). So your only options are to run ldd internally and parse it's output or try to model it's behavior in Python (but note that such model would have to be quite complex, especially once you get to setuid binaries).

Related

ldd says "not found" for one file, and found for another

I'm trying to gather all the dependencies needed by some .so file. I use the Recursive ldd script, but that doesn't really matter for the manner of sake.
I want to put all the .so files in one directory, say it's in
/home/user/project/lib
I'm having a weird experience: Say there's a file libmat.so which I want to gather all its dependencies. So I ran
/home/user/project/lib$ ldd libmat.so
...
libmwboost_system.so.1.65.1 => /home/user/project/lib/libmwboost_system.so.1.65.1
libmwboost_filesystem.so.1.65.1 => /home/user/project/lib/libmwboost_filesystem.so.1.65.1
...
So we see that ldd recognized the libmwboost_system.so.1.65.1 file in the current directory.
Turns out that libmwboost_filesystem.so.1.65.1.so also depends on libmwboost_system.so.1.65.1,
But when I run:
/home/user/project/lib$ ldd libmwboost_filesystem.so.1.65.1
...
libmwboost_system.so.1.65.1 => not found
...
How come ldd can find it when I run in on libmat.so and can't when I run it on libmwboost_filesystem.so.1.65.1 ?
I would be glad if someone could provide an explanation in the context of the linking process. As far as I know, when you link a file against a library, you use the following flags:
~$ gcc my_program.c -Lpath/to/solib/for/static/linker -lnameoflib -wl,-rpath=path/to/solib/for/dynamic/linker
This -Wl,-rpath flag embeds in the executable the path of the library that the dynamic linker will search for at run time. In the case of a shared library that depends on other libraries - does it work the same?
Ok so that's how it works:
When linking a binary - whether it's an executable or another shared library - against a shared library, the static linker embeds in the binary the names of the libraries we linked against. Those libraries will be loaded by the dynmaic linker - ld.so - once the program is run by the user.
Now the question is where the dynamic linker will search for these libraries at runtime. Briefly, according to the man page of ld.so, when the dynamic linker first inspects the binary to resolve its dependencies, it goes through the strings of the dependencies. If a string contains a slash "/" then the string is interpreted as a path. This can happen if the library name was specified with a slash at link time, like this:
~$ gcc prog.c ../path/to/library.so
In this case, the string embeded in the executable will be: ../path/to/library.so and the dynamic linker will search it relatively to the location of the binary.
If not, it will search for the library in a list of locations (the whole list is specified in the man page). The first location is the directories specified in the DT_RUNPATH section attribute of the binary. This can be set at link time using the -Wl,-rpath flag:
~$ gcc prog.c -Lpath/of/lib/ -lmylib -Wl,-rpath=path/of/lib
In this case, the path/of/lib will be searched for the library by the dynamic library.
You can inspect this attribute using readelf:
~$ readelf -d binary | grep RUNPATH
In my case, the libmat.so library contained a RUNPATH attribute set to $ORIGIN, meaning that libraries will be searched in the same location of the binary library. Whereas, the libmwboost_filesystem.so.1.65.1 didn't have this attribute set, that is why ldd didn't find the library.
ldd is just using ld.so to try and load the libraries, and shows where it found them, according to the search path specified in the ld.so man page.

Setting shared libraries prefix when compiling

I'm trying to compile a program, I need to set a prefix path on shared library path, I try using -Wl,-rpath -Wl,-dynamic-linker and what I got from ldd was:
linux-vdso.so.1 => (0x00007fff75336000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f19d55b8000)
/pathtolib/lib64/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x000055d3d67e1000)
but what I expecting to get was:
linux-vdso.so.1 => (0x00007fff75336000)
libc.so.6 => /pathtolib/lib/x86_64-linux-gnu/libc.so.6 (0x00007f19d55b8000)
/pathtolib/lib64/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x000055d3d67e1000)
I really need to setup this and force program to use non-default path (/pathtolib) for all shared libraries.
this is the command I used to compile:
gcc list/list.c -o bin/list -Wl,-rpath,/pathtolib -Wl,--dynamic-linker,/pathtolib/lib64/ld-linux-x86-64.so.2
what did I do wrong?!! :|
The -rpath option does not specify a prefix for individual directories in the search path; rather, it specifies a colon-delimited list of paths. The dynamic linker searches these directories first, but it continues on to the rest of the search path if it needs to do. Thus, if you want the dynamic linker to resolve libc.so.6 to /pathtolib/lib/x86_64-linux-gnu/libc.so.6, then you must specify not /pathtolib but /pathtolib/lib/x86_64-linux-gnu in your rpath.
I am not aware of a link option or ELF header field that would instruct the dynamic linker to do what you seem to be asking: to modify the standard library search path by prefixing each element. If you want to do something like that, then you probably need to provide your own dynamic linker.
Alternatively, perhaps you want to construct and use a chroot environment instead of of messing with rpaths. That requires considerably more work to set up, but it would have the effect of allowing you to use a whole different set of libraries.

Executable cannot find dynamically linked mkl library, but ldd does

I have a code which was dynamically linked with the mkl library. when running the code, it reports mkl not found.
./bmdl
/g/software/EMTO/5.7/intel_12.1/ser/bin/bmdl: error while loading shared libraries: libmkl_intel_lp64.so: cannot open shared object file: No such file or directory
But when I use ldd to check the dynamically linked libraries in the executable, it shows the mkl library is found
ldd bmdl
libmkl_intel_lp64.so => /g/software/intelXE/composer_xe_2011_sp1/mkl/lib/intel64/libmkl_intel_lp64.so (0x00002b975d76d000)
libmkl_sequential.so => /g/software/intelXE/composer_xe_2011_sp1/mkl/lib/intel64/libmkl_sequential.so (0x00002b975df53000)
libmkl_core.so => /g/software/intelXE/composer_xe_2011_sp1/mkl/lib/intel64/libmkl_core.so (0x00002b975e631000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x0000003785600000)
libm.so.6 => /lib64/libm.so.6 (0x0000003784e00000)
libc.so.6 => /lib64/libc.so.6 (0x0000003784a00000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x000000378a600000)
libdl.so.2 => /lib64/libdl.so.2 (0x0000003785200000)
/lib64/ld-linux-x86-64.so.2 (0x0000003784600000)
Any idea what could be wrong?
Output from readelf -l ./bmdl
Elf file type is EXEC (Executable file)
Entry point 0x4034b0
There are 8 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x00000000000001c0 0x00000000000001c0 R E 8
INTERP 0x0000000000000200 0x0000000000400200 0x0000000000400200
0x000000000000001c 0x000000000000001c R 1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x00000000000e4eb4 0x00000000000e4eb4 R E 200000
....
More debugging
$ export LD_DEBUG=libs,files
$ ./bmdl
./bmdl: error while loading shared libraries: libmkl_intel_lp64.so: cannot open shared object file: No such file or directory
$ ldd ./bmdl
15133:
15133: file=libtermcap.so.2 [0]; needed by /bin/sh [0]
15133: find library=libtermcap.so.2 [0]; searching
15133: search path=/g/software/intelXE/composer_xe_2011_sp1/mkl/lib/intel64/tls/x86_64:/g/software/intelXE/composer_xe_2011_sp1/mkl/lib/intel64/tls:/g/software/intelXE/composer_xe_2011_sp1/mkl/lib/intel64/x86_64:/g/software/intelXE/composer_xe_2011_sp1/mkl/lib/intel64:/g/software/intelXE/composer_xe_2011_sp1/lib/intel64/tls/x86_64:/g/software/intelXE/composer_xe_2011_sp1/lib/intel64/tls:/g/software/intelXE/composer_xe_2011_sp1/lib/intel64/x86_64:/g/software/intelXE/composer_xe_2011_sp1/lib/intel64 (LD_LIBRARY_PATH)
....
Seems LD_DEBUG doesn't have effect on running ./bmdl alone.
I just realized the old bmdl has a 'setgid' flag and my new built doesn't have it. Maybe that was the cause?
-rwxr-sr-x 1 root gants 1123992 Jul 23 16:14 /scratch/helpdesk/bmdl
I removed the setgid bit from the old bmdl and running ./bmdl doesn't complain about library not found. Now the question is why setgid can interfere with dynamically linked library?
It happens that setgid with a dynamically linked executable can cause security problems and is greatly limited by GNU glibc. For example, LD_LIBRARY_PATH will be ignored. Maybe the old built never worked before?!
Using MKL reqires env variables including INCLUDE, MKLROOT, LD_LIBRARY_PATH, LIBRARY_PATH, CPATH, FPATH and NLSPATH being properly set.
This can be done by a single script provided by Intel.
If you use intel compiler,
$ source ${intel_dir}/bin/compilervars.sh intel64
If you use MKL only with gcc compiler,
$ source ${intel_dir}/mkl/bin/mklvars.sh intel64
You can add this cmd line to your .bashrc so you don't need to run it every time.
The code is dynamically linked and has the setgid attribute. setgid with a dynamically linked executable can cause security problems and is greatly limited by GNU glibc. For example, LD_LIBRARY_PATH will be ignored. That is why the code keeps on complaining some shared library not found.
But when I use ldd to check the dynamically linked libraries in the executable, it shows the mkl library is found
It is highly improbable that you've told us the whole story, because ldd (on Linux) is just a small shell script around ld-linux.so. If ld-linux.so can find the shared library when invoked by ldd, ld-linux.so should also be able to do that when the executable is invoked directly (ld-linux.so is what actually maps shared libraries when you run a.out).
The only plausible explanations I can think of:
you execute bmdl in a different environment from the one you execute ldd in, or
you have a modified ldd on your PATH, which perhaps modifies the environment before running "real" ldd.
How do I know which linker is used?
readelf -l bmdl
and look for "Requesting program interpreter".
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2].
Very strange.
The next debugging suggestion: set LD_DEBUG=libs,files and see where ld-linux is searching. You can do that for both ldd and bmdl, and see where the difference comes from.

GLIBC dependancy

I have a binary which needs some *.so files to execute.
Now when I try to execute it on some older machines it shows
/lib/libc.so.6: version `GLIBC_2.4' not found
how can I change its search path to /lib/i386-linux-gnu/libc.so.6 from /lib/libc.so.6
So I can run two different libc files on a same machine.
Are you on a 32-Bit system and maybe trying to execute a binary that uses the 64 bit glibc?
Modifying the library search path can be done by using LD_LIBRARY_PATH variable, e.g. in a subshell:
(export LD_LIBRARY_PATH=/lib/i386-linux-gnu:${LD_LIBRARY_PATH}; my_program)
You can change the search path by using the LD_LIBRARY_PATH environment variable when calling your binary.
Something along the lines of:
LD_LIBRARY_PATH=/lib/i386-linux-gnu/libc.so.6 ./your_binary
should work. Bear in mind that depending on the shell you're using you might need to call either export or env to set the variable.
You can check if it's working using the following command:
LD_LIBRARY_PATH=/lib/i386-linux-gnu/libc.so.6libc.so.6 ldd ./your_binary
linux-vdso.so.1 => (0x00007fff140e9000)
libselinux.so.1 => /lib/libselinux.so.1 (0x008f9000)
librt.so.1 => /lib/librt.so.1 (0x006f1000)
libacl.so.1 => /lib/libacl.so.1 (0x004e8000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x00129000)
libdl.so.2 => /lib/libdl.so.2 (0x00f25000)
/lib/ld-linux.so.2 (0x003b3b000)
libpthread.so.0 => /lib/libpthread.so.0 (0x00d07000)
libattr.so.1 => /lib/libattr.so.1 (0x00b02000)
You just need to check if libc.so.6 is being resolved to the shared object that you want.
UPDATE: It seems that you want to load a 32 bit shared object for a 64 bit binary. As far as I know there is no way to do this since the target architectures are different and the loader will refuse to load the 32 bit so. If this is your case, this might explain why the loader loads the default libc. Depending on your case, it might be possible to compile the binary as 32 bits, in which case it should run with a 32 bit libc.
If you want to run 32-bit executables on a 64-bit machine, you'll need to install the 32 bit versions. On Fedora or other systems with yum run:
yum install glibc.i686
(note the .i686 suffix, it asks specifically for the 32 bit versions) and try again. The ldd(1) command should help identifying the needed libraries, and yum should be smart enough to find them by the name it gives.

Dependency resolution in Linux

Under Windows I have used a program called dependency walker to examine the libraries the application is using. I was wondering how I can achieve this on Linux for a standard binary:
ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.0, stripped
Thanks.
Try:
ldd executable
For example:
[me#somebox ~]$ ldd /bin/ls
linux-gate.so.1 => (0xb7f57000)
librt.so.1 => /lib/tls/i686/cmov/librt.so.1 (0xb7f4c000)
libselinux.so.1 => /lib/libselinux.so.1 (0xb7f32000)
libacl.so.1 => /lib/libacl.so.1 (0xb7f2b000)
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7ddc000)
libpthread.so.0 => /lib/tls/i686/cmov/libpthread.so.0 (0xb7dc4000)
/lib/ld-linux.so.2 (0xb7f58000)
libdl.so.2 => /lib/tls/i686/cmov/libdl.so.2 (0xb7dc0000)
libattr.so.1 => /lib/libattr.so.1 (0xb7dbb000)
[me#somebox ~]$
Note that this will only report shared libraries. If you need to find out what static libraries were linked in at compile time, that's a bit trickier, especially seeing as your executable is 'stripped' (no debugging symbols).
Use ldd
ldd /bin/sh
If you want something a little less raw than iteratively calling ldd and somewhat more like MSVC depends, you should try Visual-ldd. It hasn't been updated in 4 years, but it should still work given that the ELF format hasn't changed. It still won't show you individual symbols inside those libraries - for that you'll need something like nm, and I don't know of any GUI wrapper for that, unfortunately.
Use ldd. It will show the dynamic libraries the binary needs.
Note that the libraries themselves may in turn need more libraries. To get these, you can run ldd on the libraries you got from running ldd on the binary.

Resources