Default_Handler redirect ARM Bluepill

I want to redirect the hardFault on STM32F103 to the normal ‘debug’ function (as below). I used to use this code using CUBEMX project.
Currently I use the following ini file

[env:bluepill_f103c8]
platform = GitHub - platformio/platform-ststm32: ST STM32: development platform for PlatformIO
board = bluepill_f103c8
framework = arduino
board_build.core = stm32
monitor_speed = 115200
;monitor_port = /dev/ttyACM0
build_flags =
-D WATCHDOG_ON
-D PIO_FRAMEWORK_ARDUINO_ENABLE_CDC
-D USBCON
-D USBD_VID=0x0483
-D USB_MANUFACTURER=“Unknown”
-D USB_PRODUCT=“"ADD_PSU_F103C8"”
-D HAL_PCD_MODULE_ENABLED
-D UART_MODBUS=USART2
; Generate linker map in firmware.map and show memory usage.
-Wl,-Map,firmware.map,–print-memory-usage
; Output debug symbols for gdb debugger.
;-g
;-fexceptions

;lib_extra_dirs = ~/Documents/Arduino/libraries
upload_protocol = stlink
debug_tool = stlink
;debug_build_flags = -O0 -ggdb3 -g3
;build_flags = -fexceptions
debug_init_break = break setup

If I debug and force a ‘hardfault’ the debugger step into an assembler routine and loop for ever

Default_Handler:
Infinite_Loop:
b Infinite_Loop
.size Default_Handler, .-Default_Handler

I want to redirect it to my own function, the hardfaullt is usually weak and one override this with your custom function, something like

#include "gprintf.h"
#include "hardware.h"

// The prototype shows it is a naked function - in effect this is just an
// assembly function. 
static void HardFault_Handler( void ) __attribute__( ( naked ) );

/** ---------------------------------------------------------------------------
 * @brief prvGetRegistersFromStack
 * @param pulFaultStackAddress
 */
void prvGetRegistersFromStack( uint32_t *pulFaultStackAddress )
{
    /* These are volatile to try and prevent the compiler/linker optimising them
    away as the variables never actually get used.  If the debugger won't show the
    values of the variables, make them global my moving their declaration outside
    of this function. */
    uint32_t cfsr  = SCB->CFSR;
    uint32_t hfsr  = SCB->HFSR;
    uint32_t mmfar = SCB->MMFAR;
    uint32_t bfar  = SCB->BFAR;

    volatile uint32_t r0;
    volatile uint32_t r1;
    volatile uint32_t r2;
    volatile uint32_t r3;
    volatile uint32_t r12;
    volatile uint32_t lr;     // Link register. 
    volatile uint32_t pc;     // Program counter. 
    volatile uint32_t psr;    // Program status register. 
    volatile uint32_t counter;

    r0 = pulFaultStackAddress[ 0 ];
    r1 = pulFaultStackAddress[ 1 ];
    r2 = pulFaultStackAddress[ 2 ];
    r3 = pulFaultStackAddress[ 3 ];

    r12 = pulFaultStackAddress[ 4 ];
    lr  = pulFaultStackAddress[ 5 ];
    pc  = pulFaultStackAddress[ 6 ];
    psr = pulFaultStackAddress[ 7 ];

    gprintf ("\r\n> Hard Error ");
    gprintf ("\r\n    SCB->CFSR=0x%08lX SCB->HFSR=0x%08lX SCB->MMFAR=0x%08lX SCB->BFAR=0x%08lX\n", cfsr, hfsr, mmfar, bfar);
    gprintf ("\r\n    r0 =0x%08X r1==0x%08X r2=0x%08X r3 =0x%08X,"
             "\r\n    r12=0x%08X lr==0x%08X pc=0x%08X pcr=0x%08X,", r0,r1,r2,r3,r12,lr,pc, psr);

    // When the following line is hit, the variables contain the register values. 
    for( ;; )
    {
        //digital_io_write(GPIO_TypeDef *port, uint32_t pin, uint32_t val);
    
        digitalWrite (LED_STATUS, 1);
        digitalWrite (CPU, 0);
        for (counter = 0; counter < 1000000; counter++) {};
        digitalWrite (LED_STATUS, 0);
        digitalWrite (CPU, 1);
        for (counter = 0; counter < 1000000; counter++) {};
    }
}
/** ---------------------------------------------------------------------------
 * @brief HardFault_Handler  The fault handler implementation calls a
 * function called prvGetRegistersFromStack().
 */
static void HardFault_Handler(void)
{
    __asm volatile
    (
        " tst lr, #4                                                \n"
        " ite eq                                                    \n"
        " mrseq r0, msp                                             \n"
        " mrsne r0, psp                                             \n"
        " ldr r1, [r0, #24]                                         \n"
        " ldr r2, handler2_address_const                            \n"
        " bx r2                                                     \n"
        " handler2_address_const: .word prvGetRegistersFromStack    \n"
    );
}
  1. Is it possible to redirect in PlatformIO ?
  2. Correct “howto” test that this redirection code indeed works.

Whenever playing with defining interrupt handlers in PlatformIO, try the setting

lib_archive = no

in the platformio.ini (docs) first. They way usually PIO builds the firmware (core → archive, your code → archive, link) has been proven to not work with weak ISR handlers multiple times, so you need to disable archiving.

I also see you calling Arduino functions in the Hardfault handler. If this code is written in C++, then you need to also declare extern "C" so that C++ name mangling doesn’t get in the way for your weak function override.

When in doubt I would also remove the static keyword.

Remove the “static”, only extern will override a weak definition in the library.