天天看点

c语言调试过运行崩溃,C程序在GDB中运行,在自己运行时崩溃(示例代码)

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

在某些情况下,每次我自己运行程序时,程序都会出现分段错误,但在同样的情况下,它会在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)

这没有任何意义,因为这些都是全局变量,并且在这些索引的值最后更改之后,此行会执行数千次。

答案

通常,调试C程序意味着将局部变量(和其他内存)初始化为一些众所周知的模式。在释放模式下运行时,您的内存将具有分配时的任何位。

另一个问题是优化。如果你有一个并发错误,在调试器中运行会改变时间,遮挡东西。优化还可以巧妙地改变事物的布局,以便在释放模式中爆炸的指针错误(特别是偏移)无害地在调试模式中覆盖未使用的字节(反之亦然)。

另一答案

$ gdb ./main ./memdump

这不符合你的想法。

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

你要:

$ gdb ./main

(gdb) set args ./memdump

(gdb) run

... wait for crash

(gdb) bt

另一答案

可能是“没有堆栈”可能意味着没有足够的堆栈。通常,如果没有在堆上初始化大型数组/结构(使用malloc或new)。您可以使用-fstack-usage在gnu编译器环境中进行检查,这将在编译时创建摘要main.su文件。

指针未正确设置或阵列边界交叉也是一个原因,通常在他们可能指出的调试中。某处或边界上的写入可能不会使程序崩溃,但在发布版本中它会崩溃。不确定如何使用gdb,但使用Microsoft,您可以通过CrtDebugHeap获得此功能。可能是gnu工具链有类似的选项/库。

另一答案

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

另一个人评论说,一种方法是增加广泛的日志记录。但是,这通常会向您显示近端原因,而不是实际原因:当日志记录停止时,您可以很好地了解该程序当时正在执行的操作。

更好的解决方案是使用内存检查器,例如Valgrind。此工具可以检测您的代码,并在它们变成分段违规之前捕获并记录非法内存访问。它也可能让你更接近找到实际原因而不是近端原因。

作为旁注:我见过的大多数非法内存访问都是基于对堆栈数组或结构的基于指针的访问。