Version II.0, February 1979
The UCSD LINKER allows the user to combine pre-compiled files, which may have been written either in PASCAL or in assembly language, into the system workfile. The user may wish to incorporate certain useful routines into programs without having to rewrite or even recompile these routines. For example, one might wish to use a fast assembly language routine for some “real-time” application. This routine could be assembled separately, stored in a library, and eventually accessed via the LINKER.
To link in routines (either procedures or functions), the calling program declares those routines to be EXTERNAL, much as PROCEDUREs or FUNCTIONs may be declared FORWARD (see Section 3.3.1). This notifies the compiler that the routines may be called, but are not provided yet. The compiler will inform the system that linking is required before execution.
The LINKER is also used to link in UNITs. A UNIT is a group of related routines which will be used together to perform a common task. UCSD TURTLEGRAPHICS is an example of a UNIT containing procedures and functions with which a “turtle” can be moved on the screen. A UNIT can be used by typing the reserved word USES <unitname> directly after the PROGRAM <identifier>. For more information on UNITs, see Section 3.3.2.
Any files which reference UNITs or EXTERNAL routines and have not yet been linked may be compiled and saved, but will need to be linked before they can be executed.
1.8.1 Using the Linker
If the program in the workfile contains EXTERNAL declarations, or uses UNITs, typing R(un will automatically invoke the LINKER after the compiler. The LINKER will search the file *SYSTEM.LIBRARY for the routines or UNITs specified, and will link them into the workfile. If the UNIT or EXTERNALly declared routine is not present in *SYSTEM.LIBRARY, the LINKER will respond with an appropriate message:
Unit, Proc, Func, Global, or Public <identifier> undefined
The LINKER may also be invoked explicitly, and, in fact, must be invoked explicitly in cases where
In order to explicitly invoke the LINKER, the user types ‘L’ at Command level and receives the prompt:
Host file?The hostfile is the file into which the routines or UNITs are to be linked. The LINKER appends .CODE to all file names typed in except for *<ret>. Typing a <ret> in response to the prompt causes the LINKER to use the workfile as the hostfile. The LINKER then asks for the name(s) of the library files in which the UNITs or EXTERNAL routines are to be found:
Up to eight library files may be referenced. Typing ‘*’ in response to a request for a libfile name will cause the LINKER to reference *SYSTEM.LIBRARY. The user will be notified about each library file that is successfully opened.Lib file? <codefile identifier> Lib file? <codefile identifier>
Example:
Lib file? * <ret> Opening *SYSTEM.LIBRARY
For information on LIBRARIES and the LIBRARIAN see Section 4.2.
When all relevant libfile names have been entered the user must type <ret> to proceed. The LINKER will now prompt with:
Map file? <file identifier> <ret>The LINKER writes the map file to the file requested by the user. The map file contains relevant LINKER info regarding the linking process. Responding with <ret> to this prompt will suspend this option. Note that .TEXT is appended unless a ‘.’ is the last letter of the filename.
The LINKER now reads up all segments required to enable the linking process. The user is now prompted to enter the destination file for the linked code output (this will often be the same file name as that of the host file). Linking will commence after the <ret> following the output file name has been typed. An empty line, <ret> only, causes the output file to be placed in the workfile e.g. *SYSTEM.WRK.CODE.
During the linking process the linker will report on all segments being linked as well as all external routines being copied into the output codefile. The linking process will be aborted if any required segments or routines are missing or undefined. The user will be informed of their absence with messages as described at the beginning of this section.
1.8.2 Linker Conventions and Implementation
Codefiles may contain up to 16 segments. Block 0 of a codefile contains information regarding name, kind, relative address and length of each code segment. This information is called the segtable, and is represented as a record:
RECORD DISKINFO: ARRAY[0..15] OF RECORD CODELENG, CODEADDR: INTEGER END; SEGNAME: ARRAY[0..15] OF PACKED ARRAY[O..7] OF CHAR; SEGKIND: ARRAY[0..15] OF (LINKED, HOSTSEG, SEGPROC, UNITSEG, SEPRTSEG); TEXTADDR: ARRAY[0..15] OF INTEGER; END;
CODELENG and CODEADDR give, respectively, the length of the code segment in bytes, and the block address of the code segment. A description of SEGKINDs follows:
For an unlinked code segment (that is, a segment containing unresolved external references) the compiler generates linker information. This information is a series of variable-length records, one for each UNIT, routine or variable which is referenced in, but not defined in the source. The first 8 words of each record contain the following information:
LITYPES = (EOFMARK, UNITREF, GLOBREF, PUBLREF, PRIVREF, CONSTREF, GLOBDEF, PUBLDEF, CONSTDEF, EXTPROC, EXTFUNC, SEPPROC, SEFFUNC, SEPPREF, SEPFREF); LIENTRY = RECORD NAME: ALPHA; CASE LITYPE: LITYPES OF UNITREF, GLOBREF, PUBLREF, PRIVREF, SEP PREF, SEPFREF, CONSTREF: (FORMAT: OFFORMAT; {format of lientry.name can be any of BIG, BYTE or WORD.} NREFS: INTEGER; {# of references to lientry.name in compiled code segment) NWORDS: LCRANGE); {size of privates in words} GLOBDEF: (HOMEPROC: PROCRANGE; {which procedure it occurs in} ICOFFSET: ICRANGE); {byte offset in p-code} PUBLDEF: (BASEOFFSET: LCRANGE); {compiler assigned word offset} CONSTDEF: (CONSTVAL: INTEGER); {users defined value} EXTPROC, EXTFUNC, SEPPROC, SEPFUNC: (SRCPROC: PROCRANGE; {procedure number in source segment} NPARAMS: INTEGER); {number of parameters expected} EOFMARK: (NEXTBASELC: LCRANGE) {private var allocation info} END {lientry};
If the LITYPE is one of the first case variant, then following this portion of the record is a list of pointers into the code segment. Each of these pointers is the absolute byte address within the code segment of a reference to the variable, UNIT or routine named in the lientry. These are 8 word records, but only the first NREFs of them are valid.