注册 登录
编程论坛 汇编论坛

有关子程序参数传递和堆栈平衡问题

tigerdown 发布于 2020-09-03 17:44, 5323 次点击
有关子程序参数传递和堆栈平衡问题
发现网上一些资料对这问题解答不十分正确,所以有必要跟大家讨论一下,欢迎提意见。
子程序参数的传递是通过堆栈进行的,就是把子程序的参数压入堆栈,然后子程序在堆栈取出,比如下面例子:

Sub(Var1, Var2)  - StdCall类型

编译后:

push Var2  (待定)
push Var1  (待定)
call Sub

但是参数压入堆栈顺序是如何的呢?是Var2还是Var1先压入?看下面例子和图示:
Sub proc _Var1, _Var2
    mov  eax, _Var1
    mov  ebx, _Var2
    ret
Sub endp

反汇编后:

:00401018 55          push ebp  -  保存ebp指针
:00401019 8BEC        mov  ebp, esp  -  esp指向这里
:0040101B 8B4508      mov  eax, dword ptr[ebp+08] - Var1
:0040101E 8B5D0C      mov  ebx, dword ptr[ebp+0C] - Var2   
:00401021 C9          leave
:00401022 C20800      ret 0008 - 堆栈平衡:esp+0008 = ebp

只有本站会员才能查看附件,请 登录

可以看出Var2地址大于Var1, 所以Var2先入。完成调用后,要清除堆栈空间,由子程序用ret 0008来实现。


[此贴子已经被作者于2020-9-3 17:46编辑过]

6 回复
#2
Valenciax2020-09-03 19:56
stdcall 就是由右到左压栈,退出时由子程式清除堆栈,这些在高阶语言都由编译器处理

其实masm也会这一套,例如

invoke MyProgram,val1,val2,val3

编译器也是用 stdcall方式, 由右到左压栈,而子程序的参数框架,清空堆栈等工作都由汇编器包办

程序代码:


MyProgram proc near data1:WORD, data2:WORD, data3:WORD
    push bp        ;由汇编器填写
    mov bp,sp    ;由汇编器填写
    mov ax,data1    ;子程序代码开始
    mov bx,data2
    ...        
    ...
    ...
    mov sp,bp    ;由汇编器填写
    pop bp        ;由汇编器填写
    ret 0006    ;程式只须写ret, ret 0006由汇编器计算并代补完

MyProgram endp
#3
tigerdown2020-09-03 20:08
回复 2楼 Valenciax
我有一个疑问,有的堆栈是从低地址向高地址生长的(有可能是不同处理器),在这种情况下是不是也是Var2先压入?
#4
Valenciax2020-09-03 20:29
这是win16遗留下来的参数传递方式,有两种 C 和 PASCAL。

C 规定参数传递顺序是从右到左,即最右边的参数最先压栈,由调用者恢复堆栈指针。编译后就是
push xx
push yy
call zzz
add sp,4

PASCAL约定和C约定正好相反,它规定参数是从左向右传递,由被调用者恢复堆栈。也就是ret n 由子程序负责.

STDCALL是C约定和PASCAL约定的混合体,它规定参数的传递是从右到左, 恢复堆栈的工作交由被调用者完成。

Win32只用STDCALL约定,至于其他语言,可能有非STDCALL的方式

不管那种方式,程式師通常都不必关心,因为操作栈的细节都由编译器代劳


#5
tigerdown2020-09-03 21:08
回复 4楼 Valenciax
多谢了,不过对C类型的调用完后,要加上add esp, 8 指令来清除参数空间.
#6
Valenciax2020-09-03 21:14
回复 5楼 tigerdown
要看参数是word的16bit程式还是dword的32bit.再乘上参数个数来决定要如何调整.
#7
tigerdown2020-09-03 21:16
谢了。
1