问题背景

最近boot中遇到个用户态程序的segment fault异常,除了一句“Segment fault”打印外无其他任何打印。该问题复现概率较低,定位起来比较棘手。我们的boot是个经过裁剪的最小linux系统,由于bootflash大小的限制,加上在boot阶段也没有挂载其他储存设备,所以没有没法放gdb、动态库等体积较大的调试工具。本文以linux 3.10内核和mips cpu小系统为基础,记录下对这个问题的研究总结。

segment fault 异常处理流程

用户态程序由于系统调用或异常等原因,系统陷入内核,并伴随着CPU特权的切换和从用户态栈到内核态栈的切换,内核调用SAVE_ALL保存陷入内核前的现场(即pt_regs结构)到内核栈上,然后内核通过查找异常跳转表或系统调用跳转表获得相应的处理程序入口,处理完成后,给用户态程序发送SIGSEGV信号,并通过pt_regs恢复现场返回到用户态程序,用户态程序收到SIGSEGV信号并进行处理。至此,完成全部处理流程。

可见异常前的现场信息,即pt_regs是个很重要的信息,其具体定义如下,包括CPU通用寄存器、error pc、error cause、bad address等信息。

在linux kernel中,如下场景都会触发pt_regs压栈动作:

  • tlb异常
  • NMI中断
  • 中断
  • 异常
  • 系统调用 
struct pt_regs {
#ifdef CONFIG_32BIT
/* Pad bytes for argument save space on the stack. */
unsigned long pad0[];
#endif /* Saved main processor registers. */
unsigned long regs[]; /* Saved special registers. */
unsigned long cp0_status;
unsigned long hi;
unsigned long lo;
#ifdef CONFIG_CPU_HAS_SMARTMIPS
unsigned long acx;
#endif
unsigned long cp0_badvaddr;
unsigned long cp0_cause;
unsigned long cp0_epc;
#ifdef CONFIG_MIPS_MT_SMTC
unsigned long cp0_tcstatus;
#endif /* CONFIG_MIPS_MT_SMTC */
} __attribute__ ((aligned ()));

segment fault 常见触发源

内核会依据下列条件来判断是否发生了用户态段错误,并上报SIGSEGV信息给用户态task:

  • 用户态数据段的地址越界
  • 用户态代码段的指令读取异常
  • 访问操作与所访问的内存页面权限不匹配
  • 非对齐访问(一般是上报SIGBUS,但mips会上报SIGSEGV)

导致段错误的常见编程范式有:

  • 使用未初始化变量
  • 使用已释放的内存
  • 数组越界
  • 多进程下使用不可重入函数
  • 内存被踩(如栈被踩导致pc或数据寻址错误等)

segment fault 常用定位手段

最佳的定位手段是能直接定位到产生异常的代码,差一点的,至少能提供相关信息,通过分析能间接定位到异常代码。segment fault的定位手段还是比较丰富的,但也各有优缺点,需要根据具体场景进行选用。

gdb

gdb的优点是调试手段丰富,可以逐步跟踪调试,适用于稳定复现的故障。缺点是故障必须能必现。

coredump

coredump的优点是对于偶现的段错误故障,内核会导出一个coredump文件,然后可以用gdb离线调试coredump文件来定位。缺点是如果环境对段错误等异常有重启保护,coredump文件需要有地方存储。

用户态backtrace

glibc的execinfo库提供一套接口:backtrace、backtrace_symbols,可以通过这套接口,捕获到SIGSEGV异常后打印异常发生时的backtrace。缺点是依赖glibc的excinfo,而各CPU对其实现支持情况不一。

内核态backtrace

内核态call trace打印一般通过stack_dump来打印,由于linux的内核态栈和用户态栈是独立分开了,所以stack_dump并不支持用户态call trace打印。但内核提供了save_stack_trace_user/print_stack_trace接口,可以在异常处理程序中打印用户态进程的调用链。缺点是这套接口在arch下实现,而各CPU对其实现支持情况不一。

catchsegv

catchsegv是libc提供的支持段错误back trace打印脚本,可以在发生SIGSEGV时直接打印出异常点的backtrace。缺点是依赖libc的libSegFault.so和addr2line工具。

pt_regs

如前文所说,pt_regs对象提供了异常发生时的error pc、error cause、bad address等信息,反汇编用户程序后,通过error pc等信息可以找到具体的异常汇编指令和函数,分析汇编代码找到对应的C代码。缺点是需要人工分析汇编代码。

解决方案

回到本文一开始的问题,由于bootflash大小的限制,加上在boot阶段也没有挂载其他储存设备,gdb、coredump、catchsegv都没法用;libc对mipc arch下的backtrace实现有问题,用户态backtrace也没法用;mips arch内核没有实现内核态backtrace的接口,所以也没法用。所以只剩下打印pt_regs这一条路了,在上报SIGSEGV前,调用打印即可。虽然mips arch也没有实现打印方法,不过实现很简单,具体实现如下:

static inline void
show_signal_msg(struct pt_regs *regs, unsigned long error_code,
unsigned long address, struct task_struct *tsk)
{
unsigned long sp = regs->regs[];
unsigned long pc = regs->cp0_epc; if (!unhandled_signal(tsk, SIGSEGV))
return; if (!printk_ratelimit())
return; printk("%s%s[%d]: segfault at %lx ip %p sp %p error %lx",
task_pid_nr(tsk) > ? KERN_INFO : KERN_EMERG,
tsk->comm, task_pid_nr(tsk), address,
(void *)pc, (void *)sp, error_code); print_vma_addr(KERN_CONT " in ", pc); printk(KERN_CONT "\n");
}

实例演示:

模拟segv:
const char* p = "abcd";
*(char*)p = 'a';
内核打印:
4:<6>lxImage[1813]: segfault at 1200e5e98 ip 0000000120012628 sp 000000ffffb08140 error 1 4:<c> in lxImage[120000000+124000] 4:<c>

反汇编获知在 BSP_RstReason_Print 函数中sb v1,0(v0) 指令对地址1200e5e98写操作引起segment fault异常
00000001200125a0 <BSP_RstReason_Print>:
...
120012628: a0430000 sb v1,0(v0)

segment fault异常及常见定位手段的更多相关文章

  1. GDB调试之core文件(如何定位到Segment fault)

    core dump又叫核心转储,当程序运行过程中发生异常,程序异常退出时,由操作系统把程序当前的内存状况存储在一个core文件中,叫core dump.(内部实现是:linux系统中内存越界会收到SI ...

  2. 【Z】段错误Segment Fault定位,即core dump文件与gdb定位

    使用C++开发系统有时会出现段错误,即Segment Fault.此类错误程序直接崩溃,通常没有任何有用信息输出,很难定位bug,因而无从解决问题.今天我们介绍core dump文件,并使用gdb进行 ...

  3. segment fault 定位 与 远程 gdb

    远程 GDB  首先 ,Target 为 ARM开发板 (IP =  192.168.1.200),HOST 为 Ubuntu 14.04 虚拟机 (IP = 192.168.1.4) 1. 下载  ...

  4. STM32 F4xx Fault 异常错误定位指南

    STM32 F407 采用 Cortex-M4 的内核,该内核的 Fault 异常可以捕获非法的内存访问和非法的编程行为.Fault异常能够检测到以下几类非法行为: 总线 Fault: 在取址.数据读 ...

  5. Segment fault及LINUX core dump详解 (zz)

    C 程序在进行中发生segment fault(core dump)错误,通常与内存操作不当有关,主要有以下几种情况: (1)数组越界. (2)修改了只读内存. (3)scanf("%d&q ...

  6. 出现segment fault 错误的几种原因

    segment fault 段异常各种原因www.MyException.Cn 发布于:2012-11-26 11:48:50 浏览:24次 0 segment fault 段错误各种原因一 造成se ...

  7. Segment fault及LINUX core dump详解

    源自:http://andyniu.iteye.com/blog/1965571 core dump的概念: A core dump is the recorded state of the work ...

  8. Cortex-M3和Cortex-M4 Fault异常应用之二 ----- Fault处理函数的实现

    在项目处于调试期间,Fault处理程序可能只是一个断点指令,调试器遇到这个指令后停止程序的运行.默认情况下,由于非硬Fault被禁能,所有发生的非Fault都会上访成硬Fault,因此只要在硬Faul ...

  9. Linux下如何生成core dump 文件(解决segment fault段错误的问题)

    Linux下的C程序常常会因为内存访问等原因造成segment fault(段错误),如果此时core dump 的功能是打开的,在运行我们的可执行程序时就会生成一个名为core的文件,然后我们就可以 ...

随机推荐

  1. UVALive - 4329 Ping pong 树状数组

    这题不是一眼题,值得做. 思路: 假设第个选手作为裁判,定义表示在裁判左边的中的能力值小于他的人数,表示裁判右边的中的能力值小于他的人数,那么可以组织场比赛. 那么现在考虑如何求得和数组.根据的定义知 ...

  2. 【2016北京集训测试赛(十六)】 River (最大流)

    Description  Special Judge Hint 注意是全程不能经过两个相同的景点,并且一天的开始和结束不能用同样的交通方式. 题解 题目大意:给定两组点,每组有$n$个点,有若干条跨组 ...

  3. Frequent Pattern (FP Growth算法)

    FP树构造 FP Growth算法利用了巧妙的数据结构,大大降低了Aproir挖掘算法的代价,他不需要不断得生成候选项目队列和不断得扫描整个数据库进行比对.为了达 到这样的效果,它采用了一种简洁的数据 ...

  4. Hive语法

    1.Select 语法 SELECT [ALL | DISTINCT] select_expr, select_expr, ... FROM table_reference [WHERE where_ ...

  5. mac 系统安装 eclipse 方法

    经过好几天的折腾,终于在各种不靠谱的经验.说明的忽悠中把自己心爱的 mac 安装上了 eclipse,看到别人的不靠谱,我决定自己写一篇经验,为了大家能够不走我这么多的弯路,也为了自己将来可以回来看看 ...

  6. 火狐浏览器导出EXCEL 表格,文件名乱码问题

    牢骚:今天又是一个不太平的日子,打开任务表一看里面有一堆bug,其中有一个就是今天要说的这个关于商品导出的问题,本功能是临时授命接一个任务,本来呢这个导出功能在系统各大模块已经都很成熟了,但是总有一个 ...

  7. shell脚本 字串截取 正则表达式

    字串处理 子串截取方法一:使用${}表达式格式:echo ${x:起始位置:长度}(起始位置编号从0开始,可省略) 方法二:使用expr substr格式:expr substr "$x&q ...

  8. java Socket实现简单在线聊天(一)

    最近的项目有一个在线网页交流的需求,由于很久以前做过的demo已经忘记的差不多了,因此便重新学习一下. 我计划的大致实现步骤分这样几大步: 1.使用awt组件和socket实现简单的单客户端向服务端持 ...

  9. Qt keyPressEvent

    keyPressEvent是QWidget里面的函数,所以凡是继承自QWidget的类都可以通过实现这个函数来完成对按键事件的响应. 要让当前的widget能够响应按键事件,最先需要做的事情是,调用: ...

  10. Error Curves HDU - 3714

    Josephina is a clever girl and addicted to Machine Learning recently. She pays much attention to a m ...