从NASM调用C函数_printf会导致分段错误

时间:2019-07-11 14:11:12

标签: c assembly x86-64 nasm mach-o

我一直试图使用NASM在Mac-OS和Windows上学习64位汇编。

我的代码是

extern _printf

section .data
    msg db "Hello World!", 10, 0

section .text
    global _main

_main:
    mov rax, 0
    mov rdi, msg
    call _printf

    mov rax, 0x2000001
    mov rdi, 0
    syscall

然后我用

进行编译
nasm -f macho64 -o main.o main.asm 
gcc -o main main.o

尝试致电_printf时出现错误

  

分段错误:11

当我删除对_printf的调用时,我的代码运行良好。 为什么调用_printf会导致分段错误?

我已经找到了ABI调用约定here,但没有成功调用C函数的运气。

我希望可以打印Hello World!,但是我会得到'Segmentation Fault:11'。

1 个答案:

答案 0 :(得分:3)

~~您需要在调用_printf

之前设置堆栈帧

TL; DR::System V AMD64 ABI要求堆栈指针是16字节对齐的。在调用_printf时,堆栈指针未对齐8个字节。

使用LLDB调试二进制文件可获得:

  

帧#0:0x00007fff527d430a libdyld.dylib`stack_not_16_byte_aligned_error

MacOS使用System V AMD64 ABI,因此堆栈指针(see this question)依赖16字节对齐,简而言之,这意味着堆栈指针(rsp)应该始终可分调用函数时乘以16。

在调用_printf时,堆栈指针(rsp)未对齐8个字节。这是怎么来的?

我在this page上找到了答案,调用_main函数将返回地址(8字节)压入堆栈,因此未对齐。

我最初的想法-设置堆栈框架-将另一个地址压入堆栈,因此rsp再次被16整除。

但是,Margaret Bloom提出的更简单的解决方案就是sub rsp, 8

将代码更改为:

extern _printf

section .data
    msg: db "Hello World!", 10, 0

section .text
    global _main

_main:
    ;; Fix the stack alignment
    sub rsp, 8
    mov rax, 0
    mov rdi, msg
    call _printf

    mov rax, 0x2000001
    mov rdi, 0
    syscall

在macOS 10.13.6上测试