检测意外的弱链接符号

时间:2011-11-29 21:43:51

标签: c++

在我们公司,直到最近,我们还没有使用名称空间,因为有些编译器无法很好地支持它们。

这会导致多次出现以下错误:

file_A.cpp

class Node {
    Data *ptr;
    Node() { ptr = new Data; }
    ~Node() { delete ptr; }
};

file_B.cpp

class Node {
    vector<int> v;
    Point *pt;
    Node(int x,int y) { pt = new Point(x,y); v.push_back(0); }
    ~Node() { delete pt; }
};

void foo() {
    Node n(10,10);
    ...
}    // calls file_B::~Node() !!!

每个作者Node都不知道另一个Node的存在,但由于他希望这个类名可能被重用,所以他没有用它创建一个.hpp文件。

编译器以静默方式删除其中一个析构函数,因为它们的签名匹配,并且很难找到错误,因为它可能无法在不同的计算机中复制。

一旦发现错误,人们逐渐意识到错误,并且他们试图在未命名的命名空间中密封定义,或者避免在类的主体中隐藏成员函数[见下文]

  • 问题1:既然你不能相信程序员会永远记得防御性地编程,那么是否有一种可以检测这些“非预期的弱链接符号”的工具?

    意外我的意思是, .hpp 文件中的Node,并且至少有一个类成员没有在类定义之间匹配......

  • 问题2:如果我们不使用命名空间,但我们内联每个函数,是否有可能出现自动生成的函数(复制) -ctor,copy-assignment,析构函数)会创建上面提到的“弱链接bug”?


方式1:包含在未命名的名称空间

namespace {
  class Node {
     Data *ptr;
     Node() { ptr = new Data; }
     ~Node() { delete ptr; }
  };
}

方式2:避免内联

class Node {
   Data *ptr;
   Node();
   ~Node();
};

Node::Node()  { ptr = new Data; }
Node::~Node() { delete ptr; }

2 个答案:

答案 0 :(得分:2)

如果您的代码库足够大以证明这一努力,您可以自定义现有编译器来解决您的问题:

  1. LLVM / Clang编译器是可自定义的(它是用C ++编写的,我不太了解它。)
  2. GCC编译器(最新版本,如 4.6 )是可扩展的,通过插件以C编码,或通过扩展MELT编码。 MELT是(免费的,GPLv3许可的)高级域特定语言,用于扩展GCC。
  3. 在这两种情况下,这是几天或几周的努力,最困难的是部分理解编译器内部表示(GCC的Gimple&amp; Tree)和组织(例如传递)。

    我是MELT的主要作者,我很乐意为您提供MELT帮助,请随时与我联系。

答案 1 :(得分:2)

对于这个问题,

"C++ and the linker"是一个非常有趣的读物。具体请参阅名为'Rules Without Enforcement Mean Nothing'的部分。

一个见解是,您可以通过解析目标文件并查找“W”来检测“弱”符号:

$ nm -C foo.o | grep doSomething
00000000 W doSomething()

因此,您可以添加后处理步骤,自动收集这些步骤并列出重复项。您可以将这些与预期重复项的主列表进行比较,如果有任何新副本,则引发标记。

另一种选择可能是gcc的-Fno-weak option.从文档中不清楚复制品会发生什么,但查找它可能会很有趣。

链接的文章也回答了你的第二个问题(“上面的现象”是指删除一个重复的弱符号的一个实例):

  

在某些情况下,编译器必须创建符号   虽然它概括了功能。这可能发生在例如a时   函数指针引用该函数。那么,以上现象   启用优化后,并不总是消失。