Search code examples
c++gcclinkerelficc

How to export template instantiation as non weak?


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.


Solution

  • 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.