DIRECTORY
Basics USING [Card16FromH, Card32FromF, Comparison, FWORD, HWORD],
BasicTime USING [GMT, Now],
CCTypes USING[CCError, CCErrorCase],
CirioTypes USING [zeroBA],
Commander USING [Register, CommandProc],
CommanderOps USING [ArgumentVector, Parse],
Convert USING [IntFromRope],
IO,
List USING[LORA, CompareProc, Sort],
ObjectFiles USING[BracketPairKind, CNameOfStab, CreateParsed, DescribeModule, FileSegmentPC, GlobalVarLoc, MakeUnknownVarLoc, ReadInstruction, RopeForStabType, SimpleSeg, Stab, StabBody, StabType, UnreadableObjectFile, unspecdBitSize, VarLoc, VarLocBody, VersionStampInfo],
ObjectFilesPrivate USING[AlterFunStabType, BracketConsumer, BracketPair, BracketPairBody, CheckStaticVar, FnConsumer, FunStabSetBody, GetSPOffsetType, GetTypeRefProcType, HeaderBody, InstallStaticVarsType, LineNumToPCMapBody, MemorySegmentInfo, ModuleBody, ModuleFromParsedAndPCProcType, ObjectFileFlavorBody, Parsed, ParsedBody, PCtoLineNumMapBody, ReadHeaderProcType, ReadInitialDataAsRope, ReadStab, RegisterObjectFileFlavor, SLineData, StabList, StabRange, StabSet, TranslationTable, TranslationTableBody, VarLocFromStabProcType],
PBasics USING [BITAND, BITSHIFT],
PFS USING [PathFromRope, StreamFromOpenFile, OpenFileFromStream],
PFSNames USING [PATH],
Random USING[ChooseInt, Create, RandomStream],
Rope,
SunADotOut,
SystemInterface USING[CirioFile, CloseFileSet, CreateFileSet, FileSet, GetCirioFile, GetStreamForFile, ReleaseStreamForFile, ShowReport];



SunADotOutFiles: CEDAR MONITOR
IMPORTS Basics, BasicTime, CCTypes, Commander, CommanderOps, Convert, IO, List, ObjectFiles, ObjectFilesPrivate, PBasics, PFS, Random, Rope, SystemInterface
EXPORTS ObjectFiles, SunADotOut
= BEGIN

CCError: ERROR[case: CCTypes.CCErrorCase, msg: Rope.ROPE _ NIL] _ CCTypes.CCError;

PATH: TYPE ~ PFSNames.PATH;


Stab: TYPE = ObjectFiles.Stab;
StabList: TYPE = ObjectFilesPrivate.StabList;
StabBody: TYPE = ObjectFiles.StabBody;
StabType: TYPE = ObjectFiles.StabType;
StabRange: TYPE = ObjectFilesPrivate.StabRange;
FileSegmentPC: TYPE = ObjectFiles.FileSegmentPC;
VersionStampInfo: TYPE = ObjectFiles.VersionStampInfo;

LBrac: BYTE = 0c0H;
RBrac: BYTE = 0e0H;
SLine: BYTE = 044H;
Fun: BYTE = 024H;
PSym: BYTE = 0a0H;
LSym: BYTE = 080H;
RSym: BYTE = 040H;
STSym: BYTE = 026H;
LCSym: BYTE = 028H;
GSym: BYTE = 020H;
Main: BYTE = 02aH;
SO: BYTE = 064H;
BIncl: BYTE = 082H;
EIncl: BYTE = 0a2H;
Excl: BYTE = 0c2H;
SOL: BYTE = 084H;


Parsed: TYPE = REF ParsedBody;
ParsedBody: PUBLIC TYPE = ObjectFilesPrivate.ParsedBody;

Module: TYPE = REF ModuleBody;
ModuleBody: PUBLIC TYPE = ObjectFilesPrivate.ModuleBody;
Header: TYPE = REF HeaderBody;
HeaderBody: TYPE = ObjectFilesPrivate.HeaderBody;

MemorySegmentInfo: TYPE ~ ObjectFilesPrivate.MemorySegmentInfo;

StabSet: TYPE = ObjectFilesPrivate.StabSet;

BracketPair: TYPE ~ ObjectFilesPrivate.BracketPair;
BracketPairBody: TYPE ~ ObjectFilesPrivate.BracketPairBody;

unspecdBitSize: CARD ~ ObjectFiles.unspecdBitSize;



MOldsun2: CARD = 0;
M68010: CARD = 1;
M68020: CARD = 2;
MSparc: CARD = 3;

OMagic: CARD16 = 0407B; 
NMagic: CARD16 = 0410B; 
ZMagic: CARD16 = 0413B; 


PageSize: CARD32 = 02000H;
OldPageSize: CARD32 = 00800H;

WireHeader: TYPE = REF WireHeaderBody;
WireHeaderBody: TYPE = MACHINE DEPENDENT RECORD[
dynamic: BYTE[0..0],
toolversion: BYTE[0..127],
machtype: BYTE,
magic: Basics.HWORD,
textSize: Basics.FWORD,
iDataSize: Basics.FWORD,
bssSize: Basics.FWORD,
symsSize: Basics.FWORD,
entryPoint: Basics.FWORD,
trSize: Basics.FWORD,
drSize: Basics.FWORD];

WireSTEntry: TYPE = REF WireSTEntryBody;
WireSTEntryBody: TYPE = MACHINE DEPENDENT RECORD[
stringX(0: 0..31): Basics.FWORD,
type(0: 32..39): BYTE,
other(0: 40..47): BYTE,
desc(0: 48..63): Basics.HWORD,
value(0: 64..95): Basics.FWORD];


