Zero-initialisation code


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

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.

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.