gdb 通过指向错误的代码行显示不正确的回溯
gdb showing incorrect back trace by pointing at the wrong line of code
我们可以通过在源代码中调用多个中止调用来使用非常简单的示例重现此问题。在下面的示例代码中,我们在不同条件下总共有四个中止调用,但是当我们使用优化标志 (-O3) 进行编译时,我们只能看到一个中止调用的调试信息。因此,在这四个中止调用中发生崩溃的地方,gdb 总是给出具有调试信息的调用。
#include <stdio.h>
#include <stdlib.h>
void level_aa(int a)
{
if (a == 0)
abort();
if (a == 1)
abort();
if (a == 2)
abort();
abort();
}
int main(int argc,char *argv[])
{ int D;
D = atoi(argv[1]);
printf(" Value = %d", D);
level_aa(D);
return 0;
}
使用优化标志 (-O3) 和 运行 将上述代码与 gdb 相结合以重现该问题。
>gcc -g -O3 abort_crash.c -o abort
>gdb ./abort
(gdb)run 1
(gdb) bt
#0 0x00007ffff7ab2945 in *__GI_raise (sig=<optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
#1 0x00007ffff7ab3f21 in *__GI_abort () at abort.c:92
#2 0x0000000000400634 in level_aa (a=<optimized out>) at abort_crash.c:13
#3 main (argc=<optimized out>, argv=<optimized out>) at abort_crash.c:20
(gdb)
如果我们观察第 2 帧(#2),崩溃实际上发生在第 9 行,但 gdb 显示第 13 行。我可以理解它是由于源代码优化而发生的。
在这种情况下是否可以使 gdb 正常工作? (不删除优化)
在此先感谢您的帮助。
Is it possible to make gdb work fine in this case?
GDB 在这种情况下工作正常:它向您展示了 实际 发生的事情,即 ...
(with out removing optimization)
...编译器发现多个路径导致abort
函数不能return并且没有副作用,所以合并 这些路径结合在一起,减少了生成代码的大小。看看生成的程序集:
(gdb) disas level_aa
Dump of assembler code for function level_aa:
0x0000000000400620 <+0>: sub [=10=]x8,%rsp
0x0000000000400624 <+4>: callq 0x4004b0 <abort@plt>
End of assembler dump.
你所有的if
都完全优化了。
如果你想区分大小写,你必须让编译器知道这些路径不是等价的。
在当前翻译单元中放置一个printf
调用,或对编译器不可见的外部函数的调用(函数本身可能调用abort
):
extern int my_abort(int); // must be in a different TU
void level_aa(int a)
{
if (a == 0)
my_abort(a + 1);
if (a == 1)
my_abort(a + 2);
if (a == 2)
my_abort(a + 3);
abort();
}
注意:需要 a+1
、a+2
等以防止编译器合并单独的路径。如果您只是调用 my_abort(a)
,编译器可能 仍 将它们合并在一起,就好像您写了:
if (a >= 0 && a <= 2) my_abort(a);
请注意,如果您使用整个程序优化,即使这种外部函数方法也可能会失败。
我们可以通过在源代码中调用多个中止调用来使用非常简单的示例重现此问题。在下面的示例代码中,我们在不同条件下总共有四个中止调用,但是当我们使用优化标志 (-O3) 进行编译时,我们只能看到一个中止调用的调试信息。因此,在这四个中止调用中发生崩溃的地方,gdb 总是给出具有调试信息的调用。
#include <stdio.h>
#include <stdlib.h>
void level_aa(int a)
{
if (a == 0)
abort();
if (a == 1)
abort();
if (a == 2)
abort();
abort();
}
int main(int argc,char *argv[])
{ int D;
D = atoi(argv[1]);
printf(" Value = %d", D);
level_aa(D);
return 0;
}
使用优化标志 (-O3) 和 运行 将上述代码与 gdb 相结合以重现该问题。
>gcc -g -O3 abort_crash.c -o abort
>gdb ./abort
(gdb)run 1
(gdb) bt
#0 0x00007ffff7ab2945 in *__GI_raise (sig=<optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
#1 0x00007ffff7ab3f21 in *__GI_abort () at abort.c:92
#2 0x0000000000400634 in level_aa (a=<optimized out>) at abort_crash.c:13
#3 main (argc=<optimized out>, argv=<optimized out>) at abort_crash.c:20
(gdb)
如果我们观察第 2 帧(#2),崩溃实际上发生在第 9 行,但 gdb 显示第 13 行。我可以理解它是由于源代码优化而发生的。 在这种情况下是否可以使 gdb 正常工作? (不删除优化) 在此先感谢您的帮助。
Is it possible to make gdb work fine in this case?
GDB 在这种情况下工作正常:它向您展示了 实际 发生的事情,即 ...
(with out removing optimization)
...编译器发现多个路径导致abort
函数不能return并且没有副作用,所以合并 这些路径结合在一起,减少了生成代码的大小。看看生成的程序集:
(gdb) disas level_aa
Dump of assembler code for function level_aa:
0x0000000000400620 <+0>: sub [=10=]x8,%rsp
0x0000000000400624 <+4>: callq 0x4004b0 <abort@plt>
End of assembler dump.
你所有的if
都完全优化了。
如果你想区分大小写,你必须让编译器知道这些路径不是等价的。
在当前翻译单元中放置一个printf
调用,或对编译器不可见的外部函数的调用(函数本身可能调用abort
):
extern int my_abort(int); // must be in a different TU
void level_aa(int a)
{
if (a == 0)
my_abort(a + 1);
if (a == 1)
my_abort(a + 2);
if (a == 2)
my_abort(a + 3);
abort();
}
注意:需要 a+1
、a+2
等以防止编译器合并单独的路径。如果您只是调用 my_abort(a)
,编译器可能 仍 将它们合并在一起,就好像您写了:
if (a >= 0 && a <= 2) my_abort(a);
请注意,如果您使用整个程序优化,即使这种外部函数方法也可能会失败。