06-13-2022, 03:46 PM
最近发现GCC在对C++程序进行链接时优化的时候,如果在不同的目标文件出现了同名类,会给出一个 "type ‘...’ violates the C++ One Definition Rule [-Wodr]" 的警告。开始我以为不是什么问题,直到后来发现它的确导致了错误之后,才知道为什么会有这个警告。
如何发现此类问题:开 address sanitizer 编译项目,运行时报告错误,但 ASAN 不知道错误的类型,推测可能是 ABI 类错误。
可以导致这类错误的例子如下:
先编译这几个文件为目标文件,然后链接:
这时运行结果正常。
但是如果改为:
这时程序会报告一个 stack smashing 后崩溃。
原因就在于 v1.o 和 v2.o 里面 std::vector<A> 的成员函数都编译为弱符号,链接的时候连接器会选择其中一个符号使用,但是实际上相同符号指向的是不同的实现代码,从而导致程序出错。
解决这类问题的方法也很简单,使用 namespace 就可以了。
如何发现此类问题:开 address sanitizer 编译项目,运行时报告错误,但 ASAN 不知道错误的类型,推测可能是 ABI 类错误。
可以导致这类错误的例子如下:
代码:
// v1.cc
#include <vector>
using namespace std;
struct A
{
int a,b;
A(int x): a(x), b(x) {}
};
class T1
{
vector<A> v;
public:
void test(int n)
{
for (int i = 0; i < n; ++i) {
v.push_back(i);
}
}
};
void t1_test(int n)
{
T1 t;
t.test(n);
}
代码:
// v2.cc
#include <vector>
using namespace std;
struct A
{
int a,b,c;
A(int x): a(x), b(x), c(x) {}
};
class T2
{
vector<A> v;
public:
void test(int n)
{
for (int i = 0; i < n; ++i) {
v.push_back(i);
}
}
};
void t2_test(int n)
{
T2 t;
t.test(n);
}
代码:
// main.cc
void t1_test(int n);
void t2_test(int n);
int main()
{
t1_test(1000);
t2_test(1000);
}
先编译这几个文件为目标文件,然后链接:
引用:g++ -o main main.o v1.o v2.o
./main
这时运行结果正常。
但是如果改为:
引用:g++ -o main main.o v2.o v1.o
./main
这时程序会报告一个 stack smashing 后崩溃。
原因就在于 v1.o 和 v2.o 里面 std::vector<A> 的成员函数都编译为弱符号,链接的时候连接器会选择其中一个符号使用,但是实际上相同符号指向的是不同的实现代码,从而导致程序出错。
解决这类问题的方法也很简单,使用 namespace 就可以了。