Notes & Experiments with Linux x64 assembly
Compilation
To compile using nasm
:
nasm -felf64 <fn>.asm && ld <fn>.o -o <fn>
Hello World
A hello world program, which:
- Writes out the message.
- Exits with status code 0.
; Mark _start symbol as global
global _start
section .rodata
message: db "Hello World!", 0x0A
messageLen: equ $-message
section .text
_start:
; Write to stdout message of length messageLen
mov rax, 1 ; write syscall for x64
mov rdi, 1 ; stdout
mov rsi, message
mov rdx, messageLen
syscall
; exit 0
mov rax, 60 ; exit
xor rdi, rdi ; status code 0
syscall
The syscall table for x64: https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md
Conditionals
A small program which prints out whether the number of CLI arguments is even or odd:
- Pop argc into register.
- Bitwise AND the value with 1.
- Compare the result with 0, and jump to the corresponding label.
- Print the relevant message and exit.
; Mark _start symbol as global
global _start
section .rodata
oddMessage: db "argc indicates odd number of arguments", 0x0A
oddMessageLen: equ $-oddMessage
evenMessage: db "argc indicates even number of arguments", 0x0A
evenMessageLen: equ $-evenMessage
section .text
_start:
; Pop top of stack (argc) to r8
pop r8
; Bitwise AND of r8 and 1
mov rax, 1
and rax, r8
; If rax == 0, even number, jump to even
cmp rax, 0
je even
odd:
; Prepare write of odd message
mov rsi, oddMessage
mov rdx, oddMessageLen
jmp end
even:
; Prepare write of even message
mov rsi, evenMessage
mov rdx, evenMessageLen
end:
; Write message
mov rax, 1 ; write
mov rdi, 1 ; stdout
syscall
; exit 0
mov rax, 60 ; exit
xor rdi, rdi ; status code 0
syscall
Iterations
Loops can be achieved through a conditional and some jumps.
Here’s FizzBuzz, compiled with:
nasm -felf64 fizzbuzz.asm && gcc fizzbuzz.o -o fizzbuzz
In main
:
- The loop variable is initialised to 1.
- Evalulate the condition, and jump to
exit
if the loop variable becomes greater than 100. - Call the
fizzbuzz
function (The body of the loop). - Increment the loop variable.
- Jump back to the condition.
global main
extern printf
extern fflush
section .rodata
fizz: db "Fizz"
fizzLen: equ $-fizz
buzz: db "Buzz"
buzzLen: equ $-buzz
nl: db 0x0A
printNum: db "%d", 0x0
section .text
main:
; Initialise loop variable to 1
; Note that r12 is callee-saved
mov r12, 1
condition:
; Break if > 100
cmp r12, 100
jg exit
; Call function
; If register for loop variable is caller-saved,
; need to save value before call
mov rax, r12
call fizzbuzz
; Increment
add r12, 1
; Jump back to condition
jmp condition
exit:
mov rax, 60 ; exit
xor rdi, rdi ; status code 0
syscall
; FizzBuzz function
fizzbuzz:
; copy rax
mov r11, rax
; Divide by 3
xor rdx, rdx
mov r9, 3 ; divisor to r9
mov rax, r11 ; dividend to rax
idiv r9
mov r9, rdx ; remainder to r9
; Divide by 5
xor rdx, rdx
mov r10, 5 ; divisor to r10
mov rax, r11 ; dividend to rax
idiv r10
mov r10, rdx ; remainder to r10
; Flag to indicate no printing
xor r8, r8
; Divisible by 3?
div3:
cmp r9, 0
jne div5
; Print Fizz
or r8, 1 ; set flag
mov rax, 1 ; write
mov rdi, 1 ; stdout
mov rsi, fizz
mov rdx, fizzLen
syscall
; Divisible by 5?
div5:
cmp r10, 0
jne printnum
; Print Buzz
or r8, 1 ; set flag
mov rax, 1 ; write
mov rdi, 1 ; stdout
mov rsi, buzz
mov rdx, buzzLen
syscall
printnum:
; If number is not multiple of 3/5
cmp r8, 0
jne printline
; call printf
lea rdi, [rel printNum]
mov rsi, r11
xor eax, eax
call [rel printf wrt ..got]
; fflush
xor rdi, rdi
call [rel fflush wrt ..got]
; Print '\n'
printline:
mov rax, 1 ; write
mov rdi, 1 ; stdout
mov rsi, nl
mov rdx, 1
syscall
ret