Fabio Dalla Libera
Fabio Dalla Libera

Reputation: 1326

g++, require linker warning / error for multiple template specialization

Imagine you have a file a.h

 #include <iostream>

template<typename T> struct A{  
  int magic;
  A():magic(1234){}
  void f(){std::cout<<"default f"<<magic<<std::endl;}
};


void f(A<int>* a);

then the function f is defined in "a.cpp"

  #include "a.h"
void f(A<int>* a){
  a->f();
}

and finally, "main.cpp" specializes the template, and then uses f

#include "a.h"
template<> struct A<int>{   
};

int main(){
  A<int> a;
  f(&a);

}

Clearly the compiler uses the non specialized version for a.o, and the specialized version for main.o, i.e. there happen to be two different implementations of A. On execution, f can only print garbage / segfault, because the object passed has a different structure from the one expected.

Is there a way to make the linker warn that there're two versions of A ?

Upvotes: 12

Views: 607

Answers (3)

jthill
jthill

Reputation: 60313

I think the answer is "no" and it's going to stay that way.

Types only have names the linker sees when they appear in function parameters or template arguments (some other oddballs? maybe). Your example is actually one of the easier cases, and to detect that the linker would have to be working with an ABI that (in effect) marks template arguments supplied by a specialization. But they can't do that: you have to be able to pass a pointer to a templated struct without knowing whether it's pointing to a specialization.

Even then, you can't get much more radical than even trivial ABI changes, it means at least considering whether you need to recompile and/or relink every library and executable. If your structs were members struct trojan { A<int> greeks; } you'd have identical type names anyway, and if they never appeared as function parameters or template arguments the linker would never see them even if they were different.

To get automatic detection I'd start with an approachable OSS C++ frontend like clang. You'll need (non-standard) name-mangling rules that do mark template-specialization-argument names and make it generate sideband lists of all template declarations it finds references to. Then write a separate tool that looks at the lists for all the objects that are being linked together and complains if it finds a name+arguments used (not just referenced or declared) in one object that's also used in another but from a different specialization.

Upvotes: 1

Employed Russian
Employed Russian

Reputation: 213646

The reason Gold doesn't warn about this is that Gold only detects symbol mismatches (the same symbol being defined in multiple object files in incompatible ways), and there is no such mismatch in the example.

Running the example under Valgrind does produce this error though:

valgrind --track-origins=yes ./a.out

==11004== Memcheck, a memory error detector
==11004== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==11004== Using Valgrind-3.8.0.SVN and LibVEX; rerun with -h for copyright info
==11004== Command: ./a.out
==11004== 
==11004== Conditional jump or move depends on uninitialised value(s)
==11004==    at 0x40B6D24: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_int<long>(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, long) const (in /usr/lib64/libstdc++.so.6.0.16)
==11004==    by 0x40B703C: std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::do_put(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, long) const (in /usr/lib64/libstdc++.so.6.0.16)
==11004==    by 0x40C26DE: std::ostream& std::ostream::_M_insert<long>(long) (in /usr/lib64/libstdc++.so.6.0.16)
==11004==    by 0x40094F: A<int>::f() (a.h:6)
==11004==    by 0x4008CB: f(A<int>*) (a.cpp:3)
==11004==    by 0x400977: main (main.cpp:7)
==11004==  Uninitialised value was created by a stack allocation
==11004==    at 0x400964: main (main.cpp:5)

You should get even better report from Address Sanitizer:

Update:

The point is that I would like to detect the error at linking time, not during execution.

I understand your point, but it is not currently possible for either the compiler (doesn't have info about other translation units) or the linker (doesn't have info about types involved) to warn you about this.

Now, for a debug build, the linker could in theory do this, if for every function it also compared the debug info for parameter types. I suggest filing a feature request for gold in bugzilla.

Upvotes: 3

Jonathan Wakely
Jonathan Wakely

Reputation: 171333

The gold linker might give a warning with --detect-odr-violations

It works by comparing the file and line number for each template definition and warning if they aren't all the same.

Upvotes: 1

Related Questions