Resolving Symbols in Libraries

Part II of Libraries, Linking, Initialization, and C++ Series

By Darryl Gove and Stephen Clamage, May 2011

Part I - Introduction to Libraries and Linking
Part II - Resolving Symbols in Libraries

 

When an application is linked, the linker checks all the symbols in the application to verify that they resolve to symbols in the libraries that are specified on the link line. The flag that causes this to happen is -z defs, and this flag is included automatically whenever an application is linked. This flag is not included by default when a library is linked. This means that a library can be linked despite not resolving all of its external dependencies.

ldd is the command for checking what shared objects an executable or library links to. Listing 1 shows the result of running ldd on the main executable and one of the shared libraries.

Listing 1: Using ldd to Identify Linked Libraries
  $ ldd main
  lib2.so =>       codes/library/lib2.so
  lib1.so =>       codes/library/lib1.so
  libc.so.1 =>     /lib/libc.so.1
  libm.so.2 =>     /lib/libm.so.2
  $ ldd lib1.so
  $

The output shows that the application has dependencies on lib1.so, lib2.so, libc.so, and libm.so. When ldd is used on lib1.so, it reports that the library has no dependencies. Libraries should be linked in the same way as applications in order to record their run-time dependencies. We can start identifying unresolved symbols by passing -z defs on the link line for the library, as shown in Listing 2.

Listing 2: Identifying Unresolved Dependencies Using -z defs
  $ cc -G -Kpic lib1.c -o lib1.so -z defs
  Undefined                       first referenced
  symbol                             in file
  printf                              lib1.o
  ld: fatal: Symbol referencing errors. No output written to lib1.so

The error shows that the library has an unresolved symbol for printf(). This can be fixed by explicitly listing libc.so on the link line using -lc, as shown in Listing 3.

Listing 3: Explicitly Listing libc As a Dependency of a Library
  $ cc -G -Kpic lib1.c -o lib1.so -lc -z defs
  $ ldd lib1.so
  libc.so.1 =>     /lib/libc.so.1

Once libc.so is listed, the link proceeds and libc.so is recorded as a dependency of the library.

It is recommended that dependent libraries be included on the link line and that libraries be created with the flag -z defs. Doing this ensures that there are no missing symbols in any of the support libraries for the application. If this approach is not taken, the unresolved symbols might not be detected until the main application is linked, or they might not be detected until run time if the libraries are loaded dynamically. Listing 4 shows the code for lib1.c modified to have an unresolved dependency.

Listing 4: Library Code Modified to Have an Unresolved Dependency
  $ more lib1.c
  #include ‹stdio.h›

  void widget();

  void f()
  {
  printf("In library 1\n");
  widget();
  }

The process of compiling the library and then compiling the application is shown in Listing 5. The linker reports the unresolved symbol when the application is linked.

Listing 5: Compiling an Application that Uses a Library with an Unresolved Symbol
  $ cc -G -Kpic lib1.c -o lib1.so
  $ cc -o main main.c -L. -R'$ORIGIN' -l1 -l2
  Undefined                       first referenced
  symbol                             in file
  widget                              ./lib1.so
  ld: fatal: Symbol referencing errors. No output written to main

The techniques discussed in this article are very helpful when building applications that depend on libraries. However, the link step for the application gives the linker the opportunity to check that all the symbols do get resolved. These techniques become more critical when the libraries are loaded by the application at run time through a call to dlopen(). In this situation, it is critical that the libraries are built with explicit dependencies and with the flag -z defs to warn of unresolved symbols. Loading a library that does not have all its symbols resolved might cause an application to fail.

It is also worth investigating whether the application is linking with any libraries that are not used. When used on an application or library, the utility ldd with the options -r -U reports both libraries that are unused and symbols that are used but currently unresolved.

Summary of Recommendations

  • Libraries should be linked with the linker option -z defs for early detection of unresolved symbols.
  • When linking a library, the libraries that the library requires should be explicitly listed on the link line. Listing all the required libraries helps the linker determine the correct initialization order for the libraries.
  • Use ldd -r -U on a library to verify that it has no unneeded dependencies or unresolved symbols.
Revision 1, 04/27/2011