x86-64 Architecture
NASM Syntax
Program structure
; NASM program template
; Assemble: nasm -f elf64 program.asm -o program.o
; Link: ld program.o -o program
section .data ; Initialized data
msg: db "Hello", 10 ; String with newline
len: equ $ - msg ; Length = current pos - start
number: dd 42 ; 32-bit integer
array: dq 1, 2, 3, 4 ; Array of 64-bit integers
section .bss ; Uninitialized data (zeroed)
buffer: resb 256 ; Reserve 256 bytes
count: resd 1 ; Reserve 1 dword (4 bytes)
ptr: resq 1 ; Reserve 1 qword (8 bytes)
section .text ; Code
global _start ; Entry point for linker
_start:
; ... code here ...
mov rax, 60 ; sys_exit
xor rdi, rdi ; status 0
syscall
Data definition directives
db Define Byte 1 byte "abc", 0x41, 10
dw Define Word 2 bytes 0x1234
dd Define Doubleword 4 bytes 42, 3.14 (float)
dq Define Quadword 8 bytes 0x123456789ABCDEF0
dt Define Ten bytes 10 bytes (extended precision float)
resb Reserve Bytes 1 byte each
resw Reserve Words 2 bytes each
resd Reserve Dwords 4 bytes each
resq Reserve Qwords 8 bytes each
equ Constant (no storage allocated)
SIZE equ 100 ; Like #define SIZE 100 in C
times Repeat
buffer: times 256 db 0 ; 256 zero bytes
Addressing Modes Summary
Intel syntax (NASM)
; Immediate: mov rax, 42
; Register: mov rax, rbx
; Direct: mov rax, [label] ; absolute address
; RIP-relative: mov rax, [rel label] ; position-independent (PIE)
; Indirect: mov rax, [rbx] ; register as pointer
; Displaced: mov rax, [rbx + 8] ; base + offset
; Indexed: mov rax, [rbx + rcx*4] ; base + index × scale
; Full: mov rax, [rbx + rcx*4 + 8] ; base + index × scale + disp
; Scale must be 1, 2, 4, or 8
; This matches common data sizes:
; 1 = byte (char)
; 2 = word (short)
; 4 = dword (int, float)
; 8 = qword (long, double, pointer)
; LEA: calculate address without memory access
lea rax, [rbx + rcx*4 + 8] ; rax = rbx + rcx*4 + 8 (math only!)
Build Workflow
Assembly, linking, debugging
# Pure assembly (no libc)
nasm -f elf64 -g -F dwarf program.asm -o program.o
ld program.o -o program
# With libc (for printf, malloc, etc.)
nasm -f elf64 -g -F dwarf program.asm -o program.o
gcc program.o -o program -no-pie
# With debug symbols (-g -F dwarf)
nasm -f elf64 -g -F dwarf program.asm -o program.o
ld program.o -o program
gdb ./program
# C to assembly (see what compiler does)
gcc -S -O0 -masm=intel program.c # Unoptimized
gcc -S -O2 -masm=intel program.c # Optimized
# Disassemble a binary
objdump -d -M intel program
objdump -d -S -M intel program # With source if debug info
# Check binary properties
file program
readelf -h program # ELF header
readelf -S program # Section headers
nm program # Symbol table
Makefile for assembly projects
ASM = nasm
ASMFLAGS = -f elf64 -g -F dwarf
LD = ld
.PHONY: all clean
all: program
program: program.o
$(LD) $< -o $@
%.o: %.asm
$(ASM) $(ASMFLAGS) $< -o $@
clean:
rm -f *.o program
Debugging Assembly with GDB
Essential GDB commands for assembly
# Start with Intel syntax
gdb -q ./program
(gdb) set disassembly-flavor intel
# Breakpoints
(gdb) break _start # At label
(gdb) break *0x401000 # At address
(gdb) info break # List breakpoints
# Execution
(gdb) run # Start program
(gdb) stepi # Step ONE instruction
(gdb) nexti # Next instruction (skip calls)
(gdb) continue # Run until next breakpoint
(gdb) finish # Run until function returns
# Examine registers
(gdb) info reg # All registers
(gdb) info reg rax rbx rsp # Specific registers
(gdb) print/x $rax # Print RAX in hex
(gdb) print/d $rax # Print RAX in decimal
(gdb) print/t $rax # Print RAX in binary
# Examine memory
(gdb) x/10i $rip # Next 10 instructions
(gdb) x/20xg $rsp # 20 qwords at stack pointer
(gdb) x/s 0x402000 # String at address
(gdb) x/10xb $rax # 10 bytes at RAX address
# Examine flags
(gdb) print $eflags # Show flags
(gdb) info reg eflags # Detailed flag view
# Modify registers
(gdb) set $rax = 0x42
(gdb) set *(int*)0x601050 = 100
# Watch memory changes
(gdb) watch *0x601050 # Break when value changes
(gdb) display/x $rax # Show RAX after each step
Common Instruction Patterns
Idioms
; Zero a register (fastest/smallest encoding)
xor eax, eax ; 2 bytes, zeros RAX too
; Test if register is zero
test rax, rax ; Sets ZF=1 if zero
jz is_zero
; Test if odd
test rax, 1 ; ZF=0 if odd (bit 0 set)
jnz is_odd
; Multiply by constant without MUL
lea rax, [rax + rax*2] ; × 3
shl rax, 2 ; × 4
lea rax, [rax + rax*4] ; × 5
lea rax, [rax + rax*2] ; × 3
shl rax, 1 ; then × 2 = × 6
; Swap without temp
xchg rax, rbx ; Atomic on x86 (has implicit LOCK for mem)
; Conditional move (branchless)
cmp rax, rbx
cmovl rax, rbx ; rax = min(rax, rbx)
; Function prologue/epilogue
push rbp
mov rbp, rsp
sub rsp, 32 ; Allocate locals
; ... body ...
leave ; mov rsp, rbp; pop rbp
ret