ObjectFilesImpl.mesa
Copyright Ó 1990, 1991, 1992, 1993 by Xerox Corporation. All rights reserved.
Modified from DotOAccessImpl.mesa
Laurie Horton, June 24, 1992 6:08 pm PDT
Philip James, February 22, 1992 11:05 am PST
Katsuyuki Komatsu January 23, 1993 4:35 pm PST
Last tweaked by Mike Spreitzer January 21, 1993 5:53 pm PST
Willie-s, June 30, 1992 2:42 pm PDT
Jas, January 5, 1993 2:27 pm PST
Ychou, June 29, 1993 12:06 pm PDT
DIRECTORY
Basics USING [Card32FromF, FWORD],
Commander USING[CommandProc, Register],
CommanderOps USING[ArgumentVector, Parse],
Convert USING[CardFromRope, IntFromRope, RopeFromCard],
ObjectFiles USING[BracketNest, BracketPairKind, BracketProc, CGrammar, FunInfo, GlobalVarLoc, Module, ModuleInfo, NameAndNumber, NoFileSegmentPC, noFSeg, Parsed, PCRange, FileSegmentPC, Stab, StabType, SymbolProc, UnreadableObjectFile, VarLoc, VarLocBody, VersionStampInfo],
ObjectFilesPrivate USING[BracketConsumer, BracketFinisher, BracketPair, BracketPairBody, FnConsumer, FnFinisher, FunHandleBody, FunStabInfo, FunStabSet, FunStabSetBody, GetTypeRefProcType, Header, HeaderBody, LineNumToPCMap, LineNumToPCMapBody, ModuleBody, ObjectFileFlavor, ParsedBody, PCtoLineNumMap, PCtoLineNumMapBody, SLineData, StabList, StabSet, TranslationTable, TranslationTableBody],
IO USING[card, Close, GetChar, GetIndex, PutF, PutF1, PutFL, PutFLR, PutFR, PutFR1, PutRope, rope, SetIndex, STREAM, UnsafeGetBlock],
PFS USING [PathFromRope, RopeFromPath, StreamOpen],
PFSNames USING [PATH, ShortName, ComponentRope, EmptyPath, Equal],
Rope USING[Cat, Concat, Equal, Fetch, Find, Index, IsEmpty, IsPrefix, Length, NewText, ROPE, Substr, Text],
SGI USING [WireTables, UnPackLineNumbers],
SystemInterface USING[CirioFile, CloseFileSet, CreateFileSet, FileSet, GetCirioFile, GetNameOfFile, GetStreamForFile, ReleaseStreamForFile, ShowReport];
ObjectFilesImpl: CEDAR MONITOR
IMPORTS Basics, Commander, CommanderOps, Convert, IO, ObjectFiles, PFS, PFSNames, Rope, SystemInterface, SGI
EXPORTS ObjectFiles, ObjectFilesPrivate
= {
Useful types
PATH: TYPE ~ PFSNames.PATH;
ROPE: TYPE ~ Rope.ROPE;
Following types are defined in ObjectFiles and ObjectFilesPrivate
Parsed: TYPE ~ REF ParsedBody;
ParsedBody: PUBLIC TYPE ~ ObjectFilesPrivate.ParsedBody;
Module: TYPE ~ REF ModuleBody;
ModuleBody: PUBLIC TYPE ~ ObjectFilesPrivate.ModuleBody;
ModuleInfo: TYPE ~ ObjectFiles.ModuleInfo;
Header: TYPE ~ REF HeaderBody;
HeaderBody: TYPE ~ ObjectFilesPrivate.HeaderBody;
FileSegmentPC: TYPE ~ ObjectFiles.FileSegmentPC;
Stab: TYPE ~ ObjectFiles.Stab;
StabType: TYPE ~ ObjectFiles.StabType;
StabList: TYPE ~ ObjectFilesPrivate.StabList;
StabSet: TYPE ~ ObjectFilesPrivate.StabSet;
BracketConsumer: TYPE ~ ObjectFilesPrivate.BracketConsumer;
BracketFinisher: TYPE ~ ObjectFilesPrivate.BracketFinisher;
FnConsumer: TYPE ~ ObjectFilesPrivate.FnConsumer;
FnFinisher: TYPE ~ ObjectFilesPrivate.FnFinisher;
Registration
FlavorSeq: TYPE = RECORD[SEQUENCE length: CARD OF ObjectFilesPrivate.ObjectFileFlavor];
translationTable: ObjectFilesPrivate.TranslationTable ¬ NIL;
registry: REF FlavorSeq ← NEW[FlavorSeq[0]];
OFFFromStream: PUBLIC PROC[stream: IO.STREAM] RETURNS [ROPE] ~ {
index: INT;
ValueFromStream: PROC [num: CARD] RETURNS [val: CARD] = {
val ¬ 0;
IO.SetIndex[stream, translationTable[num].byteOffset];
FOR j: CARD IN [0..translationTable[num].length) DO
val ¬ (val * 256) + ORD[IO.GetChar[stream]];
ENDLOOP;
};
IF stream = NIL THEN
RETURN["stream was NIL, so no OFF rope was returned."];
IF translationTable = NIL THEN
RETURN["table of OFF's was NIL."];
index ¬ IO.GetIndex[stream];
FOR i: CARD IN [0..translationTable.length) DO
IF translationTable[i].type = middle OR translationTable[i].type = last THEN LOOP;
IF ValueFromStream[i] = translationTable[i].compareValue THEN {
IF translationTable[i].type = point THEN {
IO.SetIndex[stream, index];
RETURN[translationTable[i].format];
};
FOR cont: CARD IN [i+1..translationTable.length) DO
IF ValueFromStream[cont] # translationTable[cont].compareValue THEN EXIT;
IF translationTable[cont].type = last THEN {
IO.SetIndex[stream, index];
RETURN[translationTable[cont].format];
};
ENDLOOP;
};
ENDLOOP;
IO.SetIndex[stream, index];
RETURN["No OFF rope for object File."];
};
RegisterObjectFileFlavor: PUBLIC PROC[flavor: ObjectFilesPrivate.ObjectFileFlavor, table: ObjectFilesPrivate.TranslationTable ¬ NIL] ~ {
i: CARD;
IF (i ¬ RetrieveObjectFileFlavorInner[flavor.formatSpecificString]) # LAST[CARD] THEN registry[i] ¬ flavor
ELSE {
newR: REF FlavorSeq ¬ NEW[FlavorSeq[registry.length+1]];
FOR i IN [0..registry.length) DO
newR[i] ¬ registry[i];
ENDLOOP;
newR[registry.length] ¬ flavor;
registry ¬ newR;
};
IF table # NIL THEN {
newTT: ObjectFilesPrivate.TranslationTable ¬ NIL;
IF translationTable = NIL THEN
translationTable ¬ NEW[ObjectFilesPrivate.TranslationTableBody[table.length]]
ELSE {
newLen: CARD ¬ translationTable.length + table.length;
newTT ¬ translationTable;
translationTable ¬ NEW[ObjectFilesPrivate.TranslationTableBody[newLen]];
FOR i: CARD IN [0..newTT.length) DO
translationTable[i+table.length] ¬ newTT[i];
ENDLOOP;
};
FOR i: CARD IN [0..table.length) DO
translationTable[i] ¬ table[i];
ENDLOOP;
};
};
RetrieveObjectFileFlavor: PUBLIC PROC[formatSpecificString: ROPE] RETURNS[flavor: ObjectFilesPrivate.ObjectFileFlavor] ~ {
i:CARD ¬ RetrieveObjectFileFlavorInner[formatSpecificString];
IF i # LAST[CARD] THEN RETURN [registry[i]] ELSE ERROR;
};
RetrieveObjectFileFlavorInner: PROC[formatSpecificString: ROPE] RETURNS[registryIndex: CARD] ~ {
FOR i: CARD IN [0 .. registry.length) DO
IF Rope.Equal[formatSpecificString, registry[i].formatSpecificString] THEN RETURN [i];
ENDLOOP;
RETURN[LAST[CARD]]};