ReadHeader: ObjectFilesPrivate.ReadHeaderProcType ~ TRUSTED { 
wHeader: WireHeader _ NEW[WireHeaderBody];
nBytes: INT _ IO.UnsafeGetBlock[stream, [LOOPHOLE[@wHeader^], 0, BYTES[WireHeaderBody]]];
magic: CARD _ Basics.Card16FromH[wHeader.magic];
nBadMagic: BOOLEAN _ (magic # OMagic) AND (magic # NMagic)AND (magic # ZMagic);
nPageSize: CARD _ IF wHeader.machtype = MOldsun2 THEN OldPageSize ELSE PageSize;
textOffset: CARD _ IF wHeader.machtype = MOldsun2 THEN
(IF magic = ZMagic THEN nPageSize ELSE BYTES[WireHeaderBody])
  ELSE
(IF magic = ZMagic THEN 0 ELSE BYTES[WireHeaderBody]);
textSize: CARD _ Basics.Card32FromF[wHeader.textSize];
iDataSeg: MemorySegmentInfo _ [textOffset + textSize, Basics.Card32FromF[wHeader.iDataSize]];
textRelocSeg: MemorySegmentInfo _ [iDataSeg.byteOffset+iDataSeg.byteLength, Basics.Card32FromF[wHeader.trSize]];
dataRelocSeg: MemorySegmentInfo _ [textRelocSeg.byteOffset+textRelocSeg.byteLength, Basics.Card32FromF[wHeader.drSize]];
symsSeg: MemorySegmentInfo _ [dataRelocSeg.byteOffset+dataRelocSeg.byteLength, Basics.Card32FromF[wHeader.symsSize]];
textLoadOffset: CARD32 _ IF magic = ZMagic THEN 02000H ELSE 0;
header: Header _ NEW[HeaderBody _ [
dynamic: wHeader.dynamic # 0,
toolversion: wHeader.toolversion,
machtype: wHeader.machtype,
magic: magic,
text: [textOffset, textSize],
iData: iDataSeg,
textReloc: textRelocSeg,
dataReloc: dataRelocSeg,
syms: symsSeg,
debug: [0, 0],
linno: [0, 0],
textLoadOffset: textLoadOffset,
bssSize: Basics.Card32FromF[wHeader.bssSize],
entryPoint: Basics.Card32FromF[wHeader.entryPoint],
nPageSize: nPageSize,
nEntries: symsSeg.byteLength/BYTES[WireSTEntryBody],
stringOffset: symsSeg.byteOffset+symsSeg.byteLength
]];
RETURN[header];
};

SunADotOutModuleFromParsedAndPC: ObjectFilesPrivate.ModuleFromParsedAndPCProcType ~ {
IF whole = NIL THEN RETURN[NIL] ELSE
{
stabRange: StabRange _ AlternativeFindModuleStabRange[whole, spc.relPC];
RETURN [ModuleFromParsedInner[whole, stabRange]];
};
};


LineNumList: TYPE = REF LineNumListBody;
LineNumListBody: TYPE = RECORD [nLines: CARD, lines: LIST OF REF ANY _ NIL];

CreateModule: PROC[whole: Parsed, stream: IO.STREAM, stabRange: StabRange _ [0, 0]] RETURNS[Module] =
BEGIN
module: Module _ NEW[ModuleBody];
firstSO: Stab;
nStabs: CARD;
lineNums: LineNumList;
lineNums2: LineNumList;
[firstSO, ] _ BasicReadStab[whole, stream, stabRange.first];
nStabs _ stabRange.count;
lineNums _ NEW [LineNumListBody _ [0, NIL]];
lineNums2 _ NEW [LineNumListBody _ [0, NIL]];
module.whole _ whole;
module.firstStabX _ stabRange.first;
module.limitStabX _ stabRange.first+stabRange.count;
module.firstPC _ firstSO.value;
module.limitPC _ whole.header.text.byteLength; -- (bogus, but it will suffice for reasons given earlier)

module.stabs _ NEW[StabSet[nStabs]];

{I: CARD _ 0;
WHILE I<nStabs DO
stab: Stab;
numStabsRead:CARD;
[stab, numStabsRead]  _ BasicReadStab[whole, stream, stabRange.first+I, lineNums, lineNums2];
module.stabs[I] _ stab;
stab.module _ module;
IF stab.stabType = LBrac OR stab.stabType = RBrac THEN
stab.value _ stab.value + module.firstPC;
FOR J: CARD IN (I .. I + numStabsRead) DO
unusedStab: Stab _ NEW [StabBody];
unusedStab.module _ NIL;	-- Indicates unused entry
module.stabs[J] _ unusedStab;
ENDLOOP;
I _ I + numStabsRead;
ENDLOOP;
};

InstallPCLineNumMaps[module, lineNums, lineNums2];
module.fileName _ PFS.PathFromRope[IF module.stabs[1].stabType = SO THEN module.stabs[1].rope ELSE module.stabs[0].rope];

module.staticVarsInstalled _ FALSE;
module.versionStampInfo _ NIL;

module.outerBracket _ NEW[ObjectFilesPrivate.BracketPairBody_[
module: module,
kind: syntheticOuter,
firstX: 0,
limitX: module.whole.header.nEntries,
firstPC: module.firstPC,
pcLimit: module.limitPC,
funStab: NIL,
funIndex: 0,
symbols: NIL,
innerBrackets: NIL]];

RETURN[module];
END;

StabIndexTypeAndValue: TYPE = RECORD[stabX: CARD, stabType: StabType, value: CARD32];
NullStabIndexTypeAndValue: StabIndexTypeAndValue = [0, Invalid, 0];

AlternativeFindModuleStabRange: PROC[whole: Parsed, relativePC: CARD] RETURNS[StabRange _ [0, 0]] =
BEGIN
{
IF whole = NIL THEN RETURN[[0, 0]] ELSE
BEGIN
nominalX: CARD _ SearchForPC[whole, relativePC];
baseSO: StabIndexTypeAndValue _ NullStabIndexTypeAndValue; -- tentative
maxPC: CARD _ 0;
highDbx: CARD _ 0;
IF nominalX = whole.header.nEntries THEN RETURN[[0, 0]];
FOR x: CARD DECREASING IN [0..nominalX] DO
info: StabIndexTypeAndValue _ CheckStab[whole, x];
IF info.stabType = Fun OR info.stabType = SLine OR info.stabType = SO THEN
maxPC _ MAX[maxPC, info.value];
IF info.stabType = SO THEN {baseSO _ info};
IF info.stabType = Unspecified OR info.stabType = Invalid THEN EXIT;
highDbx _ MAX[highDbx, info.stabX];
ENDLOOP;
IF baseSO.stabType # SO THEN {
RETURN[[0, 0]];};
FOR x: CARD IN (nominalX..whole.stabLimit) DO
info: StabIndexTypeAndValue _ CheckStab[whole, x];
IF info.stabType = Fun OR info.stabType = SLine THEN
maxPC _ MAX[maxPC, info.value];
IF info.stabType = Unspecified OR info.stabType = Invalid THEN EXIT;
highDbx _ MAX[highDbx, info.stabX];
ENDLOOP;
IF baseSO.value <= relativePC AND relativePC <= maxPC THEN {
RETURN[[baseSO.stabX, highDbx-baseSO.stabX+1]];};
RETURN[[0, 0]];
END;
};
END;

SearchForPC: PROC[whole: Parsed, pc: CARD] RETURNS[CARD] =
BEGIN
x: CARD _ 0;
y: CARD _ whole.stabLimit;

WHILE x+1 < y DO
mid: CARD _ (x+y)/2;
midStab: StabIndexTypeAndValue _ SearchFwdForPCStab[whole, mid, y];
SELECT TRUE FROM
midStab.stabX = y => y _ mid;
pc < midStab.value => y _ mid;
midStab.value = pc => RETURN[midStab.stabX];
midStab.value < pc => x _ mid;
ENDCASE => ERROR;
ENDLOOP;
RETURN[x];
END;

SearchFwdForPCStab: PROC[whole: Parsed, start: CARD, limit: CARD] RETURNS[StabIndexTypeAndValue] =
BEGIN
dbxLimit: CARD _ 0; -- tentative
FOR x: CARD _ start, x+1 WHILE x < limit DO
info: StabIndexTypeAndValue _ CheckStab[whole, x];
IF info.stabType = SO OR info.stabType = Fun OR info.stabType = SLine THEN RETURN[info];
IF info.stabType # Unspecified AND info.stabType # Invalid THEN dbxLimit _ x+1;
ENDLOOP;
IF limit = whole.stabLimit AND dbxLimit > start THEN whole.stabLimit _ dbxLimit;
RETURN[[limit, Invalid, 0]];
END;

CheckStab: ENTRY PROC[whole: Parsed, stabX: CARD] RETURNS[StabIndexTypeAndValue] =
BEGIN
ENABLE UNWIND => NULL;
stream: IO.STREAM _ SystemInterface.GetStreamForFile[whole.file];

IO.SetIndex[stream, whole.header.syms.byteOffset+stabX*BYTES[WireSTEntryBody]];

TRUSTED{[] _ IO.UnsafeGetBlock[stream, [LOOPHOLE[@WireStabBuffer^], 0, BYTES[WireSTEntryBody]]]};
SystemInterface.ReleaseStreamForFile[whole.file, stream];

RETURN[[stabX, TypeFromData[WireStabBuffer.type], Basics.Card32FromF[WireStabBuffer.value]]];
END;

SunADotOutVarLocFromStab: ObjectFilesPrivate.VarLocFromStabProcType ~ { 
header: Header ~ stab.module.whole.header;
MkSegment: PROC [kind: ObjectFiles.SimpleSeg] RETURNS [ObjectFiles.VarLoc] ~ {
segRope:Rope.ROPE;
segBase: CARD ~ SELECT kind FROM
text => 0,
data => header.text.byteLength,
bss => header.text.byteLength + header.iData.byteLength,
ENDCASE => ERROR;
IF stab.value < segBase THEN ObjectFiles.UnreadableObjectFile[IO.PutFR["value (%xH) less than segment base (%xH) for symbol %g in %g", [cardinal[stab.value]], [cardinal[segBase]], [rope[ObjectFiles.CNameOfStab[stab]]], [rope[ObjectFiles.DescribeModule[stab.module]]] ]];
SELECT kind FROM
text => segRope _ "text";
data => segRope _ "data";
bss => segRope _ "bss";
ENDCASE => ERROR;
RETURN [NEW [ObjectFiles.VarLocBody _ [
bitSize: IF stab.size#0 THEN stab.size*8 ELSE unspecdBitSize,
where: fSegment[
[0, segRope],
(stab.value - segBase)*8,
stab.value + header.text.byteOffset - header.textLoadOffset] ]]]};
SELECT stab.stabType FROM
Fun => RETURN MkSegment[text];
STSym => RETURN MkSegment[data];
LCSym => RETURN MkSegment[bss];
LSym, PSym => {
byteOffset: INT ~ LOOPHOLE[stab.value];
vLB: ObjectFiles.VarLoc;
IF stab.stabType=PSym AND stab.size>4 AND stab.size # LAST[CARD] THEN
vLB _ NEW[ObjectFiles.VarLocBody _ [
bitSize: stab.size*8,
where: indirect[
base: NEW[ObjectFiles.VarLocBody _ [
bitSize: 32,
where: frame[bitOffset: 8*byteOffset]]],
offset: CirioTypes.zeroBA]]]
ELSE vLB _ NEW[ObjectFiles.VarLocBody _ [
bitSize: IF stab.size#0 THEN stab.size*8 ELSE unspecdBitSize,
where: frame[bitOffset: 8*byteOffset]]];
RETURN[vLB]};
RSym => {
sz: CARD ~ IF stab.size#LAST[CARD] AND stab.size#0 THEN stab.size*8 ELSE unspecdBitSize;
vLB: ObjectFiles.VarLoc;
IF stab.size>4 AND stab.size # LAST[CARD] THEN
vLB _ NEW[ObjectFiles.VarLocBody _ [
bitSize: sz,
where: indirect[
base: NEW[ObjectFiles.VarLocBody _ [
bitSize: 32,
where: register[regNum: stab.value]]],
offset: CirioTypes.zeroBA]]]
ELSE vLB _ NEW[ObjectFiles.VarLocBody _ [
bitSize: sz,
where: register[regNum: stab.value]]];
RETURN[vLB]
};
GSym => RETURN [NEW [ObjectFiles.VarLocBody _ [
bitSize: IF stab.size#0 THEN stab.size*8 ELSE unspecdBitSize,
where: namedCommon[Rope.Concat["_", ObjectFiles.CNameOfStab[stab]], 0, 0, FALSE, FALSE] ]]];
ENDCASE => RETURN ObjectFiles.MakeUnknownVarLoc[IO.PutFR["unrecognized stabType (%02xH) for %g", [cardinal[ORD[stab.stabType]]], [rope[ObjectFiles.CNameOfStab[stab]]] ]];
};


