这是这个问题的后续。
我明白在MacO上编程arm64时,我不能写:
.text
ldr X0, =my_string
.data
my_string:
.asciz "Hello There"
但需要将
ldr
更改为:
adrp, X0, my_string@PAGE
add X0, X0, my_string@PAGEOFF
对于普通的非 MacOs 加载程序,第一个将变成:
ldr X0, fakeLabel
...
fakeLabel: .quad my_string
然后
fakeLabel
处的值将在链接时固定。
同时,对于 MacO 和非 MacO,链接器需要
ldrp
所在的页面与 my_string
为 1 的页面之间的差异,并将该增量放入 ldrp
指令中。
根据我在另一页上给出的答案,前者是“动态”加载,因此非法,而后者是“静态”加载,合法。我不确定我是否理解其中的区别,因为两者都涉及更改指令。
ldrp
和 my_string
之间的增量是否以某种方式固定?文本和数据加载时是否总是相隔一定距离,即使我们不知道它们将加载到哪里?
我错过了什么吗?这有记录吗?
问题不在于指令 - 所有指令修改都发生在链接时(=静态)。这些都不是问题,在链接时你可以做任何你想做的事。
问题出在
fakeLabel
。那里存储了一个指针。由于 ASLR,指针需要在运行时(=动态)重新设置基准。如果当前在 .text
段中并且编写了 ldr xN, =...
,那么编译器插入的 fakeLabel
也将在 .text
段中。当进程启动时,动态链接器将尽职尽责地尝试重新设置指针的基址,但由于该段被映射为只读,因此这样做会使进程崩溃。
如果
fakeLabel
在数据段中发出,这不会成为问题,但由于 PC 相对 ldr
的最大偏移量为 ±1MiB,并且由于链接器可以根据需要自由重新排列段,因此这是不可能的在一般情况下。如果可能的话,它必须发出 adrp+ldr
并让链接器将其修复回 PC 相关的 ldr
,但编译器不允许将一条指令变成两条指令,并且如果您手动使用两条指令.. . 好吧,那么你不妨立即使用 adrp+add
并避免使用隐式指针。