【问题标题】:Calling the C-function _printf from NASM causes a Segmentation Fault从 NASM 调用 C 函数 _printf 会导致分段错误
【发布时间】:2019-11-21 07:00:20
【问题描述】:

我一直在尝试使用 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”。

【问题讨论】:

  • 您确定mov rdi, msg 被编码为mov r64, imm64 而不是mov r/m64, imm32?尝试改用mov rdi, qword msg
  • 您可能会尝试在 C 中调用该函数并查看创建的汇编程序输出。
  • @Michael 我换行了,输出没变。
  • @Gerhardh 我之前尝试过这样做,但是除了非常基本的内容之外,我很难理解其中的大部分内容,尤其是因为语法不同。

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


【解决方案1】:

~~你需要在调用_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 整除。

不过,更简单的解决方案是 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 上测试

【讨论】:

  • 您是否在没有“堆栈框架”的情况下对其进行了测试?
  • 是的,我遇到了与问题中描述的相同的错误
  • sub rsp, 8 代替两条指令怎么样?我忘记了 OP 正在使用 CRT,所以执行从 main 开始,它使堆栈与其返回地址不对齐。 mov rbp, rsp 是多余的。
  • @MargaretBloom,你当然是对的。我意识到我的错误并更新了我的答案
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-07-23
  • 2017-10-02
  • 1970-01-01
  • 2015-01-18
  • 2021-05-25
  • 1970-01-01
  • 2020-07-16
相关资源
最近更新 更多