c-如何分支/跳转到Clang中的任意地址



我将KeilµVision用于一个嵌入式项目,该项目会跳回引导程序进行更新。Keil以前使用ARMCC作为编译器,下面的代码运行良好。

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在他们的新版本中切换到Clang,我正在尝试移植代码。现在,当调用runBootloader()时,该代码将导致重置。

生成的程序集当然是不同的。因此,我从列表中提取了ARMCC生成的程序集,并将其编写为内联汇编程序。

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}");
}

逐步执行,寄存器中的值似乎与以前一样发生了变化。但是ARMCC版本跳转到BLX上的引导程序,而Clang版本重置。从矢量表中提取的地址在两者中是相同的。

我看到有人提到Clang不允许这种事情,比如对这个答案的评论。但必须有一种方法可以跳回到引导程序。我错过了什么?链接器中是否有一些设置需要启用才能允许这种行为?


更新

一些评论让我找到了一种更好的重置方法,这篇文章讨论了通过写入应用程序中断和重置控制寄存器(AIRCR(的SYSTEMRESETREQ位来重置处理器。这让我在CMSIS头文件中找到了这个函数。我继承了代码,我不知道为什么没有使用它。

/**
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();
}
}

但是,应用程序仍然会在不运行引导加载程序的情况下重新启动。

更多信息

引导加载程序和应用程序是独立的项目。该应用程序正在运行带有CMSIS层的Keil RTX操作系统。我的想法是应用程序在自己的地址空间中运行,并且不知道引导加载程序。因此,当它重置时,它只在自己的地址空间内重置,并且引导加载程序永远不会运行。

我试图通过将引导程序添加到我的分散文件中来通知应用程序有关引导程序的信息。

LR_IROM0 0x00  0x00012000   {
ER_IROM0 0x0 0x00012000   {
.ANY (+RO)
}
}

但结果是一样的。

为了澄清,我真的在尝试重置处理器。我试图跳转到引导加载程序,同时向它传达这不是一个正常的引导,而是一个更新。


工具链版本

PS C:Keil_v5ARMARMCCbin> .armcc.exe --version_number
5060750
PS C:Keil_v5ARMARMCLANGbin> .armclang.exe --version
Product: MDK Plus 5.25 (Flex)
Component: ARM Compiler 6.9
Tool: armclang [5ced1d00]
Target: unspecified-arm-none-unspecified

生成的程序集

C代码:

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

clang的哪个版本以及如何使用?

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();
}

gcc 8.2.0

00000000 <run_bootloader>:
0:   2304        movs    r3, #4
2:   b510        push    {r4, lr}
4:   681b        ldr r3, [r3, #0]
6:   4798        blx r3
8:   bd10        pop {r4, pc}
a:   46c0        nop         ; (mov r8, r8)

clang/llvm 3.8.0

00000000 <run_bootloader>:
0:   b580        push    {r7, lr}
2:   af00        add r7, sp, #0
4:   2004        movs    r0, #4
6:   6800        ldr r0, [r0, #0]
8:   4780        blx r0
a:   bd80        pop {r7, pc}

两者都应该正常工作,因为它们使用blx而不是bl。

如果你想模拟重置,尽管你需要做这样的

.thumb
mov r0,#0
ldr r1,[r0,#0]
ldr r2,[r0,#4]
mov sp,r1
bx r2
00000000 <.text>:
0:   2000        movs    r0, #0
2:   6801        ldr r1, [r0, #0]
4:   6842        ldr r2, [r0, #4]
6:   468d        mov sp, r1
8:   4710        bx  r2

并在未编译的汇编中执行。

最新更新