DiscoStu
DiscoStu

Reputation: 569

Understand g++ linker failure

Given a static library that you can inspect with

$nm libgtest.a

Why would the following build fail to find symbols?

g++ -ISomePathToGTest -lgtest SomeUnitTests.cpp

One of the many undefined references looks like this:

SomeUnitTests.cpp:(.text+0x28): undefined reference to `testing::InitGoogleTest(int*, char**)'

But I have these lines from the 'nm' command above:

000000000000ffca T _ZN7testing14InitGoogleTestEPiPPc
000000000000ffca T _ZN7testing14InitGoogleTestEPiPPw

Which, according to the manual for nm, the 'T' means that this system is in the text section of the library I'm inspecting. I'm certain it's not picking this library up from anywhere else, and that it is finding this library I'm using with that build command.

Building just the object for SomeUnitTests.cpp like this:

g++ -c -ISomePathToGTest SomeUnitTests.cpp

And inspecting it with nm shows only this entry below for a search on InitGoogleTest:

U _ZN7testing14InitGoogleTestEPiPPc

So, they should match right? But, Focusing on how the linker is matching symbols and not that I'm working with gtest, why is it failing?

Upvotes: 2

Views: 1030

Answers (1)

Mike Kinghan
Mike Kinghan

Reputation: 61137

Simplifying for the sake of clarity, your commandline:

g++ -ISomePathToGTest -lgtest SomeUnitTests.cpp

invokes the g++ tooldriver to do the following:

  1. Invoke the GNU C++ compiler cc1plus to compile SomeUnitTests.cpp to a temporary assembly language file, with a commandline essentially like:

    cc1plus -ISomePathToGTest SomeUnitTests.cpp -o /tmp/aaaa.s

  2. Invoke the GNU assembler as to assemble aaaa.s to a temporary object file, with a commandline essentially like:

    as -ISomePathToGTest -o /tmp/bbbb.o /tmp/aaaa.s

  3. Invoke the GNU linker ld to link bbbb.o with the specified and default libraries, with a commandline essentially like:

    ld -L/default/library/search/paths... -lgtest /tmp/bbbb.o -lstdc++ -lother -ldefault -llibraries...

In the linker commandline, the ordering of the object files and the specified libraries reflects that of the corresponding source files and the specified libraries in your original commandline: because the ordering of object files and libraries matters to the linker, and g++ works on the assumption that you know this, and that the commandline expresses the order you want.

The ordering of object files and libraries matters to the linker because it will search a library to find a definition of some symbol foo if and only if some object file (or library) it has already seen makes undefined references to foo; and if it finds a definition in a library then it will not search later libraries for other definitions of it.

A consequence of this is that a library must be mentioned after all object files that make references into it, or undefined references will ensure. In the linkage of your commandline, libgtest is not examined by the linker at all, because so far no object files have been seen and there are no undefined symbols to resolve. When the object file /tmp/bbbb.o is subsequently examined, its undefined references can only get definitions in the default libraries that follow, leaving all references to libgest's symbols unresolved.

Now you see the answer to your question why moving -lgtest to the end of the commandline "changes the linking order". It's because that's what you're asking for: instead of library object-file (fail), you are asking for object-file library (succeed).

Here is the linker's commandline manual. The documentation for -l namespec is the relevant item.

Upvotes: 3

Related Questions