Introduction
x86-64 assembly is the programming language for the 64-bit version of the x86 instruction set. It is based on the original 8086 instruction set from 1978.
This post is intended as a sort of cheat-sheet, assembled while working through the x86-64 Assembly track on Exercism.
Registers
There are 16 general purpose registers, each of which can be addressed in full, or by the lower 32, 16, and 8 bits:
64-bit | 32-bit | 16-bit | 8-bit | Special Purpose for functions | When calling a function | When writing a function |
---|---|---|---|---|---|---|
rax | eax | ax | ah , al | Return Value | Might be changed | Use freely |
rbx | ebx | bx | bh , bl | Will not be changed | Save before using! | |
rcx | ecx | cx | ch , cl | 4th integer argument | Might be changed | Use freely |
rdx | edx | dx | dh , dl | 3rd integer argument | Might be changed | Use freely |
rsi | esi | si | sil | 2nd integer argument | Might be changed | Use freely |
rdi | edi | di | sil | 1st integer argument | Might be changed | Use freely |
rbp | ebp | bp | bpl | Frame Pointer | Maybe Be Careful | Maybe Be Careful |
rsp | esp | sp | spl | Stack Pointer | Be Very Careful! | Be Very Careful! |
r8 | r8d | r8w | r8b | 5th integer argument | Might be changed | Use freely |
r9 | r9d | r9w | r9b | 6th integer argument | Might be changed | Use freely |
r10 | r10d | r10w | r10b | Might be changed | Use freely | |
r11 | r11d | r11w | r11b | Might be changed | Use freely | |
r12 | r12d | r12w | r12b | Will not be changed | Save before using! | |
r13 | r13d | r13w | r13b | Will not be changed | Save before using! | |
r14 | r14d | r14w | r14b | Will not be changed | Save before using! | |
r15 | r15d | r15w | r15b | Will not be changed | Save before using! |
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ rax ━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ ┏━━━━━━━━━━━━━ eax ━━━━━━━━━━━━┫
┃ ┃ ┏━━━━━ ax ━━━━━┫
┃ ┃ ┣━━ ah ━┳━ al ━┫
0000000000000000000000000000000000000000000000000000000000000000
Usage during syscall/function call:
- First six arguments are in
rdi
,rsi
,rdx
,rcx
,r8
,r9
; remaining arguments are on the stack. - For syscalls, the syscall number is in
rax
. For procedure calls,rax
should be set to 0. - Return value is in
rax
. - The called routine is expected to preserve
rsp
,rbp
,rbx
,r12
,r13
,r14
, andr15
but may trample any other registers.
Instructions
Data Transfer
mov rax, 0x000F ; Store the value 15 into the rax register
mov rdi, rsi ; Copy the value in the rsi register into the rdi register
lea rdi, [message] ; Load the address of [message] into the rdi register
push rcx ; Push the value in the rcx register onto the top of the stack
pop rcx ; Pop the value off the top of the stack and into the rcx register
Arithmetic
add rdi, rsi ; Add the value in rsi to rdi, and store the result in rdi
; (i.e. rdi = rdi + rsi)
sub rdi, rsi ; Subtract the value in rsi from rdi, and store the result in rdi
; (i.e. rdi = rdi - rsi)
inc rax ; Increment the value in rax by 1
; (i.e. rax = rax + 1)
dec rax ; Decrement the value in rax by 1
; (i.e. rax = rax - 1)
Binary
and rdi, rsi ; Bitwise AND, store result in rdi
; (i.e. rdi = rdi & rsi)
or rdi, rsi ; Bitwise OR, store result in rdi
; (i.e. rdi = rdi | rsi)
xor rdi, rsi ; Bitwise XOR, store result in rdi
; (i.e. rdi = rdi ^ rsi)
not rax ; Bitwise Logical Not
shl rax, 1 ; Shift left the bits in rax, padding the resulting empty bit positions with zeros
shr rax, 1 ; Shift right the bits in rax, padding the resulting empty bit positions with zeros
Comparison
cmp rdi, rsi ; Compare rdi and rsi (rdi - rsi) and set flags.
; Similar to sub, but result is discarded.
test rdi, rsi ; Compare rdi and rsi (rdi & rsi) and set flags.
; Similar to and, but result is discarded.
Jump
Instruction | Synonym | Description |
---|---|---|
jmp | Unconditional | |
je | jz | Equal / zero |
jne | jnz | Not equal / not zero |
js | Negative | |
jns | Non-negative | |
jg | jnle | Greater than (signed >) |
jge | jnl | Greater than or equal to (signed >=) |
jl | jnge | Less then (signed <) |
jle | jng | Less then or equal to (signed <=) |
ja | jnbe | Above (unsigned >) |
jae | jnb | Above or equal (unsigned >=) |
jb | jnae | Below (unsigned <) |
jbe | jna | Below or equal (unsigned <=) |
For all jump instructions other than jmp
(which is unconditional), some previous instruction (cmp
, test
, etc.)
is needed to set the condition codes to be examined by the jump.
cmp rdi, rsi ; Compare rdi and rsi
je .label ; Jump to .label if equal
Procedure calls
call label ; Push return address and jump to label
ret ; Pop return address from stack and jump there
System Calls
Assembly programs can interact with the operating system using system calls:
- Put the system call number into
rax
- Load any arguments into the corresponding registers (
rdi
,rsi
,rdx
,rcx
,r8
,r9
) - Call
syscall
System call numbers differ for Linux and macOS:
Linux: https://github.com/torvalds/linux/blob/master/arch/x86/entry/syscalls/syscall_64.tbl
macOS: https://github.com/opensource-apple/xnu/blob/master/bsd/kern/syscalls.master
Note you need to add 0x2000000 to the call number.
; ----------------------------------------------------------------------------------------
; Writes "Hello, World" to the console using only system calls. Runs on 64-bit macOS only.
; To assemble and run:
;
; nasm -fmacho64 hello.asm
; ld hello.o -static -o hello
; ./hello
; ----------------------------------------------------------------------------------------
global start
section .text
start: mov rax, 0x02000004 ; system call for write
mov rdi, 1 ; file handle 1 is stdout
mov rsi, message ; address of string to output
mov rdx, 13 ; number of bytes
syscall ; invoke operating system to do the write
mov rax, 0x02000001 ; system call for exit
mov rdi, 0 ; exit code 0
syscall ; invoke operating system to exit
section .data
message: db "Hello, World", 10 ; note the newline at the end
Using C libraries
; ----------------------------------------------------------------------------------------
; Prints the numbers 1-10 using the C library printf function. Runs on 64-bit macOS only.
; To assemble and run:
;
; nasm -fmacho64 numbers.asm
; ld -L /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib -lc -o numbers numbers.o
; ./numbers
; ----------------------------------------------------------------------------------------
global _main
extern _printf
default rel
section .text
_main:
mov rcx, 1 ; initialise the counter to 1
print_loop:
; Print the counter
push rcx ; caller - save register
lea rdi, [format] ; set 1st parameter (format)
mov rsi, rcx ; set 2nd parameter (current number)
call _printf ; call the C library printf function
pop rcx ; restore caller-saved register
; Increment the counter and loop while <= 10
inc rcx ; increment the counter
cmp rcx, 10 ; compare the counter value to 10
jle print_loop ; jump to print_loop if less then or equal to
; Exit the program
mov rax, 0 ; set the exit code
ret
section .data
format: db "%d", 10, 0 ; note the newline and NULL characters at the end
The above is equivalent to this in C:
#include <stdio.h>
int main() {
int i;
for (i = 1; i <= 10; i++) {
printf("%d\n", i);
}
return 0;
}
or this in Ruby:
(1..10).each {|n| puts n }