And*_*rew 1 c++ linker-errors gold-linker c++17
我正在讨论如何调试一个我无法简化为最小例子的重大问题.
问题:我编译了链接到许多不同库的应用程序.标志包括:
-static-libstdc++ -static-libgcc -pipe -std=c++1z -fno-PIC -flto=10 -m64 -O3 -flto=10 -fuse-linker-plugin -fuse-ld=gold -UNDEBUG -lrt -ldl
编译器是gcc-7.3.0,针对binutils-2.30编译.Boost编译时使用与程序其余部分相同的标志,并静态链接.
当程序被链接时,我得到关于重定位的各种警告,指的是丢弃的部分,在我自己的代码和boost中.例如:
/tmp/ccq2Ddku.ltrans13.ltrans.o:<artificial>:function boost::system::(anonymous namespace)::generic_error_category::message(int) const: warning: relocation refers to discarded section
Run Code Online (Sandbox Code Playgroud)
然后,当我运行该程序时,它会使用回溯进行破坏:
Program received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()
(gdb) bt
#0 0x0000000000000000 in ?? ()
#1 0x00007ffff7345a49 in __run_exit_handlers () from /lib64/libc.so.6
#2 0x00007ffff7345a95 in exit () from /lib64/libc.so.6
#3 0x00007ffff732eb3c in __libc_start_main () from /lib64/libc.so.6
#4 0x000000000049b3e3 in _start ()
Run Code Online (Sandbox Code Playgroud)
尝试调用的函数指针是0x0.
如果我使用static-libstdc ++删除,链接器警告和运行时段错误就会消失.
如果我从c ++ 1z更改为c ++ 14,链接器警告和运行时段错误就会消失.
如果我删除-flto,链接器警告和运行时段错误就会消失.
如果我在编译标志中添加"-g",链接器警告和运行时段错误就会消失.
我已经尝试通过指定-Wl, - debug = all来请求黄金进行额外调试,但它告诉我看似没什么相关性.
如果我尝试使用出现相关的代码的一小部分,并单独编译和链接,但相同的Boost库(即试图产生最小的例子),没有任何链接器警告,以及程序运行到完成没有问题.
救命!我该怎么做才能缩小问题范围?
此警告通常表示两个编译单元之间COMDAT组内容不一致.如果编译器发出一个COMDAT组G,其中符号A在一个编译单元中定义,但是发出与第二个编译单元中定义的符号A和B相同的组G,则链接器将保留第一个编译单元中的G组并丢弃组G从第二个.在第二个编译单元中从组外部对符号B的任何引用都将产生此错误.
原因通常是编译器中的错误,使用-flto会使诊断更加困难.在这种情况下,您的第二个编译单元是链接时优化的结果(*.ltrans.o文件名).有了LTO,你提到的很多改变都会让问题消失,这是相当可信的.
binutils git repo主分支上最新版本的gold有一个新[-Wl,]--debug=plugin选项,它将保存日志和所有临时.ltrans.o文件.拥有日志和那些文件,以及所有原始输入文件(您可以通过添加[-Wl,]-t选项获得列表),应该有助于更好地隔离问题.
最新版本的gold还将打印重定位引用的符号.对于本地符号,它将显示符号索引; 用于readelf -s获取有关该符号的更多信息.对于全球符号,它将显示名称; 您可以添加--no-demangle确切名称的选项.
如果它是一个本地符号,问题几乎肯定是编译器.严禁将comdat组外部引用到组中的本地符号.
如果它是全局符号,则可能是源代码中的编译器问题或单定义规则(ODR)违规.您需要在命名对象文件中标识comdat组,找到其关键符号,然后找到提供链接器保留定义的目标文件(-y选项将有帮助),并比较这些组中定义的符号由两个对象.这些步骤应该有助于:
(1)从错误消息开始:
b.o(.data+0x0): warning: relocation refers to symbol "two" defined in discarded section
Run Code Online (Sandbox Code Playgroud)
(2)在bo中寻找符号"two":
$ readelf -sW b.o | grep two
7: 0000000000000008 0 NOTYPE WEAK DEFAULT 6 two
Run Code Online (Sandbox Code Playgroud)
倒数第二个字段("6")是定义"两个"的部分编号.
(3)确认第6节实际上是一个comdat组:
$ readelf -SW b.o
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 6] .one PROGBITS 0000000000000000 000058 000018 00 WAG 0 0 1
Run Code Online (Sandbox Code Playgroud)
sh_flags字段中的"G"("Flg")表示该部分属于comdat组.
(4)找到包含该部分的comdat组:
$ readelf -g b.o
COMDAT group section [ 1] `.group' [one] contains 1 sections:
[Index] Name
[ 6] .one
Run Code Online (Sandbox Code Playgroud)
这向我们表明第6节是第1组的成员.
(5)找到该组的关键符号:
$ readelf -SW b.o
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 1] .group GROUP 0000000000000000 000040 000008 04 7 8 4
Run Code Online (Sandbox Code Playgroud)
sh_info字段("Inf")告诉我们键符号是符号#8,它是"1".(这应与步骤4中括号中显示的名称相匹配.)
$ readelf -sW b.o
Num: Value Size Type Bind Vis Ndx Name
8: 0000000000000000 0 NOTYPE WEAK DEFAULT 6 one
Run Code Online (Sandbox Code Playgroud)
(6)现在您可以在-y one链接中添加选项,以查找哪些对象提供了"one"的定义:
$ gcc -Wl,-y,one ...
a.o: definition of one
b.o: definition of one
Run Code Online (Sandbox Code Playgroud)
列出的第一个(ao)是黄金保存的那个; 它将丢弃具有相同键符号的所有后续comdat组.
如果您使用相同的技术来检查在ao中定义"one"的comdat组,并将属于该组的符号与属于bo中的组的符号进行比较,则应该为您提供更多线索.
| 归档时间: |
|
| 查看次数: |
1166 次 |
| 最近记录: |