How to set the path that a .so library will search for other .so libraries?

Dan picture Dan · Apr 2, 2015 · Viewed 10.2k times · Source

I have a libA.so that depends on libB.so, which is located at ../libB/ (from libA.c). I'm trying to compile things in such a way that I don't have to set any environment variables. I have:

    cc -std=c99 -c -fPIC -I../libB/ -Wall libA.c
    cc -std=c99 -shared libA.o -L../libB -lB -o libA.so

This compiles fine. When I run a program that loads libA with dlopen I get:

dyld: Library not loaded: libB.so
  Referenced from: libA/libA.so
  Reason: image not found
Trace/BPT trap: 5

so libA is not finding libB at runtime. I found this solution to change the runtime path on Mac OS X:

install_name_tool -change libB.so @loader_path/../libB.so libA.so

but I'd like to find a solution that would work on both OS X and Linux. Again, I'm trying to make the end-user do as little as possible so I don't want them to have to set environment variables, and I have to use cc (which for me is Apple LLVM version 4.2 (clang-425.0.27) (based on LLVM 3.2svn), and I'd like for it to work on Linux too, so presumably cc=gcc there).

EDIT My problem may be more complicated than I realized. I'm making this dynamic library in C, but trying to use it from within python. I can use libB.so (which has no dependencies) from within python no problem, and when I load libA.so from within python it finds it (see error above), it's just that at that point libA.so realizes it doesn't know where to find libB.so. If I understand your answers correctly below, the solutions depend on setting the linker path when you compile the executable, which in my case is in python.

Is there no way to tell libA.so where to look for libB.so when I compile it? I can do it afterward with install_name_tool on OSX, but is there not a way with the compiler that would work on both OSX and linux?

Answer

David C. Rankin picture David C. Rankin · Apr 4, 2015

The bottom line is that your final executable must know where your library resides. You can accomplish that 2 ways (1) exporting the LD_LIBRARY_PATH that includes the path to your library, or (2) using rpath so your executable knows where to find your library. Exporting the LD_LIBRARY_PATH generally looks something like this:

LD_LIBRARY_PATH=/path/to/your/lib:${LD_LIBRARY_PATH}
export LD_LIBRARY_PATH

I prefer to use rpath. To use rpath compile your library as normal (example below for my extended test function library libetf.so)

gcc -fPIC -Wall -W -Werror -Wno-unused -c -o lib_etf.o lib_etf.c
gcc -o libetf.so.1.0 lib_etf.o -shared -Wl,-soname,libetf.so.1

Then to compile an executable making use of this library, you compile to object, then link the object with rpath given as a linker option. You would provide a path for both libA.so and libB.so in your case. Building my testso executable:

gcc -O2 -Wall -W -Wno-unused -c -o testetf.o testetf.c
gcc -o testso testetf.o -L/home/david/dev/src-c/lib/etf -Wl,-rpath=/home/david/dev/src-c/lib/etf -letf

Use ldd to confirm that the executable has correctly located your library:

$ ldd testso
        linux-vdso.so.1 (0x00007fffd79fe000)
        libetf.so.1 => /home/david/dev/src-c/lib/etf/libetf.so.1 (0x00007f4d1ef23000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f4d1eb75000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f4d1f126000)

Note: libetf.so.1 points to /home/david/dev/src-c/lib/etf/libetf.so.1.