PCtoLineNumMap: TYPE = REF PCtoLineNumMapBody;
PCtoLineNumMapBody: TYPE = ObjectFilesPrivate.PCtoLineNumMapBody;

LineNumToPCMap: TYPE = REF LineNumToPCMapBody;
LineNumToPCMapBody: TYPE = ObjectFilesPrivate.LineNumToPCMapBody;

SLineData: TYPE = ObjectFilesPrivate.SLineData;

InstallPCLineNumMaps: PROC[module: Module, lineNums, lineNums2: LineNumList] =
{
CompareByCLineNum: PROC[ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison] =
{
info1: REF SLineData _ NARROW[ref1];
info2: REF SLineData _ NARROW[ref2];
IF info1.cLineNum < info2.cLineNum THEN RETURN[less];
IF info1.cLineNum > info2.cLineNum THEN RETURN[greater];
IF info1.parsedRelPC.relPC < info2.parsedRelPC.relPC THEN RETURN[less];
IF info1.parsedRelPC.relPC > info2.parsedRelPC.relPC THEN RETURN[greater];
RETURN[equal];
};

CompareByPC: PROC[ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison] =
{
info1: REF SLineData _ NARROW[ref1];
info2: REF SLineData _ NARROW[ref2];
IF info1.parsedRelPC.relPC < info2.parsedRelPC.relPC THEN RETURN[less];
IF info1.parsedRelPC.relPC > info2.parsedRelPC.relPC THEN RETURN[greater];
IF info1.cLineNum < info2.cLineNum THEN RETURN[less];
IF info1.cLineNum > info2.cLineNum THEN RETURN[greater];
RETURN[equal];
};

sortedList: LIST OF REF ANY _ NIL;
IF module.pcToLineNum # NIL OR module.lineNumToPC # NIL THEN
{
IF module.pcToLineNum = NIL OR module.lineNumToPC  = NIL THEN ERROR;
RETURN;
};

sortedList _ List.Sort[lineNums.lines, CompareByCLineNum];
module.lineNumToPC _ NEW[LineNumToPCMapBody[lineNums.nLines]];
FOR I: INTEGER IN [0..lineNums.nLines) DO
info: REF SLineData _ NARROW[sortedList.first];
module.lineNumToPC[I] _ info^;
sortedList _ sortedList.rest;
ENDLOOP;

sortedList _ List.Sort[lineNums2.lines, CompareByPC];
module.pcToLineNum _ NEW[PCtoLineNumMapBody[lineNums2.nLines]];
FOR I: INTEGER IN [0..lineNums2.nLines) DO
info: REF SLineData _ NARROW[sortedList.first];
module.pcToLineNum[I] _ info^;
sortedList _ sortedList.rest;
ENDLOOP;
};
TypeFromData: PROC [data: BYTE] RETURNS [stabType: ObjectFiles.StabType] ~ {
val: CARD _ data;
stabType _ SELECT val FROM 
LBrac => LBrac,
RBrac => RBrac,
SLine => SLine,
Fun => Fun,
PSym => PSym,
LSym => LSym,
RSym => RSym,
STSym => STSym,
LCSym => LCSym,
GSym => GSym,
Main => Main,
SO => SO,
BIncl => BIncl,
EIncl => EIncl,
Excl => Excl,
SOL => SOL,
ENDCASE => Unspecified
};

BasicReadStab: ENTRY PROC[whole: Parsed, stream: IO.STREAM, stabX: CARD, lineNum, lineNum2: LineNumList _ NIL] RETURNS[stab: Stab, numStabsRead: CARD] ~ {

ENABLE UNWIND => NULL;
stab _ NEW [StabBody];
[stab^, numStabsRead] _ ReadStabBody[whole, stream, stabX, lineNum, lineNum2];
RETURN
};

previousStream: IO.STREAM _ NIL;
cloneStream: IO.STREAM _ NIL;

ReadStabBody: PROC [whole: Parsed, stream: IO.STREAM, stabX: CARD, lineNum, lineNum2: LineNumList _ NIL] RETURNS [stab: StabBody, numStabsRead: CARD] ~ {
rope: Rope.ROPE _ "\\";
size, rLen, offset: CARD16 _ 0;
value: CARD32;
stabType: ObjectFiles.StabType;
IO.SetIndex[stream, whole.header.syms.byteOffset+stabX*BYTES[WireSTEntryBody]];
TRUSTED{[] _ IO.UnsafeGetBlock[stream, [LOOPHOLE[@WireStabBuffer^], 0, BYTES[WireSTEntryBody]]]};
stabType _ TypeFromData[WireStabBuffer.type];
size _ Basics.Card16FromH[WireStabBuffer.desc];
value _ Basics.Card32FromF[WireStabBuffer.value];
IF stream#previousStream THEN {
cloneStream_PFS.StreamFromOpenFile[openFile~PFS.OpenFileFromStream[stream]];
previousStream _ stream;
};
rope _ ReadStringX[cloneStream, whole, Basics.Card32FromF[WireStabBuffer.stringX]];
rLen _ rope.Length[];
numStabsRead _ 1;
IF ~rope.IsEmpty[] THEN
WHILE rope.Fetch[rLen-1] = '\\ DO
offset _ offset + 1;
IO.SetIndex[stream, whole.header.syms.byteOffset+(stabX + offset)*BYTES[WireSTEntryBody]];
TRUSTED{[] _ IO.UnsafeGetBlock[stream, [LOOPHOLE[@WireStabBuffer^], 0, BYTES[WireSTEntryBody]]]};
rope _ rope.Substr[len:rLen-1].Concat[ReadStringX[cloneStream, whole, Basics.Card32FromF[WireStabBuffer.stringX]]];
rLen _ rope.Length[];
numStabsRead _ numStabsRead + 1;
ENDLOOP;
stab _ [
module: NIL,
stabX: stabX,
stabType: stabType,
size: size,
value: value,
rope: rope
];
IF stab.stabType = SLine AND lineNum # NIL THEN {
info: REF SLineData _ NEW[SLineData_[cLineNum: Basics.Card16FromH[WireStabBuffer.desc], parsedRelPC: [[1, ".text"], stab.value]]];
lineNum.lines _ CONS[info, lineNum.lines];
lineNum.nLines _ lineNum.nLines + 1;
lineNum2.lines _ CONS[info, lineNum2.lines];
lineNum2.nLines _ lineNum2.nLines + 1;
};
RETURN};

WireStabBuffer: WireSTEntry _ NEW[WireSTEntryBody];


ReadStringX: PROC[stream: IO.STREAM, whole: Parsed, stringX: CARD] RETURNS[rope: Rope.ROPE] ~ {
IO.SetIndex[stream, whole.header.stringOffset+stringX];
rope _ ReadRope[stream]};


RopeBufferSize: CARD = 100;
RopeBuffer: REF PACKED ARRAY [0..RopeBufferSize) OF CHAR _ NEW[PACKED ARRAY [0..RopeBufferSize) OF CHAR];


ReadRope: PROC[s: IO.STREAM] RETURNS[rope: Rope.ROPE] =
BEGIN
ENABLE UNWIND => NULL;
rope: Rope.ROPE _ NIL;
WHILE TRUE DO
nChars: CARD _ RopeBufferSize; -- tentative
rt: Rope.Text;

TRUSTED{[] _ IO.UnsafeGetBlock[s, [LOOPHOLE[@RopeBuffer^], 0, RopeBufferSize]]};

FOR I: CARD IN [0..RopeBufferSize) DO
IF RopeBuffer[I] = '\000 THEN {nChars _ I; EXIT};
ENDLOOP;

rt _ Rope.NewText[nChars];
FOR I: CARD IN [0..nChars) DO rt[I] _ RopeBuffer[I] ENDLOOP;
rope _ IF rope = NIL THEN (IF nChars = 0 THEN "" ELSE rt)
ELSE (IF nChars = 0 THEN rope ELSE Rope.Concat[rope, rt]);
IF nChars < RopeBufferSize THEN EXIT;
ENDLOOP;
RETURN[rope];
END;
RandomTestFindStabRange: Commander.CommandProc = 
{
args: CommanderOps.ArgumentVector _ CommanderOps.Parse[cmd];
path: PATH _ PFS.PathFromRope[args[1]];
fileSet: SystemInterface.FileSet _ SystemInterface.CreateFileSet[];
{ ENABLE UNWIND => SystemInterface.CloseFileSet[fileSet];
file: SystemInterface.CirioFile _ SystemInterface.GetCirioFile[fileSet, path];
nextSeed: INT _ IF args.argc < 3 THEN 4466 ELSE Convert.IntFromRope[args[2]];
start: BasicTime.GMT _ BasicTime.Now[];
WHILE TRUE DO
rs: Random.RandomStream _ Random.Create[seed: nextSeed];
whole: Parsed _ ObjectFiles.CreateParsed[file, "SunADotOut"];
IO.PutF[cmd.out, "beginning seed = %g at %g\N", IO.card[nextSeed], IO.time[start]];
FOR I: INT IN [0..100) DO
pc: CARD _ Random.ChooseInt[rs, 0, whole.header.text.byteLength];
range: StabRange _ AlternativeFindModuleStabRange[whole, pc];
IO.PutF[cmd.out, "\Tpc = %g gives firstX: %g  count: %g\N", IO.card[pc], IO.card[range.first], IO.card[range.count]];
ENDLOOP;
ENDLOOP;
};
SystemInterface.CloseFileSet[fileSet];
};


