最近要实现程序打印函数调用信息,就研究了一下linux下的函数调用堆栈
参考:http://www.360doc.com/content/15/0105/11/982782_438311997.shtml
注:这个里面详细介绍了栈帧(Stack Frame)的结构,每个未完成函数都会占用一个栈帧结构,里面保存了函数的一些相关信息(函数地址、形参、返回值等等)
参考:http://blog.csdn.net/dremi/article/details/6723804
注:这个里面有个代码实现的例子,给出了两种方法:
一、使用libc库里面backtrace函数实现;
二、自己通过分析调用栈信息来实现;
编译问题:
在使用网上的例子时:使用-rdynaminc选项时gcc报错,解决办法使用-export-dynamic代替,成功!
参考:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=37454
代码部分:
源码网上几乎都是一样就不贴了;
主要是用了libc库的两个函数:
#include <execinfo.h>
int backtrace (void **buffer, int size)
//用来获取堆栈信息。size需要获取的堆栈层次,返回值为实际获得的堆栈层次
char ** backtrace_symbols (void *const *buffer, int size)
//size 是backtrace的返回值该函数是用来翻译从bacetrace获得的堆栈信息。返回值为存放各个函数的地址 的二维指针。
void backtrace_symbols_fd (void *const *buffer, int size, int fd) //扩展
//该函数的功能和backtrace_symbols一样只是它的返回字符串是写入文件描术符为fd的文件
扩展:
在intel cpu中,寄存器BP(EBP)被用作帧指针,所以读取EBP的值就可以获得栈帧的起始地址. ---其它类型CPU自行查找相关资料
ESP(指针存放于对应的寄存器中)被用来作为栈顶指针,指向当前栈帧顶部(低地址)
获取寄存器的值需要利用内嵌汇编,相关语法可参考:http://blog.csdn.net/pbymw8iwm/article/details/8227839
Linux和windows下汇编语法的区别http://www.docin.com/p-115580288.html,特别是两者语法中源操作数和目的操作数是反过来的。
附:
栈帧结构图 ( 这是主调函数(caller)和被调函数(callee)的栈帧布局 )
总结:
1、完成的函数不会出现在栈帧里面(函数未完成会压栈,完成后出栈)
2、待定