为什么esp后面加的是0x10呢?

psp*_*int 1 x86 assembly stack callstack

我正在阅读维基百科文章,并且无法理解为什么add esp, 0x10下面的代码块末尾有一个。我会放弃我自己的假设,只是问 \xe2\x80\x93 为什么?

\n
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\n
Run Code Online (Sandbox Code Playgroud)\n

Pet*_*des 5

此处毫无意义,因为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 浪费指令。)