linux下怎么查看程序异常,最常见的Linux用户程序异常----Segment Fault

应用层非法访问地址空间的结果和途径

地址空间访问的结果分为三种:

分配一个新的页面。

发送SIGSEGV信号给对应进程。

内核错误杀死进程。

如图所示:

f52f6aafdf7545a253c8db39652761db.png

应用程序访问地址的路径,有五种:

应用程序非法访问了内核态地址.

应用程序读取了读保护的线性区地址.

应用程序写入了写保护的线性区地址.

应用程序访问了不存在的用户虚拟地址.

应用程序系统调用参数错误.

bf3b41675e9c816ab2f4ee538b640568.png

如何尽可能捕捉异常打印

内核配置CONFIG_DEBUG_USER

17c80b3ad632149899917ef6104d84de.png

2146d1852da6e47266f43f8d248a5c64.png

全局变量user_debug

以下是打印具体信号量的code代码行。

72be37e8ffd5a437b8259d766ee2ded2.png

方式1: 修改内核源码

方式2: 修改U-boot的bootargs参数

解析uboot参数的代码如下所示:

7de407535c6948cfad5a5467ae0678d7.png

全局变量print_fatal_signals

print_fatal_signals的作用点如下所示:

166ec8f618b8a400b2196ca0ed7b8cc2.png

print_fatal_signals的赋值方式同样有两种:

方式1: 修改内核源码

方式2: 修改U-boot的bootargs参数

以下是内核解析print_fatal_signals的相关代码行:

1d454abc14fd5e73a80f93817e2d98ba.png

异常信息定位

根据signal num判断错误原因。

获取到进程的虚拟地址空间(/proc/pid/maps),根据内核dump的pc地址进行定位。

使用gdb、objdump进行反汇编辅助定位。

实例1 非法访问导致的SIGSEGV 11信号

实验代码如下所示:

83abd9655ec202447a50ec2ef9bfc72b.png

在上面代码中,我们对预留的物理地址进行了映射;

但在访问时,出现了越界,即非法的地址访问。

由于打开了debug user,于是出现了以下打印:

47d637b2ec90a0177c314b2c7e28ded0.png

从上面打印可以知道,出现问题时,

触发的信号为signal 11.我们来回顾下之前所描述的,即SIGSEGV:

2e2330e526af8e78f620ef1f3f3e44bb.png

另外,PC指针在地址0x19b10908,

而这个地址是属于哪里的呢?我们可以通过/proc/pid/maps一探究竟(pid在打印中有显示,为795)

ed15eab34f6442831d274baa7a342a9c.png

原来出错代码在libc-2.15库对应代码段的偏移

!Offset = 0x19b10908 – 0x19a98000 = 0x78908

而/dev/mem的地址空间在19bd5000-19cd5000,

这显然不是操作预留重映射的地址空间了。

接下来进行反汇编查看,看下glibc中地址偏移为0x78908究竟作了什么操作,

arm-marvell-linux-gnueabi-objdump -S libc-2.15.so > libc-2.15.asm

f0bfaf8cc6a39437831df8e379524525.png

该句汇编的意思是,取r1地址上的数据放到[r0]+1的地址上。

实际上,78908偏移上的内容无所谓,

我们知道这个地址在glibc中就可以确认:应用层的确出现了异常内存访问。

实例2

代码如下所示:

#include

#define __1M 1024*1024

#define __16K 0x4000

void main(void)

{

void * ptr;

ptr=malloc(__1M);

printf("alloc virtual addr is 0x%0x\n", ptr);

memset(ptr, 0x5a, __1M+4);

while(1);

return 0;

}

示例代码malloc申请了1M的内存,但是,使用时,多访问了4个字节。

运行结果:

/user_debug_test # ./out_of_range2 &

/user_debug_test # alloc virtual addr is 0x19bbe008

/user_debug_test #

a953b0e829bd4632a896b7284586c874.png

从图中可以确认堆大小为 0x19c9f000-0x19b9b000=0x104000,

而0x19bbe008属于该段地址。

因此即使是多访问了4个字节,也是不会报错的。

实例3 SIGFPE信号8错误

e5eb4ec60ad17d5573295908fb3017c8.png

实例4用户空间访问异常地址导致的SIGSEGV 错误

我们用同样的方法来看另一个问题,代码如下所示:

5f1c3859d6362f6bd4cf54323c4ed803.png

如上所示,用户空间线程访问了0地址(不该访问的内核空间),导致奔溃。

7ddedc880099019c9af4aaddfa368602.png

PC地址0x10688在我们示例的代码段中,

而LR地址0x19b80e64在pthread库代码中。

使用反汇编也能证明这点。

11c24067991da1063fb9ae66c46e7884.png

总结

首先,内核需要打开相关的选项;然后,根据PC和LR地址,

结合出错pid的maps以及出错的信号,定位出错点。

现在kernel默认没有了USER_DUBUG选项,所以不用在打开

但是要打开如下开关

/proc/sys/kernel/print-fatal-signals

ulimit -c x

objdump汇编出源码查看即可

文档信息

--------------

* 版权声明:自由转载-非商用

* 转载: [最常见的Linux用户程序异常----Segment Fault]

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Segmentation fault(段错误)通常是由于程序访问了无效的内存地址引起的。在 Linux 中,可以使用以下方法来查看段错误的原因: 1. 使用 gdb 调试器 使用 gdb 可以帮助我们定位代码中出现段错误的位置。首先,需要在编译时加上 `-g` 选项生成调试信息。然后,在运行程序时,使用 gdb 启动程序,并在出现段错误时停止程序。可以使用 `bt` 命令查看调用栈,找到出现段错误的位置。例如: ``` $ gcc -g -o myprogram myprogram.c $ gdb myprogram (gdb) run Starting program: /home/user/myprogram Program received signal SIGSEGV, Segmentation fault. 0x00005555555546d3 in main () (gdb) bt #0 0x00005555555546d3 in main () ``` 在上面的示例中,我们在 `main` 函数中出现了段错误。 2. 使用 valgrind 工具 valgrind 是一款用于检测内存泄漏和内存访问错误的工具。可以在 Linux 中使用以下命令来安装 valgrind: ``` $ sudo apt-get install valgrind ``` 然后,在运行程序时使用 valgrind 工具检测内存错误。例如: ``` $ valgrind ./myprogram ``` valgrind 会输出内存错误的详细信息,帮助我们定位问题。 3. 使用系统日志 当程序出现段错误时,Linux 系统会将错误信息写入系统日志。可以使用以下命令来查看系统日志: ``` $ dmesg | tail ``` 在输出中,可以找到与段错误相关的信息,例如内存地址和错误代码。 通过以上方法,我们可以定位并解决程序中出现的段错误。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值