Gravitational plate of three masses and a slashed discABC0Static engraved plate. Three-dimensional view is unavailable or reduced motion is requested.

← back to fieldarchive

archiveFeb 10, 2021

ARM: Loading Addresses into Registers

ADR/ADRL vs LDR Rd,=label for address materialization, jump-table patterns, and string-copy examples in ARM and Thumb.

Loading an address into a register

  • You often need an address in a register: a variable, a string constant, the start of a jump table, and so on.
  • Addresses are usually expressed as an offset from the current PC or from another register.
  • Direct load with ADR / ADRL, or load the address from a literal pool with LDR Rd, =label.

Direct loading with ADR and ADRL

  • ADR and ADRL are pseudo-instructions that build an address within range without a data load.
    • A program-relative expression: a label (optional offset) whose address is relative to the current PC.
    • A register-relative expression: a label (optional offset) relative to an address already in a general-purpose register.

Assembler expansion of ADR rn, label

  • If the address is in range: a single ADD or SUB that builds it.
  • If one instruction cannot reach it: an error.
- Offset range is ±255 bytes for non-word-aligned addresses,
- and ±1020 bytes (255 words) for word-aligned addresses.
- In Thumb the address must be word-aligned and the offset must be positive.

Assembler expansion of ADRL rn, label

  • If the address is in range: two data-processing instructions that build it.
  • If two instructions still cannot form it: an error.
- ADRL range is ±64KB for non-word-aligned addresses,
- and ±256KB for word-aligned addresses. (No Thumb ADRL; ARM code only.)
- On success ADRL always assembles to two instructions.
- The assembler emits both even when a single instruction could have done the job.

Labels used with ADR / ADRL must be in the same code section

  • Assembler error: label in the same section but out of range.
  • Linker error: label in a different code section and out of range.
  • In Thumb, ADR can only produce word-aligned addresses.
  • ADRL is unavailable in Thumb; use it in ARM code only.

Exercise 1

  • Code shapes the assembler emits when assembling ADR / ADRL.
AREA    adrlabel, CODE,READONLY
            ENTRY                          ; first ENTRY
Start
            BL      func                   ; branch to subroutine (func)
stop        MOV     r0, #0x18              ; angel_SWIreason_ReportException
            LDR     r1, =0x20026           ; ADP_Stopped_ApplicationExit
            SWI     0x123456               ; ARM semihosting SWI
            LTORG                          ; emit literals
func        ADR     r0, Start              ; => SUB r0, PC, #offset to Start
            ADR     r1, DataArea           ; => ADD r1, PC, #offset to DataArea
            ; ADR   r2, DataArea+4300      ; fails: offset not representable as ADD operand2
            ADRL    r2, DataArea+4300      ; => ADD r2, PC, #offset1
                                           ;    ADD r2, r2, #offset2
            MOV     pc, lr                 ; Return
DataArea    SPACE   8000                   ; Starting at the current location,
                                           ; clears a 8000 byte area of memory
                                           ; to zero
            END

Jump tables with ADR

  • Exercise 2 shows ARM code for a jump table.
  • ADR loads the table address.
  • arithfunc takes three arguments and returns in r0.
  • The first argument selects the operation on the second and third:
arg1=0
	Result = arg2 + arg3
arg1=1
	Result = arg2 - arg3
  • The table is built with the following instructions and directives.

EQU

  • Assembler directive that gives a symbol a value.
  • Here num gets 2; later uses of num substitute 2.
  • Same idea as #define for constants in C.

DCD

  • Declares one or more stored words.
  • Each DCD holds the address of the routine that handles one table entry.

