【问题标题】:Understanding C pointers using GDB by examining core and call stack通过检查核心和调用堆栈来了解使用 GDB 的 C 指针
【发布时间】:2014-02-23 07:50:24
【问题描述】:

我从事 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? 

为什么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         |

inp在第(4)行的func2()中变成NULL时,在func1()中是如何体现的?

【问题讨论】:

  • 我喜欢你的表现。
  • 这很奇怪。我希望第 1 帧的回溯会显示 func1() at thelinenumberoflocationlabeled(2) 而不会显示对 func2 的调用。你是在使用bt 命令还是别的什么?
  • @MarkPlotnick 我正在使用bt 命令。它打印所有堆栈帧,而不仅仅是第 1 帧。我上面对 GDB 回溯的表示只是为了帮助解释我的问题。如果有什么问题,请告诉我。
  • 好的。我的 gdb 的 bt 命令显示调用 func2() 的第 0 帧和调用 func1 的第 1 帧。它的第 1 帧显示不像您那样显示对 func2 的调用。我只是无法重现您的确切问题。

标签: c pointers gdb stack


【解决方案1】:

GDB从栈中构造调用栈,因为inpfunc2的参数,为0,GDB假定传递的参数为0,所以它说func2被调用了0.

崩溃时的堆栈是这样的:

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

【讨论】:

  • 哦!在这种情况下,旧帧中的信息不准确,尤其是指针变量。
  • 在上面的堆栈表示中,当inp 变为0 时,为什么这会反映在GDB 中的ptr 上?它们位于不同的函数堆栈中,对吧?
  • func2(0x0) 并不意味着 ptr 为空,它也指func2 参数(inp)。两个函数框架都在同一个堆栈上(与同一线程中的任何其他函数框架一样......)
  • 好的。谢谢你的解释。
  • bt full 也说ptr0x0
【解决方案2】:

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

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

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

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

我无法解释为什么您的gdb 的回溯显示func1 的框架中的ptr 的值为0。将inp 设置为NULL 对ptr 的值或gdb 显示ptr 值的能力。

【讨论】:

  • 这个解释更清楚了。谢谢。
猜你喜欢
  • 2020-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多