微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

LTO 基础知识构建顺序以及如何分析核心转储

如何解决LTO 基础知识构建顺序以及如何分析核心转储

正如标题所说,我确实有两个关于 LTO 的基本问题。首先,这里有一些简单的演示源文件来展示我的基本理解问题。

noop.c 只发送一个空的/死的函数,没有任何操作:

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,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,1)
11eb:       00 00 00
11ee:       66 90                   xchg   %ax,%ax

问题 1:为什么在 (b) 的情况下 for 循环没有优化?

接下来:分析上面两个应用程序生成的核心转储。 假设我们有 test_lto,它使用来自 (a) 的优化的 testlib 共享库,在我们的生产系统中,它强制核心转储,然后用户将其发送给我。我将从以下方面开始分析: (1) 加载到gdb中查找crash地址,例如:

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 ()

因此,无法收集(有用的)崩溃细节。即使使用来自 (b) 的 lto_single 版本的 testlib 也没有提供更多细节。但是,构建相同的对象而无需 -flto 但使用 -g 并使用相同的地址 0x00007fba3aa20000+0x1040 加载到 gdb 给出:

Reading symbols from nlto/libtestlib.so.1...
(gdb) bt
#0  force_sigsegv () at testlib.c:13

But(!) 检查第 13 行指向 for 循环内的 fnoop() 调用。所以跟踪不正确导致信息错误

问题 2:如何分析优化二进制文件 (-flto) 生成的核心转储?

谢谢!

解决方法

解决了! GCC-6 存在限制(参见 here):

链接时优化不适用于调试信息的生成。将 -flto 与 -g 结合使用目前处于试验阶段,预计会产生意想不到的结果。

使用 GCC-10(也使用 GCC-9 进行检查)删除了此限制(请参阅 here)。

因此,当混合 -flto-g 时,有关错误核心转储跟踪的问题得到解决(使用 GCC-9/10 进行测试)。照常做,发布剥离版本并保留调试版本以供事后分析。

,

然而,问题 1 仍然存在。

同样来自 GCC-9 文档:

启用链接时优化的另一种(更简单)方法是: gcc -o myprog -flto -O2 foo.c bar.c

我想知道为什么这不会产生与(甚至文档说明应该的)相同的反汇编:

gcc -c -O2 -flto foo.c
gcc -c -O2 -flto bar.c
gcc -o myprog -flto -O2 foo.o bar.o

所以如果有人可以在这里提供帮助,请这样做:-)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。