Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

pidb — a bare-metal runtime debugger for the Raspberry Pi

pidb is a debugger for bare-metal ARM programs that have no OS, no gdbserver, and no debug port to attach to. It runs on a second Raspberry Pi, wired directly to the target board, and takes control of the running target by hijacking a GPIO interrupt and injecting machine code over a bit-banged serial link.

Once attached, you can read and write arbitrary memory, inspect and drive GPIO pins, single-step through code that has no stepping support, and soft-restart the target — all from a menu-driven UI on an LCD, navigated with an IR remote.

(need to add photo/demo here still)


The problem

On a bare-metal target there is nothing to attach a debugger to. No OS means no ptrace, no gdbserver, no signals, no runtime to ask questions of. If the program misbehaves, your options are printk and a reboot.

pidb solves this from the outside: a second Pi acts as the debugger, drives an interrupt line into the target, and ships executable code to it on demand.


Architecture

   ┌──────────────────┐                              ┌──────────────────┐
   │   DEBUGGER PI    │  doorbell (GPIO 20) ───────▶ │    TARGET PI     │
   │                  │                              │                  │
   │  ST7735R LCD     │  bit-banged UART             │  GPIO interrupt  │
   │  IR remote       │  TX 16 / RX 19  ◀──────────▶ │  handler         │
   │  menu state      │  packet protocol             │  JIT exec buffer │
   └──────────────────┘                              └──────────────────┘

1. Interrupt hijacking. The debugger asserts a GPIO "doorbell" line (pin 20). The target's GPIO interrupt fires, yanking it out of whatever it was doing and into a debug handler that parks it, waiting for commands. No cooperation from the target program is required beyond linking the handler.

2. Bit-banged UART + packet protocol. The two Pis talk over a software UART on GPIO 16/19 (the hardware UART is left free). A small framed protocol carries commands and responses:

enum {
    PKT_READY    = 0x4e,
    PKT_ACK      = 0x4c,
    PKT_RESP     = 0x4d,
    PKT_CMD_JIT  = 0x01,
    PKT_CMD_MEM  = 0x02,
    PKT_CMD_GPIO = 0x03,
};

3. JIT code injection. Rather than implementing a command interpreter on the target, the debugger ships raw ARM machine code across the wire and the target executes it out of a buffer. Each operation is a precompiled blob:

Payload Purpose
get32_jit / put32_jit arbitrary memory read/write
gpio_set_on_jit / gpio_set_off_jit drive a pin
gpio_set_mode_jit / gpio_get_mode_jit configure / query pin mode
jit_read_cpsr_code processor status
jit_read_dfsr_code / jit_read_dfar_code data fault status / address
jit_read_ifsr_code / jit_read_ifar_code instruction fault status / address
soft_restart_jit restart the target in place
minimal_step_payload single-step engine (see below)

Because a fixed blob can't be recompiled per call, argument_injector() patches the arguments directly into the machine code before it goes over the wire — rewriting the immediate operands so a static payload can be invoked with dynamic values:

int argument_injector(uint32_t *jit_code, uint32_t words_n,
                      uint32_t *args,     uint32_t args_n,
                      uint32_t *out);

4. Single-stepping without stepping support. ARM's CP14 debug coprocessor supports mismatch breakpoints: set the breakpoint value register to the current PC and configure the breakpoint control register to fire on any instruction that is not that address. The next instruction executed traps. Chain that and you have single-stepping on hardware that has no single-step mode.

The stepping payload is compiled standalone and embedded as a byte array:

arm-none-eabi-gcc -O2 -static -nostdlib -march=armv6k -marm -T payload.ld minimal-step.c \
    -o minimal-step-payload.elf
arm-none-eabi-objcopy -O binary --only-section=.text minimal-step-payload.elf minimal-step-payload.bin
xxd -i minimal-step-payload.bin > minimal-step-payload.h

5. The UI. A full-screen interface on an ST7735R LCD over SPI, with a custom graphics and font library. Navigation is by IR remote — a hand-rolled NEC-protocol decoder on GPIO 21 that times pulse widths against the mark/space thresholds and maps them to key codes.

Screens: main menu, GPIO dashboard (live pin states, editable), memory viewer (browse and patch arbitrary addresses), memory address entry, single-step control, and reset.


Features

  • Memory read/write at arbitrary addresses on a running target
  • GPIO inspection and control — live pin state dashboard, toggle pins, change modes
  • Single-stepping via CP14 mismatch breakpoints, on targets with no stepping support
  • Hardware breakpoints and watchpoints (CP14 BVR/BCR, DSCR)
  • Fault register inspection — CPSR, DFSR, DFAR, IFSR, IFAR
  • Soft restart of the target without a power cycle
  • Self-debug mode (self = 1 in main.c) — the debugger can attach to itself for development

Hardware

Debugger Raspberry Pi (ARM1176, bare metal)
Target Raspberry Pi (ARM1176, bare metal)
Display ST7735R LCD over SPI
Input IR receiver on GPIO 21 (NEC protocol)
Link Bit-banged UART, TX GPIO 16 / RX GPIO 19
Doorbell GPIO 20 (debugger → target interrupt line)

Layout

Path Contents
debugger/main.c UI state machine, top-level debugger loop
debugger/jdebug.c / jtarget.c debugger-side and target-side entry points
debugger/comms/ packet protocol over the software UART
debugger/jit-lib/, jit-finder.c JIT payloads and the runtime argument injector
debugger/debugger-lib/ breakpoints, watchpoints, single-stepping engine
debugger/ir-driver/ IR receiver and NEC decoder
debugger/display-driver/, graphics-lib/, font/ ST7735R driver and UI rendering
debugger/interrupts.c, interrupt-asm.S interrupt vectors and handlers

Attribution

Everything under debugger/ is original work, written jointly by Joe Robertazzi and Julian Reed as a two-person project for Stanford's CS240LX.

The support libraries are not our work and are vendored here only so the project builds:

  • libpi/ (including libpi/staff-src/) — course support library from Stanford CS240LX.
  • lib/libm/openlibm, vendored unmodified.

About

Bare-metal runtime debugger for ARM/Raspberry Pi - hardware breakpoints, watchpoints, and single-stepping via GPIO interrupt hijacking and JIT code injection.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages