自己动手实现arm函数栈帧回溯【转】

简介: 转自:http://blog.csdn.net/dragon101788/article/details/18668505 内核版本:2.6.14 glibc版本:2.3.6 CPU平台:arm glic中其实有这些函数,当时用的uclib版本较低,没有这些函数,但又需要,只能自己实现了(较高的版本应该有这些函数,换版本很麻烦),而且可以加深自己对这方面的理解.

转自:http://blog.csdn.net/dragon101788/article/details/18668505

内核版本:2.6.14

glibc版本:2.3.6

CPU平台:arm

glic中其实有这些函数,当时用的uclib版本较低,没有这些函数,但又需要,只能自己实现了(较高的版本应该有这些函数,换版本很麻烦),而且可以加深自己对这方面的理解.原理性的东西就不深入讲解了,直接上例子!

 

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
 
 
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <signal.h>  
  4. #include <assert.h>  
  5. #include <ucontext.h>  
  6.   
  7. void A(int a);  
  8. void B(int b);  
  9. void C(int c);  
  10. void DebugBacktrace(unsigned int sn , siginfo_t  *si , void *ptr);  
  11.   
  12. typedef struct  
  13. {  
  14.     const char *dli_fname;  /* File name of defining object.  */  
  15.     void *dli_fbase;        /* Load address of that object.  */  
  16.     const char *dli_sname;  /* Name of nearest symbol.比如函数名*/  
  17.     void *dli_saddr;        /* Exact value of nearest symbol.比如函数的起始地址*/  
  18. } Dl_info;  
  19.   
  20. struct ucontext_ce123 {  
  21.     unsigned long     uc_flags;  
  22.     struct ucontext  *uc_link;  
  23.     stack_t       uc_stack;  
  24.     struct sigcontext uc_mcontext;  
  25.     sigset_t      uc_sigmask;   /* mask last for extensibility */  
  26. }ucontext_ce123_;  
  27.   
  28. struct sigframe_ce123 {    
  29.     struct sigcontext sc;//保存一组寄存器上下文    
  30.     unsigned long extramask[1];    
  31.     unsigned long retcode;//保存返回地址    
  32.     //struct aux_sigframe aux __attribute__((aligned(8)));    
  33. }sigframe_ce123;   
  34.   
  35. int backtrace_ce123 (void **array, int size);  
  36. char ** backtrace_symbols_ce123 (void *const *array, int size);  
  37.   
  38.   
  39. int backtrace_ce123 (void **array, int size)  
  40. {  
  41.     if (size <= 0)  
  42.         return 0;  
  43.   
  44.     int *fp = 0, *next_fp = 0;  
  45.     int cnt = 0;  
  46.     int ret = 0;  
  47.   
  48.     __asm__(  
  49.         "mov %0, fp\n"   
  50.         : "=r"(fp)  
  51.     );  
  52.   
  53.   
  54.     array[cnt++] = (void *)(*(fp-1));  
  55.   
  56.     next_fp = (int *)(*(fp-3));  
  57.     while((cnt <= size) && (next_fp != 0))  
  58.     {  
  59.         array[cnt++] = (void *)*(next_fp - 1);  
  60.         next_fp = (int *)(*(next_fp-3));  
  61.     }  
  62.   
  63.   
  64.     ret = ((cnt <= size)?cnt:size);  
  65.     printf("Backstrace (%d deep)\n", ret);  
  66.   
  67.     return ret;  
  68. }  
  69.   
  70. char ** backtrace_symbols_ce123 (void *const *array, int size)  
  71. {  
  72. # define WORD_WIDTH 8  
  73.     Dl_info info[size];  
  74.     int status[size];  
  75.     int cnt;  
  76.     size_t total = 0;  
  77.     char **result;  
  78.   
  79.     /* Fill in the information we can get from `dladdr'.  */  
  80.     for (cnt = 0; cnt < size; ++cnt)  
  81.     {  
  82.         status[cnt] = _dl_addr (array[cnt], &info[cnt]);  
  83.         if (status[cnt] && info[cnt].dli_fname && info[cnt].dli_fname[0] != '\0')  
  84.         /* We have some info, compute the length of the string which will be  
  85.         "<file-name>(<sym-name>) [+offset].  */  
  86.         total += (strlen (info[cnt].dli_fname ?: "")  
  87.             + (info[cnt].dli_sname ? strlen (info[cnt].dli_sname) + 3 + WORD_WIDTH + 3 : 1)  
  88.             + WORD_WIDTH + 5);  
  89.         else  
  90.             total += 5 + WORD_WIDTH;  
  91.     }  
  92.   
  93.   
  94.     /* Allocate memory for the result.  */  
  95.     result = (char **) malloc (size * sizeof (char *) + total);  
  96.     if (result != NULL)  
  97.     {  
  98.         char *last = (char *) (result + size);  
  99.   
  100.         for (cnt = 0; cnt < size; ++cnt)  
  101.         {  
  102.             result[cnt] = last;  
  103.   
  104.             if (status[cnt] && info[cnt].dli_fname && info[cnt].dli_fname[0] != '\0')  
  105.             {  
  106.                 char buf[20];  
  107.   
  108.                 if (array[cnt] >= (void *) info[cnt].dli_saddr)  
  109.                     sprintf (buf, "+%#lx", \  
  110.                         (unsigned long)(array[cnt] - info[cnt].dli_saddr));  
  111.                 else  
  112.                     sprintf (buf, "-%#lx", \  
  113.                         (unsigned long)(info[cnt].dli_saddr - array[cnt]));  
  114.   
  115.                 last += 1 + sprintf (last, "%s%s%s%s%s[%p]",  
  116.                     info[cnt].dli_fname ?: "",  
  117.                     info[cnt].dli_sname ? "(" : "",  
  118.                     info[cnt].dli_sname ?: "",  
  119.                     info[cnt].dli_sname ? buf : "",  
  120.                     info[cnt].dli_sname ? ") " : " ",  
  121.                     array[cnt]);  
  122.             }  
  123.             else  
  124.                 last += 1 + sprintf (last, "[%p]", array[cnt]);  
  125.         }  
  126.         assert (last <= (char *) result + size * sizeof (char *) + total);  
  127.     }  
  128.   
  129.     return result;  
  130. }  
  131.   
  132.   
  133. void A(int a)  
  134. {  
  135.     printf("%d: A call B\n", a);  
  136.     B(2);  
  137. }  
  138.   
  139. void B(int b)  
  140. {  
  141.     printf("%d: B call C\n", b);  
  142.     C(3);       /* 这个函数调用将导致段错误*/  
  143. }  
  144.   
  145. void C(int c)  
  146. {  
  147.     char *p = (char *)c;  
  148.     *p = 'A';   /* 如果参数c不是一个可用的地址值,则这条语句导致段错误 */  
  149.     printf("%d: function C\n", c);  
  150. }  
  151.   
  152. /* SIGSEGV信号的处理函数,回溯栈,打印函数的调用关系*/  
  153. void DebugBacktrace(unsigned int sn , siginfo_t  *si , void *ptr)  
  154. {  
  155.     /*int *ip = 0;  
  156.     __asm__(  
  157.         "mov %0, ip\n"   
  158.         : "=r"(ip)  
  159.     );  
  160.     printf("sp = 0x%x\n", ip);  
  161.     struct sigframe_ce123 * sigframe = (struct sigframe_ce123 * )ip;*/  
  162.   
  163.     if(NULL != ptr)  
  164.     {  
  165.         printf("\n\nunhandled page fault (%d) at: 0x%08x\n", si->si_signo,si->si_addr);  
  166.   
  167.         struct ucontext_ce123 *ucontext = (struct ucontext_ce123 *)ptr;  
  168.         int pc = ucontext->uc_mcontext.arm_pc;         
  169.   
  170.         void *pc_array[1];   
  171.         pc_array[0] = pc;  
  172.         char **pc_name = backtrace_symbols_ce123(pc_array, 1);  
  173.         printf("%d: %s\n", 0, *pc_name);  
  174.   
  175. #define SIZE 100  
  176.         void *array[SIZE];  
  177.         int size, i;  
  178.         char **strings;  
  179.         size = backtrace_ce123(array, SIZE);  
  180.         strings = backtrace_symbols_ce123(array, size);   
  181.   
  182.         for(i=0;i<size;i++)  
  183.             printf("%d: %s\n", i+1, strings[i]);  
  184.         free(strings);  
  185.     }  
  186.     else  
  187.         printf("error!\n");  
  188.     exit(-1);  
  189. }  
  190.   
  191. int main(int argc, char **argv)  
  192. {     
  193.     char a;  
  194.   
  195.     struct sigaction s;  
  196.     s.sa_flags = SA_SIGINFO;  
  197.     s.sa_sigaction = (void *)DebugBacktrace;  
  198.   
  199.     sigaction (SIGSEGV,&s,NULL);  
  200.   
  201.     A(1);  
  202.     C(&a);      
  203.   
  204.     return 0;  
  205.   
  206. }  

