思考人生、专注技术
分类: C/C++
2015-08-27 09:54:12
人总是会犯错,所以程序也就有bug;这里我们就简单说说Linux下几种用户态程序内存相关异常。
首先需要了解的是Linux进程内存空间布局,如图:
1、内核空间3~4G,由于CPU有运行级别限制,用户态程序没有权限修改内核地址空间,我们只需要考虑用户态空间0~3G;
2、0~3G空间,被划分为不同的段,Linux系统在进程刚创建的时候,就已经将“0~128M空间”以及“程序文件.text”段保护起来,用户程序指令无法修改;
3、“用户栈”空间,主要是指程序运行过程中保存函数调用链的地方,如果函数调用过深或是局部变量过大,则会出现栈溢出;另外如果某级函数里面局部变量越界,会造成函数栈空间破坏;
4、“.data”、“.bss”、“heap”等空间,因为我们会操作这些内存空间,比如malloc一块内存,然后进行写操作,一个不小心,可能会写越界,从而覆盖其它内存空间;
通过以上分析,我们可以得出以下几种内存异常:
一:非法地址访问
这种类型的段错误,比较好处理,只要gdb上去看下是哪一行哪个变量,基本就可以解决(需要说明的是如果编译进行了优化,比如“-O2”选项,需要能阅读基本的汇编指令,才可以定位出哪一行)。
二:栈溢出
这里main文件调用一个递归函数,该递归函数里面有个比较大的局部变量,256K大小,我们可以看到递归到第八次栈就溢出,那么基本可以判定该进程栈空间大小约为4M(MAC系统)。
三:全局变量越界
对于全局变量或是堆变量越界,基本上需要将该变量所在地址空间前面一段内存打印出来,查看是哪一块内存越界;对于全局变量,gdb下可以直接翻译出符号,很容易识别一段内存区域属于哪个全局变量;但是堆内存约为是动态分配,如果没有经过封装,将内存申请调用栈记录在内存头上,我们很难知道这块内存在哪里申请的,只能根据内存内容大概猜测一下。
四:栈破坏
首先,我们来看下进程的栈空间布局:
根据不同的CPU体系、不同的编译器及优化级别,形成的栈也有所区别,不过上图大致反映了Linux C/C++进程的栈布局。如图一段代码,如下图:
gdb上去,如图:
找到rbp、rsp两个寄存器值,然后打印栈内容:
从被破坏的栈里面找出残余信息,如图:
1、被破坏的内容是蓝色矩形框住的部分;
2、红色箭头表示的即是函数栈帧的链表,栈最上面存储的是一个指针,指向调用者的栈顶,后面紧接着存储的是调用者的返回地址;
3、绿色矩形框住的就是函数返回地址,通过gdb可以找到对应的函数就是func_c和func_b;
至此就分析出异常发生前的函数调用链。