SIGSEGV 和 SIGBUS & gdb看汇编
参考这篇文章:
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
- #include<stdio.h>
- #include<malloc.h>
- int callee(int a, int b, int c, int d, int e)
- {
- return ;
- }
- int main(){
- callee(,,,,);
- (gdb) disassemble main
- Dump of assembler code for function main:
- 0x0000000000400463 <main+>: push %rbp
- 0x0000000000400464 <main+>: mov %rsp,%rbp
- 0x0000000000400467 <main+>: mov $0x5,%r8d
- 0x000000000040046d <main+>: mov $0x4,%ecx
- 0x0000000000400472 <main+>: mov $0x3,%edx
- 0x0000000000400477 <main+>: mov $0x2,%esi
- 0x000000000040047c <main+>: mov $0x1,%edi
- 0x0000000000400481 <main+>: callq 0x400448 <callee>
- 0x0000000000400486 <main+>: mov $0x2,%eax
- 0x000000000040048b <main+>: leaveq
- 0x000000000040048c <main+>: retq
- End of assembler dump.
- (gdb) x/10i main
- 0x400463 <main>: push %rbp
- 0x400464 <main+>: mov %rsp,%rbp
- 0x400467 <main+>: mov $0x5,%r8d
- 0x40046d <main+>: mov $0x4,%ecx
- 0x400472 <main+>: mov $0x3,%edx
- 0x400477 <main+>: mov $0x2,%esi
- 0x40047c <main+>: mov $0x1,%edi
- 0x400481 <main+>: callq 0x400448 <callee>
- 0x400486 <main+>: mov $0x2,%eax
- 0x40048b <main+>: 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则是由于内存地址不合法造成的。
2) SIGSEGV(Segment fault)意味着指针所对应的地址是无效地址,没有物理内存对应该地址。
使用映射可能涉及到如下信号
SIGSEGV 试图对只读映射区域进行写操作
SIGBUS 试图访问一块无文件内容对应的内存区域,比如超过文件尾的内存区域,或者以前有文件内容对应,现在为另一进程截断过的内存区域。
最后gdb -c core <exec file>
SIGSEGV 和 SIGBUS & gdb看汇编的更多相关文章
- GDB调试汇编分析
GDB调试汇编分析 代码 本次实践我参照了许多先做了的同学的博客,有卢肖明,高其,张梓靖同学.代码借用的是卢肖明同学的代码进行调试运行. GCC编译 使用gcc -g gdbtest.c -o gdb ...
- 20145233 GDB调试汇编分析
GDB调试汇编分析 代码 #include<stdio.h> short addend1 = 1; static int addend2 = 2; const static long ad ...
- 20145318 GDB调试汇编堆栈分析
20145318 GDB调试汇编堆栈分析 代码 #include<stdio.h> short addend1 = 1; static int addend2 = 2; const sta ...
- 20145311利用gdb调试汇编代码
利用GDB调试汇编代码 首先编写c语言原代码,我使用的是同学分析过的代码 #include<stdio.h>short addend1 = 1;static int addend2 = 2 ...
- 20145219 gdb调试汇编堆栈分析
20145219 gdb调试汇编堆栈分析 代码gdbdemo.c int g(int x) { return x+19; } int f(int x) { return g(x); } int mai ...
- 20145310 GDB调试汇编堆栈分析
GDB调试汇编堆栈分析 由于老师说要逐条分析汇编代码,所以我学习卢肖明同学的方法,重新写了一篇博客. 代码: #include<stdio.h> short addend1 = 1; st ...
- GDB调试汇编堆栈过程分析
GDB调试汇编堆栈过程分析 分析过程 这是我的C源文件:click here 使用gcc - g example.c -o example -m32指令在64位的机器上产生32位汇编,然后使用gdb ...
- 20145212——GDB调试汇编堆栈过程分析
GDB调试汇编堆栈过程分析 测试代码 #include <stdio.h> short val = 1; int vv = 2; int g(int xxx) { return xxx + ...
- gdb调试汇编堆栈过程的学习
gdb调试汇编堆栈过程的学习 以下为C源文件 使用gcc - g code.c -o code -m32指令在64位的机器上产生32位汇编,然后使用gdb example指令进入gdb调试器: 进入之 ...
随机推荐
- Coding Pages 服务与万网域名的配置
1071220 http://support.huawei.com/learning/NavigationAction!createNavi?navId=MW000001_term1000190292 ...
- CodeDOMProvider 类
CodeDomProvider 可用来创建和检索代码生成器和代码编译器的实例.代码生成器可以生成特定语言的代码,如:C#.Visual Basic.JScript 等,而代码编译器可以将代码文件编译成 ...
- CentOS 7.4 下搭建 Elasticsearch 6.3 搜索群集
上个月 13 号,Elasticsearch 6.3 如约而至,该版本和以往版本相比,新增了很多新功能,其中最令人瞩目的莫过于集成了 X-Pack 模块.而在最新的 X-Pack 中 Elastics ...
- vscode使用教程(web开发)
1.安装 进入官网下载https://code.visualstudio.com/ 一直下一步就好了,中间可以选择把软件安装在哪个目录. 2.常用插件安装 a. 进入扩展视图界面安装/卸载 a1.快捷 ...
- OKHTTP 简单分析
内部使用了OKIO库, 此库中Source表示输入流(相当于InputStream),Sink表示输出流(相当于OutputStream) 特点: ·既支持同步请求,也支持异步请求,同步请求会阻塞当前 ...
- 牛客多校Round 3
Solved:2 rank:306 跑路场..... A.PACM team 简单背包记录路径都写挂 退役算了 #include <bits/stdc++.h> using namespa ...
- Deepin系统关于每次启动终端都要输入source /etc/profile的问题
关于每次启动终端都要输入source /etc/profile的问题 当我在Deepin系统中下载了node以及npm之后,我为了将node导入到系统文件,使用了以下命令sudo gedit ``/e ...
- 用 Systemtap 统计 TCP 连接
转自: https://mp.weixin.qq.com/s?__biz=MzIxMjAzMDA1MQ==&mid=2648946009&idx=1&sn=3a0be2fe4f ...
- numpy.random模块常用函数解析
numpy.random模块中常用函数解析 numpy.random模块官方文档 1. numpy.random.rand(d0, d1, ..., dn)Create an array of the ...
- MySql-了解存储引擎
怎么应对不同版本 在不同的 mysql 版本中,很多特性和语法有可能是不一样的,我们怎么样才能知道当前版本的语法是什么样呢?最好的办法是学会使用 mysql 的帮助. A.按照层次看帮助 例如:mys ...