Assembly 理解ARM重新定位(例如:str x0,[tmp,:lo12:zbi_paddr])

Assembly 理解ARM重新定位(例如:str x0,[tmp,:lo12:zbi_paddr]),assembly,arm,arm64,linker-scripts,Assembly,Arm,Arm64,Linker Scripts,我在工厂里发现了这条装配线 用于ARM64。我还发现zbi_paddr是用C++定义的: extern paddr_t zbi_paddr; 所以我开始研究:lo12:是什么意思 我发现这似乎是一个很好的解释,但它并没有解释最基本的问题:什么是重新定位以及为什么需要一些东西 我想既然ZBIXPADRR是在start .s中定义的,并且在C++代码中使用,因为start .s在对象文件start .o上生成,地址从0开始,链接过程将不得不重新分配所有地址到最终的可执行文件中的地址。 为了跟踪需要

我在工厂里发现了这条装配线

用于ARM64。我还发现zbi_paddr是用C++定义的:

extern paddr_t zbi_paddr;
所以我开始研究:lo12:是什么意思

我发现这似乎是一个很好的解释,但它并没有解释最基本的问题:什么是重新定位以及为什么需要一些东西

我想既然ZBIXPADRR是在start .s中定义的,并且在C++代码中使用,因为start .s在对象文件start .o上生成,地址从0开始,链接过程将不得不重新分配所有地址到最终的可执行文件中的地址。 为了跟踪需要重新定位的符号,ELF存储这些结构,如回答中所述:

typedef struct
{
    Elf64_Addr r_offset;    /* Address of reference */
    Elf64_Xword r_info;     /* Symbol index and type of relocation */
} Elf64_Rel;

typedef struct
{
    Elf64_Addr r_offset;    /* Address of reference */
    Elf64_Xword r_info;     /* Symbol index and type of relocation */
    Elf64_Sxword r_addend;  /* Constant part of expression */
} Elf64_Rela;
例如,r_offset将zbi_paddr的地址存储在最终的可执行文件中。然后,加载程序后,加载程序查看这些结构,然后从C++代码中填充ZBIIIPADDR的地址。
在那之后,我完全不需要像S、A、p、X、abs\u g0\u S和lo12这样的东西。他说这与指令不能在寄存器中插入64位有关。有人能给我更多的背景吗?我不明白,已经有办法在寄存器中插入64位。这与重新分配有什么关系?

根本问题是ARM64指令的大小都是32位,这限制了可以在任何一条指令中编码的即时数据的位数。您当然不能对64位地址进行编码,甚至不能对32位地址进行编码

内核的代码和静态数据预计会低于4GB,因此为了将数据存储在静态变量zbi_paddr中,程序员可以编写以下两条指令,包括您省略但至关重要的前一条指令。请注意,tmp是上面定义为x9的宏,因此代码扩展为:

