x86/OSX中的pop/push导致段错误

3

我很难理解为什么这段x86汇编代码可以在OSX上使用gcc 4.2.1(llvm)编译成功,但运行可执行文件时会出现分段错误:

    .globl  _main
_main:
        push    %rbp
        mov     %rsp, %rbp
        mov     $1, %rbx
        push    %rbx
        lea     L_.str0(%rip), %rdi
        mov     %rbx, %rsi
        call    _printf
        pop     %rbx
        pop     %rbp
        ret

        .section        __TEXT,__cstring,cstring_literals
L_.str0:
        .asciz  "%d \000"

我注意到如果将pop %rbx行移动到call _printf之前,程序就可以正常工作。但是为什么它在原始形式下会失败呢?


1
你需要对齐堆栈... - Macmade
@Macmade 是正确的,我相信 - 我编译并运行了你的代码,崩溃日志甚至表明它是堆栈未对齐(不是16字节对齐)。 - Paul R
@PaulR - 谢谢。有没有用指令的方法来做到这一点?还是我必须手动添加代码来对齐? - fairville
可能是如何在NASM中打印argv [0]?的重复问题。 - Macmade
同样的问题...请看我在刚提到的帖子中的答案,学习如何解决这个问题。 - Macmade
最简单的方法是创建一个非常简单的C程序,使用gcc -S -O3 ...编译它,然后使用生成的.s文件作为自己代码的基础。这样你就可以免费获得所有堆栈对齐和其他样板代码。 - Paul R
1个回答

5
这个问题的详细解答可以在以下链接中找到:How to print argv[0] in NASM?x86 Assembly on a Mac。这实际上是在MacOSX上编写汇编程序时的一个陷阱。
总结如下:
  • 这个段错误是由于堆栈未对齐造成的。
  • 这仅发生在使用System V调用约定的操作系统(包括MacOSX,但不包括Linux)上,该约定要求在进行函数调用(例如printf)之前,堆栈指针必须是16的倍数。
一个简单的解决方案是在调用之前对齐堆栈指针(即按照Sys V的要求对齐到16字节的倍数),并在调用后恢复它:
.globl  _main
_main:
        push    %rbp
        mov     %rsp, %rbp
        mov     $1, %rbx
        lea     L_.str0(%rip), %rdi
        mov     %rbx, %rsi
        push    %rbx

    mov %rsp, %rax   ; Save copy of the stack pointer (SP)
    and $-16, %rsp   ; Align the SP to the nearest multiple of 16.
    sub $8, %rsp     ; Pad the SP by 8 bytes so that when we ...  
    push %rax        ; push the saved SP (=8 bytes on a 64-bit OS), 
                     ; we remain aligned to 16 bytes (8+8 = 16).

        call    _printf

    pop %rax         ; retrieve the saved SP
    mov %rax, %rsp   ; restore SP using saved value. 

        pop     %rbx
        pop     %rbp
        ret

        .section        __TEXT,__cstring,cstring_literals
L_.str0:
        .asciz  "%d \000"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接