参考这篇文章:

http://blog.chinaunix.net/uid-24599332-id-2122898.html

SIGBUS和SIGSEGV也许是我们在平时遇到的次数最多的两个内存错误信号。内存问题一直是最令我们头疼的事情,弄清楚两个信号的发生缘由对我们很好的理解程序的运行是大有裨益的。

我们来看两段程序:
//testsigsegv.c
int main() {
        char *pc = (char*)0x00001111;
        *pc = 17;
}

//testsigbus.c
int main() {
        int *pi = (int*)0x00001111;
        *pi = 17;
}

上面的代码那么的相似,我们也同样用gcc编译(加上-g选项,便于gdb调试;平台Solaris Sparc),执行结果也都是dump core。但通过GDB对core进行观察,你会发现细微的不同。

第一个例子出的core原因是:Program terminated with signal 11, Segmentation fault.

而第二个例子的core则提示:Program terminated with signal 10, Bus error. 两者有什么不同呢?这两段代码的共同点都是将一个非法地址赋值给指针变量,然后试图写数据到这个地址。

如果要说清楚这个问题,我们就要结合汇编码和一些计算机的体系结构的知识来共同分析了。

先来看testsigsegv.c的汇编码:
... ...
main:
        !#PROLOGUE# 0
        save    %sp, -120, %sp
        !#PROLOGUE# 1
        sethi   %hi(4096), %i0
        or      %i0, 273, %i0
        st      %i0, [%fp-20]
        ld      [%fp-20], %i1
        mov     17, %i0
        stb     %i0, [%i1]
        nop
        ret
        restore
... ...

我们关注的是这句:stb     %i0, [%i1]
从计算机底层的执行角度来说,过程是如何的呢?%i0寄存器里存储的是立即数17,我们要将之存储到寄存器%i1的值指向的内存地址。这一过程对于CPU来说其指挥执行的正常过程是:将寄存器%i0中的值送上数据总线,将寄存器%i1的值送到地址总线,然后使能控制总线上的写信号完成这一向内存写1 byte数据的过程。

我们再看testsigbus.c的汇编码:
... ...
main:
        !#PROLOGUE# 0
        save    %sp, -120, %sp
        !#PROLOGUE# 1
        sethi   %hi(4096), %i0
        or      %i0, 273, %i0
        st      %i0, [%fp-20]
        ld      [%fp-20], %i1
        mov     17, %i0
        st      %i0, [%i1]
        nop
        ret
        restore
... ...

同样最后一句:st      %i0, [%i1],CPU执行的过程与testsigsegv.c中的一致(只是要存储数据长度是4字节),那为什么产生错误的原因不同呢?一个是SIGSEGV,而另一个是SIGBUS。这里涉及到的就是对内存地址的校验的问题了,包括对内存地址是否对齐的校验以及该内存地址是否合法的校验。

注:gdb看汇编代码,

这可以通过disassemble命令或x命令或类似的命令:

http://blog.csdn.net/mergerly/article/details/8538272

[root@localhost test]# gdb ./a.out -q
(gdb) list
1   #include<stdio.h>
2   #include<malloc.h>
3
4   int callee(int a, int b, int c, int d, int e)
5   {
6       return 1;
7   }
8
9   int main(){
10      callee(1,2,3,4,5);
(gdb) disassemble main
Dump of assembler code for function main:
0x0000000000400463 <main+0>:  push   %rbp
0x0000000000400464 <main+1>:  mov    %rsp,%rbp
0x0000000000400467 <main+4>:  mov    $0x5,%r8d
0x000000000040046d <main+10>: mov    $0x4,%ecx
0x0000000000400472 <main+15>: mov    $0x3,%edx
0x0000000000400477 <main+20>: mov    $0x2,%esi
0x000000000040047c <main+25>: mov    $0x1,%edi
0x0000000000400481 <main+30>: callq  0x400448 <callee>
0x0000000000400486 <main+35>: mov    $0x2,%eax
0x000000000040048b <main+40>: leaveq
0x000000000040048c <main+41>: retq
End of assembler dump.
(gdb) x/10i main
0x400463 <main>:  push   %rbp
0x400464 <main+1>:    mov    %rsp,%rbp
0x400467 <main+4>:    mov    $0x5,%r8d
0x40046d <main+10>:   mov    $0x4,%ecx
0x400472 <main+15>:   mov    $0x3,%edx
0x400477 <main+20>:   mov    $0x2,%esi
0x40047c <main+25>:   mov    $0x1,%edi
0x400481 <main+30>:   callq  0x400448 <callee>
0x400486 <main+35>:   mov    $0x2,%eax
0x40048b <main+40>:   leaveq
(gdb)

回到SIGSEGV和SIGBUS

我们假设如果首先进行的内存地址是否合法的校验(是否归属于用户进程的地址空间),那么我们回顾一下,这两个程序中的地址0x00001111显然都不合法,按照这种流程,两个程序都应该是SIGSEGV导致的core才对,但是事实并非如此。那难道是先校验内存地址的对齐?我们再看这种思路是否合理?

testsigsegv.c中,0x00001111这个地址值被赋给了char *pc;也就是告诉CPU通过这个地址我们要存取一个字节的值,对于一个字节长度的数据,无所谓对齐,所以该地址通过对齐校验;

并被放到地址总线上了。而在testsigbus.c里,0x00001111这个地址值被赋给了int *pi;也就是告诉CPU通过这个地址我们要存取一个起码4个字节的值,那么对于长度4个字节的对象,其存放地址起码要被4整除才可以,而0x00001111这个值显然不能满足要求,也就不能通过内存对齐的校验(也就是说地址也要被4整除,否则不对齐)。也就是说SIGBUS这个信号在地址被放到地址总线之后被检查出来的不符合对齐的错误;而SIGSEGV则是在地址已经放到地址总线上后,由后续流程中的某个设施检查出来的内存违法访问错误。

一般我们平时遇到SIGBUS时总是因为地址未对齐导致的,而SIGSEGV则是由于内存地址不合法造成的。

1) SIGBUS(Bus error)意味着指针所对应的地址是有效地址,但总线不能正常使用该指针。通常是未对齐的数据访问所致。
2) SIGSEGV(Segment fault)意味着指针所对应的地址是无效地址,没有物理内存对应该地址。
Linux的mmap(2)手册页
使用映射可能涉及到如下信号
SIGSEGV    试图对只读映射区域进行写操作
SIGBUS     试图访问一块无文件内容对应的内存区域,比如超过文件尾的内存区域,或者以前有文件内容对应,现在为另一进程截断过的内存区域。
调试方法:
gcc -g 编译 
ulimit -c 20000 
之后运行程序,等core dump 
最后gdb -c core <exec file> 