代码的下载地址:http://download.csdn.net/detail/ce123/5063160

编译命令:arm-linux-gcc -rdynamic -o segfault segfault.c

_dl_addr链接不通过时,使用-ldl。如果没有该函数,可用dladdr函数代替。

【作者】 张昺华
【新浪微博】 张昺华--sky
【twitter】 @sky2030_
【facebook】 张昺华 zhangbinghua
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
目录
相关文章
|
1月前
|
存储 编译器 C语言
深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-1
深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-1
|
1月前
|
存储 安全 C语言
深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-2
深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-2
|
4月前
|
编译器 Linux C语言
函数栈帧的创建和销毁(以C语言代码为例,汇编代码的角度分析)(上)
函数栈帧的创建和销毁(以C语言代码为例,汇编代码的角度分析)
|
5月前
|
编译器
!!!教你如何搞懂字符串函数(详细,后期会讲函数栈帧)(中)
!!!教你如何搞懂字符串函数(详细,后期会讲函数栈帧)(上)
|
5月前
|
存储 安全
!!!教你如何搞懂字符串函数(详细,后期会讲函数栈帧)(下)
!!!教你如何搞懂字符串函数(详细,后期会讲函数栈帧)(上)
|
4月前
|
编译器 C语言
函数栈帧的创建和销毁(以C语言代码为例,汇编代码的角度分析)(下)
函数栈帧的创建和销毁(以C语言代码为例,汇编代码的角度分析)
|
5月前
!!!教你如何搞懂字符串函数(详细,后期会讲函数栈帧)(上)
!!!教你如何搞懂字符串函数(详细,后期会讲函数栈帧)
|
8月前
|
存储 编译器
函数栈帧深度剖析(一篇带你牢牢掌握函数栈帧)(二)
函数栈帧深度剖析(一篇带你牢牢掌握函数栈帧)(二)
65 0
|
8月前
|
存储 机器学习/深度学习 编译器
函数栈帧深度剖析(一篇带你牢牢掌握函数栈帧)(一)
函数栈帧深度剖析(一篇带你牢牢掌握函数栈帧)(一)
161 0
|
10月前
|
存储 编译器 程序员
C语言代码函数栈帧的创建与销毁(修炼内功)
目录 在前期的学习中我们可能有很多困惑 例如:局部变量是怎么创建的 为什么局部变量的值是随机值 函数是怎么样传参的 传参的顺序是什么 形参和实参的关系是什么 函数调用是怎么做的 函数掉调用结束后怎么返回的 这篇博客我们来修炼自己的内功,掌握好这篇博客的大部分知识就已经很不错了 我们用到VS2013这个编译器,目的是为了看到更详细的函数封装内容 提示不要使用太过高级的编译器,因为越高级的编译器越不容易观察。同时这里需要注意的是在不同的编译器下,函数调用过程中栈帧的创建是略有差异的,不是完全相同的,具体细节取决于编译器