SADOScanModuleStructure: PROC [module: Module, perFn: ObjectFilesPrivate.FnConsumer] ~ {
I: CARD _ 0;
WHILE I < module.stabs.nStabs DO
stab: Stab ¬ module.stabs[I];
IF stab.module = NIL THEN I _ I+1
ELSE SELECT stab.stabType FROM
GSym, LCSym, STSym, LSym => I _ I + 1;
Fun => {funStab: Stab ~ stab;
firstLocal: Stab _ NIL;
WHILE I+1 < module.stabs.nStabs DO
I _ I + 1;
stab _ module.stabs[I];
IF stab.module = NIL THEN LOOP;
SELECT stab.stabType FROM
PSym, RSym => NULL;
STSym, LSym => IF BracketFollows[module, I]
THEN {firstLocal _ stab; EXIT}
ELSE {firstLocal _ NIL; EXIT};
LBrac => {firstLocal _ stab; EXIT};
Fun, GSym, LCSym => {firstLocal _ NIL; EXIT};
SLine, Invalid, Unspecified, Main, SO, BIncl, EIncl, Excl, SOL => NULL;
ENDCASE => SystemInterface.ShowReport[IO.PutFR["Found %g stab while scanning the arguments for function %g in %g", [rope[ObjectFiles.RopeForStabType[stab.stabType]]], [rope[ObjectFiles.CNameOfStab[funStab]]], [rope[ObjectFiles.DescribeModule[module]]] ], $normal];
ENDLOOP;
I _ perFn[funStab, firstLocal, I+module.firstStabX] - module.firstStabX};
SLine, Invalid, Unspecified, Main, SO, BIncl, EIncl, Excl, SOL => I _ I + 1;
ENDCASE => {
SystemInterface.ShowReport[IO.PutFR["Found %g stab while scanning in the global space of %g", [rope[ObjectFiles.RopeForStabType[stab.stabType]]], [rope[ObjectFiles.DescribeModule[module]]] ], $normal];
I _ I + 1};
ENDLOOP;
RETURN};

BracketFollows: PROC [module: Module, J: CARD] RETURNS [BOOL] ~ {
FOR J _ J+1, J+1 WHILE J < module.stabs.nStabs DO
stab: Stab ¬ module.stabs[J];
IF stab.module = NIL THEN LOOP;
SELECT stab.stabType FROM
Fun, GSym, LCSym => RETURN [FALSE];
LBrac => RETURN [TRUE];
ENDCASE => NULL;
ENDLOOP;
RETURN [FALSE]};

SADOScanFnStructure: PROC [module: Module, funStab: Stab, firstLocal: Stab _ NIL, nextX: CARD, perParm: PROC [Stab] _ NIL, perBracket: ObjectFilesPrivate.BracketConsumer _ NIL] RETURNS [limitX, limitPc: CARD] ~ {
K: CARD;
more: BOOL _ TRUE;
IF perParm#NIL THEN PassLocals[module, funStab, funStab.stabX+1, nextX, perParm];
IF firstLocal=NIL THEN limitX _ nextX
ELSE limitX _ DoBrack[module, funStab, firstLocal, perBracket];
K _ limitX-module.firstStabX;
WHILE K < module.stabs.nStabs DO --seek limitPC, and more bracket pairs.
stab: Stab _ module.stabs[K];
IF stab.module = NIL THEN K _ K + 1
ELSE SELECT stab.stabType FROM
Fun => RETURN [limitX, stab.value];
LBrac, RSym => IF more
THEN {limitX _ DoBrack[module, funStab, stab, perBracket];
K _ limitX-module.firstStabX}
ELSE K _ K + 1;
STSym, LSym => IF more AND BracketFollows[module, K]
THEN {limitX _ DoBrack[module, funStab, stab, perBracket];
K _ limitX-module.firstStabX}
ELSE {K _ K + 1; more _ FALSE};
ENDCASE => K _ K + 1;
ENDLOOP;
RETURN [limitX, module.limitPC]};

DoBrack: PROC [module: Module, funStab, first: Stab, perSubBracket: ObjectFilesPrivate.BracketConsumer] RETURNS [limitX: CARD] ~ {
IF perSubBracket#NIL THEN limitX _ perSubBracket[first]
ELSE limitX _ SADOScanBktStructure[module, funStab, first, NIL, NIL].limitX;
RETURN};

SADOScanBktStructure: PROC [module: Module, funStab, first: Stab, perLocal: PROC [Stab] _ NIL, perSubBracket: ObjectFilesPrivate.BracketConsumer _ NIL] RETURNS [limitX, firstPc, limitPc: CARD] ~ {
I: CARD _ first.stabX - module.firstStabX;
stab: Stab;
FOR I _ I, I+1 WHILE I < module.stabs.nStabs DO
stab ¬ module.stabs[I];
IF stab.module = NIL THEN LOOP;
SELECT stab.stabType FROM
STSym, LSym, RSym => IF perLocal#NIL THEN perLocal[stab];
LBrac => {
J: CARD _ I+1;
firstPc _ stab.value;
WHILE J < module.stabs.nStabs DO
stab ¬ module.stabs[J];
IF stab.module = NIL THEN J _ J+1
ELSE SELECT stab.stabType FROM
RBrac => RETURN [J+1+module.firstStabX, firstPc, stab.value];
STSym, LSym, RSym, LBrac => J _ DoBrack[module, funStab, stab, perSubBracket] - module.firstStabX;
SLine, Invalid, Unspecified, Main, SO, BIncl, EIncl, Excl, SOL => J _ J +1;
Fun => {--Since we're debugging C, which has no nested functions, this shouldn't happen; if it does, we assume the bracket parsing has failed and re-synch with top level enumeration of functions
SystemInterface.ShowReport[IO.PutFR["Found FUN stab after an LBRAC in function %g in %g", [rope[ObjectFiles.CNameOfStab[funStab]]], [rope[ObjectFiles.DescribeModule[module]]] ], $normal];
RETURN [J+module.firstStabX, firstPc, stab.value]};
ENDCASE => SystemInterface.ShowReport[IO.PutFR["Found %g stab after an LBRAC in function %g in %g", [rope[ObjectFiles.RopeForStabType[stab.stabType]]], [rope[ObjectFiles.CNameOfStab[funStab]]], [rope[ObjectFiles.DescribeModule[module]]] ], $normal];
ENDLOOP;
SystemInterface.ShowReport[IO.PutFR["Missed an RBRAC in function %g in %g", [rope[ObjectFiles.CNameOfStab[funStab]]], [rope[ObjectFiles.DescribeModule[module]]] ], $normal];
RETURN[J+module.firstStabX, firstPc, module.limitPC]};
Fun => {--shouldn't happen, but ...
SystemInterface.ShowReport[IO.PutFR["Found FUN stab before an LBRAC in function %g in %g", [rope[ObjectFiles.CNameOfStab[funStab]]], [rope[ObjectFiles.DescribeModule[module]]] ], $normal];
RETURN [I+module.firstStabX, stab.value, stab.value]};
SLine, Invalid, Unspecified, Main, SO, BIncl, EIncl, Excl, SOL => NULL;
ENDCASE => SystemInterface.ShowReport[IO.PutFR["Found %g stab before an LBRAC in function %g in %g", [rope[ObjectFiles.RopeForStabType[stab.stabType]]], [rope[ObjectFiles.CNameOfStab[funStab]]], [rope[ObjectFiles.DescribeModule[module]]] ], $normal];
ENDLOOP;
SystemInterface.ShowReport[IO.PutFR["Missed an LBRAC in function %g in %g", [rope[ObjectFiles.CNameOfStab[funStab]]], [rope[ObjectFiles.DescribeModule[module]]] ], $normal];
RETURN[I+module.firstStabX, module.limitPC, module.limitPC]};

PassLocals: PROC [module: Module, funStab: Stab, firstX, limitX: CARD, to: PROC [Stab]] ~ {
FOR I: CARD IN [firstX - module.firstStabX .. limitX-module.firstStabX) DO
stab: Stab _ module.stabs[I];
IF stab.module#NIL THEN SELECT stab.stabType FROM
PSym, RSym, LSym, STSym => to[stab];
SLine, Invalid, Unspecified, Main, SO, BIncl, EIncl, Excl, SOL => NULL;
ENDCASE => SystemInterface.ShowReport[IO.PutFR["Found %g stab in parms/locals of %g in %g", [rope[ObjectFiles.RopeForStabType[stab.stabType]]], [rope[ObjectFiles.CNameOfStab[funStab]]], [rope[ObjectFiles.DescribeModule[module]]] ], $normal];
ENDLOOP;
RETURN};

