Come Raczy
Come Raczy

Reputation: 1680

How to force the linker to resolve weak symbols locally?

Using g++, when two compilation units "a1.o" and "a2.o" both define and use the same weak symbol, the linker will silently resolve to the first occurrence of the symbol wherever it is used. As a result, the behavior of the application will depend on the order of the object files on the linker command line. What can be done to ensure that these symbols are resolved locally to each compilation unit?

For instance, as a minimalist example, if I have the following source files:

a1.cpp:

#include <iostream>
struct A
{
    void foo() {std::cerr << __FILE__ << std::endl;}
};
void bar1() {A a; a.foo();}

a2.cpp:

#include <iostream>
struct A
{
    void foo() {std::cerr << __FILE__ << std::endl;}
};
void bar2() {A a; a.foo();}

main.cpp:

void bar1();
void bar2();
int main()
{
    bar1();
    bar2();
}

and compile them with:

for i in a1 a2 main ; do g++ -c -o $i.o $i.cpp ; done

The output will depend on the relative position of a1.o and a2.o on the linker command line:

g++ -o main main.o a{1,2}.o ; ./main
a1.cpp
a1.cpp

g++ -o main main.o a{2,1}.o ; ./main
a2.cpp
a2.cpp

I'd like to get the same result as if using the '-fno-weak' command line option:

for i in a1 a2 main ; do g++ -fno-weak -c -o $i.o $i.cpp ; done
g++ -o main main.o a{1,2}.o ; ./main
a1.cpp
a2.cpp

but '-fno-weak' seems to lead to other complications. What are the alternatives (besides not inlining and fixing collisions)?

For those wondering what could be a typical use case: when writing mock components it is sometimes convenient to have header-only implementations. Different test fixtures end-up having different mock implementations of the same component type, which becomes an issue when all the fixtures are linked into a single test runner.

Upvotes: 2

Views: 766

Answers (1)

R Sahu
R Sahu

Reputation: 206607

You asked:

What are the alternatives (besides not inlining and fixing collisions)?

Use local namespaces or anonymous namespaces.

a1.cpp:

#include <iostream>
namespace A1_namespace
{
   struct A
   {
      void foo() {std::cerr << __FILE__ << std::endl;}
   };
}

using namespace A1_namespace;

void bar1() {A a; a.foo();}

or

#include <iostream>
namespace
{
   struct A
   {
      void foo() {std::cerr << __FILE__ << std::endl;}
   };
}

void bar1() {A a; a.foo();}

Make similar changes to a2.cpp.

Upvotes: 4

Related Questions