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;
}