Search code examples
c++cfunction-pointersvoid-pointers

C++ trying to cast address to void* and assign to function pointer (so that my bootloader can jump to actual app)


I am writing a bootloader for an STM32, where I need to jump from bootloader to the real app.

In C this works, because I can cast an address to a void pointer and assign that to a function pointer, and call the function pointer as follows:

void jump_to_firmware(uint32_t address)
{
    uint32_t reset_handler_add = *((volatile uint32_t *)(address + 4));
    void (*app_reset_handler)(void) = (void *)reset_handler_add;

    SCB->VTOR = address;
    uint32_t msp_value = *((volatile uint32_t *)address);
    
    __set_MSP(msp_value);
    app_reset_handler();    
}

If I use the same implementation in a C++ the gnu compiler will give an error on the cast to void pointer.

include/bootloader.hpp:58:39: error: invalid conversion from 'void*' to 'void (*)()' [-fpermissive]

After googling I found this SO page, which I tried and came to the following implementation in my class:

void JumpToApp()
{
    // Quick test if C style cast does work
    //jump_to_firmware(_appStartAddress);

    uint32_t mspAdress = *((volatile uint32_t *)_appStartAddress);
    uint32_t resetHandlerAddress = *((volatile uint32_t *)(_appStartAddress + sizeof(uint32_t)));

    // https://stackoverflow.com/questions/1096341/function-pointers-casting-in-c
    typedef void (*functionPointer)();
    functionPointer resetHandler = 0;
    reinterpret_cast<void*&>(resetHandler) = (void*)resetHandlerAddress;

    SCB->VTOR = _appStartAddress;
    __set_MSP(mspAdress);

    resetHandler();
}

In the C++ implementation:

  • functionPointer resetHandler is assigned with 0x8035065
  • SCB->VTOR is assigned with 0x08020000
  • mspAddress is assigned with `0x20020000
  • then the function pointer resetHandler is called

In the C implementation:

  • app_reset_handler is assigned with 0x8035065
  • SCB->VTOR is assigned with 0x08020000
  • mspAddress is assigned with `0x20020000
  • then the function pointer app_reset_handler is called

The C implementation works, it jumps to my app, the app runs without issues.

The C++ implementation ends up nowhere. It hangs/crashes on the following (to me meaningless) address:

enter image description here

I am trying to keep the amount of source files to a minimum, so I would like to keep the logic in the single class definition.

My questions:

  • Did I misunderstand the linked SO page and can somebody see where I went wrong in the C++ implementation?
  • Is there a better/easier way to cast an address to a function pointer in C++?
  • Or is there a technical reason why it simply can't be done in C++?

PS: The bootloader code is the same in both cases. The only difference I made to test either implementation is to comment out the code in Bootloader::JumpToApp and call the C function jump_to_firmware(uint32_t) instead.

PPS: all peripherals are deinitialized properly. Again, it works fine, the problem only appears when I use this C++ implementation.


Solution

  • The same code will compile in C and C++. You simple has to cast to the correct cast (in C++ you cant assign a void * to non void * pointer. It is much more strict than in C.

    void jump_to_firmware(uint32_t address)
    {
        uint32_t reset_handler_add = *((volatile uint32_t *)(address + 4));
        void (*app_reset_handler)(void) = (void (*)(void))reset_handler_add;
        /* ... */ 
    }
    

    If you do not like those weird casts you can typedef the function.

    typedef void handler(void);
    
    void jump_to_firmware(uint32_t address)
    {
        uint32_t reset_handler_add = *((volatile uint32_t *)(address + 4));
        handler *app_reset_handler = (handler *)reset_handler_add;
        /* ... */ 
    }