Thomas Thomas - 1 month ago 5
C++ Question

cpp: usr/bin/ld: cannot find -l<nameOfTheLibrary>

I created a cpp project, which used a lib file named:

libblpapi3_64.so

This file comes from a library which I download it from Internet.

My project runs without any error. So I update it to bitbucket.
Then my colleague downloads it and runs it at his own computer. But he gets an error:

usr/bin/ld: cannot find -lblpapi3_64
.

In fact, I have copied it into my project repository. I mean I created a file named lib under my project and all lib files that I used are in it.


There are also other lib files such as
liblog4cpp.a
, but they are all good. Only the
libblpapi3_64.so
gets the error.


Is it because it's a .so file not
.a
file? Or there is other reason?

Btw, the file name of
libblpapi3_64.so
is
green
and others files(.a) is
white
. I think it's not a link file, it's the original file.

Answer

Briefly:

ld does not know about where your project libs are located. You have to place it into ld's known directories or specify the full path of your library by -L parameter to the linker.

To be able to build your program you need to have your library in /bin/ld search paths and your colleague too. Why? See detailed answer.

Detailed:

At first we should understand what tools do what:

  1. Compiler produces object files.
  2. Linker produces executable files.

Libraries, in fact, are just an object files which are placed into a single archive by using ar tool with a single symbols table which is created by ranlib tool.

Compiler, when compiling object files, does not resolve symbols. These symbols will be replaced to addresses by linkers. Why linkers? Because here are two linkers:

  1. Static linker: ld. It does 2 jobs:

    a) for static libs, this linker changes external symbols in the object files to the addresses of the real entities. For example, if we use C++ name mangling linker will change _ZNK3MapI10StringName3RefI8GDScriptE10ComparatorIS0_E16DefaultAllocatorE3hasERKS0_ to 0x07f4123f0.

    b) for dynamic libs it only checks if the symbols can be resolved at all but does not replace the symbols by address. If symbols can't be resolved (so they are not implemented in the shared library you are linking to) - it throws undefined reference to error and break up building process because you try to use them but linker can't find such symbol in it's object files which it is processing at this time. Otherwise, this linker adds .interp section to the ELF executable which has:

    i. Request for an interpreter (dynamic linker) to be called before executing.

    ii. A list of libraries which interpreter will be looking for before executing.

Static linker works at build time.

  1. Dynamic linker: ld.so or ld-linux. It finds and loads all the shared libraries needed by a program (if they were not loaded before), resolves the symbols by replacing them to real addresses right before start of the program, prepares the program to run, and then runs it. It works after build and before running the program. Less speaking, dynamic linking means resolving symbols in your executable before each program start.

Actually, when you run an ELF executable with .interp section (it needs to load some shared libraries) the OS (Linux) runs an interpreter at first but not actually your program. Otherwise you have an undefined behavior - you have symbols in your program but they are not defined by addresses which usually means that the program will be unable to work properly.

You may also run dynamic linker by yourself but it is unnecessary (binary is /lib/ld-linux.so.2 for 32-bit architecture elf and /lib64/ld-linux-x86-64.so.2 for 64-bit architecture elf).

Why does static linker claim that usr/bin/ld: cannot find -lblpapi3_64 in your case? Because it tries to find all the libraries in it's known paths. Why does it search the library if it will be loaded during runtime? Because it needs to specify which library dynamic linker should link to during runtime and to add the .interp section. Actually, the .interp section exists in almost each c/c++ elf because the libc and libstdc++ libraries are both shared and compiler by default links dynamically to them but you may do that link to them statically also but this will enlarge the total executable size. So, if the shared library can't be found your symbols will remain unresolved and you will be UNABLE to run your application, thus it can't produce an executable. You may get the list of these dirs by:

  1. Passing a command to the linker in compiler arguments.
  2. By parsing ld --verbose's output.
  3. By parsing ldconfig's output.

Some of these methods are explained here.

Dynamic linker tries to find all the libraries by using:

  1. DT_RPATH dynamic section of ELF file.
  2. DT_RUNPATH section of the executable.
  3. LD_LIBRARY_PATH environment variable.
  4. /etc/ld.so.cache - own cache file which contains a compiled list of candidate libraries previously found in the augmented library path.
  5. Default paths: In the default path /lib, and then /usr/lib. If the binary was linked with -z nodeflib linker option, this step is skipped.

ld-linux search algorithm

Resume

After all of that:

  1. You, your colleague and EACH user of your application code must have all the libraries in their system linker paths to be able to compile (build your application). Otherwise, they have to change Makefile (or compile command) to add the shared library location directory by adding -L<somePathToTheSharedLibrary> as argument.
  2. After successful build you also need your library again to be able to run the program. Your library will be searched by dynamic linker (ld-linux) so it needs to be in it's paths (see above) or in system linker paths. In most of linux program distributions, for example, games from steam, there is a shell-script which sets the LD_LIBRARY_PATH variable which points to all shared libraries needed by the game.
Comments