如何让gdb告诉我给定地址的内存特征

时间:2016-12-13 20:51:52

标签: c++ memory gdb

我有一个核心的gdb会话,它表示在读取其中一个成员时,在指针的解除引用中存在段错误。指针的值非空。我认为这意味着进程无法访问内存地址。如何从核心的事后gdb会话中获取此信息?

例如,给出以下程序:

#include <iostream>

using namespace std;

int
main(int argc, char* argv[])
{
    int *ptr = new int(5);
    cout << "I can access it here: " << *ptr << endl;

    delete ptr;

    cout << "But I shouldn't do so here: " << *ptr << endl;
    return 0;
}

如果我用gdb调试这个程序:

$ g++ -g -Wall test.cc -o test
$ gdb ./test
(gdb) b 13
...
Breakpoint 1 at 0x400943: file test.cc, line 13.
(gdb) run
Starting program: /usr/home/nfs/bneradt/test/test
I can access it here: 5

Breakpoint 1, main (argc=1, argv=0x7fffffffe348) at test.cc:13
13          cout << "But I shouldn't do so here: " << *ptr << endl;
(gdb)

我可以从ptr获得什么样的内存信息?我可以确定ptr指向释放内存吗?因为在核心我调试(不是在上面的玩具测试二进制文件中)我得到一个段错误解除引用 - 即读取,而不是写入 - 指针,我想原来的内存位置被分页,因此没有&过程可访问的内存?我可以从gdb会话中确定吗?

1 个答案:

答案 0 :(得分:2)

gdb没有动态分配的概念,也没有关于释放内存的想法。它可以判断地址是否映射到进程地址空间:

gdb> info files
gdb> maintenance info sections

但由于您的程序已经使用SIGSEGV崩溃,您已经知道它已尝试访问未映射的地址。

其他工具可以检测对已释放内存的访问。 valgrind通常是您的朋友,但不适用于事后调试。您需要在valgrind下运行该程序以深入了解其行为。

解除引用指针时的段错误很少意味着指针指向释放的内存。通常情况下,指针本身位于释放的内存中,并被重用和覆盖。所以

int** ptrarr = new int*[5];
ptrarr[3] = new int(5);
// later
delete [] ptrarr;
// later still after many memory allocations
int* ptr = ptrarr[3]; // ptrarr points to freed memory; UB but no segfault
                      // ptr contains a seemingly random value
int num = *ptr;       // possible segfault

很难从非检测可执行文件的事后转储中提取此类信息。您需要熟悉mallocnew的内部工作,能够追逐他们的内部数据结构,并手动绘制他们的竞技场。这不是一项微不足道的任务。使用带有保留调试符号的标准库版本将有所帮助。

如果不这样做,您可以尝试绘制程序的内存,包括静态/全局变量,堆栈变量,上面指向的所有已分配数据结构,以及其他已分配数据结构中指针指向的所有已分配数据结构。我知道没有工具能够从死后转储中自动执行此操作,但理论上这些工具是可行的。

映射了程序存储器,或者可能是它的某些部分,您可以尝试找出不同类型的不相关对象是否位于无效指针所在的地址。如果是这样,您可以得出结论,两个对象中的一个可能会释放被释放后非法访问的内存。