为 ARM M4 编译和链接位置独立代码 (PIC)

Compiling and linking position independent code (PIC) for ARM M4

我正在制作引导加载程序并赋予它自我更新的能力。此过程涉及将二进制文件复制到新位置,跳转到该位置,并使用它在原始位置刷新新的引导加载程序。这一切都是在 Eclipse 中使用 ARM GCC 工具链为 M4 处理器开发的。

为此,我收集到我需要编译为位置独立代码 (PIC)。

我四处搜索并找到了这篇优秀的文章,所以当我将“-fPIC”添加到 ARM GCC 编译器调用时,我预计会看到 link关于 GOT 和 PLT 丢失的错误 https://eli.thegreenplace.net/2011/11/03/position-independent-code-pic-in-shared-libraries/

在我的 linker 脚本中,我将这些位置添加到 .data 部分,如下所示:

.data : AT(__DATA_ROM)
{
. = ALIGN(4);
__DATA_RAM = .;
__data_start__ = .; /* Create a global symbol at data start. */
*(.got*) /* .got and .plt for position independent code */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
KEEP(*(.jcr*))
. = ALIGN(4);
__data_end__ = .; /* Define a global symbol at data end. */
} > m_data

但是,此代码无法从 ROM 复制到 RAM。

我的下一个想法是,也许我的 link 人需要知道它是 link 一个 PIC 可执行文件。为了找出答案,我在 LD linker 脚本调用中添加了“--pic-executable”。然而,linker 现在生成了“interp”、“dyn”、“rel.dyn”和“hash”的部分。我也尝试将它们放入数据部分,但出现以下错误:

gcc-arm-none-eabi-4_9/bin/../lib/gcc/arm-none-eabi/4.9.3/../../../../arm-none-eabi/bin/ld.exe: could not find output section .hash

gcc-arm-none-eabi-4_9/bin/../lib/gcc/arm-none-eabi/4.9.3/../../../../arm-none-eabi/bin/ld.exe: final link failed: Nonrepresentable section on output

我认为这意味着编译器实际上并没有用任何东西填充“.hash”部分,所以 link 失败了。

我做的正确吗?我还需要添加什么才能让编译器执行操作吗?任何帮助将不胜感激。

启动代码和重新定位涉及许多仔细的步骤以及 RAM、SPI 和其他必要外围设备配置的初始化。

我知道 U-Boot 会执行您要实现的序列。因此,更好的开始是遍历 u-boot documentation 和来源,处理器或感兴趣的板的机器特定文件夹。

无论如何,我和 NXP 的技术支持团队都无法让他们的 S32DS IDE 编译真正与位置无关的代码。

时至今日,我们有两个引导加载程序 - 一个是为位置 A 编译的,另一个是为位置 B 编译的。两者都是更新所必需的。

TI 使用 Tivaware 引导加载程序执行此操作。您可以使用链接器 gnu ld 技巧来做到这一点:

.text 0x20000000 : AT (0x00000000)
{
_text = .;
KEEP(*(.isr_vector))
*(.text*)
*(.rodata*)
_etext = .;
}

将此代码从 0x0 处的闪存复制到 0x2000_0000 处的 SRAM 的启动代码留作 reader 的练习。

最近我广泛研究了 Cortex-M4 引导加载 PIC 固件映像。

需要几样东西:

一个非常简单的引导程序。它只需要从固件映像的闪存位置读取 2 个 4 字节的字。第一个是堆栈指针地址,第二个是 Reset_Handler 地址。引导加载程序需要跳转到 Reset_Handler。但由于固件在闪存中的位置更远,Reset_Handler 实际上更远了一点。请参阅此图片以进行说明:

因此,引导加载程序在跳转到固件映像的 Reset_Handler 之前添加了正确的偏移量。没有完成其他修补,但引导加载程序(至少在我的解决方案中)存储固件映像的位置和偏移量,计算校验和并将此信息传递到寄存器中以供固件映像使用。

然后我们需要修改固件 linker 文件以在 RAM 的开头强制 ISR 向量 table。对于 Cortex-M4 和 VTOR(矢量偏移 Table 寄存器),ISR 矢量 table 需要与 512 边界对齐。在 RAM 的开头,它自然存在。在 linker 脚本中,我们还在 RAM 中专门为全局偏移 Table (GOT) 分配了一个位置,以便于操作。地址范围也应通过 linker 脚本符号导出。

我们还需要 C 编译器选项。基本上是这些:

-fpic

-mpic-register=r9

-msingle-pic-base

-mno-pic-data-is-text-relative

他们只使用 C 编译器!这将创建全局抵消 Table (GOT) 会计。

最后,我们需要在固件映像项目中使用专门且繁琐的汇编 bootstrap 例程。这些例程执行设置 C 运行时环境的正常启动任务,但它们还会从闪存中读取 ISR 和 GOT,并将它们复制到 RAM。在 RAM 中,那些指向闪存的 table 的地址被固件映像与引导加载程序 运行 的偏移量偏移。示例图片:

我花了 6 个月的时间研究这个主题,我在这里写了一篇关于它的深入文章:

https://techblog.paalijarvi.fi/2022/01/16/portable-position-independent-code-pic-bootloader-and-firmware-for-arm-cortex-m0-and-cortex-m4/

通过提供 link 我希望我能为 Whosebug 做出贡献,我将其用作我研究的起点。我希望这可以帮助一些人学习内在函数。