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 ?
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With