0

C++ template functions are exported as weak symbols to work around the one definition rule (related question). In a situation where the function is explicitly instantiated for every use case, is there a way to export the symbol as non-weak?

Example use case:

// foo.hpp
template<typename T>
void foo();

// All allowed instantiations are explicitly listed.
extern template void foo<int>();
extern template void foo<short>();
extern template void foo<char>();
// foo.cpp
template<typename T>
void foo()
{
  // actual implementation
}

// All explicit instantiations.
template void foo<int>();
template void foo<short>();
template void foo<char>();

When I compile the code above with GCC or ICC, they are tagged as weak:

$ nm foo.o
                 U __gxx_personality_v0
0000000000000000 W _Z3fooIcEvv
0000000000000000 W _Z3fooIiEvv
0000000000000000 W _Z3fooIsEvv

Is there a way to prevent that? Since they are actually definitive, I would want them to not be candidate for replacement.

Julien
  • 2,139
  • 1
  • 19
  • 32
  • Why would you expect other instantiations to differ? In situations like this, the idea is that it doesn't matter that symbols are weak, because all have the same definitions. – Sneftel May 12 '21 at 15:37
  • **Weak** does not mean "candidate for replacement". It means the same exact candidate, of which any one of them is as good as any other of them, because they are identical. – Eljay May 12 '21 at 16:21
  • 1
    what problem are you trying to solve? Why do you prevent it? – 463035818_is_not_an_ai May 12 '21 at 16:41
  • Thanks for the comments. I agree that it does not matter for the final binary. As far as I can tell `weak` means that the symbol can be switched with another symbol of the same name during link; not necessary the same implementation. It is used with templates to avoid ODR violation because template instantiations tend to appear in many object files. The problem I am trying to solve is that the link time is too slow and I want to reduce the work of the linker to the minimum. – Julien May 12 '21 at 19:24

1 Answers1

2

objcopy supports the --weaken option, but you want the opposite.

It also supports the --globalize-symbol, but that appears to have no effect on weak symbols:

gcc -c t.cc
readelf -Ws t.o | grep _Z3fooI

    14: 0000000000000000     7 FUNC    WEAK   DEFAULT    7 _Z3fooIiEvv
    15: 0000000000000000     7 FUNC    WEAK   DEFAULT    8 _Z3fooIsEvv
    16: 0000000000000000     7 FUNC    WEAK   DEFAULT    9 _Z3fooIcEvv

objcopy -w --globalize-symbol _Z3fooI* t.o t1.o && 
readelf -Ws t1.o | grep _Z3fooI

    14: 0000000000000000     7 FUNC    WEAK   DEFAULT    7 _Z3fooIiEvv
    15: 0000000000000000     7 FUNC    WEAK   DEFAULT    8 _Z3fooIsEvv
    16: 0000000000000000     7 FUNC    WEAK   DEFAULT    9 _Z3fooIcEvv

Not to be deterred, we can first localize the symbols, then globalize them:

objcopy -w -L _Z3fooI* t.o t1.o &&
objcopy -w --globalize-symbol _Z3fooI* t1.o t2.o &&
readelf -Ws t2.o | grep _Z3fooI

    14: 0000000000000000     7 FUNC    GLOBAL DEFAULT    7 _Z3fooIiEvv
    15: 0000000000000000     7 FUNC    GLOBAL DEFAULT    8 _Z3fooIsEvv
    16: 0000000000000000     7 FUNC    GLOBAL DEFAULT    9 _Z3fooIcEvv

Voilà: the symbols are now strongly defined.

The problem I am trying to solve is that the link time is too slow and I want to reduce the work of the linker to the minimum.

If this makes the linker do less work (which I doubt), I'd consider that a bug in the linker -- if the symbol is defined once, it shouldn't matter to the linker whether that definition is strong or weak.

Employed Russian
  • 199,314
  • 34
  • 295
  • 362
  • After doing some benchmarks, I do not see any difference indeed in the link duration when switching from weak to strong symbols :) – Julien May 15 '21 at 20:07