archiveFeb 10, 2021
ARM LDR Rd, =const
How the LDR Rd, =const pseudo-instruction builds 32-bit immediates via MOV/MVN or a literal pool, and when LTORG keeps the pool in range.
LDR Rd, =const
The LDR Rd, =const pseudo-instruction builds a 32-bit numeric constant in one logical step. Use it when the value sits outside what MOV / MVN can encode directly.
The assembler picks the cheapest form
If MOV or MVN can synthesize the constant, the assembler emits that instruction. Otherwise it:
- Places the value in a literal pool (a slice of memory embedded in the code for constants)
- Emits a PC-relative
LDRthat reads that pool entry
LDR rn, [pc, #offset to literal pool] ; load register n with one word
; from the address [pc + offset]You still need the literal pool to fall inside the range of that generated LDR.
Where literal pools go
By default the assembler drops a literal pool at the end of each section — bounded by the next AREA or a final END. An END at the end of an included file does not close the section.
In a large section the default pool can drift out of range of one or more LDRs. PC-to-constant offset limits:
- ARM state: less than 4 KB, either direction
- Thumb state: less than 1 KB forward
Reusing constants across pools
When LDR Rd, =const needs a pool entry, the assembler first checks whether an earlier in-range pool already holds that value. If not, it tries the next pool.
When the next pool is still out of range
Out of range, the assembler errors. Insert an LTORG so an extra pool appears within 4 KB (ARM) or 1 KB (Thumb) after the failing pseudo-instruction.
LTORG
LTORG assembles the current literal pool immediately. Section ends still get a default pool via the next AREA or assembly END. Those defaults can miss some LDR / LDFD / LDFS pseudo-ops — LTORG keeps them reachable.
Do not let the CPU execute the pool
Put pools after an unconditional branch or after a subroutine return so the core never treats constants as instructions.
AREA Loadcon, CODE, READONLY
ENTRY
start BL func1 ; Branch 첫 번째 서브루틴
BL func2 ; Branch 두 번째 서브루틴
stop MOV r0, #0x18 ; angel_SWIreason_ReportException
LDR r1, =0x20026 ; ADP_Stopped_ApplicationExit
SWI 0x123456 ; ARM semihosting SWI
func1
LDR r0, =42 ; => MOV R0, #42
LDR r1, =0x55555555 ; => LDR R1, [PC, #offset to
; Literal Pool 1]
LDR r2, =0xFFFFFFFF ; => MVN R2, #0
MOV pc, lr
LTORG ; Literal Pool 1 contains
; literal Ox55555555
func2
LDR r3, =0x55555555 ; => LDR R3, [PC, #offset to
; Literal Pool 1]
; LDR r4, =0x66666666 ; 만약에 주석 처리가 아니라면
; 실패한다. 왜냐하면 두 개의 리터럴 풀이기 때문
; 범위에 벗어난다.
MOV pc, lr
LargeTable
SPACE 4200 ; Starting at the current location,
; clears a 4200 byte area of memory
; to zero
END ; Literal Pool 2 is empty