当程序具有命令行参数时,如何使用 GDB 分析程序的核心转储文件?
- 2024-10-09 09:11:00
- admin 原创
- 80
问题描述:
我的程序运行如下:
exe -p param1 -i param2 -o param3
它崩溃并生成了核心转储文件core.pid
。
我想通过以下方式分析核心转储文件
gdb ./exe -p param1 -i param2 -o param3 core.pid
但是GDB将EXE文件的参数识别为GDB的输入。
在这种情况下,我该如何分析核心转储文件?
解决方案 1:
您可以通过多种方式将核心文件与 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)up
和down
(在堆栈框架(调用链)中导航)
解决方案 2:
GDB的简单使用,调试coredump文件:
gdb <executable_path> <coredump_file_path>
“进程”的核心转储文件被创建为“core.pid”文件。
进入 GDB 提示符后(执行上述命令),输入:
...
(gdb) where
这将为您提供堆栈信息,您可以据此分析崩溃/故障的原因。
其他命令,出于相同目的,是:
...
(gdb) bt full
这与上面的相同。按照惯例,它列出了整个堆栈信息(最终指向崩溃位置)。
解决方案 3:
GDB 最小可运行示例
简单.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 来观察核心文件的内容,以便更好地理解它。因为我们可以。
让我们创建一个程序,打印它自己的一些内存地址,以便我们可以关联事物:
主程序
#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
", text_ptr);
printf("data addr: %p
", data_ptr);
printf("mmap addr: %p
", 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'
这就是为什么我们能够使用常用的 binutils 工具更直接地检查它。
快速浏览一下ELF 标准,可以发现实际上有一个专用于它的 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
注释区域中还存在一些元数据,特别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 中测试。
Mozillarr
逆向调试之终极“核心文件”
核心文件允许您检查中断时的堆栈。
但一般来说,您真正需要做的是回顾过去,进一步确定故障的根本原因。
令人惊奇的 Mozilla rr 允许您这样做,但代价是更大的跟踪文件和轻微的性能损失。
例如:反向调试如何工作?
参见
https://unix.stackexchange.com/questions/89933/how-to-view-core-files-for-debugging- Purposes-in-linux
解决方案 4:
跳过参数。GDB 不需要它们:
gdb ./exe core.pid
解决方案 5:
来自RMS 的 GDB 调试器教程:
prompt > myprogram
Segmentation fault (core dumped)
prompt > gdb myprogram
...
(gdb) core core.pid
...
确保您的文件确实是core
图像--使用 检查它file
。
解决方案 6:
稍微不同的方法将允许您完全跳过 GDB。如果您想要的只是回溯,Linux 特定的实用程序“catchsegv”将捕获 SIGSEGV 并显示回溯。
解决方案 7:
可执行文件是否带有参数并不重要。要对任何带有生成的核心文件的二进制文件运行 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)
从上面的输出,你可以猜测一些有关核心的事情,无论是NULL访问,SIGABORT等。
这些数字 #0 到 #10 是 GDB 的堆栈框架。这些堆栈框架不是你的二进制文件。在上述 0 - 10 帧中,如果你怀疑有任何错误,请选择该帧
(gdb) frame 8
现在查看有关它的更多详细信息:
(gdb) list +
为了进一步调查该问题,您可以在此处打印此时可疑的变量值。
(gdb) print thread_name
解决方案 8:
我只是使用coredumpctl debug
(在 Fedora 32 上),它给我一个 GDB 控制台来调试我最近的核心转储。
解决方案 9:
只需输入命令:
$ gdb <Binary> <codeDump>
或者
$ gdb <binary>
$ gdb) core <coreDump>
无需提供任何命令行参数。代码转储是早期练习生成的。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件