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调试器: 进入之 ...
随机推荐
- python获取主机名和用户名
import socketimport getpassuser_name = getpass.getuser() # 获取当前用户名hostname = socket.gethostname() # ...
- 【转】数据倾斜是多么痛?spark作业/面试/调优必备秘籍
原博文出自于: http://sanwen.net/a/gqkotbo.html 感谢! 来源:数盟 调优概述 有的时候,我们可能会遇到大数据计算中一个最棘手的问题——数据倾斜,此时Spark作业的性 ...
- git add . 的时候报错fatal: Unable to create : …File exists.
报错内容: $ git add . fatal: Unable to create 'E:/project/qbm_cs/.git/index.lock': File exists. Another ...
- C++学习笔记(三)之函数库
1.标准库函数 begin end begin 返回数组首地址 end 返回数组尾地址 2.const 在声明变量时对变量限制为只读,不允许修改 const int i = 5; 单个const作 ...
- Pro ASP.NET Core MVC 第6版 第一章
目录 第一章 ASP.NET Core MVC 的前世今生 ASP.NET Core MVC 是一个微软公司开发的Web应用程序开发框架,它结合了MVC架构的高效性和简洁性,敏捷开发的思想和技术和.N ...
- mongodb Shell 启动
开始运行mongodb 准备 上篇说过,通过brew安装的程序目录在 /usr/local/Cellar下面 下面,我们先看一下 mongodb的可执行程序命令 cd /usr/local/Cella ...
- 导入RPA应该了解的一些知识
上次概要介绍了下RPA的基本知识,这次说一下导入RPA时应该知道的一些知识点. RPA是为了实现用软件自动化代替在各个行业中用人操作电脑办公的这部分业务,即使是在多个应用之间需要共享数据才能完成的业务 ...
- jquery选择器,筛选器,属性,事件 基础
左边栏实例: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UT ...
- C++ 程序的编译
一.编译器都具备集成开发环境(Integrated Developed Environment,IDE) 二.程序源文件命名约定: C++ 的后缀一般是 .cpp .cc .C .cpp .cxx 三 ...
- scp 上传文件自动录入密码
--- 服务器IP地址 des_host=serverIp 服务器存储路径(文件上传后存储指定目录下) des_direc=/home/lk/ 服务器用户密码 des_pass=root_passwo ...