Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 ?

like image 929
Fabio Dalla Libera Avatar asked Jun 18 '12 11:06

Fabio Dalla Libera


2 Answers

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.

like image 152
Employed Russian Avatar answered Oct 12 '22 23:10

Employed Russian


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.

like image 33
Jonathan Wakely Avatar answered Oct 12 '22 23:10

Jonathan Wakely