Program/Module abstraction
Module: TYPE ~ REF ModuleObject;
ModuleObject: PUBLIC TYPE ~ SunADotOutPrivate.ModuleObject;
ModuleFromFile:
PUBLIC
PROC [ filename:
ROPE ]
RETURNS [ m: Module ←
NIL ] ~ {
s: IO.STREAM ~ OpenFile[filename];
m ← ModuleFromStream[s];
m.filename ← filename;
};
ModuleFromStream:
PUBLIC
PROC [ s:
IO.
STREAM ]
RETURNS [ m: Module ←
NIL ] ~ {
m ← NEW[ModuleObject];
m.filename ← NIL;
m.header ← ReadHeader[s];
{
bytes: CARD32 ~ m.header.text;
interval: VM.Interval ~ GetInterval[s, bytes];
block: Basics.UnsafeBlock ~ [VM.AddressForPageNumber[interval.page], 0, bytes];
m.programText ← [interval, block, 0];
};
{
bytes: CARD32 ~ m.header.data;
interval: VM.Interval ~ GetInterval[s, bytes];
block: Basics.UnsafeBlock ~ [VM.AddressForPageNumber[interval.page], 0, bytes];
m.programData ← [interval, block, 0];
};
{
bytes: CARD32 ~ m.header.bss;
interval: VM.Interval ~ VM.nullInterval;
block: Basics.UnsafeBlock ~ [NIL, 0, bytes];
m.programBss ← [interval, block, 0];
};
m.textRelocation ← GetRelocationInfo[s, m.header.textRelocationSize];
m.dataRelocation ← GetRelocationInfo[s, m.header.dataRelocationSize];
{
namelist: LIST OF Symbol; table: CardTab.Ref;
symbolTableBase: CARD32 ~ SymbolTablePosition[m]; -- or s.GetIndex[];
[namelist, table] ← GetSymbolTable[s, m.header.symbolTableSize];
m.header.stringTableSize ← GrabSymbols[s, namelist];
m.symbolTable ← RecordInterestingSymbols[namelist];
TranslateSymbolIndex[table, m.textRelocation, symbolTableBase];
TranslateSymbolIndex[table, m.dataRelocation, symbolTableBase];
table.Erase[]; table ← NIL; -- throw it away now!
};
};
WriteModule:
PUBLIC
PROC [ s:
IO.
STREAM, m: Module ] ~ {
WriteHeader[s, m.header];
};
Internal (RedBlackTree) Symbol Table
GetKey: RedBlackTree.GetKey ~ {
symbol: Symbol ~ NARROW[data];
key: ROPE ~ symbol.text;
RETURN[key];
};
Compare: RedBlackTree.Compare ~ {
t1: ROPE ~ NARROW[k];
t2: ROPE ~ NARROW[GetKey[data]];
relation: Basics.Comparison ~ Rope.Compare[t1, t2, TRUE];
RETURN[relation];
};
CreateProc: TYPE ~ PROC [ key: ROPE ] RETURNS [ s: Symbol ];
FindEntry:
PROC [ table: RedBlackTree.Table, key:
ROPE, create: CreateProc ←
NIL ]
RETURNS [ symbol: Symbol ←
NIL ] ~ {
WITH table.Lookup[key]
SELECT
FROM
s: Symbol => { symbol ← s };
ENDCASE => {
IF ( create = NIL ) THEN ERROR; -- avoid procedure error runtime event!
symbol ← create[key];
table.Insert[symbol, key]
};
};
UndefinedNames:
PUBLIC
PROC [ m: Module, itemProc: SunADotOut.ItemProc ] ~ {
table: RedBlackTree.Table ~ m.symbolTable;
EachNode: RedBlackTree.EachNode ~ {
s: Symbol ~ NARROW[data];
IF ( s.type = undefined ) THEN itemProc[s.text, s];
};
IF ( itemProc = NIL ) THEN ERROR;
table.EnumerateIncreasing[EachNode];
};
RecordInterestingSymbols:
PROC [ namelist:
LIST
OF Symbol, m: Module ←
NIL ]
RETURNS [ table: RedBlackTree.Table ←
NIL ] ~ {
table ← RedBlackTree.Create[getKey: GetKey, compare: Compare];
FOR item:
LIST
OF Symbol ← namelist, item.rest
WHILE ( item #
NIL )
DO
s: Symbol ~ item.first;
IF ( m # NIL ) THEN s.backlink ← m;
SELECT
TRUE
FROM
( s.permanent ) => { NULL };
( s.text = NIL ) => { ERROR };
( s.type # undefined ) => { NULL };
ENDCASE => { table.Insert[s, s.text] };
ENDLOOP;
};
File Header
HdrPtr: TYPE ~ LONG POINTER TO HdrObj;
HdrPreamble:
TYPE ~
MACHINE
DEPENDENT
RECORD [
dynamic(0:0..0): BOOL, toolVersion(0:1..7): [0..128),
machineType(0:8..15): BYTE,
magic(0:16..31): Basics.HWORD
];
HdrObj:
TYPE ~
MACHINE
DEPENDENT
RECORD [
preamble: HdrPreamble,
text: Basics.FWORD,
data: Basics.FWORD,
bss: Basics.FWORD,
symbolTableSize: Basics.FWORD,
entryPoint: Basics.FWORD,
textRelocationSize: Basics.FWORD,
dataRelocationSize: Basics.FWORD
];
Header: TYPE ~ REF HeaderObject;
HeaderObject: PUBLIC TYPE ~ SunADotOutPrivate.HeaderObject;
rawHdrSiz: NAT ~ BYTES[HdrObj];
ReadHeader:
PROC [ s:
IO.
STREAM ]
RETURNS [ h: Header ←
NIL ] ~
TRUSTED {
hdrobj: HdrObj;
buf: Basics.UnsafeBlock ~ [@hdrobj, 0, rawHdrSiz];
IF ( s.UnsafeGetBlock[buf] # rawHdrSiz ) THEN ERROR;
h ← NEW[HeaderObject];
h.dynamic ← hdrobj.preamble.dynamic;
h.toolVersion ← hdrobj.preamble.toolVersion;
h.machineType ← VAL[hdrobj.preamble.machineType];
h.magic ← VAL[Basics.Card16FromH[hdrobj.preamble.magic]];
h.text ← Basics.Card32FromF[hdrobj.text];
IF ( h.magic = ZMAGIC ) THEN h.text ← h.text - rawHdrSiz; -- silly rabbit!
h.data ← Basics.Card32FromF[hdrobj.data];
h.bss ← Basics.Card32FromF[hdrobj.bss];
h.symbolTableSize ← Basics.Card32FromF[hdrobj.symbolTableSize];
h.entryPoint ← Basics.Card32FromF[hdrobj.entryPoint];
h.textRelocationSize ← Basics.Card32FromF[hdrobj.textRelocationSize];
h.dataRelocationSize ← Basics.Card32FromF[hdrobj.dataRelocationSize];
h.stringTableSize ← 0;
};
WriteHeader:
PROC [ s:
IO.
STREAM, h: Header ] ~
TRUSTED {
hdrobj: HdrObj;
buf: Basics.UnsafeBlock ~ [@hdrobj, 0, rawHdrSiz];
hdrobj.preamble.dynamic ← h.dynamic;
hdrobj.preamble.toolVersion ← h.toolVersion;
hdrobj.preamble.machineType ← ORD[h.machineType];
hdrobj.preamble.magic ← Basics.HFromCard16[ORD[h.magic]];
hdrobj.text ← Basics.FFromCard32[h.text];
hdrobj.data ← Basics.FFromCard32[h.data];
hdrobj.bss ← Basics.FFromCard32[h.bss];
hdrobj.symbolTableSize ← Basics.FFromCard32[h.symbolTableSize];
hdrobj.entryPoint ← Basics.FFromCard32[h.entryPoint];
hdrobj.textRelocationSize ← Basics.FFromCard32[h.textRelocationSize];
hdrobj.dataRelocationSize ← Basics.FFromCard32[h.dataRelocationSize];
s.UnsafePutBlock[buf];
};
Relocation Item Lists
RelocationInfo: TYPE ~ REF RelocationInfoObject;
RelocationInfoObject: TYPE ~ SunADotOutPrivate.RelocationInfoObject;
IndexFor:
PROC [ table: CardTab.Ref, text:
ROPE ]
RETURNS [ index:
CARD32 ← 0 ] ~ {
EachPairAction: CardTab.EachPairAction ~ {
s: Symbol ~ NARROW[val];
IF ( text.Equal[s.text] ) THEN { index ← key; quit ← TRUE };
};
[] ← table.Pairs[EachPairAction];
};
DumpIndexTable:
PROC [ out:
IO.
STREAM, table: CardTab.Ref ] ~ {
EachPairAction: CardTab.EachPairAction ~ {
s: Symbol ~ NARROW[val];
out.PutF["\t%g: %g\n", IO.card[key], IO.rope[s.text]];
};
out.PutF["Index Table\n"];
[] ← table.Pairs[EachPairAction];
};
TranslateSymbolIndex:
PROC [ table: CardTab.Ref, list:
LIST
OF RelocationInfo, symbolTableBase:
CARD32 ] ~ {
FOR item:
LIST
OF RelocationInfo ← list, item.rest
WHILE ( item #
NIL )
DO
offset: CARD32 ~ item.first.index * BYTES[xx]; or - symbolTableBase
IF ( NOT item.first.extern ) THEN LOOP;
WITH table.Fetch[item.first.index].val
SELECT
FROM
s: Symbol => { item.first.symbol ← s };
ENDCASE => { ERROR };
ENDLOOP;
};
InfoGetKey: RedBlackTree.GetKey ~ {
RETURN[data];
};
InfoCompare: RedBlackTree.Compare ~ {
t1: RelocationInfo ~ NARROW[k];
t2: RelocationInfo ~ NARROW[data];
SELECT ( t2.address )
FROM
-- backward for CONS[item, list]
< t1.address => { RETURN[less] };
= t1.address => { RETURN[equal] };
> t1.address => { RETURN[greater] };
ENDCASE => { ERROR };
};
RelObj:
TYPE ~
MACHINE
DEPENDENT
RECORD [
addr : Basics.FWORD,
oddBytes: PACKED ARRAY [0..BYTES[Basics.FWORD]) OF CHAR,
addend: Basics.FWORD
];
BuildRelocationList:
PROC [ interval:
VM.Interval, bytes:
CARD32 ]
RETURNS [ list:
LIST
OF RelocationInfo ←
NIL ] ~ {
table: RedBlackTree.Table ~ RedBlackTree.Create[InfoGetKey, InfoCompare];
EachNode: RedBlackTree.EachNode ~ {
info: RelocationInfo ~ NARROW[data];
list ← CONS[info, list];
};
SniffCard24:
PROC [ buf:
PACKED
ARRAY [0..
BYTES[Basics.
FWORD])
OF
CHAR ]
RETURNS [ c:
CARD32 ] ~
TRUSTED {
long: Basics.LongNumber ~ [bytes[
lh: buf[1].ORD, ll: buf[2].ORD,
hh: 0, hl: buf[0].ORD ]];
c ← long.lc;
};
base: LONG POINTER ~ VM.AddressForPageNumber[interval.page];
entries: CARD32 ~ bytes / BYTES[RelObj];
IF ( entries = 0 ) THEN RETURN;
FOR i:
CARD32
IN [0..entries)
DO
info: RelocationInfo ~ NEW[RelocationInfoObject];
TRUSTED {
finger: LONG POINTER TO RelObj ~ LOOPHOLE[base + ( i * SIZE[RelObj] )];
info.address ← Basics.Card32FromF[finger.addr];
info.index ← SniffCard24[finger.oddBytes];
info.extern ← ( Basics.BITAND[finger.oddBytes[3].ORD, 080H] # 0 );
IF ( NOT info.extern ) THEN info.basedUpon ← VAL[finger.oddBytes[2].ORD]; -- fishy!
info.unused ← finger.oddBytes[3].ORD;
info.type ← VAL[Basics.BITAND[finger.oddBytes[3].ORD, 01FH]];
info.addend ← Basics.Int32FromF[finger.addend];
};
table.Insert[info, info];
ENDLOOP;
table.EnumerateIncreasing[EachNode];
};
GetRelocationInfo:
PROC [ s:
IO.
STREAM, bytes:
CARD32 ]
RETURNS [ list:
LIST
OF RelocationInfo ←
NIL ] ~ {
interval: VM.Interval ~ GetInterval[s, bytes];
list ← BuildRelocationList[interval, bytes];
TRUSTED { VM.Free[interval] };
};
Symbol Table (namelist)
Symbol: TYPE ~ REF SymbolObject;
SymbolObject: TYPE ~ SunADotOutPrivate.SymbolObject;
SymObj:
TYPE ~
MACHINE
DEPENDENT
RECORD [
stringTableIndex: Basics.FWORD,
oddBytes: PACKED ARRAY [0..BYTES[Basics.FWORD]) OF CHAR,
value: Basics.FWORD
];
BuildNameList:
PROC [ interval:
VM.Interval, bytes:
CARD32 ]
RETURNS [ list:
LIST
OF Symbol ←
NIL, table: CardTab.Ref ←
NIL ] ~
TRUSTED {
base: LONG POINTER ~ VM.AddressForPageNumber[interval.page];
entries: CARD32 ~ bytes / BYTES[SymObj];
table ← CardTab.Create[];
IF ( entries = 0 ) THEN RETURN;
FOR i:
CARD32
IN [0..entries)
DO
symbol: Symbol ~ NEW[SymbolObject];
index: CARD32 ~ i * BYTES[SymObj];
finger: LONG POINTER TO SymObj ~ LOOPHOLE[base + ( i * SIZE[SymObj] )];
symbol.text ← NIL;
symbol.stringTableIndex ← Basics.Card32FromF[finger.stringTableIndex];
symbol.type ← VAL[Basics.BITAND[finger.oddBytes[0].ORD, NAMETYPEMASK]];
symbol.description ← VAL[Basics.BITAND[finger.oddBytes[0].ORD, SYMDESCMASK]];
symbol.permanent ← ( symbol.description # notPermanent );
symbol.other ← finger.oddBytes[1].ORD;
symbol.details ← Basics.Card16FromH[
[hi: finger.oddBytes[2].ORD, lo: finger.oddBytes[3].ORD] ];
symbol.value ← Basics.Card32FromF[finger.value];
IF ( NOT table.Insert[i, symbol] ) THEN ERROR; -- index
list ← CONS[symbol, list];
ENDLOOP;
};
String Table
GetSymbolTable:
PROC [ s:
IO.
STREAM, bytes:
CARD32 ]
RETURNS [ namelist:
LIST
OF Symbol ←
NIL, table: CardTab.Ref ←
NIL ] ~ {
interval: VM.Interval ~ GetInterval[s, bytes];
[namelist, table] ← BuildNameList[interval, bytes];
TRUSTED { VM.Free[interval] };
};
GetCard32:
PROC [ s:
IO.
STREAM ]
RETURNS [ c:
CARD32 ← 0 ] ~ {
buf: REF TEXT ~ RefText.ObtainScratch[BYTES[Basics.FWORD]];
IF ( CARD32[s.GetBlock[block: buf, count: BYTES[Basics.FWORD]]] # BYTES[Basics.FWORD] ) THEN ERROR;
{
long: Basics.LongNumber ~ [bytes[
lh: buf[2].ORD, ll: buf[3].ORD,
hh: buf[0].ORD, hl: buf[1].ORD]];
c ← long.lc;
};
RefText.ReleaseScratch[buf];
};
maxString: NAT ~ 512;
GetStringFromTable:
PROC [ stringBlock: Basics.UnsafeBlock, stringTableIndex:
CARD32 ]
RETURNS [ text:
ROPE ←
NIL ] ~
TRUSTED {
GetByte:
PROC [ i:
CARD32 ]
RETURNS [ ch:
CHAR ] ~
TRUSTED {
buf: PACKED ARRAY [0..BYTES[Basics.FWORD]) OF CHAR;
to: PrincOps.ByteBltBlock ~ [@buf, 0, 1];
from: PrincOps.ByteBltBlock ~ [stringBlock.base, i, i.SUCC];
IF ( PrincOpsUtils.ByteBlt[to, from] # 1) THEN ERROR;
ch ← buf[0];
};
IF ( stringTableIndex = 0 ) THEN RETURN;
stringTableIndex ← stringTableIndex -
BYTES[Basics.
FWORD];
bias because stringTableSize is included in the Table image!
{
buf: REF TEXT ~ RefText.ObtainScratch[maxString];
FOR i:
NAT
IN [0..
maxString)
DO
buf[i] ← GetByte[stringTableIndex + i];
IF ( buf[i] = Ascii.NUL ) THEN { buf.length ← i; EXIT };
ENDLOOP;
text ← Rope.FromRefText[buf];
RefText.ReleaseScratch[buf];
};
};
GrabSymbols:
PROC [ s:
IO.
STREAM, namelist:
LIST
OF Symbol ]
RETURNS [ size:
CARD32 ] ~ {
bytes: CARD32 ~ GetCard32[s] - BYTES[Basics.FWORD];
interval: VM.Interval ~ GetInterval[s, bytes];
stringBlock: Basics.UnsafeBlock ~ [VM.AddressForPageNumber[interval.page], 0, bytes - 4];
m.header.stringTableSize ← bytes;
FOR item:
LIST
OF Symbol ← namelist, item.rest
WHILE ( item #
NIL )
DO
item.first.text ← GetStringFromTable[stringBlock, item.first.stringTableIndex];
ENDLOOP;
TRUSTED { VM.Free[interval] };
size ← bytes;
};
Utility Routines
OpenFile:
PROC [ name:
ROPE ]
RETURNS [ s:
IO.
STREAM ←
NIL ] ~ {
ENABLE {
UNWIND => { NULL };
FS.Error => { IF ( error.group = user )
THEN { Error[Rope.Concat["FS.Error: ", error.explanation]] } };
};
s ← FS.StreamOpen[name, $read];
};
bufSiz: NAT ← 8192;
GetInterval:
PROC [ s:
IO.
STREAM, bytes:
CARD32 ]
RETURNS [ interval:
VM.Interval ←
VM.nullInterval ] ~ {
count: VM.PageCount ~ VM.PagesForBytes[bytes];
chunk: CARD32 ~ VM.PagesForBytes[bufSiz];
IF ( count <= 0 ) THEN RETURN;
interval ← VM.SimpleAllocate[count];
FOR p:
CARD32 ← 0, p + chunk
WHILE ( p <
CARD32[count] )
DO
stuff: CARD32 ~ MIN[bytes, bufSiz];
finger: LONG POINTER ~ VM.AddressForPageNumber[interval.page + p];
block: Basics.UnsafeBlock ~ [finger, 0, stuff];
TRUSTED { IF ( CARD32[s.UnsafeGetBlock[block]] # stuff ) THEN ERROR };
bytes ← bytes - stuff;
ENDLOOP;
};
SymbolTablePosition:
PROC [ m: Module ]
RETURNS [ c:
CARD32 ] ~ {
h: Header ~ m.header;
c ← rawHdrSiz + h.text + h.data + h.textRelocationSize + h.dataRelocationSize;
};
ComputeSize:
PROC [ m: Module ]
RETURNS [ c:
CARD32 ] ~ {
h: Header ~ m.header;
c ← rawHdrSiz + h.text + h.data + h.textRelocationSize + h.dataRelocationSize + h.symbolTableSize + BYTES[Basics.FWORD] + h.stringTableSize;
};
Debugging and Test routines
DisplaySymbolDescription:
PROC [ out:
IO.
STREAM, d: SymbolDescription ] ~ {
name:
ROPE ~
SELECT d
FROM
notPermanent => "notPermanent",
GSYM => "GSYM",
FNAME => "FNAME",
FUN => "FUN",
STSYM => "STSYM",
LCSYM => "LCSYM",
MAIN => "MAIN",
PC => "PC",
RSYM => "RSYM",
M2C => "M2C",
SLINE => "SLINE",
SSYM => "SSYM",
SO => "SO",
LSYM => "LSYM",
BINCL => "BINCL",
SOL => "SOL",
PSYM => "PSYM",
EINCL => "EINCL",
ALTENTRY => "ALTENTRY",
LBRAC => "LBRAC",
EXCL => "EXCL",
SCOPE => "SCOPE",
RBRAC => "RBRAC",
BCOMM => "BCOMM",
ECOMM => "ECOMM",
ECOML => "ECOML",
LENG => "LENG",
ENDCASE => Rope.Concat["unknown - ", Convert.RopeFromCard[d.ORD]];
out.PutF[" desc: %g", IO.rope[name]];
};
DisplayNameType:
PROC [ out:
IO.
STREAM, type: NameType ] ~ {
name:
ROPE ~
SELECT type
FROM
undefined => "undefined",
absolute => "absolute",
text => "text",
data => "data",
bss => "bss",
common => "common",
fileNameSymbol => "fileNameSymbol",
ENDCASE => Rope.Concat["unknown - ", Convert.RopeFromCard[type.ORD]];
out.PutF[" nametype: %g", IO.rope[name]];
};
DisplayMachineType:
PROC [ out:
IO.
STREAM, m: MachineType ] ~ {
name:
ROPE ~
SELECT m
FROM
oldSun2 => "oldSun2",
mc68010 => "mc68010",
mc68020 => "mc68020",
sparc => "sparc",
ENDCASE => Rope.Concat["unknown - ", Convert.RopeFromCard[m.ORD]];
out.PutF[" machineType: %g", IO.rope[name]];
};
DisplayMagic:
PROC [ out:
IO.
STREAM, m: Magic ] ~ {
name:
ROPE ~
SELECT m
FROM
OMAGIC => "OMAGIC",
NMAGIC => "NMAGIC",
ZMAGIC => "ZMAGIC",
ENDCASE => Rope.Concat["unknown - ", Convert.RopeFromCard[m.ORD]];
out.PutF[" magic: %g", IO.rope[name]];
};
DisplayHeader:
PROC [ out:
IO.
STREAM, h: Header ] ~ {
out.PutF[" dynamic: %g\n", IO.bool[h.dynamic]];
out.PutF[" toolVersion: %g\n", IO.card[h.toolVersion]];
DisplayMachineType[out, h.machineType]; out.PutF["\n"];
DisplayMagic[out, h.magic]; out.PutF["\n"];
out.PutF[" text segment size: %g\n", IO.card[h.text]];
out.PutF[" initialized data size: %g\n", IO.card[h.data]];
out.PutF[" un-initialized data size: %g\n", IO.card[h.bss]];
out.PutF[" symbolTableSize: %g\n", IO.card[h.symbolTableSize]];
out.PutF[" entryPoint: %g\n", IO.card[h.entryPoint]];
out.PutF[" textRelocationSize: %g\n", IO.card[h.textRelocationSize]];
out.PutF[" dataRelocationSize: %g\n", IO.card[h.dataRelocationSize]];
out.PutF[" stringTableSize: %g\n", IO.card[h.stringTableSize]];
};
DisplayNameList:
PROC [ out:
IO.
STREAM, list:
LIST
OF Symbol ] ~ {
FOR item:
LIST
OF Symbol ← list, item.rest
WHILE ( item #
NIL )
DO
out.PutF[" text: %g\n", IO.rope[item.first.text]];
IF ( item.first.text = NIL ) THEN
out.PutF[" stringTableIndex: %g\n", IO.card[item.first.stringTableIndex]];
DisplayNameType[out, item.first.type]; out.PutF["\n"];
out.PutF[" permanent: %g\n", IO.bool[item.first.permanent]];
IF ( item.first.permanent ) THEN
DisplaySymbolDescription[out, item.first.description]; out.PutF["\n"];
out.PutF[" other: %g\n", IO.card[item.first.other]];
out.PutF[" details: %g\n", IO.card[item.first.details]];
out.PutF[" value: %g\n", IO.card[item.first.value]];
out.PutF["\n"];
ENDLOOP;
};
DisplayTable:
PROC [ out:
IO.
STREAM, table: RedBlackTree.Table ] ~ {
EachNode: RedBlackTree.EachNode ~ {
s: Symbol ~ NARROW[data];
out.PutF[" %g", IO.rope[s.text]];
out.PutF["\n\t"];
out.PutF["value: %g", IO.card[s.value]];
DisplayNameType[out, s.type];
out.PutF[" other: %g", IO.card[s.other]];
out.PutF[" details: %g", IO.card[s.details]];
out.PutF["\n"];
};
table.EnumerateIncreasing[EachNode];
};
DisplayUndefinedSymbols:
PROC [ out:
IO.
STREAM, m: Module ] ~ {
some: BOOL ← FALSE;
DisplaySymbol: SunADotOut.ItemProc ~ {
some ← TRUE;
out.PutF["\t%g\n", IO.rope[text]];
};
out.PutF[" Unresolved symbols:\n"];
out.PutF["\t\n"];
UndefinedNames[m, DisplaySymbol];
IF ( some )
THEN { out.PutF["\t\n"]; out.PutF["\tdone.\n"] }
ELSE { out.PutF["\tno unresolved symbols.\n"] };
};
DisplayModule:
PROC [ out:
IO.
STREAM, m: Module ] ~ {
out.PutF["log for filename: %g\n", IO.rope[m.filename]];
out.PutF["\n"];
DisplayUndefinedSymbols[out, m];
out.PutF["\n"];
DisplayHeader[out, m.header];
out.PutF[" computed file size: %g\n", IO.card[ComputeSize[m]]];
out.PutF["\n"];
DisplayTable[out, m.symbolTable];
out.PutF["\n"];
};
test: Module;
TestLoader:
PROC ~ {
out: IO.STREAM ~ FS.StreamOpen["SparcLoader.log", $create];
test ← ModuleFromFile["Sun4>SparcLoaderTestCodeImpl.C2C.o"];
DisplayModule[out, test];
out.Close[];
};
}.