Chapter 5 Key Takeaways: Your Development Environment
The 12 Most Important Points from This Chapter
1. NASM assembles .asm → .o; ld links .o → executable.
Two separate steps. NASM translates mnemonics to machine code and produces object files with relocations. ld resolves addresses, combines sections, and produces a runnable ELF executable. The command nasm -f elf64 file.asm -o file.o && ld file.o -o file is the fundamental build sequence for standalone assembly programs.
2. Use gcc to link when calling C library functions; use ld for pure assembly.
gcc automatically links the C runtime startup code (crt1.o, crti.o, crtn.o) and libc.so.6. Pure assembly programs that go directly to system calls use ld with no extra setup. When writing a program with main instead of _start, use gcc to link.
3. GDB stepi (si) is the fundamental assembly debugging command.
stepi executes exactly one assembly instruction and stops. For assembly debugging, stepi is your primary navigation command. nexti (ni) is like stepi but steps over function calls without entering them. Never use step or next (source-level) when debugging assembly; they don't work meaningfully.
4. info registers and display /x $reg are your register inspection tools.
info registers shows all 16 GPRs plus RIP, RFLAGS, and segment registers. display /x $rax auto-prints RAX in hex after every command. Setting up auto-display for the registers you care about (RAX, RDI, RSI, RDX, RCX, R11 before syscalls) eliminates the need to type print repeatedly.
5. x/FMT addr examines memory in many formats.
x/17xb 0x402000: 17 hex bytes. x/s 0x402000: string. x/8gx $rsp`: 8 quadwords. `x/5i $rip: 5 instructions. The format is N=count, F=format (x/d/s/i), S=size (b/h/w/g). This command covers every memory examination need.
6. GDB TUI mode (layout regs + layout asm) shows registers and code simultaneously.
layout regs adds a register display panel. layout asm adds a disassembly panel with the current instruction highlighted. Ctrl+X, A toggles TUI mode. TUI mode is significantly more efficient than alternating between info registers and disassembly manually.
7. syscall clobbers RCX (saves return RIP) and R11 (saves RFLAGS).
This is observable in GDB: before syscall, RCX holds your value; after syscall, RCX holds the address of the next instruction. This is the mechanism by which SYSRET returns to user space. Any value in RCX or R11 across a syscall boundary must be explicitly saved and restored if needed.
8. The full toolchain includes: NASM, ld, gcc, objdump, readelf, nm, strace, ltrace, QEMU.
Each tool has a specific purpose: objdump -d disassembles, readelf -h shows ELF headers, nm shows the symbol table, strace shows system calls at runtime, ltrace shows library calls, QEMU virtualizes an entire machine for OS development. Learning which tool to reach for when is as important as knowing how to use it.
9. The -g -F dwarf NASM flags add source-level debug information.
Without debug info, GDB shows raw addresses. With -g -F dwarf, GDB can show file names, line numbers, and label names. Always use these flags for development and debugging; omit them for release builds (the debug info increases binary size but not runtime overhead).
10. A Makefile with pattern rules automates the build and handles incremental compilation.
The pattern %.o: %.asm defines how any .asm file becomes an .o file. Adding include files as dependencies (%.o: %.asm $(wildcard include/*.inc)) ensures recompilation when headers change. The make utility's dependency tracking is the correct solution to "which files need to be rebuilt after this change?"
11. Multi-file projects use %include for shared definitions and extern for cross-file function references.
%include "macros.inc" pastes the content of the included file at that point in the source (like C's #include). extern printf declares that printf is defined in another object file or library. global my_func makes my_func visible to other objects. These three mechanisms — include, extern, global — are how you modularize assembly code.
12. QEMU provides a complete virtualized x86-64 machine for OS kernel testing.
qemu-system-x86_64 -s -S starts the CPU halted with a GDB server on port 1234. gdb minos.elf then target remote :1234 connects GDB to the running QEMU machine. This allows single-step debugging of kernel code from the very first instruction after power-on — the same tool chain used for professional embedded and OS development.
Visual Summary: The Build Pipeline
hello.asm
│
│ nasm -f elf64 -g -F dwarf hello.asm -o hello.o
▼
hello.o (object file: machine code + unresolved symbols + DWARF debug)
│
│ ld hello.o -o hello
▼
hello (executable ELF: resolved, self-contained)
│
├── ./hello (run it)
├── gdb ./hello (debug it)
├── objdump -d hello (disassemble it)
├── readelf -h hello (inspect ELF structure)
├── strace ./hello (trace system calls)
└── nm hello (show symbols)
Visual Summary: Essential GDB Commands for Assembly
Starting:
gdb ./program load executable
break _start / break *addr set breakpoint
run [args] start program
Navigation:
stepi (si) one instruction, enter calls
nexti (ni) one instruction, skip calls
continue (c) run to next breakpoint
finish run until current function returns
Registers:
info registers show all GPRs
print $rax / print/x $rax print one register
display /x $rax auto-display after every command
set $rax = 0x1234 set a register value
Memory:
x/Nxb addr N hex bytes
x/Ngx addr N 64-bit values
x/s addr string
x/Ni addr N instructions
Layout:
layout asm show disassembly window
layout regs add register window
Ctrl+X, A toggle TUI on/off