自己动手实现arm函数栈帧回溯【转】
转自:http://blog.csdn.net/dragon101788/article/details/18668505
内核版本:2.6.14
glibc版本:2.3.6
CPU平台:arm
glic中其实有这些函数,当时用的uclib版本较低,没有这些函数,但又需要,只能自己实现了(较高的版本应该有这些函数,换版本很麻烦),而且可以加深自己对这方面的理解.原理性的东西就不深入讲解了,直接上例子!
- #include <stdio.h>
- #include <stdlib.h>
- #include <signal.h>
- #include <assert.h>
- #include <ucontext.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-1));
- next_fp = (int *)(*(fp-3));
- while((cnt <= size) && (next_fp != 0))
- {
- array[cnt++] = (void *)*(next_fp - 1);
- next_fp = (int *)(*(next_fp-3));
- }
- 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] = _dl_addr (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;
- *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;
- void *pc_array[1];
- pc_array[0] = pc;
- char **pc_name = backtrace_symbols_ce123(pc_array, 1);
- printf("%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("%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;
- }
代码的下载地址:http://download.csdn.net/detail/ce123/5063160
编译命令:arm-linux-gcc -rdynamic -o segfault segfault.c
_dl_addr链接不通过时,使用-ldl。如果没有该函数,可用dladdr函数代替。
转载于:https://www.cnblogs.com/sky-heaven/p/7678651.html
自己动手实现arm函数栈帧回溯【转】相关推荐
- C语言内功修炼之函数栈帧的创建与销毁(举例加图解)
大家可能会函数栈帧不了解,可能都没有听过这个,不用着急,在理解函数栈帧之前,我们先来了解一下程序对内存使用的分区大概情况: 区域 作用 栈区(stack) 由编译器自动分配和释放,存放函数的参数值, ...
- 函数调用过程详解:函数栈帧的创建与销毁
前言:我们在学习C语言的过程中,可以会产生很多疑问,比如: 局部变量是怎么创建的 为什么局部变量的值不做初始化就是随机值 函数是怎么传参的?传参的顺序是怎么样的? 形参和实参是什么关系? 函数调用是怎 ...
- 【软件开发底层知识修炼】二十三 ABI-应用程序二进制接口三之深入理解函数栈帧的形成与摧毁
上两篇文章我们初步接触了ABI-应用程序二进制接口的概念,点击链接查看上一篇文章:[软件开发底层知识修炼]二十二 ABI-应用程序二进制接口 二.了解了为什么会有ABI的存在.本篇文章继续学习ABI ...
- 函数栈帧的创建和销毁图解
目录 一.问题: 二.寄存器 栈区 1.寄存器有哪些?有什么作用? 2.编译环境 3.栈区的使用习惯: 4.main函数也是被其他函数调用的 5.汇编代码 三.为main函数创建栈帧 1.main函数 ...
- C函数调用过程原理及函数栈帧分析
在x86的计算机系统中,内存空间中的栈主要用于保存函数的参数,返回值,返回地址,本地变量等.一切的函数调用都要将不同的数据.地址压入或者弹出栈.因此,为了更好地理解函数的调用,我们需要先来看看栈是怎么 ...
- 函数栈帧(详细图解)
目录 一.栈 二.常用寄存器及简单汇编指令 三.理解栈帧 3.1 main函数栈帧创建 3.1.1 main函数栈帧创建动态演示 3.2 局部变量创建 3.2.1 局部变量创建动态演示 3.3 函数传 ...
- 修炼内功——理解函数栈帧创建和销毁
函数栈帧 1. 全局观: 今天我们讲的是栈区,以下的图上方是低地址,下方是高地址,跟上图相反,因此使用顺序是从下方到上方,跟上图本质上一致. 2. 函数的调用关系 ebp,esp这两个寄存器中存放的是 ...
- (动图详解)汇编视角观察函数栈帧的创建和销毁
目录 1.阅读本文的价值 2.函数栈帧及栈的概念 3.部分寄存器及汇编指令 4.main函数的调用 5.main函数的栈帧创建 6.变量的栈帧创建 6.函数传参 7.函数内部运算及销毁 ...
- 程序员内功修炼——函数栈帧的创建与销毁
一.什么是函数的栈帧 c语言是由函数构成的,那么函数是如何进行传参的?如何调用的?如何返回值的?这些问题与函数的栈帧有关. 函数栈帧:就是函数调用过程中程序的调用栈所开辟的空间,这些空间用来存放: 1 ...
最新文章
- 在DataGrid(WebControl)中处理DropDownList事件的补充说明
- java架构程序员月入破3万到底是怎么炼成的,一篇文章让你了解
- mysql忘记密码,怎么办?
- Gentle中的数据表实体类相关自定义属性的设置和获得
- 拥抱 Android Studio 之五:Gradle 插件开发
- 关于爬虫数据的解析器设计
- 从门户网站看内容传播的开放式革命
- Windows绘图中的GDI映射模式
- 款装机热门电源横向评测
- 使用php函数对变量进行比较,PHP:将变量传递给函数,对变量进行处理,然后将其返回...
- LT8618SX寄存器配置
- OkHttp使用及工具类封装
- matlab图论软件包,MATLAB_bgl_toolbox 图论通用工具箱总汇:GraphTheory for St 247万源代码下载- www.pudn.com...
- 最新电脑cpu性能排行服务器,服务器cpu性能排行,教您服务器cpu性能排行
- 5G常见缩略语大全(一)
- Vibosoft ePub Converter(ePub转换器)v2.1.24官方版
- 陶伟死因 从微博看明星
- slam第六讲_g2o
- Microsoft SQL Server 图书管理数据库的建立
- 2555555555555555555
热门文章
- dns文件传输服务器,MOOC云计算 - DNS三部曲之DNS区域传输限制
- python 生成excel_python 数据生成excel导出(xlwt,wlsxwrite)代码实例
- 泄密Number的数据类型转换
- 小数乘分数怎么算过程_广东新高考分数怎么算
- 虚拟机上的linux里安装ngnix,虚拟机(linux)下安装nginx的步骤教程
- mysql gtid 双主_MySQL5.7配置GTID双主
- android system_server中的dump_Android 10.0系统启动之SystemServer进程(二)
- php反序列化java.long_细数java中Long与Integer比较容易犯的错误总结
- jfinal中使用freemarker
- keepalive+lvs负载均衡及高可用总结