我如何分析一个程序的核心转储文件与GDB时,它有命令行参数?

我的程序是这样运行的:

exe -p param1 -i param2 -o param3

它崩溃并生成了一个核心转储文件core.pid

我想分析一下核心转储文件

gdb ./exe -p param1 -i param2 -o param3 core.pid

但是GDB将EXE文件的参数识别为GDB的输入。

在这种情况下,我如何分析核心转储文件?

609677 次浏览

跳过参数即可。广发银行不需要他们:

gdb ./exe core.pid

RMS的GDB调试器教程:

prompt > myprogram
Segmentation fault (core dumped)
prompt > gdb myprogram
...
(gdb) core core.pid
...

确保你的文件确实是一个core图像——使用file检查它。

您可以通过多种方式将核心文件与GDB一起使用,但是将要传递给可执行文件的参数传递给GDB并不是使用核心文件的方法。这也可能是你得到错误的原因。您可以通过以下方式使用核心文件:

gdb <executable> <core-file>gdb <executable> -c <core-file>

gdb <executable>
...
(gdb) core <core-file>

当使用核心文件时,你不需要传递参数。崩溃场景显示在GDB中(在Ubuntu上使用GDB 7.1版本进行了检查)。

例如:

$ ./crash -p param1 -o param2
Segmentation fault (core dumped)
$ gdb ./crash core
GNU gdb (GDB) 7.1-ubuntu
...
Core was generated by `./crash -p param1 -o param2'. <<<<< See this line shows crash scenario
Program terminated with signal 11, Segmentation fault.
#0  __strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:99
99    ../sysdeps/i386/i686/multiarch/../../i586/strlen.S: No such file or directory.
in ../sysdeps/i386/i686/multiarch/../../i586/strlen.S
(gdb)

如果你想将参数传递给要在GDB中调试的可执行文件,请使用--args

例如:

$ gdb --args ./crash -p param1 -o param2
GNU gdb (GDB) 7.1-ubuntu
...
(gdb) r
Starting program: /home/@@@@/crash -p param1 -o param2


Program received signal SIGSEGV, Segmentation fault.
__strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:99
99    ../sysdeps/i386/i686/multiarch/../../i586/strlen.S: No such file or directory.
in ../sysdeps/i386/i686/multiarch/../../i586/strlen.S
(gdb)

手册页将有助于查看其他GDB选项。

最有用的命令是:

  • bt(回溯)
  • info locals(显示局部变量的值)
  • info registers(显示CPU寄存器的值)
  • frame X(更改为堆栈帧X)
  • updown(在堆栈框架中导航(调用链))

简单使用GDB,调试coredump文件:

gdb <executable_path> <coredump_file_path>

“进程”的coredump文件被创建为“核心”。pid”文件。

在你进入GDB提示符后(在执行上面的命令时),输入:

...
(gdb) where
这将为您提供堆栈的信息,在那里您可以分析崩溃/故障的原因。 其他命令,用于同样的目的是:

...
(gdb) bt full

这和上面一样。按照惯例,它列出了整个堆栈信息(最终导致崩溃的位置)。

稍微不同的方法将允许您完全跳过GDB。如果你想要的只是一个回溯,linux特定的实用程序“catchsegv”将捕获SIGSEGV并显示回溯。

只需键入命令:

$ gdb <Binary> <codeDump>

$ gdb <binary>


$ gdb) core <coreDump>

不需要提供任何命令行参数。由于前面的练习而生成的代码转储。

可执行文件是否有参数并不重要。要在任何具有生成的核心文件的二进制文件上运行GDB,语法如下所示。

Syntax:
gdb <binary name> <generated core file>
Eg:
gdb l3_entity 6290-corefile

为了更好地理解,让我举下面的例子。

bash-4.1$ **gdb l3_entity 6290-corefile**


**Core was generated** by `/dir1/dir2/dir3/l3_entity **Program terminated with signal SIGABRT, Aborted.**
#0
#1
#2
#3
#4
#5
#6
#7
#8
#9
#10
(gdb)

从上面的输出中,您可以猜测一些关于core的信息,无论是NULL访问还是SIGABORT等等。

这些数字#0到#10是GDB的堆栈帧。这些堆栈帧不是二进制的。在上面的0 - 10帧中,如果你怀疑有任何错误,选择该帧

(gdb) frame 8

现在来看看更多的细节:

(gdb) list +

要进一步研究这个问题,您可以在此时打印可疑的变量值。

