Answers:
您可以通过多种方式将核心与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不需要它们:
gdb ./exe core.pid
objdump
+ gdb
最小的可运行示例
TL; DR:
objdump -s core
可用于批量转储内存现在进行完整的教育测试设置:
main.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);
}
编译并运行以生成核心:
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分析
通过使用像readelf
和objdump
这样的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上进行了测试。
prompt > myprogram
Segmentation fault (core dumped)
prompt > gdb myprogram
...
(gdb) core core.pid
...
确保您的文件确实是core
图像-使用进行检查file
。
稍有不同的方法将使您完全跳过GDB。如果您想要的只是回溯,Linux专用实用程序“ catchsegv”将捕获SIGSEGV并显示回溯。
可执行文件是否具有参数都没有关系。要在具有生成的核心文件的任何二进制文件上运行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”命令分析核心转储文件。
gdb - The GNU Debugger
syntax:
# gdb executable-file core-file
example: # gdb out.txt core.xxx
exe
不是像firefox
在Linux上那样的Shell脚本(用于设置一些变量等)吗?