Skip to content

STM32 »

ARM Semihosting - native but slow Debugging

ARM Semihosting is a distinctive feature of the ARM platform, that allows to use input and output functions on a host computer that get forwarded to the microcontrollers over a hardware debugger. It is helpful when there is no input/output interface dedicated for logging on the target MCU.

Last update: 2022-06-29


Semihosting setup

  1. Connect a debugger via SWD interface
  2. Include Semihosting library in GCC Linker: -l rdimon --specs=rdimon.specs. Exclude the default syscall.c implementation if needed
  3. Call initialise_monitor_handles() at the begining of the main function
  4. Run OpenOCD with command monitor arm semihosting enable


  • Semihosting implementation in OpenOCD is designed so that every string must be terminated with the newline character \n before the string appears on the OpenOCD console

  • Semihosting only works during a debug session, and it’s slow and affects the system performance

  • CPU is halt when Semihosting is executing in host machine, therefore Semihosting is not suitable for realtime application


There are some debug techniques used to inspect the firmware running on ARM-based MCUs:

  • Semihosting: built-in to every ARM chips, need adding additional library and running in debug mode.

  • Console log: forward to a native UART port, a Virtual COM port through a USB port.

  • Serial Wire View (SWV): fast output over dedicated Single Wire Output (SWO) pin, but it’s only available on Cortex-M3+, and this is uni-direction communication.

  • Real Time Transfer (RTT): extremely fast but only work with SEGGER Debugger, can have a real-time bi-direction communication.

Ways to print debug


ARM Semihosting is a distinctive feature of the ARM platform, that allows to use input and output functions on a host computer that get forwarded to the microcontrollers over a hardware debugger, by hooking into I/O functions, such as printf() and scanf(), or even fopen().

Semihosting is implemented by a set of defined software instructions, for example, SVC, that generate exceptions from program control. The application invokes the appropriate Semihosting call and the debugger then handles the exception by communicating with the debugging application on the host computer.

ARM processors prior to ARMv7 use the SVC instructions, formerly known as SWI instructions, to make Semihosting calls. However, for an ARMv6-M or ARMv7-M, in a Cortex-M1 or Cortex-M3 processor, Semihosting is implemented using the BKPT instruction.

Semihosting overview

Hardware setup#

Semihosting need to be run under a debug session to communicate with Semihosting-enabled debugger, such as OpenOCD. In STM32, debugging channel maybe ST-LINK debugger (onboard, or external) which connects to the MCU via SWCLK and SWDIO in the SWD interface.

Linker options#

To use Semihosting, it has to be set in the linker options, and initialized in the main program.

Standard C libraries

GNU ARM libraries use newlib to provide standard implementation of C libraries. To reduce the code size and make it independent to hardware, there is a lightweight version newlib-nano used in MCUs.

However, newlib-nano does not provide an implementation of low-level system calls which are used by C standard libraries, such as print() or scan(). To make the application compilable, a new library named nosys should be added. This library just provide a simple implementation of low-level system calls which mostly return a by-pass value.

The lib newlib-nano is enabled via linker options --specs=nano.specs, and nosys is enabled via linker option --specs=nosys.specs. These two libraries are included by default in GCC linker options in generated project, check it here.

Rdimon library

There is a rdimon library that implements interrupt for some special system calls, which pauses the processor and interacts with the host debugger to exchange data, such as SYS_WRITE (0x05) or SYS_READ (0x06). This library provides low-level system calls to handling the newlib-nano specs.

The lib rdimon is enabled via linker option -l rdimon --specs=rdimon.specs

STM32CubeIDE automatically generates syscalls.c with a simple implementation for nosys.specs. However, it conflicts with the implementation in rdimon.specs.

Exclude syscalls.c from the build to avoid compilation error of multiple definitions.

Add Semihosting in GCC Linker

Exclude syscalls.c

Initialize Semihosting#

The rdimon library has to be initialized before it can run properly. It exposes a function to do that, then use it:

extern void initialise_monitor_handles(void);

int main(void) {

After that, the application can use printf(), scanf(), or gets().

Debugger option#

The final thing is to enable Semihosting on debugger that will handle the interruptions fired from MCUs. We can select ST-LINK (OpenOCD) to use OpenOCD through ST-Link debugger. To start Semihosting in the debugger process, add below command in the Startup commands option:

monitor arm semihosting enable

Enable Semihosting in debugger

Debug with Semihosting#

Run the project in debug mode and then interact with MCUs. Here are some lines of code to print a message, get a string, and write to a file on the host machine:

#include <stdio.h>
#include <string.h>

extern void initialise_monitor_handles(void);

char buffer[255];

int main(void) {


    printf("Please enter your name: \n");
    printf("\nAh, I know you, %s!\n", buffer);

    // test.out will be created in the host machine
    FILE *fd= fopen("D:\\test.out", "w+");
    if(fd) {
        fwrite(buffer, sizeof(char), strlen(buffer), fd);

    uint8_t counter = 0;
    while (1) {
        printf("counter = %d\n", counter++);

Interact with Semihosting

Using files

When using fopen(), the file path is specified either as relative to the current directory of the host process (e.g. openocd.exe), or absolute, using the path conventions of the host operating system. Read more in here.