x86/OSX 中带有弹出/推送的段错误

fai*_*lle 3 macos assembly

我很难理解为什么这个 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"
Run Code Online (Sandbox Code Playgroud)

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

fai*_*lle 5

这个问题的详细回答如下:How to print argv[0] in NASM? 以及Mac 上的 x86 程序集。在 MacOSX 上编程程序集时,它本质上是一个问题。

总结一下:

  • 此段错误是由于堆栈未对齐造成的。
  • 这仅发生在使用 System V 调用约定(包括 MacOSX,但不包括 Linux)的操作系统上,该约定坚持在进行函数调用(例如 to 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"
Run Code Online (Sandbox Code Playgroud)