The Zero-initialisation code is as follows:
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
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.
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
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.
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.
The following code calculates the length of the image inclusive of its relocation data, and decides whether a move up store is possible.
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
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.
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
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).
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.
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.