Whole Object Files
CreateParsed: PUBLIC ENTRY PROC[f: SystemInterface.CirioFile, fs: ROPE ¬ NIL, targetData: REF ANYNIL] RETURNS[Parsed] = {
ENABLE UNWIND => NULL;
parsed: Parsed ¬ NEW[ParsedBody];
stream: IO.STREAM ¬ SystemInterface.GetStreamForFile[f];
parsed.file ¬ f;
parsed.targetData ¬ targetData;
IF fs = NIL THEN fs ¬ OFFFromStream[stream];
parsed.formatSpecificString ¬ fs;
parsed.cGrammar ¬ RetrieveObjectFileFlavor[parsed.formatSpecificString].cGrammar;
parsed.header ¬ RetrieveObjectFileFlavor[parsed.formatSpecificString].readHeaderProc[stream];
parsed.stabLimit ¬ parsed.header.nEntries;
SystemInterface.ReleaseStreamForFile[f, stream];
RETURN[parsed];
Are we supposed to create the modules list too?
Maybe we should push this down into a file specific implementation...
};
GetObjectFile: PUBLIC PROC[whole: Parsed] RETURNS[SystemInterface.CirioFile] =
{IF whole = NIL THEN RETURN[NIL] ELSE RETURN[whole.file]};
GetFormatString: PUBLIC PROC[whole: Parsed] RETURNS[ROPE] ~ {
{IF whole = NIL THEN RETURN[NIL] ELSE RETURN[whole.formatSpecificString]};
};
Modules
GetModule: PUBLIC PROC[info: ModuleInfo] RETURNS[Module] = {
IF info.whole = NIL THEN
RETURN[NIL]
ELSE
This should search info.whole.modules looking for info.fileName & info.instance.
FOR mods: LIST OF Module ¬ info.whole.modules, mods.rest WHILE mods # NIL DO
IF PFSNames.Equal[mods.first.fileName, info.fileName] AND mods.first.instance = info.instance THEN
RETURN[mods.first];
ENDLOOP;
RETURN[NIL];
};
GetModuleInfo: PUBLIC PROC[module: Module] RETURNS[REF ModuleInfo] = {
ENABLE UnreadableObjectFile => GOTO fails;
IF module = NIL THEN RETURN[NIL];
RETURN[NEW[ModuleInfo¬[
whole: IF module.moduleWhole = NIL THEN module.whole ELSE module.moduleWhole,
fileName: module.fileName,
instance: module.instance
]]];
EXITS
fails => RETURN[NIL];
};
Finding Modules
ModuleFromParsedAndPC: PUBLIC PROC [whole: Parsed, spc: FileSegmentPC, moduleRope: ROPE ¬ NIL] RETURNS [Module] ~ {
IF whole = NIL THEN RETURN[NIL] ELSE
RETURN[RetrieveObjectFileFlavor[whole.formatSpecificString].moduleFromParsedAndPCProc[whole: whole, spc: spc, moduleRope: moduleRope]];
};
Globalish variables
FindVersionStamp: PUBLIC PROC[module: Module] RETURNS[REF ObjectFiles.VersionStampInfo] =
{
IF module = NIL THEN RETURN[NIL];
InstallStaticVars[module];
RETURN[module.versionStampInfo];
};
FindGlobalFrameVar: PUBLIC PROC[module: Module] RETURNS[ObjectFiles.GlobalVarLoc] =
{
IF module = NIL THEN RETURN[NIL];
InstallStaticVars[module];
RETURN[module.globalFrameGvl]
};
InstallStaticVars: PROC[module: Module] ~ {
RetrieveObjectFileFlavor[module.whole.formatSpecificString].installStaticVarsProc[module];
};
UnreadableDotO: PUBLIC ERROR[msg: ROPE] = CODE;
VarLocFromStab: PUBLIC PROC [stab: Stab] RETURNS [ObjectFiles.VarLoc ¬ NIL] ~ {
RETURN[RetrieveObjectFileFlavor[stab.module.whole.formatSpecificString].varLocFromStabProc[stab]];
};
GetTypeRef: PUBLIC PROC[module: Module, sourceStream: IO.STREAM] RETURNS [ROPE]~ {
RETURN[RetrieveObjectFileFlavor[module.whole.formatSpecificString].getTypeRefProc[sourceStream]];
};
MakeUnknownVarLoc: PUBLIC PROC [why: ROPE] RETURNS [ObjectFiles.VarLoc] ~ {
RETURN [NEW [ObjectFiles.VarLocBody ¬ [bitSize: 8 -- should this be unspecdBitSize?? , indirect: FALSE--, where: unknown[why] ]]]};
CheckStaticVar: PUBLIC PROC[stab: Stab, namePrefix: ROPE, allowSuffix: BOOL] RETURNS [ObjectFiles.VarLoc] = {
foundName: ROPE;
foundStab: Stab;
[foundName, foundStab] ¬ CheckStaticStab[stab, namePrefix, allowSuffix];
IF foundStab = NIL THEN RETURN[NIL];
RETURN VarLocFromStab[stab]};
a returned NIL stab means that the check failed
CheckStaticStab: PROC[stab: Stab, namePrefix: ROPE, allowSuffix: BOOL] RETURNS[foundName: ROPE, foundStab: Stab] =
{
namePrefixLen: CARD = namePrefix.Length[];
text: ROPE ¬ ReadStabRope[stab];
length: CARD ¬ Rope.Length[text];
IF Rope.IsPrefix[namePrefix, text] THEN -- possible hit
{
we expect to find 0 or more digits followed by a colon
FOR J: CARD IN [namePrefixLen..length) DO
char: CHAR ¬ Rope.Fetch[text, J];
IF char = ': THEN -- bingo
RETURN [text.Substr[len: J], stab];
IF char < '0 OR char > '9 OR NOT allowSuffix THEN EXIT; -- non digit
ENDLOOP;
};
RETURN[NIL, NIL];
};
Instructions
ReadInstruction: PUBLIC PROC[module: Module, spc: FileSegmentPC] RETURNS[inst: CARD] =
{
IF module = NIL THEN RETURN[0] ELSE
{
buffer: Basics.FWORD;
stream: IO.STREAM ¬ SystemInterface.GetStreamForFile[module.whole.file];
IO.SetIndex[stream, module.whole.header.text.byteOffset+spc.relPC];
TRUSTED{[] ¬ IO.UnsafeGetBlock[stream, [LOOPHOLE[LONG[@buffer]], 0, 4]]};
SystemInterface.ReleaseStreamForFile[module.whole.file, stream];
RETURN[Basics.Card32FromF[buffer]];
};
};
GetSPOffset: PUBLIC PROC[module: Module, spc: FileSegmentPC] RETURNS[INT] =
{
IF module = NIL THEN
RETURN[0]
ELSE
RETURN[RetrieveObjectFileFlavor[module.whole.formatSpecificString].getSPOffsetProc[module, spc]];
};
Initialized Storage
ReadInitialDataAsRope: PUBLIC PROC[module: Module, fileByteOffset: CARD] RETURNS[ROPE] =
{
rope: ROPE;
stream: IO.STREAM ¬ SystemInterface.GetStreamForFile[module.whole.file];
{
ENABLE UNWIND => SystemInterface.ReleaseStreamForFile[module.whole.file, stream];
IO.SetIndex[stream, fileByteOffset];
rope ¬ ReadRope[stream];
};
SystemInterface.ReleaseStreamForFile[module.whole.file, stream];
RETURN[rope];
};
Functions
FunHandle: TYPE = REF FunHandleBody;
FunHandleBody: PUBLIC TYPE = ObjectFilesPrivate.FunHandleBody;
FunInfo: TYPE = ObjectFiles.FunInfo;
pcs are relative to the whole
GenFuns: PUBLIC PROC[module: Module, for: PROC[FunHandle] RETURNS[--stop-- BOOLEAN]] = {
IF module = NIL THEN RETURN;
InstallFunStabs[module];
FOR I: CARD IN [0..module.funStabs.nFunStabs) DO
IF for[module.funStabs[I].funHandle] THEN EXIT;
ENDLOOP;
};
GetFunInfo: PUBLIC PROC[fun: FunHandle] RETURNS[FunInfo] =
{
funIndex: CARD ¬ fun.index;
module: Module ¬ fun.module;
stab: Stab ¬ module.funStabs[funIndex].stab;
cName: ROPE ¬ CNameOfStab[stab];
firstPC: CARD ¬ stab.value;
nextIndex: CARD ¬ funIndex+1;
limitPC: CARD ¬ IF nextIndex = module.funStabs.nFunStabs THEN module.limitPC ELSE module.funStabs[nextIndex].stab.value;
RETURN[[stab, cName, [firstPC, limitPC]]];
};
GetFunBrackets: PUBLIC PROC[fun: FunHandle] RETURNS[BracketPair] =
{
funIndex: CARD ¬ fun.index;
module: Module ¬ fun.module;
IF NOT module.funStabs[funIndex].bracketsScanned THEN InstallBracketPairsForOneFunStab[module, funIndex];
RETURN[module.funStabs[funIndex].brackets];
};
Bracket Pairs
BracketPair: TYPE = REF BracketPairBody;
BracketPairBody: PUBLIC TYPE = ObjectFilesPrivate.BracketPairBody;
BracketNest: TYPE = ObjectFiles.BracketNest;
BracketPairKind: TYPE = ObjectFiles.BracketPairKind;
FunStabSet: TYPE = ObjectFilesPrivate.FunStabSet;
FunStabSetBody: TYPE = ObjectFilesPrivate.FunStabSetBody;
FunStabInfo: TYPE = ObjectFilesPrivate.FunStabInfo;
SymbolProc: TYPE = ObjectFiles.SymbolProc;
BracketProc: TYPE = ObjectFiles.BracketProc;
For Function (synthetic) bracket pairs: leftX is the index of the FUN stab, and rightX is one less than the index of the next function stab, except for the last such bracketPair, in which case rightX is the highest existing stab index. For this pair, the pc on the LBrac is the pc on the Fun stab, and the pc on the RBrac is the pc of the following Fun stab (or, if not embedded or embedded and the last embedded, then header.textSize, or if embedded and not the last embedded, then the first PC occurring in the next embedded file).
GenOtherSymbolStabs: PUBLIC PROC[module: Module, for: SymbolProc]
= {GenSymbolStabs[GetOuterBracketPair[module], for]};
GenFunBracketPairs: PUBLIC PROC[module: Module, for: BracketProc] =
{
IF module = NIL THEN RETURN;
InstallFunStabs[module];
FOR I: CARD IN [0..module.funStabs.nFunStabs) DO
InstallBracketPairsForOneFunStab[module, I];
this is a crock, but until I can build a bracket pair for a function without building the nested bracket pairs, I don't have a bp to hand to for.
IF for[module.funStabs[I].brackets] THEN EXIT;
ENDLOOP;
};
GetOuterBracketPair: PUBLIC PROC[module: Module] RETURNS[BracketPair] =
{IF module = NIL THEN RETURN[NIL] ELSE RETURN[module.outerBracket]};
GetFunStab: PUBLIC PROC[bp: BracketPair] RETURNS[Stab] =
{IF bp = NIL OR bp.kind # syntheticFun THEN RETURN[NIL] ELSE RETURN[bp.funStab]};
GetFunHandle: PUBLIC PROC[bp: BracketPair] RETURNS[FunHandle] =
{
InstallFunStabs[bp.module];
IF bp = NIL OR bp.kind # syntheticFun THEN RETURN[NIL]
ELSE
RETURN[bp.module.funStabs[bp.funIndex].funHandle];
};
GetFunHandleFromNest: PUBLIC PROC[nest: BracketNest] RETURNS[FunHandle] =
{RETURN[IF nest = NIL OR nest.rest = NIL THEN NIL ELSE GetFunHandle[nest.rest.first]]};
GetBracketPairKind: PUBLIC PROC[bp: BracketPair] RETURNS[BracketPairKind] =
{IF bp = NIL THEN RETURN[nil] ELSE RETURN[bp.kind]};
GetPCRange: PUBLIC PROC[bp: BracketPair] RETURNS[ObjectFiles.PCRange] =
{IF bp = NIL THEN RETURN[[0, 0]] ELSE RETURN[[bp.firstPC, bp.pcLimit]]};
GetStabRange: PUBLIC PROC[bp: BracketPair] RETURNS[StabRange] =
{IF bp = NIL THEN RETURN[[0, 0]] ELSE RETURN[[bp.firstX, bp.limitX-bp.firstX]]};
GenSubBracketPairs: PUBLIC PROC[bp: BracketPair, for: BracketProc] =
{
IF bp = NIL THEN RETURN;
IF (bp.kind = syntheticFun OR bp.kind = actual) AND NOT bp.module.funStabs[bp.funIndex].bracketsScanned THEN InstallBracketPairsForOneFunStab[bp.module, bp.funIndex];
FOR bps: LIST OF BracketPair ¬ bp.innerBrackets, bps.rest WHILE bps # NIL DO
IF for[bps.first] THEN EXIT;
ENDLOOP;
};
foo: IO.STREAM;
GenSymbolStabs: PUBLIC PROC[bp: BracketPair, for: SymbolProc] = {
IF bp = NIL THEN RETURN;
{module: Module ~ bp.module;
SELECT bp.kind FROM
syntheticFun, actual => {
IF NOT module.funStabs[bp.funIndex].bracketsScanned THEN
<<MJS August 22, 1990: Every BracketPair is created with its symbols filled in, except for syntheticOuter bps, so I comment out the following (which wouldn't work anyway, 'cause it doesn't side-effect my bp):
InstallBracketPairsForOneFunStab[module, bp.funIndex];>>
UnreadableObjectFile[IO.PutFR1["unscanned actual or syntheticFun bp in %g", [rope[DescribeModule[module]]] ]];
FOR stabs: StabList ¬ bp.symbols, stabs.rest WHILE stabs # NIL DO
IF for[stabs.first] THEN EXIT;
ENDLOOP;
RETURN};
syntheticOuter => {
IF Rope.Equal[module.whole.formatSpecificString, "XCOFF"] THEN {
stabX: CARD ¬ module.firstStabX;
limitX: CARD ¬ module.funStabs.firstX;
FOR i: CARDINAL IN [stabX-module.firstStabX .. limitX-module.firstStabX) DO
stab: Stab ~ module.stabs[i];
IF stab.stabType#SLine AND for[stab] THEN RETURN;
ENDLOOP;
FOR fsi: CARD IN [0..module.funStabs.nFunStabs) DO
funStab: Stab ¬ AlterFunStab[module, fsi];
IF for[funStab] THEN
RETURN;
ENDLOOP;
RETURN}
ELSE {
stabX: CARD ¬ module.firstStabX;
fsi: CARDINAL ¬ 0;
WHILE stabX < module.limitStabX DO
limitX, newX: CARD ¬ module.limitStabX;
funStabX: CARD ¬ LAST[CARD];
funStab: Stab ¬ NIL;
IF fsi<module.funStabs.nFunStabs THEN {
funStabX ¬ fsi;
funStab ← module.funStabs[fsi].stab;
limitX ¬ module.funStabs[fsi].firstX;
newX ¬ limitX + module.funStabs[fsi].count;
fsi ¬ fsi.SUCC;
IF fsi<module.funStabs.nFunStabs AND module.funStabs[fsi].firstX < newX THEN UnreadableObjectFile[IO.PutFR1["funStabs out of order in %g", [rope[DescribeModule[module]]] ]];
};
FOR i: CARDINAL IN [CARDINAL[stabX-module.firstStabX] .. CARDINAL[limitX-module.firstStabX]) DO
stab: Stab ~ module.stabs[i];
IF stab.stabType#SLine AND for[stab] THEN RETURN;
ENDLOOP;
IF funStabX#LAST[CARD] THEN {
funStab ¬ AlterFunStab[module, funStabX];
IF for[funStab] THEN
RETURN;
};
IF funStab#NIL AND for[funStab] THEN RETURN;
stabX ¬ newX;
ENDLOOP;
RETURN};};
nil => ERROR--MJS August 22, 1990: there are no nil bps--;
ENDCASE => ERROR;
}};
AlterFunStab: PROC [module: Module, funStabX: CARD] RETURNS [Stab] ~ {
IF module = NIL THEN
RETURN[NIL]
ELSE
RETURN[RetrieveObjectFileFlavor[module.whole.formatSpecificString].alterFunStabProc[module, funStabX]];
};
GetBracketNestForPC: PUBLIC PROC[module: Module, spc: FileSegmentPC] RETURNS[BracketNest] =
{
IF module = NIL THEN RETURN[NIL];
InstallFunStabs[module];
FOR I: CARD IN [0..module.funStabs.nFunStabs) DO
we assume that the fun stabs appear in increasing pc order
IF I+1 = module.funStabs.nFunStabs OR spc.relPC < module.funStabs[I+1].stab.value THEN -- we have the function
{innerNest: BracketNest;
InstallBracketPairsForOneFunStab[module, I];
{fsi: FunStabInfo ~ module.funStabs[I];
IF fsi.brackets=NIL THEN UnreadableObjectFile[IO.PutFLR[
"Lack (cached) of brackets for relPC %g(%xH) in %g, probably in procedure %g",
LIST[[cardinal[spc.relPC]], [cardinal[spc.relPC]],
[rope[PFS.RopeFromPath[module.fileName]]],
[rope[IF fsi.stab#NIL THEN fsi.stab.rope ELSE "?NIL stab?"]]] ]];
innerNest ¬ FindBracketNestInBracketPair[fsi.brackets, spc];
RETURN[CONS[module.outerBracket, CONS[fsi.brackets, innerNest]]];
}};
ENDLOOP;
RETURN[LIST[module.outerBracket]];
};
FindBracketNestInBracketPair: PROC[bp: BracketPair, spc: FileSegmentPC] RETURNS[BracketNest] =
{
FOR subBpis: LIST OF BracketPair ¬ bp.innerBrackets, subBpis.rest WHILE subBpis # NIL DO
IF spc.relPC < subBpis.first.firstPC THEN RETURN[NIL];
IF spc.relPC < subBpis.first.pcLimit THEN RETURN[CONS[subBpis.first, FindBracketNestInBracketPair[subBpis.first, spc]]];
ENDLOOP;
RETURN[NIL];
};
StabRecList: TYPE = LIST OF StabRec;
StabRec: TYPE = RECORD[i, count, nextX: CARD, stab, firstLocal: Stab];
InsertFunStabSorted: PROCEDURE [list: StabRecList, stab: StabRec] RETURNS [StabRecList]~ {
IF list = NIL THEN
RETURN[LIST[stab]]
ELSE IF stab.stab.value > list.first.stab.value THEN
RETURN[CONS[stab, list]]
ELSE RETURN[CONS[list.first, InsertFunStabSorted[list.rest, stab]]];
};
ScanModuleStructure: PUBLIC PROC [module: Module, perFn: FnConsumer] ~ {
flavor: ObjectFilesPrivate.ObjectFileFlavor ~ RetrieveObjectFileFlavor[module.whole.formatSpecificString];
flavor.scanModuleStructure[module, perFn];
RETURN};
ScanFnStructure: PUBLIC PROC
[
module: Module,
funStab: Stab,
firstLocal: Stab ← NIL,
nextX: CARD,
perParm: PROC [Stab] ← NIL,
perBracket: BracketConsumer ← NIL
]
RETURNS [limitX, limitPc: CARD] ~ {
flavor: ObjectFilesPrivate.ObjectFileFlavor ~ RetrieveObjectFileFlavor[module.whole.formatSpecificString];
RETURN flavor.scanFnStructure[module, funStab, firstLocal, nextX, perParm, perBracket]};
ScanBracketStructure: PUBLIC PROC
[
module: Module,
funStab: Stab,
first: Stab,
perLocal: PROC [Stab] ← NIL,
perSubBracket: BracketConsumer ← NIL
]
RETURNS [limitX, firstPc, limitPc: CARD] ~ {
flavor: ObjectFilesPrivate.ObjectFileFlavor ~ RetrieveObjectFileFlavor[module.whole.formatSpecificString];
RETURN flavor.scanBktStructure[module, funStab, first, perLocal, perSubBracket]};
InstallFunStabs: PROC[module: Module] = {
flavor: ObjectFilesPrivate.ObjectFileFlavor ~ RetrieveObjectFileFlavor[module.whole.formatSpecificString];
fnsOrderedByStart: BOOL ~ flavor.fnsOrderedByStart;
funStabs: StabRecList ¬ NIL;
nFunStabs: CARD ¬ 0;
index: CARD;
nextX: CARD ¬ module.limitStabX;
firstFunStabX: CARD ¬ LAST[CARD];
funStabSet: FunStabSet;
IF module.funStabsInstalled THEN RETURN;
IF flavor.scanModuleStructure=NIL THEN {
privX: CARD ¬ LAST[CARD];
FOR I: CARD IN [0..module.stabs.nStabs) DO
stab: Stab ¬ module.stabs[I];
IF stab.module = NIL THEN LOOP;
IF stab.stabType = Fun THEN
{
IF firstFunStabX = LAST[CARD] THEN firstFunStabX ¬ stab.stabX;
IF privX # LAST[CARD] THEN {
funStabs ¬ InsertFunStabSorted[funStabs, [nFunStabs, I - privX, 0, module.stabs[privX], NIL]];
nFunStabs ¬ nFunStabs+1;
};
privX ¬ I;
};
ENDLOOP;
IF privX # LAST[CARD] THEN {
funStabs ¬ InsertFunStabSorted[funStabs, [nFunStabs, module.stabs.nStabs - privX, 0, module.stabs[privX], NIL]];
nFunStabs ¬ nFunStabs+1;
};
}
ELSE {
NoteFn: PROC [funStab: Stab, firstLocal: Stab ← NIL, nextX: CARD] RETURNS [limitX: CARD] ~ {
IF nFunStabs=0 THEN firstFunStabX ← funStab.stabX;
limitX ← ScanFnStructure[module, funStab, firstLocal, nextX, NIL, NIL].limitX;
funStabs ← InsertFunStabSorted[funStabs, [nFunStabs, limitX - funStab.stabX, nextX, funStab, firstLocal]];
nFunStabs ¬ nFunStabs+1;
RETURN};
ScanModuleStructure[module, NoteFn];
};
funStabSet ¬ NEW[FunStabSetBody[nFunStabs]];
funStabSet.firstX ¬ firstFunStabX;
index ¬ nFunStabs-1;
FOR stabs: StabRecList ¬ funStabs, stabs.rest WHILE stabs # NIL DO
firstX: CARD ¬ stabs.first.stab.stabX;
handle: FunHandle ¬ NEW[FunHandleBody¬[module, index --stabs.first.i--]];
IF fnsOrderedByStart AND nextX <= firstX THEN UnreadableObjectFile[IO.PutFR["bug in InstallFunStabs at %g'th fun in %g", [cardinal[stabs.first.i]], [rope[DescribeModule[module]]] ]];
funStabSet[index --stabs.first.i--] ¬ [stabs.first.stab, firstX, stabs.first.count, stabs.first.firstLocal, stabs.first.nextX, handle, FALSE, NIL];
nextX ¬ firstX;
index ¬ index - 1;
ENDLOOP;
module.funStabs ¬ funStabSet;
module.funStabsInstalled ¬ TRUE;
RETURN};
InstallBracketPairsForOneFunStab: PROC [module: Module, funStabIndex: CARD] ~ {
flavor: ObjectFilesPrivate.ObjectFileFlavor ~ RetrieveObjectFileFlavor[module.whole.formatSpecificString];
fsi: FunStabInfo ~ module.funStabs[funStabIndex];
funStab: Stab ~ fsi.stab;
fbp: BracketPair;
parmsTail: StabList ← NIL;
innerBracketsTail: LIST OF BracketPair ← NIL;
NoteParm: PROC [stab: Stab]
~ {[fbp.symbols, parmsTail] ← AppendStabToList[stab, fbp.symbols, parmsTail]};
NoteTopBlock: PROC [first: Stab] RETURNS [limitX: CARD] ~ {
bp: BracketPair;
this: LIST OF BracketPair;
[bp, limitX] ← MakeBracket[first];
this ← LIST[bp];
IF innerBracketsTail=NIL
THEN fbp.innerBrackets ← this
ELSE innerBracketsTail.rest ← this;
innerBracketsTail ← this;
RETURN};
MakeBracket: PROC [first: Stab] RETURNS [bp: BracketPair, limitX: CARD] ~ {
localsTail: StabList ← NIL;
innerTail: LIST OF BracketPair ← NIL;
AddLocal: PROC [stab: Stab]
~ {[bp.symbols, localsTail] ← AppendStabToList[stab, bp.symbols, localsTail]};
AddChild: PROC [first: Stab] RETURNS [limitX: CARD] ~ {
sub: BracketPair;
this: LIST OF BracketPair;
[sub, limitX] ← MakeBracket[first];
this ← LIST[sub];
IF innerTail#NIL
THEN innerTail.rest ← this
ELSE bp.innerBrackets ← this;
innerTail ← this};
bp ← NEW [BracketPairBody ← [
module: module,
kind: actual,
firstX: first.stabX, limitX: 0,
firstPC: 0, pcLimit: 0,
funStab: funStab,
funIndex: funStabIndex,
symbols: NIL,
innerBrackets: NIL]];
[bp.limitX, bp.firstPC, bp.pcLimit] ← ScanBracketStructure[module, funStab, first, AddLocal, AddChild];
RETURN [bp, bp.limitX]};
IF module.funStabs[funStabIndex].bracketsScanned THEN RETURN;
module.funStabs[funStabIndex].bracketsScanned ← TRUE; -- if error occurs, then we won't do this again.
IF flavor.installBracketPairsForOneFunStabProc#NIL THEN {
flavor.installBracketPairsForOneFunStabProc[module, funStabIndex];
RETURN};
module.funStabs[funStabIndex].brackets ← fbp ← NEW[BracketPairBody←[
module: module,
kind: syntheticFun,
firstX: funStab.stabX,
limitX: 0,
firstPC: funStab.value,
pcLimit: 0,
funStab: funStab,
funIndex: funStabIndex,
symbols: NIL,
innerBrackets: NIL]];
[fbp.limitX, fbp.pcLimit] ← ScanFnStructure[module, fsi.stab, fsi.firstLocal, fsi.nextX, NoteParm, NoteTopBlock];
IF fbp.innerBrackets#innerBracketsTail --more than one bracket-- THEN {
tbp: BracketPair ~ NEW[BracketPairBody←[
module: module,
kind: actual,
firstX: fbp.innerBrackets.first.firstX,
limitX: innerBracketsTail.first.limitX,
firstPC: fbp.innerBrackets.first.firstPC,
pcLimit: innerBracketsTail.first.pcLimit,
funStab: funStab,
funIndex: funStabIndex,
symbols: NIL,
innerBrackets: fbp.innerBrackets]];
fbp.innerBrackets ← LIST[tbp]};
RETURN};
AppendStabToList: PROC [elt: Stab, head, tail: StabList] RETURNS [StabList, StabList] ~ {
this: StabList ~ LIST[elt];
IF tail#NIL THEN tail.rest ← this ELSE head ← this;
RETURN [head, this]};
terminated by a LBrac, RBrac, Fun, global symbol, or attempting to go past the end of the symbol table.
ScanSymbolStabs: PUBLIC PROC[module: Module, firstX: CARD, head, tail: StabList] RETURNS[--nextX-- CARD, --head, tail of collected symbols-- StabList, StabList] =
{x: CARD ¬ firstX;
WHILE x < module.limitStabX DO
stab: Stab ¬ ReadStab[module, x];
IF stab = NIL THEN LOOP;
SELECT stab.stabType FROM
LBrac, RBrac, Fun, GSym, STSym, LCSym => RETURN[x, head, tail];
SLine => NULL; -- we ignore SLines
ENDCASE => { -- we simply collect all others as potentially interesting to our clients
symbol: StabList ¬ LIST[stab];
IF head#NIL AND stab=head.first THEN ERROR;
IF head = NIL THEN head ¬ symbol ELSE tail.rest ¬ symbol;
tail ¬ symbol};
x ¬ x+1;
ENDLOOP;
if we reach here, then we have attempted to go past the end of the symbol table.
RETURN[x, head, tail]};
C Line-Number to Relative-PC maps
LineNumToPCMap: TYPE ~ ObjectFilesPrivate.LineNumToPCMap;
LineNumToPCMapBody : TYPE ~ ObjectFilesPrivate.LineNumToPCMapBody;
sorted by Line Num
PCtoLineNumMap: TYPE ~ REF PCtoLineNumMapBody;
PCtoLineNumMapBody: TYPE ~ ObjectFilesPrivate.PCtoLineNumMapBody;
sorted by FileSectionId
SLineData: TYPE = ObjectFilesPrivate.SLineData;
pc will be relative to containing objectFile.
One should think as follows.
For the map from PC to CLine number:
sort the SLines, first by increasing PC, and second by increasing CLine
Then, search backwards among the SLines until we find the first SLine with a PC less or equal to the target pc. Report the CLine in that SLine.
For the map from CLine number to PC
sort the SLines, first by increasing CLine, then by increasing PC
Then, search forwards among the SLines until we find the first SLine with CLine greater or equal to the target CLine. Report the PC in that SLine.
It is intended to support the following model:
given a pc, find the line for which that pc was actually generated
given a line, find the first pc generated after the start of that line.
we tacitly assume that whenever the C compiler generates an SLine containing <CLine, PC>, that the compiler is processing CLine, and that its "nextPC" variable is PC. It may be that no instructions are actually generated until we get to another CLine.
GetPCForLineNum: PUBLIC PROC[module: Module, cLineNum: CARD] RETURNS[FileSegmentPC] = TRUSTED
BEGIN
map: LineNumToPCMap;
IF module = NIL THEN RETURN[ObjectFiles.NoFileSegmentPC];
IF module.lineNumToPC = NIL THEN
{
IF Rope.Equal[module.whole.formatSpecificString, "SGI"] THEN {
Must be a SGI file, need to unpack the line numbers
stream: IO.STREAM ¬ SystemInterface.GetStreamForFile [module.whole.file];
parsed: Parsed ¬ LOOPHOLE[module.whole, Parsed];
wireTables: SGI.WireTables ¬ NARROW[parsed.privateInfo, SGI.WireTables];
SGI.UnPackLineNumbers[stream, module, wireTables];
} ELSE ERROR;
};
map ¬ module.lineNumToPC;
FOR I: CARDINAL IN [0..map.nSlines) DO
IF map[I].cLineNum >= cLineNum THEN RETURN[map[I].parsedRelPC];
ENDLOOP;
IF map.nSlines # 0 THEN RETURN[[ObjectFiles.noFSeg, module.limitPC]];
RETURN[ObjectFiles.NoFileSegmentPC]; -- probably no dbx stabs
END;
pc is expected to be relative to containing objectFile.
We attempt to find the highest line number such that the pc applies to an instruction generated after the imaginary point at the start of the line.
GetLineNumForPC: PUBLIC PROC[module: Module, spc: FileSegmentPC] RETURNS[CARD] = TRUSTED
BEGIN
map: PCtoLineNumMap;
IF module = NIL THEN RETURN[0];
IF module.pcToLineNum = NIL THEN
{
IF Rope.Equal[module.whole.formatSpecificString, "SGI"] THEN {
Must be a SGI file, need to unpack the line numbers
stream: IO.STREAM ¬ SystemInterface.GetStreamForFile [module.whole.file];
parsed: Parsed ¬ LOOPHOLE[module.whole, Parsed];
wireTables: SGI.WireTables ¬ NARROW[parsed.privateInfo, SGI.WireTables];
SGI.UnPackLineNumbers[stream, module, wireTables];
} ELSE ERROR;
};
map ¬ module.pcToLineNum;
FOR I: CARDINAL DECREASING IN [0..map.nSlines) DO
IF map[I].parsedRelPC.relPC <= spc.relPC THEN
RETURN[map[I].cLineNum];
ENDLOOP;
IF map.nSlines # 0 THEN RETURN[map[0].cLineNum];
RETURN[0]; -- probably no dbx stabs
END;
Stabs
This routine treats stabX as relative to the entire Module.
ReadStab: PUBLIC PROC[module: Module, stabX: CARD] RETURNS[Stab] =
{
IF module=NIL THEN RETURN[NIL];
IF stabX >= module.limitStabX THEN ObjectFiles.UnreadableObjectFile[IO.PutFR["Attempt to fetch stab (%g) beyond limit (%g) from %g", [cardinal[stabX]], [cardinal[module.limitStabX]], [rope[PFS.RopeFromPath[module.fileName]]] ]];
RETURN[module.stabs[stabX- module.firstStabX]];
};
ReadStabRope: PUBLIC ENTRY PROC [stab: Stab] RETURNS [rope: ROPE] ~ {
ENABLE UNWIND => NULL;
IF stab=NIL THEN RETURN[NIL] ELSE RETURN[stab.rope]};
CNameOfStab: PUBLIC PROC[stab: Stab] RETURNS[rope: ROPE] =
{
ENABLE UNWIND => NULL;
rope: ROPE ¬ ReadStabRope[stab];
SELECT stab.stabType FROM
Fun, PSym, LSym, RSym, GSym, LCSym, STSym =>
{
colPos: INT ¬ Rope.Find[rope, ":"];
IF colPos < 0 THEN RETURN[rope] ELSE RETURN[Rope.Substr[rope, 0, colPos]];
};
ENDCASE => RETURN[rope];
};
CGrammarOfStab: PUBLIC PROC[stab: Stab] RETURNS[ObjectFiles.CGrammar] ~ {
IF stab # NIL AND stab.module # NIL AND stab.module.whole # NIL THEN
RETURN[stab.module.whole.cGrammar]
ELSE
RETURN [UNKNOWN]
};
symbol ropes
RopeBufferSize: CARD = 100;
RopeBuffer: REF PACKED ARRAY [0..RopeBufferSize) OF CHAR ¬ NEW[PACKED ARRAY [0..RopeBufferSize) OF CHAR];
we assume that the stream has been positioned at the beginning of a sequence of chars terminated by a '\000 char.
ReadRope: PROC[s: IO.STREAM] RETURNS[rope: ROPE] =
BEGIN
ENABLE UNWIND => NULL;
rope: ROPE ¬ NIL;
WHILE TRUE DO
start: CARD ¬ 0;
end: CARD ¬ RopeBufferSize; -- tentative
nChars: CARD;
rt: Rope.Text;
TRUSTED{[] ¬ IO.UnsafeGetBlock[s, [LOOPHOLE[@RopeBuffer­], 0, RopeBufferSize]]};
Our assumption about the initial stream position does not strictly hold. We have observed strings like
&24 ← RopeBuffer
^(100)['\000, '\000, '\000, '\000, '@, '(, '#, '), 'm, 'o,
'b, '←, 'v, 'e, 'r, 's, 'i, 'o, 'n, ' , '[, '1, '8, '7,
'3, '3, '5, '2, '2, '5, '1, ...]
Take care of case where this buffer ends in non-zero character and next starts with zero.
IF NOT rope.IsEmpty AND RopeBuffer[0] = '\000 THEN EXIT;
Find first non-zero character.
FOR I: CARD IN [0..RopeBufferSize) DO
IF RopeBuffer[I] = '\000 THEN LOOP
ELSE {start ¬ I; EXIT};
ENDLOOP;
Find last non-zero character after the first non-zero character.
FOR I: CARD IN [start..RopeBufferSize) DO
IF RopeBuffer[I] = '\000 THEN {end ¬ I; EXIT};
ENDLOOP;
nChars ¬ end - start;
rt ¬ Rope.NewText[nChars];
FOR I: CARD IN [0..nChars) DO rt[I] ¬ RopeBuffer[start + 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 end < RopeBufferSize THEN EXIT;
ENDLOOP;
RETURN[rope];
END;
ReadRope: PROC[s: IO.STREAM] RETURNS[rope: ROPE] =
{
ENABLE UNWIND => NULL;
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];
};
Name Ropes
ParseNameRope: PUBLIC PROC[stab: Stab] RETURNS[ObjectFiles.NameAndNumber] =
{
BadParse: ERROR = CODE;
{
ENABLE BadParse => GO TO failure;
text: ROPE ¬ ReadStabRope[stab];
pos: INT ¬ 0;
length: INT ¬ Rope.Length[text];
findFailure: INT ~ -1;
note: each of the following procedures read a field starting with the char at pos, then update pos to point to the first character following the field that they just read.
ReadInitialName: PROC RETURNS[ROPE] =
{
sep1: ROPE ~ "←"; -- an underscore in modern Ascii.
sep1Index: INT ¬ text.Find[s2: sep1];
IF sep1Index = findFailure THEN BadParse[];
pos ¬ sep1Index+1;
RETURN[text.Substr[len: sep1Index]];
};
ReadChar: PROC RETURNS[CHAR] =
{
tentativeChar: CHAR ¬ Rope.Fetch[text, pos];
IF tentativeChar < '0 OR '9 < tentativeChar THEN
{pos ¬ pos+1; RETURN[tentativeChar]}
ELSE RETURN[' ];
};
ReadNumber: PROC RETURNS[INT] =
{
firstNonDigit: INT ¬ pos;
number: INT;
WHILE firstNonDigit < length DO
c: CHAR ¬ Rope.Fetch[text, firstNonDigit];
IF c < '0 OR '9 < c THEN EXIT;
firstNonDigit ¬ firstNonDigit + 1;
ENDLOOP;
IF firstNonDigit = pos THEN BadParse[];
number ¬ Convert.IntFromRope[text.Substr[start: pos, len: firstNonDigit-pos]];
IF text.Fetch[pos] = '0 THEN number ¬ -number;
pos ¬ firstNonDigit;
RETURN[number];
};
this works even if there is no colon
ReadTrailer: PROC RETURNS[ROPE] =
{
trailerStart: INT ¬ pos;
colonLoc: INT ¬ Rope.Index[text, pos, ":"];
pos ¬ colonLoc;
RETURN[text.Substr[start: trailerStart, len: colonLoc-trailerStart]];
};
name: ROPE ¬ ReadInitialName[];
char: CHAR ¬ ReadChar[];
number: INT ¬ ReadNumber[!BadParse => GOTO failure];
trailer: ROPE ¬ ReadTrailer[];
RETURN[[name, char, number, trailer, TRUE]];
EXITS
failure => RETURN[[NIL, ' , 0, "", FALSE]];
};
};
Printing
RopeForBracketPair: PUBLIC PROC[bp: BracketPair] RETURNS[ROPE] =
{IF bp = NIL THEN RETURN[NIL] ELSE RETURN[Rope.Cat["<", Convert.RopeFromCard[bp.firstX], ", ", Convert.RopeFromCard[bp.limitX], ">"]]};
Print Module
use: PrintModule /dir/.../dir/[sun4/]foo[.c2c].o relativePC
writes foo.ModuleDetails
PrintModule: Commander.CommandProc =
{
args: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd];
name: ROPE ¬ args[1];
type: ROPE ¬ IF args.argc < 4 THEN NIL ELSE args[3];
path: PATH ¬ PFS.PathFromRope[name];
outPath: PATH ¬ PrintFileName[path];
outFile: IO.STREAM ¬ PFS.StreamOpen[outPath, create];
fileSet: SystemInterface.FileSet ¬ SystemInterface.CreateFileSet[];
{ ENABLE UNWIND => SystemInterface.CloseFileSet[fileSet];
file: SystemInterface.CirioFile ¬ SystemInterface.GetCirioFile[fileSet, path];
whole: Parsed ¬ CreateParsed[file, type];
relativePC: CARD ¬ IF args.argc < 3 THEN 0 ELSE Convert.CardFromRope[args[2]];
stream: IO.STREAM ← SystemInterface.GetStreamForFile[whole.file];
nest so that we can catch unwinds and release the stream
module: Module ¬ ModuleFromParsedAndPC[whole, [[0, ""], relativePC]];
IO.PutF[outFile, "Module details for %g embedding at relative pc: %g\N\N", IO.rope[name], IO.card[relativePC]];
IO.PutF[outFile, "textSize: %g, nEntries: %g\N", IO.card[whole.header.text.byteLength], IO.card[whole.header.nEntries]];
IO.PutF1[cmd.out, "\Ndetails being written to %g\N\N", IO.rope[PFS.RopeFromPath[outPath]]];
FOR x: CARD IN [0..module.stabs.nStabs) DO
stab: Stab;
rope: ROPE;
fields: ROPE;
stab ¬ ReadStab[module, module.firstStabX+x];
IF stab = NIL THEN LOOP;
rope ¬ ReadStabRope[stab];
fields ¬ RopeForStabFields[stab];
IO.PutF1[outFile, "%g\N", IO.rope[fields]];
IF Rope.Length[rope] # 0 THEN IO.PutF1[outFile, " %g\N", IO.rope[rope]];
IO.PutRope[outFile, "\N"];
ENDLOOP;
};
SystemInterface.CloseFileSet[fileSet];
IO.Close[outFile];
IO.PutF1[cmd.out, "\Ndetails written to %g\N\N", IO.rope[PFS.RopeFromPath[outPath]]];
};
RopeForStabFields: PROC[stab: Stab] RETURNS[ROPE] =
{
fields: ROPE ¬ NIL;
fields ¬ Rope.Concat[fields, IO.PutFR1["stabX: %g, ", IO.card[stab.stabX]]];
fields ¬ Rope.Concat[fields, IO.PutFR1["stabType: %g, ", IO.rope[RopeForStabType[stab.stabType]]]];
fields ¬ Rope.Concat[fields, IO.PutFR1["size: %g, ", IO.card[stab.size]]];
fields ¬ Rope.Concat[fields, IO.PutFR1["value: %g, ", IO.card[stab.value]]];
fields ← Rope.Concat[fields, IO.PutFR1["rope: %g, ", IO.rope[stab.rope]]];
RETURN[fields];
};
RopeForStabType: PUBLIC PROC[stabType: StabType] RETURNS[ROPE] =
{
RETURN[SELECT stabType FROM
BIncl => "BIncl",
EIncl => "EIncl",
Excl => "Excl",
Fun => "Fun",
GSym => "GSym",
Invalid => "Invalid",
LBrac => "LBrac",
LCSym => "LCSym",
LSym => "LSym",
Main => "Main",
PSym => "PSym",
RBrac => "RBrac",
RSym => "RSym",
SLine => "SLine",
SO => "SO",
SOL => "SOL",
STSym => "STSym",
Unspecified => "Unspecified",
ENDCASE => ""];
};
PrintFileName: PROC[filePath: PATH] RETURNS[PATH] =
{
stemRope: ROPE ¬ filePath.ShortName.ComponentRope;
length: INT ¬ Rope.Length[stemRope];
firstDot: INT ¬ Rope.Find[stemRope, ".", 1];
RETURN[PFS.PathFromRope[Rope.Concat[Rope.Substr[stemRope, 0, firstDot], ".ObjectFileDetails"]]];
};
ShowModuleFunctions foo.o pc
(note: stab indices are only approx plus or minus 1)
ShowModuleFunctions: Commander.CommandProc =
{
args: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd];
path: PATH ¬ PFS.PathFromRope[args[1]];
type: ROPE ¬ IF args.argc < 4 THEN NIL ELSE args[3];
fileSet: SystemInterface.FileSet ¬ SystemInterface.CreateFileSet[];
{ ENABLE {
UNWIND => SystemInterface.CloseFileSet[fileSet];
SystemInterface.ShowReport => {
cmd.out.PutF["%g: %g\n", [atom[priority]], [rope[msgText]] ];
RESUME};
};
file: SystemInterface.CirioFile ¬ SystemInterface.GetCirioFile[fileSet, path];
whole: Parsed ¬ CreateParsed[file, type];
relativePC: CARD ¬ IF args.argc < 3 THEN 0 ELSE Convert.CardFromRope[args[2]];
module: Module ¬ ModuleFromParsedAndPC[whole, [[0, NIL], relativePC]];
ShowOne: PROC[fun: FunHandle] RETURNS[--stop-- BOOLEAN] =
{
info: FunInfo ¬ GetFunInfo[fun];
bp: BracketPair ¬ GetFunBrackets[fun];
IO.PutFL[cmd.out, "%g\N\Tx: [%g..%g), pc: [%g..%g)\N", LIST[IO.rope[info.cName], IO.card[bp.firstX], IO.card[bp.limitX], IO.card[bp.firstPC], IO.card[bp.pcLimit]] ];
RETURN[FALSE];
};
IO.PutF[cmd.out, "Module functions for %g embedding at relative pc = %g\N\N", IO.rope[PFS.RopeFromPath[path]], IO.card[relativePC]];
GenFuns[module, ShowOne];
};
};
ShowOneModuleFunction foo.o pc
(note: stab indices are only approx plus or minus 1, depending on whether one is dealing with a synthetic fun bracket or a real bracket. Recall that the symbols stabs are not even between the brackets anyway, so no one should be looking at these values.)
ShowOneModuleFunction: Commander.CommandProc =
{
args: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd];
path: PATH ¬ PFS.PathFromRope[args[1]];
type: ROPE ¬ IF args.argc < 4 THEN NIL ELSE args[3];
fileSet: SystemInterface.FileSet ¬ SystemInterface.CreateFileSet[];
BEGIN ENABLE {
UNWIND => SystemInterface.CloseFileSet[fileSet];
SystemInterface.ShowReport => {
cmd.out.PutF["%g: %g\n", [atom[priority]], [rope[msgText]] ];
RESUME};
};
file: SystemInterface.CirioFile ¬ SystemInterface.GetCirioFile[fileSet, path];
whole: Parsed ¬ CreateParsed[file, type];
relativePC: CARD ¬ IF args.argc < 3 THEN 0 ELSE Convert.CardFromRope[args[2]];
module: Module ¬ ModuleFromParsedAndPC[whole, [[0, ""], relativePC]];
bn: BracketNest ¬ GetBracketNestForPC[module, [[0, ""], relativePC]];
funbp: BracketPair ¬ bn.rest.first;
IO.PutF[cmd.out, "\NOne Module function for %g embedding at relative pc = %g\N\N", IO.rope[PFS.RopeFromPath[path]], IO.card[relativePC]];
IO.PutF1[cmd.out, "%g\N", IO.rope[CNameOfStab[funbp.funStab]]];
Showbps[cmd.out, 5, funbp];
END;
};
Showbps: PROC[on: IO.STREAM, depth: INT, bp: BracketPair] =
{
tab: PROC[plus: CARD] = {FOR I: CARD IN [0..depth+plus) DO IO.PutRope[on, " "] ENDLOOP};
tab[0];
IO.PutFL[on, "{ x: [%g..%g), pc: [%g..%g)\N", LIST[IO.card[bp.firstX], IO.card[bp.limitX], IO.card[bp.firstPC], IO.card[bp.pcLimit]] ];
FOR symbols: StabList ¬ bp.symbols, symbols.rest WHILE symbols # NIL DO
ShowStab[on, depth, symbols.first];
ENDLOOP;
FOR innerbps: LIST OF BracketPair ¬ bp.innerBrackets, innerbps.rest WHILE innerbps # NIL DO
Showbps[on, depth+5, innerbps.first];
ENDLOOP;
tab[0];
IO.PutRope[on, "}\N\N"];
};
ShowStab: PROC[on: IO.STREAM, depth: CARD, stab: Stab] =
{
fields: ROPE ¬ RopeForStabFields[stab];
text: ROPE ¬ ReadStabRope[stab];
FOR I: CARD IN [0..depth) DO IO.PutRope[on, " "] ENDLOOP;
IO.PutF1[on, "%g\N", IO.rope[fields]];
IF Rope.Length[text] # 0 THEN
{
FOR I: CARD IN [0..depth+2) DO IO.PutRope[on, " "] ENDLOOP;
IO.PutF1[on, "%g\N", IO.rope[text]];
};
IO.PutRope[on, "\N"];
};
ShowModuleGlobalStabs foo.o pc
ShowModuleGlobalStabs: Commander.CommandProc =
{
args: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd];
path: PATH ¬ PFS.PathFromRope[args[1]];
type: ROPE ¬ IF args.argc < 4 THEN NIL ELSE args[3];
fileSet: SystemInterface.FileSet ¬ SystemInterface.CreateFileSet[];
{ ENABLE {
UNWIND => SystemInterface.CloseFileSet[fileSet];
SystemInterface.ShowReport => {
cmd.out.PutF["%g: %g\n", [atom[priority]], [rope[msgText]] ];
RESUME};
};
file: SystemInterface.CirioFile ¬ SystemInterface.GetCirioFile[fileSet, path];
whole: Parsed ¬ CreateParsed[file, type];
relativePC: CARD ¬ IF args.argc < 3 THEN 0 ELSE Convert.CardFromRope[args[2]];
module: Module ¬ ModuleFromParsedAndPC[whole, [[0, ""], relativePC]];
ReportStab: PROC [stab: Stab] RETURNS [--stop-- BOOLEAN] ~ {
IF stab.module#NIL THEN ShowStab[cmd.out, 5, stab];
RETURN [FALSE]};
IO.PutF[cmd.out, "\NModule global stabs for %g embedding at relative pc = %g\N\N", IO.rope[PFS.RopeFromPath[path]], IO.card[relativePC]];
InstallFunStabs[module];
GenOtherSymbolStabs[module, ReportStab];
};
};
CheckModuleBrackets: Commander.CommandProc =
{
args: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd];
path: PATH ¬ PFS.PathFromRope[args[1]];
type: ROPE ¬ IF args.argc < 4 THEN NIL ELSE args[3];
fileSet: SystemInterface.FileSet ¬ SystemInterface.CreateFileSet[];
{ ENABLE {
UNWIND => SystemInterface.CloseFileSet[fileSet];
SystemInterface.ShowReport => {
cmd.out.PutF["%g: %g\n", [atom[priority]], [rope[msgText]] ];
RESUME};
};
file: SystemInterface.CirioFile ¬ SystemInterface.GetCirioFile[fileSet, path];
whole: Parsed ¬ CreateParsed[file, type];
relativePC: CARD ¬ IF args.argc < 3 THEN 0 ELSE Convert.CardFromRope[args[2]];
module: Module ¬ ModuleFromParsedAndPC[whole, [[0, ""], relativePC]];
CheckOne: PROC[fun: FunHandle] RETURNS[--stop-- BOOLEAN] =
{
info: FunInfo ¬ GetFunInfo[fun];
funBPI: BracketPair ¬ GetFunBrackets[fun];
RETURN[FALSE];
};
IO.PutF[cmd.out, "\Nchecking Module brackets for %g embedding at relative pc = %g\N\N", IO.rope[PFS.RopeFromPath[path]], IO.card[relativePC]];
GenFuns[module, CheckOne];
};
};
Random test FindStabRange
This procedure can be used to compare the mechanism embedded in the load state with our Alternative find routine in this module.
<<RandomCompareFindStabRange: PUBLIC PROC[firstSeed: CARD, whole: Parsed, otherFind: PROC[relativePC: CARD] RETURNS[StabRange], out: IO.STREAM, totalToTest: CARD] =
{
nextSeed: INT ¬ firstSeed;
tested: CARD ¬ 0;
errors: CARD ¬ 0;
start: BasicTime.GMT ¬ BasicTime.Now[];
WHILE tested < totalToTest DO
rs: Random.RandomStream ¬ Random.Create[seed: nextSeed];
IO.PutF[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];
range1: StabRange ¬ AlternativeFindStabRange[whole, pc];
range2: StabRange ¬ otherFind[pc];
IO.PutF[out, "\Tpc = %g gives firstX: %g count: %g\N", IO.card[pc], IO.card[range1.firstX], IO.card[range1.count]];
IF range1.firstX # range2.firstX OR range1.count # range2.count THEN
{IO.PutF[out, "ERROR: otherFind gives firstX: %g count: %g\N", IO.card[range2.firstX], IO.card[range2.count]]; errors ¬ errors+1};
tested ¬ tested+1;
ENDLOOP;
IO.PutF[out, "after %g cumulative tests, we have %g errors\N", IO.card[tested], IO.card[errors]];
ENDLOOP;
};>>
Errors
UnreadableObjectFile: PUBLIC ERROR[msg: ROPE] = CODE;
RaiseUnreadableObjectFileForFun: PUBLIC PROC[msg: ROPE, module: Module, fun: Stab] =
{
funName: ROPE ¬ IF fun = NIL THEN NIL ELSE CNameOfStab[fun];
UnreadableObjectFile[IO.PutFR["%g in function: %g of %g", [rope[msg]], [rope[funName]], [rope[DescribeModule[module]]] ]];
};
RaiseUnreadableObjectFileForModule: PROC[msg: ROPE, stabX: CARD, module: Module] =
{
UnreadableObjectFile[IO.PutFR["%g at stabX: %g in %g", [rope[msg]], [cardinal[stabX]], [rope[DescribeModule[module]]] ]];
};
DescribeModule: PUBLIC PROC [module: Module] RETURNS [ROPE] ~ {
moduleName: PATH ~ IF module = NIL
THEN PFSNames.EmptyPath
ELSE module.fileName;
wholeName: PATH ~ IF module = NIL OR module.whole = NIL OR module.whole.file = NIL
THEN PFSNames.EmptyPath
ELSE SystemInterface.GetNameOfFile[module.whole.file];
RETURN IO.PutFR["module: %g of objectFile: %g", [rope[PFS.RopeFromPath[moduleName]]], [rope[PFS.RopeFromPath[wholeName]]] ]};
Main code
Commander.Register["PrintModule", PrintModule, "usage:\N\TPrintModule name relPC [format]\N\Twhere\N\T\Tname is file name of the containing whole object file\N\T\TrelPC is a containingWhole-relative PC within the module\N\T\Tformat is XCOFF or SunADotOut"];
Commander.Register["ShowModuleFunctions", ShowModuleFunctions, "usage:\N\TShowModuleFunctions name relPC [format]\N\Twhere\N\T\Tname is file name of the containing whole object file\N\T\TrelPC is a containingWhole-relative PC within the module\N\T\Tformat is XCOFF or SunADotOut"];
Commander.Register["ShowOneModuleFunction", ShowOneModuleFunction, "usage:\N\TShowOneModuleFunction name relPC [format]\N\Twhere\N\T\Tname is file name of the containing whole object file\N\T\TrelPC is a containingWhole-relative PC within the module\N\T\Tformat is XCOFF or SunADotOut"];
Commander.Register["ShowModuleGlobalStabs", ShowModuleGlobalStabs, "usage:\N\TShowModuleGlobalStabs name relPC [format]\N\Twhere\N\T\Tname is file name of containing whole object file\N\T\TrelPC is a containingWhole-relative PC within the module\N\T\Tformat is XCOFF or SunADotOut"];
Commander.Register["CheckModuleBrackets", CheckModuleBrackets, "usage:\N\TCheckModuleBrackets name relPC [format]\N\Twhere\N\T\Tname is file name of containing whole object file\N\T\TrelPC is a containingWhole-relative PC within the module\N\T\Tformat is XCOFF or SunADotOut"];
}..