通过检查核心和调用堆栈来了解使用GDB的C指针

时间:2014-01-30 19:50:07

标签: c pointers gdb stack

我已经在C专业编码了一段时间,但仍然被一些与指针相关的问题困扰。我真的很感谢SO社区帮助理解下面的问题。

以下代码崩溃并生成核心文件。

void func1()    // Frame 1 in GDB stack trace.  
{ 
    UTYPE  *ptr;  // pointer to user defined type  
    ...

    // data is of type UTYPE and has valid contents.
    // lets say its address is 0x100 
    ptr = &data;     --- (1)  
    ...

    func2(ptr);      --- (2) 
    ...
} 

void func2(UTYPE *inp)    // Frame 0 in GDB stack trace.  
{
    if(!inp)         --- (3) 
        return; 
    ...

    // another_ptr is of UTYPE * which is a NULL.  
    inp = another_ptr;   ---- (4)  

    /* Did not check for NULL and dereference inp and CRASH */    ---- (5) 
} 

简化GDB的回溯:

Frame 0: 
    func2(inp = 0x0) 
    // crash at line (5) due to dereference 

Frame 1: 
    func1: func2(0x0)  
    // `ptr` at line (2) is 0x0. Why is this so? 

为什么{1}在{1}中显示为ptr

调用0x0 (NULL)时,其调用堆栈如下所示:

func2()

对于 | //local vars | | | | another_ptr = | | NULL | +---------------+ | return addr | +---------------+ | input args | | copy of ptr | | contents | | 0x100 | ,其调用堆栈应如下所示:

func1()

| | | ptr = 0x100 | | | +---------------+ | return addr | +---------------+ | input args | | none in this | | func | 在第(4)行的inp中变为NULL时,它如何反映在func1()中?

2 个答案:

答案 0 :(得分:2)

GDB从堆栈构造调用堆栈,并且由于inpfunc2的参数)为0,因此GDB假定传递的参数为0,因此它表示{{用0调用1}}。

崩溃时的堆栈是这样的:

func2

答案 1 :(得分:2)

在C中调用函数时,参数将复制到寄存器中或压入堆栈。被调用的函数可以出于任何目的重用这些寄存器和堆栈位置。通常,但并非总是如此,在函数调用的整个生命周期中,参数都保存在同一寄存器或堆栈位置。

在32位系统上,函数的第一个参数 - 在您的情况下为inp - 通常位于堆栈中,距离堆栈帧的基指针指向的位置为12个字节。请参阅stackoverflow.com: what exactly is program stack's growth direction

gdb执行回溯时,它从编译器获得的唯一指导就是“func2的第一个参数被命名为inp,并且是一个4字节的类型值*UTYPE位于距离%ebp寄存器“。

的12字节偏移处

如果在func2中某处,您改变inp,就像在位置(4)处所做的那样,那么从该点开始的任何回溯都可以很好地显示inp的更改值,在你的情况下,0 inp输入func2时的值永远丢失,除非编译器足够聪明,包括像func2的第一个参数那样的指导名为inp并且是类型*UTYPE的4字节值,通过将堆栈展开到前一帧并查看{{func2的值,可以找到它在ptr上的值。 1}},位于与%ebp寄存器相差4字节的位置。“我相信较新版本的DWARF调试格式可以指定类似的东西。

我无法解释为什么gdb的回溯在ptr的框架中显示为func1的值为0.将inp设置为NULL应该对ptr的价值和gdb显示ptr价值的能力。