Debugging C程序在GDB中工作,在自己运行时崩溃

Debugging C程序在GDB中工作,在自己运行时崩溃,debugging,stack,Debugging,Stack,这是一个大项目,实际上是我定制设计的虚拟机 在某些情况下,每次我单独运行程序时,程序都会因分段错误而崩溃,但在GDB中,在同样的情况下,它运行得非常完美,而且从不崩溃 在GDB内外运行时,我给它提供了完全相同的参数和输入 所以基本上,我找不到GDB的bug,因为当我使用GDB时,它从来没有任何问题 二进制文件是用gcc-g选项编译的 当我调用 $gdb./main./memdump 其中main是编译的二进制程序 给bt命令,我就没有堆栈了。我读到这意味着堆栈已被完全破坏 是什么导致了这种情况?

这是一个大项目,实际上是我定制设计的虚拟机

在某些情况下,每次我单独运行程序时,程序都会因分段错误而崩溃,但在GDB中,在同样的情况下,它运行得非常完美,而且从不崩溃

在GDB内外运行时,我给它提供了完全相同的参数和输入

所以基本上,我找不到GDB的bug,因为当我使用GDB时,它从来没有任何问题

二进制文件是用gcc-g选项编译的

当我调用

$gdb./main./memdump

其中main是编译的二进制程序

给bt命令,我就没有堆栈了。我读到这意味着堆栈已被完全破坏

是什么导致了这种情况?我如何才能真正找到这个bug

编辑:指令日志的最后几行

这个输出在屏幕上打印,我将它重定向到一个文件

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) 

这是没有意义的,因为这些都是全局变量,在这些索引的值上次更改后,这一行执行数千次。

一般来说,调试C程序意味着局部变量和其他内存被初始化为一些已知的模式。在释放模式下运行时,您的内存将在分配时具有任何位

另一个问题是优化。如果您有并发错误,在调试器中运行会改变时间,使事情变得模糊。优化还可以微妙地改变事物的布局,使指针错误偏移量,特别是在发布模式下爆炸的指针错误偏移量,在调试模式下无害地覆盖未使用的字节,反之亦然

$ gdb ./main ./memdump
这和你想象的不一样

GDB的第二个参数被解释为核心文件,而不是程序的参数

你想要:

$ gdb ./main
(gdb) set args ./memdump
(gdb) run
... wait for crash
(gdb) bt

分段错误是由程序访问不在其合法地址空间内的内存引起的。错误的近端原因通常与实际原因关系不大:实际错误可能存储无效指针,然后从无关代码中取消引用该指针

另一个人评论说,一种方法是添加大量日志记录。然而,这通常会向您显示最接近的原因,而不是实际原因:当日志记录停止时,您对程序当时在做什么有相当好的了解

更好的解决方案是使用内存检查器,例如。此工具将插入代码,并在非法内存访问转化为分段冲突之前捕获并记录它们。这也可能让你更接近于找到真正的原因,而不是近端的原因


作为旁注:我看到的大多数非法内存访问都是以基于指针的方式访问堆栈上的数组或结构。

可能是“无堆栈”“可能意味着堆栈不足。通常情况下,如果没有使用malloc或new在堆上初始化大型数组/结构,则可以在gnu编译器环境中使用-fstack usage检查这一点,它会在编译时创建一个摘要main.su文件


指针未正确设置或数组边界交叉也是一个原因,通常在调试过程中它们可能会指向。边界上的某个地方或写操作可能不会使程序崩溃,但在发布版本中会崩溃。不确定gdb如何实现这一点,但Microsoft可以通过CrtDebugHeap实现。可能gnu toolchain具有类似的选项/libraries.

我建议在VM中添加一个日志记录功能,在该功能中,它会将每个操作码的CPU状态输出到一个文件中。请确保刷新输出。这至少会缩小正常执行失败的确切范围。关于部分printf输出,这可能是不刷新的情况。您需要调用fflush,这将强制输出ut在下一行之前退出您的程序。这样您就可以得到整行。另外,如果您重定向到一个文件,它可能会缓冲,请参阅您的shell文档关于您的gdb:尝试打印内存和int_config变量,看看它们是否仍然指向它们应该指向的位置。通常,我发现当内存不正确时,会发生这种情况通过写入超过数组或相关数组的结尾而中断。但您也可以按照@kdgregory的建议签出valgrind,它旨在捕获内存错误,例如您遇到的内存错误。此处可能不适用。这取决于您是否为发布模式或调试模式编译,而不是您是否正在运行调试器不是。这实际上是我想要的,memdump是我系统中核心文件的名称。当我在gdb中使用set args运行程序以提供所需的参数时,它不会崩溃。