【发布时间】:2018-09-05 21:51:54
【问题描述】:
我正在阅读IDA Pro Book。在第 86 页讨论调用约定时,作者展示了一个 cdecl 调用约定示例,它消除了调用者从堆栈中清除参数的需要。我正在复制下面的代码sn-p:
; demo_cdecl(1, 2, 3, 4); //programmer calls demo_cdecl
mov [esp+12], 4 ; move parameter z to fourth position on stack
mov [esp+8], 3 ; move parameter y to third position on stack
mov [esp+4], 2 ; move parameter x to second position on stack
mov [esp], 1 ; move parameter w to top of stack
call demo_cdecl ; call the function
作者接着说
在上面的示例中,编译器在函数序言期间为 demo_cdecl 的参数预先分配了存储空间。
我将假设在代码 sn-p 的顶部有一个sub esp, 0x10。否则,您只会破坏堆栈。
他后来说调用 demo_cdecl 完成时调用者不需要调整堆栈。但可以肯定的是,通话后必须有一个add esp, 0x10。
我到底错过了什么?
【问题讨论】:
-
如果调用以
ret 0x10完成,ret指令将调整esp寄存器。所以检查子程序机器代码,它确实使用了哪种ret。编辑:或者如果这是没有子程序代码的书,那么注意调用约定定义,它可能被定义为使用ret imm16来调整子程序中的堆栈。我不记得到底是哪个平台使用了这个(一些窗口?),但我真的不喜欢它,幸运的是在 linux 上调用约定不同,所以我不在乎。 -
调用者仍然需要清除堆栈,只是没有立即完成,而是在函数结尾处。这种风格有效地将参数合并到局部变量中。它是
cdecl的事实意味着调用者清理。被调用者清理称为stdcall或pascal(取决于顺序)。 -
@Ped7g:它是
cdecl,被调用者以正常的ret结尾。那是 32 位Linux 调用约定,可能由gcc 用-maccumulate-outgoing-argsthat defers clearing the stack until the end of the function. 编译,并且避免使用push,即使是在最初的增长。这曾经是一件好事,但现在是now just a waste of code-size and instructions. -
@PeterCordes 感谢您的纠正,我不记得他们的名字的调用约定。实际上再次阅读我的评论,我确实以有点模棱两可的方式写了它,因为我不确定,所以它并不是完全不正确,但仍然很高兴你确实正确地纠正了。
-
请注意,在您的示例中,值是 not 推送的,而是在不更改堆栈指针的情况下移动到堆栈中。这样就不需要分任何东西了
标签: assembly x86 calling-convention cdecl