我有一个讨厌的segfault,它已经困扰了我一段时间了。它与代码从32位迁移到64位有关,但这是一个偶然的错误,很难追踪。
我想知道——有没有任何工具(首选Linux、FOSS)可以用来从segfault进行追溯,找到我的代码中非法(越界)指针的值分配到哪里?
例如,如果我通过尝试读取变量int *a
所指向的int
值(该值已在代码中的其他地方分配)来获得segfault,那么我如何在代码中分配该值的位置找到?
这似乎是一种可以用clang/llvm做的事情,但我真的不知道该去哪里看。我想这样的想法真的不能用gdb或valgrind来完成,因为IFAIK他们没有办法在程序执行期间存储所需的信息。
任何人得到的建议都将不胜感激!
编辑: 经过大量挖掘,我发现了我一直在寻找的错误。基本上,一个"unsigned long*"被强制转换为"int*",从而以某种方式抑制了警告(http://ascend4.org/b564)。然而,问题仍然存在,因为我的错误搜索非常手动和乏味:如果我的程序中有一个变量,我如何追溯到是什么序列/链/树的语句导致它取当前值是否有任何工具可以自动执行此操作?这包括将参数传递给函数、赋值语句(包括通过取消引用的指针进行赋值)等。
内存断点(GDB文档中的观察点)听起来像是要走的路。使用-g
进行编译以调试符号,然后放置一个内存写入断点,如下所示:
print &a
watch *0xdeadbeef
如果您也想包含读取内容,可以使用awatch
。查看GDB文档了解更多信息。这样,您应该能够在分段故障发生之前跟踪最后一次写入。
对于此类问题,您应该使用的工具是valgrind
。例如,使用Valgrind尝试以下代码:
char *str = malloc(10);
str[10] = ' ';
它打印:
==14272== Invalid write of size 1
==14272== at 0x80483E4: main (in /path/to/a.out)
==14272== Address 0x4025032 is 0 bytes after a block of size 10 alloc'd
==14272== at 0x4005BDC: malloc (vg_replace_malloc.c:195)
==14272== by 0x80483D8: main (in /path/to/a.out)
但是,如果valgrind
不适用,则可以选择用mmap
替换malloc
,使分配的块开始之前的字节或分配的块结束之后的字节未映射。因为块大小通常不是页面大小的倍数,所以只能选择其中一个选项,而不能同时选择两个选项。但您可以分别使用"不映射开始"one_answers"不映射结束"策略来处理问题,以捕获这两种错误。
不幸的是,关于如何用mmap
替换malloc
的代码对于这个答案来说太长了。