adrp    x9, zbi_paddr
str     x0, [x9, #:lo12:zbi_paddr]
现在,当链接发生时,链接器将知道整个内核的布局以及所有符号的相对位置。此方案支持位置无关代码,因此绝对地址不需要知道,但我们肯定会知道zbi_paddr和上面的adrp指令之间的位移,这将适合有符号的32位值,以及zbi_paddr在其4KB页面内的偏移量,因为内核必须在页面对齐的地址加载

因此,该位移的第12位和更高的位将被编码到adrp指令中,该指令有一个21位立即数字段。adrp将对其进行签名扩展,将其添加到程序计数器的相应位,并将结果放入x9中。然后x9将包含zbi_paddr绝对地址的第63-12位,低12位归零

zbi_paddr在其页面内的12位偏移量将被编码到str指令的12位立即数字段中。它将这个立即数与x9中的值相加,然后得到zbi_paddr的地址,并将x0存储在该地址。因此,我们只需要两条指令就可以在zbi_paddr中存储一个值

为了支持这一点,汇编代码生成的目标文件需要指示链接器位移的32-12位需要插入adrp指令,zbi_paddr地址的11-0位需要插入str指令。链接器的这些指令是什么样的重新定位;它们将包含一个对符号的引用,该符号的地址将在这里被编码为zbi_paddr,以及具体如何使用它。ELF支持专门为这些指令设计的重定位,即将正确的位放在指令字的正确位置

的确,还有其他方法可以将64位值放入寄存器。例如,它可以放在文字池中,文字池是一个与相应代码足够接近的数据区域,可以通过一条带PC相对位移的ldr指令访问该区域。您可以重新定位,告诉链接器在文本池中插入zbi_paddr的绝对地址。但加载它需要额外的内存访问,这比adrp慢;此外,8字节的文字,加上ldr,再加上实际执行存储的str,总共需要16字节的内存。adrp/str方法只需要8个,并且它在位置独立代码中工作得更好,因为链接器可能实际上不知道zbi_paddr的绝对地址

如果您不喜欢从内存加载,可以使用最多四条mov/movk指令将zbi_paddr的绝对地址放入寄存器,一次加载16位。这方面也有搬迁。但是对于最后一个str,我们使用了多达20字节的代码;执行五条指令比执行两条指令需要更多的时钟周期;与位置无关的代码仍然存在问题

因此,adrp/str和:lo12:是访问全局或静态变量的标准公认方法。如果要加载而不是存储, 您使用adrp/ldr。如果你想把zbi_paddr的地址放在寄存器里,你可以

adrp x9, zbi_paddr
add x9, x9, #:lo12:zbi_paddr
add指令还支持12位立即数,正是出于此目的


这些特性在中进行了解释。

根本问题是ARM64指令的大小都是32位,这限制了可以在任何一条指令中编码的即时数据的位数。您当然不能对64位地址进行编码,甚至不能对32位地址进行编码

内核的代码和静态数据预计会低于4GB,因此为了将数据存储在静态变量zbi_paddr中,程序员可以编写以下两条指令,包括您省略但至关重要的前一条指令。请注意,tmp是上面定义为x9的宏,因此代码扩展为:

adrp    x9, zbi_paddr
str     x0, [x9, #:lo12:zbi_paddr]
现在,当链接发生时,链接器将知道整个内核的布局以及所有符号的相对位置。此方案支持位置无关代码,因此绝对地址不需要知道,但我们肯定会知道zbi_paddr和上面的adrp指令之间的位移,这将适合有符号的32位值,以及zbi_paddr在其4KB页面内的偏移量,因为内核必须在页面对齐的地址加载

因此,该位移的第12位和更高的位将被编码到adrp指令中,该指令有一个21位立即数字段。adrp将对其进行签名扩展,将其添加到程序计数器的相应位,并将结果放入x9中。然后x9将包含zbi_paddr绝对地址的第63-12位,低12位归零

zbi_paddr在其页面内的12位偏移量将被编码到str指令的12位立即数字段中。它将这个立即数与x9中的值相加,然后得到zbi_paddr的地址,并将x0存储在该地址。因此,我们只需要两条指令就可以在zbi_paddr中存储一个值

为了支持这一点,汇编代码生成的目标文件需要指示链接器位移的32-12位需要插入adrp指令,zbi_paddr地址的11-0位需要插入str指令。链接器的这些指令是什么样的重新定位;它们将包含一个对符号的引用,该符号的地址将在这里被编码为zbi_paddr,以及具体如何使用它。ELF支持专门为这些指令设计的重定位,即将正确的位放在指令字的正确位置

的确,还有其他方法可以将64位值放入寄存器。例如,它可以放在文字池中,文字池是一个与相应代码足够接近的数据区域,可以通过一条带PC相对位移的ldr指令访问该区域。您可以重新定位,告诉链接器在文本池中插入zbi_paddr的绝对地址。但加载它需要额外的内存访问,这比adrp慢;此外,8字节的文字,加上ldr,再加上实际执行存储的str,总共需要16字节的内存。adrp/str方法只需要8个,并且它在位置独立代码中工作得更好,因为链接器可能实际上不知道zbi_paddr的绝对地址

如果您不喜欢从内存加载,可以使用最多四条mov/movk指令将zbi_paddr的绝对地址放入寄存器,一次加载16位。这方面也有搬迁。但是对于最后一个str,我们使用了多达20字节的代码;执行五条指令比执行两条指令需要更多的时钟周期;与位置无关的代码仍然存在问题

因此,adrp/str和:lo12:是访问全局或静态变量的标准公认方法。如果希望加载而不是存储,则使用adrp/ldr。如果你想把zbi_paddr的地址放在寄存器里,你可以

adrp x9, zbi_paddr
add x9, x9, #:lo12:zbi_paddr
add指令还支持12位立即数,正是出于此目的


这些功能在。

arm指令中有详细说明。arm指令的大小是固定的,根本没有空间加载完整的64位立即数。您可以从内存文字池加载,也可以从部件将其组合在一起。各种重新定位类型都是根据特定的指令格式定制的。您显示的STR指令只允许12位偏移量。请结合前面的adrp指令阅读此指令。arm指令的大小是固定的,根本没有空间加载完整的64位立即数。您可以从内存文字池加载,也可以从部件将其组合在一起。各种重新定位类型都是根据特定的指令格式定制的。您显示的STR指令只允许12位偏移量。请结合前面的adrp指令阅读此指令。这里有一个正在做的例子。