常用32位编程调用规范简介
本节将给出 Windows 环境中两种最常用的 32 位编程调用规范。首先是 C 语言发布的 C 调用规范,该语言用于 Unix 和 Windows。然后是 STDCALL 调用规范,它描述了调用 Windows API 函数的协议。这两种规范都很重要,因为在 C 和 C++ 程序中会调用汇编函数, 同时汇编语言程序也会调用大量的 Windows API 函数。
另一方面,C 调用规范则允许子程序声明不同数量的参数,主调程序可以决定传递多少个参数。C 语言的 printf 函数就是一个例子,它的参数数量取决于初始字符串参数中的格式说明符的个数:
调用 32 位 Windows API 函数时,Irvine32 链接库使用的是 STDCALL 调用规范。Irvine64 链接库使用的是 x64 调用规范。
例如,假设如下过程 MySub 有一个堆栈参数。在 EBP 被设置为堆栈帧基址后,ECX 和 EDX 入栈,然后堆栈参数加载到 EAX:
C 调用规范
C 调用规范用于 C 和 C++ 语言。子程序的参数按逆序入栈,因此,C 程序在调用如下函数时,先将 B 入栈,再将 A 入栈:AddTwo(A, B)
C 调用规范用一种简单的方法解决了清除运行时堆栈的问题:程序调用子程序时,在 CALL 指令的后面紧跟一条语句使堆栈指针(ESP)加上一个数,该数的值即为子程序参数所占堆栈空间的总和。下面的例子在执行 CALL 指令之前,将两个参数(5 和 6)入栈:Example1 PROC push 6 push 5 call AddTwo add esp, 8 ;从堆栈移除参数 ret Example1 ENDP因此,用 C/C++ 编写的程序在从子程序返回后,总是能把参数从堆栈中删除。
STDCALL 调用规范
另一种从堆栈删除参数的常用方法是使用名为 STDCALL 的规范。如下所示的 AddTwo 过程给 RET 指令添加了一个整数参数,这使得程序在返回到调用过程时,ESP 会加上数值 8。这个添加的整数必须与被调用过程参数占用的堆栈空间字节数相等:AddTwo PROC push ebp mov ebp,esp ;堆栈帧基址 mov eax, [ebp + 12 ] ;第二个参数 add eax, [ebp + 8 ] ;第一个参数 pop ebp ret 8 ;清除堆栈 AddTwo ENDP要说明的是,STDCALL 与 C 相似,参数是按逆序入栈的。通过在 RET 指令中添加参数,STDCALL 不仅减少了子程序调用产生的代码量(减少了一条指令),还保证了调用程序永远不会忘记清除堆栈。
另一方面,C 调用规范则允许子程序声明不同数量的参数,主调程序可以决定传递多少个参数。C 语言的 printf 函数就是一个例子,它的参数数量取决于初始字符串参数中的格式说明符的个数:
int x = 5; float y = 3.2; char z = 'Z'; printf("Printing values: %d, %f, %c", xz y, z);C 编译器按逆序将参数入栈,被调用的函数负责确定要传递的实际参数的个数,然后依次访问参数。这种函数实现没有像给 RET 指令添加一个常数那样简便的方法来清除堆栈,因此,这个责任就留给了主调程序。
调用 32 位 Windows API 函数时,Irvine32 链接库使用的是 STDCALL 调用规范。Irvine64 链接库使用的是 x64 调用规范。
保存和恢复寄存器
通常,子程序在修改寄存器之前要将它们的当前值保存到堆栈。这是一个很好的做法,因为可以在子程序返回之前恢复寄存器的原始值。理想情况下,相关寄存器入栈应在设置 EBP 等于 ESP 之后,在为局部变量保留空间之前。这有利于避免修改当前堆栈参数的偏移量。例如,假设如下过程 MySub 有一个堆栈参数。在 EBP 被设置为堆栈帧基址后,ECX 和 EDX 入栈,然后堆栈参数加载到 EAX:
MySub PROC push ebp ;保存基址指针 mov ebp,esp ;堆栈帧基址 push ecx push edx ;保存 EDX mov eax,[ebp+8] ;取堆栈参数 . . pop edx ;恢复被保存的寄存器 pop ecx pop ebp ;恢复基址指针 ret ;清除堆栈 MySub ENDPEBP 被初始化后,在整个过程期间它的值将保持不变。ECX 和 EDX 的入栈不会影响到已入栈参数与 EBP 之间的位移量,因为堆栈的增长位于 EBP 的下方,如下图所示。
所有教程
- socket
- Python基础教程
- C#教程
- MySQL函数
- MySQL
- C语言入门
- C语言专题
- C语言编译器
- C语言编程实例
- GCC编译器
- 数据结构
- C语言项目案例
- C++教程
- OpenCV
- Qt教程
- Unity 3D教程
- UE4
- STL
- Redis
- Android教程
- JavaScript
- PHP
- Mybatis
- Spring Cloud
- Maven
- vi命令
- Spring Boot
- Spring MVC
- Hibernate
- Linux
- Linux命令
- Shell脚本
- Java教程
- 设计模式
- Spring
- Servlet
- Struts2
- Java Swing
- JSP教程
- CSS教程
- TensorFlow
- 区块链
- Go语言教程
- Docker
- 编程笔记
- 资源下载
- 关于我们
- 汇编语言
- 大数据
- 云计算
- VIP视频