โš™๏ธ

x86-64 Assembly

The layer underneath C โ€” what your compiler actually turns your code into. Not something you write day-to-day, but understanding it is the whole point of a computer-architecture or systems course (like CMU's Bomb Lab and Attack Lab).

๐Ÿ“ This is a reference page, not a live-execution one โ€” assembly needs to run directly on real hardware or a full CPU emulator, so there's no safe free "Run" button the way there is for C or Python. Read this alongside disassembled C code from a real compiler (objdump -d) for the fastest way to actually learn it.
Jump to: RegistersMoving dataArithmetic The stackControl flowFunction calls

Registers

A handful of tiny, extremely fast storage slots built directly into the CPU โ€” far faster than RAM, and where almost all work actually happens.

RegisterCommon use
%rax"Accumulator" โ€” usually holds a function's return value
%rbx, %rcx, %rdxGeneral-purpose working registers
%rdi, %rsiFirst and second function arguments (System V calling convention)
%rspStack pointer โ€” points at the top of the stack
%rbpBase pointer โ€” anchors the current function's stack frame

Moving data

movq $5, %rax        # %rax = 5 (the "q" suffix = 64-bit quadword)
movq %rax, %rbx       # %rbx = %rax
movq (%rax), %rbx     # %rbx = *(the value AT the address in %rax) โ€” like a C pointer dereference

Arithmetic

addq $1, %rax        # %rax += 1
subq %rbx, %rax       # %rax -= %rbx
imulq %rbx, %rax      # %rax *= %rbx  (signed multiply)

The stack

A last-in-first-out region of memory used for local variables, saved registers, and return addresses โ€” this is what your program uses every time you call a function.

pushq %rax     # save %rax's value onto the stack, move %rsp down
popq %rbx      # take the top of the stack into %rbx, move %rsp back up

Control flow

Assembly has no if or while โ€” every branch is really "compare, then jump."

cmpq %rbx, %rax     # compute %rax - %rbx (result discarded, only FLAGS set)
je equal_case      # jump if the last comparison was EQUAL
jg greater_case    # jump if GREATER
jmp done           # unconditional jump
equal_case:
    # ...
done:
๐Ÿ“ This is exactly what CMU's Bomb Lab is built around โ€” reverse-engineering a compiled binary means reading chains of cmp/j* instructions and figuring out what C if/while statement they came from.

Function calls

callq my_function   # pushes the return address, then jumps
ret                 # pops the return address, jumps back to the caller

Arguments are passed in registers (%rdi, %rsi, ...), and the return value comes back in %rax โ€” this is exactly why a C function like int add(int a, int b) compiles down to reading %rdi and %rsi and leaving its answer in %rax.

โ† See the CMU systems track this ties into