根据ebp/rbp/fp获取backtrace

本文介绍了一种在不同架构(如X86、ARM)上获取调用栈的方法,包括使用特定寄存器(如ebp/rbp/fp)和内联汇编来获取当前帧指针,以及通过addr2line还原调用栈。此外,还提供了具体的代码示例。
摘要由CSDN通过智能技术生成

1.

   1.1. X86用 ebp/rbp  其中32位用ebp,64位用rbp.  参考https://blog.csdn.net/wang010366/article/details/52015264 

   1.2. arm用fp 获取当前frame指针

   1.3. gcc用内置函数 __builtin_return_address获取当前frame指针

 

2. 获取ebp/rbp/fp的方法,参考:https://www.cnblogs.com/sky-heaven/p/10195810.html

获取ebp/rbp/fp的方法为 “mov %%rbp”中的rbp分别改成ebp/rbp/fp 就行,这是汇编指令

void ** STACKCALL getEBP(void){
void **ebp=NULL;
__asm__ __volatile__("mov %%rbp, %0;\n\t"
:"=m"(ebp) /* 输出 */
: /* 输入 */
:"memory"); /* 不受影响的寄存器 */
return (void **)(*ebp);
}

 

3. 栈的基本模型: 参考  https://www.cnblogs.com/lijinlei/p/4728607.html

从这里可以得出, ebp/rbp+一个地址单元32/64bit,就可获得eip/rip - "返回本次调用后,下一条指令的地址",这里就看作函数调用栈地址,后续代码实现如下:

unsigned long *stack_addr;
	int i = 0;
    int retRBPDistence = 1;
	unsigned long rbp = getRBP();
    while(rbp != 0 && *(unsigned long*)rbp != 0 && *(unsigned long *)rbp != rbp)  
    {
		stack_addr = (rbp+(retRBPDistence * sizeof(unsigned long)));
		rbp = *(unsigned long*)rbp;
		printf(" *stack_addr:%lx \n",*stack_addr);
			
    }  

 

4. Linux栈空间:参考 https://www.cnblogs.com/dongzhiquan/p/11415722.html

Linux内存映射机制,每次启动的时候栈空间都是随机分配,获取栈起始地址: cat  /proc/<pid>/maps参考:https://www.cnblogs.com/arnoldlu/p/10272466.html

 

5.获取调用栈实例:

64bit

backLib.c

#include <stdio.h>

unsigned long getRBP()
{
	unsigned long rbp = 0;
	__asm__ __volatile__("mov %%rbp, %0 \r\n" :"=m"(rbp) ::"memory");
	return rbp;
}

int my_backtrace()  
{  
	unsigned long *stack_addr;
	int i = 0;
    int retRBPDistence = 1;
	unsigned long rbp = getRBP();
    while(rbp != 0 && *(unsigned long*)rbp != 0 && *(unsigned long *)rbp != rbp)  
    {
		stack_addr = (rbp+(retRBPDistence * sizeof(unsigned long)));
		rbp = *(unsigned long*)rbp;
		printf(" *stack_addr:%lx \n",*stack_addr);
			
    }  
  
    return 0;  
} 

void c()
{
	my_backtrace();
	printf("C \n");
}

void b()
{
	c();
	printf("B \n");
}

void a()
{
	b();
	printf("A \n");
}

backLib.h

#ifndef _BACKLIB_H_
#define _BACKLIB_H_

void a();

#endif

backtrace.c


#include <stdio.h>
#include "backLib.h"
int main()
{
	a();
	while(1)
	{
		usleep(1*1000);
	}
	return 0;
}

编译:  (本人测试过直接编译可执行文件,想看看lib库的方式有什么不一样,静态库的方式运行结果和一个可执行文件的效果一样,也许动态库的结果不一样,待后续验证)

gcc -g -c backLib.c -o backLib.o   #编译.o

ar cr libback.a backLib.o2             #编译lib

gcc -g -o backtrace backtrace.c -L./ -lback -I./   #编译可执行文件

运行:./backtrace

运行结果如下:

zhang@ubuntu:~/share/exchange/backtraceWithLib$ ./backtrace
 *stack_addr:55dd1f82f231
 *stack_addr:55dd1f82f2ae
 *stack_addr:55dd1f82f2cf
 *stack_addr:55dd1f82f2f0
 *stack_addr:55dd1f82f1bb
