cancel
Showing results for 
Search instead for 
Did you mean: 

How to debug an external .elf file in VS Code

B.Montanari
ST Employee

Summary

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.

Introduction

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.

Prerequisites

This article assumes that the following tools and files are installed and available:

1. Prepare your debugging environment

  • Create a new folder on your computer and place your compiled *.elf file inside it. In this case, the file is named: STM32G0_GPIOToggle.elf:
         BMontanari_0-1753989954047.png
  • Open this folder in Visual Studio Code by selecting [File] > [Open Folder].
         BMontanari_1-1753989954054.png
  • Ensure your *.elf file includes debug symbols, which are typically generated by default in debug builds.

2. Create debug configuration

  • Open the [Run and Debug] view (Ctrl+Shift+D).
  • Click [create a launch.json] file.
         BMontanari_2-1753989954054.png
  • Select [STM32Cube: STLink GDB Server] from the list of debuggers provided by STM32CubeExtension.
         BMontanari_3-1753989954055.png
  • The extension generates a default launch.json file with configurations tailored for STLINK debugging.

3. Modify launch.json

Edit the generated launch.json file to support debugging with all features enabled. Here is an example configuration for a NUCLEO-G0B1RE:

BMontanari_4-1753989954059.png

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:

  • Removed “prebuild” command:

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.

  • Explicit deviceName and deviceCore:

Specifying "deviceName" (for example, STM32G0B1RE) and "deviceCore" (for example, Cortex-M0+) ensures correct initialization, memory mapping, and peripheral support during debugging.

  • Added svdPath:

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”

  • Replaced dynamic program and symbol paths:

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.

4. Modify launch.json (detailed version)

For advanced options, use the detailed version of the launch configuration provided by the STM32CubeExtension:

  • Open the [Run and Debug] view (Ctrl+Shift+D).
  • Click [Add Configuration...]
         BMontanari_5-1753989954061.png
  • Select [STM32Cube: STM32 Launch ST-Link GDB Server (detailed)]
         BMontanari_6-1753989954065.png

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:

  • Removed preBuild command:

Since you are working with an external *.elf file, there is no need to trigger a build step before starting the debugger.

  • Commented out unused field:

The serverApID is commented out because it is not needed for this workflow and leaving it active could cause confusion or errors.

  • Explicit deviceName and deviceCore:

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.

  • Added svdPath:

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”

  • Replaced dynamic program and symbol paths:

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.

5. Start debugging

  • Connect your STM32 board to your PC using the STLINK debugger, the pop-up message appears at the lower right corner, click [Open DEVICES AND BOARDS view]:
         BMontanari_7-1753989954065.png
  • In VS Code, select the debug configuration you created, either the normal or detailed settings:
         BMontanari_8-1753989954066.png
  • Press F5 or click the green [Start Debugging] button.
  • The STM32CubeExtension automatically connects to your board, load the *.elf file, and halt at the main() function (if runEntry: “main”).
  • You can now:
    • Set breakpoints in your source code.
    • Step through your program.
    • Inspect variables, registers, and memory.
    • View call stacks.
BMontanari_9-1753989954073.png

6. Troubleshooting: memory viewer not updating

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:

  • Press Ctrl+Shift+P, type Preferences: Open  User Settings (JSON), and select it.
         BMontanari_10-1753989954074.png
  • Add the following configuration snippet:
  "memory-view.trackDebuggers": [
        "stlinkgdbtarget",
    ],
  • Save the file and restart VS Code or press Ctrl+Shift+P and issue the command to Reload Window:
         BMontanari_11-1753989954074.png

Conclusion

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.

Related links

Version history
Last update:
‎2025-08-06 10:37 AM
Updated by: