Howto write PC relative adressing on arm asm?

7

I'm using GNU AS as assembler and I'm trying to write a position independent binary and I have some problems writing the PC relative address of an external symbols. Normaly a relative address is loaded with

adr r0, symbol

but that only works for symbols defined in the same section in the assembly file. The other method to load a symbol is

ldr r0, =symbol

which stores the absolute address of the symbol in a constant and loads it pc relative from there. So you get code like this:

  48:   e59f0008        ldr     r0, [pc, #8]    ; 58 <text+0xc>
  ...
  58:   00000008        .word   0x00200018
                        58: R_ARM_ABS32 symbol

But what I need is a R_ARM_REL32 linker reference.

What I have come up with is this code:

tmp1:    ldr    r0, [pc, #tmp2 - tmp1 - 8]
         ldr    r0, [pc, r0]
tmp2:    .word  symbol - tmp1

This produces:

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

I can nearly put that into a macro so it is reusesable for whatever symbol I need. Except I don't know how to tell the assembler that tmp2 needs to go to the constants block instead of ending up right there in the middle of the code.

But isn't there already some existing syntax or macro for that in GNU AS?

assembly
arm

3 Answers

1

ARMv8

Note that in ARMv8, the PC cannot be treated as a regular register anymore as was the case for ARMv7.

There is however a special PC-relative encoding for ldr called "LDR (literal)" with syntax:

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

GitHub upstream.

Trying to do an old "LDR (register)" syntax as in:

ldr x0, [pc]

fails with:

64-bit integer or SP register expected at operand 2 -- `ldr x0,[pc]'

For some reason, there is not such PC-relative encoding for str. I think you just have to use adr + "STR (register)" as in:

    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.

where adr loads a PC-relative address into a register.

You might also be interested in ADRP if you need longer jumps, see: What are the semantics of ADRP and ADRL instructions in ARM assembly?

answered on Stack Overflow Feb 1, 2019 by Ciro Santilli • edited Feb 14, 2019 by Ciro Santilli
0

Don't define the word explicitly. How about something like:

  .text                                                                          
1:  ldr   r0, =symbol-1b                                                         
    ldr   r0, [pc, r0]

This gives something like:

[~] 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

with the right reloc:

RELOCATION RECORDS FOR [.text]:                                                  
OFFSET   TYPE              VALUE 
00000008 R_ARM_REL32       symbol

The relocated word will be placed somewhere after your code.

Caveat: I assembled this using clang's integrated assembler. I assume GNU as will handle the same thing since they try to be bug for bug compatible.

[Edit] Unfortunately, as Jester mentions in the comments, this doesn't work with GNU as. I don't think I've ever seen an example where clang's integrated assembler did something that the GNU assembler couldn't handle before.

answered on Stack Overflow Feb 20, 2015 by Richard Pennington • edited Feb 20, 2015 by Richard Pennington
-1

What you need to do is movw + movt an address into a register and then ldr relative to the PC. The challenge is that the address you're loading need to be pre-corrected for PC relative access. You can accomplish this by doing some pointer math in the constant passed to the mov instructions by subtracting the address of the ldr instruction from the absolute address of the symbol you're loading (plus a PC correction factor).

Since I don't have an assembler in front of me, actual implementation is left as an exercise to the reader.


User contributions licensed under CC BY-SA 3.0