C
B
A

查看栈起始地址:

top | grep  backtrace   #找到进程号

cat /proc/72506/maps  # 查看mem maps, 运行结果如下:

zhang@ubuntu:~$ top | grep  backtrace
  72506 zhang     20   0    2496    588    524 R   1.0   0.0   0:00.09 backtrace

zhang@ubuntu:~$ cat /proc/72506/maps
55dd1f82e000-55dd1f82f000 r--p 00000000 08:05 2228253                    /home/zhang/share/exchange/backtraceWithLib/backtrace
55dd1f82f000-55dd1f830000 r-xp 00001000 08:05 2228253                    /home/zhang/share/exchange/backtraceWithLib/backtrace
55dd1f830000-55dd1f831000 r--p 00002000 08:05 2228253                    /home/zhang/share/exchange/backtraceWithLib/backtrace
55dd1f831000-55dd1f832000 r--p 00002000 08:05 2228253                    /home/zhang/share/exchange/backtraceWithLib/backtrace
55dd1f832000-55dd1f833000 rw-p 00003000 08:05 2228253                    /home/zhang/share/exchange/backtraceWithLib/backtrace
55dd205de000-55dd205ff000 rw-p 00000000 00:00 0                          [heap]
7fd7937dc000-7fd793801000 r--p 00000000 08:05 6031499                    /usr/lib/x86_64-linux-gnu/libc-2.31.so
7fd793801000-7fd793979000 r-xp 00025000 08:05 6031499                    /usr/lib/x86_64-linux-gnu/libc-2.31.so
7fd793979000-7fd7939c3000 r--p 0019d000 08:05 6031499                    /usr/lib/x86_64-linux-gnu/libc-2.31.so
7fd7939c3000-7fd7939c4000 ---p 001e7000 08:05 6031499                    /usr/lib/x86_64-linux-gnu/libc-2.31.so
7fd7939c4000-7fd7939c7000 r--p 001e7000 08:05 6031499                    /usr/lib/x86_64-linux-gnu/libc-2.31.so
7fd7939c7000-7fd7939ca000 rw-p 001ea000 08:05 6031499                    /usr/lib/x86_64-linux-gnu/libc-2.31.so
7fd7939ca000-7fd7939d0000 rw-p 00000000 00:00 0
7fd7939e6000-7fd7939e7000 r--p 00000000 08:05 6031495                    /usr/lib/x86_64-linux-gnu/ld-2.31.so
7fd7939e7000-7fd793a0a000 r-xp 00001000 08:05 6031495                    /usr/lib/x86_64-linux-gnu/ld-2.31.so
7fd793a0a000-7fd793a12000 r--p 00024000 08:05 6031495                    /usr/lib/x86_64-linux-gnu/ld-2.31.so
7fd793a13000-7fd793a14000 r--p 0002c000 08:05 6031495                    /usr/lib/x86_64-linux-gnu/ld-2.31.so
7fd793a14000-7fd793a15000 rw-p 0002d000 08:05 6031495                    /usr/lib/x86_64-linux-gnu/ld-2.31.so
7fd793a15000-7fd793a16000 rw-p 00000000 00:00 0
7ffe26626000-7ffe26647000 rw-p 00000000 00:00 0                          [stack]
7ffe266dc000-7ffe266e0000 r--p 00000000 00:00 0                          [vvar]
7ffe266e0000-7ffe266e2000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0                  [vsyscall]
zhang@ubuntu:~$

可以得出栈起始地址为: 55dd1f82e000

6.还原调用栈:addr2line

每一个地址减去栈起始地址得到的调用栈分别为:

55dd1f82f231 - 55dd1f82e000 = 1231

55dd1f82f2ae - 55dd1f82e000 = 12ae

55dd1f82f2cf - 55dd1f82e000 = 12cf

55dd1f82f2f0 - 55dd1f82e000 = 12f0

55dd1f82f1bb - 55dd1f82e000 = 11bb

然后 addr2line -C -f -e backtrace 可得出调用栈

结果:

