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:
the directive .option pic2 appears in the assembler file
the assembler, as, is invoked with the –KPIC argument in the absence of an explicit .option pic0 directive in the file
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).
.option pic2
This directive forces the assembler to mark the output object file "PIC" and activates the following directives. It overrides the command line argument. Normally, you don't need to specify this directive. Instead, you should use the –KPIC or –non_shared options to toggle between generating PIC or non-PIC.
Even though –KPIC will be made the default for the high-level language drivers (such as cc, f77, and pc) in future releases, it will not be the default for assembly sources. You must explicitly specify –KPIC for compiling .s files.
.cpload reg
This directive expands into three instructions that sets the gp register to the context pointer value for the current function. The three instructions are:
lui gp,_gp_disp addui gp,gp,_gp_disp addu gp,gp,reg |
_gp_disp is a reserved symbol that the linker sets to the distance between the lui instruction and the context pointer. This directive is required at the beginning of each subroutine that uses the gp register.
You must add this directive at the beginning of every procedure, with the exception of leaf-procedures that do not access any global variables, and procedures that are static (i.e., not marked .globl or .extern).
.cprestore offset
This directive causes the assembler to issue:
sw gp,offset(sp) |
at the point where it appears. Additionally, it causes the assembler to emit:
lw gp,offset(sp) |
after every jump-and-link (jal) operation (but not after a branch-and-link (bal) operation), thereby restoring the gp register after function calls. The programmer is responsible for allocating the stack space for the gp. This space should be in the saved register area of the stack frame to remain consistent with MIPS' calling and debugger conventions.
.gpword local-sym
This directive is similar to .word except that the relocation entry for local-sym has the R_MIPS_GPREL32 type. After linkage, this results in a 32-bit value that is the distance between local-sym and the context pointer (that is, the gp). local-sym must be local. It is currently used for PIC switch tables.
.cpadd reg
This directive adds the value of the context pointer (gp) to reg.
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 |
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.