自己动手实现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函数代替。
自己动手实现arm函数栈帧回溯【转】的更多相关文章
- c函数调用过程原理及函数栈帧分析
转载自地址:http://blog.csdn.net/zsy2020314/article/details/9429707 今天突然想分析一下函数在相互调用过程中栈帧的变化,还是想尽量以比 ...
- Cortex-M3双堆栈MSP和PSP+函数栈帧
为了防止几百年以后找不到该文章,特此转载 ------------------------------------------------开始转载--------------------------- ...
- C函数调用过程原理及函数栈帧分析(转)
在x86的计算机系统中,内存空间中的栈主要用于保存函数的参数,返回值,返回地址,本地变量等.一切的函数调用都要将不同的数据.地址压入或者弹出栈.因此,为了更好地理解函数的调用,我们需要先来看看栈是怎么 ...
- 谈谈arm下的函数栈
引言 这篇文章简要说说函数是怎么传入参数的,我们都知道,当一个函数调用使用少量参数(ARM上是少于等于4个)时,参数是通过寄存器进行传值(ARM上是通过r0,r1,r2,r3),而当参数多于4个时,会 ...
- [Android Pro] 深入理解函数的调用过程——栈帧
cp :http://blog.csdn.net/x_perseverance/article/details/78897637 每一个函数被调用时,都会为函数开辟一块空间,这块空间就称为栈帧. 首先 ...
- Linux内核调试方法总结之栈帧
栈帧 栈帧和指针可以说是C语言的精髓.栈帧是一种特殊的数据结构,在C语言函数调用时,栈帧用来保存当前函数的父一级函数的栈底指针,当前函数的局部变量以及被调用函数返回后下一条汇编指令的地址.如下图所示: ...
- C语言的函数调用过程(栈帧的创建与销毁)
从汇编的角度解析函数调用过程 看看下面这个简单函数的调用过程: int Add(int x,int y) { ; sum = x + y; return sum; } int main () { ; ...
- Windows x64 栈帧结构
0x01 前言 Windows 64位下函数调用约定变为了快速调用约定,前4个参数采用rcx.rdx.r8.r9传递,多余的参数从右向左依次使用堆栈传递.本次文章是对于Windows 64位下函数调用 ...
- dwarf是如何处理栈帧的?
dwarf是如何处理栈帧的? DW_AT_frame_base 表明函数栈帧的起始点 95 < 1><0x000000ca> DW_TAG_subprogram 96 ...
随机推荐
- cmd批处理中set /a和set /p的区别介绍
在 SET 命令中添加了两个新命令行开关: SET /A expression SET /P variable=[promptString]/p 是让你输入/a 是指定一个变量等于一串运算字符 什么参 ...
- [剑指Offer] 55.链表中环的入口结点
题目描述 一个链表中包含环,请找出该链表的环的入口结点. [思路]根据set集合的不重复,遍历链表时遇到的第一个重复结点就是环的入口结点. /* struct ListNode { int val; ...
- Bootstrap 折叠(collapse) 初见
以下代码来自bootstrap中文网 <!DOCTYPE html> <html lang="zh-CN"> <head> <meta c ...
- C语言指针【转】
一.C语言指针的概念 在计算机中,所有的数据都是存放在存储器中的.一般把存储器中的一个字节称为一个内存单元,不同的数据类型所占用的内存单元数不等,如整型量占2个单元,字符量占1个单元等,在前面已有详细 ...
- Django+haystack实现全文搜索出现错误 ImportError: cannot import name signals
原因是在你的settings.py或者其他地方使用了 "import haystack" 当我们使用django-haysatck库时,表面上会有haystack库,但实际上并不 ...
- CentOS 转义字符
常用转义字符 反斜杠(\):使反斜杠后面的一个变量变为单纯的字符串. 单引号(''):转义其中所有的变量为单纯的字符串. 双引号(""):保留其中的变量属性,不进行转义处理. 反引 ...
- apt-key 命令
学习参照网上教程在容器中搭建nginx时看到apt-key命令不解,记录一下.一下是 --help中的解释. apt-key命令解释: apt-key add <file> - add t ...
- BZOJ5333:[SDOI2018]荣誉称号——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=5333 https://www.luogu.org/problemnew/show/P4620 题意 ...
- BZOJ1043:[HAOI2008]下落的圆盘——题解(配图片)
http://www.lydsy.com/JudgeOnline/problem.php?id=1043 Description 有n个圆盘从天而降,后面落下的可以盖住前面的.求最后形成的封闭区域的周 ...
- BZOJ3521 [Poi2014]Salad Bar 【线段树 + 单调栈】
题目链接 BZOJ3521 题解 容易想到用前缀和搞 如果我们令\(p\)为\(1\),\(j\)为\(-1\),记前缀和为\(s[i]\) 我们就是要找到一段区间\([l,r]\),使得 \[\fo ...