add arm/riscv samples
This commit is contained in:
parent
b652bc257a
commit
05e7c5878f
85
hello-aarch64.s
Normal file
85
hello-aarch64.s
Normal file
|
@ -0,0 +1,85 @@
|
|||
// 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
|
71
hello-arm7l.s
Normal file
71
hello-arm7l.s
Normal file
|
@ -0,0 +1,71 @@
|
|||
// ARM 7l - 32 bit arm. Used on 32 bit hardware or 64 bit hardware with 32 bit
|
||||
// linux (e.g. most raspbian as of 2022)
|
||||
//
|
||||
// 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 hello-arm7l.s && ld.gold -s -n -o hello a.out
|
||||
.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:
|
||||
// Function entry
|
||||
sub sp, sp, #4 // Move stack pointer for locals
|
||||
|
||||
// Function arguments
|
||||
str r0, [sp] // Store return address in stack memory
|
||||
// not specifically necessary, but good safety
|
||||
// mechanism. We could instead simply ignore
|
||||
// the str/ldr operations here and just go
|
||||
// for it
|
||||
//
|
||||
// This would remove memory access completely,
|
||||
// but this allows us to demonstrate the
|
||||
// general pattern we can use for functions
|
||||
|
||||
|
||||
// Function body
|
||||
ldr r0, [sp] // Load first operand with our argument
|
||||
mul r1, r0, r0 // Do the multiplication
|
||||
mov r0, r1 // Move result to return register
|
||||
|
||||
// Function exit
|
||||
add sp, sp, #4 // Restore stack pointer
|
||||
bx lr // Return
|
||||
|
||||
_start:
|
||||
|
||||
// https://man7.org/linux/man-pages/man2/syscall.2.html
|
||||
// Syscall numbers captured from https://syscalls.w3challs.com/?arch=arm_strong
|
||||
// arm syscall table here:
|
||||
// https://github.com/torvalds/linux/blob/v4.19/arch/arm/tools/syscall.tbl
|
||||
# Hello world to stdout
|
||||
mov r7, #4 // System call (sys_write)
|
||||
mov r0, #1 // first argument: file handle (stdout)
|
||||
ldr r1, =msg // second argument: pointer to message to write
|
||||
ldr r2, =len // third argument: message length
|
||||
swi #0 // call kernel. We are using Linux's EABI -
|
||||
// the embedded application binary interface
|
||||
// which is more consistent with the way it is
|
||||
// done in other architectures
|
||||
|
||||
// Square argc
|
||||
ldr r0, [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 r0
|
||||
// r0 will also be the first argument to sys_exit
|
||||
// so no need to load
|
||||
|
||||
// exit
|
||||
mov r7, #1 // system call number (sys_exit)
|
||||
swi #0 // call kernel and exit
|
73
hello-riscv64.s
Normal file
73
hello-riscv64.s
Normal file
|
@ -0,0 +1,73 @@
|
|||
# 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 hello-riscv64.s && ld -s -n -o hello a.out
|
||||
.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.
|
||||
|
||||
# RISC-V register reference:
|
||||
# https://xuanxuanblingbling.github.io/assets/pic/riscv/register.png
|
||||
# RISC-V instruction cheat sheet:
|
||||
# https://risc-v.guru/instructions/
|
||||
|
||||
square:
|
||||
# Function entry
|
||||
addi sp, sp, -32 # Move stack pointer for locals
|
||||
# 32 bytes gives us (32/8 =) 4 64 slots to use
|
||||
# We will only store our return address/frame pointer
|
||||
sd ra, 24(sp) # Store return address in stack memory
|
||||
sd s0, 16(sp) # Store frame pointer in stack memory
|
||||
addi s0, sp, 32 # Capture original stack pointer in s0
|
||||
# Interpret as s0 = sp + 32
|
||||
|
||||
# Function arguments
|
||||
sw a0, -20(s0) # Save first argument (num) to stack
|
||||
# Be aware that we're now subtracting rather
|
||||
# than adding because we just changed s0
|
||||
# A long version of this would be sd a0, -24(s0)
|
||||
|
||||
# Function body
|
||||
lw a0, -20(s0) # Load first argument (num) from stack
|
||||
# This save/load is to make sure we have a copy
|
||||
# of the original argument, which is not important
|
||||
# here, but would be if this was a more serious
|
||||
# function. We could just have easily comment
|
||||
# out both instructions and avoid memory access
|
||||
|
||||
mulw a0, a0, a0 # Actually do the multiplication.
|
||||
|
||||
# Function exit
|
||||
ld ra, 24(sp) # Restore return address from stack
|
||||
ld s0, 16(sp) # Restore frame pointer from stack memory
|
||||
addi sp, sp, 32 # Restore stack pointer
|
||||
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
|
||||
li a7, 64 # System call (sys_write)
|
||||
li a0, 1 # first argument: file handle (stdout)
|
||||
lla a1, msg # second argument: pointer to message to write
|
||||
li a2, len # third argument: message length
|
||||
scall # call kernel
|
||||
|
||||
# Square argc
|
||||
ld a0, (sp) # argc is on the stack
|
||||
# https://www.reddit.com/r/RISCV/comments/p2na17/command_line_arguments_in_assembly/
|
||||
call square # Square our argc, result in a0
|
||||
# a0 will also be the first argument to sys_exit
|
||||
# so no need to load
|
||||
|
||||
# exit
|
||||
li a7, 93 # system call number (sys_exit)
|
||||
scall # call kernel and exit
|
Loading…
Reference in New Issue
Block a user