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

标签 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 时,出现错误

Segmentation fault: 11

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

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

我希望打印 Hello World!,但我得到的却是“Segmentation Fault: 11”。

最佳答案

~~你需要在调用_printf之前设置栈帧

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

使用 LLDB 调试二进制文件会得到:

frame #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 整除。

然而,一个更简单的解决方案是 sub rsp, 8,如 Margaret Bloom 所建议的那样

将您的代码更改为:

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 上测试

关于从 NASM 调用 C 函数 _printf 导致段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56991218/

相关文章:

c - 程序收到信号 SIGPIPE,Broken pipe

C - 特定天数是多少小时?

linux - 好的 Linux 汇编语言调试器?

assembly - 如何将 3 个字节(24 位)从内存移动到寄存器?

assembly - x86 128 位原子操作

c - 如何使用 valgrind 查找内存泄漏?

c - 函数和指向指针的指针

memory - 低级编程: How to find data in a memory of another running process?

assembly - 游戏男孩 : Half-carry flag and 16-bit instructions (especially opcode 0xE8)

c - 将生成的程序集重写为 GCC 内联汇编