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:

  1. Writes out the message.
  2. 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:

  1. Pop argc into register.
  2. Bitwise AND the value with 1.
  3. Compare the result with 0, and jump to the corresponding label.
  4. 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:

  1. The loop variable is initialised to 1.
  2. Evalulate the condition, and jump to exit if the loop variable becomes greater than 100.
  3. Call the fizzbuzz function (The body of the loop).
  4. Increment the loop variable.
  5. 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
← Previous Post