What is the difference between LD_LIBRARY_PATH and -L at link time?

Dan picture Dan · Dec 15, 2009 · Viewed 30.2k times · Source

I'm having problems with LD_LIBRARY_PATH at link time (this question has nothing to do with run time).

The link line looks like this when I run make (this is a Linux system using g++ version 4.1.x):

g++ a.o b.o c.o -o myapp \
 -L/long/path/to/libs/ \
 -L/another/long/path/ \
 -labc -ldef -lghi

The -l options reference shared libraries (e.g., libabc.so) which exist in the directories specified by the -L options. Those directories also appear in LD_LIBRARY_PATH. With that configuration, the link is successful, and I can run the application.

If I remove the directories from LD_LIBRARY_PATH, then I get a single error line such as:

/usr/bin/ld: cannot find -labc

On the other hand, if I remove the directories from the list of -L options, then I get many warnings such as:

/usr/bin/ld: warning: libabc.so, needed by /long/path/to/libs/libxyz.so,
    not found (try using -rpath or -rpath-link)

and then many more errors, such as:

/long/path/to/libs/libdef.so: undefined reference to `Foo::Bar<Baz>::junk(Fred*)'

Can someone explain the difference between LD_LIBRARY_PATH and -L? I would like to understand this stuff in depth, so references are greatly appreciated!

Also, what do I have to add to the link line to avoid using LD_LIBRARY_PATH?

EDIT: When directories were missing from -L, the compiler suggested to "try using -rpath or -rpath-link". I don't think I've ever seen those options in a makefile before. Have you? Not sure if that would help the LD_LIBRARY_PATH problem though.

Answer

John Ewart picture John Ewart · Dec 15, 2009

There are two answers to this question, part of the answer lies in the compile-time linking (i.e gcc -lfoo -L/usr/lib ... which in turn calls ld), and run-time linker lookups.

When you compile your program, the compiler checks syntax, and then the linker ensures that the symbols required for execution exist (i.e variables / methods / etc), among other things. LD_LIBRARY_PATH, as has been noted, has the side-effect of altering the way gcc/ld behave as well as the way the the run-time linker behaves by modifying the search path.

When you run your program, the run-time linker actually fetches the shared libraries (on disk or from memory if possible), and loads in the shared symbols / code / etc. Again, LD_LIBRARY_PATH affects this search path implicitly (sometimes not a good thing, as has been mentioned.)

The correct fix for this without using LD_LIBRARY_PATH on most Linux systems is to add the path that contains your shared libraries to /etc/ld.so.conf (or in some distributions, create a file in /etc/ld.so.conf.d/ with the path in it) and run ldconfig (/sbin/ldconfig as root) to update the runtime linker bindings cache.

Example on Debian:

jewart@dorfl:~$ cat /etc/ld.so.conf.d/usrlocal.conf 
/usr/local/lib

Then when the program is executed, the run-time linker will look in those directories for libraries that your binary has been linked against.

If you want to know what libraries the run-time linker knows about, you can use:

jewart@dorfl:~$ ldconfig -v 

/usr/lib:
libbfd-2.18.0.20080103.so -> libbfd-2.18.0.20080103.so
libkdb5.so.4 -> libkdb5.so.4.0
libXext.so.6 -> libXext.so.6.4.0

And, if you want to know what libraries a binary is linked against, you can use ldd like such, which will tell you which library your runtime linker is going to choose:

jewart@dorfl:~$ ldd /bin/ls
linux-vdso.so.1 =>  (0x00007fffda1ff000)
librt.so.1 => /lib/librt.so.1 (0x00007f5d2149b000)
libselinux.so.1 => /lib/libselinux.so.1 (0x00007f5d2127f000)
libacl.so.1 => /lib/libacl.so.1 (0x00007f5d21077000)
libc.so.6 => /lib/libc.so.6 (0x00007f5d20d23000)