正如标题所述,我确实有两个关于LTO的基本问题。首先,这里有一些简单的演示源文件来展示我的基本理解问题。
noop.c
只提供一个空/死函数w/o任何操作,如下所示:
void fnoop(void) {
}
testlib.c
(作为共享库)在fnoop函数上循环以演示优化,并强制分段错误如下:
#include <stdio.h>
#include <stdlib.h>
void fnoop(void);
void force_sigsegv(void) {
int *p = NULL;
int a = 0;
unsigned long i = 0;
for (i; i < 1000000000; ++i) {
fnoop();
}
a = *p; //segfault
printf("print a: %d \n",a);
}
在这里,主要应用程序链接到libtestlib,如:
#include <stdio.h>
#include <stdlib.h>
void force_sigsegv(void);
int main(void) {
printf("force a sigsegv \n");
force_sigsegv();
return 0;
}
因此,这里有两种构建主应用程序的方法:(a)构建testlib共享库和将应用程序链接为:
mkdir lto
gcc -flto -O3 -c noop.c
gcc -flto -O3 -fPIC -shared -Wl,-soname,libtestlib.so.1 -o lto/libtestlib.so.1 testlib.c noop.o
ln -sf libtestlib.so.1 lto/libtestlib.so
gcc -flto -O3 -o test_lto main.c -L $(pwd)/lto -ltestlib
(b)构建testlib共享库和链接应用程序的一步如下:
mkdir lto_single
gcc -flto -O3 -fPIC -shared -Wl,-soname,libtestlib.so.1 -o lto_single/libtestlib.so.1 testlib.c noop.c
ln -sf libtestlib.so.1 lto_single/libtestlib.so
gcc -flto -O3 -o test_lto_single main.c -L $(pwd)/lto_single -ltestlib
使用(a)将for-循环优化为:
00000000000011b0 <force_sigsegv>: 11b0: 8b 04 25 00 00 00 00 mov 0x0,%eax
11b7: 0f 0b ud2
11b9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
对于(b)项,for-循环仍为:
00000000000011c0 <force_sigsegv>: 11c0: 53 push %rbx
11c1: bb 00 ca 9a 3b mov $0x3b9aca00,%ebx
11c6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
11cd: 00 00 00
11d0: e8 4b fe ff ff callq 1020 <fnoop@plt>
11d5: 48 83 eb 01 sub $0x1,%rbx
11d9: 75 f5 jne 11d0 <force_sigsegv+0x10>
11db: 8b 04 25 00 00 00 00 mov 0x0,%eax
11e2: 0f 0b ud2
11e4: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
11eb: 00 00 00
11ee: 66 90 xchg %ax,%ax
问题1:为什么在(b)?情况下不优化for-循环
接下来:分析上述两个应用程序生成的核心转储。假设我们的生产系统中有test_lto,它使用优化的testlib共享库(a),它强制核心转储,用户将其发送给我。我将从以下几个方面着手进行分析:(1)将其加载到gdb以找到崩溃地址,例如:
Core was generated by `./test_lto'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x00007fba3aa211b0 in ?? ()
(2)使用readelf -n corefile
查找入口所属的对象,例如:
0x00007fba3aa20000 0x00007fba3aa21000 0x0000000000000000
/volume/LTO/lto/libtestlib.so.1
0x00007fba3aa21000 0x00007fba3aa22000 0x0000000000000001
/volume/LTO/lto/libtestlib.so.1
(3)使用readelf -t libtestlib.so.1
查找lto版本testlib的文本段偏移量,例如:
[ 8] .text
PROGBITS 0000000000001040 0000000000001040 0
最后,将这些信息添加到gdb,如下所示:
(gdb) core lto/core
[New LWP 1599]
Core was generated by `./test_lto'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x00007fba3aa211b0 in ?? ()
(gdb) add-symbol-file lto/libtestlib.so.1 0x00007fba3aa20000+0x1040
add symbol table from file "lto/libtestlib.so.1" at
.text_addr = 0x7fba3aa21040
(y or n) y
Reading symbols from lto/libtestlib.so.1...
(gdb) bt
#0 0x00007fba3aa211b0 in force_sigsegv ()
因此,无法收集(有用的)崩溃细节。即使在使用testlib (b)的lto_single版本时,也没有给出更多细节。但是,构建相同的对象w/o -flto
,但使用-g
并使用相同的地址0x00007fba3aa20000+0x1040
加载到gdb中,就会得到以下结果:
Reading symbols from nlto/libtestlib.so.1...
(gdb) bt
#0 force_sigsegv () at testlib.c:13
但是(!)检查line#13指向for-循环中的fnoop()
调用。因此,跟踪不正确,导致错误的信息。
问题2:如何分析优化二进制文件(-flto)生成的核心转储?
谢谢!
发布于 2021-07-15 12:33:00
发布于 2021-07-15 12:42:17
然而,问题1仍然存在。
也来自GCC-9名医生:
启用链接时间优化的另一种(更简单的)方法是:
gcc -o myprog -flto -O2 foo.c bar.c
我想知道为什么这不产生与(即使是docs声明它应该)相同的反汇编:
gcc -c -O2 -flto foo.c
gcc -c -O2 -flto bar.c
gcc -o myprog -flto -O2 foo.o bar.o
因此,如果有人能在这里提供帮助,请这样做:-)
https://stackoverflow.com/questions/68384797
复制相似问题