LINUX C/C++捕获段错误,打印出错的具体位置(精确到哪一行) ​ --Xilinx ARM版本

    之前文章转载了 LINUX C/C++捕获段错误,打印出错的具体位置(精确到哪一行) , 但在arm-xilinx-linux-gnueabi-gcc 编译是无法通过,现在把能在arm-xilinx-linux-gnueabi-gcc下编译的源码发布如下:

/*
* 作者: leehomwu
* 日期: 2011-05-14
* 修订: 2011-06-11, 处理部分处理器会将出错时的 EIP 也入栈的情况
* 描述: 捕获段错误、浮点错误,输出发生错误时的具体位置、调用路径
* 使用: 在 main 函数所在文件包含该头文件即可
* 示例: 当发生段错误或浮点错误时,会向STDOUT打印调用路径上的指令地址,类似:
			signal[8] catched when running code at 80486f0
			signal[8] catched when running code at 80488ea
			signal[8] catched when running code at 80488d9

		进一步运行命令行,解析指令地址:
			addr2line  80486f0 80488ea 80488d9 -s -C -f -e [可执行文件]

		得到输出:
			main
			newsig.cpp:14
			a()
			kk.cpp:23
			b()
			kk.cpp:19
*/


#ifndef __SEGV_CATCH_H
#define __SEGV_CATCH_H


#include <execinfo.h>
#include <signal.h>
#include <stdio.h>



#ifndef __USE_GNU
	#define __USE_GNU
	#include <ucontext.h>
	#include <ucontext.h>
	#undef __USE_GNU
#else
	#include <ucontext.h>
	#include <sys/ucontext.h>
#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 i;
	if (i++ >= 1)
	{
		//容错处理:如果访问 ucontext_t 结构体时产生错误会进入该分支
		printf("ReEnter %s is not allowed!\n", __FUNCTION__);
		abort();
	}

	void* pBuff[32];
	int   nSize = backtrace(pBuff, sizeof(pBuff)/sizeof(pBuff[0]));

	for (i = 0; i < nSize; i++)
	{
	    printf("backtrace: 0x%x 0x%x\n", pBuff[i], *((int*)pBuff[i]));
	}

	char** strSymbols = backtrace_symbols(pBuff, nSize);
    if (NULL != strSymbols)
    {
        for (i = 0; i < nSize; i++)
        {
            printf("%03d: %s\n", i, strSymbols[i]);
        }

        free(strSymbols);
    }


	if (NULL != ptr)
	{
		ucontext_t* ptrUC = (ucontext_t*)ptr;


        printf("signal[%d]  r0 0x%x\n", signum, ptrUC->uc_mcontext.arm_r0);
        printf("signal[%d]  r1 0x%x\n", signum, ptrUC->uc_mcontext.arm_r1);
        printf("signal[%d]  r2 0x%x\n", signum, ptrUC->uc_mcontext.arm_r2);
        printf("signal[%d]  r3 0x%x\n", signum, ptrUC->uc_mcontext.arm_r3);
        printf("signal[%d]  r4 0x%x\n", signum, ptrUC->uc_mcontext.arm_r4);
        printf("signal[%d]  r5 0x%x\n", signum, ptrUC->uc_mcontext.arm_r5);
        printf("signal[%d]  r6 0x%x\n", signum, ptrUC->uc_mcontext.arm_r6);
        printf("signal[%d]  r7 0x%x\n", signum, ptrUC->uc_mcontext.arm_r7);
        printf("signal[%d]  r8 0x%x\n", signum, ptrUC->uc_mcontext.arm_r8);
        printf("signal[%d]  r9 0x%x\n", signum, ptrUC->uc_mcontext.arm_r9);
        printf("signal[%d]  r10 0x%x\n",signum, ptrUC->uc_mcontext.arm_r10);
        printf("signal[%d]  ip 0x%x\n", signum, ptrUC->uc_mcontext.arm_ip);
        printf("signal[%d]  sp 0x%x\n", signum, ptrUC->uc_mcontext.arm_sp);
        printf("signal[%d]  fp 0x%x\n", signum, ptrUC->uc_mcontext.arm_fp);
        printf("signal[%d]  pc 0x%x\n", signum, ptrUC->uc_mcontext.arm_pc);
        printf("signal[%d]  lr 0x%x\n", signum, ptrUC->uc_mcontext.arm_lr);
        printf("signal[%d]  cpsr 0x%x\n", signum, ptrUC->uc_mcontext.arm_cpsr);
        printf("signal[%d]  trap_no 0x%x\n", signum, ptrUC->uc_mcontext.trap_no);
        printf("signal[%d]  error_code 0x%x\n", signum, ptrUC->uc_mcontext.error_code);
        printf("signal[%d]  fault_address 0x%x\n", signum, ptrUC->uc_mcontext.fault_address);
	}
	else
	{
		printf("signal[%d]  when running code at unknown address\n", signum);
	}

	abort();
}



