Why does GNU ld resolve symbols differently when l

2019-06-17 11:17发布

问题:

I have a trivial piece of C++ code that looks something like this:

#include <boost/timer/timer.hpp>

int main(void) {
    boost::timer::auto_cpu_timer t;
    return 0;
}

I tried to compile and link it (with gcc 4.8.1 and GNU ld 2.23.52.20130828) as follows:

$ g++ -o test test.cc -lboost_timer
/usr/bin/ld: /tmp/cc2jP1jv.o: undefined reference to symbol '_ZN5boost6system15system_categoryEv'
/usr/lib/libboost_system.so.1.54.0: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status

One solution is to explicitly mention -lboost_system on the command line, and that works. However, I can also do:

$ g++ -Wl,--copy-dt-needed-entries -o test test.cc -lboost_timer

According to the ld documentation "with --copy-dt-needed-entries dynamic libraries mentioned on the command line will be recursively searched, following their DT_NEEDED tags to other libraries, in order to resolve symbols required by the output binary", so this all makes sense: ld is figuring out from boost_timer that it also needs to link against boost_system in order to resolve all the symbols.

However, I realised that this also works:

$ g++ -fPIC -shared -o test test.cc -lboost_timer

Obviously, I've now generated a shared object rather than an executable. Apparently, though, ld was able to figure out that it needed to link the shared object against boost_system:

$ ldd test | grep boost_system
        libboost_system.so.1.54.0 => /usr/lib/libboost_system.so.1.54.0 (0x00007f385246e000)

So my question is this: why is symbol resolution different when building a shared object versus an executable? How is ld able to figure out that my shared object should be linked against boost_system without my specifying --copy-dt-needed-entries?

回答1:

The direct answer to my question is in the --[no-]allow-shlib-undefined option to ld, I think. From the man page:

The default behaviour is to report errors for any undefined symbols referenced in shared libraries if the linker is being used to create an executable, but to allow them if the linker is being used to create a shared library.

Therefore, when I build with -shared, the symbols in boost_system are undefined, but the default behaviour of ld is not to care. It can be told to care:

$ g++ -fPIC -shared -Wl,--no-allow-shlib-undefined -o test test.cc -lboost_timer
/usr/bin/ld: /tmp/cc6j1de3.o: undefined reference to symbol '_ZN5boost6system15system_categoryEv'
/usr/lib/libboost_system.so.1.54.0: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status

Similarly, we can tell it not to care when building an executable:

$ g++ -Wl,--allow-shlib-undefined -o test test.cc -lboost_timer
/tmp/ccUHoCIU.o: In function `__static_initialization_and_destruction_0(int, int)':
test.cc:(.text+0x7a): undefined reference to `boost::system::generic_category()'
test.cc:(.text+0x86): undefined reference to `boost::system::generic_category()'
test.cc:(.text+0x92): undefined reference to `boost::system::system_category()'
collect2: error: ld returned 1 exit status

But creating the binary fails without being able to define those symbols.

Thanks @CharlesBailey for pointing me in the right direction!



回答2:

There are differences in the collect2 command that may be relevant. The different commands are compared at http://imgur.com/eMr2tY2 . The right hand side creates the shared library (-fPic -shared), the left is the normal compile.