zhang@ubuntu:~/share/exchange/backtraceWithLib$ addr2line -C -f -e backtrace 1231
my_backtrace
/home/zhang/share/exchange/backtraceWithLib/backLib.c:15
zhang@ubuntu:~/share/exchange/backtraceWithLib$ addr2line -C -f -e backtrace 12ae
c
/home/zhang/share/exchange/backtraceWithLib/backLib.c:30
zhang@ubuntu:~/share/exchange/backtraceWithLib$ addr2line -C -f -e backtrace 12cf
b
/home/zhang/share/exchange/backtraceWithLib/backLib.c:36
zhang@ubuntu:~/share/exchange/backtraceWithLib$ addr2line -C -f -e backtrace 12f0
a
/home/zhang/share/exchange/backtraceWithLib/backLib.c:42
zhang@ubuntu:~/share/exchange/backtraceWithLib$ addr2line -C -f -e backtrace 11bb
main
/home/zhang/share/exchange/backtraceWithLib/backtrace.c:9 (discriminator 1)

7. 32位可执行文件代码:  参考“2.获取ebp/rbp/fp的方法” 将以上代码rbp换成ebp即可。

TODO: 后续最好是自动化获取调用栈。

8. arm获取backtrace,待验证

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <assert.h>
#include <ucontext.h>
#include <fcntl.h>
#include <string.h>


void A(int a);
void B(int b);
void C(int c);
void DebugBacktrace(unsigned int sn , siginfo_t  *si , void *ptr);

typedef struct
{
	const char *dli_fname;	/* File name of defining object.  */
	void *dli_fbase;		/* Load address of that object.  */
	const char *dli_sname;	/* Name of nearest symbol.比如函数名*/
	void *dli_saddr;		/* Exact value of nearest symbol.比如函数的起始地址*/
} Dl_info;

struct ucontext_ce123 {
	unsigned long	  uc_flags;
	struct ucontext  *uc_link;
	stack_t		  uc_stack;
	struct sigcontext uc_mcontext;
	sigset_t	  uc_sigmask;	/* mask last for extensibility */
}ucontext_ce123_;

struct sigframe_ce123 {  
    struct sigcontext sc;//保存一组寄存器上下文  
    unsigned long extramask[1];  
    unsigned long retcode;//保存返回地址  
    //struct aux_sigframe aux __attribute__((aligned(8)));  
}sigframe_ce123; 

int backtrace_ce123 (void **array, int size);
char ** backtrace_symbols_ce123 (void *const *array, int size);


int backtrace_ce123 (void **array, int size)
{
	if (size <= 0)
		return 0;

	int *fp = 0, *next_fp = 0;
	int cnt = 0;
	int ret = 0;

	__asm__(
		"mov %0, fp\n" 
		: "=r"(fp)
	);
    

	array[cnt++] = (void *)(*(fp));
	next_fp = (int *)(*(fp-1));
	while((cnt <= size) && (next_fp != 0))
	{
		array[cnt++] = (void *)*(next_fp);
		next_fp = (int *)(*(next_fp-1));
	}


	ret = ((cnt <= size)?cnt:size);
	printf("Backstrace (%d deep)\n", ret);

	return ret;
}


char ** backtrace_symbols_ce123 (void *const *array, int size)
{
# define WORD_WIDTH 8
	Dl_info info[size];
	int status[size];
	int cnt;
	size_t total = 0;
	char **result;

	/* Fill in the information we can get from `dladdr'.  */
	for (cnt = 0; cnt < size; ++cnt)
	{
		status[cnt] = dladdr (array[cnt], &info[cnt]);
		if (status[cnt] && info[cnt].dli_fname && info[cnt].dli_fname[0] != '\0')
		/* We have some info, compute the length of the string which will be
		"<file-name>(<sym-name>) [+offset].  */
		total += (strlen (info[cnt].dli_fname ?: "")
			+ (info[cnt].dli_sname ? strlen (info[cnt].dli_sname) + 3 + WORD_WIDTH + 3 : 1)
			+ WORD_WIDTH + 5);
		else
			total += 5 + WORD_WIDTH;
	}


	/* Allocate memory for the result.  */
	result = (char **) malloc (size * sizeof (char *) + total);
	if (result != NULL)
	{
		char *last = (char *) (result + size);

		for (cnt = 0; cnt < size; ++cnt)
		{
			result[cnt] = last;

			if (status[cnt] && info[cnt].dli_fname && info[cnt].dli_fname[0] != '\0')
			{
				char buf[20];

				if (array[cnt] >= (void *) info[cnt].dli_saddr)
					sprintf (buf, "+%#lx", \
						(unsigned long)(array[cnt] - info[cnt].dli_saddr));
				else
					sprintf (buf, "-%#lx", \
						(unsigned long)(info[cnt].dli_saddr - array[cnt]));

				last += 1 + sprintf (last, "%s%s%s%s%s[%p]",
					info[cnt].dli_fname ?: "",
					info[cnt].dli_sname ? "(" : "",
					info[cnt].dli_sname ?: "",
					info[cnt].dli_sname ? buf : "",
					info[cnt].dli_sname ? ") " : " ",
					array[cnt]);
			}
			else
				last += 1 + sprintf (last, "[%p]", array[cnt]);
		}
		assert (last <= (char *) result + size * sizeof (char *) + total);
	}

	return result;
}


