我正在使用以下CFLAGS为Cortex-M4 (STM32F4) 编译我的FreeRTOS应用程序:
-fpic -msingle-pic-base -mpic-data-is-text-relative -mpic-register=r10
并且使用-fpic
标志设置链接器。
向量表已经正确地复制到RAM,并且更改了特定固件槽设备当前启动的偏移值。
但是,在从固件槽启动主应用程序之后,设备在HardFault中挂起,CFSR寄存器中设置了IMPRECISERR。
当执行以下行时会发生这种情况:vPortSVCHandler。
ldmia r0!, {r4-r11, r14}
此操作后,链接寄存器的值等于0000 0000
。
我需要做些什么才能正确创建适用于STM32的位置无关应用程序?重新定位全局偏移表吗?如果是,应将其放置在内存中的何处?
编辑
根据下面的问题,我的目标是创建具有引导加载程序和2个固件插槽的应用程序。我需要将固件插槽放在FLASH中,以便我的设备可以在运行时进行更新。
为什么需要使用PIC标志编译固件?每个插槽都有其在内存中的空间。默认情况下,它被编译为第一个插槽地址。从第二个插槽运行这样的二进制文件是不可能的,因为数据访问等(老实说,这是我第一次使用GOT和PIC编译)是基于程序计数器寄存器值(程序存储器空间中的当前位置)。固件更新使用下一个可用插槽,该插槽当前未使用。因为我不知道当前正在使用哪个插槽,所以我需要构建二进制文件,使其在所有插槽(在这种情况下为2个)上都能正常工作。这就是为什么在阅读了许多网站、帖子等之后,我认为我需要这个。
从引导加载程序切换到主应用程序的流程如下:
- 检查应使用哪个固件插槽
- 禁用IRQs。
- 将向量表复制到RAM中。该部分RAM对于两个插槽都相同。在复制过程中,我正在更改每个地址的偏移量,以便它们与特定的固件插槽兼容。默认情况下,地址没有偏移量,在编译后阶段被删除。
- 根据RAM中向量表中的第一个字设置堆栈指针。在将向量表复制到RAM时,该地址不会更改。
- 设置SCB->VTOR。
- 执行数据同步障碍DSB()。
- 跳转到已复制到RAM的向量表中的重置处理程序。
在old_timer发布答案后编辑
因此,我尝试为两个插槽编译代码。这里是GOT反汇编的结果:
Disassembly of section .got:
080083ac <_got_address>:
80083ac: 0800beb9 stmdaeq r0, {r0, r3, r4, r5, r7, r9, sl, fp, ip, sp, pc}
80083b0: 0800bf4c stmdaeq r0, {r2, r3, r6, r8, r9, sl, fp, ip, sp, pc}
80083b4: 20000274 andcs r0, r0, r4, ror r2
80083b8: 2000022c andcs r0, r0, ip, lsr #4
80083bc: 20012fb4 ; <UNDEFINED> instruction: 0x20012fb4
80083c0: 080086c5 stmdaeq r0, {r0, r2, r6, r7, r9, sl, pc}
80083c4: 20000200 andcs r0, r0, r0, lsl #4
80083c8: 200132f0 strdcs r3, [r1], -r0
80083cc: 20013330 andcs r3, r1, r0, lsr r3
80083d0: 080088e1 stmdaeq r0, {r0, r5, r6, r7, fp, pc}
80083d4: 20013334 andcs r3, r1, r4, lsr r3
80083d8: 20013338 andcs r3, r1, r8, lsr r3
80083dc: 20000230 andcs r0, r0, r0, lsr r2
80083e0: 200132d0 ldrdcs r3, [r1], -r0
80083e4: 20012fb8 ; <UNDEFINED> instruction: 0x20012fb8
80083e8: 20000234 andcs r0, r0, r4, lsr r2
80083ec: 200131cc andcs r3, r1, ip, asr #3
80083f0: 0800bed1 stmdaeq r0, {r0, r4, r6, r7, r9, sl, fp, ip, sp, pc}
80083f4: 080089a1 stmdaeq r0, {r0, r5, r7, r8, fp, pc}
80083f8: 0800bf7c stmdaeq r0, {r2, r3, r4, r5, r6, r8, r9, sl, fp, ip, sp, pc}
80083fc: 080086a5 stmdaeq r0, {r0, r2, r5, r7, r9, sl, pc}
8008400: 080087f1 stmdaeq r0, {r0, r4, r5, r6, r7, r8, r9, sl, pc}
8008404: 200132cc andcs r3, r1, ip, asr #5
按照 @old_timer 的建议更改固件插槽后,这样就改变了:
Disassembly of section .got:
081043ac <_got_address>:
81043ac: 08107eb9 ldmdaeq r0, {r0, r3, r4, r5, r7, r9, sl, fp, ip, sp, lr}
81043b0: 08107f4c ldmdaeq r0, {r2, r3, r6, r8, r9, sl, fp, ip, sp, lr}
81043b4: 20000274 andcs r0, r0, r4, ror r2
81043b8: 2000022c andcs r0, r0, ip, lsr #4
81043bc: 20012fb4 ; <UNDEFINED> instruction: 0x20012fb4
81043c0: 081046c5 ldmdaeq r0, {r0, r2, r6, r7, r9, sl, lr}
81043c4: 20000200 andcs r0, r0, r0, lsl #4
81043c8: 200132f0 strdcs r3, [r1], -r0
81043cc: 20013330 andcs r3, r1, r0, lsr r3
81043d0: 081048e1 ldmdaeq r0, {r0, r5, r6, r7, fp, lr}
81043d4: 20013334 andcs r3, r1, r4, lsr r3
81043d8: 20013338 andcs r3, r1, r8, lsr r3
81043dc: 20000230 andcs r0, r0, r0, lsr r2
81043e0: 200132d0 ldrdcs r3, [r1], -r0
81043e4: 20012fb8 ; <UNDEFINED> instruction: 0x20012fb8
81043e8: 20000234 andcs r0, r0, r4, lsr r2
81043ec: 200131cc andcs r3, r1, ip, asr #3
81043f0: 08107ed1 ldmdaeq r0, {r0, r4, r6, r7, r9, sl, fp, ip, sp, lr}
81043f4: 081049a1 ldmdaeq r0, {r0, r5, r7, r8, fp, lr}
81043f8: 08107f7c ldmdaeq r0, {r2, r3, r4, r5, r6, r8, r9, sl, fp, ip, sp, lr}
81043fc: 081046a5 ldmdaeq r0, {r0, r2, r5, r7, r9, sl, lr}
8104400: 081047f1 ldmdaeq r0, {r0, r4, r5, r6, r7, r8, r9, sl, lr}
8104404: 200132cc andcs r3, r1, ip, asr #5
1.首先要在RAM内存空间中为GOT保留空间,并请求引导程序/启动程序将GOT复制到该位置并添加插槽偏移量(如果需要)。我认为可以通过更改链接器代码来实现,使其将GOT放置在RAM的VMA中。因此,我假设,在应用程序的其余部分中,GOT地址将更改为放置在RAM中的地址,我是正确的吗?
为什么这个二进制文件在设备上不起作用?这是因为我应该添加编译标志-mpic-register=r10
并将此寄存器设置为将GOT复制到RAM后的地址,并在从插槽执行主应用程序之前进行设置吗?哦!等等,这在固件插槽0上运行,但我不知道为什么。我还没有检查第二个插槽,很快会更新。