A from-scratch, ground-up series on x86-64 assembly on Linux - for people who already know C and the command line but have never actually written a line of assembly.
This isn't an instruction reference. Instruction references already exist, and skimming one won't teach you why the machine behaves the way it does. This series is built around a different goal:
"Give you an accurate mental model of what's actually happening at the CPU and OS level, and only then hand you the syntax to express it."
The primary target is x86-64 (AMS64) assembly on Linux, using NASM and, ld and GDB, with real C interoperability throughout.
There's also enough 32-bit x86 and x86 history woven in to explain why x86-64 looks the way it does.
- You want to actually understand what's happening under your programs.
- You've never actually written assembly, or have only poked at it.
- You want to understand x86-64 assembly from both a programming and systems perspective
- You want a foundation for further study in areas such as OS development, reverse engineering, binary exploitation, or CPU architecture.
- Read x86-64 assembly confidently, including compiler-generated output.
- Understand how Linux system calls actually work, and use them directly.
- Write, assemble, and link your own x86-64 assembly program on Linux.
- Understand ELF, linking, and loading well enough to explain how a source file becomes a running process.
- Debug and inspect binaries with GDB,
objdump,readelf,nm, andstrace. - Have a solid enough foundation to move into OS development, reverse engineering, binary exploitation, or deeper CPU architecture study.
- A working knowledge of C, including:
- variables
- pointers
- arrays
- functions
- basic memory concepts
- Familiarity with basic computer architecture concepts like:
- Bits and bytes
- Binary and hexadecimal numbers
- Signed and Unsigned numbers
- etc.
- Comfortable with Linux and the command line.
- A basic understanding of how programs are compiled and executed is helpful but not required.
- A 64-bit x86 Linux Machine (or a virtual machine)
- NASM - assembler
- GNU
ld- linker - GDB + pwndbg - debugger and debugging interface used throughout this tutorial
objdump- disassembler and binary inspectionstrace- system call tracing
Note
pwndbg is a GDB extension designed to make debugging binaries easier and more informative. Learn more at pwndbg.re.
This tutorial series will use pwndbg alongside GDB for debugging, register inspection, memory inspection, stack analysis, etc.
This tutorial series is currently undergoing a major refactor.
The new version is being reorganized into a more structured curriculum, with additional concepts, improved explanations, and a more deliberate progression through x86-64 asm programming.
If you're starting the series now, you can either follow the existing tutorials from the previous version, or explore the new curriculum in the Table of Contents below.
Note
As of 28th September 2026, the new curriculum only represents the planned structure of the refactored series.
Feel free to raise an issue or submit a PR if you think something is worth adding!
Below are the tutorials from the previous version of the series:
- Introduction to Computer Architecture
- CPU
- Memory
- Buses
- Endianness
- Memory Hierarchy
- Instruction Set Architecture (ISA)
- Interrupts And Exceptions
- Settign Up The Environment
- Installing NASM (assembler)
- Installing ld (linker)
- Installing gdb (debugger)
- Introduction to Assembly Language
- What is Assembly?
- Why Assembly?
- Structure of an Assembly Program
- Syntax of Assembly
- Registers for Assembly
- Addressing Modes
- Assembly syntax/format
- Switching syntax/format in assemblers or debugger
- Hello World Program in Assembly
- Syscall Structure
- Source Code
- Explanation
- Data Definition Directives
- Steps to execution
- Same programs Using Labels
- Jumps, Calls, and Flags in Assembly
- Flags
- Jumps
- Calls
- Reading User Input And Greeting in Assembly
- Source Code
- Explanation
- Arithmetic and Logical Instructions in Assembly
- Arithmetic Instructions
- Logical Instructions
- Displaying A Digit in RAX
- Source Code
- Explanation
- Calculating The Length of String During Runtime
- Source Code
- Explanation
- Virtual Memory in Assembly: Understanding Stack, Heap, and Segments
- Introduction to Virtual Memory
- Segments of Virtual Memory
- Stack
- Heap
- Macros in NASM
- What are macros?
- How to define a macro?
- Local Labels in a macro definition
- Defining constants using
equ - Including external files using
%include
- Subroutine to Print an Integer
- Source code
- Overview of the logic used
- Explanation of the source code
- Command Line Arguments
- What are command line arguments (CLI arguments)
- Arguments on the stack
- Program to print CLI arguments onto the screen.
- Introduction to files
- Introduction to files
- File permissions
- File descriptors
- Opening & closing a file
- Opening a file using
sys_opensystem call - Flags used while opening a file
- Modes used while opening a file
- Combining multiple flags and modes
- Closing a file using
sys_closesystem call - Example of opening and closing a file
- Opening a file using
- Writing to an open file
sys_writesystem call breakdown- Example of writing to a file
- Reading from an open file
sys_readsystem call breakdown- Example of reading from a file
- Appending to a file
- Example of appending to a file
This series is under active development. What's below is the roadmap of what each section/note will eventually cover - not links to finished content yet. As a concept gets written up, its entry will turn into a hyperlink to the actual note.
In addition to that, the structure and grouping may also get reorganized over time if a clearer sequence turns up - treat the numbering as current-best-guess, not final.
- 1. Foundations
- 2. History of x86
- 3. Assembly Environment
- 4. First Assembly Program
- 5. Core Instructions
- 6. Control Flow
- 7. Strings and Memory Access
- 8. Virtual Memory
- 9. The Stack and Functions
- 10. The Heap
- 11. Program Startup and Termination
- 12. NASM Code Organization
- 13. C and Assembly
- 14. Linux System Interface
- 15. ELF and the Toolchain
-
Computer Architecture Fundamentals
- CPU
- ALU
- Control Unit
- Registers
- Memory
- Buses
- Instruction execution
-
Memory Hierarchy
- Registers
- Cache
- RAM
- Storage
- Locality
- Why the hierarchy exists
-
Interrupts and Exceptions
- Interrupts
- Exceptions
- Hardware vs Software interrupts
- Synchronous vs Asynchronous events
- Basic CPU response
-
From Source Code to Machine Code
- High-level language
- Compiler
- Assembly
- Assembler
- Object files
- Linker
- Executable
- Loader
-
Instruction Set Architecture (ISA)
- What an ISA is
- ISA vs microarchitecture
- Instructions
- Registers
- Memory model
-
ABI and Calling Conventions
- What an ABI is
- ABI vs API
- Calling conventions
- Binary compatibility
-
RISC and CISC
- RISC
- CISC
- Design philosophies
- Trade-offs
-
Character & Data Representation
- ASCII
- Unicode overview
- Characters as integers
- Strings as byte sequences
-
Endianness
- Little-endian
- Big-endian
- Multi-byte values in memory
- Why x86 uses little-endian
-
The 8086 and the Origins of x86
- 8086
- 16-bit architecture
- General-purpose registers
- Segmentation
- Early x86 design
-
x86 Real Mode
- Real mode
- Segment:offset addressing
- 20-bit addresses
- 1 MiB address space
- BIOS-era execution
-
The 80286 and Protected Mode
- 80286
- Protected mode
- Privilege levels
- Segmentation
- Protection
-
The 80386 and 32-bit x86
- 32-bit registers
- 32-bit addressing
- Flat memory model
- Paging
- Virtual memory foundations
-
The Evolution of 32-bit x86
- 386 -> 486 -> Pentium
- Major architectural changes
- Growing ISA extensions
-
The 64-bit Transition
- Why 64-bit?
- AMD64
- Intel's original IA-64 approach
- Why AMD64 became x86-64
-
x86-64 Architecture
- 64-bit registers
- Expanded register set
- Larger address space
- Long mode
- Compatibility mode
- Legacy support
-
x86 ISA Extension
- MMX
- SSE family
- AVX family
- AVX-512
- Other important extensions
- Feature detection
-
Setting up the Assembly Environment
- NASM
ld- GDB + pwndbg
objdump- Basic build commands
-
Introduction to Assembly Language
- Assembly vs machine code
- Why assembly?
- Assembly language structure
- Instructions
- Operands
- Directives
- Comments
-
Intel (NASM) Syntax vs AT&T Syntax
- Intel and AT&T Syntax
- Nasm syntax
- Instruction format
- Operand order and sizes
- Constants
- Labels
- Directives
-
Assembly Program Anatomy
.text/.data/.bss- Labels
- Entry point
- Instructions vs directives
-
x86-64 Registers and Sub-registers
- General-puprose registers:
RAX-R15 RIP/RSP/RSP/RBPRFLAGS- Sub-registers
- Register naming
- General-puprose registers:
-
Addressing Modes
- Immediate
- Register
- Memory
- Base
- Index
- Scale
- Displacement
- Effective address
-
Introduction to Linux System Calls
- User space and Kernel space
- What a systemcall is
- Why programs need system calls
- System call numbers
- Basic syscall convention on x86-64 Linux
- Passing arguments in registers
- The
syscallinstruction - Return values
-
Hello World in Assembly
writesystem call- Source code
- Loading syscall arguments into registers
- Invoking
syscall - Return value
- Building and Linking
- Execution
-
Data Definition Directives
db/dw/dd/dqresb/resw/resd/resq- Defining strings and buffers
-
Data Movement Instructions
mov/movzx/movsx/movsxd- Operand sizes
- Memory/register movement
-
LEA and Effective Address Calculation
lea- Address calculation
- Arithmetic uses of
lea - Difference between
leaandmov
-
Arithmetic Instructions
add/subinc/decnegmul/imuldiv/idiv
-
Logical and Bitwise Instructions
and/or/xor/not- Bit masks
- Common bitwise patterns
-
Shift and Rotate Instructions
shl/shr/sarrol/ror- Logical vs Arithmetic shifts
-
Example: Displaying a Digit in RAX
-
The FLAGS Register
RFLAGS,EFLAGS,FLAGS- Purpose of the Flags register
- Status flags:
CF/ZF/SF/OF/PF/AF - Control flags:
DF - When each flag is set (carrry vs overflow)
- Which instructions modify which flags
- Which instructions leave flags unchanged
- Reading flags with
pushfq/pop - Inspecting flags with pwndbg
- Clearing and Setting flags:
clc/stc/cld/std
-
Comparisons and TEST
cmpandtestcmpas subtraction without storing the resulttestas AND without storing the result- How
cmpandtestaffect FLAGS - Signed vs Unsigned comparison semantics
-
Labels and Unconditional Jumps
- Labels
jmp- Relative jumps
- Jump targets
- Basic control-flow structure
-
Conditional Jumps
je/jnejg/jge/jl/jlejz/jnz- Other common
jccinstructions - Signed vs Unsigned conditions
- How conditional jumps read FLAGS
cmp-> FLAGS ->jcc
-
Loops in Assembly
- Loop structure
loop- Counter-based loops
- Conditional-jump loops
- Translating C
for/while/do-whileloops
-
Function CALL and RET
- Functions / Subroutines
call/retinstructions and what they do- Return addresses
- Stack interaction (will be covered in detail later)
- Direct vs Indirect calls
- Basic subroutine control flow
-
Working with Strings
- Strings in memory
- Null termination
- String pointers
- Traversing strings
-
String Instructions
movs/stos/lods/scas/cmps/rep- Direction flag
-
Example: Reading User Input and Greeting the User
readsystem call- Input buffers in
.bss - Buffer lengths
-
Example: Calculating String Length at Runtime
- Pointer traversla
- Counting bytes
-
Virtual Memory and Process Address Space
- Virtual memory vs Physical memory
- Virtual addresses
- Process address spaces
- User space vs Kernel space
- Why each process has its own virtual address space
-
Process Memory Regions
- Typical x86-64 Linux process memory layout
.text/.rodata/.data/.bss- Heap
- Memory-mapped region
- Shared libraries
- Stack
- Purpose of each region
- Read / write / execute permissions
- File-backed vs anonymous memory
- Private vs shared mappings
- Which regions grow and in which direction
-
Inspecting Process Memory
/proc/<pid>/maps- Memory mappings
- Virtual address ranges
- Permissions
- File-backed mappings
- Shared libraries
- Inspecting memory regions with pwndbg
-
Virtual Memory Concepts
- Pages and page frames
- Page boundaries
- Virtual pages vs physical frames
- Memory protection
- Demand paging
- Page faults
- Copy-on-write
- ASLR
- Introduction to
mmap
-
The Stack
- Purpose/Use/Lifetime of the stack
- Growth of the stack
- Use of
RSPandRBP - Stack instructions:
pushandpop - Inspecting the stack with pwndbg
-
Stack Frames
- What is a stack frame
- Function prologue and epilogue
- General layout of a stack frame
- Frame base, stack pointer
- Local variables, return address
- Inspecting the stack frames with pwndbg
-
System V AMD64 Calling Convention
- Why calling conventions exists
- Integer and pointer arguments
- Argument registers
- Return values
- Caller-saved registers
- Callee-saved registers
- Register preservation
- Stack-passed arguments
-
Stack Alignment and the Red Zone
- 16-byte stack alignment
- Stack alignment at function calls
- Why alignment matters
- The 128-byte red zone
- When the red zone can be used
-
Floating-Point and Variadic Arguments
- XMM registers
- Floating-point arguments
- Floating-point return values
- Variadic functions
ALand variadic calls
-
32-bit CDECL vs System V AMD64
- 32-bit x86 calling conventions
- Stack-based arguments
- Register-based arguments
- Caller-saved and Callee-saved registers
- Differences between 32-bit and 64-bit calling conventions
-
Example: Subroutine to Print an Integer
-
The Heap
- Purpose/Use/Lifetime of the heap
- The heap in the process address space
- Static vs Dynamic memory
- Growth of heap
- Heap vs Stack
brkandsbrk
-
Dynamic Memory Allocation
malloc/calloc/realloc/free- What a memory allocator actually does
- Allocation and Deallocation
- Allocation metadata
- Memory alignment
-
Heap Internals
- Heap chunks
- Chunk metadata
- Free lists
- Splitting and coalescing
- Internal fragmentation
- External fragmentation
- Relationship between
mallocandmmap
-
Inspecting Heap Memory
- Heap mappings
- Examining heap memory with pwndbg
- Tracking allocations
- Examining allocator metadata
- Observing heap growth
-
Common Heap Memory Errors
- Memory leaks
- Use-after-free
- Double-free
- Heap buffer overflows
- Dangling pointers
-
Program Startup
- What happens when a program is executed
- Loading an executable
- Entry point
_start- Initial register state
- Initial stack
- How the kernel initializes the process
_startvsmain
-
argc, argv, and envp
argc/argv/envp- Initial stack layout
- Argument strings
- Environment strings
- Walking
argvandenvp
-
Example: Command-Line Arguments
- Accessing
argcandargvfrom assembly - Walking the argument array
- Accessing individual arguments
- Argument pointers vs Argument strings
- Accessing
-
Program Termination
- Normal program termination
exitsystem call- Exit status
- Returning from
main _exit- What happens when a process terminates
-
NASM Macros
%macro- Parameters
- Expansion
- Local labels
-
NASM Constants and Conditional Assembly
equ/%define%ifdef%ifndef- Conditional assembly
-
NASM Include Files and Code Organization
%include- Shared definitions
- Reusable assembly components
- Organizing larger projects
-
Reading Compiler-Generated Assembly
- Assembly from
gcc:gcc -S - Optimization levels
- Mapping C -> assembly
- Assembly from
-
Variables and Pointers in Assembly
- C variables
- Addresses
- Pointers
- Dereferencing
-
Arrays in Assembly
- Array layout
- Indexing
- Pointer arithmetic
- Element sizes
-
Structures in Assembly
- Struct layout
- Member offsets
- Padding
- Alignment
-
Calling C from Assembly
- System V ABI in practice
- External symbols
- Linking against libc
- Calling C functions
-
Calling Assembly from C
- External assembly functions
- Object files
- Linking
- ABI compatibility
-
Switch Statements and Jump Tables
switch- Jump tables
- Indirect jumps
- Compiler-generated control flow
-
User Mode and Kernel Mode
- Privilege levels
- User space
- Kernel space
- Protection boundaries
-
Linux System Calls
- Syscall interface
- Syscall numbers
- Argument registers
- Return values
syscallandsysret
-
File Descriptors
- File descriptors
stdin/stdout/sterr- Descriptor tables
- File descriptions
-
Introduction to Files
- Files
- File metadata
- Permissions
- Paths
-
Opening and Closing Files
openandclosesystem calls- Flags and Modes
- Combining multiple flags and modes
- Example: Opening and closing a file on disk
-
Writing to a File
writesystem call- Buffers and Return values
- Example: Writing to a file on disk
-
Reading From a File
readsystem call- Buffers, EOF, and Return values
- Example: Reading from a file on disk
-
Appending to a File
O_APPEND- File offsets
- Example: Appending to a file on disk
-
From Object File to Process
- Assembler
- Object file
- Linker
- Executable
- Loader
-
ELF Object Files
- ELF header
- Sections
- Symbols
- Relocations
-
ELF Program Headers and Segments
- Program headers
- Loadable segments
- Section vs Segment
-
ELF Symbols and Relocations
- Symbol tables
- Symbol resolution
- Relocations
- Relocation types
-
Static Linking
- Static libraries
- Link-time symbol resolution
- Static executables
-
Dynamic Linking
- Shared libraries
- Dynamic loader
- Runtime symbol resolution
-
PLT and GOT
- PLT - Procedure Linkage Table
- GOT - Global Offest Table
- Lazy binding
- Function calls through the PLT
-
Position Independent code
- RIP-relative addressing
- PIC
- Shared libraries
-
PIE and ASLR
- PIE
- ASLR
- Load addresses
- Relationship between them
This tutorial series is actively being enhanced and expanded. I am committed to continuously updating the content to cover a broader range of topics and advanced concepts in assembly programming.
Your feedback and suggestions for additional topics are welcome! Thank you for your interest and support as I develop this resource for the assembly programming community :D
This repository is dual-licensed by content type:
- Tutorial text and diagrams (everything in
notes/andassets/) are licensed under CC BY-SA 4.0. - Example source code (
.asm,.c, build scripts, and any other code embedded in the tutorials) is licensed under GPL-3.0.
GitHub's license badge in the sidebar will only pick up the LICENSE file (CC BY-SA 4.0) since it doesn't support dual-licensed repos natively. This section is the source of truth for the code split.
If you spot an error, have a suggestion, or want to contribute a note, feel free to open an issue or a pull request. Fork the repo, create a branch, make your changes, and submit a PR.