想知道在 gdb 中收到段错误时到底发生了什么
want to know exactly what happen when receiving seg fault in gdb
也许初学者程序中最常见的错误是它包含段错误。但是,当使用 gdb 进行调试时,我无法了解段错误到底是什么以及它是如何发生的。例如,gdb 调试器会给出 program received SEGMENTATION FAULT
或 program receive SEGMENTATION FAULT, in main.c:13: bool flag=false
。 (实际上,我相信程序不会因为布尔变量定义而收到段错误。肯定还有其他一些东西)类似的东西。
但这并不具体,信息量也不够。我想确切地知道是哪个变量导致段错误及其位置。例如,如果我在没有初始化的函数中定义了一个指针 A
,然后在之后使用它,大多数情况下我会收到段错误。我希望 gdb 准确地告诉我是那个变量 A
导致了 seg 错误,它的值和位置是...
有什么想法吗?
回溯 (bt) 连同源代码行/变量打印输出通常足以诊断导致问题的原因。为此,您需要使用调试信息进行构建,并且无需进行大多数优化(GCC 中的 -O0 -g3)。
或者尝试一些不同的工具,例如 Valgrind,这使得诊断内存分配问题变得更加容易。
那我举个简单的例子如下
#include <stdio.h>
void main()
{
int *p=NULL;
printf("I Should be coring now");
printf("%d", *p);
}
正如您可能猜到的那样,printf("%d", *p);
中存在问题,但它不会在 gdb 的输出中清楚地显示出来。
bash-4.1$ gdb test
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-60.SCLC6_4.1.R1.1.1)
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /tmp/test...done.
(gdb) run
Starting program: /tmp/test
Program received signal SIGSEGV, Segmentation fault.
0x080483e6 in main () at test.c:7
7 printf("%d", *p);
在这个微不足道的案例中,它非常简单。但为了名字起见,让我们假设,我们需要更多信息。因此,现在让我们尝试在 SIGSEGV
发生时找到 pc
。 pc
有效地表示,在执行该特定指令时,核心发生了。
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.107.SCLC6_4.5.R1.1.1.i686
(gdb) info registers
eax 0x0 0
ecx 0xbffff6f8 -1073744136
edx 0x2c5340 2904896
ebx 0x2c3ff4 2899956
esp 0xbffff710 0xbffff710
ebp 0xbffff738 0xbffff738
esi 0x0 0
edi 0x0 0
eip 0x80483e6 0x80483e6 <main+34>
eflags 0x10292 [ AF SF IF RF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
所以在我们的例子中,pc
是 eip 0x80483e6 0x80483e6 <main+34>
现在很清楚,在执行距 main 偏移量为 34 的指令时,发生了核心。所以现在,重要的问题是用高级语言映射这条指令。为此使用 disasm
命令如下。
(gdb) disas /m main
Dump of assembler code for function main:
4 {
0x080483c4 <+0>: push %ebp
0x080483c5 <+1>: mov %esp,%ebp
0x080483c7 <+3>: and [=13=]xfffffff0,%esp
0x080483ca <+6>: sub [=13=]x20,%esp
5 int *p=NULL;
0x080483cd <+9>: movl [=13=]x0,0x1c(%esp)
6 printf("I Should be coring now");
0x080483d5 <+17>: mov [=13=]x80484c4,%eax
0x080483da <+22>: mov %eax,(%esp)
0x080483dd <+25>: call 0x80482f4 <printf@plt>
7 printf("%d", *p);
0x080483e2 <+30>: mov 0x1c(%esp),%eax
=> 0x080483e6 <+34>: mov (%eax),%edx
0x080483e8 <+36>: mov [=13=]x80484db,%eax
0x080483ed <+41>: mov %edx,0x4(%esp)
0x080483f1 <+45>: mov %eax,(%esp)
0x080483f4 <+48>: call 0x80482f4 <printf@plt>
8 }
0x080483f9 <+53>: leave
0x080483fa <+54>: ret
End of assembler dump.
(gdb)
虽然这可能无法直接使用,但根据我的经验,它将提供接近 90% 的准确率。
也许初学者程序中最常见的错误是它包含段错误。但是,当使用 gdb 进行调试时,我无法了解段错误到底是什么以及它是如何发生的。例如,gdb 调试器会给出 program received SEGMENTATION FAULT
或 program receive SEGMENTATION FAULT, in main.c:13: bool flag=false
。 (实际上,我相信程序不会因为布尔变量定义而收到段错误。肯定还有其他一些东西)类似的东西。
但这并不具体,信息量也不够。我想确切地知道是哪个变量导致段错误及其位置。例如,如果我在没有初始化的函数中定义了一个指针 A
,然后在之后使用它,大多数情况下我会收到段错误。我希望 gdb 准确地告诉我是那个变量 A
导致了 seg 错误,它的值和位置是...
有什么想法吗?
回溯 (bt) 连同源代码行/变量打印输出通常足以诊断导致问题的原因。为此,您需要使用调试信息进行构建,并且无需进行大多数优化(GCC 中的 -O0 -g3)。
或者尝试一些不同的工具,例如 Valgrind,这使得诊断内存分配问题变得更加容易。
那我举个简单的例子如下
#include <stdio.h>
void main()
{
int *p=NULL;
printf("I Should be coring now");
printf("%d", *p);
}
正如您可能猜到的那样,printf("%d", *p);
中存在问题,但它不会在 gdb 的输出中清楚地显示出来。
bash-4.1$ gdb test
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-60.SCLC6_4.1.R1.1.1)
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /tmp/test...done.
(gdb) run
Starting program: /tmp/test
Program received signal SIGSEGV, Segmentation fault.
0x080483e6 in main () at test.c:7
7 printf("%d", *p);
在这个微不足道的案例中,它非常简单。但为了名字起见,让我们假设,我们需要更多信息。因此,现在让我们尝试在 SIGSEGV
发生时找到 pc
。 pc
有效地表示,在执行该特定指令时,核心发生了。
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.107.SCLC6_4.5.R1.1.1.i686
(gdb) info registers
eax 0x0 0
ecx 0xbffff6f8 -1073744136
edx 0x2c5340 2904896
ebx 0x2c3ff4 2899956
esp 0xbffff710 0xbffff710
ebp 0xbffff738 0xbffff738
esi 0x0 0
edi 0x0 0
eip 0x80483e6 0x80483e6 <main+34>
eflags 0x10292 [ AF SF IF RF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
所以在我们的例子中,pc
是 eip 0x80483e6 0x80483e6 <main+34>
现在很清楚,在执行距 main 偏移量为 34 的指令时,发生了核心。所以现在,重要的问题是用高级语言映射这条指令。为此使用 disasm
命令如下。
(gdb) disas /m main
Dump of assembler code for function main:
4 {
0x080483c4 <+0>: push %ebp
0x080483c5 <+1>: mov %esp,%ebp
0x080483c7 <+3>: and [=13=]xfffffff0,%esp
0x080483ca <+6>: sub [=13=]x20,%esp
5 int *p=NULL;
0x080483cd <+9>: movl [=13=]x0,0x1c(%esp)
6 printf("I Should be coring now");
0x080483d5 <+17>: mov [=13=]x80484c4,%eax
0x080483da <+22>: mov %eax,(%esp)
0x080483dd <+25>: call 0x80482f4 <printf@plt>
7 printf("%d", *p);
0x080483e2 <+30>: mov 0x1c(%esp),%eax
=> 0x080483e6 <+34>: mov (%eax),%edx
0x080483e8 <+36>: mov [=13=]x80484db,%eax
0x080483ed <+41>: mov %edx,0x4(%esp)
0x080483f1 <+45>: mov %eax,(%esp)
0x080483f4 <+48>: call 0x80482f4 <printf@plt>
8 }
0x080483f9 <+53>: leave
0x080483fa <+54>: ret
End of assembler dump.
(gdb)
虽然这可能无法直接使用,但根据我的经验,它将提供接近 90% 的准确率。