VSCode tooling for HALucinator firmware emulation — config editing, DAP debugging, and project management.
Published at: https://github.com/GrammaTech/halucinator-vscode
| Branch | Purpose |
|---|---|
main |
Active development. Unified extension — both DAP and GDB debug backends, selectable per-project. |
archive/gtirb-main |
Frozen snapshot of the pre-unified extension with GTIRB support. |
archive/gview-only |
Frozen snapshot of the DAP-only variant (no GTIRB). Superseded by main. |
archive/gdb-debug-mode |
Frozen snapshot of the GDB-only variant. Superseded by main. |
archive/* branches are reference-only and don't receive updates. The unified extension on main lets you choose DAP or GDB via the halucinator.debugBackend setting or the Debug Backend dropdown in the project editor. You don't need to pick a branch — main is the one to use.
halucinator-vscode/
├── extensions/
│ ├── halucinator/ # Main extension (config, debug, project, runtime)
│ └── gview-extension/ # Ghidra assembly viewer
- VSCode 1.80+
- Node.js 20+ (for building)
- HALucinator installed locally (
pip install -e src) or available as a Docker image - QEMU built for your target architecture (via halucinator's
build_qemu.sh)
cd extensions/halucinator
npm install
npm run build # Compiles TypeScript + bundles with esbuildThis produces halucinator-vscode-1.0.0.vsix.
# Install the main extension
code --install-extension extensions/halucinator/halucinator-vscode-1.0.0.vsix
# Install required dependency
code --install-extension redhat.vscode-yaml
# Install gview extension (for Ghidra assembly disassembly)
cd extensions/gview-extension
npm install
npx @vscode/vsce package --allow-missing-repository
code --install-extension gview-extension-1.0.0.vsix
cd ../..This walkthrough uses the STM32 UART test included in the halucinator repo.
# Clone and install halucinator (if not already done)
git clone https://github.com/halucinator/halucinator.git
cd halucinator
git submodule update --init --recursive
# Create virtualenv and install
python3 -m venv venv
source venv/bin/activate
pip install -e deps/avatar2/
pip install -r src/requirements.txt
pip install -e src
# Build QEMU (ARM)
./build_qemu.sh arm-softmmuexport HALUCINATOR_QEMU_ARM="$(pwd)/deps/build-qemu/arm-softmmu/qemu-system-arm"code test/STM32/example/- Open the Command Palette (
Ctrl+Shift+P) - Run "HALucinator: Open/Create Project"
- In the project editor that opens:
- Docker Image: Leave empty (we're using local mode)
- ELF Binary: Click "Browse..." and select
Uart_Hyperterminal_IT_O0.elf - gview File: Click "Generate" to auto-create from the ELF using Ghidra (~1-2 min)
Or click "Browse..." if you already have a
.gviewfile. - Configuration Files: Click "Add Config File(s)..." and select all three:
Uart_Hyperterminal_IT_O0_config.yamlUart_Hyperterminal_IT_O0_memory.yamlUart_Hyperterminal_IT_O0_addrs.yaml
- Click "Save Project"
This creates a halucinator.json file in the project directory.
Open VSCode Settings (Ctrl+,) and search for "halucinator":
- Mode:
auto(default) — automatically detects Docker or local install - DAP Port:
34157(default)
In the project editor, click "Start Debugging". This single button:
- Launches halucinator with
--dapand your config files - Waits for the DAP server on port 34157
- Opens the gview disassembly file
- Starts the VSCode debug session
The debug toolbar appears — you can step, continue, inspect variables, set breakpoints in the gview assembly.
Look at the bottom panel — the "HALucinator" tab shows:
- Config Files: The three YAML files you loaded
- Intercepts: All function intercepts (HAL_UART_Init, HAL_Delay, etc.)
- Add Intercept: Form to add new intercepts with handler class dropdown
In a separate terminal, open an interactive UART console using the built-in peripheral tool:
source /path/to/halucinator/venv/bin/activate
hal_dev_uart --id=0x40011000 --newlineThis opens a live UART terminal — type input and see firmware responses. The --id is the UART peripheral address from the memory config.
Alternatively, you can script it with Python:
from halucinator.external_devices.ioserver import IOServer
from halucinator.external_devices.uart import UARTPrintServer
import time
io = IOServer(5556, 5555)
uart = UARTPrintServer(io)
io.start()
time.sleep(3)
uart.send_data(0x40013800, '1234567890')
time.sleep(15)
io.shutdown()If you're running halucinator inside Docker:
cd halucinator
docker build -t halucinator:latest .In VSCode Settings (Ctrl+,), search for "halucinator":
- Mode:
docker - Docker Image:
halucinator:latest - Container Name:
halucinator(default)
code /path/to/your/firmware/project/Ctrl+Shift+P→ "HALucinator: Open/Create Project"- Set Docker Image to
halucinator:latest - Browse for your ELF binary and config files
- Click "Save Project"
Ctrl+Shift+P→ "HALucinator: Start Container" (starts the Docker container)Ctrl+Shift+H→ Opens an interactive shell inside the containerCtrl+Shift+P→ "HALucinator: Start Debugger" (launches halucinator with --dap)Ctrl+Shift+P→ "Debug with HALucinator" (connects VSCode to the DAP server)
| Command | Shortcut | Description |
|---|---|---|
| HALucinator: Open/Create Project | Open the project editor webview | |
| HALucinator: Start Debugger | Launch halucinator (Docker or local) | |
| HALucinator: Stop Debugger | Stop running halucinator instance | |
| HALucinator: Open Shell | Ctrl+Shift+H |
Open interactive terminal (Docker: shell in container) |
| HALucinator: Start Container | Start the Docker container (Docker mode only) | |
| Debug with HALucinator | Connect VSCode debugger to DAP server | |
| HALucinator: Import Config | Add existing YAML config files | |
| HALucinator: Create Config | Create a new config file from template | |
| HALucinator: Refresh Handler Class List | Re-scan for available bp_handler classes | |
| HALucinator: Add Handler Directory | Add custom handler search directory (Docker) | |
| HALucinator: Reset Handler Directories | Remove all custom handler directories (Docker) | |
| HALucinator: Reload Configuration | Reload Docker/tool settings |
| Setting | Default | Description |
|---|---|---|
halucinator.mode |
auto |
Runtime mode: auto, docker, or local |
halucinator.dockerImage |
(empty) | Docker image name (empty = halucinator:latest) |
halucinator.containerName |
halucinator |
Docker container name |
halucinator.dapPort |
34157 |
Debug Adapter Protocol server port |
halucinator.project.fileName |
halucinator.json |
Project config file name |
halucinator.project.dap.enabled |
true |
Pass --dap flag when launching halucinator |
When connected to the HALucinator DAP server:
- Breakpoints: Set in assembly listings (gtgas, gtmips, gview languages)
- Step/Continue/Pause: Standard debug controls
- Variable inspection: View registers, stack, and global memory
- Variable formatting: Right-click variables to change display format (hex, signed, unsigned, arrays, pointers, different encodings)
- HAL break toggle: Toolbar button to pause/continue on HAL intercepts
- Memory inspection: Read arbitrary memory addresses
HALucinator uses YAML config files with these sections:
# Machine configuration
machine:
arch: cortex-m3
cpu_model: cortex-m3
entry_addr: 0x08003D49
init_sp: 0x20014000
# Memory regions
memories:
flash: { base_addr: 0x8000000, file: firmware.bin, permissions: r-x, size: 0x200000 }
ram: { base_addr: 0x20000000, size: 0x51000 }
# Function intercepts
intercepts:
- class: halucinator.bp_handlers.ReturnZero
function: HAL_Init
symbol: HAL_Init
- class: halucinator.bp_handlers.stm32f4.stm32f4_uart.STM32F4UART
function: HAL_UART_Init
symbol: HAL_UART_InitThe extension provides YAML schema validation for these files — you'll get autocomplete and error highlighting.
VSCode
├── HALucinator Extension (gt-halucinator.halucinator-vscode)
│ ├── Config Editor Panel (bottom panel webview)
│ ├── Project Editor (webview panel)
│ ├── DAP Client ──────────── TCP:34157 ──── HALucinator DAP Server
│ ├── Docker Runner ──────── docker exec ──── Container
│ │ or Local Runner ────── direct exec ──── halucinator CLI
│ ├── Tool Base ──────────── commands ──────── gview-extension
│ └── Handler Autocomplete ── reads ────────── .hal_bp_handlers.json
│
└── gview-extension
└── GView LSP Server ──── stdio ──────────── Python pygls server
"DAP server not reachable on port 34157"
- Make sure halucinator is running with
--dapflag - Check that nothing else is using port 34157
- In Docker mode, ensure
--network=hostis used
"No config files loaded" in the Intercepts panel
- Import config files:
Ctrl+Shift+P→ "HALucinator: Import Config" - Or open a project:
Ctrl+Shift+P→ "HALucinator: Open/Create Project"
Handler autocomplete not working
- Run
Ctrl+Shift+P→ "HALucinator: Refresh Handler Class List" - Check that
.hal_bp_handlers.jsonexists in your project directory
Docker container won't start
- Verify the Docker image exists:
docker image inspect halucinator:latest - Check the container name isn't already in use:
docker ps -a
