OpenOCD
Open On-Chip Debugger: bridge a JTAG/SWD probe to GDB and a Telnet console to halt an MCU, read its memory and flash, and step through code.
OpenOCD (Open On-Chip Debugger) bridges a JTAG or SWD hardware probe to the rest of your toolchain. It exposes a GDB server and a Telnet console onto a target microcontroller, letting you halt the CPU, read and write its memory and flash, set breakpoints, and step through code, the software half of on-chip debugging. It is the piece that turns "there are debug pads on this board" into an interactive session.
What it is
JTAG and SWD are hardware debug interfaces built into most microcontrollers: a few pins that let an external probe stop the core and inspect it. OpenOCD is the daemon that speaks those interfaces through a probe (an FT2232-based adapter, an ST-Link, a J-Link, or even bit-banged Raspberry Pi GPIO) and re-exports them as standard, tool-friendly endpoints, GDB on TCP 3333 and a Telnet console on 4444. You describe your setup with two config files: an interface config for the probe and a target config for the chip.
Why it matters
On a hardware target the debug port is often the shortest path to the firmware. If the chip's read-out protection is not fully set, OpenOCD can dump internal flash straight off the die, memory that esptool or an external SPI clip never sees because it lives inside the MCU. It also lets you halt a running system, read RAM to recover keys and state, patch memory live, and single-step to understand a protection check. Where a UART gives you whatever the firmware chooses to print, JTAG gives you the silicon.
How to use it
openocd -f interface/stlink.cfg -f target/stm32f1x.cfg # start the server (interface + target)
# In another shell, drive it over the Telnet console on :4444
telnet localhost 4444
> halt
> dump_image firmware.bin 0x08000000 0x10000 # dump 64 KB of internal flash
> flash write_image erase patched.bin 0x08000000 # reflash
# Or attach GDB to the :3333 server instead
arm-none-eabi-gdb -ex "target remote :3333" -ex "monitor halt"
The pattern is: start openocd with the right interface and target configs, confirm it finds the scan chain, then drive it from either the Telnet console (quick halt/dump_image) or GDB (breakpoints, stepping, symbolic debugging). dump_image <file> <addr> <size> is the workhorse for pulling firmware; flash write_image puts it back.
Pitfalls
- If
dump_imagereturns all0xFFor all0x00, read-out protection (STM32 RDP, other vendors' CRP) is almost certainly enabled, the chip is answering but refusing to disclose flash. - The interface and target
.cfgmust both be correct, or OpenOCD never finds the scan chain. A wrong target config can also hang or brick a session. - Long or noisy leads make JTAG scans flaky. Keep TCK short, share ground, and drop the adapter speed (
adapter speed 1000) if the chain is unreliable. - SWD and JTAG are not interchangeable pins. Many modern parts expose only SWD (SWCLK/SWDIO); pick the matching transport in the interface config.
What it pairs with
A jtag-swd-probe is the hardware OpenOCD drives, and jtag-swd-pads covers finding the debug points on an unlabelled board. Once you have a firmware dump, hand it to binwalk to carve it and to ghidra or radare2 to reverse it. For deeper debugging you attach gdb to the OpenOCD server and work symbolically.