问题背景

最近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. LeetCode第七天

    ==数组 Medium== 40.(162)Find Peak Element JAVA //斜率思想,二分法 class Solution { public int findPeakElement( ...

  2. 一步一步创建ASP.NET MVC5程序[Repository+Autofac+Automapper+SqlSugar](十)

    前言 朋友们, 大家好,我还是Rector,写ASP.NET MVC 5系列文章[一步一步创建ASP.NET MVC5程序Repository+Autofac+Automapper+SqlSugar] ...

  3. 项目实战14—ELK 企业内部日志分析系统

    一.els.elk 的介绍 1.els,elk els:ElasticSearch,Logstash,Kibana,Beats elk:ElasticSearch,Logstash,Kibana ① ...

  4. 前端js,后台python实现RSA非对称加密

    先熟悉使用 在后台使用RSA实现秘钥生产,加密,解密; # -*- encoding:utf-8 -*- import base64 from Crypto import Random from Cr ...

  5. 详解Java的自动装箱与拆箱(Autoboxing and unboxing)

    一.什么是自动装箱拆箱 很简单,下面两句代码就可以看到装箱和拆箱过程 //自动装箱 Integer total = 99; //自定拆箱 int totalprim = total; 简单一点说,装箱 ...

  6. PCI9054芯片的型号说明及购买建议

    个脚,这也是大部分人用到的:而"BI"结尾的是BGA封装的,225个脚,较少用到,对它不再多说. 这几种系列量产的时间如下: 年11月 年8月 年2月 年 年 年到2006年期间, ...

  7. 嵌入式linux------SDL移植(am335x下显示yuv420)

    #include<stdio.h> #include "/usr/local/ffmpeg_arm/include/SDL/SDL.h" char *bmp_name[ ...

  8. OpenGL直线点画模式

    程序来自<OpenGL编程指南第七版> #include "stdafx.h" #include <Windows.h>//头文件一定要加上,因为windo ...

  9. (二十三)mongodb中group的问题

    今天的工作中我需要从mongodb数据库中查出一定的数据,并排序后返回给前台,数据库表中包含了ruleID,processingID,userID,updateTime等字段.    同一个ruleI ...

  10. View的平移、缩放、旋转以及位置、坐标系

    原创 2015年05月12日 13:15:29 标签: Android / Scroll / Scale / Translation / Rotation 24733 Android开发中,经常会接触 ...