我使用GNU AS作为汇编程序而我正在尝试编写与位置无关的二进制文件,并且在编写外部符号的PC相对地址时遇到了一些问题。 Normaly相对地址加载了
adr r0, symbol
但这仅适用于程序集文件中同一部分中定义的符号。加载符号的另一种方法是
ldr r0, =symbol
将符号的绝对地址存储在常量中,并从那里加载pc。所以你得到这样的代码:
48: e59f0008 ldr r0, [pc, #8] ; 58 <text+0xc>
...
58: 00000008 .word 0x00200018
58: R_ARM_ABS32 symbol
但我需要的是 R_ARM_REL32 链接器引用。
我想出的是这段代码:
tmp1: ldr r0, [pc, #tmp2 - tmp1 - 8]
ldr r0, [pc, r0]
tmp2: .word symbol - tmp1
这会产生:
0000003c <tmp1>:
3c: e59f0000 ldr r0, [pc] ; 44 <tmp2>
40: e79f0000 ldr r0, [pc, r0]
00000044 <tmp2>:
44: 00000008 .word 0x00000008
44: R_ARM_REL32 symbol
我几乎可以将它放入一个宏中,因此它可以重用于我需要的任何符号。除非我不知道如何告诉汇编程序tmp2需要转到常量块而不是在代码中间结束。
但是在GNU AS中是否已经有一些现有的语法或宏?
答案 0 :(得分:1)
ARMv8
请注意,在ARMv8中,PC不再像ARMv7一样被视为常规寄存器。
但是,对于ldr,有一种特殊的PC相对编码,称为“ LDR(文字)”,其语法为:
ldr x0, pc_relative_ldr
b 1f
pc_relative_ldr:
.quad 0x123456789ABCDEF0
1:
尝试使用旧的“ LDR(注册)”语法,如下所示:
ldr x0, [pc]
失败:
64-bit integer or SP register expected at operand 2 -- `ldr x0,[pc]'
由于某种原因,str没有这种PC相对编码。我认为您只需要使用adr
+“ STR(register)”,如下所示:
adr x1, pc_relative_str
ldr x0, pc_relative_ldr
str x0, [x1]
ldr x0, pc_relative_str
.data
pc_relative_str:
.quad 0x0000000000000000
其中adr
将PC相对地址加载到寄存器中。
如果您需要更长的跳跃时间,可能还对ADRP感兴趣,请参见:What are the semantics of ADRP and ADRL instructions in ARM assembly?
答案 1 :(得分:0)
不要明确定义单词。怎么样:
.text
1: ldr r0, =symbol-1b
ldr r0, [pc, r0]
这就是:
[~] dev% ~/ellcc/bin/ecc-objdump -d test.o
test.o: file format elf32-bigarm
Disassembly of section .text:
00000000 <.text>:
0: e59f0000 ldr r0, [pc] ; 8 <.text+0x8>
4: e79f0000 ldr r0, [pc, r0]compatable
8: 00000008 .word 0x00000008
使用正确的reloc:
RELOCATION RECORDS FOR [.text]:
OFFSET TYPE VALUE
00000008 R_ARM_REL32 symbol
重新定位的单词将放在代码之后的某处。
警告:我使用clang的集成汇编程序组装了这个。我认为GNU会处理相同的事情,因为他们试图成为bug兼容的bug。
[编辑] 不幸的是,正如Jester在评论中提到的那样,这不适用于GNU。我不认为我曾经见过一个例子,其中clang的集成汇编程序做了GNU汇编程序以前无法处理的东西。
答案 2 :(得分:-1)
您需要做的是movw
+ movt
地址到寄存器中,然后ldr
相对于PC。挑战在于,您需要为PC相对访问预先校正您要加载的地址。你可以通过在传递给mov
指令的常量中做一些指针数学运算来实现这一点,方法是从你正在加载的符号的绝对地址中减去ldr
指令的地址(加上一台PC)校正因子)。
由于我没有在我面前安装汇编程序,所以实际的实现仍然是读者的练习。