Skip to content

Assembly Debugging — GDB and Debugging Techniques

DodaTech Updated 2026-06-28 6 min read

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