C程序在GDB中工作,单独运行时崩溃
Posted
技术标签:
【中文标题】C程序在GDB中工作,单独运行时崩溃【英文标题】:C program works within GDB, crashes when run on its own 【发布时间】:2018-08-11 22:12:25 【问题描述】:这是一个大项目,实际上是我定制设计的虚拟机。
在某些情况下,每次我单独运行程序时,程序都会因分段错误而崩溃,但在相同的情况下,在 GDB 中,它可以完美运行并且永远不会崩溃!
在 GDB 内外运行时,我为其提供完全相同的参数和输入。
所以基本上,我找不到 GDB 的错误,因为当我使用 GDB 时它从来没有任何问题。
二进制文件已使用 gcc -g 选项编译。
当我调用时
$ gdb ./main ./memdump
(其中 main 是编译后的程序二进制文件)
并给出 bt 命令,我得到“无堆栈”。我看了这意味着堆栈已经被完全销毁了?
可能是什么原因造成的,我如何才能真正找到错误?
编辑:指令日志的最后几行
这个输出打印在屏幕上,我将它重定向到一个文件。
cmp at address 313
je at address 314
jmp at address 316
inc at address 306
div at address 307
mult at address 308
sub at address 309
cmp at address 310
ecall at ad
它每次都会在随机位置崩溃,并且通常无法完成 printf() 调用,正如您在此处看到的那样。这是什么意思?
对不起,我实际上有错误的核心转储文件。
现在我选对了... 核心回溯显示:
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x000000000040414e in int_call_internal_f (arg=14) at
./opcode_func.c:1503
1503 if (memory[int_config[0] + memory[ip + 1]] !=
INTERRUPT_BLOCKING_VALUE)
(gdb)
这是没有意义的,因为这些都是全局变量,并且在这些索引的值最后一次更改后,这一行会执行数千次。
【问题讨论】:
我建议在你的虚拟机中添加一个日志功能,它将每个操作码的 CPU 状态输出到一个文件中。确保刷新输出。这至少应该缩小正常执行失败的确切范围。 关于部分 printf 输出,这可能是没有刷新的情况。您需要调用fflush
,这将在下一行之前强制输出您的程序。这样你就可以得到整条线。 (此外,如果您重定向到它可能缓冲的文件,请查阅您的 shell 文档)
关于您的 gdb:尝试打印 memory 和 int_config 变量以查看它们是否仍然指向它们应该指向的位置。通常我发现当内存被写入数组或相关的末尾而损坏时会发生这种情况。
但是您也可以按照@kdgregory 的建议检查 valgrind,它旨在捕获您遇到的内存错误。
【参考方案1】:
通常,调试 C 程序意味着将局部变量(和其他内存)初始化为一些众所周知的模式。在释放模式下运行时,您的内存将在分配时有任何位。
另一个问题是优化。如果你有并发错误,在调试器中运行会改变时间,使事情变得模糊。优化还可以巧妙地改变事物的布局,以便在发布模式下爆发的指针错误(尤其是偏移量)无害地覆盖在调试模式下未使用的字节(反之亦然)。
【讨论】:
这里可能不适用。这是基于您是否为发布模式或调试模式编译的差异,而不是您是否正在运行调试器。【参考方案2】:$ gdb ./main ./memdump
这并不像你想象的那样。
GDB 的第二个参数被解释为核心文件,而不是程序的参数。
你想要:
$ gdb ./main
(gdb) set args ./memdump
(gdb) run
... wait for crash
(gdb) bt
【讨论】:
这实际上是我的意图,memdump 是我系统上核心文件的名称。当我使用 set args 在 gdb 中运行程序以提供所需的参数时,它不会崩溃。【参考方案3】:可能是“没有堆栈”可能意味着没有足够的堆栈。通常,如果大型数组/结构未在堆上初始化(使用 malloc 或 new)。您可以使用 -fstack-usage 在 gnu 编译器环境中检查这一点,它会在编译时创建摘要 main.su 文件。
指针设置不正确或数组边界越界也是一个原因,通常在调试中它们可能指向。某处或越界写入可能不会使程序崩溃,但在发布版本中会崩溃。不确定如何使用 gdb 完成此操作,但使用 Microsoft 您可以通过 CrtDebugHeap 获得此功能。可能是 gnu 工具链有类似的选项/库。
【讨论】:
【参考方案4】:分段错误是由程序访问不在其合法地址空间内的内存引起的。错误的近端原因通常与实际原因几乎没有关系:实际错误可能存储了一个无效指针,然后从不相关的代码中取消引用。
正如另一个人评论的那样,一种方法是添加大量日志记录。但是,这通常向您显示近端原因,而不是实际原因:当记录停止时,您可以相当清楚地了解当时程序在做什么。
更好的解决方案是使用内存检查器,例如Valgrind。该工具检测您的代码,并在非法内存访问变成分段违规之前捕获并记录它们。它还可能使您更接近找到实际原因而不是近端原因。
附带说明:我见过的大多数非法内存访问的根源在于基于指针的对堆栈数组或结构的访问。
【讨论】:
以上是关于C程序在GDB中工作,单独运行时崩溃的主要内容,如果未能解决你的问题,请参考以下文章
应用程序在 iPad 中崩溃但在 iPad 模拟器中工作正常
mapView.onCreate 在 api 级别 26 的cordova android 应用程序中工作正常,但在 api 级别 28 时崩溃