1. Overview

The environment variables LIBRARY_PATH and LD_LIBRARY_PATH, whose names look similar, may sometimes confuse programmers. They may try to use one instead of the other. However, these environment variables have different purposes.

In this tutorial, we’ll discuss with examples the difference between the LIBRARY_PATH and LD_LIBRARY_PATH environment variables.

2. Brief Information About LIBRARY_PATH and LD_LIBRARY_PATH

LIBRARY_PATH is an environment variable that affects gcc during linking, after compiling source codes and generating object files. It’s a list of colon-separated directories like the PATH environment variable.

In addition to the standard default directories like /usr/lib or /usr/local/lib, the GNU linker, ld, also uses the directories in LIBRARY_PATH while searching for libraries in the linking phase.

LD_LIBRARY_PATH, on the other hand, is used at runtime.

In addition to a set of standard directories, the dynamic linker, ld.so, also uses LD_LIBRARY_PATH for finding shared libraries before loading them to the address space of the process.

3. Example Setup

We’ll use a shared library from a C program in our example.

We’ll use the following file, foo.c, for generating the shared library:

#include <stdio.h>
#include "foo.h"

void foo() {
    printf("foo\n");
}

The foo() function in the source code prints just foo and exits.

Let’s look at the header file, foo.h:

#ifndef _FOO_H_
#define _FOO_H_

void foo();

#endif

The header file only has the declaration of the function foo().

At this point, we have the following files in the current directory, /home/alice/foo:

$ pwd
/home/alice/foo 
$ ls
foo.c  foo.h

Now, let’s build the shared library using gcc:

$ gcc -shared -fPIC -o libfoo.so foo.c

We must use the -shared option of gcc to build a shared library. In addition to this option, we must also use the -fPIC option while building a shared library to generate position-independent code. The -fPIC option makes the generated machine code independent of being located at a specific address.

The -o option of gcc specifies the name of the generated library, which is libfoo.so in our example.

Let’s check the files in the current directory once more:

$ ls
foo.c  foo.h  libfoo.so

As is apparent from the output, we built the library, libfoo.so, successfully.

We’ll use the generated library in another C program, main.c, which is in the /home/alice/main directory:

#include "foo.h"

int main()
{
    foo();

    return 0;
}

This program calls the function foo() and then exits. The foo() function is in libfoo.so.

4. Example Using LIBRARY_PATH

Now, it’s time to build the program, main.c, using gcc:

$ pwd
/home/alice/main
$ ls 
main.c
$ gcc -o main main.c -I/home/alice/foo -lfoo
/usr/bin/ld: cannot find -lfoo
collect2: error: ld returned 1 exit status

The –o option specifies the name of the executable as main. We specify the path of the included header file in main.c, namely foo.h, using –I/home/alice/foo. Finally, we specify the shared library that the linker must link with the executable using –lfoo.

However, we were unsuccessful in building the executable main since the linker couldn’t find the shared library libfoo.so. collect2 is a wrapper over linker ld that is called by gcc.

Let’s set the LIBRARY_PATH environment variable:

$ export LIBRARY_PATH=/home/alice/foo
$ gcc -o main main.c -I/home/alice/foo -lfoo
$ ls
main  main.c

This time, we were successful in generating the executable main. Therefore, we can use the environment variable LIBRARY_PATH during linkage to find the libraries that we need to link to the executable.

If there’s more than one library that must be linked to the executable and these libraries reside in different directories, then we must separate each directory in the LIBRARY_PATH using colons.

In fact, we have another option to specify the path of the library using the –L option of gcc:

$ unset LIBRARY_PATH
$ rm main
$ gcc -o main main.c -I/home/alice/foo -L/home/alice/foo -lfoo
$ ls
main  main.c

We first unset LIBRARY_PATH using the unset LIBRARY_PATH command and then removed the existing executable using rm main.

We were again successful in building the executable main. However, we specified the path of libfoo.so using -L/home/alice/foo in this case. The -L option adds the specified path to the paths that the linker searches for finding libraries.

Our example used a shared library, but the two methods are also applicable in the case of using static libraries.

5. Example Using LD_LIBRARY_PATH

Now that we built the executable, we can run it:

$ ./main
./main: error while loading shared libraries: libfoo.so: cannot open shared object file: No such file or directory

We weren’t successful in running it as it couldn’t find the library libfoo.so. Now, it’s time to use the environment variable LD_LIBRARY_PATH.

The LD_LIBRARY_PATH environment variable is for finding the shared libraries during runtime. It’s a colon-separated list of directories.

Let’s add the directory /home/alice/foo to LD_LIBRARY_PATH, and then run main once more:

$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/alice/work
$ ./main
foo

This time, the program called the function foo() in the shared library, libfoo.so, successfully.

In the case of using static libraries, we don’t have to update the LD_LIBRARY_PATH as the static libraries are part of the executable.

6. Conclusion

In this article, we discussed the difference between two environment variables, LIBRARY_PATH and LD_LIBRARY_PATH.

First, we learned that LIBRARY_PATH is for specifying the paths of the libraries while linking the libraries to the executable. Then, we discussed LD_LIBRARY_PATH, which is useful for specifying the paths of shared libraries during runtime. Finally, we looked at two examples showing the usage of these two environment variables.