我在 NASM 中为 Windows 进行汇编编程,我在代码中发现了这一点:
extern _ExitProcess@4
;Rest of code...
; ...
call _ExitProcess@4
@4
winapi库函数的声明和调用是什么意思?
winapi 使用 __stdcall 调用约定。调用者将堆栈上的所有参数从右向左推送,被调用者再次弹出它们以清理堆栈,通常使用RET n
指令。
它是 __cdecl 调用约定的对立面,这是 C 和 C++ 代码中的常见默认值,调用者清理堆栈,通常ADD ESP,n
在 CALL 之后使用指令。__stdcall 的优点是它生成更紧凑的代码,在被调用的函数中只需要一条清理指令,而不是每次调用函数都需要多条清理指令。但有一个很大的缺点:它很危险。
危险潜伏在调用该函数的代码中,该代码已使用过时的函数声明进行编译。例如,通过添加参数更改函数时的典型情况。这结果很糟糕,除了函数试图使用不可用的参数之外,新函数会从堆栈中弹出太多参数。这会使堆栈不平衡,不仅导致被调用者失败,还导致调用者失败。极难诊断。
所以他们对此做了一些事情,他们装饰了函数的名称。首先使用前导 _underscore,就像对 __cdecl 函数所做的那样。并附加@n
, 的值是函数末尾指令n
的操作数。RET
或者换句话说,堆栈上的参数占用的字节数。
这会在出现不匹配时提供链接器诊断,例如生成名称的foo(int)
函数更改。尚未重新编译的调用代码将寻找一个函数。链接器失败,它找不到该符号。灾难避免了。foo(int, int)
_foo@8
_foo@4
C 的名称装饰方案记录在Format of a C Decorated Name中。包含@
字符的修饰名称用于__stdcall
调用约定:
__stdcall
: 前导下划线 (_
) 和尾随符号 (@
) 后跟一个数字,表示参数列表中的字节数
像Dependency Walker这样的工具能够显示修饰和未修饰的名称。
非官方文档可以在这里找到:名称装饰