SIGSEGV 和 SIGBUS gdb看汇编相关推荐

  1. GDB调试汇编堆栈过程分析

    GDB调试汇编堆栈过程分析 分析过程 .c代码文件 #include<stdio.h>short addend1 = 1;static int addend2 = 2;const stat ...

  2. 20145223《信息安全系统设计基础》 GDB调试汇编堆栈过程分析

    20145223<信息安全系统设计基础> GDB调试汇编堆栈过程分析 分析的c语言源码 生成汇编代码--命令:gcc -g example.c -o example -m32 进入gdb调 ...

  3. 【汇编优化】之linux下如何利用gdb调试汇编代码

    1.gdb调试汇编代码 (1).假设有如下代码,test.c /*test.c*/ void main() {int a, int b, int c;a = 1;b = 2;add_mmx(a, b, ...

  4. GDB调试汇编堆栈过程的学习

    GDB调试汇编堆栈过程的学习 分析过程 这是我的C源文件: 1.安装32位兼容包 2.使用gcc - g example.c -o example -m32指令在64位的机器上产生32位汇编,然后使用 ...

  5. gdb调试汇编打印寄存器内容和指向的内容

    1 以x265视频编码器中的汇编为例, 计划打印calc_satd_16x8的中间计算过程 编译好x265 ,设置断点. (gdb) b common/x86/pixel-a.asm:13550 No ...

  6. Linux的gcc可以看汇编,linux gcc 内嵌汇编

    通常嵌入到 C 代码中的汇编语句很难做到与其它部分没有任何关系,因此更多时候需要用到完整的内联汇编格式,即汇编模板: __asm__  __volatile__ ("asm statemen ...

  7. gdb学习汇编(三)

    内容来源:   <Debug Hack 中文版>#13   汇编语言(指令简表)   汇编指令对照表   x86汇编指令集大全(带注释 实践   ①创建源文件:vim assemble.c ...

  8. Visual Studio-查看汇编代码

    在程序进入debug的情况下,注意,必须在进入debug的情况下, 按alt+8(注意是8而不是F8),即可打开反汇编界面,或者也可以通过点击 调试->窗口->反汇编,来打开反汇编界面

  9. 安卓开发中遇到的奇奇怪怪的问题(三)

    本文已授权微信公众号:鸿洋(hongyangAndroid)原创首发. 距离上一篇 安卓开发中遇到的奇奇怪怪的问题(二)又过了半年了,转眼也到年底了,是时候拿出点干货了.这篇算是本年度个人印象最深的几 ...

最新文章

  1. 【Qt】使用QPalette设置按钮颜色时,不生效
  2. linux shell mkdosfs 命令用于建立 dos 文件系统
  3. python实例变量初始化_Python – 应该在__init__中初始化所有成员变量
  4. 默认优先级值是多少 ospf_OSPF是什么?网工必备技能——OSPF详解
  5. linux命令echo的实现,Linux echo命令的使用及三种实现方式
  6. Windows系统怎么查看电脑的系统位数?
  7. 【STM32基础】第四篇、控制PWM占空比
  8. zz 超级拖拉机 4.02 破解算法分析
  9. 《爱的五种能力》读书笔记22.02
  10. html5程序阅读题,20 个重要的 HTML5 面试题及答案
  11. Verdi调整字体大小
  12. 技嘉B560M VCCIO2电压设计缺陷
  13. 5款超级好用的效率软件,办公学习都用得上
  14. Apache服务器的启动方法
  15. SQL之一种通用的连续性问题处理方法【重分组算法】--HiveSQL面试题33
  16. python中的步长值是什么意思?
  17. 抖音Dou+到底该怎么投放?dou+投放的最佳时间:国仁网络资讯
  18. 三大电信运营商携号转网数据_携号转网正式上线:三大运营商谁是最大赢家?中国电信偷偷告诉你...
  19. 全新起航:Tizen能否四分移动操作系统的天下
  20. 无产阶级游戏制作人必须明白的哲理(转)

热门文章

  1. html中失焦事件怎么写的,详解HTML onfocus获得焦点和onblur失去焦点事件
  2. android 自动化web,如何在android上使用selenium或appium自动化Chrome浏览器?
  3. 虚拟机访问svn服务器超时_SVN卡顿原因及简单修复方法
  4. laytpl语法_layui语法基础
  5. 田忌赛马贪心算法_田忌赛马 贪心算法
  6. 计算机应用主要设计到哪些方面,大学计算机应用基础教案设计.doc
  7. python yield理解_对Python中Yield的理解
  8. 模拟器抓取https方法
  9. 树莓派 raspberry安全关机命令重启命令
  10. 企业为什么要使用基于Docker的PaaS/CaaS平台