之前文章转载了 LINUX C/C++捕获段错误,打印出错的具体位置(精确到哪一行) , 但再arm-xilinx-linux-gnueabi-gcc 编译是无法通过,现在把能在arm-xilinx-linux-gnueabi-gcc下编译的源码发布如下:
#ifndef __SEGV_CATCH_H #define __SEGV_CATCH_H #include#include #include #ifndef __USE_GNU #define __USE_GNU #include #include #undef __USE_GNU #else #include #include #endif #ifdef __cplusplus static void initSegvCatch(void); class C_SEGVCATCH{ public: C_SEGVCATCH(){ initSegvCatch(); } }; static C_SEGVCATCH C_segv_catch; #else static void initSegvCatch(void) __attribute__ ((constructor)); #endif static void OnSIGSEGV(int,siginfo_t*,void*); static void initSegvCatch() { struct sigaction act; sigemptyset(&act.sa_mask); act.sa_sigaction = OnSIGSEGV; act.sa_flags = SA_SIGINFO; if (sigaction(SIGSEGV, &act, NULL)<0 || sigaction(SIGFPE, &act, NULL)<0) { //perror("sigaction:"); printf("set segment catch faile!n"); } printf("set segment catch ok!n"); } static void OnSIGSEGV(int signum, siginfo_t *info, void *ptr) { static int iTime; if (iTime++ >= 1) { //容错处理:如果访问 ucontext_t 结构体时产生错误会进入该分支 printf("ReEnter %s is not allowed!n", __FUNCTION__); abort(); } void * array[32]; int nSize = backtrace(array, sizeof(array)/sizeof(array[0])); printf("signal: %d n", nSize); int i; for (i = nSize-3; i > 2; i--) { //头尾几个地址不必输出 //对array修正一下,使地址指向正在执行的代码 printf("signal[%d] catched when running code at 0x%pn", signum, array[i] - 1); } if (NULL != ptr) { ucontext_t* ptrUC = (ucontext_t*)ptr; printf("signal[%d] r0 0x%xn", signum, ptrUC->uc_mcontext.arm_r0); printf("signal[%d] r1 0x%xn", signum, ptrUC->uc_mcontext.arm_r1); printf("signal[%d] r2 0x%xn", signum, ptrUC->uc_mcontext.arm_r2); printf("signal[%d] r3 0x%xn", signum, ptrUC->uc_mcontext.arm_r3); printf("signal[%d] r4 0x%xn", signum, ptrUC->uc_mcontext.arm_r4); printf("signal[%d] r5 0x%xn", signum, ptrUC->uc_mcontext.arm_r5); printf("signal[%d] r6 0x%xn", signum, ptrUC->uc_mcontext.arm_r6); printf("signal[%d] r7 0x%xn", signum, ptrUC->uc_mcontext.arm_r7); printf("signal[%d] r8 0x%xn", signum, ptrUC->uc_mcontext.arm_r8); printf("signal[%d] r9 0x%xn", signum, ptrUC->uc_mcontext.arm_r9); printf("signal[%d] r10 0x%xn",signum, ptrUC->uc_mcontext.arm_r10); printf("signal[%d] ip 0x%xn", signum, ptrUC->uc_mcontext.arm_ip); printf("signal[%d] sp 0x%xn", signum, ptrUC->uc_mcontext.arm_sp); printf("signal[%d] fp 0x%xn", signum, ptrUC->uc_mcontext.arm_fp); printf("signal[%d] lr 0x%xn", signum, ptrUC->uc_mcontext.arm_lr); printf("signal[%d] cpsr 0x%xn", signum, ptrUC->uc_mcontext.arm_cpsr); printf("signal[%d] trap_no 0x%xn", signum, ptrUC->uc_mcontext.trap_no); printf("signal[%d] error_code 0x%xn", signum, ptrUC->uc_mcontext.error_code); printf("fault_address 0x%xn", ptrUC->uc_mcontext.fault_address); } else { printf("signal[%d] catched when running code at unknown addressn", signum); } abort(); } #endif // __SEGV_CATCH_H
uc_mcontext 结构体如下
#ifndef _ASMARM_SIGCONTEXT_H
#define _ASMARM_SIGCONTEXT_H
struct sigcontext {
unsigned long trap_no;
unsigned long error_code;
unsigned long oldmask;
unsigned long arm_r0;
unsigned long arm_r1;
unsigned long arm_r2;
unsigned long arm_r3;
unsigned long arm_r4;
unsigned long arm_r5;
unsigned long arm_r6;
unsigned long arm_r7;
unsigned long arm_r8;
unsigned long arm_r9;
unsigned long arm_r10;
unsigned long arm_fp;
unsigned long arm_ip;
unsigned long arm_sp;
unsigned long arm_lr;
unsigned long arm_pc;
unsigned long arm_cpsr;
unsigned long fault_address;
};
#endif
arm栈帧结构
通常情况下,arm的调用栈大致结构与x86相同,都是从高地址向低地址扩张。
pc, lr, sp, fp是处理器的寄存器,其含义如下:
- pc, program counter,程序计数器。程序当前运行的指令会放入到pc寄存器中
- fp, 即frame pointer,帧指针。通常指向一个函数的栈帧底部,表示一个函数栈的开始位置。
- sp, stack pointer,栈顶指针。指向当前栈空间的顶部位置,当进行push和pop时会一起移动。
- lr, link register。在进行函数调用时,会将函数返回后要执行的下一条指令放入lr中,对应x86架构下的返回地址。
调用栈从高地址向低地址增长,当函数调用时,分别将分别将pc, lr, ip和 fp寄存器压入栈中,然后移动sp指针,为当前程序开辟栈空间。
arm官方手册描述如下:
一个arm程序,在任一时刻都存在十五个通用寄存器,这取决于当前的处理器模式。 它们分别是 r0-r12、sp、lr。
sp(或 r13)是堆栈指针。 C 和 C++ 编译器始终将 sp 用作堆栈指针。 在 Thumb-2 中,sp 被严格定义为堆栈指针,因此许多对堆栈操作无用而又使用了 sp 的指令会产生不可预测的结果。 建议您不要将 sp 用作通用寄存器。
在用户模式下,lr(或 r14)用作链接寄存器 (lr),用于存储调用子例程时的返回地址。 如果返回地址存储在堆栈上,则也可将 r14 用作通用寄存器。
在异常处理模式下,lr 存放异常的返回地址;如果在一个异常内执行了子例程调用,则 lr 存放子例程的返回地址。如果返回地址存储在堆栈上,则可将 lr 用作通用寄存器。
除了官方手册中描述的sp,lr寄存器,通常r12还会作为fp寄存器。fp寄存器对于程序的运行没有帮助,主要用于对栈帧的回溯。因为sp时刻指向的栈顶,通过fp得知上一个栈帧的起始位置。



