Arm64 Assembly on Raspberry Pi: AArch64 Tutorial¶
Arm64 assembly on Raspberry Pi uses the AArch64 instruction set when the board runs a 64-bit operating system. This tutorial verifies the environment, builds a working Linux hello-world program with GNU tools, explains registers and system calls, and shows how to single-step the result in GDB.
Quick start
Confirm that uname -m prints aarch64, install build-essential and gdb, then assemble with as and link with ld. If the command prints armv7l, these 64-bit examples will not assemble as written.
This page covers Linux user-space assembly. Bare-metal code uses the same instruction set but has a different entry environment, memory map, exception setup, and output path. For that route, use the Raspberry Pi OS development series after learning the registers and calling convention here.
Learning AArch64 assembly is useful when you want to:
- Direct Hardware Control: Access GPIO, memory-mapped peripherals, and system resources without abstraction layers
- Performance Optimization: Write critical code sections that execute faster than compiler-generated code
- Deeper Understanding: Gain insight into how processors execute instructions, manage memory, and handle function calls
- Embedded Systems Development: Essential knowledge for bare-metal programming and operating system development
- Debugging Skills: Better understand crashes, stack traces, and low-level behavior of your programs
The examples assume basic command-line familiarity. Follow the complete AArch64 and Arm64 learning path for registers, address loading, functions, C++ integration, GPIO, and NEON SIMD.
What is Arm64/AArch64?¶
Arm64 is the common Linux platform name for AArch64, Arm's 64-bit execution state and instruction set. Key differences from 32-bit Arm include:
- 64-bit Registers: 31 general-purpose 64-bit registers (x0-x30) plus special registers
- Larger Address Space: Can address up to 2^64 bytes of memory (16 exabytes)
- Simplified Instruction Set: Removed some legacy 32-bit instructions, added new powerful instructions
- Better Performance: More registers reduce memory access, improved SIMD (NEON) capabilities
- Uniform Instruction Encoding: All instructions are 32-bits wide, simplifying instruction fetch
Development Environment Setup¶
Required Tools¶
Raspberry Pi OS (64-bit) includes all necessary tools for assembly development:
Tool versions vary with the Raspberry Pi OS release. The architecture checks matter more than matching version numbers copied from another system.
Creating Your First Assembly Project¶
Let's create a dedicated directory for assembly projects:
Hello World in Arm64 Assembly¶
Our first program will print "Hello, Arm64!\n" to the console using Linux system calls.
Create a file named hello.s:
Understanding the Code¶
Let's break down each section:
Directives:
- .global _start: Makes _start visible to the linker as the program entry point
- .section .data: Defines the data section for initialized variables
- .section .text: Defines the text section for executable code
Data Section:
- msg: .ascii "Hello, Arm64!\n": Declares a string without null termination
- len = . - msg: Calculates message length (current position minus message start)
Registers Used:
- x0 - x7: Used for passing arguments to system calls and functions
- x8: System call number
- Other registers will be explained in detail in the next tutorial
System Calls:
- write(fd, buffer, count): Syscall 64 writes data to a file descriptor
- x0: File descriptor (1 = stdout)
- x1: Pointer to buffer
- x2: Number of bytes to write
- x8: Syscall number
- svc #0: Supervisor call instruction
exit(status): Syscall 93 terminates the programx0: Exit status codex8: Syscall number
Assembling and Linking¶
Output:
Verifying the Binary¶
You can inspect the generated binary:
Output:
Basic Instructions Overview¶
Here are a few basic instructions to get started (detailed coverage in the next tutorial):
Data Movement¶
Arithmetic Operations¶
Comments¶
A More Complex Example: Sum of Numbers¶
Let's write a program that calculates the sum of numbers from 1 to 10:
Create sum.s:
Build and run:
Output:
This example demonstrates:
- Loop structures with conditional branching (b.lt)
- Function calls using bl (branch with link)
- Comparison using cmp
- Conditional branches for control flow
- More complex register usage
The details of these instructions and control flow will be covered in later tutorials.
Debugging Assembly Programs¶
GDB (GNU Debugger) is invaluable for assembly development:
Useful GDB Commands:
Example Debug Session:
Makefile for Assembly Projects¶
Create a Makefile to simplify building:
Usage:
System Call Reference¶
Here are the most common system calls you'll use in assembly programming:
| Syscall | Number | Arguments | Description |
|---|---|---|---|
read |
63 | x0=fd, x1=buf, x2=count |
Read from file descriptor |
write |
64 | x0=fd, x1=buf, x2=count |
Write to file descriptor |
open |
56 | x0=filename, x1=flags, x2=mode |
Open file |
close |
57 | x0=fd |
Close file descriptor |
exit |
93 | x0=status |
Terminate process |
brk |
214 | x0=addr |
Change data segment size |
For a complete list, see /usr/include/asm-generic/unistd.h or search for "Linux arm64 syscall table".
Next Steps¶
Now that you have your development environment set up and understand basic assembly program structure, the next tutorial will cover:
- Registers in Detail: Complete explanation of all 31 general-purpose registers (x0-x30)
- Special Registers: SP, LR, PC, XZR and their purposes
- Register Naming: Understanding x vs w registers, when to use each
- AAPCS64 Calling Convention: How registers are used for function calls
- SIMD/FP Registers: v0-v31 for floating-point and vector operations
- Data Movement Instructions: mov, movz, movk, movn with immediate encoding
- Arithmetic Operations: add, sub, mul, div, and their variants
- Logical Operations: and, orr, eor, bic, shifts and rotations
- Memory Access Instructions: ldr, str, ldp, stp with addressing modes
Conclusion¶
You've learned how to set up an Arm64 assembly development environment on Raspberry Pi and written your first assembly programs. We covered:
- What Arm64/AArch64 is and why it's important for Raspberry Pi
- Installing and verifying development tools (assembler, linker, debugger)
- Basic program structure with sections (.data, .bss, .text)
- System calls for I/O operations (write, exit)
- Assembly and linking process
- Simple loop and function call examples
- Debugging with GDB
- Building with Makefiles
Assembly language provides unprecedented control over the processor and is essential for understanding computer architecture at a fundamental level. While modern compilers generate excellent code, knowing assembly enables you to optimize critical sections, debug complex issues, and work on systems programming projects.
Continue with the ARM64 register and instruction guide, then use the AArch64 address-loading guide when compiler output introduces ADRP, ADD, or literal loads.