SunADotOutGetTypeRef: ObjectFilesPrivate.GetTypeRefProcType ~ {

typeRef: Rope.ROPE;
lastChar: CHAR _ IO.PeekChar[sourceStream];
IF IsDigit[lastChar] THEN {
typeRef _ IO.GetTokenRope[sourceStream, NumTok].token;
RETURN[typeRef]}
ELSE IF lastChar='( THEN DO
lastChar _ IO.GetChar[sourceStream];
typeRef _ Rope.Concat[typeRef, Rope.FromChar[lastChar]];
IF lastChar = ') THEN RETURN[typeRef]
ENDLOOP
ELSE CCError[cirioError, IO.PutFR1["malformed type rep (starts with %q)", [character[lastChar]] ]];
};

IsDigit: PROC [c:CHAR] RETURNS [BOOL]
= INLINE { RETURN [c IN ['0 .. '9]] };

NumTok: PROC [char: CHAR] RETURNS [IO.CharClass] ~ {
SELECT char FROM
IN ['0..'9] => RETURN [other];
ENDCASE => RETURN [break]};


ModuleFromParsedAndStabRange: PUBLIC PROC[whole: Parsed, stabRange: StabRange] RETURNS[Module] =
{
RETURN [ModuleFromParsedInner[whole, stabRange]];
};


ModuleFromParsedInner: PROC[whole: Parsed, stabRange: StabRange] RETURNS[Module] = 
{
firstX, limitX: CARD;
stream: IO.STREAM;

IF stabRange.count = 0 THEN RETURN[NIL];
firstX _ stabRange.first;
limitX _ stabRange.first+stabRange.count;
stream _ SystemInterface.GetStreamForFile[whole.file];

{
ENABLE UNWIND => {
SystemInterface.ReleaseStreamForFile[whole.file, stream];
};

firstSO: Stab;
firstPC: CARD;
lag: LIST OF Module _ NIL;
[firstSO, ] _ BasicReadStab[whole, stream, firstX]; -- rope field not filled
firstPC _ firstSO.value;
FOR modules: LIST OF Module _ whole.modules, modules.rest WHILE modules # NIL DO
IF modules.first.firstPC = firstPC THEN -- we already have it
{
SystemInterface.ReleaseStreamForFile[whole.file, stream]; 
RETURN[modules.first]
};
IF firstPC < modules.first.firstPC THEN -- we should have found it by now
{
newModule: Module _ CreateModule[whole, stream, stabRange];
cell: LIST OF Module _ LIST[newModule];
cell.rest _ modules;
IF lag # NIL THEN lag.rest _ cell ELSE whole.modules _ cell;
SystemInterface.ReleaseStreamForFile[whole.file, stream];
RETURN[newModule];
};
lag _ modules;
ENDLOOP;
{
newModule: Module _ CreateModule[whole, stream, stabRange];
cell: LIST OF Module _ LIST[newModule];
IF lag # NIL THEN lag.rest _ cell ELSE whole.modules _ cell;
SystemInterface.ReleaseStreamForFile[whole.file, stream];
RETURN[newModule];
};
};
};

SADOInstallStaticVars: ObjectFilesPrivate.InstallStaticVarsType ~
{
IF NOT module.staticVarsInstalled THEN
FOR x: CARD IN [module.firstStabX..module.limitStabX)
DO
stab: Stab _ ObjectFilesPrivate.ReadStab[module, x];
IF stab = NIL THEN LOOP;
IF stab.stabType = Fun THEN EXIT;
IF stab.stabType = STSym OR stab.stabType = LCSym THEN
{
IF module.versionStampStab = NIL THEN
{
gvi: ObjectFiles.GlobalVarLoc _ NIL;
gvi _ NARROW[ObjectFilesPrivate.CheckStaticVar[stab, "versionStamp", TRUE]];
IF gvi # NIL THEN
{
dataRope: Rope.ROPE _ ObjectFilesPrivate.ReadInitialDataAsRope[module, gvi.fileByteOffset];
module.versionStampStab _ stab;
module.versionStampInfo _ NEW[ObjectFiles.VersionStampInfo_[gvi, dataRope]];
LOOP;
};
};
IF module.globalFrameStab = NIL THEN
{
gvi: ObjectFiles.GlobalVarLoc _ NIL;
gvi _ NARROW[ObjectFilesPrivate.CheckStaticVar[stab, "globalframe", TRUE]];
IF gvi # NIL THEN
{
module.globalFrameStab _ stab;
module.globalFrameGvl _ gvi;
LOOP;
};
};
};
ENDLOOP;

module.staticVarsInstalled _ TRUE;
};

SADOAlterFunStab: ObjectFilesPrivate.AlterFunStabType ~ {RETURN [module.funStabs[funStabX].stab]; };

SADOGetSPOffset: ObjectFilesPrivate.GetSPOffsetType ~ {
entryRelativePC: CARD _ spc.relPC;
inst0: CARD _ ObjectFiles.ReadInstruction[module, [spc.fSeg, spc.relPC]];
inst1: CARD _ ObjectFiles.ReadInstruction[module, [spc.fSeg, spc.relPC+4]];
inst2: CARD _ ObjectFiles.ReadInstruction[module, [spc.fSeg, spc.relPC+8]];
spOffset: INT _ SaveProtocol1[inst0, inst1, inst2];
IF spOffset = 0 THEN spOffset _ SaveProtocol2[inst0];
RETURN[spOffset];
};

SaveProtocol1: PROC[inst0, inst1, inst2: CARD] RETURNS[spOffset: INT] ~ {
decoded0: DecodedSparcInstruction _ DecodeSparcInstruction[inst0];
decoded1: DecodedSparcInstruction _ DecodeSparcInstruction[inst1];
decoded2: DecodedSparcInstruction _ DecodeSparcInstruction[inst2];

IF decoded0.op # 0 THEN RETURN[0];
IF decoded0.op2 # 4 THEN RETURN[0];
IF decoded0.rd # 1 THEN RETURN[0];  -- this is %g1

IF decoded1.op # 2 THEN RETURN[0];
IF decoded1.op3 # 0 THEN RETURN[0];
IF decoded1.i # 1 THEN RETURN[0];
IF decoded1.rs1 # 1 THEN RETURN[0]; -- this is %g1
IF decoded1.rd # 1 THEN RETURN[0]; -- this is %g1

IF decoded2.op # 2 THEN RETURN[0];
IF decoded2.op3 # 74B THEN RETURN[0];
IF decoded2.i # 0 THEN RETURN[0];
IF decoded2.rs1 # 16B THEN RETURN[0]; -- this is %sp
IF decoded2.rs2 # 1 THEN RETURN[0]; -- this is %g1
IF decoded2.rd # 16B THEN RETURN[0]; -- this is %sp

spOffset _ LOOPHOLE[PBasics.BITSHIFT[decoded0.imm22, 10] + decoded1.simm13];
};

SaveProtocol2: PROC[inst0: CARD] RETURNS[spOffset: INT] ~ {
decoded0: DecodedSparcInstruction _ DecodeSparcInstruction[inst0];

IF decoded0.op # 2 THEN RETURN[0];
IF decoded0.op3 # 74B THEN RETURN[0];
IF decoded0.i # 0 THEN RETURN[0];
IF decoded0.rs1 # 16B THEN RETURN[0]; -- this is %sp
IF decoded0.rd # 16B THEN RETURN[0]; -- this is %sp

spOffset _ decoded0.simm13;
};

DecodedSparcInstruction: TYPE = RECORD[
op, disp30, rd, a, cond, op2, imm22, disp22, op3, rs1, i, asi, rs2, simm13, opf: CARD]; 

DecodeSparcInstruction: PROC[inst: CARD] RETURNS[DecodedSparcInstruction] ~ {
RETURN[[
op: PBasics.BITAND[3, PBasics.BITSHIFT[inst, -30]],
disp30: PBasics.BITAND[7777777777B, inst],
rd: PBasics.BITAND[37B, PBasics.BITSHIFT[inst, -25]],
a: PBasics.BITAND[1, PBasics.BITSHIFT[inst, -29]],
cond: PBasics.BITAND[17B, PBasics.BITSHIFT[inst, -25]],
op2: PBasics.BITAND[7, PBasics.BITSHIFT[inst, -22]],
imm22: PBasics.BITAND[17777777B, inst],
disp22: PBasics.BITAND[17777777B, inst],
op3: PBasics.BITAND[77B, PBasics.BITSHIFT[inst, -19]],
rs1: PBasics.BITAND[37B, PBasics.BITSHIFT[inst, -14]],
i: PBasics.BITAND[1, PBasics.BITSHIFT[inst, -13]],
asi: PBasics.BITAND[377B, PBasics.BITSHIFT[inst, -5]],
rs2: PBasics.BITAND[37B, inst],
simm13: PBasics.BITAND[17777B, inst],
opf: PBasics.BITAND[777B, PBasics.BITSHIFT[inst, -5]]]];
};


