What Happens When An Application Starts?

Part III 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 a process fails to start because of some issues with libraries, it is critical to inspect what is happening. The environment variable LD_DEBUG tells the run-time linker to output information about what is happening at application startup. There are various options for specifying what information is provided. The information can be brief or voluminous, so it is important to pick the appropriate option to get just the information you need. In most instances, the first thing that needs to be checked is which libraries were loaded. This can be seen by setting LD_DEBUG to the value files, as shown in Listing 1.

Listing 1: Viewing the Loaded Libraries
 $ LD_DEBUG=init ./main
 ...
 16211: 1: calling .init (from sorted order): codes/library/lib1.so
 16211: 1:
 16211: 1: calling .init (done): codes/library/lib1.so
 16211: 1:
 16211: 1: calling .init (from sorted order): codes/library/lib2.so
 16211: 1:
 16211: 1: calling .init (done):codes/library/lib2.so
 16211: 1:
 16211: 1: calling .init (from sorted order): /lib/libc.so.1
 16211: 1:
 16211: 1: calling .init (done): /lib/libc.so.1
 16211: 1:
 16211: 1:
 16211: 1: transferring control: main
 16211: 1:
 16211: 1:
 In library 1
 In library 2
 In main
 16211: 1: calling .fini: /codes/library/lib2.so
 16211: 1:
 16211: 1: calling .fini: /codes/library/lib1.so
 16211: 1:
 16211: 1: calling .fini: /lib/libc.so.1

In this example, the main application is inspected and that identifies that lib1.so and lib2.so are needed. The application was built with a command line that contains -l1 before -l2, and that ordering was preserved when the libraries were loaded.

By default, the output goes to stderr, but the output can be redirected using the environment variable LD_DEBUG_OUTPUT. This environment variable will direct the output to the specified file name appended with the PID of the process.

The next question is often what symbols are bound to which library. This information can be obtained using the bindings output from LD_DEBUG, as shown in Listing 2. The output is quite verbose, because it lists every symbol that is bound.

Listing 2: Identifying What Symbols Are Bound
 $ LD_DEBUG=bindings ./main
 13147:
 ...
 13147: 1: binding file=main to file=codes/library/lib1.so: symbol `f1'
 13147: 1: binding file=codes/library/lib1.so to file=/lib/libc.so.1: symbol `printf'
 ...
 13147: 1: binding file=main to file=codes/library/lib2.so: symbol `f2'
 13147: 1: binding file=codes/library/lib2.so to file=/lib/libc.so.1: symbol `printf'

This output shows that main binds to the symbol f1 in lib1.so and the symbol f2 in the library lib2.so. It also shows the bindings from those libraries to printf().

When libraries are loaded, they might have initialization sections that get run. Neither of the two libraries in the example have explicit initialization sections. Setting LD_DEBUG to init allows us to see the initialization activity, as shown in Listing 3.

Listing 3: Viewing the Initialization Order
 $ LD_DEBUG=init ./main
 ...
 16211: 1: calling .init (from sorted order): codes/library/lib1.so
 16211: 1:
 16211: 1: calling .init (done): codes/library/lib1.so
 16211: 1:
 16211: 1: calling .init (from sorted order): codes/library/lib2.so
 16211: 1:
 16211: 1: calling .init (done):codes/library/lib2.so
 16211: 1:
 16211: 1: calling .init (from sorted order): /lib/libc.so.1
 16211: 1:
 16211: 1: calling .init (done): /lib/libc.so.1
 16211: 1:
 16211: 1:
 16211: 1: transferring control: main
 16211: 1:
 16211: 1:
 In library 1
 In library 2
 In main
 16211: 1: calling .fini: /codes/library/lib2.so
 16211: 1:
 16211: 1: calling .fini: /codes/library/lib1.so
 16211: 1:
 16211: 1: calling .fini: /lib/libc.so.1

The libraries are initialized in the same order in which they are loaded. They are finalized in the reverse order. The load order depends on the order in which the libraries are specified on the command line, possibly overridden by dependencies determined by the linker.

Summary of Recommendations

Use the environment variable LD_DEBUG to investigate run-time linker activity.

Revision 1, 05/05/2011