# 7. ARM64 Assembly System Call Tutorial ## Overview This tutorial demonstrates how to write a simple ARM64 (AArch64) assembly program that uses system calls to print "Hello World!" to the console. ## What is a System Call? A system call (syscall) is a way for user programs to request services from the operating system kernel. On ARM64 Linux, system calls are invoked using the `svc` (Supervisor Call) instruction. ## Complete Source Code ### hello.S ```assembly // hello.S - ARM64 Assembly Hello World using system calls // This program demonstrates the use of the SVC (Supervisor Call) instruction // to make system calls on AArch64 Linux .global _start // Make _start visible to the linker .section .data msg: .ascii "Hello World!\n" msg_len = . - msg // Calculate message length .section .text _start: // System call: write(fd, buffer, count) // On AArch64 Linux: // - System call number goes in x8 // - Arguments go in x0, x1, x2, x3, x4, x5 // - syscall number for write is 64 mov x0, #1 // x0 = file descriptor (1 = stdout) ldr x1, =msg // x1 = pointer to message buffer mov x2, #msg_len // x2 = message length mov x8, #64 // x8 = syscall number for write (64 on AArch64) svc #0 // Supervisor call - trigger system call // System call: exit(status) // syscall number for exit is 93 mov x0, #0 // x0 = exit status (0 = success) mov x8, #93 // x8 = syscall number for exit (93 on AArch64) svc #0 // Supervisor call - trigger system call ``` ### Makefile ```makefile # Makefile for ARM64 Assembly Programs # This Makefile compiles and links AArch64 assembly programs # Assembler and Linker AS = as LD = ld # Flags ASFLAGS = LDFLAGS = # Target executable TARGET = hello # Source file SRC = hello.S # Object file OBJ = hello.o # Default target - build the executable all: $(TARGET) # Link object file to create executable $(TARGET): $(OBJ) @echo "Linking $(OBJ) to create $(TARGET)..." $(LD) $(LDFLAGS) -o $(TARGET) $(OBJ) @echo "Build successful! Run with: ./$(TARGET)" # Assemble source file to create object file $(OBJ): $(SRC) @echo "Assembling $(SRC)..." $(AS) $(ASFLAGS) -o $(OBJ) $(SRC) # Run the program run: $(TARGET) @echo "Running $(TARGET)..." @./$(TARGET) @echo "Exit code: $$?" # Clean up generated files clean: @echo "Cleaning up..." rm -f $(OBJ) $(TARGET) @echo "Clean complete." # Phony targets (not actual files) .PHONY: all run clean help # Help target help: @echo "ARM64 Assembly Makefile" @echo "=======================" @echo "Available targets:" @echo " make - Build the executable (default)" @echo " make all - Build the executable" @echo " make run - Build and run the program" @echo " make clean - Remove generated files" @echo " make help - Show this help message" @echo "" @echo "Files:" @echo " Source: $(SRC)" @echo " Object: $(OBJ)" @echo " Target: $(TARGET)" ``` ## ARM64 System Call Convention ### User Mode vs Kernel Mode ```mermaid flowchart LR subgraph USER["USER MODE (EL0)"] B[Setup Registers<br/>x8=64 x0=1 x1=msg x2=13] end B -->|svc #0| SVC[SVC Instruction<br/>Privilege Switch] subgraph KERNEL["KERNEL MODE (EL1)"] C[Exception Handler] D[System Call Table<br/>syscall_table 64] E[sys_write Function] F[Return x0] C --> D --> E --> F end SVC -->|EL0 to EL1| C F -->|EL1 to EL0| B ``` ### Calling Convention on AArch64 Linux: - **x8**: System call number - **x0-x5**: Arguments (up to 6 arguments) - **x0**: Return value after the syscall ### The SVC Instruction: ```assembly svc #0 ``` The `svc` (Supervisor Call) instruction triggers a synchronous exception, transferring control to the kernel. The immediate value (#0) is ignored on Linux but required by the instruction format. ## Important AArch64 Linux System Call Numbers | System Call | Number | Arguments | |-------------|--------|-----------| | read | 63 | (fd, buf, count) | | write | 64 | (fd, buf, count) | | exit | 93 | (status) | | openat | 56 | (dirfd, pathname, flags, mode) | | close | 57 | (fd) | **Note**: You can find the syscall number in Linux source code [unistd.h](https://elixir.bootlin.com/linux/v6.18.6/source/include/uapi/asm-generic/unistd.h#L174) ## The Hello World Program ### Program Structure: ```assembly .global _start // Entry point for the linker .section .data msg: .ascii "Hello World!\n" msg_len = . - msg // Calculate length at assembly time .section .text _start: // write(1, msg, msg_len) mov x0, #1 // stdout file descriptor ldr x1, =msg // address of message mov x2, #msg_len // length of message mov x8, #64 // syscall number for write svc #0 // invoke system call // exit(0) mov x0, #0 // exit status mov x8, #93 // syscall number for exit svc #0 // invoke system call ``` ## How to Compile and Run ### Using the Makefile (Recommended): ```bash # Build the program make # Build and run make run # Clean up generated files make clean # Show help make help ``` ### Manual Compilation (Alternative): ```bash # Assemble the program as -o hello.o hello.S # Link the object file ld -o hello hello.o # Run the program ./hello ``` ### Expected Output: ``` Hello World! ``` ## Detailed Explanation ### 1. Data Section ```assembly .section .data msg: .ascii "Hello World!\n" msg_len = . - msg ``` - Defines a read-only data section - `msg:` is a label pointing to the string - `.ascii` directive stores the string in memory - `msg_len = . - msg` calculates the length (current position minus msg start) ### 2. Write System Call ```assembly mov x0, #1 // File descriptor: 1 = stdout ldr x1, =msg // Load address of message into x1 mov x2, #msg_len // Number of bytes to write mov x8, #64 // System call number for write svc #0 // Execute system call ``` ### 3. Exit System Call ```assembly mov x0, #0 // Exit status: 0 = success mov x8, #93 // System call number for exit svc #0 // Execute system call ``` ## Key Differences from x86-64 | Feature | x86-64 | ARM64 (AArch64) | |---------|--------|-----------------| | Syscall instruction | `syscall` | `svc #0` | | Syscall number register | rax | x8 | | Argument registers | rdi, rsi, rdx, r10, r8, r9 | x0, x1, x2, x3, x4, x5 | | write syscall number | 1 | 64 | | exit syscall number | 60 | 93 | ## Additional Notes - ARM64 uses 64-bit registers (x0-x30) and 32-bit registers (w0-w30, lower 32 bits of x registers) - The `svc` instruction was called `swi` (Software Interrupt) in older ARM architectures - Always remember to call exit, or the program will crash when it runs past the end - The immediate value in `svc #0` can be 0-65535, but Linux ignores it ## References - ARM Architecture Reference Manual - Linux System Call Table for AArch64: https://github.com/torvalds/linux/blob/master/arch/arm64/include/asm/unistd.h - AArch64 Procedure Call Standard (AAPCS64)