Skip to content

OpenOCD Cheat Sheet

Overview

OpenOCD (Open On-Chip Debugger) is a free, open-source on-chip debugging, in-system programming, and boundary-scan testing tool for embedded systems. It provides a bridge between a debug adapter (JTAG/SWD probe) and GDB, enabling developers and security researchers to halt processors, set breakpoints, inspect memory, program flash, and perform low-level hardware debugging. OpenOCD supports hundreds of target devices including ARM Cortex-M/A/R, MIPS, RISC-V, ESP32, and various FPGAs, along with dozens of debug adapter hardware from SEGGER, FTDI, ST-LINK, CMSIS-DAP, and more.

OpenOCD operates as a daemon that listens for connections from GDB (port 3333), telnet (port 4444), and TCL (port 6666). It translates high-level debug commands into the appropriate JTAG/SWD sequences for the target hardware. The tool is extensively used in embedded development workflows with IDEs like VS Code, Eclipse, and PlatformIO, as well as in security research for firmware extraction, reverse engineering, and hardware hacking. OpenOCD’s Tcl-based scripting engine allows complex automation of debug and test procedures.

Installation

Package Manager

# Ubuntu/Debian
sudo apt update
sudo apt install openocd

# CentOS/RHEL
sudo yum install openocd

# macOS
brew install openocd

# Arch Linux
sudo pacman -S openocd

# Verify
openocd --version

From Source (Latest)

git clone https://git.code.sf.net/p/openocd/code openocd
cd openocd
./bootstrap
./configure --enable-ftdi --enable-stlink --enable-jlink --enable-cmsis-dap
make -j$(nproc)
sudo make install

# Install udev rules (Linux)
sudo cp contrib/60-openocd.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules

Quick Start

Basic Connection

# Connect to STM32 via ST-LINK
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg

# Connect via J-Link
openocd -f interface/jlink.cfg -f target/stm32f4x.cfg

# Connect via FTDI adapter
openocd -f interface/ftdi/tigard.cfg -f target/stm32f1x.cfg

# Connect via CMSIS-DAP
openocd -f interface/cmsis-dap.cfg -f target/stm32f4x.cfg

# Use SWD instead of JTAG
openocd -f interface/stlink.cfg -c "transport select hla_swd" -f target/stm32f4x.cfg

# Custom adapter speed
openocd -f interface/jlink.cfg -c "adapter speed 4000" -f target/nrf52.cfg

One-Shot Commands

# Program flash and exit
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg \
  -c "program firmware.elf verify reset exit"

# Program binary at specific address
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg \
  -c "program firmware.bin 0x08000000 verify reset exit"

# Program hex file
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg \
  -c "program firmware.hex verify reset exit"

# Dump firmware
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg \
  -c "init" -c "reset halt" \
  -c "flash read_image dump.bin 0x08000000 0x100000" \
  -c "shutdown"

# Erase flash
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg \
  -c "init" -c "reset halt" \
  -c "flash erase_sector 0 0 last" \
  -c "shutdown"

Telnet Interface

# Connect to OpenOCD telnet (after starting daemon)
telnet localhost 4444

Target Control

CommandDescription
haltHalt the target processor
resumeResume execution
resume 0x08000000Resume at specific address
stepSingle-step one instruction
step 0x08001000Step at specific address
resetReset target
reset haltReset and halt at reset vector
reset initReset and run init scripts
reset runReset and run
regShow all registers
reg pcShow program counter
reg spShow stack pointer
reg r0 0x1234Set register value

Memory Access

# Read memory
mdw 0x08000000          # Read word (32-bit)
mdw 0x08000000 16       # Read 16 words
mdh 0x08000000 8        # Read halfwords (16-bit)
mdb 0x08000000 64       # Read bytes

# Write memory
mww 0x20000000 0xDEADBEEF   # Write word
mwh 0x20000000 0x1234       # Write halfword
mwb 0x20000000 0x42         # Write byte

# Dump memory to file
dump_image firmware.bin 0x08000000 0x100000
dump_image ram.bin 0x20000000 0x20000

# Load file to memory
load_image firmware.bin 0x08000000
load_image data.bin 0x20000000

# Verify memory against file
verify_image firmware.bin 0x08000000

Flash Operations

# Flash information
flash info 0
flash banks
flash list

# Erase flash
flash erase_sector 0 0 last        # Erase all sectors
flash erase_sector 0 0 3           # Erase sectors 0-3
flash erase_address 0x08000000 0x10000  # Erase by address

# Program flash
flash write_image erase firmware.bin 0x08000000
flash write_image erase firmware.elf

# Verify flash
flash verify_image firmware.bin 0x08000000

# Protect/unprotect flash
flash protect 0 0 3 on
flash protect 0 0 3 off

# Mass erase
flash erase_check 0

Breakpoints and Watchpoints

# Hardware breakpoint
bp 0x08001234 4 hw
bp 0x08001234 2 hw          # Thumb instruction

# Software breakpoint
bp 0x08001234 4

# Remove breakpoint
rbp 0x08001234

# Watchpoint (break on memory access)
wp 0x20000100 4 r           # Read watchpoint
wp 0x20000100 4 w           # Write watchpoint
wp 0x20000100 4 a           # Access watchpoint

