I'm using Keil µVision for an embedded project that jumps back to the bootloader for updating. Keil previously used ARMCC as a compiler and the following code worked fine.
void run_bootloader(void)
{
    uint32_t runBootloaderAddress;
    // Read the entry point from bootloader's vector table
    runBootloaderAddress = *(uint32_t*)(0x00000004);
    void (*runBootloader)(void) = (void (*) (void))runBootloaderAddress;
    runBootloader();
}
Keil is switching to Clang in their newer versions and I'm trying ot port the code over. That code now causes a reset when runBootloader() is called.
The generated assembly is, of course, different. So I pulled the ARMCC generated assembly from the listing and wrote it as inline assembler.
void run_bootloader(void)
{
    __asm("PUSH     {r4-r6,lr}");
    __asm("MOVS     r0,#0");
    __asm("LDR      r4,[r0,#4]");
    __asm("MOV      r5,r4");
    __asm("BLX      r5");
    __asm("POP      {r4-r6,pc}");
}
Stepping through, the values in the registers seem to change the same as before. But the ARMCC version jumps to the bootloader on the BLX while the Clang version resets. The address pulled from the vector table is the same in both.
I've seen mentions that Clang does not allow this sort of thing like the comment to this answer. But there has to be a way to jump back to the bootloader. What am I missing? Is there some setting in the linker I need to enable to allow for this behavior?
Update
Some comments lead me to search out a better way to reset which lead me to this article which discusses resetting the processor by writing to the SYSTEMRESETREQ bit of the Application Interrupt and Reset Control Register (AIRCR). Which lead me to find this function in a CMSIS header file. I inherited the code, I don't know why this wasn't being used.
/**
  \brief   System Reset
  \details Initiates a system reset request to reset the MCU.
 */
__STATIC_INLINE void __NVIC_SystemReset(void)
{
  __DSB();                                                          /* Ensure all outstanding memory accesses included
                                                                       buffered write are completed before reset */
  SCB->AIRCR  = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos)    |
                           (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) |
                            SCB_AIRCR_SYSRESETREQ_Msk    );         /* Keep priority group unchanged */
  __DSB();                                                          /* Ensure completion of memory access */
  for(;;)                                                           /* wait until reset */
  {
    __NOP();
  }
}
However, the application still reboots without running the bootloader.
More information
The bootloader and the application are separate projects. The application is running Keil RTX OS with a CMSIS layer. My thought is the application is running in its own address space and does not know about the bootloader. So when it resets, it only resets within its own address space and the bootloader is never run.
I attempted to inform the application about the bootloader by adding this to my scatter file.
LR_IROM0 0x00  0x00012000   {
  ER_IROM0 0x0 0x00012000   {
   .ANY (+RO)
  }
}
But the results are the same.
To clarify, I'm really trying to reset the processor. I'm trying to jump to the bootloader while communicating to it that it is not a normal boot but rather an update.
Toolchain Versions
PS C:\Keil_v5\ARM\ARMCC\bin> .\armcc.exe --version_number
5060750
PS C:\Keil_v5\ARM\ARMCLANG\bin> .\armclang.exe --version
Product: MDK Plus 5.25 (Flex)
Component: ARM Compiler 6.9
Tool: armclang [5ced1d00]
Target: unspecified-arm-none-unspecified
Generated Assembly
C Code:
void run_bootloader(void)
{
    volatile uint32_t runBootloaderAddress;
    // Read the entry point from bootloader's vector table
    runBootloaderAddress = *(uint32_t*)(0x00000004);
    void (*runBootloader)(void) = (void (*) (void))runBootloaderAddress;
    runBootloader();
}
ARMCC:
                          AREA ||i.run_bootloader||, CODE, READONLY, ALIGN=1
                  run_bootloader PROC
;;;1335   }
;;;1336   void run_bootloader(void)
000000  b570              PUSH     {r4-r6,lr}
;;;1337   {
;;;1338   uint32_t runBootloaderAddress;
;;;1339   
;;;1340   // Read the entry point from bootloader's vector table
;;;1341   runBootloaderAddress = *(uint32_t*)(0x00000004);
000002  2000              MOVS     r0,#0
000004  6844              LDR      r4,[r0,#4]
;;;1342   void (*runBootloader)(void) = (void (*) (void))runBootloaderAddress;
000006  4625              MOV      r5,r4
;;;1343   runBootloader();
000008  47a8              BLX      r5
;;;1344   }
00000a  bd70              POP      {r4-r6,pc}
;;;1345   
                          ENDP
Clang:
    .section    .text.run_bootloader,"ax",%progbits
    .hidden run_bootloader          @ -- Begin function run_bootloader
    .globl  run_bootloader
    .p2align    2
    .type   run_bootloader,%function
    .code   16                      @ @run_bootloader
    .thumb_func
run_bootloader:
.Lfunc_begin2:
    .loc    2 1338 0                @ ../_Primary/source/can_tools.c:1338:0
    .fnstart
    .cfi_startproc
@ BB#0:
    .save   {r7, lr}
    push    {r7, lr}
.Lcfi8:
    .cfi_def_cfa_offset 8
.Lcfi9:
    .cfi_offset lr, -4
.Lcfi10:
    .cfi_offset r7, -8
    .pad    #8
    sub sp, #8
.Lcfi11:
    .cfi_def_cfa_offset 16
.Ltmp8:
    .loc    2 1343 28 prologue_end  @ ../_Primary/source/can_tools.c:1343:28
    movs    r0, #4
    ldr r0, [r0]
    .loc    2 1343 26 is_stmt 0     @ ../_Primary/source/can_tools.c:1343:26
    str r0, [sp, #4]
    .loc    2 1344 52 is_stmt 1     @ ../_Primary/source/can_tools.c:1344:52
    ldr r0, [sp, #4]
    .loc    2 1344 12 is_stmt 0     @ ../_Primary/source/can_tools.c:1344:12
    str r0, [sp]
    .loc    2 1345 5 is_stmt 1      @ ../_Primary/source/can_tools.c:1345:5
    ldr r0, [sp]
    blx r0
    .loc    2 1346 1                @ ../_Primary/source/can_tools.c:1346:1
    add sp, #8
    pop {r7, pc}
.Ltmp9:
.Lfunc_end2:
    .size   run_bootloader, .Lfunc_end2-run_bootloader
    .cfi_endproc
    .cantunwind
    .fnend
                                        @ -- End function
 
    