首页
社区
课程
招聘
[求助]关于堆栈平衡的一点疑惑
发表于: 2008-4-26 10:55 6996

[求助]关于堆栈平衡的一点疑惑

2008-4-26 10:55
6996
在书上看到如下内容

; 这里是Sub1 - C类型

:00401000 55                    push ebp

:00401001 8BEC                  mov ebp, esp

:00401003 8B4508                    mov eax, dword ptr [ebp+08]

:00401006 8B5D0C                mov ebx, dword ptr [ebp+0C]

:00401009 C9                    leave

:0040100A C3                    ret

; 这里是Sub2 - PASCAL类型

:0040100B 55                    push ebp

:0040100C 8BEC                  mov ebp, esp

:0040100E 8B450C                mov eax, dword ptr [ebp+0C]

:00401011 8B5D08                mov ebx, dword ptr [ebp+08]

:00401014 C9                    leave

:00401015 C20800                ret 0008

; 这里是Sub3 — StdCall类型

:00401018 55                    push ebp

:00401019 8BEC                  mov ebp, esp

:0040101B 8B4508                mov eax, dword ptr [ebp+08]

:0040101E 8B5D0C                mov ebx, dword ptr [ebp+0C]

:00401021 C9                    leave

:00401022 C20800                ret 0008

        …

; 这里是invoke Sub1,1,2 — C类型

:00401025 6A02                  push 00000002

:00401027 6A01                  push 00000001

:00401029 E8D2FFFFFF            call 00401000

:0040102E 83C408                add esp, 00000008

; 这里是invoke Sub2,1,2 — PASCAL类型

:00401031 6A01                  push 00000001

:00401033 6A02                  push 00000002

:00401035 E8D1FFFFFF            call 0040100B

; 这里是invoke Sub3,1,2 — StdCall类型

:0040103A 6A02                  push 00000002

:0040103C 6A01                  push 00000001

:0040103E E8D5FFFFFF            call 00401018

可以清楚地看到,在参数入栈顺序上,C类型和StdCall类型是先把右边的参数先压入堆栈,而PASCAL类型是先把左边的参数压入堆栈。在堆栈平衡上,C类型是在调用者在使用call指令完成后,自行用add esp,8指令把8个字节的参数空间清除,而PASCAL和StdCall的调用者则不管这个事情,堆栈平衡的事情是由子程序用ret 8来实现的(ret指令后面加一个操作数表示在ret后把堆栈指针esp加上操作数)。

我有一点疑惑就是,调整堆栈使其平衡不就是为了让子程序能够正常的返回吗?那C类型在call之后才调整堆栈,子程序是如何正常返回到call的地方的呢?

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (7)
雪    币: 208
活跃值: (40)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
2
子程序只需要正常返回到返回地址而已,其他的和它无关啊
2008-4-26 11:38
0
雪    币: 437
活跃值: (273)
能力值: ( LV12,RANK:240 )
在线值:
发帖
回帖
粉丝
3
call 时候 push 了子程序要返回的地址,  到ret的时候, 返回到当前ESP指向的地址。
    ret相当与 转到ESP指向的地址并add 下ESP(4字节) ,   
    ret8 想当于 转到ESP指向的地址并add 下ESP(12字节)。

你自己写个测试程序,然后OD 下 答案就立刻出来了, 我这里随便说下,可能有错误
2008-4-26 11:41
0
雪    币: 221
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
4
call和ret指令我当然懂
我的意思是,没有在子程序中调整好堆栈的值,栈顶就不是call时压入的地址,ret的时候pop出来的地址不对,应该出错啊,怎么正常返回的呢
2008-4-26 17:23
0
雪    币: 2384
活跃值: (766)
能力值: (RANK:410 )
在线值:
发帖
回帖
粉丝
5
  我猜想你是将栈保存的数据理解错误,你认为函数的参数是指向栈顶的,而函数返回地址是指向参数下面的,不将参数修正就无法令函数返回地址保持在栈顶吧?如果是这样,你的看法就错了,
  不管是stdcall还是pascal以及cdecl调用过程,函数在返回时esp栈顶都指向函数返回地址,而参数则保存在函数返回地址的后面esp+4、esp+n等位置才对,所以不管是stdcall还是pascal以及cdecl的调用过程,返回过程时栈顶都是一样环境的,只是stdcall和pascal调用约定会在先返回后再自动修正esp的值,而cdecl调用约定则是程序返回后必须手动修正esp的值。
2008-4-26 17:38
0
雪    币: 485
活跃值: (12)
能力值: ( LV9,RANK:490 )
在线值:
发帖
回帖
粉丝
6
平衡堆栈是调用前后的堆栈平衡,不是为了平衡子程序中的堆栈的。
调用一个程序,要通过一定手段使得调用前后的堆栈是不变的,否则没人敢调用这个子程序
调用这样的子程序死的难看的调用者,而不是被调用的子程序。

个人意见,仅供参考
2008-4-26 17:44
0
雪    币: 221
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
7
谢谢上面两位的解答,原来我看书的时候看掉了很重要的一句,所以导致我的理解错误
就是下面这一句
(ret指令后面加一个操作数表示在ret后把堆栈指针esp加上操作数)
我一直以为ret 8就相当于add esp,8 再ret,顺序反了,esp的值被改变之后,pop出来的值就不是开始栈顶那个call时压入的地址了,如果是ret然后在add esp,8
这样我就能想通了,谢谢大家了
2008-4-26 18:01
0
雪    币: 215
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
cdecl调用类型的子函数由调用者自己平衡堆栈!
其他的在子函数内部平衡堆栈
2008-4-27 15:58
0
游客
登录 | 注册 方可回帖
返回
//