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