psp*_*int 1 x86 assembly stack callstack
我正在阅读维基百科文章,并且无法理解为什么add esp, 0x10下面的代码块末尾有一个。我会放弃我自己的假设,只是问 \xe2\x80\x93 为什么?
printnums:\n ; stack setup\n push ebp\n mov ebp, esp\n sub esp, 0x08\n mov [ebp-0x04], ecx ; in x86, ecx = first argument.\n mov [ebp-0x08], edx ; arg2\n push [ebp+0x08] ; arg3 is pushed to stack.\n push [ebp-0x08] ; arg2 is pushed\n push [ebp-0x04] ; arg1 is pushed\n push 0x8065d67 ; "The numbers you sent are %d %d %d"\n call printf\n ; stack cleanup\n add esp, 0x10\n nop\n leave\n retn 0x04\nRun Code Online (Sandbox Code Playgroud)\n
此处毫无意义,因为leave无论当前指向何处都会恢复 ESP。
正如维基百科所说,这是对上面显示的 C 函数的 GCC 输出的反汇编
( https://en.wikipedia.org/wiki/X86_calling_conventions#Microsoft_fastcall )。我们可以看出__attribute__((fastcall))它不是 MSVC,并且nop看起来leave像是未优化的 GCC 输出。
这就是为什么它将 2 个传入寄存器参数存储到堆栈空间,然后再使用push.
函数调用语句本身的代码块以弹出实际传递给 printf (是 cdecl,而不是 fastcall)的 4 个双字参数结束。即 0x10 = 16 字节。
sub esp, 8它不会弹出为本地变量保留的空间;剩下的就是leave.
您可以在 Godbolt 编译器资源管理器( https://godbolt.org/z/vdM4cxM7q )上看到相同的内容,并且通过查看编译器 asm 输出(而不是反汇编),您可以获得符号名称而不是数字地址,例如0x8065d67。更重要的是,它将用颜色突出显示 C 源代码行,以将它们与汇编代码行相匹配。
(实际gcc -O0 -m32输出使用sub esp, 24,而不是 8。也许维基百科输出来自更早的 GCC 版本。或者它来自针对 Windows 或 *BSD 的 GCC 版本,其中 16 字节堆栈对齐不是 32 位代码的要求;它似乎浪费空间的错误消失了-mpreferred-stack-boundary=2,所以 GCC5.4 https://godbolt.org/z/KWzK6zdrj准确地重现了 asm 输出。GCC 4.9 及更早版本不会用 NOP 浪费指令。)