如何在arm asm上编写PC相对寻址?

时间:2015-02-20 22:01:28

标签: assembly arm

我使用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中是否已经有一些现有的语法或宏?

3 个答案:

答案 0 :(得分:1)

ARMv8

请注意,在ARMv8中,PC不再像ARMv7一样被视为常规寄存器。

但是,对于ldr,有一种特殊的PC相对编码,称为“ LDR(文字)”,其语法为:

    ldr x0, pc_relative_ldr
    b 1f
pc_relative_ldr:
    .quad 0x123456789ABCDEF0
1:

GitHub upstream

尝试使用旧的“ 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

GitHub upstream

其中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)校正因子)。

由于我没有在我面前安装汇编程序,所以实际的实现仍然是读者的练习。