本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/gumbour/archive/2009/08/30/4500424.aspx
先来看这一个小函数,猜猜他的运行结果(VC6环境)?
#include <stdio.h>
void b()
{
int data[10];
printf("helloworld!/r/n");
data[11]-=5;
}
int main()
{
b();
return 0;
}
堆栈溢出,肯定不正常,马上有人叫起来了。
没错, 那么结果是什么呢,为什么会不停打印helloworld呢,我们将用堆栈揭开他的奥秘。
且看main函数汇编代码。
很简单, L12 调用b函数, L13对返回值赋0.
这里有个很关键的东东: call
call包含2部分操作,call的下一条指令地址入栈,跳转,也就是从效果来说,包含push 0040108D 和 jmp 00401005两条操作。 假如,你打开内存窗口,你会看到,堆栈里已经有0040108D 这个值了。
10: int main()
11: {
...........
12: b();
00401088 call @ILT+0(b) (00401005)
13: return 0;
0040108D xor eax,eax
14: }
再来看函数b
当你把 printf("helloworld!/r/n"); 替换为 printf("%08x!/r/n",data[11]);时,你会发现,程序在不停的打印0040108D!, 显而易见,你修改的data[11]其实就是函数b的返回值地址,而data[11] -= 5;更是巧妙的利用 call 00401005 这条指令正好是5个字节的特点,将返回地址正好修改到了 0040108D ,也就是说函数返回时会再次调用函数b。每次b()都会把返回值改为b返回的地址,导致b()被不停的调用。
为什么data[11]正好是函数的返回值呢,让我们来看堆栈和任务有和关系
任务(线程)都有一个堆栈,任务创建时创建,任务撤销时撤销。 任务的创建本质上包含2点。
1 任务资源的分配(任务TCB和任务堆栈),很多嵌入式操作系统把TCB和堆栈是分配在一起的,比如Vxworks操作系统,其任务ID,堆栈基地址,TCB指针其实指向同一块内存。 创建任务时要指定任务大小,分配堆栈空间其实是一个特殊的malloc函数,他从堆栈空间分配,而不是从系统空间分配内存。任务堆栈windows下默认比较大,嵌入式OS则比较小,经常64k左右。 而局部变量就保存在堆栈中,当访问局部变量越界时,就发生了我们常说的"堆栈被踩了",堆栈被踩得话后果严重,轻则导致某次运行结果不对(这种问题很难定位),重则导致程序崩溃,例如把上面程序改为data[11]-=4,则程序直接崩溃。
2 任务的初始化,包含2部分,任务TCB的初始化,并且把TCB和操作系统关联。
TCB中包含任务的很多东西, 比如任务拥有的信号量的链表,文件描述符的链表,CPU寄存器的值(任务切换时用的),任务优先级,堆栈地址,任务名称等等,这些都需要初始化。初始化完成之后,操作系统会把这个任务TCB假如调度队列,如果加入调度队列时任务状态是就绪,那么当他拿到CPU时就可以直接运行了。
堆栈中包含任务的栈帧,也就是说在函数调用链(A call B,B call C,C call D,D call E),那么堆栈中,ABCDE函数分别对应自己的一段栈帧。以E为例 E的栈帧包含A函数的传入参数,函数返回值,局部变量和临时保存的寄存器值。
函数栈帧在主调函数和被掉函数中分配,在函数返回时释放,这就是为什么局部变量地址在函数返回后其值可能失效。
例如 下面代码FuncB分配的函数栈帧在FuncB执行完后又被分配给FuncC,FuncC中很可能会踩到FuncB曾经的局部变量。
FuncA{
FuncB();
FuncC();
}
任务(线程)的栈以及上面函数b的栈为下图。
*debug版本的函数b其实除了data[10],还在局部变量位置分配了一部分内存用来做调试,不过我们不用关系他。
*为什么是data[11],而不是data[10]/data[12]或者其他? x86下编译器函数入口一般会有2条指令。
push ebp
move ebp,esp
其实就是将ebp作为帧指针来用(函数帧即为栈中一个函数所拥有的一段内存)。
而这样就可以在函数中采用ebp-XXX表示局部变量,用ebp+XXX来表示传入参数。 函数中经常会有一些push操作,
采用esp对局部变量和参数寻址远不如用ebp来的省事了,因为esp是经常变化的,而ebp是相对横的的。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/gumbour/archive/2009/08/30/4500424.aspx