Assembly Debugging — GDB and Debugging Techniques
In this tutorial, you will learn about Assembly Debugging. We cover key concepts, practical examples, and best practices to help you master this topic.
Debugging assembly programs with GDB enables single-stepping through instructions, inspecting registers, memory, and the stack to trace program execution and diagnose bugs.
What You'll Learn
- Setting up GDB for assembly
- Breakpoints and single-stepping
- Inspecting registers and memory
- Examining the stack
- Common debugging workflows
Why It Matters
Assembly bugs are subtle — wrong register values, off-by-one addressing, stack corruption. Durga Antivirus Pro uses GDB to diagnose low-level signature scanning routines in its assembly-optimized detection engine.
Real-World Use
Reverse engineering malware, debugging OS kernels, verifying compiler output, and optimizing critical code paths where source-level debugging is insufficient.
flowchart LR
A["GDB"] --> B["Breakpoints"]
A --> C["Single Step"]
A --> D["Register View"]
A --> E["Memory View"]
B --> F["stop at instruction"]
C --> G["execute one insn"]
D --> H["rsp, rax, flags"]
style A fill:#2563eb,stroke:#2563eb,color:#fff
style B fill:#dbeafe,stroke:#2563eb,color:#1e40af
style C fill:#dbeafe,stroke:#2563eb,color:#1e40af
style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
style E fill:#dbeafe,stroke:#2563eb,color:#1e40af
Compiling for Debugging
# Assemble with debug symbols
nasm -f elf64 -g program.asm -o program.o
ld program.o -o program
# Or with gcc
gcc -no-pie -g program.o -o program
The -g flag adds DWARF debug information to the object file.
Basic GDB Commands
gdb ./program
# Inside GDB:
(gdb) break _start # Set breakpoint at _start
(gdb) run # Run program
(gdb) stepi # Execute one instruction (si)
(gdb) nexti # Step over calls (ni)
(gdb) info registers # Show all registers
(gdb) info registers rax rbx # Show specific registers
(gdb) quit # Exit GDB
Examining Registers
; test.asm
section .data
val dq 42
section .text
global _start
_start:
mov rax, 100
mov rbx, [val]
add rax, rbx
mov rdi, rax
mov rax, 60
syscall
(gdb) break _start
(gdb) run
(gdb) info registers rax rbx rdi
# rax 0x0 0
# rbx 0x0 0
(gdb) stepi 3 # execute 3 instructions
(gdb) info registers rax rbx
# rax 0x64 100
# rbx 0x2a 42
Examining Memory
(gdb) x/10xb &val # Examine 10 hex bytes at val
(gdb) x/10dg &val # Examine 10 dword (quad)
(gdb) x/s &msg # Examine as string
(gdb) x/10i $rip # Examine 10 instructions at RIP
(gdb) x/20gx $rsp # Examine stack (20 qwords)
Format specifiers: x (hex), d (decimal), s (string), i (instruction).
Watchpoints
(gdb) watch *0x601020 # Stop when memory at address changes
(gdb) watch var # Stop when variable changes
(gdb) rwatch var # Stop when variable is read
(gdb) awatch var # Stop on read or write
Stack Inspection
(gdb) x/10gx $rsp # Examine top of stack
(gdb) info frame # Show current stack frame
(gdb) backtrace # Show call stack
(gdb) x/gx $rbp+8 # Return address on stack
(gdb) x/gx $rbp # Saved RBP
(gdb) x/4gx $rbp-16 # Local variables
Conditional Breakpoints
(gdb) break _start if $rax == 42
(gdb) break *0x401000 if $rcx > 10
(gdb) condition 1 $rdi == 0 # Modify breakpoint 1
Debugging with Layout
(gdb) layout asm # Show assembly source window
(gdb) layout regs # Show register window
(gdb) layout split # Show both
(gdb) tui enable # Enable TUI mode
(gdb) focus cmd # Focus on command window
Common Debugging Workflow
# 1. Set breakpoint at entry
(gdb) break _start
# 2. Run to breakpoint
(gdb) run
# 3. Set breakpoint at potential problem area
(gdb) break *0x40101a
# 4. Continue execution
(gdb) continue
# 5. Inspect state at breakpoint
(gdb) info registers
(gdb) x/10gx $rsp
(gdb) x/10i $rip-20
# 6. Single step
(gdb) stepi
(gdb) stepi
Disassembly in GDB
(gdb) disassemble _start # Disassemble function
(gdb) disassemble 0x401000,0x401020 # Address range
(gdb) disassemble /r _start # Show raw bytes too
(gdb) set disassembly-flavor intel # Use Intel syntax
(gdb) set disassembly-flavor att # Use AT&T syntax
Common Mistakes
1. Forgetting -g flag
Without debug symbols, GDB can show raw addresses but not source line or label information.
2. Mixing Intel and AT&T syntax checking
GDB defaults to AT&T for disassembly on Linux. set disassembly-flavor intel switches to Intel.
3. Not checking flags register
The flags register (RFLAGS) controls conditional jumps. info registers eflags shows carry, zero, sign, overflow flags.
4. Stopping at wrong symbol
_start is the ELF entry point. If linking with libc, main is the C entry. Use the right symbol.
5. Forgetting address space layout randomization
ASLR changes addresses each run. Use set disable-randomization on in GDB for repeatable debugging.
Practice Questions
1. What GDB command shows all register values?
info registers or info reg.
2. How do you execute one assembly instruction in GDB?
stepi or si (step instruction).
3. What does x/10gx $rsp do?
Examines 10 quadwords (8 bytes each) in hex format at the current stack pointer.
4. How do you set a breakpoint at a specific address?
break *0x401000 or break _start (if label has debug info).
Challenge: Use GDB to trace the execution of a recursive factorial function and observe the stack growth.
FAQ
{{< faq question="Can I debug assembly without debug symbols?" >}}
Yes. GDB works with raw addresses. Use break *0x401000 for address-based breakpoints and x/10i $rip to see instructions.
{{< /faq >}}
{{< faq question="How do I debug a program that crashes?" >}}
Run with GDB and use run. The program stops at the crash site. Use bt (backtrace) and info registers to see the state.
{{< /faq >}}
{{< faq question="What is a core dump and how do I use it?" >}
A core dump is a snapshot of the crashed Process. Load with gdb ./program core and inspect the crash state.
{{< /faq >}}
{{< faq question="How do I debug programs with -pie enabled?" >}
PIE binaries have ASLR. Use starti to break at the entry point before any code runs, then set breakpoints at runtime addresses.
{{< /faq >}}
{{< faq question="Can GDB debug optimized code?" >}} Yes, but optimized code may have jumps, inlined functions, and reordered instructions that make single-stepping confusing. {{< /faq >}}
Mini Project
Write a simple assembly program with a deliberate bug and use GDB to find it.
section .data
array dq 10, 20, 30, 40, 50
section .text
global _start
_start:
xor rax, rax
mov rcx, 5
sum_loop:
; Bug: missing multiplication by 8 for qword access
add rax, [array + rcx] ; should be [array + rcx * 8]
dec rcx
jnz sum_loop
mov rdi, rax
mov rax, 60
syscall
gdb ./buggy
(gdb) break _start
(gdb) run
(gdb) stepi 6
(gdb) info registers rcx rax
# Notice rax has wrong value due to wrong memory access
What's Next
Now that you understand debugging, proceed to profiling assembly code.
| Topic | Description | Link |
|---|---|---|
| Profiling | Performance analysis | {{< ref "20-profiling" >}} |
| Tools | Assembly development tools | {{< ref "30-tools" >}} |
| Instructions | CPU instruction set | {{< ref "06-instructions" >}} |
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro