MobObjectFilesImpl.mesa
Copyright Ó 1990, 1991, 1992 by Xerox Corporation. All rights reserved.
Sturgis, March 29, 1990 2:31 pm PST
Last tweaked by Mike Spreitzer on December 23, 1991 9:54 am PST
Coolidge, July 17, 1990 5:16 pm PDT
Laurie Horton, May 27, 1992 8:48 am PDT
Philip James, December 26, 1991 10:13 am PST
Baran, October 21, 1991 2:14 pm PDT
Katsuyuki Komatsu April 1, 1992 3:40 pm PST
DIRECTORY
Basics USING[BITXOR, LongNumber],
CCTypes USING[CCError, CCErrorCase],
CirioTypes USING[bitsPerPtr, zeroBA],
Commander USING[CommandProc, Register],
CommandTool USING[ArgumentVector, Parse],
Convert USING[CardFromRope],
IO USING[card, int, Put, PutF, PutFR, rope, STREAM],
MobAccess USING[BlockDesc, BodySE, BTH, BTR, BTRExtension, ConstVal, CreateMobCookie, CTXH, CTXR, FetchBTR, FetchCTXR, FetchSER, FieldDesc, GenCallableBodies, GetCodedBTIFromBTH, GetCodedSeiFromSEH, GetParentOfBTH, GetRootBTH, IsLost, MakeSEHFromCodedSei, MobCookie, SEH, SER, TypeDesc, TypeInfoConsSE],
MobObjectAccessPrivate,
MobObjectFiles USING[VarLoc, VarLocBody],
ObjectFiles USING[BracketNest, BracketPair, CreateParsed, FindGlobalFrameVar, FunHandle, FunInfo, GenFunBracketPairs, GenFuns, GenSubBracketPairs, GenSymbolStabs, GetBracketNestForPC, GetFunBrackets, GetFunHandleFromNest, GetFunInfo, GetPCRange, GetSPOffset, IndirectVarLoc, Module, ModuleFromParsedAndPC, NameAndNumber, Parsed, ParseNameRope, PCRange, Stab, StabType, VarLoc, VarLocBody, VarLocCase, VarLocFromStab],
PBasics USING[bitsPerByte],
PFS USING [PathFromRope],
PFSNames USING [PATH],
RefTab USING[Create, Fetch, Insert, Key, Ref],
Rope USING[Concat, Equal, Length, ROPE],
SystemInterface USING[CirioFile, CloseFileSet, CreateFileSet, FileSet, GetCirioFile, ShowReport];
MobObjectFilesImpl: CEDAR PROGRAM
IMPORTS Basics, CCTypes, Commander, CommandTool, Convert, IO, MobAccess, ObjectFiles, PFS, RefTab, Rope, SystemInterface
EXPORTS MobObjectFiles
= BEGIN OPEN ObjF:ObjectFiles, MA:MobAccess, MOF:MobObjectFiles, MobObjectAccessPrivate;
This module provides information that requires joint inspection of mob and dotO files.
1) Given a pc, delivers the bth of the block most tightly enclosing the given pc. (Actually, the most tightly enclosing recognized block.) In addition, ensures that an association between bracket pairs and bths is constructed for the result bth and for all enclosing bths. (In so far as the association can be constructed from the dotO and mob.)
2) Given a bth and the seh for a variable occurring in the context of the bth, returns the field offset information for that variable. (This works only for blocks for which the bracket-pair-bth association has been constructed.) [Not yet installed]
BTH: TYPE = MA.BTH;
BTR: TYPE = MA.BTR;
CTXH: TYPE = MA.CTXH;
CTXR: TYPE = MA.CTXR;
SEH: TYPE = MA.SEH;
SER: TYPE = MA.SER;
CCE: ERROR[case: CCTypes.CCErrorCase, msg: Rope.ROPENIL] ← CCTypes.CCError;
JointMobParsedInfo: TYPE = REF JointMobParsedInfoBody;
JointMobParsedInfoBody: PUBLIC TYPE = MobObjectAccessPrivate.JointMobParsedInfoBody;
CreateJointMobParsedInfo: PUBLIC PROC[mob: MA.MobCookie, whole: ObjF.Parsed, module: ObjF.Module] RETURNS[JointMobParsedInfo] =
BEGIN
jmpi: JointMobParsedInfo ← NEW[JointMobParsedInfoBody←[
mob,
whole,
module,
CreateBTHashTable[],
CreateBTHashTable[],
CreateVarHashTable[],
CreateBpHashTable[]
CreateFBHashTable[],
CreateRawBodyHashTable[],
CreateSEHInfoHashTable[]]
]];
AnalyzeDotOBrackets[jmpi];
AnalyzeCallableBodySet[jmpi];
RETURN[jmpi];
END;
Stack Pointer Offset
GetSPOffset: PUBLIC PROC[callableBTH: BTH, jmpi: JointMobParsedInfo] RETURNS[INT] = {
entryRelativePC: CARD ← GetEntryPCofCallableBTH[callableBTH, jmpi];
spOffset: INT ← ObjF.GetSPOffset[jmpi.module, [[0, ""], entryRelativePC]];
RETURN[spOffset];
};
New Code starts here and continues upto debugging software February 28, 1990 9:04:08 am PST
Public Procs for procedure Frame analysis
Assumes that seh is an id entry for a field in a local frame. Assumes that the seh is in the context associated with bth.
GetVarLoc: PUBLIC PROC[seh: SEH, bth: BTH, jmpi: JointMobParsedInfo] RETURNS[MOF.VarLoc] =
BEGIN
IF seh = NIL THEN RETURN[NullVarLoc["GetVarLoc[NIL]"]]
ELSE
BEGIN
varKey: RefTab.Key ← CreateVarKeyFromSEH[seh];
vi: VarInfo ← NARROW[RefTab.Fetch[jmpi.vars.table, varKey].val];
IF vi = NIL THEN -- perhaps the appropriate analysis has not been done
BEGIN
ancestors: LIST OF BTH ← GetNearInclusiveAncestorBTHList[bth, jmpi];
cbi: CallableBodyInfo ← GetCBIForBth[jmpi, ancestors.first];
IF cbi # NIL THEN AnalyzeOneCallableBody[cbi];
vi ← NARROW[RefTab.Fetch[jmpi.vars.table, varKey].val];
END;
IF vi # NIL THEN RETURN[GetVarLocFromVarInfo[vi]] ELSE RETURN[NullVarLoc["GetVarLoc: no vi"]];
END;
END;
FindNearBTHAncestorsForPC: PUBLIC PROC[pc: CARD, jmpi: JointMobParsedInfo] RETURNS[LIST OF BTH] =
BEGIN
nest: ObjF.BracketNest ← ObjF.GetBracketNestForPC[jmpi.module, [[0, ""], pc]];
fun: ObjF.FunHandle ← ObjF.GetFunHandleFromNest[nest];
GetAnalyzedCBIForFun: PROC RETURNS[CallableBodyInfo] =
BEGIN
result: CallableBodyInfo ← GetCBIForFun[jmpi, fun];
IF result # NIL AND result.bi = NIL THEN AnalyzeOneCallableBody[result];
RETURN[result];
END;
cbi: CallableBodyInfo ← IF fun = NIL THEN NIL ELSE GetAnalyzedCBIForFun[];
FindTightestRecognizedBP: PROC[subNest: ObjF.BracketNest] RETURNS[BPInfo] =
BEGIN
tentative: BPInfo ← IF subNest.rest = NIL THEN NIL ELSE FindTightestRecognizedBP[subNest.rest];
IF tentative # NIL THEN RETURN[tentative];
tentative ← NARROW[RefTab.Fetch[jmpi.bracketPairs.table, subNest.first].val];
IF tentative # NIL AND tentative.associatedBody # NIL THEN RETURN[tentative] ELSE RETURN[NIL];
END;
tightestRecognizedBP: BPInfo ← FindTightestRecognizedBP[nest];
(maybe I should do FindTightestRecognizedBP[nest.rest.rest??)
tightestRecognizedBth: MA.BTHIF tightestRecognizedBP # NIL THEN tightestRecognizedBP.associatedBody.bth ELSE IF cbi#NIL THEN cbi.bth ELSE NIL;
IF tightestRecognizedBth=NIL THEN RETURN [NIL];
RETURN[GetNearInclusiveAncestorBTHList[tightestRecognizedBth, jmpi]];
END;
FindNearBTHAncestorsForBlock: PUBLIC PROC[block: BTH, jmpi: JointMobParsedInfo] RETURNS[LIST OF BTH] =
BEGIN
parent: BTHMA.GetParentOfBTH[block];
RETURN[GetNearInclusiveAncestorBTHList[parent, jmpi]];
END;
GetRootBTH: PUBLIC PROC[jmpi: JointMobParsedInfo] RETURNS[BTH] =
{RETURN[MA.GetRootBTH[jmpi.mob]]};
GetEntryPCofCallableBTH: PUBLIC PROC[callableBTH: BTH, jmpi: JointMobParsedInfo] RETURNS[CARD] =
BEGIN
cbi: CallableBodyInfo ← GetCBIForBth[jmpi, callableBTH];
fun: ObjF.FunHandle ← IF cbi = NIL THEN NIL ELSE cbi.fun;
funInfo: ObjF.FunInfo ← IF fun = NIL THEN CCE[cirioError] ELSE ObjF.GetFunInfo[fun];
IF funInfo.stab.stabType # Fun THEN CCE[cirioError];
RETURN[funInfo.stab.value];
END;
GetNearInclusiveAncestorBTHList: PROC[tightestBth: BTH, jmpi: JointMobParsedInfo] RETURNS[LIST OF BTH] =
BEGIN
rootBTH: BTHMA.GetRootBTH[jmpi.mob];
list: LIST OF BTHLIST[tightestBth];
DO
IF list.first = rootBTH THEN RETURN[list];
IF RefTab.Fetch[jmpi.callableBodies.table, CreateBTKeyFromBTH[list.first]].val # NIL THEN RETURN[list];
list ← CONS[MA.GetParentOfBTH[list.first], list];
ENDLOOP;
END;
Public procs for special variables
GetLocalFrameExtensionVar: PUBLIC PROC[bth: BTH, jmpi: JointMobParsedInfo] RETURNS[SEH] =
BEGIN
cbi: CallableBodyInfo ← GetCBIForBth[jmpi, bth];
IF cbi = NIL THEN RETURN[NIL];
AnalyzeOneCallableBody[cbi];
IF cbi.frameExtension = NIL THEN RETURN[NIL];
RETURN[cbi.frameExtension.seh];
END;
GetGlobalLinkVar: PUBLIC PROC[bth: BTH, jmpi: JointMobParsedInfo] RETURNS[SEH] =
BEGIN
cbi: CallableBodyInfo ← GetCBIForBth[jmpi, bth];
IF cbi = NIL THEN RETURN[NIL];
AnalyzeOneCallableBody[cbi];
IF cbi.globalLink = NIL THEN RETURN[NIL];
RETURN[cbi.globalLink.seh];
END;
GetStaticLinkVar: PUBLIC PROC[bth: BTH, jmpi: JointMobParsedInfo] RETURNS[SEH] =
BEGIN
cbi: CallableBodyInfo ← GetCBIForBth[jmpi, bth];
IF cbi = NIL THEN RETURN[NIL];
AnalyzeOneCallableBody[cbi];
IF cbi.staticLink = NIL THEN RETURN[NIL];
RETURN[cbi.staticLink.seh];
END;
GetStrandedStaticLinkLoc: PUBLIC PROC[bth: BTH, jmpi: JointMobParsedInfo] RETURNS[MOF.VarLoc] ~ {
cbi: CallableBodyInfo ← GetCBIForBth[jmpi, bth];
IF cbi = NIL THEN RETURN[NIL];
AnalyzeOneCallableBody[cbi];
IF cbi.staticLink=NIL AND cbi.topStabs#NIL AND cbi.topStabs.rest=NIL AND cbi.bi.btr.class=Scope THEN RETURN ObjF.VarLocFromStab[cbi.topStabs.first];
ENABLE scopes are translated into C procedures of one argument, whose codedSei is negative (ie, not from Mimosa), so there's no corresponding SEH.
RETURN[NIL]};
GetCatchPhraseStrandedStaticLinkLoc: PUBLIC PROC[bth: BTH, jmpi: JointMobParsedInfo] RETURNS[MOF.VarLoc] ~ {
cbi: CallableBodyInfo ← GetCBIForBth[jmpi, bth];
IF cbi = NIL THEN RETURN[NIL];
AnalyzeOneCallableBody[cbi];
IF cbi.staticLink=NIL AND cbi.topStabs#NIL AND cbi.bi.btr.class=Catch THEN
BEGIN
count: CARD ← 0;
last: ObjF.Stab ← NIL;
prev: ObjF.Stab;
FOR x: LIST OF ObjF.Stab ← cbi.topStabs, x.rest WHILE x#NIL DO
count ← count + 1;
prev ← last;
last ← x.first;
ENDLOOP;
IF count=5 THEN
RETURN ObjF.VarLocFromStab[prev];
END;
Catch phrases are translated into C procedures of five arguments, the second of which
is a stand-in for the Static Link (a pointer to the frame extension). There is no
corresponding Mesa SEH.
RETURN[NIL]};
GetGlobalFrameVarLoc: PUBLIC PROC[jmpi: JointMobParsedInfo] RETURNS[MOF.VarLoc]
= {RETURN [ObjF.FindGlobalFrameVar[jmpi.module]]};
NullVarLoc: PROC [why: Rope.ROPE ←] RETURNS[MOF.VarLoc] =
{RETURN[NEW[ObjF.VarLocBody ← [0, unknown[why]]]]};
Individual CFunction analysis
jmpi.bodies is a hash table that maps from coded bti to BodyInfo
jmpi.vars is a hash table that maps from coded sei to VarInfo
jmpi.bracketPairs is a hash table that maps from ObjF.BracketPair to BPInfo
These are filled in for one entire C function at a time
Note: for a callableBody, the args and results will be installed in the CallableBodyInfo, while the local vars of the outermost block will be installed in a cbi.bi (a BodyInfo).
That is, there are two records for the same body table entry: a CallableBodyInfo and a BodyInfo.
To get from a pc to an innermost block containing the pc:
call AnalyzeCallableBodySet, if it hasn't been done (should have been done when creating the jmpi)
using ObjF.GetBracketNestForPC, get a bracket nest
using ObjF.GetFunHandleFromNest, get a FunHandle
using GetCBIForFun, obtain a cbi
if cbi.bi = NIL, then call AnalyzeOneCallableBody[cbi]
starting with the tightest bracket pair in the nest, look in jmpi.bracketPairs for an entry
having found an entry, BPInfo.associatedBody.bth is the tightest recognized block containing the PC
BodyInfo: TYPE = REF BodyInfoBody;
BodyInfoBody: TYPE = RECORD[
bth: BTH,
codedBti: CARD,
btr: BTR,
callable: BOOLEAN,
jmpi: JointMobParsedInfo,
parent: REF ANY, -- either BodyInfo or CallableBodyInfo. (It will be CallableBodyInfo exactly when this bti is callable, in which case it will be the CallableBodyInfo for this bti.)
callableAncestor: CallableBodyInfo, -- will be for this bti when this bti is callable.
associatedBps: LIST OF BPInfo, -- will be used for printing, bad situation if none or if more than one
vars: LIST OF VarInfo,
subBodies: LIST OF BodyInfo,
invalid: BOOLEANFALSE,
bodyOccurredTwice: BOOLEANFALSE];
VarInfo: TYPE = REF VarInfoBody;
VarInfoBody: TYPE = RECORD[
seh: SEH,
codedSei: CARD,
idBody: REF id MA.BodySE,
name: Rope.ROPE,
containingBody: REF ANY, -- either BodyInfo or CallableBodyInfo. (It will be CallableBodyInfo exactly when this is an arg or result.)
associatedStabs: LIST OF StabInfo,
correctedByteOffset: INT, -- this field can be removed when we go to a future Sun C compiler that does not make the offset error following multi word parameters. This field is only used for argument vars.
varLoc: MOF.VarLoc,
invalid: BOOLEANFALSE,
varOccuredTwice: BOOLEANFALSE];
StabInfo: TYPE = RECORD[stab: ObjF.Stab, owningBp: ObjF.BracketPair];
BPInfo: TYPE = REF BPInfoBody;
BPInfoBody: TYPE = RECORD[
bp: ObjF.BracketPair,
associatedBody: BodyInfo,
thereAreOtherAssociatedBodies: BOOLEAN -- TRUE if this bp associates with more than one BodyInfo. (A BAD situation.)
];
AnalyzeOneCallableBody: PROC[cbi: CallableBodyInfo] = {
jmpi: JointMobParsedInfo ← cbi.jmpi;
containingBody is either BodyInfo or CallableBodyInfo
Create a VarInfo for the codedSei.
show them to be embedded in containingBody
VisitOneVarSeh: PROC[containingBody: REF ANY, seh: SEH, ser: SER, idBody: REF id MA.BodySE] RETURNS[VarInfo] = {
codedSei: CARDMA.GetCodedSeiFromSEH[seh];
varKey: REF CARDNEW[CARD𡤌odedSei];
vi: VarInfo ← NEW[VarInfoBody←[
seh: seh,
codedSei: codedSei,
idBody: idBody,
name: idBody.hash,
containingBody: containingBody,
associatedStabs: NIL,
correctedByteOffset: 0,
varLoc: NIL]];
IF NOT RefTab.Insert[jmpi.vars.table, varKey, vi] THEN
BEGIN
oldVi: VarInfo ← NARROW[RefTab.Fetch[jmpi.vars.table, varKey].val];
oldVi.varOccuredTwice ← oldVi.invalid ← TRUE;
SystemInterface.ShowReport[IO.PutFR["seh with coded sei = %g occurred more than once", IO.card[codedSei]], $debug];
END;
(other cases probably should be added)
(should I test for multiple instances?)
SELECT idBody.special FROM
globalLink => cbi.globalLink ← vi;
staticLink => cbi.staticLink ← vi;
frameExtension => cbi.frameExtension ← vi;
ENDCASE => NULL;
RETURN[vi]};
containingBody is either BodyInfo or CallableBodyInfo
Create VarInfos for each.
show them to be embedded in containingBody
VisitAllVarSehsInAContext: PROC[containingBody: REF ANY, ctxh: CTXH] RETURNS[LIST OF VarInfo] = {
ctxr: CTXRMA.FetchCTXR[ctxh];
seh: SEHIF ctxr = NIL THEN NIL ELSE ctxr.seList;
results: LIST OF VarInfo ← NIL;
lastResult: LIST OF VarInfo ← NIL;
WHILE seh # NIL DO
ser: SERMA.FetchSER[seh];
WITH ser.body SELECT FROM
idBody: REF id MA.BodySE =>
BEGIN
vi: VarInfo ← VisitOneVarSeh[containingBody, seh, ser, idBody];
viCell: LIST OF VarInfo ← LIST[vi];
IF results = NIL THEN results ← viCell ELSE lastResult.rest ← viCell;
lastResult ← viCell;
seh ← idBody.ctxLink;
END
ENDCASE => ERROR;
ENDLOOP;
RETURN[results]};
VisitAllVarSehsInArgsOrResults: PROC[containingBody: REF ANY, seh: SEH] RETURNS[LIST OF VarInfo] = {
IF seh = NIL THEN RETURN[NIL]
ELSE
{
underSeh: SEH ← UnderTypeSeh[seh];
underSer: SERMA.FetchSER[underSeh];
WITH underSer.body SELECT FROM
cons: REF cons MA.BodySE =>
WITH cons.typeInfo SELECT FROM
ti: REF record MA.TypeInfoConsSE =>
RETURN[VisitAllVarSehsInAContext[containingBody, ti.fieldCtx]];
ENDCASE => CCE[cirioError];
ENDCASE => CCE[cirioError];
};
};
(0) we exit if the analysis has already been done
IF cbi.bi # NIL THEN RETURN;
(1) we begin by creating VarInfos for the args and results
BEGIN
btr: BTRMA.FetchBTR[cbi.bth];
WITH btr.extension SELECT FROM
ext: REF Callable MA.BTRExtension => {
ioTypeSer: SERMA.FetchSER[ext.ioType];
WITH ioTypeSer.body SELECT FROM
cons: REF cons MA.BodySE =>
WITH cons.typeInfo SELECT FROM
transfer: REF transfer MA.TypeInfoConsSE => {
cbi.args ← VisitAllVarSehsInArgsOrResults[cbi, transfer.typeIn];
cbi.results ← VisitAllVarSehsInArgsOrResults[cbi, transfer.typeOut]};
basic: REF basic MA.TypeInfoConsSE => {
I assume that this is an attempt to indicate that there are no arguments or results. This seems to occur for manufactured procedures that contain a block covered by an enable clause.
cbi.args ← NIL;
cbi.results ← NIL};
ENDCASE => CCE[cirioError];
ENDCASE => CCE[cirioError]};
ENDCASE => CCE[cirioError];
END;
(2) we walk the body tree, creating appropriate BodyInfo and VarInfo entries
{
alwaysRecord will be true for the root callable body, false for all visited sub bodies.
VisitOneBody: PROC[parent: BodyInfo, bth: BTH, alwaysRecord: BOOLEAN] RETURNS[BodyInfo] = {
bodyKey: RefTab.Key ← CreateBTKeyFromBTH[bth];
codedBti: CARDNARROW[bodyKey, REF CARD]^;
btr: MA.BTRMA.FetchBTR[bth];
biIsCallable: BOOLEAN
WITH btr.extension SELECT FROM
callable: REF Callable MA.BTRExtension => TRUE,
other: REF Other MA.BTRExtension => FALSE,
ENDCASE => ERROR;
bi: BodyInfo ← NEW[BodyInfoBody←[
bth: bth,
btr: btr,
callable: biIsCallable,
codedBti: codedBti,
jmpi: jmpi,
parent: parent,
callableAncestor: cbi,
associatedBps: NIL,
vars: NIL,
subBodies: NIL]];
IF alwaysRecord OR NOT bi.callable THEN -- examine its details
{
we record this new block
IF NOT RefTab.Insert[jmpi.bodies.table, bodyKey, bi] THEN
{
oldBi: BodyInfo ← NARROW[RefTab.Fetch[jmpi.bodies.table, bodyKey].val];
oldBi.bodyOccurredTwice ← oldBi.invalid ← TRUE;
SystemInterface.ShowReport[IO.PutFR["body with coded bti = %g occured more than once", IO.card[codedBti]], $debug];
};
now we visit the variables of the block
bi.vars ← VisitAllVarSehsInAContext[bi, btr.localCtx];
next, we visit the sub bodies, if any.
{
subBth: BTH ← btr.firstSon;
lastSubBody: LIST OF BodyInfo ← NIL;
WHILE subBth # NIL DO
subBtr: BTRMA.FetchBTR[subBth];
sub: BodyInfo ← VisitOneBody[bi, subBth, FALSE];
subCell: LIST OF BodyInfo ← LIST[sub];
IF bi.subBodies = NIL THEN bi.subBodies ← subCell ELSE lastSubBody.rest ← subCell;
lastSubBody ← subCell;
subBth ← IF subBtr.link.which = parent THEN NIL ELSE subBtr.link.index;
ENDLOOP;
};
};
RETURN[bi];
};
cbi.bi ← VisitOneBody[NIL, cbi.bth, TRUE];
};
(3) we walk the Fun, creating BPInfo entries and constructing associations between BPIs and BodyInfos; because Mimosa erroneously puts all static links in the null Context, this is also where we find the static links for the callable body.
{
the following two vars are used to compute corrected byte offsets for function parameters. The current Sun C compiler miscomputes the offsets for params following multi word arguments. These corrected byte offsets will be recorded in the appropriate VarInfo.
The code that constructs this correction appears at several places in ViewOneStab.
That code assumes that
the PSym stabs will be generated in first to last order among the parameters
the PSym stabs will occur before any modifying stabs (do we really assume this?)
cumByteOffset: CARD ← 0;
somePSymsSeen: BOOLEANFALSE;
ViewSubBracketPair: PROC[bp: ObjF.BracketPair] RETURNS[--stop--BOOLEAN] = {
ViewOneBracketPair[bp, FALSE];
RETURN[FALSE]};
ViewOneBracketPair: PROC[bp: ObjF.BracketPair, top: BOOL] = {
containingBody is either BodyInfo or CallableBodyInfo. (It will be CallableBodyInfo exactly when the var triggering the assocition is an arg or result.) (Because DotOAccessImpl builds different BracketPairs for the a CFunction and for its outermost block, we should never find ourselves attempting to associate a CallableBodyInfo and its corresponding BodyInfo with the same BracketPair.)
Also, we avoid calling this procedure for certain troublesome vars whose locations in the mob and DotO do not correspond; e.g., staticLinks
NoteBpBtiAssociation: PROC[containingBody: REF ANY] = {
WITH containingBody SELECT FROM
bi: BodyInfo => {
info: BPInfo ← NARROW[RefTab.Fetch[bi.jmpi.bracketPairs.table, bp].val];
alreadyRecordedInBi: BOOLEANFALSE; -- tentative
IF info = NIL THEN {
info ← NEW[BPInfoBody←[bp, bi, FALSE]];
IF NOT RefTab.Insert[bi.jmpi.bracketPairs.table, bp, info] THEN CCE[cirioError];
};
IF info.associatedBody # bi THEN {
pcRange: ObjF.PCRange ← ObjF.GetPCRange[bp];
SystemInterface.ShowReport[IO.PutFR["warning: multiple Mesa block associations for the CBlock containing (ContainingDotO) relative PCs: [%g..%g)", IO.card[pcRange.first], IO.card[pcRange.limit]], $debug];
info.thereAreOtherAssociatedBodies ← TRUE;
};
FOR knownBPIs: LIST OF BPInfo ← bi.associatedBps, knownBPIs.rest WHILE knownBPIs # NIL DO
IF knownBPIs.first = info THEN {alreadyRecordedInBi ← TRUE; EXIT};
ENDLOOP;
IF NOT alreadyRecordedInBi THEN {
IF bi.associatedBps # NIL THEN
SystemInterface.ShowReport[IO.PutFR["warning: Mesa block with codedBti = %g has multiple associated CBlocks", IO.card[bi.codedBti]], $debug];
bi.associatedBps ← CONS[info, bi.associatedBps];
};
};
cbi: CallableBodyInfo => NULL; -- we needn't record this association (We shall handle PCs outside the outermost C block of a function by going to the Fun, then the CallableBodyInfo, then the associated cbi.)
ENDCASE => ERROR;
};
ViewOneStab: PROC[stab: ObjF.Stab] RETURNS[--stop-- BOOLEAN] = {
IF stab.stabType = LSym OR stab.stabType = PSym OR stab.stabType = RSym THEN {
varKey: RefTab.Key ← CreateVarKeyFromVarStab[stab];
codedSei: CARDIF varKey = NIL THEN 0 ELSE NARROW[varKey, REF CARD]^;
vi: VarInfo ← IF varKey = NIL THEN NIL ELSE NARROW[RefTab.Fetch[jmpi.vars.table, varKey].val];
IF stab.stabType = PSym AND NOT somePSymsSeen THEN
{ -- for correcting param byte offsets due to errors in C compiler
somePSymsSeen ← TRUE;
cumByteOffset ← StabValueAsInt[stab];
};
IF varKey#NIL AND vi=NIL AND (cbi.staticLink=NIL OR cbi.globalLink=NIL OR cbi.frameExtension=NIL) THEN {
seh: SEHMA.MakeSEHFromCodedSei[jmpi.mob, codedSei, NIL];
IF seh#NIL AND MA.IsLost[seh] THEN {
ser: SERMA.FetchSER[seh];
WITH MA.FetchSER[seh].body SELECT FROM
idser: REF id MA.BodySE => IF (SELECT idser.special FROM staticLink => cbi.staticLink=NIL, globalLink => cbi.globalLink=NIL, frameExtension => cbi.frameExtension=NIL, ENDCASE => FALSE) THEN {
[] ← VisitOneVarSeh[cbi, seh, ser, idser];
vi ← NARROW[RefTab.Fetch[jmpi.vars.table, varKey].val]};
ENDCASE => NULL;
};
};
IF vi # NIL THEN {
vi.associatedStabs ← CONS[[stab, bp], vi.associatedStabs];
IF vi.containingBody= NIL THEN ERROR;
IF vi.idBody.special # staticLink THEN
NoteBpBtiAssociation[vi.containingBody];
We do not record the staticLinks because
in the mob they appear in the outer block of a procedure
in the DotO they appear among the params
Thus, they would confuse the mapping.
IF stab.stabType = PSym THEN vi.correctedByteOffset ← cumByteOffset;
for correcting param byte offsets due to errors in C compiler
}
ELSE cbi.unMatchedStabs ← CONS[stab, cbi.unMatchedStabs];
IF top THEN cbi.topStabs ← CONS[stab, cbi.topStabs];
IF stab.stabType = PSym THEN cumByteOffset ← cumByteOffset + 4;
For correcting param byte offsets due to errors in C compiler.
We assume that each fun param occupies 4 bytes or 1 word.
};
RETURN[FALSE]};
ObjF.GenSymbolStabs[bp, ViewOneStab];
ObjF.GenSubBracketPairs[bp, ViewSubBracketPair];
RETURN};
fun: ObjF.FunHandle ← cbi.fun;
brackets: ObjF.BracketPair ← ObjF.GetFunBrackets[fun];
[] ← ViewOneBracketPair[brackets, TRUE];
};
};
this is adapted from RMTWAtomics.UnderTypeSEH. I hope this version works here.
UnderTypeSeh: PROC[seh: SEH] RETURNS[SEH] =
BEGIN
ser: MA.SERMA.FetchSER[seh];
WITH ser.body SELECT FROM
id: REF id MA.BodySE =>
BEGIN
we simply spin deeper into the Mob type structure
WITH id.idInfoAndValue SELECT FROM
idInfo: REF MA.TypeDesc =>
RETURN[UnderTypeSeh[idInfo.seh]];
ENDCASE => ERROR;
END;
cons: REF cons MA.BodySE => RETURN[seh];
ENDCASE => ERROR;
END;
Not useful until after completion of the appropriate call on AnalyzeOneCallableBody. (Specifically, not until after all calls on ViewOneStab for stabs related to this VarInfo.)
(Are there other cases of embedded fields? e.g., return values? why don't I handle them?)
We construct the varLoc if we have not already done so.
Note: Temporarily, we compute during pass (3) of AnalyzeOneCallableBody a correction to the byte offsets of C function parameters. This is to compensate for a bug in the current C compiler. This correction is used below.
GetVarLocFromVarInfo: PROC[vi: VarInfo] RETURNS[MOF.VarLoc] = {
IF vi.varLoc = NIL THEN {-- we have better build one
mobFD: REF MA.FieldDesc ← NIL; -- tentative
WITH vi.idBody.idInfoAndValue SELECT FROM
fd: REF MA.FieldDesc => mobFD ← fd;
ENDCASE => NULL;
IF mobFD = NIL THEN -- unexpected situation, seh is not a var?
vi.varLoc ← NullVarLoc["mobFD = NIL"]
ELSE IF vi.idBody.flags.valid AND vi.idBody.flags.upLevel THEN -- known to be in frame extension
vi.varLoc ← NEW[ObjF.VarLocBody←[bitSize: mobFD.bitSize, where: frameExtension[bitOffset: mobFD.bitOffset]]]
ELSE IF vi.associatedStabs # NIL THEN {-- it is represented as a C var
FOR stabs: LIST OF StabInfo ← vi.associatedStabs, stabs.rest WHILE stabs # NIL DO
stab: ObjF.Stab ← stabs.first.stab;
IF vi.varLoc # NIL THEN {-- we have seeen at least one previous stab
(We assume that the stabs are ordered as they appear in the dotO, hence the PSym will occur before any modifying RSym.)
baseCase: ObjF.VarLocCase ~ WITH vi.varLoc SELECT FROM
x: ObjF.IndirectVarLoc => x.base.case,
ENDCASE => vi.varLoc.case;
SELECT TRUE FROM
baseCase = register => NULL; -- let it stand
baseCase # register AND stab.stabType = RSym => {-- overwrite it
bitSize: CARD16 ← BitsForBytes[stab.size];
newVarLoc: ObjF.VarLoc;
IF bitSize > 32
THEN newVarLoc ← NEW[ObjF.VarLocBody ← [
bitSize: bitSize,
where: indirect[
base: NEW[ObjF.VarLocBody ← [
bitSize: CirioTypes.bitsPerPtr,
where: register[regNum: stab.value]]],
offset: CirioTypes.zeroBA]]]
ELSE newVarLoc ← NEW[ObjF.VarLocBody ← [
bitSize: bitSize,
where: register[regNum: stab.value]]];
vi.varLoc ← newVarLoc;
};
ENDCASE => CCE[cirioError] -- I dont think this is supposed to happen
}
ELSE { -- we have seen no previouis stabs
SELECT TRUE FROM
stab.stabType = RSym => {
bitSize: CARD16 ← BitsForBytes[stab.size];
IF bitSize>32
THEN vi.varLoc ← NEW[ObjF.VarLocBody ← [
bitSize: bitSize,
where: indirect[
base: NEW[ObjF.VarLocBody ← [
bitSize: CirioTypes.bitsPerPtr,
where: register[regNum: stab.value]]],
offset: CirioTypes.zeroBA]]]
ELSE vi.varLoc ← NEW[ObjF.VarLocBody ← [
bitSize: bitSize,
where: register[regNum: stab.value]]];
};
Note: when we get a corrected C compiler, then this case can again be combined with ObjF.LSym, replacing vi.correctedByteOffset with StabValueAsInt[stab].
stab.stabType = PSym => {
bitSize: CARD16 ← BitsForBytes[stab.size];
IF bitSize>32
THEN vi.varLoc ← NEW[ObjF.VarLocBody ← [
bitSize: bitSize,
where: indirect[
base: NEW[ObjF.VarLocBody ← [
bitSize: CirioTypes.bitsPerPtr,
where: frame[bitOffset: BitsForBytes[vi.correctedByteOffset]]]],
offset: CirioTypes.zeroBA]]]
ELSE vi.varLoc ← NEW[ObjF.VarLocBody ← [
bitSize: bitSize,
where: frame[bitOffset: BitsForBytes[vi.correctedByteOffset]]]];
};
stab.stabType = LSym => {
bitSize: CARD16 ← BitsForBytes[stab.size];
vi.varLoc ← NEW[ObjF.VarLocBody ← [
bitSize: bitSize,
where: frame[bitOffset: BitsForBytes[StabValueAsInt[stab]]]]];
};
ENDCASE => NULL; -- ignore it
};
ENDLOOP;
IF vi.varLoc = NIL THEN -- nothing filled it in
vi.varLoc ← NullVarLoc["not in any associated stab"];
}
ELSE -- hmm... perhaps the mob forgot to mark it as uplevel (this issue should go away eventually) So, we use exactly the same code as the above uplevel case.
vi.varLoc ← NEW[ObjF.VarLocBody←[bitSize: mobFD.bitSize, where: frameExtension[bitOffset: mobFD.bitOffset]]];
IF vi.varLoc.bitSize#0 AND mobFD#NIL AND vi.varLoc.bitSize#mobFD.bitSize THEN CCE[cirioError, IO.PutFR["mob and .o disagree on bit size of \"%q\" (codedSei=%g=%xH)", [rope[vi.name]], [cardinal[vi.codedSei]], [cardinal[vi.codedSei]] ]];
note: gnu symbols seem to include register symbols with bitsize = 0. So, we only check for consistency when bitSize (in the dotO) is non-zero.
};
RETURN[vi.varLoc]};
BitsForBytes: PROC[bytes: INT] RETURNS[INT] =
{RETURN[PBasics.bitsPerByte*bytes]};
StabValueAsInt: PROC[stab: ObjF.Stab] RETURNS[INT] = TRUSTED
{RETURN[LOOPHOLE[stab.value, INT]]};
FullShowOneAnalyzedCBI: PROC[cbi: CallableBodyInfo, depth: CARD, on: IO.STREAM] =
BEGIN
Tab: PROC[added: CARD] = {FOR I: CARD IN [0..depth+added) DO IO.PutF[on, " "] ENDLOOP};
Tab[0]; IO.PutF[on, "arguments\N"];
FOR vis: LIST OF VarInfo ← cbi.args, vis.rest WHILE vis # NIL DO
ShowOneVI[vis.first, depth+5, on];
ENDLOOP;
IO.PutF[on, "\N"];
Tab[0]; IO.PutF[on, "results\N"];
FOR vis: LIST OF VarInfo ← cbi.results, vis.rest WHILE vis # NIL DO
ShowOneVI[vis.first, depth+5, on];
ENDLOOP;
IO.PutF[on, "\N"];
IF cbi.globalLink = NIL THEN
{Tab[0]; IO.PutF[on, "(no global link var)\N"]}
ELSE
{Tab[0]; IO.PutF[on, "global link var coded sei = %g\N", IO.card[cbi.globalLink.codedSei]]};
IF cbi.staticLink = NIL THEN
{Tab[0]; IO.PutF[on, "(no static link var)\N"]}
ELSE
{Tab[0]; IO.PutF[on, "static link var coded sei = %g\N", IO.card[cbi.staticLink.codedSei]]};
IF cbi.frameExtension = NIL THEN
{Tab[0]; IO.PutF[on, "(no frame extension var)\N"]}
ELSE
{Tab[0]; IO.PutF[on, "frame extension var coded sei = %g\N", IO.card[cbi.frameExtension.codedSei]]};
FullShowOneBI[cbi.bi, depth, on, TRUE];
IF cbi.unMatchedStabs # NIL THEN
BEGIN
Tab[0]; IO.PutF[on, "unmatched stabs\N"];
ShowStabList[on, depth+5, cbi.unMatchedStabs];
IO.PutF[on, "\N"];
END;
IF cbi.topStabs # NIL THEN
BEGIN
Tab[0]; IO.PutF[on, "top-level stabs\N"];
ShowStabList[on, depth+5, cbi.topStabs];
IO.PutF[on, "\N"];
END;
END;
always show details will be true for the root callable body of a tree
FullShowOneBI: PROC[bi: BodyInfo, depth: CARD, on: IO.STREAM, alwaysShowDetails: BOOLEAN] =
BEGIN
Tab: PROC[added: CARD] = {FOR I: CARD IN [0..depth+added) DO IO.PutF[on, " "] ENDLOOP};
Tab[0]; IO.PutF[on, "body for coded bti = %g\N", IO.card[bi.codedBti]];
IF bi.invalid THEN
BEGIN
Tab[0]; IO.PutF[on, "invalid"];
IF bi.bodyOccurredTwice THEN IO.PutF[on, " bodyOccurredTwice"];
IO.PutF[on, "\N"];
END;
Tab[5];
WITH bi.btr.extension SELECT FROM
c: REF Callable MA.BTRExtension =>
BEGIN
idSer: SERMA.FetchSER[c.id];
IF idSer # NIL THEN WITH idSer.body SELECT FROM
id: REF id MA.BodySE => IO.PutF[on, "name: %g", IO.rope[id.hash]];
cons: REF cons MA.BodySE => IO.PutF[on, "(c.id is not an id)"];
ENDCASE => IO.PutF[on, "(c.id is impossible)"]
ELSE
IO.PutF[on, "(c.id is NIL)"];
IO.PutF[on, ", bodyKind: %g, ", IO.rope[SELECT c.kind FROM
Outer => "Outer",
Inner => "Inner",
Catch => "Catch",
Other => "Other",
ENDCASE => "impossible"]];
END;
o: REF Other MA.BTRExtension => NULL;
ENDCASE => ERROR;
IO.PutF[on, "level: %g", IO.card[bi.btr.level]];
IO.PutF[on, "\N"];
Tab[5];
SELECT bi.btr.link.which FROM
sibling => IO.PutF[on, "sibling: %g", IO.card[MA.GetCodedBTIFromBTH[bi.btr.link.index]]];
parent => IO.PutF[on, "parent: %g", IO.card[MA.GetCodedBTIFromBTH[bi.btr.link.index]]];
ENDCASE => ERROR;
IO.PutF[on, "\N"];
IF bi.associatedBps = NIL AND (alwaysShowDetails OR NOT bi.callable) THEN
{Tab[5]; IO.PutF[on, "WARNING: no associated CBlocks\N"]}
ELSE IF bi.associatedBps # NIL AND bi.associatedBps.rest # NIL THEN
{Tab[5]; IO.PutF[on, "WARNING: multiple associated CBlocks\N"]};
IF bi.associatedBps # NIL THEN
BEGIN
Tab[5]; IO.PutF[on, "associated CBlock PC ranges\N"];
FOR knownBPIs: LIST OF BPInfo ← bi.associatedBps, knownBPIs.rest WHILE knownBPIs # NIL DO
pcRange: ObjF.PCRange ← ObjF.GetPCRange[knownBPIs.first.bp];
Tab[7];
IO.PutF[on, "[%g..%g)\N", IO.card[pcRange.first], IO.card[pcRange.limit]];
ENDLOOP;
END;
IF alwaysShowDetails OR NOT bi.callable THEN
BEGIN
Tab[5]; IO.PutF[on, "BEGIN\N"];
FOR vis: LIST OF VarInfo ← bi.vars, vis.rest WHILE vis # NIL DO
ShowOneVI[vis.first, depth+5, on];
ENDLOOP;
FOR bis: LIST OF BodyInfo ← bi.subBodies, bis.rest WHILE bis # NIL DO
IO.PutF[on, "\N"];
FullShowOneBI[bis.first, depth+5, on, FALSE];
ENDLOOP;
Tab[5]; IO.PutF[on, "END\N"];
END;
END;
ShowOneVI: PROC[vi: VarInfo, depth: CARD, on: IO.STREAM] =
BEGIN
Tab: PROC[added: CARD] = {FOR I: CARD IN [0..depth+added) DO IO.PutF[on, " "] ENDLOOP};
Tab[0]; IO.PutF[on, "var for codedSei = %g\N", IO.card[vi.codedSei]];
Tab[2]; IO.PutF[on, "mob reports:\N"];
IF Rope.Length[vi.name] = 0 THEN
{Tab[4]; IO.PutF[on, "(unnamed)\N"]}
ELSE {Tab[4]; IO.PutF[on, "name = %g\N", IO.rope[vi.name]]};
IF vi.idBody.extended THEN {Tab[4]; IO.PutF[on, "extended\N"]};
IF vi.idBody.public THEN {Tab[4]; IO.PutF[on, "public\N"]};
IF vi.idBody.immutable THEN {Tab[4]; IO.PutF[on, "immutable\N"]};
IF vi.idBody.constant THEN {Tab[4]; IO.PutF[on, "constant\N"]};
IF vi.idBody.linkSpace THEN {Tab[4]; IO.PutF[on, "linkSpace\N"]};
IF vi.idBody.flags.valid THEN
BEGIN
IF vi.idBody.flags.used THEN {Tab[4]; IO.PutF[on, "used\N"]};
IF vi.idBody.flags.addressed THEN {Tab[4]; IO.PutF[on, "addressed\N"]};
IF vi.idBody.flags.assigned THEN {Tab[4]; IO.PutF[on, "assigned\N"]};
IF vi.idBody.flags.upLevel THEN {Tab[4]; IO.PutF[on, "upLevel\N"]};
IF vi.idBody.flags.sized THEN {Tab[4]; IO.PutF[on, "sized\N"]};
IF vi.idBody.flags.spare1 THEN {Tab[4]; IO.PutF[on, "spare1\N"]};
IF vi.idBody.flags.spare2 THEN {Tab[4]; IO.PutF[on, "spare2\N"]};
END;
IF vi.idBody.special # normal THEN
BEGIN
text: Rope.ROPESELECT vi.idBody.special FROM
globalLink => "globalLink",
staticLink => "staticLink",
frameExtension => "frameExtension",
memoryLink => "memoryLink",
returnLink => "returnLink",
argLink => "argLink",
returnVar => "returnVar",
argVar => "argVar",
globalVar => "globalVar",
extensionVar=> "extensionVar",
invalid => "invalid",
ENDCASE => IO.PutFR["specialKind = %g", IO.card[ORD[vi.idBody.special]]];
Tab[4]; IO.PutF[on, "%g\N", IO.rope[text]];
END;
WITH vi.idBody.idInfoAndValue SELECT FROM
fd: REF MA.FieldDesc =>
{Tab[4]; IO.PutF[on, "(bitSize: %g, bitOffset: %g)\N", IO.card[fd.bitSize], IO.int[fd.bitOffset]]};
td: REF MA.TypeDesc =>
{Tab[4]; IO.PutF[on, "(TypeDesc)\N"]};
bd: REF MA.BlockDesc =>
{Tab[4]; IO.PutF[on, "(BlockDesc)\N"]};
cv: REF MA.ConstVal =>
{Tab[4]; IO.PutF[on, "cv = %g\N", IO.card[cv.value]]};
ENDCASE =>
{Tab[4]; IO.PutF[on, "(unexpected idInfoAndValue)\N"]};
Tab[2]; IO.PutF[on, "correctedByteOffset: %g\N", IO.int[vi.correctedByteOffset]];
[] ← GetVarLocFromVarInfo[vi];
IF vi.varLoc = NIL THEN
{Tab[2]; IO.PutF[on, "(no varLoc)\N"]}
ELSE
BEGIN
Tab[2]; IO.PutF[on, "varLoc[bitSize: %g, ", IO.card[vi.varLoc.bitSize]];
WITH vi.varLoc SELECT FROM
reg: REF register MOF.VarLocBody =>
IO.PutF[on, "register[regNum: %g]]]\N", IO.card[reg.regNum]];
frm: REF frame MOF.VarLocBody =>
IO.PutF[on, "frame[bitOffset: %g]]]\N", IO.int[frm.bitOffset]];
frmExt: REF frameExtension MOF.VarLocBody =>
IO.PutF[on, "frameExtension[bitOffset: %g]]]\N", IO.int[frmExt.bitOffset]];
seg: REF fSegment MOF.VarLocBody =>
BEGIN
kindRope: Rope.ROPE ← seg.fSeg.fSegName;
kindRope: Rope.ROPESELECT seg.kind FROM
patch => "patch",
text => "text",
data => "data",
bss => "bss",
common => "common",
ENDCASE => "ILLEGAL";
IO.PutF[on, "segment[kind: %g, bitOffset: %g]]]\N", IO.rope[kindRope], IO.int[seg.bitOffset]];
END;
unk: REF unknown MOF.VarLocBody =>
IO.PutF[on, "unknown[]]]\N"];
ENDCASE =>
IO.PutF[on, "ILLEGAL]]\N"];
END;
IF vi.associatedStabs # NIL THEN
BEGIN
Tab[2];
IO.PutF[on, "stabs\N"];
ShowStabInfoList[on, depth+4, vi.associatedStabs];
END;
IO.PutF[on, "\N"];
END;
ShowStabList: PROC[on: IO.STREAM, depth: CARD, stabs: LIST OF ObjF.Stab] =
BEGIN
Tab: PROC[added: CARD] = {FOR I: CARD IN [0..depth+added) DO IO.PutF[on, " "] ENDLOOP};
FOR ss: LIST OF ObjF.Stab ← stabs, ss.rest WHILE ss # NIL DO
stab: ObjF.Stab ← ss.first;
stabRope: Rope.ROPE ← stab.rope;
fieldsRope: Rope.ROPE ← RopeForStabFields[stab];
Tab[0]; IO.PutF[on, "%g\N", IO.rope[fieldsRope]];
Tab[2]; IO.PutF[on, "%g\N", IO.rope[stabRope]];
ENDLOOP;
END;
ShowStabInfoList: PROC[on: IO.STREAM, depth: CARD, list: LIST OF StabInfo] =
BEGIN
Tab: PROC[added: CARD] = {FOR I: CARD IN [0..depth+added) DO IO.PutF[on, " "] ENDLOOP};
FOR ss: LIST OF StabInfo ← list, ss.rest WHILE ss # NIL DO
stabInfo: StabInfo ← ss.first;
stabRope: Rope.ROPE ← stabInfo.stab.rope;
fieldsRope: Rope.ROPE ← RopeForStabFields[stabInfo.stab];
pcRange: ObjF.PCRange ← ObjF.GetPCRange[stabInfo.owningBp];
Tab[0]; IO.PutF[on, "stab: %g\N", IO.rope[fieldsRope]];
Tab[2]; IO.PutF[on, "OwningBPI-PCRange: [%g..%g)\N", IO.card[pcRange.first], IO.card[pcRange.limit]];
Tab[2]; IO.PutF[on, "%g\N", IO.rope[stabRope]];
ENDLOOP;
END;
RopeForStabFields: PROC[stab: ObjF.Stab] RETURNS[Rope.ROPE] =
BEGIN
fields: Rope.ROPENIL;
fields ← Rope.Concat[fields, IO.PutFR["stabX: %g, ", IO.card[stab.stabX]]];
fields ← Rope.Concat[fields, IO.PutFR["stabType: %g, ", IO.rope[RopeForStabType[stab.stabType]]]];
fields ← Rope.Concat[fields, IO.PutFR["size: %g, ", IO.card[stab.size]]];
fields ← Rope.Concat[fields, IO.PutFR["value: %g, ", IO.card[stab.value]]];
RETURN[fields];
END;
RopeForStabType: PROC[stabType: ObjF.StabType] RETURNS[Rope.ROPE] =
BEGIN
RETURN[SELECT stabType FROM
LBrac => "LBrac",
RBrac => "RBrac",
SLine => "SLine",
Fun => "Fun",
PSym => "PSym",
LSym => "LSym",
RSym => "RSym",
ENDCASE => ""];
END;
Callable Bodies analysis
jmpi.callableBodies is a hash table that maps from coded bti to CallableBodyInfo
CallableBodyInfo: TYPE = REF CallableBodyInfoBody;
CallableBodyInfoBody: TYPE = RECORD[
bth: BTH,
codedBti: CARD,
jmpi: JointMobParsedInfo,
fun: ObjF.FunHandle,
bi: BodyInfo ← NIL, -- will be filled in when needed
args: LIST OF VarInfo ← NIL, -- filled in when bi is filled in
results: LIST OF VarInfo ← NIL, -- filled in when bi is filled in
globalLink: VarInfo ← NIL, -- filled in when bi is filled in
staticLink: VarInfo ← NIL, -- filled in when bi is filled in
frameExtension: VarInfo ← NIL, -- filled in when bi is filled in
unMatchedStabs, topStabs: LIST OF ObjF.Stab ← NIL,
invalid: BOOLEANFALSE,
multiFuns: BOOLEANFALSE,
bodyOccurredTwice: BOOLEANFALSE];
AnalyzeCallableBodySet: PROC[jmpi: JointMobParsedInfo] =
BEGIN
first, we walk the body tree in the mob, creating an entry for each callable body
BEGIN
SeeOneBody: PROC[callableBody: BTH] =
BEGIN
key: RefTab.Key ← CreateBTKeyFromBTH[callableBody];
codedBti: CARDNARROW[key, REF CARD]^;
cbi: CallableBodyInfo ← NEW[CallableBodyInfoBody←[callableBody, codedBti, jmpi, NIL]];
IF NOT RefTab.Insert[jmpi.callableBodies.table, key, cbi] THEN
BEGIN
oldCbi: CallableBodyInfo ← NARROW[RefTab.Fetch[jmpi.callableBodies.table, key].val];
oldCbi.bodyOccurredTwice ← oldCbi.invalid ← TRUE;
SystemInterface.ShowReport[IO.PutFR["callable body with coded bti = %g occured more than once", IO.card[codedBti]], $debug];
END;
END;
MA.GenCallableBodies[jmpi.mob, SeeOneBody];
END;
second, we walk the Funs in the DotO, updating the appropriate entry (if any) in the hash table
BEGIN
SeeOneFun: PROC[fun: ObjF.FunHandle] RETURNS[--stop-- BOOLEAN] =
BEGIN
cbi: CallableBodyInfo ← GetCBIForFun[jmpi, fun];
IF cbi # NIL THEN
BEGIN
IF cbi.fun = NIL THEN cbi.fun ← fun ELSE
BEGIN
cbi.multiFuns ← cbi.invalid ← TRUE;
SystemInterface.ShowReport[IO.PutFR["callable body with coded bti = %g has more than one corresponding fun", IO.card[cbi.codedBti]], $debug];
END;
END;
RETURN[FALSE]
END;
ObjF.GenFuns[jmpi.module, SeeOneFun];
END;
END;
returns NIL if not a callable body
not valid until first part of AnalyzeCallableBodySet has been completed
GetCBIForBth: PROC[jmpi: JointMobParsedInfo, bth: BTH] RETURNS[CallableBodyInfo] =
BEGIN
key: RefTab.Key ← CreateBTKeyFromBTH[bth];
RETURN[NARROW[RefTab.Fetch[jmpi.callableBodies.table, key].val]];
END;
returns NIL if no corresponding callable body
not valid until AnalyzeCallableBodySet has been completed
GetCBIForFun: PROC[jmpi: JointMobParsedInfo, fun: ObjF.FunHandle] RETURNS[CallableBodyInfo] =
BEGIN
key: RefTab.Key ← CreateBTKeyFromFun[fun];
IF key # NIL THEN RETURN[NARROW[RefTab.Fetch[jmpi.callableBodies.table, key].val]]
ELSE RETURN[NIL];
END;
GetBiFromCbi: PROC[cbi: CallableBodyInfo] RETURNS[bi: BodyInfo] =
BEGIN
IF cbi.bi = NIL THEN AnalyzeOneCallableBody[cbi];
RETURN[cbi.bi];
END;
ShowOneCallableBody: PROC[jmpi: JointMobParsedInfo, callableBody: BTH, on: IO.STREAM] =
BEGIN
cbi: CallableBodyInfo ← GetCBIForBth[jmpi, callableBody];
IF cbi # NIL THEN
BEGIN
IO.PutF[on, "\Nfor codedBti = %g we have:\N", IO.card[cbi.codedBti]];
IF cbi.fun = NIL THEN IO.PutF[on, "\Tno Fun\N"]
ELSE
BEGIN
info: ObjF.FunInfo ← ObjF.GetFunInfo[cbi.fun];
IO.PutF[on, "\T Fun CName = %g\N", IO.rope[info.cName]];
END;
IF cbi.invalid THEN
BEGIN
IO.PutF[on, "\Tinvalid = TRUE"];
IF cbi.multiFuns THEN IO.PutF[on, "\TmultiFuns = TRUE"];
IF cbi.bodyOccurredTwice THEN IO.PutF[on, "\TbodyOccuredTwice = TRUE"];
END;
END
ELSE
BEGIN
key: RefTab.Key ← CreateBTKeyFromBTH[callableBody];
codedBti: CARDNARROW[key, REF CARD]^;
IO.PutF[on, "\Nfound NO cbi for coded bti = %g\N", IO.card[codedBti]];
END;
END;
ShowCallableBodiesInfo: PROC[jmpi: JointMobParsedInfo, on: IO.STREAM] =
BEGIN
badFunHeaderPrinted: BOOLEANFALSE; -- initial value
ShowOneBody: PROC[callableBody: BTH] =
{ShowOneCallableBody[jmpi, callableBody, on]};
CheckOneFun: PROC[fun: ObjF.FunHandle] RETURNS[--stop-- BOOLEAN] =
BEGIN
cbi: CallableBodyInfo ← GetCBIForFun[jmpi, fun];
IF cbi = NIL THEN
BEGIN
info: ObjF.FunInfo ← ObjF.GetFunInfo[fun];
IF NOT badFunHeaderPrinted THEN
BEGIN
badFunHeaderPrinted ← TRUE;
IO.PutF[on, "Following Funs have no cbi:\N"];
END;
IO.PutF[on, "\T%g\N", IO.rope[info.cName]];
END;
RETURN[FALSE]
END;
MA.GenCallableBodies[jmpi.mob, ShowOneBody];
ObjF.GenFuns[jmpi.module, CheckOneFun];
IF NOT badFunHeaderPrinted THEN
IO.PutF[on, "All Funs have corresponding cbis. This is unexpected.\N"];
END;
body table hash table
bodies will be indexed by their coded body table index
CreateBTHashTable: PROC RETURNS[BTHashTable] =
{RETURN[NEW[BTHashTableBody ← [RefTab.Create[equal: BTKeyEqualProc, hash: BTKeyHashProc]]]]};
CreateBTKeyFromCodedBTI: PROC[codedBti: CARD] RETURNS[RefTab.Key] =
BEGIN
RETURN[NEW[CARD ← codedBti]];
END;
returns NIL if fun name improperly formed
CreateBTKeyFromFun: PROC[fun: ObjF.FunHandle]RETURNS[RefTab.Key] =
BEGIN
info: ObjF.FunInfo ← ObjF.GetFunInfo[fun];
funStab: ObjF.Stab ← info.stab;
nameAndNumber: ObjF.NameAndNumber ← ObjF.ParseNameRope[funStab];
IF nameAndNumber.valid AND nameAndNumber.char # 'L AND nameAndNumber.number >= 0 THEN
BEGIN
codedBti: CARD ← nameAndNumber.number;
key: RefTab.Key ← NEW[CARD ← codedBti];
RETURN[key];
END
ELSE RETURN[NIL]
END;
CreateBTKeyFromBTH: PROC[bth: BTH] RETURNS[RefTab.Key] =
BEGIN
codedBti: CARDMA.GetCodedBTIFromBTH[bth];
RETURN[NEW[CARD ← codedBti]];
END;
BTKeyEqualProc: PROC[key1, key2: RefTab.Key] RETURNS [BOOL] =
BEGIN
codedBti1Ref: REF CARDNARROW[key1];
codedBti2Ref: REF CARDNARROW[key2];
RETURN[codedBti1Ref^ = codedBti2Ref^];
END;
BTKeyHashProc: PROC[key: RefTab.Key] RETURNS [CARDINAL] =
BEGIN
codedBtiRef: REF CARDNARROW[key];
asLongNumber: Basics.LongNumber ← LOOPHOLE[codedBtiRef^];
RETURN[Basics.BITXOR[asLongNumber.lo, asLongNumber.hi]];
END;
var hash table
vars will be indexed by their coded sei
CreateVarHashTable: PROC RETURNS[VarHashTable] =
{RETURN[NEW[VarHashTableBody ← [RefTab.Create[equal: VarKeyEqualProc, hash: VarKeyHashProc]]]]};
CreateVarKeyFromCodedSEI: PROC[codedSei: CARD] RETURNS[RefTab.Key] =
BEGIN
RETURN[NEW[CARD ← codedSei]];
END;
returns NIl if no apparent codedSei
CreateVarKeyFromVarStab: PROC[stab: ObjF.Stab]RETURNS[RefTab.Key] =
BEGIN
nameAndNumber: ObjF.NameAndNumber ← ObjF.ParseNameRope[stab];
December 10, 1989, MJS thinks, after experimenting and reading C2CNamingDoc.tioga of December 11, 1989, that char = 'v means means variables declared in the mesa source, and char = 'c or 'w means generated names for links, extensions, and so on. The number will either be a valid ISei or negative.
IF nameAndNumber.valid AND (nameAndNumber.char = 'v OR nameAndNumber.char = 'c) AND nameAndNumber.number > 0 THEN
BEGIN
codedSei: CARD ← nameAndNumber.number;
key: RefTab.Key ← NEW[CARD ← codedSei];
RETURN[key];
END
ELSE RETURN[NIL]
END;
CreateVarKeyFromSEH: PROC[seh: SEH] RETURNS[RefTab.Key] =
BEGIN
codedSei: CARDMA.GetCodedSeiFromSEH[seh];
RETURN[NEW[CARD ← codedSei]];
END;
VarKeyEqualProc: PROC[key1, key2: RefTab.Key] RETURNS [BOOL] =
BEGIN
codedSei1Ref: REF CARDNARROW[key1];
codedSei2Ref: REF CARDNARROW[key2];
RETURN[codedSei1Ref^ = codedSei2Ref^];
END;
VarKeyHashProc: PROC[key: RefTab.Key] RETURNS [CARDINAL] =
BEGIN
codedSeiRef: REF CARDNARROW[key];
asLongNumber: Basics.LongNumber ← LOOPHOLE[codedSeiRef^];
RETURN[Basics.BITXOR[asLongNumber.lo, asLongNumber.hi]];
END;
BPI hash table
BracketPairs will be indexed by their REF. Thus, we assume that DotOAccess provides unique REFs. (This is sufficient, because we shall never wish to look up BPInfo by some more specific key.)
CreateBpHashTable: PROC RETURNS[BPHashTable] =
{RETURN[NEW[BPHashTableBody ← [RefTab.Create[]]]]};
debugging software
PerformJMDITest: PUBLIC PROC[what: Rope.ROPE, jmpi: JointMobParsedInfo, out: IO.STREAM] =
BEGIN
ENABLE ShowOff => {IO.Put[out, IO.rope[msg]]; RESUME};
SELECT TRUE FROM
Rope.Equal[what, "CallableBodies"] => ShowAllCallableBodies[jmpi];
ENDCASE => CCE[cirioError];
END;
ShowAllCallableBodies: PROC[jmpi: JointMobParsedInfo] =
BEGIN
showOne: PROC[callableBody: BTH] =
BEGIN
btr: MA.BTRMA.FetchBTR[callableBody];
ShowOff[IO.PutFR["\Tbti = %g, at source pos %g\N", IO.card[MA.GetCodedBTIFromBTH[callableBody]], IO.int[btr.sourceIndex]]];
END;
ShowOff[IO.PutFR["\N\Nall Callable Bodies\N"]];
MA.GenCallableBodies[jmpi.mob, showOne];
ShowOff[IO.PutFR["\N"]];
END;
tries to generate one PC for each bracket. That is, a pc within the bounds of the bracket pair, but not within the bounds of any included bracket pair. (It is possible that some bracket pairs are completely covered by their interior brackets.)
GenInterestingPCs: PUBLIC PROC[module: ObjF.Module, for: PROC[bp: ObjF.BracketPair, pc: CARD]] =
BEGIN
lowestFunPC: CARDLAST[CARD];
SeeOneFunBracket: PROC[bp: ObjF.BracketPair] RETURNS[--stop-- BOOLEAN] =
BEGIN
pcRange: ObjF.PCRange ← ObjF.GetPCRange[bp];
IF pcRange.first < lowestFunPC THEN lowestFunPC ← pcRange.first;
GenInterestingPCsWithinABracket[bp, for];
RETURN[FALSE];
END;
ObjF.GenFunBracketPairs[module, SeeOneFunBracket];
IF lowestFunPC > 0 THEN for[NIL, lowestFunPC-1];
END;
The bracket param to for is the tightest bracket containing the pc.
GenInterestingPCsWithinABracket: PROC[bp: ObjF.BracketPair, for: PROC[bp: ObjF.BracketPair, pc: CARD]] =
BEGIN
pcRange: ObjF.PCRange ← ObjF.GetPCRange[bp];
tentative: CARD ← pcRange.first;
SeeOneSubBracket1: PROC[bp: ObjF.BracketPair] RETURNS[--stop-- BOOLEAN] =
BEGIN
subRange: ObjF.PCRange ← ObjF.GetPCRange[bp];
IF subRange.first <= tentative THEN {tentative ← subRange.limit; RETURN[FALSE]}
ELSE RETURN[TRUE];
END;
SeeOneSubBracket2: PROC[bp: ObjF.BracketPair] RETURNS[--stop-- BOOLEAN] =
{GenInterestingPCsWithinABracket[bp, for]; RETURN[FALSE]};
ObjF.GenSubBracketPairs[bp, SeeOneSubBracket1];
IF tentative < pcRange.limit THEN for[bp, tentative];
ObjF.GenSubBracketPairs[bp, SeeOneSubBracket2];
END;
DescribeBTHInfo: PROC[info: BTHInfo, jmpi: JointMobParsedInfo] =
BEGIN
IF info = NIL THEN
ShowOff["\TNIL\N"]
ELSE
ShowOff[IO.PutFR["\Tself = %g\N", IO.rope[RopeForBTH[info.self, jmpi]]]];
END;
RopeForBTH: PROC[bth: BTH, jmpi: JointMobParsedInfo] RETURNS[Rope.ROPE] =
BEGIN
IF bth = NIL THEN RETURN["NIL"] ELSE
BEGIN
btr: BTRMA.FetchBTR[bth];
bthInfo: BTHInfo ← NARROW[RefTab.Fetch[jmpi.rawBodyInfo.table, bth].val];
bracket: ObjF.BracketPair ← IF bthInfo = NIL THEN NIL ELSE bthInfo.bracketPair;
bthInfoRope: Rope.ROPEIF bthInfo = NIL THEN "no info" ELSE IF bracket = NIL THEN "[no associated bracket]" ELSE RopeForBracketPair[bracket, jmpi];
RETURN[IO.PutFR["bti = %g, source position = %g, bthInfo = %g", IO.card[MA.GetCodedBTIFromBTH[bth]], IO.card[btr.sourceIndex], IO.rope[bthInfoRope]]]
END;
END;
DescribeBracketPair: PROC[bp: ObjF.BracketPair, jmpi: JointMobParsedInfo] =
BEGIN
kind: ObjF.BracketPairKind ← ObjF.GetBracketPairKind[bp];
SELECT kind FROM
syntheticOuter => ShowOff["\T\Toutermost pair\N\N"];
syntheticFun =>
BEGIN
stab: ObjF.Stab ← ObjF.GetFunStab[bp];
ShowOff["\T\Tfunction body\N"];
ShowOff[IO.PutFR["\T\T text = %g\N\N", IO.rope[stab.rope]]];
END;
actual =>
BEGIN
pcRange: ObjF.PCRange ← ObjF.GetPCRange[bp];
stabRange: ObjF.StabRange ← ObjF.GetStabRange[bp];
ShowOff["\T\Tblock\N"];
ShowOff[IO.PutFR["\T\T pc range = [%g..%g)\N", IO.card[pcRange.first], IO.card[pcRange.limit]]];
ShowOff[IO.PutFR["\T\T stab range = [%g..%g)\N\N", IO.card[stabRange.first], IO.card[stabRange.first+stabRange.count]]];
END;
ENDCASE => CCE[cirioError];
END;
RopeForBracketPair: PROC[bp: ObjF.BracketPair, jmpi: JointMobParsedInfo] RETURNS[Rope.ROPE] =
BEGIN
kind: ObjF.BracketPairKind ← ObjF.GetBracketPairKind[bp];
SELECT kind FROM
syntheticOuter => RETURN["\T\Toutermost pair"];
syntheticFun =>
BEGIN
stab: ObjF.Stab ← ObjF.GetFunStab[bp];
RETURN[IO.PutFR["function body, text = %g", IO.rope[stab.rope]]];
END;
actual =>
BEGIN
pcRange: ObjF.PCRange ← ObjF.GetPCRange[bp];
stabRange: ObjF.StabRange ← ObjF.GetStabRange[bp];
rope: Rope.ROPE;
rope ← Rope.Cat[rope, "\T\Tblock\N"];
rope ← Rope.Cat[rope, IO.PutFR["\T\T pc range = [%g..%g)\N", IO.card[pcRange.first], IO.card[pcRange.limit]]];
rope ← Rope.Cat[rope, IO.PutFR["\T\T stab range = [%g..%g)\N\N", IO.card[stabRange.first], IO.card[stabRange.first+stabRange.count]]];
RETURN[rope];
END;
ENDCASE => CCE[cirioError];
END;
ShowOff: SIGNAL[msg: Rope.ROPE] = CODE;
ShowMob: Commander.CommandProc =
BEGIN
args: CommandTool.ArgumentVector ← CommandTool.Parse[cmd];
mobPath: PFSNames.PATHPFS.PathFromRope[args[1]];
wholePath: PFSNames.PATHPFS.PathFromRope[args[2]];
relativePC: CARDIF args.argc < 4 THEN 0 ELSE Convert.CardFromRope[args[3]];
type: Rope.ROPEIF args.argc < 5 THEN "SunADotOut" ELSE args[4];
fileSet: SystemInterface.FileSet ← SystemInterface.CreateFileSet[];
BEGIN ENABLE UNWIND => SystemInterface.CloseFileSet[fileSet];
mobFile: SystemInterface.CirioFile ← SystemInterface.GetCirioFile[fileSet, mobPath];
wholeFile: SystemInterface.CirioFile ← SystemInterface.GetCirioFile[fileSet, wholePath];
mob: MA.MobCookie ← MA.CreateMobCookie[mobFile];
whole: ObjF.Parsed ← ObjF.CreateParsed[wholeFile, type];
module: ObjF.Module ← ObjF.ModuleFromParsedAndPC[whole, [[0, ""], relativePC]];
jmpi: JointMobParsedInfo ← CreateJointMobParsedInfo[mob, whole, module];
IO.PutF[cmd.out, "\NAllContexts\N"];
PerformJMDITest["AllContexts", jmpi, cmd.out];
IO.PutF[cmd.out, "\NCallableBodies\N"];
PerformJMDITest["CallableBodies", jmpi, cmd.out];
IO.PutF[cmd.out, "\NFunBracketCounts\N"];
PerformJMDITest["FunBracketCounts", jmpi, cmd.out];
IO.PutF[cmd.out, "\NFunBracketHashTable\N"];
PerformJMDITest["FunBracketHashTable", jmpi, cmd.out];
END;
SystemInterface.CloseFileSet[fileSet];
END;
ShowCallableBodies: Commander.CommandProc =
BEGIN
args: CommandTool.ArgumentVector ← CommandTool.Parse[cmd];
mobPath: PFSNames.PATHPFS.PathFromRope[args[1]];
wholePath: PFSNames.PATHPFS.PathFromRope[args[2]];
relativePC: CARDIF args.argc < 4 THEN 0 ELSE Convert.CardFromRope[args[3]];
type: Rope.ROPEIF args.argc < 5 THEN "SunADotOut" ELSE args[4];
fileSet: SystemInterface.FileSet ← SystemInterface.CreateFileSet[];
BEGIN ENABLE UNWIND => SystemInterface.CloseFileSet[fileSet];
mobFile: SystemInterface.CirioFile ← SystemInterface.GetCirioFile[fileSet, mobPath];
wholeFile: SystemInterface.CirioFile ← SystemInterface.GetCirioFile[fileSet, wholePath];
mob: MA.MobCookie ← MA.CreateMobCookie[mobFile];
whole: ObjF.Parsed ← ObjF.CreateParsed[wholeFile, type];
module: ObjF.Module ← ObjF.ModuleFromParsedAndPC[whole, [[0, ""], relativePC]];
jmpi: JointMobParsedInfo ← CreateJointMobParsedInfo[mob, whole, module];
ShowCallableBodiesInfo[jmpi, cmd.out];
END;
SystemInterface.CloseFileSet[fileSet];
END;
ShowOneBody: Commander.CommandProc =
BEGIN
args: CommandTool.ArgumentVector ← CommandTool.Parse[cmd];
mobPath: PFSNames.PATHPFS.PathFromRope[args[1]];
wholePath: PFSNames.PATHPFS.PathFromRope[args[2]];
relativePC: CARD ← Convert.CardFromRope[args[3]];
codedBti: CARD ← Convert.CardFromRope[args[4]];
type: Rope.ROPEIF args.argc < 6 THEN "SunADotOut" ELSE args[5];
fileSet: SystemInterface.FileSet ← SystemInterface.CreateFileSet[];
BEGIN ENABLE UNWIND => SystemInterface.CloseFileSet[fileSet];
mobFile: SystemInterface.CirioFile ← SystemInterface.GetCirioFile[fileSet, mobPath];
wholeFile: SystemInterface.CirioFile ← SystemInterface.GetCirioFile[fileSet, wholePath];
mob: MA.MobCookie ← MA.CreateMobCookie[mobFile];
whole: ObjF.Parsed ← ObjF.CreateParsed[wholeFile, type];
module: ObjF.Module ← ObjF.ModuleFromParsedAndPC[whole, [[0, ""], relativePC]];
jmpi: JointMobParsedInfo ← CreateJointMobParsedInfo[mob, whole, module];
BEGIN
btKey: RefTab.Key ← NEW[CARD ← codedBti];
cbi: CallableBodyInfo ← NARROW[RefTab.Fetch[jmpi.callableBodies.table, btKey].val];
bi: BodyInfo ← GetBiFromCbi[cbi]; -- forces the analysis of cbi??
FullShowOneAnalyzedCBI[cbi, 0, cmd.out];
END;
END;
SystemInterface.CloseFileSet[fileSet];
END;
CheckAllCallableBodies: Commander.CommandProc =
BEGIN
args: CommandTool.ArgumentVector ← CommandTool.Parse[cmd];
mobPath: PFSNames.PATHPFS.PathFromRope[args[1]];
wholePath: PFSNames.PATHPFS.PathFromRope[args[2]];
relativePC: CARDIF args.argc < 4 THEN 0 ELSE Convert.CardFromRope[args[3]];
type: Rope.ROPEIF args.argc < 5 THEN "SunADotOut" ELSE args[4];
fileSet: SystemInterface.FileSet ← SystemInterface.CreateFileSet[];
BEGIN ENABLE UNWIND => SystemInterface.CloseFileSet[fileSet];
mobFile: SystemInterface.CirioFile ← SystemInterface.GetCirioFile[fileSet, mobPath];
wholeFile: SystemInterface.CirioFile ← SystemInterface.GetCirioFile[fileSet, wholePath];
mob: MA.MobCookie ← MA.CreateMobCookie[mobFile];
whole: ObjF.Parsed ← ObjF.CreateParsed[wholeFile, type];
module: ObjF.Module ← ObjF.ModuleFromParsedAndPC[whole, [[0, ""], relativePC]];
jmpi: JointMobParsedInfo ← CreateJointMobParsedInfo[mob, whole, module];
CheckOneCallableBody: PROC[callableBody: BTH] =
BEGIN
ShowOneCallableBody[jmpi, callableBody, cmd.out];
now, check it
BEGIN
cbi: CallableBodyInfo ← GetCBIForBth[jmpi, callableBody];
bi: BodyInfo ← GetBiFromCbi[cbi]; -- forces the analysis of cbi??
END;
END;
MA.GenCallableBodies[mob, CheckOneCallableBody];
END;
SystemInterface.CloseFileSet[fileSet];
END;
main code
Commander.Register["ShowMob", ShowMob, "usage:\N\TShowMob fullPathMobName fullPathDotOName relPC [format]\N\Twhere\N\T\TfullPathMobName is the mob\N\T\TfullPathDotOName is name of the containing whole\N\T\TrelPC is a containingDotO-relative PC within the desired embeddedd DotO\N\T\Tformat is XCOFF or SunADotOut"];
ShowMob [Menhir-ux]<menhir>sturgis>cirio>SymbolFindingImpl.mob [Menhir-ux]<menhir>sturgis>cirio>sun4>SymbolFindingImpl.c2c.o 0
Commander.Register["ShowCallableBodies", ShowCallableBodies, "usage:\N\TShowCallableBodies fullPathMobName fullPathDotOName relPC [format]\N\Twhere\N\T\TfullPathMobName is the mob\N\T\TfullPathDotOName is name of the containing whole\N\T\TrelPC is a containingDotO-relative PC within the desired embeddedd DotO\N\T\Tformat is XCOFF or SunADotOut"];
ShowCallableBodies StackCirioImpl.mob sun4>StackCirioImpl.c2c.o 0
Commander.Register["ShowOneCallableBody", ShowOneBody, "usage:\N\TShowOneCallableBody fullPathMobName fullPathDotOName relPC codedBti [format]\N\Twhere\N\T\TfullPathMobName is the mob\N\T\TfullPathDotOName is name of the containing whole\N\T\TrelPC is a containingDotO-relative PC within the desired embeddedd DotO\N\T\TcodedBti is the codedBti of a callable block\N\T\Tformat is XCOFF or SunADotOut"];
ShowOneCallableBody StackCirioImpl.mob sun4>StackCirioImpl.c2c.o 0 xxx
Commander.Register["CheckAllCallableBodies", CheckAllCallableBodies, "usage:\TCheckAllCallableBodies fullPathMobName fullPathDotOName relPC [format]\N\Twhere\N\T\TfullPathMobName is the mob\N\T\TfullPathDotOName is name of the containing whole\N\T\TrelPC is a containingDotO-relative PC within the desired embeddedd DotO\N\T\Tformat is XCOFF or SunADotOut"];
CheckAllCallableBodies StackCirioImpl.mob sun4>StackCirioImpl.c2c.o 0
END..