(gdb) print thread_name

GDB最小可运行示例

simple.c

int myfunc(int i) {
*(int*)(0) = i;
return i - 1;
}


int main(int argc, char **argv) {
(void)argv;
int i = argc * 2;
int ret = myfunc(i);
return ret;
}

编译,并运行生成核心:

gcc -ggdb3 -std=c99 -Wall -Wextra -pedantic -o simple.out simple.c

为了生成核心文件,我们首先必须在当前终端中运行:

ulimit -c unlimited

这意味着“转储核心文件没有任何大小限制”。这是因为核心文件包含崩溃进程的整个内存,因此它们可能非常大。

在Ubuntu 16.04的测试中,你必须删除一个预先存在的核心文件(TODO强制?我忘了):

rm -f core

在Ubuntu 22.04的测试中,你需要对抗apport来获得你的核心文件:https://askubuntu.com/questions/1349047/where-do-i-find-core-dump-files-and-how-do-i-view-and-analyze-the-backtrace-st/1442665#1442665,例如:

echo 'core' | sudo tee /proc/sys/kernel/core_pattern

然后我们运行程序:

./simple.out

终端包含:

Segmentation fault (core dumped)

已生成核心文件。在Ubuntu 16.04中,文件命名为:

core

在Ubuntu 22.04中,echo 'core' | sudo tee /proc/sys/kernel/core_pattern之后的文件命名为:

core.<pid>

其中PID是进程ID,一个数字,例如:

core.162152

我认为这是因为Linux内核更新开始添加.pid后缀。做确认。

现在,我们可以将核心文件用作任何一种

gdb simple.out core
gdb simple.out core.162152

现在我们进入一个GDB会话,这与程序崩溃时的情况完全相同,当然,除了我们不能“继续运行”;随着节目即将结束:

#0  0x0000557097e0813c in myfunc (i=2) at simple.c:2
2           *(int*)(0) = i; /* line 7 */
(gdb) bt
#0  0x0000557097e0813c in myfunc (i=2) at simple.c:2
#1  0x0000557097e0816b in main (argc=1, argv=0x7ffcffc4ba18) at simple.c:9
(gdb) up
#1  0x0000557097e0816b in main (argc=1, argv=0x7ffcffc4ba18) at simple.c:9
9           int ret = myfunc(i);
(gdb) p argc
$1 = 1

因此,在运行bt之后,我们立即了解代码崩溃时的位置,这有时足以解决错误。

从示例中可以看到,现在可以在崩溃时检查程序内存,以尝试确定失败的原因,进程虚拟内存完全包含在核心文件中。

在Ubuntu 16.04和22.04 amd64中测试。

您也可以直接通过GDB运行程序

如果问题很容易重现(即快速且确定性地崩溃),并且你可以很容易地控制命令行(即不是一个程序被另一个你不想/不能修改的程序调用),那么最好的方法是通过GDB运行程序:

gdb -ex run simple.out

当接收到信号时,GDB默认情况下会因为信号原因而中断,我们将处于与使用核心文件时完全相同的情况。

直接Binutils分析

让我们在没有GDB的情况下观察核心文件的内容,以便更好地理解它。因为我们可以。

让我们创建一个程序,打印它自己的一些内存地址,这样我们就可以把事情联系起来:

c

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int myfunc(int i) {
*(int*)(NULL) = i; /* line 7 */
return i - 1;
}


int main(int argc, char **argv) {
/* Setup some memory. */
char data_ptr[] = "string in data segment";
char *mmap_ptr;
char *text_ptr = "string in text segment";
(void)argv;
mmap_ptr = (char *)malloc(sizeof(data_ptr) + 1);
strcpy(mmap_ptr, data_ptr);
mmap_ptr[10] = 'm';
mmap_ptr[11] = 'm';
mmap_ptr[12] = 'a';
mmap_ptr[13] = 'p';
printf("text addr: %p\n", text_ptr);
printf("data addr: %p\n", data_ptr);
printf("mmap addr: %p\n", mmap_ptr);


/* Call a function to prepare a stack trace. */
return myfunc(argc);
}

项目输出:

text addr: 0x4007d4
data addr: 0x7ffec6739220
mmap addr: 0x1612010
Segmentation fault (core dumped)

第一:

file core

告诉我们core文件实际上是一个ELF文件:

core: ELF 64-bit LSB core file x86-64, version 1 (SYSV), SVR4-style, from './main.out'

