标签:
http://blog.csdn.net/wishfly/article/details/50220081.栈到底是什么,如何操纵栈的?
2.参数和临时变量是以什么形式在哪存放?
3.如何传递返回值?
测试代码如下(这是一个简单的通过调用函数计算两数之和的程序):
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #include <stdio.h> int add(int a, int b) { int c = 0; c = a + b; return c; } int main(void) { int x = 0; int y = 3; int z = 4; x = add(y, z); return 0; } |
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | add函数 { ;ebp=1000,esp=900 0040D750 push ebp ;把main函数的ebp压栈,ebp=1000,esp=896 0040D751 mov ebp, esp ;得到“新”栈基址,这里的新的意思是每个函数访问属于自己的一块栈区域,其实是相邻的内存区域,或者说栈只有一个 ;ebp=896,esp=896 0040D753 sub esp, 44h ;ebp=896,esp=828 0040D756 push ebx 0040D757 push esi 0040D758 push edi ;ebp=896,esp=816 0040D759 lea edi, [ebp - 44h] 0040D75C mov ecx, 11h 0040D761 mov eax, 0CCCCCCCCh 0040D766 rep stos dword ptr [edi] ;初始化内部变量区 5: int c = 0; 0040D768 mov dword ptr [ebp - 4], 0 ;c放入“新”栈基址 ;因为“新”栈基地址就是“旧”栈顶地址,所以通过ebp访问传过来的参数 ;ebp+8到ebp+c是因为ebp上方的8字节用于存储调用函数的调用地址和“旧”堆栈基地址了 ;ebp-4则位于新栈中 6: c = a + b; 0040D76F mov eax, dword ptr [ebp + 8] 0040D772 add eax, dword ptr [ebp + 0Ch] ;运算结果放入c中 0040D775 mov dword ptr [ebp - 4], eax ;用寄存器eax返回结果 7: return c; 0040D778 mov eax, dword ptr [ebp - 4] ;恢复寄存器的值,ebp=896,esp=828 8: } 0040D77B pop edi 0040D77C pop esi 0040D77D pop ebx ;恢复“旧”栈顶地址,ebp=896,esp=896,此函数堆栈被释放! 0040D77E mov esp, ebp ;恢复“旧”栈基地址,ebp=1000,esp=900,此时恢复到调用前的栈基地址和顶地址 0040D780 pop ebp ;返回调用点,ebp=1000,esp=904,调用点地址被弹出,返回到调用点 0040D781 ret main函数 { ;用栈顶地址作为栈基地址,目的是不和调用前栈空间冲突 ;为了叙述方便假设原 ebp=1004,esp=1004 ;执行完下面两行汇编语句后 ebp=1000,esp=1000 0040D790 push ebp 0040D791 mov ebp, esp ;esp下移,开辟出0x4C字节的空间,这个空间是留给内部参数用的,这个空间的大小随内部变量多少由编译器决定。 ;ebp=1000,esp=1000-0x4C=924 0040D793 sub esp, 4Ch ;保存 ebx,esi,edi的值,ebp=1000,esp=924-12=912 0040D796 push ebx 0040D797 push esi 0040D798 push edi ;把内部参数占用的空间每个字节都初始化为0xCC,这个是为了在DUBUG程序的方便,编译器加入的 ;如果不在DEBUG状态下,这个区域是没有被初始化的,也就是说是随机值。 0040D799 lea edi, [ebp - 4Ch] 0040D79C mov ecx, 13h 0040D7A1 mov eax, 0CCCCCCCCh 0040D7A6 rep stos dword ptr [edi] ;内部变量放入刚才被初始化为0xCC的内部变量区,x占用四字节在地址9996-9999,y,z一次类推 12: int x = 0; 0040D7A8 mov dword ptr [ebp - 4], 0 13: int y = 3; 0040D7AF mov dword ptr [ebp - 8], 3 14: int z = 4; 0040D7B6 mov dword ptr [ebp - 0Ch], 4 ;把参数按照stdcall方式从右到左压栈,ebp=1000,esp=912-8=904,z首先入栈在908-911,y在904-907 15: x = add(y, z); 0040D7BD mov eax, dword ptr [ebp - 0Ch] 0040D7C0 push eax 0040D7C1 mov ecx, dword ptr [ebp - 8] 0040D7C4 push ecx ;把返回下一行代码即 0040D7CA [add esp,8] 的地址压栈 ;转向add函数,ebp=1000,esp=900,看add函数 0040D7C5 call @ILT + 15(_add) (00401014) ;ebp=1000,esp=912,恢复到压入参数前栈基地址和顶地址,这个步骤叫做堆栈修正 0040D7CA add esp, 8 ;返回的变量放到x中 0040D7CD mov dword ptr [ebp - 4], eax 16: return 0; 17: } |
现在来总结开始提出的三个问题
1.栈到底是什么,如何操纵栈的?
栈是操作系统分配给程序运行的一块内存区域,有以下特点:
1.1、改变堆栈用push, pop,用的esp栈顶指针,而读指针则用ebp栈基指针灵活访问
1.2、每当一个函数跳转到另一个函数时,会在上一个函数用到的栈空间下方开辟空间
2.参数和临时变量是以什么形式在哪存放?
2.1、参数放在旧栈的返回地址和旧栈基地址的上方,而临时变量则在新栈的最上方处,变量名会被编译器连接一个地址,程序在被编译成汇编以后,变量名就是虚无了。
3.如何传递返回值?
3.1、传递一个值的情况下,通过eax传递
可以看出,栈溢出是由于编译器没有检查栈是否还有空间。
标签:
原文地址:http://www.cnblogs.com/fengkang1008/p/4732284.html