基于MIPS的backtrace调试

文章详细介绍了当MIPS架构的Linux应用层出现错误时,内核如何处理异常并打印错误信息。通过分析内核源码,展示了如何增强错误输出,以及如何通过寄存器和堆栈信息进行调用栈回溯。同时,提到了在多线程死锁情况下如何使用GDB进行线程栈回溯,并给出了一段多线程死锁的示例代码。

首先来看一下当应用层出现错误,默认的linux-kernel输出的错误信息。应用层代码如下:

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

extern int sigset(int signo);
int func_a(unsigned char* a, unsigned char * b, int c, int d);
int func_b(unsigned char* a);
int func_c(unsigned char* a);

int func_a(unsigned char* a, unsigned char * b, int c, int d)
{
    func_b(a);
    return 0;
}


int func_b(unsigned char* a)
{
    func_c(a);
    return 0;
}

 
int func_c(unsigned char* a)
{
    *a = "Hello";
    return 0;
}


int main(int argc, char* argv[])
{
	unsigned char* a;
	unsigned char buffer[2];
	a = NULL;
	
	//*a = 1;  
	func_a(a, buffer, 2, 3);

	return 0;
}

在函数func_c中对空指针a赋值,导致异常,将内核打印打开,输出的错误信息如下:

# echo "8  4    1    7" >/proc/sys/kernel/printk

# ./main

do_page_fault(): sending SIGSEGV to main for invalid write access to 00000000
epc = 00400854 in main[400000+1000]
ra  = 00400818 in main[400000+1000]

Segmentation fault

看起来kernel对应用层引发的异常不够重视啊,我们来想办法改善一下。

当内核崩溃,将会执行异常处理程序,mips架构下溃函数执行流程是:

do_page_fault()->die()->show_registers()->show_stacktrace()->show_backtrace()

查看kernel源码,Fault.c (\linux-pdk2.8.0-20220401-auto\arch\mips\mm):

static void __kprobes __do_page_fault(struct pt_regs *regs, unsigned long write,
	unsigned long address)
{
    ...

bad_area_nosemaphore:
	/* User mode accesses just cause a SIGSEGV */
	if (user_mode(regs)) {
		tsk->thread.cp0_badvaddr = address;
		tsk->thread.error_code = write;
		if (show_unhandled_signals &&
		    unhandled_signal(tsk, SIGSEGV) &&
		    __ratelimit(&ratelimit_state)) {
			pr_info("\ndo_page_fault(): sending SIGSEGV to %s for invalid %s %0*lx",
				tsk->comm,
				write ? "write access to" : "read access from",
				field, address);
			pr_info("epc = %0*lx in", field,
				(unsigned long) regs->cp0_epc);
			print_vma_addr(" ", regs->cp0_epc);
			pr_info("ra  = %0*lx in", field,
				(unsigned long) regs->regs[31]);
			print_vma_addr(" ", regs->regs[31]);
			pr_info("\n");
		}
		current->thread.trap_nr = (regs->cp0_cause >> 2) & 0x1f;
		info.si_signo = SIGSEGV;
		info.si_errno = 0;
		/* info.si_code has been set above */
		info.si_addr = (void __user *) address;
		force_sig_info(SIGSEGV, &info, tsk);
		return;
	}

    ...
}

果然由usermode引发的异常打印的信息很有限。

不过kernel本身已经提供了很多打印现场的函数,我们在这里调用试试看效果。

下面函数打印了现场的寄存器信息及stack数据。

Traps.c (.\linux-pdk2.8.0-20220401-auto\arch\mips\kernel): :void show_registers(struct pt_regs *regs)

static void __kprobes __do_page_fault(struct pt_regs *regs, unsigned long write,
	unsigned long address)
{
    ...
		if (show_unhandled_signals &&
		    unhandled_signal(tsk, SIGSEGV) &&
		    __ratelimit(&ratelimit_state)) {
			pr_info("\ndo_page_fault(): sending SIGSEGV to %s for invalid %s %0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值