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  backtrace72506 zhang     20   0    2496    588    524 R   1.0   0.0   0:00.09 backtracezhang@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 8Dl_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);elsetotal += 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));elsesprintf (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]);}elselast += 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 100void *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);}elseprintf("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;}

根据ebp/rbp/fp获取backtrace相关推荐

  1. fseek( fp, SEEK_SET, SEEK_END );的作用?以及如何获取一个文件的大小?

    目录         一.fseek(fp,SEEK_SET,SEEK_END);的作用? 1.offset表示文件指针的偏移量 2.whence表示偏移量的基准位置 二.如何获取一个文件的大小 一. ...

  2. 获取iOS任意线程调用堆栈(一)获取任意线程的调用栈地址列表

    转载自:http://blog.csdn.net/jasonblog/article/details/49909163 如果要获取当前线程的调用栈,可以直接使用现有API:[NSThread call ...

  3. php 取得文件行数,PHP获取文件行数的方法

    这篇文章主要介绍了PHP获取文件行数的方法,实例分析了两种php获取文件行数的技巧,需要的朋友可以参考下 本文实例讲述了PHP获取文件行数的方法.分享给大家供大家参考.具体分析如下: 提供两种实现方法 ...

  4. PHP如何获取文件行数

    本文实例讲述了PHP获取文件行数的方法.分享给大家供大家参考.具体分析如下: 提供两种实现方法,虽然第二种简单易懂,但是第一种效率最好 第一种: <?php $file_path = 'xxx. ...

  5. 使用c++给程序获取管理员权限

    文章目录 背景 修改程序开启时的运行权限 c++获取程序管理员权限 简单配置vs获取程序管理员权限 背景 最近开发的新项目里面的程序,在自己电脑上运行的好好的,打包出去之后,测试多次说程序有问题,打不 ...

  6. PHP获取表单数据的方法有几种,php怎么获取表单数据

    php获取表单数据的几种方法: 一.用file_get_contents以get方式获取内容,需要输入内容为:<?php $url='http://www.domain.com/?para=12 ...

  7. vc获取硬盘物理序列号

    #pragma once #ifndef __AFXWIN_H__ #error include 'stdafx.h' before including this file for PCH #endi ...

  8. Windows上获取文件大小的几种方法及获取文件夹大小方法

    文章来自:https://blog.csdn.net/mfcing/article/details/53184921 获取文件大小 Windows提供了好几个API函数来获取文件大小,还可以使用标准C ...

  9. Windows开发--获取文件大小及获取文件夹大小的方法

    获取文件大小 Windows提供了好几个API函数来获取文件大小,还可以使用标准C和标准C++库函数来获取(当然了,标准库函数的底层实现也是调用了Windows API函数,效率上可能有所影响). c ...

最新文章

  1. Spark 1.4连接mysql诡异的问题及解决
  2. db2有主键时默认hash分区_彻底搞懂 MySQL 分区!
  3. 运算符优先级 必熟记,放到心里
  4. 谷歌浏览器怎么设置点击书签 谷歌浏览器如何设置点击书签
  5. 刚刚,“国民”APP微信崩了!官方致歉:已经逐步恢复
  6. Flutter社区和资源传送门
  7. 栗子——自定义EditText实现右下角计数控件
  8. Linux篇 | 磁盘存储和文件系统
  9. 基于springboot+vue的学生选课系统(前后端分离)
  10. linux 程序发包,软件测试常用linux发包命令
  11. Elasticsearch 使用同义词 二
  12. Fuchsia OS简介
  13. 儿童使用显微镜有好处吗?
  14. 组合游戏(Nim游戏)——SG函数
  15. Echarts画散点图
  16. flink入门3-Flink连接Kafka、Redis,实现Kafka Source/Redis Sink
  17. 微信公众号 - 下拉(展开/隐藏)
  18. MongoDB数据库运维工具的使用
  19. 亿康先达2018年取得两位数全球增长
  20. ROS aubo i5 noetic版本 机械臂 Ubuntu 20.04

热门文章

  1. 阅读《迁移学习简明手册》总结(二)
  2. S2JH 自动审计@Audited 的问题
  3. jQuery读取JSON数据(jQuery调用JSON数据学习第二天)
  4. Win11的Credential Manager怎么打开?
  5. php 微信公众号图片外链,如何制作微信图文链接?微信图文链接生成
  6. 关于Oracle数据库start whith 递归查询的另类认知
  7. Android ButterKnife(黄油刀)的使用
  8. C语言字符意思 char,c语言中char* 代表什么
  9. Android 上传头像(文件)到服务器
  10. egg mysql 模糊搜索_使用egg-mysql操作mysql数据库