C++栈回溯原理

       我们用VS调试源代码或使用Windbg调试exe程序遇到异常时,调试器会中断下来,查看此时的函数调用堆栈就能找到引发异常的线索。软件是执行到某一句汇编代码产生了异常,那么调试器如何通过该句汇编代码将所在线程此刻的函数调用堆栈给回溯进来的呢?下面我们就来讲讲这个栈回溯的原理。

       要搞清楚栈回溯的原理,需要搞清楚函数调用时的栈分布:

C++栈回溯原理_第1张图片

1、函数入口处的汇编代码

        对照着上图,看一下函数入口的ebp和esp寄存器操作。

ebp - 函数栈基址寄存器,esp - 函数栈顶地址寄存器。函数占用的栈空间(地址范围)就在esp中的栈顶地址到ebp中的栈基址之间,函数的栈空间在函数入口处就进行分配了。

        在每个函数的入口处都会有下面两句代码:

push ebp
mov ebp,esp

       push的是主调函数的ebp,当前主调函数的栈顶地址esp,给被调函数ebp,即主调函数在调用函数时的栈顶地址就是被调函数的栈基址ebp。从栈内存分布来看,被调函数的栈基址,就是主调函数的栈基址值存放在栈内存上的内存地址。

2、函数退出处的汇编代码

       每个函数退出处都会有下面两句汇编代码:(return之前的汇编代码)

mov esp, ebp
pop ebp

       被调函数即将退出时,将自己(被调函数)的栈基址给主调函数的esp,即作为主调函数当前的栈顶地址。然后从栈中将主调函数的栈基址值拿出来,放到ebp寄存器中。

3、栈回溯的过程

       首先通过当前发生异常的那句汇编代码的地址(代码段地址),通过遍历当前程序中所有函数的地址范围,即可得知当前发生异常的汇编指令位于哪个函数中

       就是以当前ebp寄存器中的值作为栈内存地址,该地址指向的栈内存中的4字节内容就是主调函数的栈基址值ebp(先记录着,为下一轮推算做准备),向下偏移4个字节的内存中存放着主调函数的返回地址,根据返回地址遍历当前程序中的函数地址范围,确定主调函数是哪个函数

       上面得到了主调函数的栈基址ebp,依次向上推算,确定更上一层的主调函数,这样就将函数调用栈给回溯出来了。

你可能感兴趣的