ZeroInit
NOP ; or
SUB ip, lr, pc ; base+12+[PSR]-(ZeroInit+12+[PSR])
; = base-ZeroInit
ADD ip, pc, ip ; base-ZeroInit+ZeroInit+16 = base+16
LDMIB ip, {r0,r1,r2,r4} ; various sizes
SUB ip, ip, #16 ; image base
ADD ip, ip, r0 ; + rO size
ADD ip, ip, r1 ; + RW size = base of 0-init area
MOV r0, #0
MOV r1, #0
MOV r2, #0
MOV r3, #0
CMPS r4, #0
00 MOVLE pc, lr ; nothing left to do
STMIA ip!, {r0,r1,r2,r3} ; always zero a multiple of 16 bytes
SUBS r4, r4, #16
B %B00
Self-move and self-relocation code
This code is added to the end of an AIF image by the linker, immediately before the list of relocations (which is terminated by -1). Note that the code is entered via a BL from the second word of the AIF header so, on entry, r14 -> AIFHeader + 8. In 26-bit ARM modes, r14 also contains a copy of the PSR flags.
On entry, the relocation code calculates the address of the AIF header (in a CPU-independent fashion) and decides whether the image needs to be moved. If the image doesn't need to be moved, the code branches to RelocateOnly.
If the image needs to be moved up memory, then the top of memory has to be found. Here, a system service (SWI 0x10) is called to return the address of the top of memory in r1. This is, of course, system specific and should be replaced by whatever code sequence is appropriate to the environment.
RelocCode
NOP ; required by ensure_byte_order()
; and used below.
SUB ip, lr, pc ; base+8+[PSR]-(RelocCode+12+[PSR])
; = base-4-RelocCode
ADD ip, pc, ip ; base-4-RelocCode+RelocCode+16 = base+12
SUB ip, ip, #12 ; -> header address
LDR r0, RelocCode ; NOP
STR r0, [ip, #4] ; won't be called again on image re-entry
LDR r9, [ip, #&2C] ; min free space requirement
CMPS r9, #0 ; 0 => no move, just relocate
BEQ RelocateOnly
The following code calculates the length of the image inclusive of its relocation data, and decides whether a move up store is possible.
LDR r0, [ip, #&20] ;image zero-init size
ADD r9, r9, r0 ;space to leave = min free + zero init
SWI #&10 ;return top of memory in r1.
Finally, the image copies itself four words at a time, being careful about the direction of copy, and jumping to the copied copy code as soon as it has copied itself.
ADR r2, End ; -> End
01 LDR r0, [r2], #4 ; load relocation offset, increment r2
CMNS r0, #1 ; terminator?
BNE %B01 ; No, so loop again
SUB r3, r1, r9 ; MemLimit - freeSpace
SUBS r0, r3, r2 ; amount to move by
BLE RelocateOnly ; not enough space to move...
BIC r0, r0, #15 ; a multiple of 16...
ADD r3, r2, r0 ; End + shift
ADR r8, %F02 ; intermediate limit for copy-up
Whether the image has moved itself or not, control eventually arrives here, where the list of locations to be relocated is processed. Each location is word sized and is relocated by the difference between the address the image was loaded at (the address of the AIF header) and the address the image was linked at (stored at offset 0x28 in the AIF header).
02 LDMDB r2!, {r4-r7}
STMDB r3!, {r4-r7}
CMPS r2, r8 ; copied the copy loop?
BGT %B02 ; not yet
ADD r4, pc, r0
MOV pc, r4 ; jump to copied copy code
03 LDMDB r2!, {r4-r7}
STMDB r3!, {r4-r7}
CMPS r2, ip ; copied everything?
BGT %B03 ; not yet
ADD ip, ip, r0 ; load address of code
ADD lr, lr, r0 ; relocated return address
You can customise the self-relocation and self-moving code generated by armlink by providing your version of it in an area called AIF_RELOC in the first object file in armlink's input list.
RelocateOnly
LDR r1, [ip, #&28] ; header + 0x28
; = code base set by Link
SUBS r1, ip, r1 ; relocation offset
MOVEQ pc, lr ; relocate by 0 so nothing to do
STR ip, [ip, #&28] ; new image base
; = actual load address
ADR r2, End ; start of reloc list
04 LDR r0, [r2], #4 ; offset of word to relocate
CMNS r0, #1 ; terminator?
MOVEQ pc, lr ; yes => return
LDR r3, [ip, r0] ; word to relocate
ADD r3, r3, r1 ; relocate it
STR r3, [ip, r0] ; store it back
B %B04 ; and do the next one
End ; List of offsets of locations to
; relocate starts here,
; terminated by -1.