LDR

  • LDR pc, [r3, r0, LSL #2] loads the chosen table entry into pc.
  • Multiplying the index in r0 by 4 yields a word offset.
  • That offset is added to the table base; the word at the combined address becomes the new PC.
AREA    Jump, CODE, READONLY     
        CODE32                           
num     EQU     2                        ; number of jump-table entries
        ENTRY                            
start                                    
        MOV     r0, #0                   
        MOV     r1, #3
        MOV     r2, #2
        BL      arithfunc                
stop    MOV     r0, #0x18                ; angel_SWIreason_ReportException
        LDR     r1, =0x20026             ; ADP_Stopped_ApplicationExit
        SWI     0x123456                 ; ARM semihosting SWI
arithfunc                                ; Label the function
        CMP     r0, #num                 ; treat function code as unsigned
        MOVHS   pc, lr                   ; If code is >= num then simply return
        ADR     r3, JumpTable            ; load JumpTable into r3
        LDR     pc, [r3,r0,LSL#2]        ; jump to the matching routine
JumpTable
        DCD     DoAdd
        DCD     DoSub
DoAdd   ADD     r0, r1, r2               ; Operation 0
        MOV     pc, lr                   ; Return
DoSub   SUB     r0, r1, r2               ; Operation 1
        MOV     pc, lr                   ; Return
        END                              ; Mark the end of this file

Converting to Thumb

  • Same jump-table idea in Thumb.
  • Thumb cannot:
    • Auto-increment the base register of LDR/STR the same way
    • Load a value into PC with LDR in this pattern
    • Apply an inline shift from a register on the LDR
AREA    Jump, CODE, READONLY
        CODE16                          
num     EQU     2
        ENTRY
start
        MOV     r0, #0
        MOV     r1, #3
        MOV     r2, #2
        BL      arithfunc
stop    MOV     r0, #0x18
        LDR     r1, =0x20026
        SWI     0xAB                     ; Thumb semihosting SWI
arithfunc
        CMP     r0, #num
        BHS     exit                     ; MOV pc, lr cannot be conditional
        ADR     r3, JumpTable
        LSL     r0, r0, #2               ; 3 instructions needed to replace
        LDR     r0, [r3,r0]              ; LDR pc, [r3,r0,LSL#2]
        MOV     pc, r0
        ALIGN                            ; Ensure that the table is aligned on a
                                         ; 4-byte boundary
JumpTable
        DCD     DoAdd
        DCD     DoSub
DoAdd   ADD     r0, r1, r2
exit    MOV     pc, lr
DoSub   SUB     r0, r1, r2
        MOV     pc, lr
        END

Loading addresses with LDR Rd, =label

  • The LDR Rd, = pseudo-instruction can load a 32-bit constant into a register.

  • It also accepts program-relative expressions such as labels and labels-with-offsets.

  • It places the label address in a literal pool (a patch of memory in the code that holds constants).

  • Then it emits a PC-relative LDR that reads that pool entry.

LDR      rn [pc, #offset to literal pool] 	; load register n with one word
												; from the address [pc + offset]
  • Unlike ADR/ADRL, LDR can target labels outside the current section.

  • If the label is elsewhere, the assembler plants a relocation directive in the object code.

  • The linker resolves the address at link time.

  • Wherever the linker places the section that holds the LDR and its literal pool, the address stays valid.

  • Exercise 3

AREA    LDRlabel, CODE,READONLY
        ENTRY                              
start
        BL      func1                      
        BL      func2                      
stop    MOV     r0, #0x18                  ; angel_SWIreason_ReportException
        LDR     r1, =0x20026               ; ADP_Stopped_ApplicationExit
        SWI     0x123456                   ; ARM semihosting SWI
func1
        LDR     r0, =start                 ; => LDR R0,[PC, offset to literal pool 1]
        LDR     r1, =Darea + 12            ; => LDR R1,[PC, offset to literal pool 1]
        LDR     r2, =Darea + 6000          ; => LDR R2, [PC, offset to literal pool 1]
        MOV     pc,lr                      ; Return
        LTORG                              ; Literal Pool 1
func2
        LDR     r3, =Darea + 6000          ; => LDR r3, [PC, offset to literal pool 1]
                                           ; shares the earlier literal
        ; LDR   r4, =Darea + 6004          ; uncommenting errors:
                                           ; literal pool 2 is out of range
        MOV     pc, lr                     ; Return
Darea   SPACE   8000                       ; start at current location
                                           ; clear 8000 bytes
        END                                ; literal pool 2 is out of range
                                           ; the LDR instructions above

String-copy exercise with LDR Rd, =label

  • ARM routine that overwrites one string with another.
  • LDR pseudo-instructions load both string addresses from the data section.

DCB

  • Defines one or more stored bytes.
  • Besides integers, DCB accepts quoted strings; each character lands in a successive byte.

LDR / STR

  • LDR and STR here use post-indexed addressing to bump the address registers.
LDRB    r2,[r1],#1
  • Load r2 from the address in r1, then add 1 to r1.
AREA    strCopy, CODE, READONLY
        ENTRY                             
start   LDR     r1, =srcstr               ; pointer to first string
        LDR     r0, =dststr               ; pointer to second string
        BL      strCopy                   ; call copy subroutine
stop    MOV     r0, #0x18                 ; angel_SWIreason_ReportException
        LDR     r1, =0x20026              ; ADP_Stopped_ApplicationExit
        SWI     0x123456                  ; ARM semihosting SWI
strCopy
        LDRB    r2, [r1],#1               ; load byte and update address
        STRB    r2, [r0],#1               ; store byte and update address
        CMP     r2, #0                    ; Check for zero terminator
        BNE     strCopy                   ; Keep going if not
        MOV     pc,lr                     ; Return
        AREA    Strings, DATA, READWRITE
srcstr  DCB     "First string - source",0
dststr  DCB     "Second string - destination",0
        END

Thumb conversion

  • Thumb LDR/STR have no post-indexed addressing mode.
  • After each LDR/STR, bump the address with ADD.
LDRB  r2, [r1]        ; load register 2
ADD   r1, #1          ; increment the address in
                      ; register 1.

related

  1. Feb 10, 2021/archiveARM Data Transfer: Pre/Post-Index and STR/LDR
  2. Feb 10, 2021/archiveARM LDR Rd, =const
  3. Feb 9, 2021/archiveARM Assembly 1

graphfeed