GCC/LD编译链接潜规则 (第一弹) : 当一个符号被多重定义时

来源:互联网 发布:淘宝鹊桥佣金 编辑:程序博客网 时间:2024/05/29 17:52

gcc/ld的编译链接的有很多潜规则, 如果不幸遇到了, 并且没有意识到这是个潜规则, 那么会浪费很久很久的调试时间而不得其解. 下面总结我所遇到的一些潜规则.

本文先出第一弹, 其他的潜规则将陆续放出.


潜规则:

当一个符号在多个目标文件(.o)里同时出现时, LD报错. 提示符号多重定义.

当一个符号在多个静态库(.a)里同时出现时, LD不报错, 以第一个遇到的为准. 并且不会有任何warning提示 !!! 这个潜规则可能导致很多意想不到的问题!!!

测试代码:
helper.h



helper1.cpp



helper2.cpp


symbol_in_multi_obj.cpp


编译命令:


测试结果:
链接.o文件失败, 提示符号重定义


链接.a文件成功. 但其实链接的是第一个.a中的符号
先链helper1.a, 输出 “call myfun in helper1.cpp”


先链helper2.a, 输出 “call myfun in helper2.cpp”


真实案例:

Shasha同学的cgi类中调用http_request_helper类发请求, 发现程序行为总是和预期的不一致, 后来排查发现, 在Makefile中有另外的依赖库(.a)中也打包了http_request_helper类, 但是类的实现比较老, 导致把后面的新库覆盖!而GCC/LD对此没有任何提醒!!! Shit.


附上perryyang同事的补充:

补充一些:

GCC/LD

1.做符号解析时,会把找到的第一个定义的代码链接进来(已经找到了就不再考虑后续的)

2.做Object链接时(*.o文件),每一个目标文件要做reloc操作,找到的第一个定义优先处理,再遇到一个相同的定义,就报"multip-definition 错误",这个可以通过-Wl,'-z muldefs'来解决,

-z muldefs会让ld在遇到重复定义时候,只处理第一个定义。

在运行时刻,如果:

1.存在多个相同的动态库名,则根据ldconfig中配置的库查找路径,先找到哪个就用哪个。如果机器中存在不同版本的动态库,则可能会用上错误的库,而从我们的代码中是检查不出错误的,

只能优先做"ldconfig -p | grep 库名"的检查,干掉一个不用的库就可以。

2.如果多个不同的动态库,拥有相同的全局变量名,则最后加载的动态库中的全局变量会冲掉之前加载的全局变量,导致结果异常(程序正常工作)

下面这篇文章对链接处理的说明的比喻挺贴切的:

http://webpages.charter.net/ppluzhnikov/linker.html

和VC++对比
另外, 对比VC++的行为, 是不一样的, VC++在发现多个依赖库中有同名符号时, 会符号重定义, 而不是默认的选择一个. 同时, VC++还提供了当符号重定义时忽略
指定的库的能力.