Keywords: GCC compilation | linker errors | library dependency order | pthread library | symbol resolution
Abstract: This paper provides an in-depth analysis of the 'DSO missing from command line' error during GCC compilation, focusing on linker symbol resolution mechanisms and library dependency ordering. Using the Open vSwitch compilation case study, it explains the root causes of pthread library linking failures and presents solutions based on link order adjustment and circular dependency handling. The article also compares behavior across different linker versions, offering comprehensive guidance for diagnosing and fixing linking issues.
Problem Phenomenon and Background Analysis
During software development, Dynamic Shared Object (DSO) related errors frequently occur at the compilation and linking stage. The error message libpthread.so.0: error adding symbols: DSO missing from command line represents a typical linker error, commonly encountered when compiling multi-threaded applications with GCC.
From the Open vSwitch 1.5.0 compilation error log, we can observe that the linker fails when attempting to resolve the pthread_create symbol. Although the libpthread.so.0 library file exists in the system and the symbol availability is confirmed via the readelf -s command, the linking process still fails. This phenomenon reveals an important characteristic of the GCC linker's symbol resolution process: link order dependency.
Linker Working Principles and Symbol Resolution
The GCC linker employs a left-to-right symbol resolution strategy. When processing command-line arguments, it maintains a list of unresolved symbols. For each object file (.o file), the linker records all undefined symbol references; for each library file, the linker only searches for matching symbol definitions within the current unresolved symbol list.
Consider the following simplified linking process example:
// Example: Incorrect link order
gcc main.o -lpthread -lother
// Correct link order
gcc main.o -lother -lpthread
In the first case, if main.o references pthread_create but -lpthread appears before main.o, the linker hasn't recorded the pthread_create reference when processing -lpthread, therefore it won't extract the symbol definition from libpthread.so.
Solution: Adjusting Link Order
Based on the linker's working principles, the most direct solution is to reposition library references after the object files that depend on them. In the Open vSwitch compilation case, -lpthread needs to be moved after all object files and static libraries:
gcc -Wstrict-prototypes -Wall -Wno-sign-compare -Wpointer-arith \
-Wdeclaration-after-statement -Wformat-security -Wswitch-enum \
-Wunused-parameter -Wstrict-aliasing -Wbad-function-cast \
-Wcast-align -Wstrict-prototypes -Wold-style-definition \
-Wmissing-prototypes -Wmissing-field-initializers \
-Wno-override-init -g -O2 -export-dynamic \
-o utilities/ovs-dpctl utilities/ovs-dpctl.o \
lib/libopenvswitch.a \
/home/jyyoo/src/dpdk/build/lib/librte_eal.a \
/home/jyyoo/src/dpdk/build/lib/libethdev.a \
/home/jyyoo/src/dpdk/build/lib/librte_cmdline.a \
/home/jyyoo/src/dpdk/build/lib/librte_hash.a \
/home/jyyoo/src/dpdk/build/lib/librte_lpm.a \
/home/jyyoo/src/dpdk/build/lib/librte_mbuf.a \
/home/jyyoo/src/dpdk/build/lib/librte_ring.a \
/home/jyyoo/src/dpdk/build/lib/librte_mempool.a \
/home/jyyoo/src/dpdk/build/lib/librte_malloc.a \
-lrt -lm -lpthread
This adjustment ensures that when the linker processes -lpthread, all symbol references from object files and static libraries have been recorded in the unresolved symbol list.
Handling Circular Dependencies
In some complex projects, circular dependencies may exist between library files. For example, libb requires symbols from libc, while simultaneously libc requires symbols from libb. In such cases, simple order adjustment cannot resolve the problem.
The GCC linker provides a mechanism for handling circular dependencies: by specifying the same library file multiple times on the command line. The specific syntax is as follows:
gcc x.o y.o z.o -la -lb -lc -lb
In this example, -lb appears twice. During the first occurrence, the linker processes the portions of libb that match current unresolved symbols; during the second occurrence, it processes the remaining portions, including those symbols that couldn't be resolved during the first pass due to dependencies on libc.
Linker Version Differences and Compatibility
Different versions of GCC linkers exhibit variations in symbol resolution strategies. Older linker versions recursively check symbols across all dependent libraries, while newer versions adopt stricter policies, checking only directly specified library files.
This change has led to situations where DSO errors may occur even with correct library dependency relationships. As a temporary solution, the -Wl,--copy-dt-needed-entries option can be used to restore older version behavior:
export LDFLAGS="-Wl,--copy-dt-needed-entries"
./configure
make
Or specify directly in the compilation command:
g++ main.cc -Wl,--copy-dt-needed-entries -ltensorflow
However, from a software engineering best practices perspective, it's recommended to directly fix link order issues rather than relying on such compatibility options.
Practical Recommendations and Debugging Techniques
For software developers, it's advised to properly set library dependencies in project build systems. In Makefile or CMakeLists.txt, ensure that library reference orders align with dependency relationships.
When debugging linking issues, the following tools and techniques can be employed:
readelf -s: Examine symbol definitions in library filesnm -D: View symbol tables of dynamic librariesldd: Check library dependencies of executable files- Add
-Wl,--verboseoption to view detailed linking process
Through systematic analysis and proper tool usage, various linker errors can be effectively diagnosed and resolved.