这就是为什么我们能够更直接地检查它与通常的双utils工具。

快速查看精灵的标准可以发现,实际上有一个专用于它的ELF类型:

Elf32_Ehd.e_type == ET_CORE

进一步的格式信息可在以下网址找到:

man 5 core

然后:

readelf -Wa core

给出一些关于文件结构的提示。内存似乎包含在常规的程序头中:

Program Headers:
Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
NOTE           0x000468 0x0000000000000000 0x0000000000000000 0x000b9c 0x000000     0
LOAD           0x002000 0x0000000000400000 0x0000000000000000 0x001000 0x001000 R E 0x1000
LOAD           0x003000 0x0000000000600000 0x0000000000000000 0x001000 0x001000 R   0x1000
LOAD           0x004000 0x0000000000601000 0x0000000000000000 0x001000 0x001000 RW  0x1000

还有一些元数据出现在notes区域,特别是prstatus包含PC:

Displaying notes found at file offset 0x00000468 with length 0x00000b9c:
Owner                 Data size       Description
CORE                 0x00000150       NT_PRSTATUS (prstatus structure)
CORE                 0x00000088       NT_PRPSINFO (prpsinfo structure)
CORE                 0x00000080       NT_SIGINFO (siginfo_t data)
CORE                 0x00000130       NT_AUXV (auxiliary vector)
CORE                 0x00000246       NT_FILE (mapped files)
Page size: 4096
Start                 End         Page Offset
0x0000000000400000  0x0000000000401000  0x0000000000000000
/home/ciro/test/main.out
0x0000000000600000  0x0000000000601000  0x0000000000000000
/home/ciro/test/main.out
0x0000000000601000  0x0000000000602000  0x0000000000000001
/home/ciro/test/main.out
0x00007f8d939ee000  0x00007f8d93bae000  0x0000000000000000
/lib/x86_64-linux-gnu/libc-2.23.so
0x00007f8d93bae000  0x00007f8d93dae000  0x00000000000001c0
/lib/x86_64-linux-gnu/libc-2.23.so
0x00007f8d93dae000  0x00007f8d93db2000  0x00000000000001c0
/lib/x86_64-linux-gnu/libc-2.23.so
0x00007f8d93db2000  0x00007f8d93db4000  0x00000000000001c4
/lib/x86_64-linux-gnu/libc-2.23.so
0x00007f8d93db8000  0x00007f8d93dde000  0x0000000000000000
/lib/x86_64-linux-gnu/ld-2.23.so
0x00007f8d93fdd000  0x00007f8d93fde000  0x0000000000000025
/lib/x86_64-linux-gnu/ld-2.23.so
0x00007f8d93fde000  0x00007f8d93fdf000  0x0000000000000026
/lib/x86_64-linux-gnu/ld-2.23.so
CORE                 0x00000200       NT_FPREGSET (floating point registers)
LINUX                0x00000340       NT_X86_XSTATE (x86 XSAVE extended state)

objdump可以很容易地转储所有内存:

objdump -s core

它包含:

Contents of section load1:


4007d0 01000200 73747269 6e672069 6e207465  ....string in te
4007e0 78742073 65676d65 6e740074 65787420  xt segment.text


Contents of section load15:


7ffec6739220 73747269 6e672069 6e206461 74612073  string in data s
7ffec6739230 65676d65 6e740000 00a8677b 9c6778cd  egment....g{.gx.


Contents of section load4:


1612010 73747269 6e672069 6e206d6d 61702073  string in mmap s
1612020 65676d65 6e740000 11040000 00000000  egment..........

它与我们运行中的stdout值完全匹配。

在Ubuntu 16.04 amd64, GCC 6.4.0, binutils 2.26.1中测试。

Mozilla rr反向调试作为最终的“核心文件”;

核心文件允许您在休息时检查堆栈。

但一般来说,你真正需要做的是回顾过去,进一步确定根本故障原因。

令人惊奇的Mozilla rr允许您这样做,但代价是创建了一个更大的跟踪文件,并有轻微的性能损失。

示例:反向调试是如何工作的?

另请参阅

  • https://unix.stackexchange.com/questions/89933/how-to-view-core-files-for-debugging-purposes-in-linux < a href = " https://unix.stackexchange.com/questions/89933/how-to-view-core-files-for-debugging-purposes-in-linux " > < / >

我只使用coredumpctl debug(在Fedora 32上),它给了我一个GDB控制台来调试我最近的核心转储。