transTable: ObjectFilesPrivate.TranslationTable _ NEW[ObjectFilesPrivate.TranslationTableBody[3]];
Commander.Register["sunadoRandomTestFindStabRange", RandomTestFindStabRange];
transTable[0] _ [2, 2, 263, "SunADotOut"];
transTable[1] _ [2, 2, 264, "SunADotOut"];
transTable[2] _ [2, 2, 267, "SunADotOut"];
ObjectFilesPrivate.RegisterObjectFileFlavor[NEW[ObjectFilesPrivate.ObjectFileFlavorBody _ [
"SunADotOut",
SunADotOut,
ReadHeader,
SunADotOutModuleFromParsedAndPC,
SunADotOutVarLocFromStab,
SunADotOutGetTypeRef,
NIL,
SADOScanModuleStructure,
SADOScanFnStructure,
SADOScanBktStructure,
TRUE,
SADOInstallStaticVars,
SADOAlterFunStab,
SADOGetSPOffset
]],
transTable];
END..
���n��
SunADotOutFiles.mesa
Copyright Ó 1990, 1991, 1992 by Xerox Corporation.  All rights reserved.
Modified from DotOAccessImpl.mesa
Laurie Horton, June 24, 1992 6:10 pm PDT
Philip James, February 10, 1992 1:42 pm PST
Last tweaked by Mike Spreitzer January 21, 1993 5:33 pm PST
Chauser, March 24, 1992 10:36 am PST
Katsuyuki Komatsu April 1, 1992 3:32 pm PST
Useful types
Following types are defined in ObjectFiles
some Stab types

Following types are defined in ObjectFilesPrivate


Following values are of global interest

theContStab: Stab ~ NEW [StabBody _ [module: NIL, stabX: CARD.LAST, stringX: CARD32.LAST, type: BYTE.LAST, other: BYTE.LAST, desc: CARD16.LAST, value: CARD32.LAST, rope: "don't look at me, I'm a continuation of an earlier stab", dx: CARD.LAST]];
This stab is put in module.stabs[i] wherever continuation stabs would appear.
The following computations are adapted from /usr/include/sys/exec.h,
obtained by: REdit cartiff -f /usr/include/sys/exec.h
The following computations are adapted from /usr/include/sun4/a.out.h,
obtained by: REdit cartiff -f /usr/include/sun4/a.out.h
The following record type was constructed based on page 1339 of the Sun Release 4.0 documentation  (A.OUT documentation)
PROC[stream: IO.STREAM] RETURNS[Header] = TRUSTED
The following computations are adapted from /usr/include/sun4/a.out.h,
obtained by: REdit cartiff -f /usr/include/sun4/a.out.h
text segment is loaded into memory at nominal 0 + textLoadOffset
the 02000H is the largest page size on certain machines, and allows the first page of memory to remain unmapped.

Module

