3

I am struggling to understand why this x86 assembly code, compiles fine with gcc 4.2.1 (llvm) on OSX, but gives a segmentation fault when the executable is run:

    .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"

I observed that if the pop %rbx line is moved before call _printf, then the program works correctly. But why should it fail at all in its original form?

fairville
  • 81
  • 5
  • 1
    You need to align the stack... – Macmade Nov 12 '14 at 09:34
  • @Macmade is correct I believe - I compiled and ran your code and the crash log even says it's a stack misalignment (not 16 byte aligned). – Paul R Nov 12 '14 at 09:46
  • @PaulR - Thanks. Is there a way to do this with a directive? or do i have to sprinkle code to manually align? – fairville Nov 12 '14 at 09:53
  • possible duplicate of [How to print argv\[0\] in NASM?](http://stackoverflow.com/questions/12678230/how-to-print-argv0-in-nasm) – Macmade Nov 12 '14 at 10:10
  • Same issue... See my answer on the post I just mentioned to learn how to do this. – Macmade Nov 12 '14 at 10:11
  • The easiest thing to do is to create a very simple C program, compile it with `gcc -S -O3 ...`, then use the generated `.s` file as a basis for your own code. That way you get all the stack alignment and other boilerplate code for free. – Paul R Nov 12 '14 at 10:47

1 Answers1

5

This question is answered in detail by: How to print argv[0] in NASM? and also x86 Assembly on a Mac. It's essentially a gotcha when programming assembly on MacOSX.

To summarize:

  • This seg fault is due to a stack misalignment.
  • This occurs only on OSes that use the System V calling convention (including MacOSX, but excluding Linux), which insists that the stack pointer to be a multiple of 16 prior to making a function call (e.g. to printf).

A simple solution is to align the stack pointer (ie, align to a multiple of 16 bytes as per the Sys V requirement) prior to the call, and restore it after the call:

.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"
Community
  • 1
  • 1
fairville
  • 81
  • 5