Jump to content United States-English
HP.com Home Products and Services Support and Drivers Solutions How to Buy
» Contact HP
More options
HP.com home
HP-UX Linker and Libraries User's Guide: HP 9000 Computers > Chapter 7 Position-Independent Code

Generating Position-Independent Code

» 

Technical documentation

Complete book in PDF
» Feedback
Content starts here

 » Table of Contents

 » Glossary

 » Index

To be position-independent, object code must restrict all references to code and data to either PC-relative or indirect references, where all indirect references are collected in a single linkage table that can be initialized on a per-process basis by dld.sl.

Register 19 (%r19) is the designated pointer to the linkage table. The linker generates stubs that ensure %r19 always points to the correct value for the target routine and that handle the inter-space calls needed to branch between shared libraries.

The linker generates an import stub for each external reference to a routine. The call to the routine is redirected to branch to the import stub, which obtains the target routine address and the new linkage table pointer value from the current linkage table; it then branches to an export stub for the target routine. In 32-bit mode, the linker generates an export stub for each externally visible routine in a shared library or program file. The export stub is responsible for trapping the return from the target routine in order to handle the inter-space call required between shared libraries and program files.

NOTE: The 64-bit mode linker does not require or support export stubs.

Shown below is the PIC code generated for import and export stubs. Note that this code is generated automatically by the linker; you don't have to generate the stubs yourself.

;Import Stub (Incomplete Executable)
X': ADDIL L'lt_ptr+ltoff,%dp ; get procedure entry point
LDW R'lt_ptr+ltoff(%r1),%r21
LDW R'lt_ptr+ltoff+4(%r1),%r19 ; get new r19 value.
LDSID (%r21),%r1
MTSP %r1,%sr0
BE 0(%sr0,%r21) ; branch to target
STW %rp,-24(%sp) ; save rp

;Import Stub (Shared Library)
X': ADDIL L'ltoff,%r19 ; get procedure entry point
LDW R'ltoff(%r1),%r21
LDW R'ltoff+4(%r1),%r19 ; get new r19 value
LDSID (%r21),%r1
MTSP %r1,%sr0
BE 0(%sr0,%r21) ; branch to target
STW %rp,-24(%sp) ; save rp

;Export Stub (Shared libs and Incomplete Executables)
X': BL,N X,%rp ; trap the return
NOP
LDW -24(%sp),%rp ; restore the original rp
LDSID (%rp),%r1
MTSP %r1,%sr0
BE,N 0(%sr0,%rp) ; inter-space return

For More Information:

The remainder of this section describes how compilers generate PIC for the following addressing situations:

You can use these guidelines to write assembly language programs that generate PIC object code. For details on assembly language, refer to the Assembler Reference Manual and PA-RISC 2.0 Architecture.

PIC Requirements for Compilers and Assembly Code

The linkage table pointer register, %r19, must be stored at %sp-32 by all PIC routines. This can be done once on procedure entry. %r19 must also be restored on return from a procedure call. The value should have been stored in %sp-32 (and possibly in a callee-saves register). If the PIC routine makes several procedure calls, the routine should copy %r19 into a callee-saves register as well, to avoid a memory reference when restoring %r19 upon return from each procedure call. Just like %r27 (%dp), the compilers treat %r19 as a reserved register whenever PIC mode is in effect.

In general, references to code are handled by the linker, and the compilers act differently only in the few cases where they would have generated long calls or long branches. References to data, however, need a new fixup request to identify indirect references through the linkage table, and the code generated will change slightly.

NOTE: Any code which is PIC or which makes calls to PIC must follow the standard procedure call mechanism.

When linking files produced by the assembler, the linker exports only those assembly language routines that have been explicitly exported as entry (that is, symbols of type ST_ENTRY). Compiler generated assembly code does not explicitly export routines with the entry type specified, so the assembly language programmer must ensure that this is done with the .EXPORT pseudo-op.

For example: In assembly language, a symbol is exported using

.EXPORT foo, type

where type can be code, data, entry, and others. To ensure that foo is exported from a shared library, the assembly statement must be:

.EXPORT foo,entry

Long Calls

Normally, the compilers generate a single-instruction call sequence using the BL instruction. The compilers can be forced to generate a long call sequence when the module is so large that the BL is not guaranteed to reach the beginning of the subspace. In the latter case, the linker can insert a stub. The existing long call sequence is three instructions, using an absolute target address:

     LDIL    L'target,%r1
BLE R'target(%sr4,%r1)
COPY %r1,%rp

When the PIC option is in effect, the compilers must generate the following instruction sequence, which is PC-relative:

      BL      .+8,%rp                    ; get pc into rp
ADDIL L'target - $L0 + 4, %rp ; add pc-rel offset to rp
LDO R'target - $L1 + 8(%r1), %r1
$L0: LDSID (%r1), %r31
$L1: MTSP %r31, %sr0
BLE 0(%sr0,%r1)
COPY %r31,%rp

