【问题标题】:BSD Stack Clean UpBSD 堆栈清理
【发布时间】:2013-03-03 00:38:00
【问题描述】:

我正在学习汇编语言,有一个关于调用约定和堆栈清理的问题。

由于我使用的是 OSX,因此我需要对系统调用使用 BSD 调用约定,如下所示。

SECTION .text
    push StringLen ; Push string length 
    push MyString  ; Push the string
    push 0x1       ; Push the file descriptor (stdout)
    mov eax, 4     ; Push the system call (sys_write)
    int 0x80       ; Call kernel dispatcher
    add esp, 0x10  ; Clean up stack 16 bytes for 4DWORDS

    mov eax, 1
    mov ebx, 0
    int 0x80       ; System exit

我的问题是,add esp, 0x10 是否被认为是清理堆栈的好习惯?我已经尝试过,清理后它会显示这些值仍在堆栈上,但在推送另一个值时会被覆盖,例如

    add esp, 0x10  ; Clean up stack 16 bytes for 4DWORDS
    push 0x1A      ; New push overwrites the previous stack values

我确信这在小程序中并没有太大的不同,但在大程序中,如果它永远不会被覆盖,它不会浪费空间吗?

【问题讨论】:

    标签: assembly stack calling-convention


    【解决方案1】:

    但在一个大的空间中,如果它永远不会被覆盖,它不是浪费空间吗?

    不,这不是浪费空间。堆栈空间的 16 个字节将被一遍又一遍地重用。

    【讨论】:

    • 那么你会说这是清理堆栈的好方法吗?我只是想尽早坚持自己的良好技术。
    • @JamesParker:这可能是最好的方法。您是否有一些您认为可能更好的替代方案?
    • 不,一点也不。我只是想我会和一些有经验的程序员核实一下,而不是把一篇文章当作福音真理。感谢您的帮助。
    【解决方案2】:

    是的,这种传递参数并清理它们的方式很好。但是,您的代码中存在一个问题:您只有 三个 推送但减去了 16 个字节。移动到eax 不算作推送,因为它不会更改esp

    在这个小程序中可能无关紧要,但在任何大小合适的程序中都会崩溃。所以修复 0x10 到 0x0C 添加 push eaxsub esp, 4 以使其平衡并符合 BSD 调用约定的要求。

    另一种方法是预先保留堆栈空间并使用movs 来设置参数而不是pushes。

    EDIT:根据FreeBSD calling convention 固定代码(额外推送)。

    SECTION .text
        ; we only need 12 bytes (3 args) but BSD ABI needs one extra stack slot
        sub esp, 0x10  
        mov  eax, StringLen
        mov  [esp-C], eax
        mov  eax, StringLen
        mov  [esp-8], eax
        mov  dword [esp-4], 0x1 ; file descriptor (stdout)
        mov  eax, 4     ; system call number (sys_write)
        int  0x80       ; make the syscall
    
        mov dword [esp-4], 0 ; exit code
        mov eax, 1      ; system call number (sys_exit)
        int 0x80       ; System exit
    
        ; unreachable in this case but necessary for returning functions
        add esp, 0x10  ; restore the stack at the end of the function
    

    如您所见,它避免了在大部分函数体中更改ESP,但它使代码更大更冗长。由于您不能 mov 内存到内存,因此您必须为变量使用临时寄存器。

    由于某种原因,这种传递参数的方式是 GCC 的默认方式,并且会导致相当大的代码膨胀。

    【讨论】:

    • 有趣。我在我的代码中推送了 EAX,但不小心复制错了,不过感谢您指出。
    • 啊,我没有意识到 BSD 调用约定需要它。根据to the rules 修复代码。正如你所看到的,FreeBSD 指南实际上也使用了推送,所以都是 kosher :)
    猜你喜欢
    • 1970-01-01
    • 2016-03-07
    • 1970-01-01
    • 2010-09-17
    • 1970-01-01
    • 2017-11-03
    • 1970-01-01
    • 1970-01-01
    • 2011-09-19
    相关资源
    最近更新 更多