使用 GCC 链接具有重复类名的库
Posted
技术标签:
【中文标题】使用 GCC 链接具有重复类名的库【英文标题】:Linking Libraries with Duplicate Class Names using GCC 【发布时间】:2010-05-04 14:13:53 【问题描述】:在链接包含同名类的库时,GCC 有没有办法产生警告?例如
Port.h
class Port
public:
std::string me();
;
端口.cpp
#include "Port.h"
std::string Port::me() return "Port";
FakePort.h
class Port
public:
std::string me();
;
FakePort.cpp
#include "FakePort.h"
std::string Port::me() return "FakePort";
main.cpp
#include "Port.h"
int main()
Port port;
std::cout << "Hello world from " << port.me() << std::endl;
return 0;
建筑
# g++ -c -o Port.o Port.cpp
# ar rc Port.a Port.o
# g++ -c -o FakePort.o FakePort.cpp
# ar rc FakePort.a FakePort.o
# g++ -c -o main.o main.cpp
# g++ main.o Port.a FakePort.a
# ./a.out
Hello world from Port
更改库顺序
# g++ main.o FakePort.a Port.a
# ./a.out
Hello world from FakePort
根据this page:
如果在两个不同的库中定义了一个符号,gcc 将使用它找到的第一个并忽略第二个,除非第二个包含在目标文件中,而该目标文件由于其他原因而被包含。
所以上述行为是有道理的。不幸的是,我继承了一个相当大的代码库,它不使用命名空间(现在添加它们是不可行的)并且在多个库中使用了一些通用类名。我想在链接时自动检测重复名称,以确保不会意外实例化一个类的错误副本。比如:
# g++ -Wl,--warnLibraryDupSymbols main.o FakePort.a Port.a
Warning: Duplicate symbol: Port
但我在 GCC 链接器选项中找不到任何内容。是否可以让 GCC 自动检测和报告此类情况?
【问题讨论】:
相关:***.com/questions/10671956/… 【参考方案1】:以下可能值得一试(老实说,我不知道它是否会做你想要的):
--whole-archive
对于命令行中在 --whole-archive 选项之后提到的每个存档,在链接中包含存档中的每个目标文件,而不是在存档中搜索所需的目标文件。这通常用于将存档文件转换为共享库,强制将每个对象包含在生成的共享库中。此选项可以多次使用。
我还没有尝试过,但听起来好像它会将库中的所有项目都拉入,就好像它们是目标文件一样。您需要为每个库指定选项。
正如 Neil 所说,这不会给您带来类级别的冲突,但如果存在具有相同签名的类成员,这可能会让链接器告诉您。
【讨论】:
不确定这是否是最好的一般情况答案,但它在这里有效。 # g++ main.cpp -Wl,--whole-archive FakePort.a Port.a -Wl,--no-whole-archive Port.a(Port.o):Port.cpp:(.text+0x0): 多个`Port::me()' FakePort.a(FakePort.o):FakePort.cpp:(.text+0x0) 的定义:首先在这里定义 如果我没有遗漏任何东西,这可能会使你的二进制文件大大膨胀,所以它只对测试运行有用。或者有人可以在正确的地方使用--no-whole-archive
来解决这个问题吗? @joesdiner
@gf:我认为你是对的;此答案的目的是在集成阶段使用它来诊断问题(或潜在问题) - 而不是作为正常的构建配置。我认为这就是 joesdiner 想要做的。
对,我只是想指出一个可能的陷阱,以防误解:)【参考方案2】:
或者,您可以使用利用nm
的脚本来查找候选人,例如类似:
import collections, subprocess, os, re, sys
def filt(s):
p = subprocess.Popen(['c++filt', s],stdout=subprocess.PIPE, stderr=subprocess.PIPE)
return p.communicate()[0].strip()
rx = re.compile('^[\\d\\w]+ T [\\w]+', re.MULTILINE)
sym = collections.defaultdict(set)
for file in sys.argv[1:]:
proc = subprocess.Popen(['nm', file], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
for s in rx.findall(proc.communicate()[0]):
sym[s.split()[2]].add(file)
for s in filter(lambda x: len(sym[x])>1, sym):
print 'Duplicate "%s" in %s' % (filt(s), str(sym[s]))
foo:$ python dups.py *.a
Duplicate "Port::me()" in set(['Port.a', 'FakePort.a'])
【讨论】:
使用这样的脚本来创建 dup 候选列表,然后处理您的 makefile。创建关注一组已知类的 makefile 模板,这些模板按包含路径的结构化顺序排列。这样就更容易跟踪正在链接的“端口” 不错的脚本。对我有用,但 Michael Burr 提供的链接器选项似乎会自动检测事物 @joesdiner:那就更好了。可悲的是--whole-archive
在 OSX 上不适合我。
Mac OS X 上有一个 -all_load 选项,其作用类似于 --whole-archive。【参考方案3】:
我也没有看到任何选项来做你想做的事。也许横向思考并使用像 Doxygen 这样的代码文档工具来为所有类生成文档并手动查找重复项
【讨论】:
【参考方案4】:不,没有。这样做的原因是,就链接器而言,类名不是符号。 C++ 编译器会使用类名来生成损坏的函数名,但在链接器参与时,类名本身已经消失。
【讨论】:
以上是关于使用 GCC 链接具有重复类名的库的主要内容,如果未能解决你的问题,请参考以下文章