broadcom6838开发环境实现函数栈追踪
在嵌入式设备开发中。内核为内核模块的函数栈追踪已经提供了非常好的支持,但用户层的函数栈追踪确没有非常好的提供支持。
在网上收集学习函数栈跟踪大部分都是描写叙述INTER体系架构支持栈帧的实现机制。或者直接给出glibc的现成库函数。
但假设开发环境是broadcom相关方案,通常使用的是MIPS32的体系架构,而且C库使用的是更小的uclibc。尽管MIPS32体系架构中也定义了栈帧寄存器s8(类似于Inter体系架构中常见的ebp寄存器),但经过GCC编译器的优化选项控制后,通常在O1以上的优化就已经去除了s8栈帧的使用,所以给函数栈追踪的实现就带来了一点点小麻烦。
通常情况下。函数的返回值还是使用压栈实现,所以仅仅要知道了函数每次调用时,返回值(ra寄存器)与当前栈顶(sp寄存器)的偏移量,就能够实现函数栈追踪,对函数反汇编能够了解到这个偏移量通过sw ra, xxxx(sp)能够得到,还有一个难题是怎么得到函数每次调用时的当前栈顶(sp寄存器),通过函数反汇编能够了解到addiu sp, sp, xxxx指令就是每次给函数分配当前栈帧大小的,所以仅仅要得到这个栈帧大小然后用sp进行差值计算就能够往回推出上一个sp的值了。还剩下最后一个问题,一步步获取上一个函数的栈顶,什么时候结束?答案就是仅仅要栈顶sp的寄存器为0就追踪到头了。通过这些分析我们能够了解到当前实现机制根本没用到程序的栈段内容。对,全然从程序指令段做为线索,获取程序执行时指令一步步找出函数栈的调用。看起来非常酷。但现实也比較残酷,比方我们上面分析说要得到栈顶sp的寄存器为0表示追踪到头,我当前调试环境在__start(能够參考一些链接载入的相关技术资料了解,其他main并非c的起始函数。__start才是c语言的起始函数)函数中使得sp为0的汇编指令为addu
ra, zero, zero,我还不清楚其他编译器是否会使用其他指令方法进行设置。所以我们当前实现的这个函数栈追跟踪功能的实现代码并非非常标准的。假设读者有幸參考该代码,请一定要在理解上面描写叙述的原理基础上。另外须要在您自己的编译开发环境进行调试,确保这个追踪功能代码自己不会引发Crash再使用,否则那玩笑就开的大了。
剩下就不多说了。直接付上我当前的开发环境及实现源代码,以及终于在broadcom6838板上执行的測试程序的效果图。
开发环境:
实现源代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <ucontext.h> #define abs(s) ((s) < 0 ? -(s) : (s)) #define CALL_TRACE_MAX_SIZE (10) #define MIPS_ADDIU_SP_SP_XXXX (0x27bd0000) /* instruction code for addiu sp, sp, xxxx */
#define MIPS_SW_RA_XXXX_SP (0xafbf0000) /* instruction code for sw ra, xxxx(sp) */
#define MIPS_ADDU_RA_ZERO_ZERO (0x0000f821) /* instruction code for addu ra, zero, zero */ void getCodeIn(unsigned long codeAddr, char *pCodeIn, int iCodeInSize, unsigned long *pOffset)
{
FILE *pFile = NULL;
char szLine[1000] = {0}; pFile = fopen("/proc/self/maps", "r");
if ( pFile != NULL )
{
while (fgets(szLine, sizeof(szLine), pFile))
{
char *pTmp = NULL;
char szAddr[500] = {0};
char szCodeIn[500] = {0};
unsigned long begin = 0;
unsigned long end = 0; sscanf(szLine, "%s %*s %*s %*s %*s %s", szAddr, szCodeIn); pTmp = strchr(szAddr, '-');
if ( pTmp != NULL )
{
*pTmp++ = '\0';
begin = strtoul(szAddr, NULL, 16);
end = strtoul(pTmp, NULL, 16);
} if ( codeAddr >= begin && codeAddr <= end )
{
strncpy(pCodeIn, szCodeIn, iCodeInSize);
*pOffset = codeAddr - begin;
return;
}
} fclose(pFile);
} strncpy(pCodeIn, "unknown", iCodeInSize);
*pOffset = 0;
} int backtrace_mips32(void **buffer, int size, ucontext_t *uc)
{
unsigned long *tmpl = NULL;
unsigned long *addr = NULL;
unsigned long *ra = NULL;
unsigned long *sp = NULL;
unsigned long *pc = NULL;
size_t ra_offset = 0;
size_t stack_size = 0;
int depth = 0; if (size == 0)
{
return 0;
} if (buffer == NULL || size < 0 || uc == NULL)
{
return -1;
} pc = (unsigned long *)(unsigned long)uc->uc_mcontext.pc;
ra = (unsigned long *)(unsigned long)uc->uc_mcontext.gregs[31];
sp = (unsigned long *)(unsigned long)uc->uc_mcontext.gregs[29]; buffer[0] = pc; if ( size == 1 )
{
return 1;
} for ( addr = pc; !ra_offset || !stack_size; --addr )
{
if ( ((*addr) & 0xffff0000) == MIPS_SW_RA_XXXX_SP)
{
ra_offset = (short)((*addr) & 0xffff);
}
else if ( ((*addr) & 0xffff0000) == MIPS_ADDIU_SP_SP_XXXX)
{
stack_size = abs((short)((*addr) & 0xffff));
}
else if ( (*addr) == MIPS_ADDU_RA_ZERO_ZERO )
{
return 1;
}
} if ( ra_offset > 0 )
{
tmpl = (unsigned long *)((char *)sp + ra_offset);
ra = (unsigned long *)(*tmpl);
} if ( stack_size > 0 )
{
sp = (unsigned long *)((unsigned long)sp + stack_size);
} for (depth = 1; depth < size && ra; ++depth)
{
buffer[depth] = ra; ra_offset = 0;
stack_size = 0; for (addr = ra; !ra_offset || !stack_size; --addr)
{
if ( ((*addr) & 0xffff0000) == MIPS_SW_RA_XXXX_SP)
{
ra_offset = (short)((*addr) & 0xffff);
}
else if ( ((*addr) & 0xffff0000) == MIPS_ADDIU_SP_SP_XXXX)
{
stack_size = abs((short)((*addr) & 0xffff));
}
else if ( (*addr) == MIPS_ADDU_RA_ZERO_ZERO )
{
return depth + 1;
}
} tmpl = (unsigned long *)((char *)sp + ra_offset);
ra = (unsigned long *)(*tmpl);
sp = (unsigned long *)((unsigned long)sp + stack_size);
} return depth;
} void signal_process(int sig_no, siginfo_t *sig_info, void *ucontext)
{
int i = 0;
unsigned long *callStack[CALL_TRACE_MAX_SIZE] = {0}; printf("\r\n*******************************************\r\n"); printf("recv signo %d\r\n", sig_no); backtrace_mips32((void **)callStack, CALL_TRACE_MAX_SIZE, (ucontext_t *)ucontext); printf("\r\ncall tracing:\r\n");
for ( i = 0; i < CALL_TRACE_MAX_SIZE; i++ )
{
char szCodeIn[100] = {0};
unsigned long addrOffset = 0; if ( callStack[i] == 0 )
{
break;
} getCodeIn((unsigned long)callStack[i], szCodeIn, sizeof(szCodeIn), &addrOffset); printf("\t[%02d] addr 0x%08x(offset 0x%08x), in %s\r\n", i, callStack[i], addrOffset, szCodeIn);
} printf("*******************************************\r\n"); if (sig_no == SIGSEGV)
{
signal(sig_no, SIG_DFL);
} sleep(3);
} void c(void)
{
int *p = 0;
printf("I am function [c]\r\n");
*p = 888;
} void b(void)
{
printf("I am function [b]\r\n");
c();
} void a(void)
{
printf("I am function [a]\r\n");
b();
} int main(int argc, char *argv[])
{
struct sigaction act = {0}; act.sa_sigaction = signal_process;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO; sigaction(SIGSEGV, &act, NULL); printf("I am function [main]\r\n"); a(); return 0;
}
执行效果图:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl1amlhbmZlbmcxOTg0/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
broadcom6838开发环境实现函数栈追踪的更多相关文章
- 转-subl配置全栈开发环境
为 Sublime Text 3 设置 Python 的全栈开发环境 Sublime Text 3 (ST3) 是一个轻量级的跨平台文字编辑器,尤以其轻快的速度,易用性和强大的社区支持而著称.它一经面 ...
- Win10构建Python全栈开发环境With WSL
目录 Win10构建Python全栈开发环境With WSL 启动WSL 总结 对<Dev on Windows with WSL>的补充 Win10构建Python全栈开发环境With ...
- Vue+koa2开发一款全栈小程序(5.服务端环境搭建和项目初始化)
1.微信公众平台小程序关联腾讯云 腾讯云的开发环境是给免费的一个后台,但是只能够用于开发,如果用于生产是需要花钱的,我们先用开发环境吧 1.用小程序开发邮箱账号登录微信公众平台 2.[设置]→[开发者 ...
- python全栈开发 生成器 :生成器函数,推导式及生成器表达式
python 全栈开发 1.生成器函数 2.推导式 3.生成器表达式 一.生成器函数 1.生成器: 生成器的本质就是迭代器 (1)生成器的特点和迭代器一样.取值方式和迭代器一样(__next__(), ...
- python全栈开发之匿名函数和递归函数
python 匿名函数和递归函数 python全栈开发,匿名函数,递归函数 匿名函数 lambda函数也叫匿名函数,即函数没有具体的名称.是为了解决一些功能很简单需求而设计的一句话函数.如下: #这段 ...
- 痞子衡嵌入式:在IAR开发环境下将关键函数重定向到RAM中执行的三种方法
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是在IAR开发环境下将关键函数重定向到RAM中执行的三种方法. 嵌入式项目里应用程序代码正常是放在 Flash 中执行的,但有时候也需要将 ...
- 痞子衡嵌入式:在MDK开发环境下将关键函数重定向到RAM中执行的几种方法
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是在MDK开发环境下将关键函数重定向到RAM中执行的几种方法. 这个关键函数重定向到 RAM 中执行系列文章,痞子衡已经写过 <IA ...
- 痞子衡嵌入式:在IAR开发环境下RT-Thread工程函数重定向失效分析
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是在IAR开发环境下RT-Thread工程函数重定向失效分析. 痞子衡旧文 <在IAR下将关键函数重定向到RAM中执行的方法> ...
- Python 全栈开发 -- 开发环境篇
开发环境是一个文本编辑器和 Python 解释器的组合.文本编辑器用来写代码,解释器提供了一种方法来运行编写的代码.一个文本编辑器可以像 Windows 上的 Notepad 一样简单,或是一个复杂的 ...
随机推荐
- N使用exus2打造企业maven仓库(三)
假设项目中,我没有使用maven,我应该做出选择,或为项目.或者用它来推动这个项目从maven.有人会问,为什么maven?无需maven我们没有很好的操作. 这里,只说两件事情我最欣赏:第一点是管理 ...
- 【Android】属性动画
转载请注明出处:http://blog.csdn.net/h28496/44338669 属性动画的原理 通过不断的设置一个View的属性让其出现动画效果.比如,不断地设置一个Button的x值.这个 ...
- Eclipse用法和技巧三:自动生成Main方法2
上一篇文章里面介绍了新建文件时候自动添加main方法,这里接着介绍自动联想main方法. 步骤一:输入"main” 步骤二:保持光标在上图位置,按ALT + /,再回车 上一篇文 ...
- Exchange Server 2013传输规则之全新附件限制
- TCanvas.CopyRect方法中参数CopyMode的意义
首先看可能取值: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 cmBlackness = BLACKNESS; cmDstInvert = DSTINVERT; cmMer ...
- baas & API 网关
最近一段时间一直在做API 网关的工作.清晰看到当前云下Baas将会是主要方向,而API网关会是一把利剑. 本人正在规划API网关,有兴趣的可以一起探讨:hotwheels_bo@163.com
- VC socket Connect 超时时间设置
设置connect超时很简单,CSDN上也有人提到过使用select,但却没有一个令人满意与完整的答案.偶所讲的也正是select函数,此函数集成在winsock1.1中,简单点讲,"作用使 ...
- HDU 1556 Color the Ball 线段树 题解
本题使用线段树自然能够,由于区间的问题. 这里比較难想的就是: 1 最后更新须要查询全部叶子节点的值,故此须要使用O(nlgn)时间效率更新全部点. 2 截取区间不能有半点差错.否则答案错误. 这两点 ...
- HDOJ 2442 -bricks 状态压缩DP 一直TLE.打表过的..
有5个砖块..加上一个空着不放..那么有6种状态..所以很明显的可以用6进制的状态DP... 不过这么做..我觉得我已经能优化的都优化了...还是超时..一看数据范围是100*6..打表先AC了.. ...
- Android之后台服务判断本应用Activity是否处于栈顶
在Android开发中,我们经常想知道是否自己的服务处于后台运行中,因为在后台运行的服务器优先级会降低,也就极有可能会被系统给回收掉,有什么好办法呢?Google推荐我们将服务运行到前台,如何知道服务 ...