无法完全理解有关 u-boot 中重定位如何工作的汇编代码



在u-boot链接脚本中,我看到了以下几行(https://source.denx.de/u-boot/u-boot/-/blob/v2021.10/arch/arm/cpu/armv8/u-boot.lds靠近线134(。

.rel_dyn_start :
{
*(.__rel_dyn_start)
}
.rela.dyn : {
*(.rela*)
}
.rel_dyn_end :
{
*(.__rel_dyn_end)
}

这是一个进行重新定位的代码。(https://source.denx.de/u-boot/u-boot/-/blob/v2021.10/arch/arm/cpu/armv8/start.S88行附近(
但我不能完全理解下面的行。

adrp    x2, __rel_dyn_start     /* x2 <- Runtime &__rel_dyn_start */
add     x2, x2, #:lo12:__rel_dyn_start
adrp    x3, __rel_dyn_end       /* x3 <- Runtime &__rel_dyn_end */
add     x3, x3, #:lo12:__rel_dyn_end
pie_fix_loop:
ldp x0, x1, [x2], #16   /* (x0, x1) <- (Link location, fixup) */
ldr x4, [x2], #8        /* x4 <- addend */
cmp w1, #1027       /* relative fixup? */                         // <==== from here??
bne pie_skip_reloc
/* relative fix: store addend plus offset at dest location */
add x0, x0, x9
add x4, x4, x9
str x4, [x0]
pie_skip_reloc:
cmp x2, x3
b.lo    pie_fix_loop

我的第一个问题是关于adrp x2, __rel_dyn_start指令
从链接器脚本中,我看到.__rel_dyn_start是一个节的名称。但是我找不到名为__rel_dyn_start的变量(没有点(。它是从哪里来的?

我的第二个问题是关于cmp w1, #1027中的代码(上面标记为<==== from here??(
我猜此时x0x1x4包含中的前三个8字节值__开始部分(假定__rel_dyn_start是组合的.rel_dyn_start部分中的第一个变量(。并且x9似乎包含用于重新定位的偏移
那么,在cmp w1, #1027中,为什么要将w1(重定位部分(?(中第二个8字节值的下部(与#1027进行比较?如果相等,为什么将重定位偏移添加到x0x4,并将复制的值x4存储到新地址x0?(可能x0x4含有某种地址(。如果不理解这一点,我就无法遵循下一个代码。

感谢您的阅读,如果有人能向我解释这背后的主要逻辑以及所有这些代码的作用,我将不胜感激。

  1. 我想你正在寻找"u-boot-spl.lds";(https://source.denx.de/u-boot/u-boot/-/blob/v2021.10/arch/arm/cpu/u-boot-spl.lds)
    至少该文件包含命名变量
.rel.dyn : {
__rel_dyn_start = .;  // <<== assigned with starting address of section
*(.rel*)
__rel_dyn_end = .;
}

2.我不知道,只能推测
整个循环看起来像是将数据从原始位置复制到"重新定位"区域,其他人会在那里寻找它。由于位置不同,地址会分别调整
可以任意选择"#1027"。或者,这可能是可以编码到指令中并根据该限制进行选择的最大相对偏移量<不过还是用一勺盐吧>

我想现在我可以理解代码在做什么了(对于缺少的__rel_dyn_start和__rel_dyn_end变量,请参阅user3124812的回答和我对它的评论(
我在中找到https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/elf.h#L182,存在这种Elf64_Rela结构。

typedef struct elf64_rela {
Elf64_Addr r_offset;  /* Location at which to apply the action */
Elf64_Xword r_info;   /* index and type of relocation */
Elf64_Sxword r_addend;    /* Constant addend used to compute value */
} Elf64_Rela;

r_offset是需要重新定位的地址(地址应该改为指向内存上的实际位置,它们是代码中的引用(,r_info是重新定位类型(doc说很多类型,arch特定..(,r_addend是添加到地址r_offset中的值(符号refernece(的值。链接器只知道段中的相对地址,但在程序加载到内存后,动态加载程序应该向其添加更多的值,因为加载的地址并不总是与链接时间地址相同
对于所有需要重新定位的符号,此Elf64_结构似乎在__rel_dyn部分中重复。代码将此结构读取到x0、x1和x4寄存器中,每个寄存器用于r_offset、r_info和r_addned。如果rinfo的较低16位(w1,它是x1的较低的16位(是#1027(我猜它指示相对固定地址类型,正如注释所说(,则符号参考位置被(链接时间偏移r_addend,它是在程序构建时确定的,相对于段起始0(+(链接时间地址和实际加载地址之间的偏移(覆盖。代码对rel_dyn部分中的所有Elf64_Rela数据重复此操作(x2从__rel_dyn_start开始,直到它达到x3,__rel_dyn_end(
因此,程序实际上并没有被复制到新位置,而是只覆盖了所有引用,以使链接时间地址与实际执行时间地址相匹配
上面的代码是针对PIE(独立于位置的可执行文件(的,因此只有引用在加载后被覆盖以匹配加载的地址,即加载位置的代码。但在arch/arm/lib/relocate_64.S中,有另一个代码真正将代码从SRAM(=onchip-ram(复制(重新定位(到SDRAM(=DDR(中的新位置,这是在SDRAM初始化之后完成的。在这里,代码实际上是被复制的,.rel.dyn数据(关于引用的信息(与重新定位一样被调整。(为每个相对引用将{现有的已添加+已添加的偏移量}写入新位置(

最新更新