skip to Main Content

I have the following source code:

  • foo.h

    void foo();
    
  • foo.cpp

    #include "foo.h"
    #include <iostream>
    
    void foo(){
        std::cout << "This is foo" << std::endl;
    }
    
  • main.cpp:

    #include "foo.h"
    
    int main(){
        foo();
    
        return 0;
    }
    

Then I run the following command to produce both static and shared versions of foo.cpp:

g++ -c -fPIC foo.cpp
ar rvs libfoo.a foo.o
g++ -shared -o libfoo.so foo.o
g++ -c main.cpp

If I try to generate an executable using the static libfoo.a and main.o, the order of the object files and library file matters:

g++ main.o libfoo.a -o main  # works
g++ libfoo.a main.o -o main  # fails with undefined references to `foo()`

As explained in this post, the order matters.

However, if I try to build an executable using the shared libfoo.so and main.o, the order of object file and library file doesn’t matter any more:

g++ main.o libfoo.so -o main   # works
g++ libfoo.so main.o -o main   # also works without errors

Does this mean that the order is not important when we link object file and shared libraries? Or this is a just a special case or some coincidence I am not aware of?

The above code and command is tested on the following platform:

  • Linux: CentOS 7.4
  • gcc: 7.3.1 or 4.8.5 (both tested)

2

Answers


  1. Because no strong symbol name resolution is required in second case. Symbols in shared object are looked up and resolved during ELF interpretation, i.e. when you run your program.

    Login or Signup to reply.
  2. As explained in this post, the order matters.

    A more detailed explanation is here.

    The order matters for traditional UNIX linkers, but recently LLD decided to break with tradition, and record availability of symbols in archive libraries even when they aren’t immediately referenced.

    Currently on Linux three different linkers are available: the original BFD linker, Gold linker (circa 2008), and LLD (circa 2019).

    Using "correct" order succeeds with all three (naturally).

    Using the "wrong" order: g++ libfoo.a main.o fails with BFD and Gold, but succeeds with LLD:

    g++ libfoo.a main.o -fuse-ld=lld && echo $?
    0
    

    However, if I try to build an executable using the shared libfoo.so and main.o, the order of object file and library file doesn’t matter any more

    The reason it doesn’t matter is that linkers treat .so similarly to how they treat .o. In particular, there is no need to "pull out parts" of .so — you get the entire .so (just as you would1 if you used foo.o)

    So when you link g++ libfoo.so main.o, this link is almost equivalent to using g++ foo.o main.o, and the order in the former command doesn’t matter for the same reason it doesn’t matter in the latter command.


    1Building with -ffunction-sections and using --gc-sections notwithstanding.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search