void A(int a)
{
	printf("%d: A call B\n", a);
	B(2);
}

void B(int b)
{
	printf("%d: B call C\n", b);
	C(3);       /* 这个函数调用将导致段错误*/
}

void C(int c)
{
	char *p = (char *)c;
 //   free(p);
	*p = 'A';   /* 如果参数c不是一个可用的地址值,则这条语句导致段错误 */
	printf("%d: function C\n", c);
}

/* SIGSEGV信号的处理函数,回溯栈,打印函数的调用关系*/
void DebugBacktrace(unsigned int sn , siginfo_t  *si , void *ptr)
{
	/*int *ip = 0;
	__asm__(
		"mov %0, ip\n" 
		: "=r"(ip)
	);
	printf("sp = 0x%x\n", ip);
	struct sigframe_ce123 * sigframe = (struct sigframe_ce123 * )ip;*/

	if(NULL != ptr)
	{
		printf("\n\nunhandled page fault (%d) at: 0x%08x\n", si->si_signo,si->si_addr);

		struct ucontext_ce123 *ucontext = (struct ucontext_ce123 *)ptr;
		int pc = ucontext->uc_mcontext.arm_pc;
        printf("trap_no:%ld,error_code:%ld,oldmask:%ld \n"
		   "arm[%p %p %p %p %p %p %p %p %p %p %p ] \n"
		   "fp:%p ip:%p sp:%p lr:%p pc:%p cpsr:%p default:%p \n",
		   ucontext->uc_mcontext.trap_no, ucontext->uc_mcontext.error_code, ucontext->uc_mcontext.oldmask, ucontext->uc_mcontext.arm_r0,
		   ucontext->uc_mcontext.arm_r1, ucontext->uc_mcontext.arm_r2, ucontext->uc_mcontext.arm_r3, ucontext->uc_mcontext.arm_r4, ucontext->uc_mcontext.arm_r5,
		   ucontext->uc_mcontext.arm_r6, ucontext->uc_mcontext.arm_r7, ucontext->uc_mcontext.arm_r8, ucontext->uc_mcontext.arm_r9,ucontext->uc_mcontext.arm_r10,
		   ucontext->uc_mcontext.arm_fp, ucontext->uc_mcontext.arm_ip, ucontext->uc_mcontext.arm_sp, ucontext->uc_mcontext.arm_lr,ucontext->uc_mcontext.arm_pc,
		   ucontext->uc_mcontext.arm_cpsr,ucontext->uc_mcontext.fault_address);

		void *pc_array[1]; 
		pc_array[0] = pc;
		char **pc_name = backtrace_symbols_ce123(pc_array, 1);
		printf("PC %d: %s\n", 0, *pc_name);

        pc_array[0] = ucontext->uc_mcontext.arm_lr;
		pc_name = backtrace_symbols_ce123(pc_array, 1);
		printf("LR %d: %s\n", 0, *pc_name);

#define SIZE 100
		void *array[SIZE];
		int size, i;
		char **strings;
		size = backtrace_ce123(array, SIZE);
		strings = backtrace_symbols_ce123(array, size);	

		for(i=0;i<size;i++)
			printf("222 %d: %s\n", i+1, strings[i]);
		free(strings);
	}
	else
		printf("error!\n");
	exit(-1);
}

int main(int argc, char **argv)
{   
	char a;

	struct sigaction s;
	s.sa_flags = SA_SIGINFO;
	s.sa_sigaction = (void *)DebugBacktrace;

	sigaction (SIGSEGV,&s,NULL);

	A(1);
	C(&a);    

	return 0;

}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值