(******************************************************************) (* *) (* Copyright (c) 1978 Regents of the University of California. *) (* Permission to copy or distribute this software or documen- *) (* tation in hard or soft copy granted only by written license *) (* obtained from the Institute for Information Systems. *) (* *) (******************************************************************) {$U-,R+ UCSD PASCAL SYSTEM PROGRAM LINKER Written summer '78 by Roger T. Sumner, IIS Copyright (c) 1978, Regents of the University of California All hope abandon ye who enter here -Dante } program systemlevel; const SYSPROG = 4; var syscom: ^integer; gfiles: array [0..5] of integer; userinfo: record filler: array [0..4] of integer; slowterm, stupid: boolean; altmode: char; gotsym, gotcode: boolean; workvid, symvid, codevid: string[7]; worktid, symtid, codetid: string[15] end; filler: array [0..4] of integer; syvid, dkvid: string[7]; junk1, junk2: integer; cmdstate: integer; { * The linker is made up of three phases: * Phase1 which open all input files, reads up seg tables * from them and decides which segments are to be * linked into the final code file. * Phase2 reads the linker info for each segment that is * going to be used, either to select sep procs from * or copy with modifications into output code. * The main symbol tree are built here, one for each * code segment. * Phase3 does the crunching of code segments into their * final form by figuring out the procs that need to * be linked in, resolves all references (PUBLREF, * GLOBREF, etc), patches the code pointed to by their * reflists, and writes the final code seg(s). } segment procedure linker(iii, jjj: integer); const MAXSEG = 15; { max code seg # in code files } MAXSEG1 = 16; { MAXSEG+1, useful for loop vars } MASTERSEG = 1; { USERHOST segment number # } FIRSTSEG = 7; { first linker assignable seg # } MAXFILE = 7; { number of lib files we can use } MAXLC = MAXINT; { max compiler assigned address } MAXIC = 14000; { max number bytes of code per proc } MAXPROC = 160; { max legal procedure number } MSDELTA = 12; { mark stack size for pub/priv fixup } type { subranges } { --------- } segrange = 0..MAXSEG; { seg table subscript type } segindex = 0..MAXSEG1; { wish we had const expressions! } lcrange = 1..MAXLC; { base offsets a la P-code } icrange = 0..MAXIC; { legal length for proc/func code } procrange = 1..MAXPROC; { legit procedure numbers } { miscellaneous } { ------------- } alpha = packed array [0..7] of char; diskblock = packed array [0..511] of 0..255; codefile = file; { trick compiler to get ^file } filep = ^codefile; codep = ^diskblock; { space management...non-PASCAL kludge } { link info structures } { ---- ---- ---------- } placep = ^placerec; { position in source seg } placerec = record srcbase, destbase: integer; length: icrange end { placerec } ; refp = ^refnode; { in-core version of ref lists } refnode = record next: refp; refs: array [0..7] of integer; end { refnode } ; litypes = (EOFMARK, { end-of-link-info marker } { ext ref types, designates } { fields to be updated by linker } UNITREF, { refs to invisibly used units (archaic?) } GLOBREF, { refs to external global addrs } PUBLREF, { refs to BASE lev vars in host } PRIVREF, { refs to BASE vars, allocated by linker } CONSTREF, { refs to host BASE lev constant } { defining types, gives } { linker values to fix refs } GLOBDEF, { global addr location } PUBLDEF, { BASE var location } CONSTDEF, { BASE const definition } { proc/func info, assem } { to PASCAL and PASCAL } { to PASCAL interface } EXTPROC, { EXTERNAL proc to be linked into PASCAL } EXTFUNC, { " func " " " " " } SEPPROC, { Separate proc definition record } SEPFUNC, { " func " " } SEPPREF, { PASCAL ref to a sep proc } SEPFREF); { " ref to a sep func } liset = set of litypes; opformat = (WORD, BYTE, BIG); { instruction operand field formats } lientry = record { format of link info records } name: alpha; case litype: litypes of SEPPREF, SEPFREF, UNITREF, GLOBREF, PUBLREF, PRIVREF, CONSTREF: (format: opformat; { how to deal with the refs } nrefs: integer; { words following with refs } nwords: lcrange; { size of private or nparams } reflist: refp); { list of refs after read in } EXTPROC, EXTFUNC, SEPPROC, SEPFUNC: (srcproc: procrange; { the procnum in source seg } nparams: integer; { words passed/expected } place: placep); { position in source/dest seg } GLOBDEF: (homeproc: procrange; { which proc it occurs in } icoffset: icrange); { its byte offset in pcode } PUBLDEF: (baseoffset: lcrange); { compiler assign word offset } CONSTDEF: (constval: integer); { users defined value } EOFMARK: (nextlc: lcrange) { private var alloc info } end { lientry } ; { symbol table items } { ------ ----- ----- } symp = ^symbol; symbol = record llink, rlink, { binary subtrees for diff names } slink: symp; { same name, diff litypes } entry: lientry { actual id information } end { symbol } ; { segment information } { ------- ----------- } segkinds =(LINKED, { no work needed, executable as is } HOSTSEG, { PASCAL host program outer block } SEGPROC, { PASCAL segment procedure, not host } UNITSEG, { library unit occurance/reference } SEPRTSEG); { library separate proc/func TLA segment } finfop = ^fileinforec; { forward type dec } segp = ^segrec; { this structure provides access to all } segrec = record { info for segs to be linked to/from } srcfile: finfop; { source file of segment } srcseg: segrange; { source file seg # } symtab: symp; { symbol table tree } case segkind: segkinds of SEPRTSEG: (next: segp) { used for library sep seg list } end { segrec } ; { host/lib file access info } { ---- --- ---- ------ ---- } I5segtbl = record { first full block of all code files } diskinfo: array [segrange] of record codeleng, codeaddr: integer end { diskinfo } ; segname: array [segrange] of alpha; segkind: array [segrange] of segkinds; filler: array [0..143] of integer end { I5segtbl } ; filekind = (USERHOST, USERLIB, SYSTEMLIB); fileinforec = record next: finfop; { link to next file thats open } code: filep; { pointer to PASCAL file...sneaky! } fkind: filekind; { used to validate the segkinds } segtbl: I5segtbl { disk seg table w/ source info } end { fileinforec } ; var hostfile, { host file info ptr, its next = libfiles } libfiles: finfop; { list of lib files, user and system } seplist: segp; { list of sep segs to search through } reflitypes: liset; { those litypes with ref lists } talkative, useworkfile: boolean; errcount: integer; heapbase: ^integer; hostsp: segp; { ptr to host prog outer block } nextbaselc: lcrange; { next base offset for private alloc } seginfo: array [segrange] of segp; { seg is available if NIL } nextseg: segindex; { next slot in seginfo available } mapname: string[40]; f0, f1, f2, f3, f4, f5, f6, f7, { input files with lurking pntrs } code: codefile; { output code file, *system.wrk.code } { * Print an error message and bump * the error counter. } procedure error(msg: string); var ch: char; begin writeln(msg); repeat write('Type (continue), (terminate)'); read(keyboard, ch); if ch = userinfo.altmode then exit(linker) until ch = ' '; errcount := errcount+1 end { error } ; { * Routines to access object code segments. There * is subtle business involving byte flipping with * the 16-bit operations. This needs more research * when the time comes. } {$R-} function fetchbyte(cp: codep; offset: integer): integer; begin fetchbyte := cp^[offset] end { fetchbyte } ; function fetchword(cp: codep; offset: integer): integer; var i: integer; begin moveleft(cp^[offset], i, 2); { byte swap i } fetchword := i end { fetchword } ; procedure storebyte(val: integer; cp: codep; offset: integer); begin cp^[offset] := val end { storebyte } ; procedure storeword(val: integer; cp: codep; offset: integer); begin { byte swap val } moveleft(val, cp^[offset], 2) end { storeword } ; {$R+} { * Enter newsym in symtab tree. The tree is binary for * different names and entries with the same name are entered * onto sideways links (slink). No check is made for dup * entry types, caller must do that. Nodes on slink will * always have NIL rlink and llink. } procedure entersym(newsym: symp; var symtab: symp); var syp, lastsyp: symp; useleft: boolean; begin newsym^.llink := NIL; newsym^.rlink := NIL; newsym^.slink := NIL; if symtab = NIL then symtab := newsym else begin { search symtab and add newsym } syp := symtab; repeat lastsyp := syp; if syp^.entry.name > newsym^.entry.name then begin syp := syp^.llink; useleft := TRUE end else if syp^.entry.name < newsym^.entry.name then begin syp := syp^.rlink; useleft := FALSE end else { equal } begin { add into sideways list } newsym^.slink := syp^.slink; syp^.slink := newsym; lastsyp := NIL; { already added flag } syp := NIL { stop repeat loop } end until syp = NIL; if lastsyp <> NIL then begin { add to bottom of tree } if useleft then lastsyp^.llink := newsym else lastsyp^.rlink := newsym end end { symtab <> NIL } end { entersym } ; { * Look up name in symtab tree and return pointer * to it. Oktype restricts what litype is * acceptable. NIL is returned if name not found. } function symsrch(var name: alpha; oktype: litypes; symtab: symp): symp; var syp: symp; begin symsrch := NIL; syp := symtab; while syp <> NIL do if syp^.entry.name > name then syp := syp^.llink else if syp^.entry.name < name then syp := syp^.rlink else { equal name } if syp^.entry.litype <> oktype then syp := syp^.slink else { found! } begin symsrch := syp; syp := NIL end end { symsrch } ; { * Search for the occurance of the unit segment * given by name in the list of files in fp. * Return the file and segment number in seg. * NIL is returned for non-existant units and * an error is given. } function unitsrch(fp: finfop; var name: alpha; var seg: segrange): finfop; label 1; var s: segindex; begin seg := 0; while fp <> NIL do begin with fp^.segtbl do for s := 0 to MAXSEG do if segname[s] = name then if segkind[s] = UNITSEG then goto 1; fp := fp^.next end; write('Unit ', name); error(' not found'); s := 0; 1: seg := s; unitsrch := fp end { unitsrch } ; { * Alphabetic returns TRUE if name contains all legal * characters for PASCAL identifiers. Used to validate * segnames and link info entries. } function alphabetic(var name: alpha): boolean; label 1; var i: integer; begin alphabetic := FALSE; for i := 0 to 7 do if not (name[i] in ['A'..'Z', '0'..'9', ' ', '_']) then goto 1; alphabetic := TRUE; 1: end { alphabetic } ; { * Getcodep is a sneaky routine to point codep's anywhere * in memory. It violates Robot's Rules of Order, but is * very useful for dealing with the variable size segments } function getcodep(memaddr: integer): codep; var r: record case boolean of TRUE: (i: integer); FALSE: (p: codep) end; begin r.i := memaddr; getcodep := r.p end { getcodep } ;