64-битная документация системного вызова для сборки MacOS

У меня проблемы с поиском хорошей документации для написания 64-битной сборки на MacOS.

64-битный SysV ABI говорит следующее в разделе A.2.1, и этот пост SO цитирует это:

  • Системный вызов выполняется через инструкцию 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 an устанавливает флаг переноса при ошибке вместо возврата -errno в rax
  • rdx регистр забит syscall
  • В Linux все работает как положено

Почему rdx затерт? Почему системный вызов не возвращается -errno? Где я могу найти настоящую документацию?

Единственное место, которое я нашел, где кто-то говорит о флаге переноса ошибок системного вызова, находится здесь

2 ответа

Я использовал это:

# 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 0
  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 0
        syscall

Это завершится с кодом ошибки один, потому что был использован дескриптор 5, если вы попробуете дескриптор 1, то он будет работать, печатая другое сообщение и выходя с 0.

Я не знаю, почему rdx затирается, просто чтобы подтвердить, что он действительно обнуляется через системный вызов «запись». Я проверил состояние каждого регистра:

      global _start
section .text
_start:

mov rax, 0xDEADBEEF; 0xDEADBEEF = 3735928559; 3735928559 mod 256 = 239
mov rbx, 0xDEADBEEF
mov rcx, 0xDEADBEEF
mov rdx, 0xDEADBEEF
mov rsi, 0xDEADBEEF
mov rdi, 0xDEADBEEF
mov rsp, 0xDEADBEEF
mov rbp, 0xDEADBEEF
mov r8, 0xDEADBEEF
mov r9, 0xDEADBEEF
mov r10, 0xDEADBEEF
mov r11, 0xDEADBEEF
mov r12, 0xDEADBEEF
mov r13, 0xDEADBEEF
mov r14, 0xDEADBEEF
mov r15, 0xDEADBEEF

mov rdx, len2           ; size
mov rsi, msg2           ; buf
mov rdi, 1              ; fd
mov rax, 0x2000004      ; write
syscall

mov rdi, rsi            ; CHANGE THIS TO EXAMINE DIFFERENT REGISTERS
mov rax, 0x2000001      ; exit
syscall

section .data
msg_pad db `aaaa\n`     ; to make the buffer not to be page-aligned
msg2 db `bbbbbb\n`      ; because then it's easier to notice whether
len2 equ $-msg2         ; clobbered or not

nasm -f macho64 syscall.asm && ld syscall.o -e _start -static && ./a.out; echo "status: $?"

Результаты, которые я получил:

      clobber list of a "write" syscall

rax     clobbered
rbx     not clobbered
rcx     clobbered
rdx     clobbered <- This is the unexpected case?!
rsi     not clobbered
rdi     not clobbered
rsp     not clobbered
rbp     not clobbered
r8      not clobbered
r9      not clobbered
r10     not clobbered
r11     clobbered
r12     not clobbered
r13     not clobbered
r14     not clobbered
r15     not clobbered

Было бы интересно узнать о других системных вызовах rdxк тому же у меня не было сил пытаться провести тщательное расследование. Но, может быть, просто на всякий случай следует добавить rdx в список засорения всех системных вызовов MacOS с этого момента.

Другие вопросы по тегам