繁体   English   中英

当程序具有命令行参数时,如何使用 GDB 分析程序的核心转储文件?

[英]How do I analyze a program's core dump file with GDB when it has command-line parameters?

我的程序是这样运行的:

exe -p param1 -i param2 -o param3

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

我想分析核心转储文件

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

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

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

您可以通过多种方式将核心与 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 选项。

GDB的简单用法,调试coredump文件:

gdb <executable_path> <coredump_file_path>

“进程”的核心转储文件被创建为“core.pid”文件。

进入 GDB 提示符后(在执行上述命令时),键入:

...
(gdb) where

这将为您提供堆栈信息,您可以在其中分析崩溃/故障的原因。 出于相同目的的其他命令是:

...
(gdb) bt full

这与上述相同。 按照惯例,它会列出整个堆栈信息(最终会导致崩溃位置)。

objdump + 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\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);
}

编译并运行以生成核心:

gcc -ggdb3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
ulimit -c unlimited
rm -f core
./main.out

输出:

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

GDB 将我们指向发生分段错误的确切行,这是大多数用户在调试时想要的:

gdb -q -nh main.out core

然后:

Reading symbols from main.out...done.
[New LWP 27479]
Core was generated by `./main.out'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x0000000000400635 in myfunc (i=1) at main.c:7
7           *(int*)(NULL) = i;
(gdb) bt
#0  0x0000000000400635 in myfunc (i=1) at main.c:7
#1  0x000000000040072b in main (argc=1, argv=0x7ffec6739328) at main.c:28

这将我们直接指向了错误的第 7 行。

CLI 参数存储在核心文件中,不需要再次传递

为了回答特定的 CLI 参数问题,我们看到如果我们更改 cli 参数,例如:

rm -f core
./main.out 1 2

那么这确实反映在之前的 bactrace 中,而我们的命令没有任何变化:

Reading symbols from main.out...done.
[New LWP 21838]
Core was generated by `./main.out 1 2'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x0000564583cf2759 in myfunc (i=3) at main.c:7
7           *(int*)(NULL) = i; /* line 7 */
(gdb) bt
#0  0x0000564583cf2759 in myfunc (i=3) at main.c:7
#1  0x0000564583cf2858 in main (argc=3, argv=0x7ffcca4effa8) at main.c:2

所以请注意现在如何argc=3 因此,这一定意味着核心文件存储了该信息。 我猜它只是将它存储为main的参数,就像它存储任何其他函数的参数一样。

如果您认为核心转储必须存储程序的整个内存和寄存器状态,那么它就具有确定当前堆栈上函数参数值所需的所有信息。

不太明显的是如何检查环境变量: 如何从核心转储中获取环境变量环境变量也存在于内存中,因此 objdump 确实包含该信息,但我不确定如何方便地一次性列出所有这些信息, 一一如下确实适用于我的测试:

p __environ[0]

Binutils 分析

通过使用像readelfobjdump这样的 binutils 工具,我们可以批量转储core文件中包含的信息,例如内存状态。

大部分/全部也必须通过 GDB 可见,但那些 binutils 工具提供了更大量的方法,这对于某些用例来说很方便,而 GDB 更方便进行更具交互性的探索。

第一的:

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 上进行了测试。

直接跳过参数。 GDB 不需要它们:

gdb ./exe core.pid

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

来自RMS 的 GDB 调试器教程

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

确保您的文件确实是core映像 - 使用file检查。

可执行文件是否有参数并不重要。 要使用生成的核心文件在任何二进制文件上运行 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 <Binary> <codeDump>

或者

$ gdb <binary>

$ gdb) core <coreDump>

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

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

您可以使用“gdb”命令分析核心转储文件。

 gdb - The GNU Debugger

 syntax:

 # gdb executable-file core-file

 example: # gdb out.txt core.xxx 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM