77

I have an executable which needs to link with libtest.so dynamically,so I put them in the same directory,then :

cd path_to_dir
./binary

But got this:

error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory

How can it be unable to find libtest.so which is already in the same directory as the executable itself?

9 Answers 9

47

The loader never checks the current directory for shared objects unless it is explicitly directed to via $LD_LIBRARY_PATH. See the ld.so(8) man page for more details.

6
  • echo $LD_LIBRARY_PATH is empty on my machine:(
    – linuxer
    Jun 10, 2011 at 8:58
  • So ld can work well without $LD_LIBRARY_PATH,what's it for then?
    – linuxer
    Jun 10, 2011 at 8:59
  • 3
    It specifies additional directories for the loader to look in for libraries. Jun 10, 2011 at 9:00
  • 4
    Paths in *nix are separated by a colon (:), not a semicolon. Jun 10, 2011 at 9:18
  • 6
    LD_LIBRARY_PATH is generally a poor choice in production. It is good for quick hacks, and things like helping uninstalled binaries find their shared libraries when running unit tests (think ./configure; make; make check). When building your binary, you can either put your library in a standard location (listed at /etc/ld.so.conf) or pass the -R flag to the linker to let the binary know where to look.
    – automaciej
    Jun 27, 2012 at 4:21
79

While you can set LD_LIBRARY_PATH to let the dynamic linker know where to look, there are better options. You can put your shared library in one of the standard places, see /etc/ld.so.conf (on Linux) and /usr/bin/crle (on Solaris) for the list of these places

You can pass -R <path> to the linker when building your binary, which will add <path> to the list of directories scanned for your shared library. Here's an example. First, showing the problem:

libtest.h:

void hello_world(void);

libtest.c:

#include <stdio.h>
void hello_world(void) {
  printf("Hello world, I'm a library!\n");
}

hello.c:

#include "libtest.h"
int main(int argc, char **argv) {
  hello_world();
}

Makefile (tabs must be used):

all: hello
hello: libtest.so.0
%.o: %.c
        $(CC) $(CFLAGS) -fPIC -c -o $@ $<
libtest.so.0.0.1: libtest.o
        $(CC) -shared -Wl,-soname,libtest.so.0 -o libtest.so.0.0.1 libtest.o
libtest.so.0: libtest.so.0.0.1
        ln -s $< $@
clean:
        rm -f hello libtest.o hello.o libtest.so.0.0.1 libtest.so.0

Let's run it:

$ make
cc  -fPIC -c -o libtest.o libtest.c
cc -shared -Wl,-soname,libtest.so.0 -o libtest.so.0.0.1 libtest.o
ln -s libtest.so.0.0.1 libtest.so.0
cc     hello.c libtest.so.0   -o hello
$ ./hello 
./hello: error while loading shared libraries: libtest.so.0: cannot open shared object file: No such file or directory

How to fix it? Add -R <path> to the linker flags (here, by setting LDFLAGS).

$ make clean
(...)
$ make LDFLAGS="-Wl,-R -Wl,/home/maciej/src/tmp"
(...)
cc   -Wl,-R -Wl,/home/maciej/src/tmp  hello.c libtest.so.0   -o hello
$ ./hello 
Hello world, I'm a library!

Looking at the binary, you can see that it needs libtest.so.0:

$ objdump -p hello | grep NEEDED
  NEEDED               libtest.so.0
  NEEDED               libc.so.6

The binary will look for its libraries, apart from the standard places, in the specified directory:

$ objdump -p hello | grep RPATH
  RPATH                /home/maciej/src/tmp

If you want the binary to look in the current directory, you can set the RPATH to $ORIGIN. This is a bit tricky, because you need to make sure that the dollar sign is not interpreted by make. Here's one way to do it:

$ make CFLAGS="-fPIC" LDFLAGS="-Wl,-rpath '-Wl,\$\$ORIGIN'"
$ objdump -p hello | grep RPATH
  RPATH                $ORIGIN
$ ./hello 
Hello world, I'm a library!
5
  • 4
    If not using make, such as when manually calling g++, try -Wl,-rpath='$ORIGIN' (note the single quotes) to prevent $ORIGIN from expanding to an empty string.
    – Morpork
    Apr 13, 2017 at 2:47
  • This info was very useful, thanks. Without it, I was resorting to launching an executable with a wrapping shell script LD_LIBRARY_PATH=. ./my-executable $@ whereas using the -rpath='.' is (still a workaround in my case, but) much cleaner.
    – rsethc
    Feb 11, 2021 at 17:50
  • I think I don't get it completly. First you are using -R and later -rpath to pass the additional search path to the linker. What's the difference? Dec 30, 2021 at 23:43
  • The -R and -rpath options (on the ld command) are interchangeable with some caveats, see man ld for details.
    – automaciej
    Jan 1, 2022 at 14:56
  • 1
    This $ORIGIN solution works really well. I used to have -rpath=., but this works only when you start the executable from it's folder (./my-exe), from another folder (./folder/my-exe) it used to fail. With -rpath=$ORIGIN, it works from any place!
    – jpo38
    Aug 24, 2023 at 13:09
35

To load the shared objects from the same directory as your executable, simply execute:

$ LD_LIBRARY_PATH=. ./binary

Note: It will not modify the LD_LIBRARY_PATH variable of your system. The change only affects to this, and only this, execution of your program.

0
15

For anyone using CMake for their build, you can set the CMAKE_EXE_LINKER_FLAGS to the following:

set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath='$ORIGIN'")

This will properly propagate the linker flags for all build types (e.g., Debug, Release, etc...) to look for .so files in the current working directory first.

2
  • Hey, this is super useful. I can prepare a derivative library B using library A, redistribute library A with my library B, and be immune to library A variations on the user's system. Are there downsides to this approach?
    – Colin
    Dec 10, 2022 at 17:10
  • Oh, and I seem to be able to do this with bazel's cc_library: linkopts = ["-Wl,-rpath '-Wl,$$ORIGIN'"]...
    – Colin
    Dec 10, 2022 at 17:12
5

For anyone that still struggles without an answer I found one myself with the following suggestion:

You could try updating the ld.so.cache using: sudo ldconfig -v

Worked for me.

2
  • Could you explain a little in which way this helps solving the OP's problem? Dec 30, 2021 at 23:11
  • It also solved problem for me, but I additionally looked at /etc/ld.so.conf for directories where the library files are searched, and placed my library to the first directory in the list (namely, /usr/local/lib/arm-linux-gnueabihf/). Then I executed this command and boom, the library is now found. Apr 28, 2022 at 9:19
2

I got it working with this :

$ gcc file.c -L. -lmylib

and to run :

$ LD_LIBRARY_PATH=. ./a.out
2

There can be subtle situations showing the same symptoms. I use to build a CMake project, both locally at /path/to/myproj and remotely on a server, at /path/to/myproj-deploy. Project contains an .so build artifact and multiple ELF files depending on that.

When I pulled back binaries from the build server, I found out that (locally) calling ELFs results in the same problems. Thanks to authomatthias, calling

$ objdump -p bin/myelf  | grep my
bin/myelf:     file format elf64-x86-64
  NEEDED               libmyso.so
  RUNPATH              /path/to/myproj-deploy/bin

ends up in a non-surprising consequence: CMake system hardcoded remote path into the RUNPATH, demonstrating some non-portability (without proper additional actions, I suppose)

1

The dynamic linker will decide where to look for libraries. In case of Linux, the dynamic linker usually is GNU ld.so (or an alternative that will usually behave identical for compatibility reasons).

To quotes from the Wikipedia:

The dynamic linker of the GNU C Library searches for shared libraries in the following locations:

  1. The (colon-separated) paths in the DT_RPATH dynamic section attribute of the binary if present and the DT_RUNPATH attribute does not exist.
  2. The (colon-separated) paths in the environment variable LD_LIBRARY_PATH, unless the executable is a setuid/setgid binary, in which case it is ignored. LD_LIBRARY_PATH can be overridden by calling the dynamic linker with the option --library-path (e.g. /lib/ld-linux.so.2 --library-path $HOME/mylibs myprogram).
  3. The (colon-separated) paths in the DT_RUNPATH dynamic section attribute of the binary if present.
  4. Lookup based on the ldconfig cache file (often located at /etc/ld.so.cache) which contains a compiled list of candidate libraries previously found in the augmented library path (set by /etc/ld.so.conf). If, however, the binary was linked with the -z nodefaultlib linker option, libraries in the default library paths are skipped.
  5. In the trusted default path /lib, and then /usr/lib. If the binary was linked with the -z nodefaultlib linker option, this step is skipped.

Source: https://en.wikipedia.org/wiki/Rpath

1

If your dependencies have also dependencies RUNPATH will not work (which is the default tag by recent linkers). To solve the problem you should use RPATH old version by adding --disable-new-dtags.

-Wl,--disable-new-dtags,-rpath=path/to/libs

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .