C++栈回溯原理

Posted IT老张

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++栈回溯原理相关的知识,希望对你有一定的参考价值。

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

       要搞清楚栈回溯的原理,需要对照着函数调用时的栈分布情况来看:

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,依次向上推算,确定更上一层的主调函数,这样就将函数调用栈给回溯出来了。

以上是关于C++栈回溯原理的主要内容,如果未能解决你的问题,请参考以下文章

内核栈回溯原理学习应用

递归和回溯求解8皇后问题

递归和回溯求解8皇后问题

(重磅原创)冬之焱: 谈谈Linux内核的栈回溯与妙用

mips体系堆栈回溯分析与实现

回溯 C++ 代码 gdb 中的指针