作用:
在一个函数调用栈中,输出 backtrace()函数返回时需要执行的下一条指令的地址,以及返回主调函数后的下一条指令的地址,递归上一步,直到从系统中链接进来的 _start() 为止。
1,示例先行
hello_glibc.c :
#include <stdlib.h>
#include <stdio.h>
#include <execinfo.h>
#define BACKTRACE_DEPTH 64
void do_backtrace()
{
void *buffer[BACKTRACE_DEPTH];
size_t depth, i;
char **strings;
depth = backtrace(buffer, BACKTRACE_DEPTH);
strings = backtrace_symbols(buffer, depth);
for(i=0; i<depth; i++)
{
printf("%p: %s\n", buffer[i], strings[i]);
}
free(strings);
}
void aaa(){
do_backtrace();
}
void bbb()
{
aaa();
}
void ccc()
{
bbb();
}
int main()
{
ccc();
return 0;
}
2, 编译执行
$ gcc ./hello_glibc.c -rdynamic -o a.out
上图中每一行的格式:
下一条指令的地址: 指令所在文件名(主调函数名 + 主调函数地址相对下一条指令的offset)[下一条指令的地址]
3,数量关系分析
对比于上一张图,除了relocation 加上的基址,可以看出aaa+0x12中的
0x12 是返回地址相对于函数aaa起始地址的偏移量 0x12个字节
4,理论解释
man backtrace
4.1 函数原型
NAME
backtrace, backtrace_symbols, backtrace_symbols_fd - support for application self-debugging
SYNOPSIS
#include <execinfo.h>
int backtrace(void **buffer, int size);
char **backtrace_symbols(void *const *buffer, int size);
void backtrace_symbols_fd(void *const *buffer, int size, int fd);
4.2 函数说明
backtrace()
backtrace()返回调用程序的回溯(跟踪)信息,存储在由buffer指向的数组中。对于特定程序,backtrace就是一系列当前激活的函数调用(active function call)。
参数
buffer 由buffer指向的数组,每一项都是void*类型,存储的是相应(调用函数的)栈帧的返回地址。
size 指定存储在buffer中的地址最大数量。
返回值
返回buffer中实际地址的数量,应当<=size。如果返回值 < size,那么完整的回溯信息被存储;如果返回值 = size,那么它可能被截断,最旧的栈帧可能没有返回。
backtrace_symbols()
backtrace() 返回一组地址,backtrace_symbols()象征性地翻译这些地址为一个描述地址的字符串数组。
参数
buffer 一个字符串数组,由backtrace()返回的buffer,每项代表一个函数地址。backtrace_symbols()会用字符串描述每个函数地址,字符串包括:函数名称,一个16进制偏移(offset),实际的返回地址(16进制)。
size 表明buffer中的地址个数。
返回值
成功时,返回一个指向由malloc(3)分配的array;失败时,返回NULl。
arrary是一个二维数组,该数组的每个元素 指向一个代表backtrace()返回的函数地址的符号信息的字符串,数组由函数内部调用malloc分配空间,必须由调用者free。
注意:指向字符串的指针的数组,不必释放,而且不应该释放。应该释放的是返回的二维数组指针。
backtrace_symbols_fd()
backtrace_symbols_fd()的参数buffer、size同backtrace_symbos(),
不同之处在于,backtrace_symbols_fd()并不会返回一个字符串数组给调用者,而是将字符串写入fd对应文件。
backtrace_symbols_fd()也不会调用malloc分配二维数组空间,因此可应用于malloc可能会失败的情形。
glibc 2.1以后
4.3 官方示例
#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define BT_BUF_SIZE 100
void
myfunc3(void)
{
int nptrs;
void *buffer[BT_BUF_SIZE];
char **strings;
nptrs = backtrace(buffer, BT_BUF_SIZE);
printf("backtrace() returned %d addresses\n", nptrs);
/* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO)
would produce similar output to the following: */
strings = backtrace_symbols(buffer, nptrs);
if (strings == NULL) {
perror("backtrace_symbols");
exit(EXIT_FAILURE);
}
for (int j = 0; j < nptrs; j++)
printf("%s\n", strings[j]);
free(strings);
}
static void /* "static" means don't export the symbol... */
myfunc2(void)
{
myfunc3();
}
void
myfunc(int ncalls)
{
if (ncalls > 1)
myfunc(ncalls - 1);
else
myfunc2();
}
int
main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "%s num-calls\n", argv[0]);
exit(EXIT_FAILURE);
}
myfunc(atoi(argv[1]));
exit(EXIT_SUCCESS);
}
编译运行
官方示例运行效果: