什么决定了内存重映射操作后的位置无关性?

What determines position-independence following a memory remap operation?

我开始阅读 Miro Samek 的 "Building Bare-Metal ARM Systems with GNU" 并发现自己卡在了某个点上。在 PDF 第 10 页的注释之一中找到了导致我混淆的原因:

NOTE: The function low_level_init() can be coded in C/C++ with the following restrictions. The function must execute in the ARM state and it must not rely on the initialization of .data section or clearing of the .bss section. Also, if the memory remapping is performed at all, it must occur inside the low_level_init() function because the code is no longer position-independent after this function returns

代码"no longer position-independent"究竟是怎样的?从 low_level_init / _cstartup 标签返回后,引用的代码(可在 PDF 第 7 - 9 页查看)似乎仍然与位置无关。 _cstartup 标签后的指令似乎唯一不同的是它们引用了链接描述文件中定义的标签(指南的第 3 节)。

那么重映射究竟如何影响其后的指令是否与位置无关?

位置无关是一个加载时的概念——而不是运行时的概念。位置独立是一种代码质量,允许它加载到内存中的任何地址并且仍然可以工作,但位置独立不是 运行 程序的质量。

一旦我们有了引用代码的调用堆栈and/or(重定位)数据,我们就不再具有代码或数据的位置独立性,也无法移动它们。实际上,当程序开始执行时,位置独立性消失了(尽管位置独立代码)。

两个 return 地址 — 通过调用动态生成(例如 BL) — 以及指向代码的数据指针(代码指针向量(如在 vtables 中)和初始化的全局函数指针)销毁运行.

程序的位置独立性

作者的警告节点是一种描述位置独立性消失的方式。更令人困惑的是,通过非常小心的操作,它们允许实际移动代码,即使它的执行已经开始,所以这里我们的位置独立性实际上在执行开始后持续了一小段时间。

但是如果不放弃位置独立性,程序就无法正常运行(例如进行调用和使用函数指针),因此他们选择在 low_level_init.[=24 的末尾划清界限=]

例如,reset 代码用了很长的篇幅使用非标准调用来调用 low_level_init — 为它提供一个 lr 值而不使用任何 BLmov lr,pc(这将捕获 cstartup 的预重映射 (ROM) 地址。)提供的 lr 值是(重定位)cstartup 的地址low_level_init 将 "return"!

    (10) LDR r0,=_reset /* pass the reset address as the 1st argument */
    (11) LDR r1,=_cstartup /* pass the return address as the 2nd argument */
    (12) MOV lr,r1 /* set the return address after the remap */
    (13) LDR sp,=__stack_end__ /* set the temporary stack pointer */
    (14) B low_level_init /* relative branch enables remap */
_cstartup:

BMOV lr,r1 组合就是调用。