{$S+} (******************************************************************) (* *) (* 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. *) (* *) (******************************************************************) {$G+,I- LIBRARY MAPPER UTILITY written in a great hurry using .... UCSD PASCAL SYSTEM PROGRAM LINKER Written September 78 by Robert Hofkin Major portions stolen from Roger T. Sumner } program LIBMAP; const MAXSEG = 15; { max code seg # in code files } MAXSEG1 = 16; { MAXSEG+1, useful for loop vars } MAXLC = MAXINT; { max compiler assigned address } MAXIC = 2400; { max number bytes of code per proc } MAXPROC = 160; { max legal procedure number } 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; { 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 privates in words } 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 } ; { 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 } { 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; textstart: array [segrange] of integer; filler: array [0..87] of integer; notice: string [79]; end { I5segtbl } ; var segtbl: I5segtbl; { disk seg table w/ source info } fp: file; mapfile: interactive; listmap, listrefs, firsttime: boolean; { * 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 } ; procedure phase2; var s: segindex; { * Readlinkinfo reads in the link info for segment s * and builds its symtab. Some simple disk io routines * do unblocking. } procedure readlinkinfo (s: segrange); var nextblk, recsleft: integer; entry: lientry; nointerface: boolean; buf: array [0..31] of array [0..7] of integer; function copyinterface (start: integer): boolean; const IMPLMTSY = 52; var j: integer; { FIXED DECLARATION ORDER } s: integer; d: integer; n: alpha; last: integer; done: boolean; buf: packed array [0..1023] of char; begin copyinterface := true; if (start <= 0) or (start > 200) then begin copyinterface := false; exit (copyinterface) end; done := false; repeat if blockread (fp, buf, 2, start) <> 2 then begin writeln (mapfile, 'Interface read error'); copyinterface := false; done := true end else begin start := start + 2; j := 0; repeat if buf [j] IN ['A'..'Z', 'a'..'z'] then begin last := j; IDSEARCH (j, buf); done := s = IMPLMTSY; end; if buf [j] = chr (13) THEN if buf [j+1] = chr (0) THEN begin last := j-1; j := 1023; end; j := j+1 until done or (j > 1023); writeln (mapfile, buf:last) end until done; writeln (mapfile) end { copyinterface } ; { * Getentry reads an 8 word record from disk buf * sequentially. No validity checking is done here, * only disk read errors. } procedure getentry (var entry: lientry); var err: boolean; begin err := FALSE; if recsleft = 0 then begin recsleft := 32; err := blockread (fp, buf, 1, nextblk) <> 1; if err then writeln (mapfile, 'library read error!') else nextblk := nextblk+1 end; moveleft(buf[32-recsleft], entry, 16); if err then entry.litype := EOFMARK; recsleft := recsleft-1 end { getentry } ; procedure ref (what: string); var nrecs: integer; temp: lientry; begin with entry do begin if listrefs then begin write (mapfile, name:12, what); case format of WORD: write (mapfile, ' word reference'); BYTE: write (mapfile, ' byte reference'); BIG: write (mapfile, ' big reference'); end; if nrefs > 1 then write (mapfile, ' (', nrefs, ' times)') else write (mapfile, ' (once)'); writeln (mapfile); end; for nrecs := 1 to (nrefs+7) div 8 do getentry (temp); { skip reference list } end { with }; end { ref }; begin { readlinkinfo } with segtbl do begin write (mapfile, segname[s]); nointerface := true; case segkind[s] of LINKED: begin writeln (mapfile, ' completely linked segment'); exit (readlinkinfo); { rein a faire } end; HOSTSEG: writeln (mapfile, ' Pascal host outer block'); SEGPROC: begin writeln (mapfile, ' Pascal segment'); nointerface := not copyinterface (textstart[s]) end; UNITSEG: begin writeln (mapfile,' library unit'); nointerface := not copyinterface (textstart[s]) end; SEPRTSEG: writeln (mapfile, ' separate procedure segment'); end; recsleft := 0; { 8 wd recs left in buf } with diskinfo[s] do nextblk := codeaddr + (codeleng+511) div 512; { seek to linkinfo } if listmap or nointerface then repeat getentry(entry); with entry do if litype <> EOFMARK then begin { list the entry } if alphabetic (name) then begin case litype of GLOBDEF: if listmap then writeln (mapfile, name:12, ' global addr P #',homeproc,', I #',icoffset); PUBLDEF: if listmap then writeln (mapfile, name:12, ' public var base = ', baseoffset); CONSTDEF: if listmap then writeln (mapfile, name:12, ' constant value of ', constval); EXTPROC, EXTFUNC: if listrefs then writeln (mapfile, name:12, ' external proc P #', srcproc); SEPPROC, SEPFUNC: writeln (mapfile, name:12, ' separate proc P #', srcproc); GLOBREF: ref (' global'); PUBLREF: ref (' public'); CONSTREF: ref (' constant'); SEPFREF, SEPPREF: ref (' separate'); UNITREF: ref (' unit'); PRIVREF: ref (' private'); end { case }; end { if alphabetic }; end { with entry }; until entry.litype = EOFMARK end { with segtbl } end { readlinkinfo } ; begin { phase2 } for s := 0 to MAXSEG do with segtbl, diskinfo[s] do if codeleng > 0 then begin write (mapfile, 'Segment #', s:2, ': '); readlinkinfo (s); writeln (mapfile); writeln (mapfile, '----------------------------------------------------------------------':75); writeln (mapfile); end; end { phase2 } ; procedure getfile; label 1; var s: segindex; CH: char; libtitle, maptitle: string; begin 1: close (fp); write ('enter library name: '); readln (libtitle); if libtitle = '' then begin close (mapfile, lock); exit (program); end; if libtitle = '*' then libtitle := '*SYSTEM.LIBRARY'; reset (fp, libtitle); if ioresult <> 0 then begin insert ('.CODE', libtitle, length(libtitle)+1); reset (fp, libtitle) end; if blockread (fp, segtbl, 1, 0) <> 1 then begin writeln ('bad file'); goto 1; end; with segtbl do for s := 0 to MAXSEG do if (diskinfo[s].codeleng < 0) or (diskinfo[s].codeaddr < 0) or (diskinfo[s].codeaddr > 300) or not alphabetic (segname[s]) then begin writeln ('not a code file'); goto 1; end; write ('list linker info table (Y/N)? '); repeat read (keyboard, CH) until CH in ['Y', 'N', 'y', 'n', ' ']; if not eoln (keyboard) then writeln (CH); listmap := (CH in ['Y', 'y']); if listmap then begin write ('list referenced items (Y/N)? '); repeat read (keyboard, CH) until CH in ['Y', 'N', 'y', 'n', ' ']; if not eoln (keyboard) then writeln (CH); listrefs := (CH in ['Y', 'y']) end else listrefs := false; if firsttime then repeat write ('map output file name: '); readln (maptitle); if maptitle = '' then exit (program); if maptitle[length(maptitle)] = '.' then delete (maptitle, length(maptitle), 1) else if maptitle[length(maptitle)] <> ':' then insert ('.TEXT', maptitle, length(maptitle)+1); rewrite (mapfile, maptitle) until ioresult = 0; page (mapfile); writeln (mapfile, ' LIBRARY MAP FOR ', libtitle); writeln (mapfile); with segtbl do if length (notice) > 0 then begin writeln (mapfile, ' ':5, notice); writeln (mapfile); end; writeln (mapfile); end { gettitle } ; begin { main } writeln ('Library map utility [I.5:4]'); firsttime := true; repeat getfile; firsttime := false; phase2; until false; END. { +------------------------------------------------------------------+ | | | F I N I S | | | +------------------------------------------------------------------+ }