# Remove watchpoint
rwp 0x20000100

GDB Integration

Connecting GDB

# Start OpenOCD in one terminal
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg

# Connect GDB in another terminal
arm-none-eabi-gdb firmware.elf
(gdb) target remote localhost:3333
(gdb) monitor reset halt
(gdb) load
(gdb) break main
(gdb) continue

# Or connect without ELF
arm-none-eabi-gdb
(gdb) target remote localhost:3333
(gdb) monitor halt

Common GDB + OpenOCD Commands

(gdb) monitor reset halt        # Reset and halt
(gdb) monitor reset init        # Reset and init
(gdb) load                      # Flash the ELF file
(gdb) monitor flash write_image erase firmware.bin 0x08000000
(gdb) monitor mdw 0x08000000 4  # Read memory via OpenOCD
(gdb) monitor reg               # Show registers via OpenOCD
(gdb) info registers             # Show registers via GDB
(gdb) x/16xw 0x08000000        # Examine memory
(gdb) break main                # Set breakpoint
(gdb) continue                  # Continue execution
(gdb) next                      # Step over
(gdb) step                      # Step into
(gdb) print variable            # Print variable value
(gdb) backtrace                 # Show call stack

Configuration Files

Interface Configurations

# Custom FTDI interface
# my_ftdi.cfg
adapter driver ftdi
ftdi vid_pid 0x0403 0x6010
ftdi channel 0

ftdi layout_init 0x0008 0x000b
ftdi layout_signal nSRST -data 0x0020 -oe 0x0020
ftdi layout_signal nTRST -data 0x0010 -oe 0x0010

adapter speed 2000
transport select jtag

Target Configurations

# Custom target configuration
# my_target.cfg
source [find interface/ftdi/tigard.cfg]
transport select jtag
adapter speed 1000

# Define JTAG chain
jtag newtap mydevice cpu -irlen 4 -expected-id 0x2ba01477

# Define target
target create mydevice.cpu cortex_m -chain-position mydevice.cpu

# Flash configuration
flash bank myflash stm32f1x 0x08000000 0 0 0 mydevice.cpu

# Reset configuration
reset_config srst_only srst_nogate connect_deassert_srst

# Init procedure
$_TARGETNAME configure -event reset-init {
    adapter speed 4000
    # Additional init commands
}

Raspberry Pi as JTAG Adapter

# rpi_jtag.cfg
adapter driver bcm2835gpio

bcm2835gpio peripheral_base 0xFE000000  # Pi 4
# bcm2835gpio peripheral_base 0x3F000000  # Pi 3

# GPIO pin assignments
bcm2835gpio tck_num 25
bcm2835gpio tms_num 24
bcm2835gpio tdi_num 23
bcm2835gpio tdo_num 22
bcm2835gpio srst_num 18

adapter speed 1000
transport select jtag

Advanced Usage

RISC-V Debugging

openocd -f interface/jlink.cfg -f target/esp32c3.cfg
# or
openocd -f interface/ftdi/tigard.cfg \
  -c "adapter speed 1000" \
  -c "transport select jtag" \
  -f target/gd32vf103.cfg

Multi-Core Debugging

# Dual-core target (e.g., STM32H7)
openocd -f interface/stlink.cfg -f target/stm32h7x_dual_bank.cfg

# In telnet:
targets                    # List all targets
targets stm32h7x.cpu0    # Select core 0
halt
targets stm32h7x.cpu1    # Select core 1
halt

Scripting with Tcl

# automation.tcl
proc dump_all_flash {} {
    halt
    flash read_image flash_dump.bin 0x08000000 0x100000
    echo "Flash dumped to flash_dump.bin"
}

proc program_and_verify {filename} {
    halt
    flash write_image erase $filename 0x08000000
    verify_image $filename 0x08000000
    echo "Programming complete and verified"
    reset run
}
# Run script
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg \
  -f automation.tcl \
  -c "init" -c "dump_all_flash" -c "shutdown"

RTOS Awareness

# Enable FreeRTOS thread awareness
$_TARGETNAME configure -rtos FreeRTOS

# In GDB:
# (gdb) info threads    # Shows FreeRTOS tasks
# (gdb) thread 2        # Switch to task 2

Semi-hosting

# Enable ARM semi-hosting (printf to debug console)
# In telnet:
arm semihosting enable

# printf() output appears in OpenOCD console
# Target code must use semihosting-compatible syscalls

Troubleshooting

IssueSolution
”Error: unable to open ftdi device”Check USB permissions, install udev rules
”JTAG scan chain interrogation failed”Check wiring, verify target power
”Target not halted”Issue halt command, check reset config
”Flash programming failed”Erase first, check flash protection
”Could not find MEM-AP”Try SWD instead of JTAG
”Adapter speed too fast”Reduce adapter speed value
”Error connecting DP”Check SWD wiring, add reset line
”No device found on JTAG chain”Verify JTAG vs SWD, check if JTAG disabled

Debug Tips

# Increase verbosity
openocd -d3 -f interface/stlink.cfg -f target/stm32f4x.cfg

# Log to file
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg \
  -l openocd.log -d2

# Check JTAG chain
# In telnet:
scan_chain
jtag names

# Check adapter
adapter speed
adapter list

# Test connection
# In telnet:
poll