segment fault异常及常见定位手段
问题背景
最近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异常及常见定位手段的更多相关文章
- GDB调试之core文件(如何定位到Segment fault)
core dump又叫核心转储,当程序运行过程中发生异常,程序异常退出时,由操作系统把程序当前的内存状况存储在一个core文件中,叫core dump.(内部实现是:linux系统中内存越界会收到SI ...
- 【Z】段错误Segment Fault定位,即core dump文件与gdb定位
使用C++开发系统有时会出现段错误,即Segment Fault.此类错误程序直接崩溃,通常没有任何有用信息输出,很难定位bug,因而无从解决问题.今天我们介绍core dump文件,并使用gdb进行 ...
- segment fault 定位 与 远程 gdb
远程 GDB 首先 ,Target 为 ARM开发板 (IP = 192.168.1.200),HOST 为 Ubuntu 14.04 虚拟机 (IP = 192.168.1.4) 1. 下载 ...
- STM32 F4xx Fault 异常错误定位指南
STM32 F407 采用 Cortex-M4 的内核,该内核的 Fault 异常可以捕获非法的内存访问和非法的编程行为.Fault异常能够检测到以下几类非法行为: 总线 Fault: 在取址.数据读 ...
- Segment fault及LINUX core dump详解 (zz)
C 程序在进行中发生segment fault(core dump)错误,通常与内存操作不当有关,主要有以下几种情况: (1)数组越界. (2)修改了只读内存. (3)scanf("%d&q ...
- 出现segment fault 错误的几种原因
segment fault 段异常各种原因www.MyException.Cn 发布于:2012-11-26 11:48:50 浏览:24次 0 segment fault 段错误各种原因一 造成se ...
- Segment fault及LINUX core dump详解
源自:http://andyniu.iteye.com/blog/1965571 core dump的概念: A core dump is the recorded state of the work ...
- Cortex-M3和Cortex-M4 Fault异常应用之二 ----- Fault处理函数的实现
在项目处于调试期间,Fault处理程序可能只是一个断点指令,调试器遇到这个指令后停止程序的运行.默认情况下,由于非硬Fault被禁能,所有发生的非Fault都会上访成硬Fault,因此只要在硬Faul ...
- Linux下如何生成core dump 文件(解决segment fault段错误的问题)
Linux下的C程序常常会因为内存访问等原因造成segment fault(段错误),如果此时core dump 的功能是打开的,在运行我们的可执行程序时就会生成一个名为core的文件,然后我们就可以 ...
随机推荐
- react+react-router+react-redux+nodejs+mongodb项目
一个实际项目(OA系统)中的部分功能.这个demo中引入了数据库,数据库使用了mongodb.安装mongodb才能运行完整的功能.要看完整的项目可以移步我的github 技术栈 React v15. ...
- Java AES加密案例
AES加密原理 http://www.blogjava.net/amigoxie/archive/2014/07/06/415503.html PHP 加密 https://segmentfault. ...
- Hadoop 错误归档库
在hive中操作任意mapreduce相关语句 The size of Container logs revealed the below error: 2015-04-24 11:41:41,858 ...
- shell脚本——mysql
很期待,学习shell脚本,减少重复工作 自动安装配置mysql脚本: #/bin/bash LOG_FILE=/home/hadoop1/log/installmysql.log function ...
- C语言拼接字符串以及进制转换
#include<stdio.h> #include<stdlib.h> #include<string.h> char *join1(char *, char*) ...
- 远程控制你的智能电视,按键|输入|安装App等都已实现,已开源!
一.序 Hi,大家好,我是承香墨影! 智能电视或者智能盒子,不知道大家了解多少? 这两年各大厂商生产的电视设备,基本上都是搭载的 Android 系统.既然电视本身就是 Android 系统的,我们也 ...
- NLP+语篇分析(五)︱中文语篇分析研究现状(CIPS2016)
摘录自:CIPS2016 中文信息处理报告<第三章 语篇分析研究进展.现状及趋势>P21 CIPS2016 中文信息处理报告下载链接:http://cips-upload.bj.bcebo ...
- linux下FFmpeg编译生成ffplay
1.确认Makefile中指定的config.mak(在ffmpeg根目录下)中:CONFIG_FFPLAY=yes,如果不是需要重新./configure 该处还有ffmpeg.ffprobe.ff ...
- stm32之keil开发环境搭建
只要按照下面的一步步来,绝对能从0开始建立一个STM32工程.不仅包括工程建立过程,还有Jlink设置方法.本文使用芯片为STM32F103CB. 1 下载stm32F10x的官方库 http://w ...
- CentOS使用nmcli配置网络
nmcli 查看网络设备信息 nmcli dev status 查看所有连接的列表 nmcli connection show nmcli connection show --active 查看活动连 ...