Writing a Linux Debugger in Rust: Ptrace, Breakpoints & More

Added:

Debugger History
Debugging Evolution
Ptrace Fundamentals
Debugger Demo
Initiating Tracing
Syscall Tracing
Register Access
Single Stepping
Step Mechanics
Breakpoints

Debugger History

2:04
Playing Section
  • 1

    Early debuggers emerged from the need to debug single-user machines like the TX-0 through single-stepping and memory inspection.

  • 2

    Batch processing led to debugging via macro calls that produced snapshots or core dumps for post-crash analysis.

  • 3

    The evolution continued with time-sharing systems enabling more interactive debugging methods like printf.

Intermediate Rust programming, particularly handling system calls, unsafe blocks, and foreign function interfaces (FFI) for interacting with C APIs.
Linux process mechanics, including parent-child process relationships, system calls, and UNIX signal handling (specifically SIGTRAP).
x86_64 CPU architecture fundamentals, particularly the role of CPU registers (like RIP), instruction execution, and software interrupt instructions (such as INT 3).
Parsing DWARF debug info to map raw memory addresses to high-level source code locations, variables, and data structures.
Handling multi-threaded debugging, including tracking thread lifecycles and managing debugger states across concurrent execution flows.
Implementing the GDB Remote Serial Protocol (RSP) to make your custom debugger compatible with standard frontends like VS Code or GDB.
Studying anti-debugging techniques (such as ptrace detection or timing checks) and reverse engineering practices.
10K views200likes36:40@linuxconfau2018Original Release: 2018-01-26

Debuggers work by intercepting process execution through the ptrace system call, which allows one process to manipulate or monitor another. Software breakpoints are implemented by replacing target instructions with the INT3 (0xCC) instruction, which triggers a breakpoint exception when executed. Hardware breakpoints use debug registers (DR0-DR3) that can monitor specific memory addresses for reads, writes, or executions, providing more efficient breakpoint handling than software methods. Single-stepping works by setting the trap flag (TF) in the eflags register, causing the CPU to generate a debug interrupt after each instruction.