Appendix A. Position-Independent Coding in Assembly Language

Several new assembler directives have been added to support generation of PIC. For more information on PIC, refer to the MIPS ABI Supplement and the PIC coding model it describes. For information on assembly language, refer to the MIPSpro Assembly Language Programmer's Guide.

The assembler generates PIC if either of two things occur:

Unless PIC is being generated, the other options in this section are ignored by the assembler, with the exception of .gpword, which becomes .word. Thus, you may easily use the same assembler file for generating PIC and non-PIC (i.e., non-shared) objects by not placing .option pic0 or .option pic2 in the assembler file and invoking the assembler without –KPIC (for non-shared) or with –KPIC (for PIC).

Examples

This following is a simplified version of the hello world program.

        .option pic2
        .data
        .align      2
$$5:
        .ascii      "hello world\X0A\X00"
        .text
        .align      2
main:
        .set        noreorder
        .cpload    $25
        .set       reorder
        subu       $sp, 40
        sw         $31, 36($sp)
        .cprestore 32
        la         $4, $$5
        jal        printf
        move       $2, $0
        lw         $31, 36($sp)
        addu       $sp, 40
        j          $31

The actual instructions generated by the assembler are:

        lui     gp,0            #
        addiu   gp,gp,0         # generated by .cpload
        addu    gp,gp,t9        #
        lw      a0,0(gp)        # gp-relative addressing used
        lw      t9,0(gp)        # t9 is used for func. call
        addiu   sp,sp,-40
        sw      ra,36(sp)
        sw      gp,32(sp)       # from .cprestore
        jalr    ra,t9           # jal is changed to jalr
        addiu   a0,a0,0
        lw      ra,36(sp)
        lw      gp,32(sp)       # activated by .cprestore
        move    v0,zero
        jr      ra
        addiu   sp,sp,40
        nop


Note: The MIPS ABI requires that register t9 ($25) be used for indirect function call, so .cpload should always use $25. No reorder mode should also be used. Also, programmers should make sure that t9 is dead before any function call.

If your program uses an indirect jump (jalr), you must also use t9 as the jump register.

If you have an unconditional jump to an external label:

j    _cerror

you have to rewrite it into indirect jump via t9:

la   t9,_cerror
j    t9

If you use branch-and-link (bal) instruction, and if the target procedure begins with a .cpload, you have to specify an alternate entry point:

foo: .set     noreorder # callee
     .cpload  $25
     .set     reorder
$$1:          ...       # alternate entry point
     ...
     j        $31       # foo returns
bar:          ...       # caller
     ...
bal  $$1                # by-pass the .cpload
     ...

This is very important because .cpload assumes register $25 contains the address of foo, but in this case $25 is not set up. Note that since both foo and bar reside in the same file, they must have the same value for gp. So the .cpload instructions can be and must be bypassed. However, since foo can still be called from outside, the .cpload is still required.

Alternatively (and less efficiently), if you don't want to have an alternate entry point, you can set up register $25 before the bal:

la      t9,foo
bal     foo

.gpword and .cpadd are used together to implement position-independent jump table (or any table of text addresses). Entries of the address table created by .gpword are converted into displacement from the context pointer. To get the correct text address, use .cpadd to add the value of gp back to them. Since the gp is updated by the run-time linker, the correct text address can be reconstructed regardless of the location of the DSO.