'How do assembly system calls in FreeBSD on ARM64 machines work?

I would like to port some assembly code from macOS (M1) to FreeBSD on a Raspberry Pi 4. Since they are both based on ARM64 processors, this should not be too difficult in theory, but I cannot find anything on how to properly execute syscalls in FreeBSD on ARM64 architectures.

Here is my sample HelloWorld program that works fine on macOS,

.global _main
.align 4

_main:
    mov     X0, #1          // arg[0] = 1 = STDOUT
    adr     X1, helloworld  // arg[1] = string to print
    mov     X2, #16         // arg[2] = length of our string
    mov     X16, #4         // Unix write system call
    svc     #0x80           // call kernel to output the string

    mov     X0, #0          // use 0 return code
    mov     X16, #1         // Unix exit system call
    svc     #0x80           // call kernel to end program

helloworld:      .ascii  "Hello M1-World!\n"

So far, I found out the following changes I need to make to have it run on FreeBSD:

  • Change _main to _start
  • Change register X16 to X8 (the register that holds the syscall number)

Also, the system call numbers seem to be correct (same as on macOS), judging from the content of /usr/include/sys/syscall.h. So that would make it:

.global _start
.align 4

_start:
    mov     X0, #1          // arg[0] = 1 = STDOUT
    adr     X1, helloworld  // arg[1] = string to print
    mov     X2, #16         // arg[2] = length of our string
    mov     X8, #4          // Unix write system call
    svc     #0x80           // call kernel to output the string

    mov     X0, #0          // use 0 return code
    mov     X8, #1          // Unix exit system call
    svc     #0x80           // call kernel to end program

helloworld:      .ascii  "Hello M1-World!\n"

Then I can assemble and link the program with

cc -g -c hello.asm
ld hello.o -o hello

and running it in the debugger gdb shows that it works until it hits the first svc instruction, when it terminates with

Program received signal SIGILL, Illegal instruction.
Illegal operand.

So my question is, how do I make this work? More precisely,

  1. Is svc the correct instruction for a syscall?
  2. If so, does it matter what argument we pass for svc? (My choice of 0x80 here was arbitrary, as it in fact does not matter on macOS.)
  3. What is the calling convention for FreeBSD ARM64 syscalls? (Here, I assumed it is passing the arguments in register X0, X1, X2,... as this is the conventions on macOS or ARM64 Linux.)

Thanks.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source