对于 cdecl,调用者需要清理堆栈。所以在函数调用之后的某个地方必须有一个堆栈操作,从堆栈中删除参数(删除调整堆栈指针)
PUSH 0xABCDEF01
CALL function
ADD ESP, 4 ; sizeof(DWORD) (the parameter)
它不需要直接在通话之后。(我认为 VS08 有时会将返回值检查放在它之前)所以这同样有效
PUSH 0xABCDEF01
CALL function
TEST EAX, EAX
ADD ESP, 4 ; sizeof(DWORD) (the parameter)
; A conditional jump probably
就像这样
PUSH 0xABCDEF01
CALL function
POP EAX
因为 EAX 持有一个 DWORD,因此sizeof(DWORD)
从堆栈中删除了== 4 个字节。(这显然也适用于所有其他通用扩展寄存器)。(例如,对于N
可以使用的参数,这是可能的,N
POP
但我确信没有编译器会这样做,并且对于每N
> 2 它将导致更大的代码,因此在手写汇编中发生也是不现实的。)
不存在最多N
可在调用后检查以确定该堆栈操作是否为清理代码的指令。但是,我认为 5 几乎适用于所有情况(如果您碰巧已经知道此功能是 CDECL,则可以根据需要进行检查)
同样对于返回值不重要但在检查另一个函数的返回值之前调用的函数,可以像这样保存一个变量:
CALL function1
PUSH EAX ; Save EAX
PUSH something
CALL function2
POP EAX ; Remove the something parameter
POP EAX ; Restore the return value of function1
你也可以尝试计算PUSH
但它带有自己的中间指令 - 这真的很常见:
PUSH something
LEA EAX, something_else ; Get a pointer to something_else
PUSH EAX
PUSH more
CALL function
因此,一旦遇到不是PUSH
.
总之,没有完美的解决方案,但我认为两者都可以产生相当可靠的价值;您选择哪个(或者两者都选择?)取决于您,IMO 第一个(搜索堆栈清理)更容易实现。