We assume that there are dbx stabs located in the whole in the symbol table entry range [firstX..limitX).
We assume that the first stab is a SO stab whose value is the first pc of the module.
We assume that this or the next stab is a SO stab containing the file name.
The first and limitX have presumably been obtained by other means.  For example, at this writing the CirioNub will provide access to a table of pc ranges located in the target world.  (In addition, we provide a rather crude mechanism below to use when the CirioNub approach is not available.  (e.g., when examining a file that is not part of a PCedar load state.)
We are unable to define a legitimate limitPC.  We assume that when, later, an attempt is made to look up a pc in the bracket structure, we have already determined that that pc definitely belongs to this Module.  Thus, we can use header.textSize as a (bogus) limitPC.

PROC [whole: Parsed, spc: FileSegmentPC, moduleRope: ROPE _ NIL] RETURNS [Module] ~ {

prepare the Module for a call on BuildBrackets
lets read the all stabs now (WITH their ropes)
(I did some timing tests on a Dorado for RopeImpl.  It took about 6 seconds to perform this loop, including the allocates.  On the other hand, it takes about 4 seconds to spin through the entries without allocating the stab records.  So, since at some point I have to traverse all the entries to find the Fun stabs, I might as well allocate them.)
(we also relocate the pc value of bracket stabs.)

now lets finish up


Finding Modules

This procedure should only be used when examining a whole which is not in the load state.  If we are given a pc in a running PCR, it is far faster to use procedures in LoadStateAccess (if supported!) to obtain the stab range for a Module.  Those procedures use a table available from the nub to find the stab range, then produce the Module through a call on CreateModule.
foo: IO.STREAM _ NIL;
ENABLE PFS.Error => {
foo _ PFS.StreamOpen[PFS.PathFromRope["/tmp/PJ.out"], create];
CONTINUE};
foo _ PFS.StreamOpen[PFS.PathFromRope["/tmp/PJ.out"], append];
IO.PutF[foo, "downLooping [%g .. 0]\n", IO.card[nominalX]];
IO.PutF[foo, "(%g)\tstabType:\t%g\n", IO.card[x], IO.rope[ObjectFiles.RopeForStabType[info.stabType]]];
IO.PutF[foo, "Bailing out because baseSO.stabType # SO\n"];
IO.Close[foo];
foo _ NIL;
IO.PutF[foo, "upLooping [%g .. %g]\n", IO.card[nominalX], IO.card[whole.stabLimit]];
IO.PutF[foo, "(%g)\tstabType:\t%g\n", IO.card[x], IO.rope[ObjectFiles.RopeForStabType[info.stabType]]];
IO.PutF[foo, "good answer: [%g, %g]\n", IO.card[baseSO.stabX], IO.card[highDbx-baseSO.stabX+1]];
IO.Close[foo];
foo _ NIL;
IO.PutF[foo, "bad answer [0, 0] because\n\tbaseSO.value\t%g\n\trelativePC\t%g\n\tmaxPC\t%g\nThese values aren't in descending order.", IO.card[baseSO.value], IO.card[relativePC], IO.card[maxPC]];
IO.Close[foo];
foo _ NIL;
if successful, the value returned is somewhere within the range of stabXs for the desired Module.  If unsuccessful, the value returned is whole.header.nEntries.
We depend upon the fact that all pc containing dbx stabs for a given whole file occur before or after any other dbx stab. We do not require that the pc values be sorted within the block of stabs for a given whole.  However, we require that all our pc values are greater than or less than a pc value from some other block of dbx stabs.

now, we assert an invariant
all SO dbx stabs to the left of or at x have a pc value < pc
all SO dbx stabs to the right of or at y have a pc value > pc

We start looking at start, and stop just before limit.  Hence, if we fail to find a pc stab, then we return [limit, 0, 0]

IF info.stabType > 1fH THEN dbxLimit _ x+1;
PROC [stab: Stab] RETURNS [ObjectFiles.VarLoc] ~ {
C Line-Number to Relative-PC maps
sorted by PC
sorted by Line Num

Stabs

MergeContinuationStabs: ENTRY PROC[module: Module, stabX: CARD] ~ {
ENABLE UNWIND => NULL;
stab: Stab _ module.stabs[stabX];
DO
rlen: INT ~ stab.rope.Length[];
IF rlen>0 AND stab.rope.Fetch[rlen-1]='\\ THEN {
s2: Stab ~ module.stabs[stabX+stab.dx];
IF s2.stabType=stab.stabType AND s2.value=stab.value AND s2.other=stab.other AND s2.desc=stab.desc THEN {
stab.dx _ stab.dx.SUCC;
stab.rope _ stab.rope.Substr[len: rlen-1].Concat[s2.rope];
}
ELSE EXIT}
ELSE EXIT;
ENDLOOP;
RETURN};

warning: does NOT relocate LBrac or RBrac stabs.
Also: the module field is set to NIL;

hack to avoid messing up the stream buffering
all users should be in the monitor
WARNING: the rope reading code causes random repositioning of the stream.  That is CRUDE and expensive.  Simply for this experiment.  I need to do something better.  Read in the whole string table?  read in the symbols, sort them, then read in string table and pick out the interesting strings?  What?
symbol ropes

we assume that the stream has been positioned at the beginning of a sequence of chars terminated by a '\000 char.

usage: RandomTestFindStabRange /dir/.../dir/[sun4/]foo[.c2c].o
Stab Structure
PROC [sourceStream:IO.STREAM] RETURNS [Rope.ROPE]
nest so that we can catch unwinds and release the stream
first, see if we already have it
PROC[module: Module] =
this procedure examines all stabs up to the first Fun stab, looking for certain expected stabs.  These are recorded in the whole.
Following procedures compute the SP offset from FP by examining the entry protocol to a procedure.
SPoffset = 0 means that the instructions do not form the expected protocol.

protocol 1 is
sethi	%hi(Offset),%g1
add	%g1,%lo(Offset),%g1
save	%sp,%g1,%sp
check for   sethi %hi(spOffset),%g1
check for   add %g1,%lo(spOffset),%g1
check for    save %sp,%g1,%sp
protocol 2 is
save	%sp, spOffset,%sp
check for    save %sp, spOffset, %sp
Main code


�Ê)Ë��•NewlineDelimiter
™�codešœ™K™HK™!K™(K™+K™;K™$K™+—K˜�šÏk	˜	Kšœœ(œœ˜BKšœ
œœ˜Kšœœ˜$Kšœœ
˜Kšœ
œ˜(Kšœ
œ˜+Kšœœ˜Kšœ˜Kšœœœ˜$Kšœœ€˜‘Kšœœþ˜–Kšœœœœ˜!Kšœœ8˜AKšœ	œœ˜Kšœœ"˜.Kšœ˜K˜Kšœœt˜‰—K˜�K˜�K˜�šÏnœœ˜Kšœ?œ2œ˜œKšœ˜—Kšœ˜K˜�Kšžœœ&œœ˜RK˜�™Kšœœœ˜—K˜�™*K˜�Kšœœ˜Kšœ
œ˜-Kšœ
œ˜&Kšœ
œ˜&Kšœœ ˜/Kšœœ˜0Kšœœ ˜6K˜�Kšœ™K™�Kšžœœ˜Kšžœœ˜Kšžœœ˜Kšžœœ˜Kšžœœ˜Kšžœœ˜Kšžœœ˜Kšžœœ˜Kšžœœ˜Kšžœœ˜Kšžœœ˜KšÐbkœœ˜Kšžœœ˜Kšžœœ˜Kšžœœ˜KšŸœœ˜K˜�—K˜�™1K™�Kšœœœ˜Kšœœœ!˜8K˜�Kšœœœ˜Kšœœœ!˜8K™�Kšœœœ˜Kšœœ!˜1K˜�Kšœœ(˜?K˜�Kšœ	œ˜+K˜�Kšœ
œ"˜3Kšœœ&˜;K˜�—™'K™�Kšœœ˜2K˜�š!œœœ	œœœœœœ	œœœœ	œœGœœ™õK™M—K˜�šœD™DKšœ5™5—K˜�Kšžœœ˜Kšœœ˜Kšœœ˜Kšžœœ˜K˜�Kšžœœ
˜Kšžœœ
˜Kšžœœ
˜K˜�šœF™FKšœ7™7—K˜�Kšžœœ
˜Kšžœœ
˜˜�Kšœfœ™x—Kšœœœ˜&š	œœœ	œœ˜0Kšœ	œ˜Kšœ
œ	˜Kšœ
œ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜—K˜�Kšœ
œœ˜(š	œœœ	œœ˜1Kšœœ˜ Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜ —K˜�K˜�šž
œ*œ˜>Kš	œ	œœœ™1Kšœœ˜*Kš	œœœœœ˜YKšœœ%˜0Kšœœœœ˜OšœF™FKšœ7™7—Kš	œœœœ
œ
˜Pšœœœ˜6Kš	œœœœœ˜=—šœ˜Kš	œœœœœ˜6—Kšœ
œ(˜6Kšœ]˜]Kšœp˜pKšœx˜xKšœu˜uš	œœœœœ˜>Kšœ@™@Kšœp™p—šœœ˜#K˜K˜!K˜Kšœ
˜
Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜Kšœ-˜-Kšœ3˜3Kšœ˜Kšœœ˜4Kšœ3˜3Kšœ˜—Kšœ	˜Kšœ˜——K˜�K™�™™�K™iK™UK™KK™ëK™ŠK™�—šžœ6˜UKšœ1œœœ
™UKšœ	œœœœ˜$šœ˜KšœH˜HKšœ+˜1—Kšœ˜—K˜K˜�K˜�Kšœ
œœ˜(Kšœœœ
œ	œœœœœ˜LK˜�š
žœœœœ!œ
˜eKš˜Kšœœ
˜!Kšœ˜Kšœœ˜
Kšœ˜Kšœ˜Kšœ<˜<Kšœ˜Kšœœœ˜,Kšœœœ˜-K™�K™.Kšœ˜Kšœ$˜$Kšœ4˜4Kšœ˜Kšœ/Ïc9˜hK˜�™.K™ÛK™1—K™�Kšœœ˜$K˜�Kšœžœœ˜
šœ
˜Kšœ˜Kšœ
œ˜Kšœ]˜]Kšœ˜Kšœ˜šœœ˜6Kšœ)˜)—š	œžœœœ˜)Kšœœ˜"Kšœœ ˜2Kšœ˜—Kšœ˜Kšœ˜Kšœ˜—K˜K˜�K™K™�Kšœ2˜2Kšœœœœœœ˜yK˜�Kšœœ˜#Kšœœ˜K˜�šœœ%˜>Kšœ˜Kšœ˜Kšœ
˜
Kšœ%˜%Kšœ˜Kšœ˜Kšœ	œ˜
K˜Kšœ	œ˜
Kšœœ˜K˜�—Kšœ	˜Kšœ˜K™�K˜�——™K™�Kš	œœœœœ˜UKšžœ*˜C˜�Kšœó™ó—Kšœœœœ™šžœœœœ˜cKš˜Kšœ˜šœœ™Kšœœœ&™>Kšœ™
—Kšœœœ&™>š	œ	œœœ	˜'Kš˜Kšœ
œ"˜0Kšœ; ˜GKšœœ˜Kšœ	œ˜Kšœ"œœ	˜8Kšœ&œ™;š	œœ
œœ˜*Kšœ2˜2Kšœ$œ
œ3™gš	œœœœ˜JKšœœ˜—Kšœœœ˜+Kšœœœœ˜DKšœ
œ˜#Kšœ˜—šœœœ˜Kšœ9™;Kšœ™Kšœœ™
Kšœ˜—Kšœ%œœ™Tšœœœ˜-Kšœ2˜2Kšœ$œ
œ3™gšœœ˜4Kšœœ˜—Kšœœœœ˜DKšœ
œ˜#Kšœ˜—šœœœ˜<Kšœ&œœ™`Kšœ™Kšœœ™
Kšœ+˜1—Kšœ…œœœ™ÃKšœ™Kšœœ™
Kšœ	˜Kšœ˜—Kšœ˜šœ˜K˜�K™ K™ÎK™�——š
žœœœœœ˜:Kš˜Kšœœ˜Kšœœ˜K˜�™K™<Kšœ=™=—K™�šœ	˜Kšœœ˜KšœC˜Cšœœ˜K˜K˜Kšœœ˜,K˜Kšœœ˜—Kšœ˜—Kšœ˜
Kšœ˜—˜�Kšœy™yK™�—š
žœœœ	œœ˜bKš˜Kšœ
œ ˜ šœœœ˜+Kšœ2˜2Kšœœœœœœ˜XKšœœœ˜OKšœœ™+Kšœ˜—Kšœœœ˜PKšœ˜Kšœ˜—K˜�š
ž	œœœœœ˜RKš˜Kšœœœ˜Kšœœœ0˜AK˜�Kšœ5œ˜OK˜�Kšœœœœ˜aKšœ9˜9K˜�KšœW˜]Kšœ˜—K˜�Kšžœ0˜Hšœœ™2Kšœ*˜*šž	œœœ˜NKšœ
œ˜šœ	œœ˜ K˜
Kšœ˜Kšœ8˜8Kšœœ˜—Kšœœ"œÎ˜Žšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœœ˜—šœœ˜'Kšœ	œ
œ
œ˜=šœ˜Kšœ
˜
Kšœ˜KšœB˜B———šœ˜Kšœœ˜Kšœ	œ˜ Kšœ	œ˜šœ˜Kšœœœ
˜'Kšœ˜šœœ
œ
œœ˜Ešœœ˜$K˜šœ˜šœœ˜$K˜Kšœ(˜(—Kšœ˜———šœœ˜)Kšœ	œ
œ
œ˜=Kšœ(˜(—Kšœ˜
—šœ	˜	Kšœœœœœœ
œ
œ˜XKšœ˜š	œ
œ
œœ˜.šœœ˜$Kšœ˜šœ˜šœœ˜$Kšœ˜Kšœ&˜&—Kšœ˜———šœœ˜)Kšœ˜Kšœ&˜&—Kšœ˜K˜—šœœœ˜/Kšœ	œ
œ
œ˜=KšœJœœ˜\—Kšœœœ9œ<˜ª—K˜K˜�——™!K˜�Kšœœœ˜.šœœ)˜AK™—K˜�Kšœœœ˜.šœœ)˜AK™—K˜�Kšœœ ˜/K˜�šžœœ4˜NKšœ˜šžœœœœœœœ˜SJšœ˜Jšœœ
œ˜$Jšœœ
œ˜$Jšœ!œœ˜5Jšœ!œœ
˜8Jšœ3œœ˜GJšœ3œœ
˜JJšœ˜Jšœ˜J˜�—šžœœœœœœœ˜MJšœ˜Jšœœ
œ˜$Jšœœ
œ˜$Jšœ3œœ˜GJšœ3œœ
˜JJšœ!œœ˜5Jšœ!œœ
˜8Jšœ˜Jšœ˜J˜�—Kšœœœœœœ˜"š	œœœœ˜<Kšœ˜Kšœœœœœœ˜DKšœ˜Kšœ˜—K˜�Kšœ:˜:Kšœœ&˜>š	œžœœœ˜)Jšœœ
œ˜/Jšœ˜Jšœ˜Jšœ˜—J˜�Jšœ5˜5Kšœœ'˜?š	œžœœœ˜*Jšœœ
œ˜/Jšœ˜Jšœ˜Jšœ˜—Kšœ˜——K™�™K™�šžœœœœ™CKšœœœ™K™!š™Kšœœ™šœœœ™0Kšœ'™'š
œœœœœ™iKšœœ™Kšœ:™:Kšœ™—Kšœœ™
—Kšœœ™
Kšœ™—Kšœ™K™�—šžœœœœ%˜LKšœœ˜šœœœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ
˜
Kšœ
˜
Kšœ
˜
Kšœ˜Kšœ˜Kšœ
˜
Kšœ
˜
Kšœœ˜	Kšœ˜Kšœ˜Kšœ
˜
Kšœœ˜Kšœ˜—K˜K˜�—šž
œœœœœ	œ#œœœ˜šK˜�Kšœ0™0K™%K™�Kšœœœ˜Kšœœ˜KšœN˜NKš˜Kšœ˜K˜�—K™-Kšœœœœ˜ Kšœ
œœœ˜K˜�šžœœœœ	œ#œœ œ˜™Kšœœ˜Kšœœ˜Kšœœ˜Kšœ˜Kšœ5œ˜OKšœœœœ˜aKšœ-˜-K˜/Kšœ1˜1šœœ˜Kšœœœ˜LK˜K˜—KšœS˜SK˜Kšœ˜šœ˜šœ˜!K˜šœ@œ˜ZKšœœœœ˜aKšœs˜sK˜Kšœ ˜ Kšœ˜———šœ˜Kšœœ˜Kšœ
˜
Kšœ˜K˜Kšœ
˜
Kšœ
˜
Kšœ˜—šœœœœ˜1Kšœœ
œi˜‚Kšœœ˜*Kšœ$˜$Kšœœ˜,Kšœ&˜&K˜—Kšœ˜—K˜�šžœœ˜3KšÏt"™"—K˜�K˜�šžœœ	œœœœœ˜_Kšœ5˜7Kšœ˜KšœLœÕ™­—K˜�K˜�—™K™�Kšžœœ˜Kšž
œœœœœœœœœœœ˜iK˜�˜�Kšœq™q—šžœœœœœœ˜7Kš˜Kšœœœ˜Kšœœœ˜šœœ˜
Kšœœ ˜+K˜K˜�Kšœœœ%˜PK˜�š	œžœœœ˜%Kšœœœ˜1Kšœ˜K˜�—K˜Kšœžœœœ
œœ˜<š
œœœœœœœ˜9Kšœœœœ˜:—Kšœœœ˜%Kšœ˜—Kšœ˜
Kšœ˜—K™�K™>šžœ˜1Kšœ˜Kšœ<˜<Kšœœœ˜'šœC˜CKšœœœ*˜9KšœN˜NKš	œ
œœœœ˜Mšœœ˜'Kšœœ˜
K˜8Kšœ=˜=Kšœ.œœ˜Sš	œžœœœ
˜Kšœœ9˜AKšœ=˜=Kšœ:œœœ˜uKšœ˜—Kšœ˜—Kšœ˜—K˜&Kšœ˜K˜�——™K˜�šžœœ;˜XKšžœœ˜šœ˜ K˜Kšœœœ˜!šœœ˜Kšœ&˜&šœ˜Kšœœ˜šœ˜"Kšœ
˜
K˜Kšœœœœ˜šœ˜Kšœœ˜šœœ˜+Kšœœ˜Kšœœœ˜—Kšœœ˜#Kšœ"œœ˜-Kšœ#œœœ˜GKšœœà˜ˆ—Kšœ˜—KšœI˜I—Kšœ#œœ˜Lšœ˜Kšœœ¬˜ÉK˜——Kšœ˜—Kšœ˜—K˜�šžœœžœœœœ˜Ašœœ˜1Kšœ˜Kšœœœœ˜šœ˜Kšœœœ˜#Kšœ	œœ˜Kšœœ˜—Kšœ˜—Kšœœ˜—K˜�šžœœ4œ	œœ
œ3œœœ˜ÔKšžœœ˜Kšœœœ˜Kšœ	œœ>˜QKšœœœ˜%Kšœ;˜?Kšœ˜šœœ '˜HK˜Kšœœœ
˜#šœœ˜Kšœœ˜#šœœ˜šœ6˜:Kšœ˜—Kšœ˜—šœœœ˜4šœ6˜:Kšœ˜—Kšœœ˜—Kšœ˜—Kšœ˜—Kšœ˜!K˜�—šžœœ[œ
œ˜‚Kšœœœ˜7Kšœ7œœ	˜LKšœ˜K˜�—šžœœ2œ
œ6œœœ˜ÄKšžœœ#˜*K˜šœœ˜/K˜Kšœœœœ˜šœ˜Kšœœ
œœ˜9šœ
˜
Kšžœœ˜K˜šœ˜ K˜Kšœœœ˜!šœœ˜Kšœ	œ.˜=Kšœb˜bKšœ#œœ
˜Kšœ º˜ÂKšœœž˜»Kšœ-˜3—KšœœÑ˜ù—Kšœ˜—Kšœœ˜­Kšœ0˜6—šœ ˜#KšœœŸ˜¼Kšœ0˜6—Kšœ#œœœ˜GKšœœÒ˜ú—Kšœ˜—Kšœœ˜­Kšœ7˜=K˜�—šž
œœ1œœ˜[š	œžœœœ:˜JK˜š	œ
œœœ˜1Kšœ$˜$Kšœ#œœœ˜GKšœœÉ˜ñ—Kšœ˜—Kšœ˜K˜�—šžœ+˜?Kš
œœœœœ™1K˜�K˜Kšœ
œœ˜+šœœ˜Kšœ
œ*˜6Kšœ
˜—šœœ
œ˜Kšœœ˜$K˜8Kšœœœ	˜%Kš˜—KšœœH˜cK˜K˜�š
Ðbnœœœœœ˜%Kšœœœœ˜&K˜�—š
žœœœœœ˜4šœ˜Kšœ
œ	˜Kšœœ
˜——K˜�——˜�šžœœœ&œ
˜`šœ˜Kšœ+˜1—Kšœ˜——˜�K˜�šžœœ&œ˜Sšœ˜Kšœœ˜Kšœœœ˜K˜�Kšœœœœ˜(Kšœ˜Kšœ)˜)Kšœ6˜6K˜�™8Kšœ˜šœœ˜Kšœ9˜9K˜K˜�—Kšœ˜Kšœ	œ˜Kšœœœ
œ˜Kšœ4 ˜LKšœ˜K™ šœ
œœ&œœ˜Pšœ!œ ˜=šœ˜Kšœ:˜:Kšœ˜Kšœ˜——šœ!œ !˜IKšœ˜Kšœ;˜;Kšœœœ
œ˜'K˜Kšœœœœ˜<Kšœ9˜9Kšœ˜Kšœ˜—K˜Kšœ˜—šœ˜Kšœ;˜;Kšœœœ
œ˜'Kšœœœœ˜<Kšœ9˜9Kšœ˜Kšœ˜—Kšœ˜—šœ˜K˜�——šžœ,˜AKšœ™K™Kšœ˜šœœ˜&šœœœ'˜5Kš˜Kšœ4˜4Kšœœœœ˜Kšœœœ˜!šœœ˜6Kšœ˜šœœ˜%Kšœ˜Kšœ œ˜$Kšœœ9œ˜Lšœœ˜Kšœ˜KšœœH˜[Kšœ˜Kšœœ/˜LKšœ˜Kšœ˜—Kšœ˜—šœœ˜$Kšœ˜Kšœ œ˜$Kšœœ8œ˜Kšœœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜—Kšœ˜—Kšœ˜—Kšœ˜——K˜�Kšœœ˜"Kšœ˜K˜�—Kšžœ)œ%˜dK˜�šžœ(˜7Kšœœ
˜"Kšœœ>˜IKšœœ@˜KKšœœ@˜KKšœ
œ&˜3Kšœœ!˜5Kšœ˜Kšœ˜—˜�Kšœ!œ
œ0™bK™K—™�™
K™K™K™——š
ž
œœœœœ˜IKšœB˜BKšœB˜BKšœB˜B˜�Kšœ#™#—Kšœœœ˜"Kšœœœ˜#KšœœœÐci˜2˜�Kšœ%™%—Kšœœœ˜"Kšœœœ˜#Kšœœœ˜!Kšœœœ£˜2Kšœœœ£˜1˜�K™—Kšœœœ˜"Kšœœœ˜%Kšœœœ˜!KšœœœÏi£˜4Kšœœœ£˜2Kšœœœ¤£˜3K˜�Kšœœ	œ(˜LKšœ˜—˜�™
Kšœ™——š
ž
œœœœœ˜;KšœB˜B˜�Kšœ$™$—Kšœœœ˜"Kšœœœ˜%Kšœœœ˜!Kšœœœ¤£˜4Kšœœœ¤£˜3K˜�Kšœ˜Kšœ˜—K˜�šœœœ˜'KšœQœ˜XK˜�—šžœœœœ˜Mšœ˜Kšœœœ
˜3Kšœœ˜*Kšœœœ
˜5Kšœœœ
˜2Kšœœœ
˜7Kšœ
œœ
˜4Kšœœ˜'Kšœœ˜(Kšœ
œœ
˜6Kšœ
œœ
˜6Kšœœœ
˜2Kšœ
œœ˜6Kšœ
œ˜Kšœœ˜%Kšœ
œœ˜8—Kšœ˜K˜�———K˜�K™	™�Kšœ2œ-˜bKšœM˜MK˜*K˜*K˜*šœ,œ,˜[Kšœ
˜
Kšœ˜Kšœ˜Kšœ ˜ Kšœ˜Jšœ˜Kšœ˜J˜J˜J˜Jšœ˜K˜Kšœ˜K˜Kšœ˜Kšœ˜——K™�Kšœ˜—�…—����~2��Äk��