Long Branches and Switch Tables

Long branches are similar to long calls, but are only two instructions because the return pointer is not needed:

     LDIL    L'target,%r1
BE R'target(%sr4,%r1)

For PIC, these two instructions must be transformed into four instructions, similar to the long call sequence:

     BL      .+8,%r1          ; get pc into r1
ADDIL L'target-L,%r1 ; add pc-relative offset
L: LDO R'target-L,%r1 ; add pc-relative offset
BV,N 0(%r1) ; and branch

The only problem with this sequence occurs when the long branch is in a switch table, where each switch table entry is restricted to two words. A long branch within a switch table must allocate a linkage table entry and make an indirect branch:

     LDW     T'target(%r19),%r1  ; load LT entry
BV,N 0(%r1) ; branch indirect

Here, the T' operator indicates a new fixup request supported by the linker for linkage table entries.

Assigned GOTO Statements

ASSIGN statements in FORTRAN must be converted to a PC-relative form. The existing sequence forms the absolute address in a register before storing it in the variable:

     LDIL    L'target,tmp
LDO R'target(tmp),tmp

This must be transformed into the following four-instruction sequence:

     BL      .+8,tmp         ; get rp into tmp
DEPI 0,31,2,tmp ; zero out low-order 2 bits
L: ADDIL L'target-L,tmp ; get pc-rel offset
LDO R'target-L(%r1),tmp

Literal References

References to literals in the text space are handled exactly like ASSIGN statements (shown above). The LDO instruction can be replaced with LDW as appropriate.

An opportunity for optimization in both cases is to share a single label (L) throughout a procedure, and let the result of BL become a common sub-expression. Thus only the first literal reference within a procedure is expanded to three instructions; the rest remain two instructions.

Global and Static Variable References

References to global or static variables currently require two instructions either to form the address of a variable, or to load or store the contents of the variable:

     ; to form the address of a variable
ADDIL L'var-$global$+x,%dp
LDO R'var-$global$+x(%r1),tmp
; to load the contents of a variable
ADDIL L'var-$global$+x,%dp
LDW R'var-$global$+x(%r1),tmp

These sequences must be converted to equivalent sequences using the linkage table pointer in %r19:

     ; to form the address of a variable
LDW T'var(%r19),tmp1
LDO x(tmp1),tmp2 ; omit if x == 0
; to load the contents of a variable
LDW T'var(%r19),tmp1
LDW x(tmp1),tmp2

Note that the T' fixup on the LDW instruction allows for a 14-bit signed offset, which restricts the DLT to be 16Kb. Because %r19 points to the middle of the DLT, we can take advantage of both positive and negative offsets. The T' fixup specifier should generate a DLT_REL fixup proceeded by an FSEL override fixup. If the FSEL override fixup is not generated, the linker assumes that the fixup mode is LD/RD for DLT_REL fixups. In order to support larger DLT table sizes, the following long form of the above data reference must be generated to reference tables that are larger. If the DLT table grows beyond the 16Kb limit, the linker emits an error indicating that the user must recompile using the +Z option which produces the following long-load sequences for data reference:

     ; form the address of a variable
ADDIL LT'var,%r19
LDW RT'var(%r1),tmp1
LDO x(tmp1),tmp2 ; omit if x == 0
; load the contents of a variable
ADDIL LT'var,%r19
LDW RT'var(%r1),tmp1
LDW x(tmp1),tmp2

Procedure Labels

The compilers already mark procedure label constructs so that the linker can process them properly. No changes are needed to the compilers.

When building shared libraries and incomplete executables, the linker modifies the plabel calculation (produced by the compilers in both shared libraries and incomplete executables) to load the contents of a DLT entry, which is built for each symbol associated with a CODE_PLABEL fixup.

In shared libraries and incomplete executables, a plabel value is the address of a PLT entry for the target routine, rather than a procedure address; therefore $$dyncall must be used when calling a routine with a procedure label. The linker sets the second-to-last bit in the procedure label to flag this as a special PLT procedure label. The $$dyncall routine checks this bit to determine which type of procedure label has been passed, and calls the target procedure accordingly.

In order to generate a procedure label that can be used for shared libraries and incomplete executables, assembly code must specify that a procedure address is being taken (and that a plabel is wanted) by using the P' assembler fixup mode. For example, to generate an assembly plabel, the following sequence must be used:

LDIL LP'function,%r1
LDO RP'function(%r1), %r22
; Now to call the routine
BL $$dyncall, %r31 ; r22 is the input register for $$dyncall
COPY %r31, %r2

This code sequence generates the necessary PLABEL fixups that the linker needs in order to generate the proper procedure label. The dyncall millicode routine in /usr/lib/milli.a must be used to call a procedure using this type of procedure label; that is, a BL or BV will not work).

Printable version
Privacy statement Using this site means you accept its terms Feedback to webmaster
© 1997 Hewlett-Packard Development Company, L.P.