首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >理解ARM重新定位(例如: str x0,[tmp,#:lo12:zbi_paddr])

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

Stack Overflow用户
提问于 2020-11-14 21:25:49
回答 1查看 1.9K关注 0票数 1

我在锆石核启动找到了这条生产线

代码语言:javascript
运行
复制
str     x0, [tmp, #:lo12:zbi_paddr]

为了ARM64。我还发现zbi_paddr是在C++中定义的:

代码语言:javascript
运行
复制
extern paddr_t zbi_paddr;

所以我开始研究#:lo12:是什么意思。

我找到了https://stackoverflow.com/a/38608738/6655884,这看起来是一个很好的解释,但它并没有解释最基本的:什么是重新定位以及为什么需要一些东西。

我猜想,由于zbi_paddrr是在start.S中定义并在C++代码中使用的,因为start.S在对象文件中生成地址从0开始,所以链接过程必须将那里的所有地址重新分配到最终可执行文件中的地址。

为了跟踪需要重新定位的符号,ELF存储这些结构,如答案中所述:

代码语言:javascript
运行
复制
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的地址存储在最终的可执行文件中。然后,当程序加载时,加载程序将查看这些结构,然后从zbi_paddr代码中填充C++地址。

在那之后,我完全忽略了对SAPXabs_g0_slo12这样的东西的需求。他说,这与指令无法将64位插入寄存器有关。有人能给我更多的背景吗?我不明白,已经有方法可以在寄存器中插入64位。这和重新分配有什么关系?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-11-15 04:03:53

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

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

代码语言:javascript
运行
复制
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位值输入寄存器。例如,它可以放置在文字池中,这是一个足够接近相应代码的数据区域,可以用单个ldr指令(具有PC相对位移)到达。您可以进行重新定位,让链接器在文字池中插入zbi_paddr的绝对地址。但是加载它需要额外的内存访问,这比adrp慢;此外,8字节的文字加上ldr,再加上实际执行存储的str,总共需要16字节的内存。adrp/str方法只需要8,并且与位置无关的代码工作得更好,其中链接器可能实际上不知道zbi_paddr的绝对地址。

如果您不喜欢内存中的加载,您可以将zbi_paddr的绝对地址用最多4个mov/movk指令输入寄存器,一次加载16位。对此也有重新定位。但是对于最终的str,我们将消耗多达20字节的代码;执行五条指令需要比两个时钟周期更长的时间;并且与位置无关的代码仍然存在问题。

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

代码语言:javascript
运行
复制
adrp x9, zbi_paddr
add x9, x9, #:lo12:zbi_paddr

add指令也支持12位的即时指令,正是为了这个目的.

这些特性在GNU汇编程序手册中得到了解释。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64838776

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档