A Deep Dive into Processes, Memory, Breakpoints, and DWARF Debug Information
Introduction
- Why Build a Debugger?
- What This Book Covers
- Prerequisites
- How to Read This Book
- What About the Code?
Chapter 1: What Is a Debugger, Really?
- The Debugger as an Operating System Feature
- The Debugging Loop
- What We Will Build
- Why Rust for a Debugger?
- The Architecture of Our Debugger
- A Brief History of Debuggers
Chapter 2: The Building Blocks: Processes, Memory, and the CPU
- Processes and the Process Table
- Virtual Memory and Address Spaces
- The CPU Registers and Program Counter
- How Programs Start: Loading and Entry Points
- System Calls and the Kernel Boundary
- Putting It All Together: A Process in Motion
Chapter 3: The ptrace System Call: Your Gateway to Debugging
- ptrace Fundamentals
- Process Stop States and Wait
- Reading and Writing Registers
- Reading and Writing Memory
- Single Stepping and Instruction Control
- ptrace Security and Permissions
- The Complete ptrace Workflow
Chapter 4: Executable Formats and DWARF Debug Information
- The ELF File Format
- Symbol Tables and Relocations
- DWARF Debug Information Overview
- Line Number Program
- Variable Locations and Expressions
- Reading DWARF in Rust with gimli
- DWARF Version Differences
- Putting It Together: From Address to Source Line
Chapter 5: Project Setup and the Debugger Shell
- Project Structure and Dependencies
- The Debugger Core Trait and Architecture
- Command Parsing
- Process Fork and Trace
- The First Debug Session
- The First Debug Session
Chapter 6: Breakpoints: Stopping at Will
- Software Breakpoints and INT3
- The Breakpoint Lifecycle
- Breakpoint Data Structures
- Handling Breakpoint Hits
- Multiple Breakpoints and Conditional Execution
- Testing Breakpoints
- Common Pitfalls
Chapter 7: Single Stepping and Register Inspection
- Single Stepping with PTRACE_SINGLESTEP
- The x86_64 Register File
- Reading Registers After a Stop
- Writing Registers
- The Step Over Problem
- Register Display Formatting
Chapter 8: Memory Inspection and Modification
- Reading Process Memory
- Memory Display Formats
- Handling Page Faults and Invalid Addresses
- The /proc/[pid]/mem Alternative
- Memory Region Discovery
- Writing Memory Safely
Chapter 9: Symbol Resolution and Source-Level Debugging
- Symbol Lookup
- Demangling C++ and Rust Symbols
- Source File Discovery
- Displaying Source Code with Current Line Highlighting
- Step Over at the Source Level
- Frame Pointer Omission and Its Consequences
Chapter 10: Stack Unwinding and Call Frames
- The Call Stack Explained
- Frame Pointer-Based Unwinding
- DWARF Call Frame Information
- Displaying the Backtrace
- Local Variable Inspection
Chapter 11: Watchpoints, Hardware Breakpoints, and Advanced Tracing
- Hardware Debug Registers
- Setting Hardware Breakpoints
- The PTRACE_GETREGSET and NT_X86_DEBUGREGS Interface
- Limitations and Fallback Strategies
- Watchpoint Manager
- Tracepoints and Logging
Chapter 12: Multithreaded Debugging and Shared Libraries
- Thread Groups and ptrace
- PTRACE_SEIZE and Group Stop
- Debugging Existing Processes
- Shared Library Loading Events
- Symbol Resolution Across Shared Libraries
- Thread-Specific Data and Stack Layout
Chapter 13: Expression Evaluation and Variable Modification
- The Expression Evaluation Problem
- Building a Simple Expression Parser
- Type Resolution from DWARF
- The Set Command
- Limitations and Pragmatic Choices
Chapter 14: Signals, Exceptions, and Error Handling
- Signal Delivery During Tracing
- Distinguishing Breakpoint Signals from Real SIGTRAPs
- Signal Masking and Filtering
- Handling Common Signals
- Crash Dump Generation
- Debugger Error Handling
Chapter 15: Architecture, Testing, and Going Further
- The Complete Debugger Architecture
- Testing a Debugger
- Performance Considerations
- Portability: macOS ptrace and Windows Debug Active Process API
- Remote Debugging Concepts
- Extending Your Debugger
Conclusion: What We Built and What It Teaches Us
- The Layers of Understanding
- What Makes a Good Debugger?
- The Bigger Picture
- Final Thoughts
Appendix A: Complete Project Structure
- Directory Layout
- Module Dependencies
- Shared Types and Conventions
- Building and Running
- Testing Strategy
- Companion Repository