assembly-samples/hello-aarch64.s
2022-12-15 23:08:11 -08:00

86 lines
4.1 KiB
ArmAsm
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ARM 8
// gold linker has smallest binary size. others probably can emit smaller
// binaries with custom linker scripts. Their default ones
// are not optimized for hello world programs
// as -march=armv8-a hello-aarch64.s && ld.gold -s -n -o hello a.out
//
// Arm instruction set reference:
// https://developer.arm.com/documentation/100076/0100/A64-Instruction-Set-Reference
//
// AArch64 has thirty-one 64-bit general-purpose registers X0-X30,
// the bottom halves of which are accessible as W0-W30
//
// Most A64 integer instructions can operato on either type of register
.data // section declaration
msg:
.string "All your codebase is belong to us\n" // output string
len = . - msg // length of output string
.text // section declaration
// we must export the entry point to the ELF linker or
.global _start // loader. They conventionally recognize _start as their
// entry point. Use ld e foo to override the default.
square:
// We're using 32 bit register variants here as eventually we will
// return this as the exit syscall, and that syscall will want
// a 32 bit return code
//
// Function entry
sub sp, sp, #16 // Move stack pointer for locals
// Interpret as "sp = sp - 16"
// 16 bytes will take 2 64 bit locals or
// 4 32 bit locals
// Function arguments
str w0, [sp, #12] // Store our first (and only) argument on the
// stack. Not specifically necessary, but good safety
// mechanism. We could instead simply:
// mov w8, w0
// mov w9, w0
// mul w0, w8, w9
// This would remove memory access completely,
// but this allows us to demonstrate the
// general pattern we can use for functions
//
// Here we are using the first "32 bit slot" of the
// area we carved out for local variables
// above
// Function body
ldr w8, [sp, #12] // Load first operand with our argument
ldr w9, [sp, #12] // Load second operand with our argument
mul w0, w8, w9 // Do the multiplication
// Interpret as "w0 = w8 * w9"
// Function exit
add sp, sp, #16 // Restore stack pointer
// Interpret as "sp = sp + 16"
ret // Return
_start:
// https://man7.org/linux/man-pages/man2/syscall.2.html
// https://github.com/torvalds/linux/blob/v4.17/include/uapi/asm-generic/unistd.h
# Hello world to stdout
mov x8, #64 // System call (sys_write)
mov x0, #1 // first argument: file handle (stdout)
ldr x1, =msg // second argument: pointer to message to write
ldr x2, =len // third argument: message length
svc #0 // call kernel
// Square argc
ldr w0, [sp] // argc is on the stack
// this is an eightbyte according to table 3.9
// of the System V AMD64 psABI
// https://gitlab.com/x86-psABIs/x86-64-ABI
bl square // Square our argc, result in w0
// w0 will also be the first argument to sys_exit
// w0 is x0 with top 32 bits zeroed
// so no need to load
// exit
mov w8, #93 // system call number (sys_exit)
svc #0 // call kernel and exit