首先来看一下当应用层出现错误,默认的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
MIPS架构下Linux内核异常处理与堆栈回溯

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

被折叠的 条评论
为什么被折叠?



