glibc backtrace backtrace_symbols 的应用示例

作用:

在一个函数调用栈中,输出 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);
       }


编译运行

官方示例运行效果:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值