【问题标题】:CALL and RET don't work as expectedCALL 和 RET 没有按预期工作
【发布时间】:2013-07-07 15:12:02
【问题描述】:

我仍然经常迷失与堆栈相关的操作,在这种情况下,问题是 RET 指令,它弹出 eip 寄存器的错误地址。我使用 NASM,我的代码如下所示:

start:
    call GiveMeAHandler
    call GetCommandLine
    ret

GiveMeAHandler:
    push ebp
    mov ebp, esp
    push edi
    push esi
    push dword -11
    call dword [F_GetStdHandle] ; It executes correctly and returns
    mov [StdHandler], eax ; StdHandler is stored in BSS
    add esp, 4
    pop esi
    pop edi
    pop ebp
    ret ; This returns to some weird address

GetCommandLine:
    ; ...
    ; I don't get here because the function above wrong return

也许我对ebp, edi, esi 的推送和弹出有点夸大了(它们毕竟没有改变),但即使我删除它们,ret 指令仍然返回错误的地址(77AE7094 ) 而不是 0040100A,我在这里调用第二个函数。

【问题讨论】:

    标签: assembly x86 return stack nasm


    【解决方案1】:

    默认情况下,Windows 使用stdcall 调用约定,其中函数参数被推入堆栈(从右到左),被调用者 清理堆栈。换句话说,当GetStdHandle 返回时,堆栈将已经恢复到您的push dword -11 指令之前。尝试删除 add esp, 4 行,看看是否可以解决问题。

    【讨论】:

    • 是的,确实,我可以看到 retn 4 并删除了我自己的堆栈清理。谢谢
    【解决方案2】:

    让我们看看 NASM 为push dword -11 生成了什么:

    6AF5    push byte -0xb
    

    因此 NASM 将您的 dword 替换为 byte(CPU 将提升为 word,因为 word 是您可以将 push 放入堆栈的最小单元) .
    为什么要这样做?嗯,an optimizer 默认启用,它会尝试优化立即数的大小。

    因为你(可以理解)认为你推了一个 dword,然后在 esp 上加了 4,你最终会得到一个不平衡的堆栈。

    要阻止这种情况发生,您可以添加 strict 说明符,如下所示:

    push strict dword -11
    

    或者在组装时使用-O0 选项。

    【讨论】:

    • 来自英特尔软件开发人员手册卷。 2、push 指令:“操作数大小(16、32 或 64 位)决定堆栈指针的递减量(2、4 或 8)。”假设这是 32 位代码,默认操作数大小将为 32 位,因此 CPU 实际上会将其提升为 dword
    • push byte -0xb 只是 dword push 的立即数的紧凑编码。没有 66 操作数大小前缀,因此 32 位的默认操作数大小适用于 32 位模式,无论模式如何,都不是可用的最小大小。无论如何,在这里为双字推送强制使用不太紧凑的编码是没有用的。
    猜你喜欢
    • 1970-01-01
    • 2018-05-28
    • 2016-08-13
    • 2012-12-05
    • 2017-05-22
    • 2019-07-28
    • 2014-05-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多