想知道在 gdb 中收到段错误时到底发生了什么

want to know exactly what happen when receiving seg fault in gdb

也许初学者程序中最常见的错误是它包含段错误。但是,当使用 gdb 进行调试时,我无法了解段错误到底是什么以及它是如何发生的。例如,gdb 调试器会给出 program received SEGMENTATION FAULTprogram 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 发生时找到 pcpc 有效地表示,在执行该特定指令时,核心发生了。

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

所以在我们的例子中,pceip 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% 的准确率。