on 2025-08-07 7:30 AM
This article explains how to debug an external *.elf file in Visual Studio Code (VS Code) using the STM32CubeExtension, without requiring a CMake project or a full development environment.
Debugging embedded applications on STM32 microcontrollers is an essential step to ensure firmware functionality. The *.elf (executable and linkable format) file is a key factor in this process. It contains the compiled program along with important debugging information, such as symbol names, variable locations, and source code mappings.
This article focuses on setting up VS Code with the STM32CubeExtension to directly debug an existing *.elf file. This approach allows you to configure the debugging interface properly and leverage the STLINK debugger without requiring a complete project setup.
This article assumes that the following tools and files are installed and available:
Edit the generated launch.json file to support debugging with all features enabled. Here is an example configuration for a NUCLEO-G0B1RE:
To facilitate, you can copy/paste and edit from here:
{
"version": "0.2.0",
"configurations": [
{
"type": "stlinkgdbtarget",
"request": "launch",
"name": "STM32Cube: STM32 Launch ST-Link GDB Server",
"origin": "snippet",
"cwd": "${workspaceFolder}",
"runEntry": "main",
"deviceName": "STM32G0B1RE", // Replace with your MCU model
"deviceCore": "Cortex-M0+", // Replace with your MCU Core
"svdPath": "C:/ST/STM32CubeCLT_1.18.0/STMicroelectronics_CMSIS_SVD/STM32G0B1.svd",
// Replace with your .svd path and board
"imagesAndSymbols": [
{
"imageFileName": "${workspaceFolder}/STM32G0_GPIOToggle.elf", // Replace this with your path and your_project.elf image
"symbolFileName": "${workspaceFolder}/STM32G0_GPIOToggle.elf" // Replace this with your path and your_project.elf symbols
}
]
}
]
}
Explanation of the changes:
Since you are debugging an external *.elf file without a full project or build system, there is no need to trigger a build before debugging.
Specifying "deviceName" (for example, STM32G0B1RE) and "deviceCore" (for example, Cortex-M0+) ensures correct initialization, memory mapping, and peripheral support during debugging.
The SVD (system view description) file provides detailed information about the MCU’s peripherals and registers. Including the path to the SVD file enables the STM32CubeExtension to show peripheral registers in the debugger UI, improving visibility and control.
“C:/ST/STM32CubeCLT_1.18.0/STMicroelectronics_CMSIS_SVD/your_board.svd”
The program, imageFileName, and symbolFileName fields now directly reference your *.elf file in the workspace. This is necessary for standalone debugging, as there is no project context to resolve these paths automatically.
For advanced options, use the detailed version of the launch configuration provided by the STM32CubeExtension:
Edit the generated configuration to support debugging with all features. You can follow the example below for a NUCLEO-G0B1RE.
Before
{
"version": "0.2.0",
"configurations": [
{
"type": "stlinkgdbtarget",
"request": "launch",
"name": "STM32Cube: STM32 Launch ST-Link GDB Server (detailed)",
"origin": "snippet",
"cwd": "${workspaceFolder}",
"preBuild": "${command:st-stm32-ide-debug-launch.build}",
"program": "${command:st-stm32-ide-debug-launch.get-projects-binary-from-context1}",
"gdb": "${command:st-stm32-ide-debug-launch.get-gdb-executable}",
"serverExe": "ST-LINK_gdbserver",
"serverCubeProgPath": "${command:st-stm32-ide-debug-launch.get-programmer-path}",
"serverInterface": "SWD",
"serverInterfaceFrequency": "Auto",
"serverVerify": false,
"serverApID": "${command:st-stm32-ide-debug-launch.get-access-port}",
"serverReset": "Connect under reset",
"serverHost": "localhost",
"serverPort": "61234",
"serverCwd": "${command:st-stm32-ide-debug-launch.get-server-path}",
"runEntry": "main",
"imagesAndSymbols": [
{
"imageFileName": "${command:st-stm32-ide-debug-launch.get-projects-binary-from-context1}",
"imageOffset": "",
"symbolFileName": "${command:st-stm32-ide-debug-launch.get-projects-binary-from-context1}",
"symbolOffset": ""
}
],
"faultDivByZero": true,
"faultUnalignedAccess": true,
"faultHaltOnException": true,
"verbose": true,
"openGdbConsole": false
}
]
}
After
"version": "0.2.0",
"configurations": [
{
"type": "stlinkgdbtarget",
"request": "launch",
"name": "STM32Cube: STM32 Launch ST-Link GDB Server (detailed)",
"origin": "snippet",
"cwd": "${workspaceFolder}",
"program": "${workspaceFolder}/STM32G0_GPIOToggle.elf",
"gdb": "${command:st-stm32-ide-debug-launch.get-gdb-executable}",
"serverExe": "ST-LINK_gdbserver",
"serverCubeProgPath": "${command:st-stm32-ide-debug-launch.get-programmer-path}",
"serverInterface": "SWD",
"serverInterfaceFrequency": "Auto",
"serverVerify": false,
//"serverApID": "${command:st-stm32-ide-debug-launch.get-access-port}",
"serverReset": "Connect under reset",
"serverHost": "localhost",
"serverPort": "61234",
"serverCwd": "${command:st-stm32-ide-debug-launch.get-server-path}",
"runEntry": "main",
"deviceName": "STM32G0B1RE", // Replace with your MCU model
"deviceCore": "Cortex-M0+", // Replace with your MCU Core
"svdPath": "C:/ST/STM32CubeCLT_1.18.0/STMicroelectronics_CMSIS_SVD/STM32G0B1.svd", // Replace with your .svd path and board
"imagesAndSymbols": [
{
"imageFileName": "${workspaceFolder}/STM32G0_GPIOToggle.elf", // Replace with your path and your_project.elf
"imageOffset": "",
"symbolFileName": "${workspaceFolder}/STM32G0_GPIOToggle.elf", // Replace with your path and your_project.elf
"symbolOffset": ""
}
],
"faultDivByZero": true,
"faultUnalignedAccess": true,
"faultHaltOnException": true,
"verbose": true,
"openGdbConsole": false
}
]
}
Explanation of the changes:
Since you are working with an external *.elf file, there is no need to trigger a build step before starting the debugger.
The serverApID is commented out because it is not needed for this workflow and leaving it active could cause confusion or errors.
Adding "deviceName": "STM32G0B1RE" and "deviceCore": "Cortex-M0+" tells the debugger exactly which STM32 microcontroller and core you are using. This ensures proper initialization, memory mapping, and peripheral support, which is especially important when not using a full project environment.
The SVD (system view description) file path is now specified (for STM32G0B1RE). This enables the debugger to display peripheral registers and memory-mapped hardware features in the UI, improving your ability to inspect and control the device during debugging.
“C:/ST/STM32CubeCLT_1.18.0/STMicroelectronics_CMSIS_SVD/your_board.svd”
The program, imageFileName, and symbolFileName fields now directly reference your *.elf file in the workspace. This is necessary for standalone debugging, as there is no project context to resolve these paths automatically.
When debugging with the STM32CubeExtension using the “stlinkgdbtarget” type, you may encounter an issue where the “Memory Viewer” in VS Code does not display memory contents or fails to update during your debug session.
By default, the MemoryView extension may not include "stlinkgdbtarget" in its tracked debuggers in the settings.json file. Without this, the debugger cannot properly read memory, causing the memory viewer to remain empty or stale.
To resolve this:
"memory-view.trackDebuggers": [
"stlinkgdbtarget",
],
Debugging an external *.elf file in VS Code with the STM32CubeExtension and STLINK is a simple and efficient way to work with STM32 firmware. With the right configuration, you can use all essential debugging features without needing a full project or build system, making your workflow faster and more flexible.