There are two steps to build a program with gcc: compilation and linking.

gcc is mostly responsible for the compilation, while when linking, it uses a linker program (e.g., ld) with some additional flags.

In compilation, gcc only requires header files ( .h  files) to ensure that external function calls have correct parameters matched with the definition.
Then, in the linking stage, the linker connects/links all compiled objects and libraries to an executable file.

There are two types of libraries: static library ( .a) and dynamic library ( .so, .dynlib, dll). Static libraries are copied directly to the executable file. In opposite, dynamic libraries are not. Dynamic libraries are supposed to be found in the system before the executable file being executed.

So, where does gcc look for header files, and dynamic libraries?

Check configuration

In compilation

For c: echo | gcc -x c -E -Wp,-v - >/dev/null
For cpp: echo | gcc -x c++ -E -Wp,-v - >/dev/null

Here are the outputs from my PC

For C

ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/7/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/lib/gcc/x86_64-linux-gnu/7/include
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/7/include-fixed
 /usr/include/x86_64-linux-gnu
 /usr/include
End of search list.

For cpp

ignoring duplicate directory "/usr/include/x86_64-linux-gnu/c++/7"
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/7/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/include/c++/7
 /usr/include/x86_64-linux-gnu/c++/7
 /usr/include/c++/7/backward
 /usr/lib/gcc/x86_64-linux-gnu/7/include
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/7/include-fixed
 /usr/include/x86_64-linux-gnu
 /usr/include
End of search list.

In linking

  • ld's search directories:
ld --verbose | grep SEARCH_DIR | tr -s ' ;' \\012

The output from my PC

SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu")
SEARCH_DIR("=/lib/x86_64-linux-gnu")
SEARCH_DIR("=/usr/lib/x86_64-linux-gnu")
SEARCH_DIR("=/usr/lib/x86_64-linux-gnu64")
SEARCH_DIR("=/usr/local/lib64")
SEARCH_DIR("=/lib64")
SEARCH_DIR("=/usr/lib64")
SEARCH_DIR("=/usr/local/lib")
SEARCH_DIR("=/lib")
SEARCH_DIR("=/usr/lib")
SEARCH_DIR("=/usr/x86_64-linux-gnu/lib64")
SEARCH_DIR("=/usr/x86_64-linux-gnu/lib")
  • gcc wraps some flags when calling ld. Thus, the directories list is different
gcc -print-search-dirs | sed '/^lib/b 1;d;:1;s,/[^/.][^/]*/\.\./,/,;t 1;s,:[^=]*=,:;,;s,;,;  ,g' | tr \; \\012 | tr : \\012

The output from my PC

libraries

  /usr/lib/gcc/x86_64-linux-gnu/7/
/usr/x86_64-linux-gnu/lib/x86_64-linux-gnu/7/
/usr/x86_64-linux-gnu/lib/x86_64-linux-gnu/
/usr/x86_64-linux-gnu/lib/
/usr/lib/x86_64-linux-gnu/7/
/usr/lib/x86_64-linux-gnu/
/usr/lib/
/lib/x86_64-linux-gnu/7/
/lib/x86_64-linux-gnu/
/lib/
/usr/lib/x86_64-linux-gnu/7/
/usr/lib/x86_64-linux-gnu/
/usr/lib/
/usr/x86_64-linux-gnu/lib/
/usr/lib/
/lib/
/usr/lib/
  • My gcc build detail with gcc --verbose
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 7.4.0-1ubuntu1~18.04.1' --with-bugurl=file:///usr/share/doc/gcc-7/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++ --prefix=/usr --with-gcc-major-version-only --program-suffix=-7 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1)

Note: gcc looks for the provided libraries' names from left to right and stop finding when finding out the first library which matches the searched name.

  • You also can show the library search directories list by adding the verbose flag -v when linking.
    For example: gcc -v foo.o bar.o -o foo
  • To figure out which libraries are linked with a program and the libraries' full path. Use ldd. For example, ldd foo. Example output from my PC.
	linux-vdso.so.1 (0x00007fff08bf3000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fbc85170000)
	libblas.so.3 => /lib/x86_64-linux-gnu/libblas.so.3 (0x00007fbc85110000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fbc84f1f000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fbc852e8000)
	libopenblas.so.0 => /lib/x86_64-linux-gnu/libopenblas.so.0 (0x00007fbc82d3a000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fbc82d17000)
	libgfortran.so.5 => /lib/x86_64-linux-gnu/libgfortran.so.5 (0x00007fbc82a77000)
	libquadmath.so.0 => /lib/x86_64-linux-gnu/libquadmath.so.0 (0x00007fbc82a2b000)
	libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fbc82a11000)

How to add a directory as library search directory

Customized library paths, which are added to the compilation/linking, by the following method have higher priority than the default library search directories. In other words, if a library is found in customized directory, it will be selected rather than the library in system default.

Compilation

Method 1: use CPATH (c or c++), C_INCLUDE_PATH (c only), CPLUS_INCLUDE_PATH (c++ only). For example: CPATH=/home/transang/my_libs_headers gcc -c foo.c -o foo.o.

Method 2: use -I<dir_path> to gcc when compiling. For example: gcc -I/home/transang/my_libs_headers -c foo.c -o foo.o.

Linking

Method 1: To add an directory to library linking search directories list, use LD_LIBRARY_PATH environment variable. For example: LD_LIBRARY_PATH=/home/transang/my_libs gcc foo.o bar.o -o foo

Method 2: add flag -L<dir_path> to gcc when linking. For example: gcc -L/home/transang/my_libs foo.o bar.o -o foo.

Note 1: LD_LIBRARY_PATH environment variable's value does not affect the results of ld --verbose and gcc -print-search-dirs commands.
Note 2: LD_LIBRARY_PATH 's value affects result of ldd command. Thus, ldd command is the more reliable way to figure out library path.
Note 3: You also have to provide the LD_LIBRARY_PATH value when running the executable file. For e.g. LD_LIBRARY_PATH=/home/transang/my_libs ./foo.


How to add dynamic library when linking

So far, I have introduced the way to figure out the current configuration and modify it to add more directories for library searching.

To link a library, add -l<lib_name> flag to the gcc command when linking. If the lib_name does not start with :, gcc will look for a library named lib<lib_name>.so. Otherwise, the file name lib_name will be searched.

For example: with -lfoo, gcc looks for libfoo.so file. With -l:foo.so, gcc looks for foo.so file.