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

通过检查核心和调用堆栈了解使用GDB的C指针,c,pointers,gdb,stack,C,Pointers,Gdb,Stack,我已经专业地用C语言编写代码一段时间了,但仍然被一些与指针相关的问题难住了。我真的很感激社区在理解以下问题上的帮助 以下代码崩溃并生成核心文件 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

我已经专业地用C语言编写代码一段时间了,但仍然被一些与指针相关的问题难住了。我真的很感激社区在理解以下问题上的帮助

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

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? 
为什么
ptr
在第1帧中显示为
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)行的
func2()
中的
inp
变为
NULL
时,它在func1()中是如何反映的

GDB从堆栈构造调用堆栈,由于
inp
(它是
func2
的一个参数)是0,GDB假设传递的参数是0,因此它表示用0调用了
func2

崩溃时的堆栈是这样的:

another_ptr = 0  ( func2 local variable )
return address to func1                   
inp = 0          ( func2 parameter )
ptr              ( func1 local variable )

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

在32位系统上,函数的第一个参数(在您的例子中为
inp
)通常位于堆栈上,距离堆栈帧的基本指针指向的位置12字节。看

gdb
执行回溯时,编译器提供的唯一指导类似于“func2的第一个参数命名为
inp
,是
*UTYPE
类型的4字节值,位于%ebp寄存器的12字节偏移量处”

如果在
func2
中的某个地方,您像在位置(4)一样更改
inp
,那么从该点开始的任何回溯都可能显示
inp
的更改值,在您的情况下,为0。当输入
func2
时,
inp
的值将永远丢失,除非编译器足够聪明,能够包含如下指导
func2
的第一个参数名为
inp
,是类型为
*UTYPE
的4字节值,进入
func2
时,可以通过将堆栈展开到上一帧并查看
ptr
的值来找到它的值,该值位于%ebp寄存器的-4字节偏移处。“我相信,较新版本的DWARF调试格式可以指定类似的内容


我无法解释为什么您的
gdb
的回溯在
func1
的帧中显示
ptr
的值为0。将
inp
设置为NULL应该不会影响
ptr
的值,也不会影响gdb显示
ptr
值的能力。

我喜欢你的表示法。这很奇怪。我希望第1帧的回溯在标记为(2)的LineNumber of定位处显示
func1(),而不显示对
func2
的调用。您使用的是
bt
命令还是其他命令?@MarkPlotnick我使用的是
bt
命令。它打印所有堆栈帧,而不仅仅是帧1。我上面对GDB回溯的描述只是为了帮助解释我的问题。如果有什么不对劲,请告诉我。我的gdb的
bt
命令显示调用
func2()
的第0帧和调用
func1
的第1帧。其第1帧的显示不像您的那样显示对
func2
的调用。我只是无法重现你的问题。哦!在这种情况下,旧帧中的信息是不准确的,尤其是指针变量。在上面的堆栈表示中,当
inp
变为
0
时,为什么这会反映在GDB中的
ptr
上?它们在单独的函数堆栈中,对吗?
func2(0x0)
并不意味着ptr为null,它也指的是
func2
参数(inp)。两个函数帧位于同一堆栈上(与同一线程中的任何其他函数帧相同…)Ok。感谢您的解释。
bt full
还说
ptr
is
0x0
这个解释更清晰了一点。谢谢