【问题标题】:64-bit syscall documentation for MacOS assemblyMacOS 程序集的 64 位系统调用文档
【发布时间】:2020-04-22 02:05:43
【问题描述】:

我很难找到在 MacOS 上编写 64 位程序集的好文档。

64-bit SysV ABI 在 A.2.1 节中说了以下内容,this SO post 引用了它:

  • 系统调用是通过 syscall 指令完成的。内核破坏 注册 %rcx 和 %r11。

  • 从系统调用返回,寄存器 %rax 包含 系统调用。 -4095 和 -1 之间的值表示错误, 它是-errno。

这两句话在 Linux 上是可以的,但在 macOS Sierra 上是错误的,代码如下:

global _start
extern _exit

section .text
_start:

; Align stack to 16 bytes for libc
and rsp, 0xFFFFFFFFFFFFFFF0

; Call write
mov rdx, 12             ; size
mov rsi, hello          ; buf
mov edi, 1              ; fd
mov rax, 0x2000004      ; write ; replace to mov rax, 0x1 on linux
syscall

jc .err                 ; Jumps on error on macOS, but why?
jnc .ok

.err:
mov rdi, -1
call _exit              ; exit(-1)

.ok:
; Expect rdx to be 12, but it isn't on macOS!
mov rdi, rdx
call _exit              ; exit(rdx)

; String for write
section .data
hello:
.str db `Hello world\n`
.len equ $-hello.str

使用 NASM 编译:

; MacOS: nasm -f macho64 syscall.asm && ld syscall.o -lc -macosx_version_min 10.12 -e _start -o syscall
; Linux: nasm -f elf64 syscall.asm -o syscall.o && ld syscall.o -lc -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o syscall

在 macOS 上运行:

./syscall      # Return value 0
./syscall >&-  # Return value 255 (-1)

我发现:

  • 系统调用返回errno 并设置错误时的进位标志,而不是在rax 中返回-errno
  • rdx 寄存器被 syscall 破坏
  • 在 Linux 上,一切正常

为什么rdx 被破坏了?为什么系统调用不返回-errno?我在哪里可以找到真正的文档?

我发现有人谈论系统调用错误的进位标志的唯一地方是here

【问题讨论】:

  • 因为这是标题为A.2 AMD64 Linux Kernel Conventions的部分的摘录?
  • 好问题。我对您链接到的the SO post 进行了快速编辑,因此它不再声称该部分也适用于 *BSD(它之前没有提到 OS X,达尔文算作 *BSD 吗?@Jean-BaptisteYunès,你呢?碰巧知道 FreeBSD 或 OpenBSD 在 x86-64 上是否使用与 Linux 或 OS X 相同的约定?)
  • @PeterCordes 唉,似乎不同的 BSD 风格不使用相同的 ABI。 FreeBSD 似乎与 Linux 兼容。 Minix 和 NetBSD 是兼容的。很难找到有关它的信息(多年来我没有读过这些东西,所以可能我只是不知道在哪里搜索)。还有一个unix.stackexchange.com/questions/3322/…

标签: macos assembly x86-64 system-calls abi


【解决方案1】:

我用过这个:

# as hello.asm -o hello.o
# ld hello.o -macosx_version_min 10.13 -e _main -o hello  -lSystem
.section __DATA,__data
str:
  .asciz "Hello world!\n"

.section __TEXT,__text
.globl _main
_main:
  movl $0x2000004, %eax           # preparing system call 4
  movl $1, %edi                   # STDOUT file descriptor is 1
  movq str@GOTPCREL(%rip), %rsi   # The value to print
  movq $13, %rdx                  # the size of the value to print
  syscall

  movl %eax, %edi
  movl $0x2000001, %eax           # exit (return value of the call to write())
  syscall

并且能够将返回值捕获到eax。这里的返回值是write系统调用实际写入的字节数。是的,MacOS 是一个 BSD 变体,它是一个进位标志,它告诉你系统调用是否错误(errno 只是一个外部链接变量)。

# hello_asm.s
# as hello_asm.s -o hello_asm.o
# ld hello_asm.o -e _main -o hello_asm
.section __DATA,__data
str:
        .asciz "Hello world!\n"
good:
        .asciz "OK\n"

.section __TEXT,__text
.globl _main
_main:
        movl $0x2000004, %eax           # preparing system call 4
        movl $5, %edi                   # STDOUT file descriptor is 5
        movq str@GOTPCREL(%rip), %rsi   # The value to print
        movq $13, %rdx                  # the size of the value to print
        syscall

        jc err

        movl $0x2000004, %eax           # preparing system call 4
        movl $1, %edi                   # STDOUT file descriptor is 1
        movq good@GOTPCREL(%rip), %rsi  # The value to print
        movq $3, %rdx                   # the size of the value to print
        syscall
        movl $0, %edi
        movl $0x2000001, %eax           # exit 0
        syscall
err:    
        movl $1, %edi
        movl $0x2000001, %eax           # exit 1
        syscall

由于使用了描述符 5,这将退出并显示错误代码 1,如果您尝试使用描述符 1,那么它将打印另一条消息并以 0 退出。

【讨论】:

  • 您的最后一条评论应该是“exit 1”,因为您将 $1 移动到 %edi。
  • movq str@GOTPCREL(%rip), %rsi 是荒谬的。只需使用相对于 RIP 的 LEA 即可获取您自己的静态数据的地址!而且您对exit 0 的评论是错误的:您实际上传递了write() 的返回值,这要么是错误(例如,如果标准输出已关闭)或13(写入的字节数)。您也不需要 0 终止数据,因为您仅将其与显式长度函数一起使用。然后您可以让汇编器为您计算长度,而不必硬编码13。见Hello World in x86 assembler on Mac 0SX
  • 是否有任何官方或广泛认可的 MacOS 系统调用约定文档,包括进位标志的使用?搜索的时候找不到任何权威的东西,只有很多“民间传说”。
猜你喜欢
  • 2018-07-28
  • 1970-01-01
  • 2018-06-06
  • 2023-03-20
相关资源
最近更新 更多