首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >LTO基础(构建顺序和如何分析核心转储)

LTO基础(构建顺序和如何分析核心转储)
EN

Stack Overflow用户
提问于 2021-07-14 20:37:47
回答 2查看 196关注 0票数 0

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

noop.c只提供一个空/死函数w/o任何操作,如下所示:

代码语言:javascript
运行
复制
void fnoop(void) {
}

testlib.c (作为共享库)在fnoop函数上循环以演示优化,并强制分段错误如下:

代码语言:javascript
运行
复制
#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,如:

代码语言:javascript
运行
复制
#include <stdio.h>
#include <stdlib.h>
void force_sigsegv(void);
int main(void) {
   printf("force a sigsegv \n");
   force_sigsegv();
   return 0;
}

因此,这里有两种构建主应用程序的方法:(a)构建testlib共享库和将应用程序链接为:

代码语言:javascript
运行
复制
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共享库和链接应用程序的一步如下:

代码语言:javascript
运行
复制
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-循环优化为:

代码语言:javascript
运行
复制
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-循环仍为:

代码语言:javascript
运行
复制
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以找到崩溃地址,例如:

代码语言:javascript
运行
复制
Core was generated by `./test_lto'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00007fba3aa211b0 in ?? ()

(2)使用readelf -n corefile查找入口所属的对象,例如:

代码语言:javascript
运行
复制
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的文本段偏移量,例如:

代码语言:javascript
运行
复制
  [ 8] .text
PROGBITS         0000000000001040  0000000000001040  0

最后,将这些信息添加到gdb,如下所示:

代码语言:javascript
运行
复制
(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中,就会得到以下结果:

代码语言:javascript
运行
复制
Reading symbols from nlto/libtestlib.so.1...
(gdb) bt
#0  force_sigsegv () at testlib.c:13

但是(!)检查line#13指向for-循环中的fnoop()调用。因此,跟踪不正确,导致错误的信息。

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

谢谢!

EN

回答 2

Stack Overflow用户

发布于 2021-07-15 12:33:00

解决了!GCC-6有一个限制(见这里):

链接时间优化不能很好地用于生成调试信息.将-flto与-g相结合目前还处于试验阶段,预计会产生意想不到的结果。

随着GCC-10 (也检查w/ GCC-9)这一限制被取消(见这里)。

因此,当-flto-g混合时,就解决了堆芯转储轨迹错误的问题(测试w/ GCC-9/10)。就像往常一样,发布已剥离的版本,并将调试版本保存在死后。

票数 1
EN

Stack Overflow用户

发布于 2021-07-15 12:42:17

然而,问题1仍然存在。

也来自GCC-9名医生:

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

我想知道为什么这不产生与(即使是docs声明它应该)相同的反汇编:

代码语言:javascript
运行
复制
gcc -c -O2 -flto foo.c
gcc -c -O2 -flto bar.c
gcc -o myprog -flto -O2 foo.o bar.o

因此,如果有人能在这里提供帮助,请这样做:-)

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68384797

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档