#endif // __SEGV_CATCH_H

uc_mcontext 结构体如下

#ifndef _ASMARM_SIGCONTEXT_H
#define _ASMARM_SIGCONTEXT_H

/*
 * Signal context structure - contains all info to do with the state
 * before the signal handler was invoked.  Note: only add new entries
 * to the end of the structure.
 */
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

参考: 通过coredump - 用backtrace和addr2line 查找异常函数栈_悟OO道的博客

backtrace: 0x2423c 0xe50b0008
backtrace: 0xb6c59e80 0xe3a070ad
backtrace: 0x174d4 0xe5823004
backtrace: 0x137ec 0xe50b0008
backtrace: 0x13d5c 0xe50b0008
backtrace: 0x140c4 0xe1a03000
backtrace: 0x144d0 0xe1a03000
backtrace: 0xb01c 0xea000066
backtrace: 0xace0 0xea00002d
backtrace: 0x1df68 0xea000031
backtrace: 0x33f4c 0xe3a00d35
backtrace: 0xb6eff038 0xe51b61c0
backtrace: 0xb6d04488 0xeaff2764
013: ./main() [0x2423c]
012: /lib/libc.so.6(__default_rt_sa_restorer_v2+0) [0xb6c59e80]
011: ./main() [0x174d4]
010: ./main() [0x137ec]
009: ./main() [0x13d5c]
008: ./main() [0x140c4]
007: ./main() [0x144d0]
006: ./main() [0xb01c]
005: ./main() [0xace0]
004: ./main() [0x1df68]
003: ./main() [0x33f4c]
002: /lib/libpthread.so.0(+0x7038) [0xb6eff038]
001: /lib/libc.so.6(clone+0x88) [0xb6d04488]
signal[11] catched r0 0xa0f8c00c
signal[11] catched r1 0xb4e40000
signal[11] catched r2 0x280000
signal[11] catched r3 0xa0f8c008
signal[11] catched r4 0x0
signal[11] catched r5 0x0
signal[11] catched r6 0x0
signal[11] catched r7 0x152
signal[11] catched r8 0x0
signal[11] catched r9 0xb6f0ab60
signal[11] catched r10 0xb6f0ab60
signal[11] catched ip 0x0
signal[11] catched sp 0xa5898f60
signal[11] catched fp 0xa5898f7c
signal[11] catched lr 0x17434
signal[11] catched cpsr 0xa0000010
signal[11] catched trap_no 0xe
signal[11] catched error_code 0x805
signal[11] catched fault_address 0x280004

使用addr2line  -- Linux debug : addr2line追踪出错地址_悟OO道的博客

             --  通过coredump - 用backtrace和addr2line 查找异常函数栈_悟OO道的博客

gdb + core --  Linux下调试段错误的方法[Segmentation Fault]--GDB_悟OO道的博客

                 --   关于Segmentation fault (core dumped)几个简单问题_悟OO道的博客

都不能解析出错误函数。ZYNQ板卡上使用报错!

下一步反汇编查找试试!!!

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得知上一个栈帧的起始位置。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值