通过检查核心和调用堆栈了解使用GDB的C指针
我已经专业地用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
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
is0x0
这个解释更清晰了一点。谢谢