XCOFFFiles.mesa
Copyright Ó 1990, 1991, 1992 by Xerox Corporation. All rights reserved.
Modified from DotOAccessImpl.mesa
Laurie Horton, June 24, 1992 6:07 pm PDT
Philip James, February 24, 1992 11:40 am PST
Last tweaked by Mike Spreitzer October 2, 1992 1:27 pm PDT
Chauser, March 24, 1992 5:25 pm PST
Katsuyuki Komatsu June 21, 1992 1:55 pm PDT
DIRECTORY
Basics USING [BITAND, BITOR, Card16FromH, Card32FromF, Comparison, FWORD, HWORD],
BasicTime USING [GMT, Now],
CCTypes USING[CCError, CCErrorCase],
CirioTypes USING [zeroBA],
Commander USING [Register, CommandProc],
CommanderOps USING [ArgumentVector, Parse],
Convert USING[IntFromRope, RopeFromChar],
IO USING[card, EndOf, GetChar, GetFWord, GetHWord, PeekChar, PutF, PutFR, SetIndex, STREAM, time, UnsafeGetBlock],
List USING[LORA, CompareProc, Sort],
ObjectFiles USING [BracketNest, BracketPairKind, CNameOfStab, CreateParsed, FileSegmentPC, GlobalVarLoc, MakeUnknownVarLoc, RopeForStabType, SimpleSeg, Stab, StabBody, StabType, unspecdBitSize, VarLoc, VarLocBody, VersionStampInfo],
ObjectFilesPrivate USING [AlterFunStabType, BracketPair, BracketPairBody, CheckStaticVar, FunStabInfo, FunStabSet, GetSPOffsetType, GetTypeRefProcType, HeaderBody, InstallStaticVarsType, LineNumToPCMapBody, MemorySegmentInfo, Module, ModuleBody, ModuleFromParsedAndPCProcType, ObjectFileFlavorBody, Parsed, ParsedBody, PCtoLineNumMapBody, RaiseUnreadableObjectFileForFun, ReadHeaderProcType, ReadInitialDataAsRope, ReadStab, ReadStabRope, RegisterObjectFileFlavor, ScanSymbolStabs, SLineData, StabList, StabRange, StabSet, TranslationTable, TranslationTableBody, VarLocFromStabProcType],
PFS USING [PathFromRope, RopeFromPath, StreamFromOpenFile, OpenFileFromStream],
PFSNames USING [PATH],
Random USING [ChooseInt, Create, RandomStream],
RCTW USING [GetTokenRope, RCTWData, RCTWDataBody],
Rope USING[Concat, Equal, Fetch, FromChar, Index, IsEmpty, Length, Match, ROPE],
SystemInterface USING[CirioFile, CloseFileSet, CreateFileSet, FileSet, GetCirioFile, GetStreamForFile, ReleaseStreamForFile];
XCOFFFiles: CEDAR MONITOR
IMPORTS Basics, BasicTime, CCTypes, Commander, CommanderOps, Convert, IO, List, ObjectFiles, ObjectFilesPrivate, PFS, Random, RCTW, Rope, SystemInterface
EXPORTS ObjectFiles
= BEGIN
CCError: ERROR[case: CCTypes.CCErrorCase, msg: Rope.ROPENIL] ← CCTypes.CCError;
Useful types
ROPE: TYPE ~ Rope.ROPE;
PATH: TYPE ~ PFSNames.PATH;
Following types are defined in ObjectFiles
Stab: TYPE = ObjectFiles.Stab;
StabBody: TYPE = ObjectFiles.StabBody;
StabType: TYPE = ObjectFiles.StabType;
FileSegmentPC: TYPE = ObjectFiles.FileSegmentPC;
VersionStampInfo: TYPE = ObjectFiles.VersionStampInfo;
some Stab types
Block: BYTE = 64H;
Brac: BYTE = 65H;
LBrac: BYTE = 00H;
RBrac: BYTE = 00H;
Fun: BYTE = 8eH;
SLine: BYTE = 00H;
STSym: BYTE = 85H;
LCSym: BYTE = 00H;
Decl: BYTE = 8cH;
GSym: BYTE = 80H;
PSym: BYTE = 82H;
LSym: BYTE = 81H;
RSym: BYTE = 83H;
SO: BYTE = 67H;
HidEnt: BYTE = 6bH;
interesting symbol type
XTYSD: BYTE = 1; -- CSECT Section Definition --
interesting storage mapping class
XMCRW: BYTE = 5; -- Read Write Data --
Following types are defined in ObjectFilesPrivate
Parsed: TYPE = REF ParsedBody;
ParsedBody: PUBLIC TYPE = ObjectFilesPrivate.ParsedBody;
Header: TYPE = REF HeaderBody;
HeaderBody: PUBLIC TYPE = ObjectFilesPrivate.HeaderBody;
Module: TYPE = REF ModuleBody;
ModuleBody: PUBLIC TYPE = ObjectFilesPrivate.ModuleBody;
StabList: TYPE = ObjectFilesPrivate.StabList;
StabSet: TYPE = ObjectFilesPrivate.StabSet;
StabRange: TYPE = ObjectFilesPrivate.StabRange;
MemorySegmentInfo: TYPE ~ ObjectFilesPrivate.MemorySegmentInfo;
Following values are of global interest
unspecdBitSize: CARD ~ ObjectFiles.unspecdBitSize;
pj theContStab: Stab ~ [edo: NIL, stabX: CARD.LAST, stringX: CARD32.LAST, type: BYTE.LAST, other: BYTE.LAST, desc: CARD16.LAST, value: CARD32.LAST, rope: "don't look at me, I'm a continuation of an earlier stab", dx: CARD.LAST];
This stab is put in edo.stabs[i] wherever continuation stabs would appear.
DotOCookies
The following computations are adapted from /usr/include/sys/exec.h,
obtained by: REdit cartiff -f /usr/include/sys/exec.h
MOldsun2: CARD = 0;
M68010: CARD = 1;
M68020: CARD = 2;
MSparc: CARD = 3;
OMagic: CARD16 = 0407B;
NMagic: CARD16 = 0410B;
ZMagic: CARD16 = 0413B;
The following computations are adapted from /usr/include/sun4/a.out.h,
obtained by: REdit cartiff -f /usr/include/sun4/a.out.h
PageSize: CARD32 = 02000H;
OldPageSize: CARD32 = 00800H;
The following symbols are adapted from /usr/include/filehdr.h
FRelFlg: CARD16 = 0001H;
FExec: CARD16 = 0002H;
FLnNo: CARD16 = 0004H;
FLSyms: CARD16 = 0008H;
FAr16WR: CARD16 = 0080H;
FAr32WR: CARD16 = 0100H;
FAr32W: CARD16 = 0200H;
FDynLoad: CARD16 = 1000H;
FShrObj: CARD16 = 2000H;
FLdMinusR: CARD16 = 8000H;
The following record type was constructed based on page 1339 of the Sun Release 4.0 documentation (A.OUT documentation)
WireHeader: TYPE = REF WireHeaderBody;
WireHeaderBody: TYPE = MACHINE DEPENDENT RECORD[
magic: Basics.HWORD,
numSections: Basics.HWORD,
timeDate: Basics.FWORD,
symPtr: Basics.FWORD,
numSyms: Basics.FWORD,
sizeOptHdr: Basics.HWORD,
flags: Basics.HWORD];
SectionList: TYPE = REF SectionListBody;
SectionListBody: TYPE = RECORD[
list: SEQUENCE length: CARD OF SectionHeader];
SectionHeader: TYPE = REF SectionHeaderBody;
SectionHeaderBody: TYPE = MACHINE DEPENDENT RECORD[
name: Rope.ROPENIL,
pAddr: CARD32,
vAddr: CARD32,
size: CARD32,
dataPtr: CARD32,
relocPtr: CARD32,
lnnoPtr: CARD32,
numRelocEntries: CARD16,
numLnnoEntries: CARD16,
flags: CARD32];
WireSectionHeader: TYPE = REF WireSectionHeaderBody;
WireSectionHeaderBody: TYPE = MACHINE DEPENDENT RECORD[
name: PACKED ARRAY [0..7] OF BYTE,
pAddr: Basics.FWORD,
vAddr: Basics.FWORD,
size: Basics.FWORD,
dataPtr: Basics.FWORD,
relocPtr: Basics.FWORD,
lnnoPtr: Basics.FWORD,
numRelocEntries: Basics.HWORD,
numLnnoEntries: Basics.HWORD,
flags: Basics.FWORD];
WireAuxHeader: TYPE = REF WireAuxHeaderBody;
WireAuxHeaderBody: TYPE = MACHINE DEPENDENT RECORD[
magic: Basics.HWORD,
vStamp: Basics.HWORD,
textSize: Basics.FWORD,
dataSize: Basics.FWORD,
bssSize: Basics.FWORD,
entryPtr: Basics.FWORD,
textBase: Basics.FWORD,
dataBase: Basics.FWORD,
tocAddress: Basics.FWORD,
entryPtSectionNum: Basics.HWORD,
textSectionNum: Basics.HWORD,
dataSectionNum: Basics.HWORD,
tocSectionNum: Basics.HWORD,
loaderSectionNum: Basics.HWORD,
bssSectionNum: Basics.HWORD,
maxAlignmentForText: Basics.HWORD,
maxAlignmentForData: Basics.HWORD,
moduleTypeField: ARRAY [0..1] OF BYTE,
reservedField: ARRAY [0..1] OF BYTE,
maxStackSize: Basics.FWORD,
reservedFields: ARRAY [0..3] OF Basics.FWORD
];
ByteEntry: TYPE = REF ByteEntryBody;
ByteEntryBody: TYPE = MACHINE DEPENDENT RECORD[
val(0:0..7): BYTE];
TwoByteEntry: TYPE = REF TwoByteEntryBody;
TwoByteEntryBody: TYPE = MACHINE DEPENDENT RECORD[
val(0:0..15): Basics.HWORD];
StabSize: CARD ← 18;
WireSTEntry: TYPE = REF WireSTEntryBody;
WireSTEntryBody: TYPE = MACHINE DEPENDENT RECORD[
name(0: 0..63): PACKED ARRAY [0..7] OF BYTE,
value(0: 64..95): Basics.FWORD,
sectionNumber(0: 96..111): Basics.HWORD,
derivedType(0: 112..119): BYTE,
type(0: 120..127): BYTE,
storageClass(0: 128..135): BYTE,
numAuxEntries(0: 136..143): BYTE,
pad(0: 144..159): Basics.HWORD
];
AuxType: TYPE = {symbol, file, csect};
WireSTAuxEntry: TYPE = REF WireSTAuxEntryBody;
WireSTAuxEntryBody: TYPE = MACHINE DEPENDENT RECORD[
aux(0: 0..159): SELECT OVERLAID AuxType FROM
symbol => [
name(0: 0..63): PACKED ARRAY [0..7] OF BYTE,
pad(0: 64..159): Basics.HWORD],
file => [
fileName(0: 0..111): PACKED ARRAY [0..13] OF BYTE,
pad(0: 112..159): Basics.HWORD],
csect => [
csectLength(0: 0..31): Basics.FWORD,
parameterType(0: 32..63): Basics.FWORD,
snParameterType(0: 64..79): Basics.HWORD,
alignment(0: 80..84): [0..31],
symbolType(0: 85..87): [0..7],
storageMappingClass(0: 88..95): BYTE,
stab(0: 96..127): Basics.FWORD,
snStab(0: 128..143): Basics.HWORD,
pad(0: 144..159): Basics.HWORD]
ENDCASE];
ReadHeader: ObjectFilesPrivate.ReadHeaderProcType ~ TRUSTED {
PROC[stream: IO.STREAM] RETURNS[Header] = TRUSTED {
wHeader: WireHeader ← NEW[WireHeaderBody];
wAuxHeader: WireAuxHeader ← NEW[WireAuxHeaderBody];
nBytes: INTIO.UnsafeGetBlock[stream, [LOOPHOLE[@wHeader^], 0, BYTES[WireHeaderBody]]];
nBytes2: INTIO.UnsafeGetBlock[stream, [LOOPHOLE[@wAuxHeader^], 0, Basics.Card16FromH[wHeader.sizeOptHdr]]];
magic: CARD ← Basics.Card16FromH[wHeader.magic];
nBadMagic: BOOLEAN ← (magic # OMagic) AND (magic # NMagic)AND (magic # ZMagic);
flags: CARD ← Basics.Card16FromH[wHeader.flags];
The following computations are adapted from /usr/include/sun4/a.out.h,
obtained by: REdit cartiff -f /usr/include/sun4/a.out.h
nPageSize: CARD ← 01000H;
IF wHeader.machtype = MOldsun2 THEN OldPageSize ELSE PageSize;
SymbolSize: CARD ← 18;
RelocEntrySize: CARD ← 10;
LnnoEntrySize: CARD ← 6;
textOffset: CARD ← Basics.Card32FromF[wAuxHeader.textBase];
IF wHeader.machtype = MOldsun2 THEN
(IF magic = ZMagic THEN nPageSize ELSE BYTES[WireHeaderBody])
ELSE
(IF magic = ZMagic THEN 0 ELSE BYTES[WireHeaderBody]);
textSize: CARD ← Basics.Card32FromF[wAuxHeader.textSize];
iDataSeg: MemorySegmentInfo ← [Basics.Card32FromF[wAuxHeader.dataBase], Basics.Card32FromF[wAuxHeader.dataSize]];
iDataSeg: SegmentInfo ← [textOffset + textSize, Basics.Card32FromF[wHeader.iDataSize]];
symsSeg: MemorySegmentInfo ← [Basics.Card32FromF[wHeader.symPtr], Basics.Card32FromF[wHeader.numSyms] * SymbolSize];
dataRelocSeg.byteOffset+dataRelocSeg.byteLength, Basics.Card32FromF[wHeader.symsSize]];
debugSeg: MemorySegmentInfo ← [Basics.Card32FromF[wHeader.symPtr], Basics.Card32FromF[wHeader.numSyms] * SymbolSize];
textLoadOffset: CARD32 ← 0;
IF magic = ZMagic THEN 02000H ELSE 0;
text segment is loaded into memory at nominal 0 + textLoadOffset
the 02000H is the largest page size on certain machines, and allows the first page of memory to remain unmapped.
linnoSeg, debugSeg, textRelocSeg, dataRelocSeg: MemorySegmentInfo ← [0, 0];
iDataSeg.byteOffset+iDataSeg.byteLength, Basics.Card32FromF[wHeader.trSize]];
textRelocSeg.byteOffset+textRelocSeg.byteLength, Basics.Card32FromF[wHeader.drSize]]
numSects: CARD ← Basics.Card16FromH[wHeader.numSections];
sectionList: SectionList ← NEW[SectionListBody[numSects]];
debugIndex, textIndex, dataIndex: CARDLAST[CARD];
header: Header;
FOR i: CARD IN [0..numSects) DO
wSectionHeader: WireSectionHeader ← NEW[WireSectionHeaderBody];
nBytes: INTIO.UnsafeGetBlock[stream, [LOOPHOLE[@wSectionHeader^], 0, BYTES[WireSectionHeaderBody]]];
sectionList[i] ← NEW[SectionHeaderBody];
FOR j: CARD IN [0..8) DO
c: CARD ← wSectionHeader.name[j];
ch: CHARVAL[c];
IF c = 0 THEN EXIT;
sectionList[i].name ← sectionList[i].name.Concat[Convert.RopeFromChar[from: ch, quote: FALSE]];
ENDLOOP;
sectionList[i].pAddr ← Basics.Card32FromF[wSectionHeader.pAddr];
sectionList[i].vAddr ← Basics.Card32FromF[wSectionHeader.vAddr];
sectionList[i].size ← Basics.Card32FromF[wSectionHeader.size];
sectionList[i].dataPtr ← Basics.Card32FromF[wSectionHeader.dataPtr];
sectionList[i].relocPtr ← Basics.Card32FromF[wSectionHeader.relocPtr];
sectionList[i].lnnoPtr ← Basics.Card32FromF[wSectionHeader.lnnoPtr];
sectionList[i].numRelocEntries ← Basics.Card16FromH[wSectionHeader.numRelocEntries];
sectionList[i].numLnnoEntries ← Basics.Card16FromH[wSectionHeader.numLnnoEntries];
sectionList[i].flags ← Basics.Card32FromF[wSectionHeader.flags];
ENDLOOP;
FOR i: CARD IN [0..numSects) DO
IF Rope.Equal[sectionList[i].name, ".debug", TRUE] THEN debugIndex ← i;
IF Rope.Equal[sectionList[i].name, ".text", TRUE] THEN textIndex ← i;
IF Rope.Equal[sectionList[i].name, ".data", TRUE] THEN dataIndex ← i;
IF Rope.Equal[sectionList[i].name, ".loader", TRUE] THEN flags ← Basics.BITOR[flags, FLdMinusR];
ENDLOOP;
IF debugIndex # LAST[CARD] THEN {
debugSeg ← [sectionList[debugIndex].dataPtr, sectionList[debugIndex].size];
};
IF textIndex # LAST[CARD] THEN {
linnoSeg ← [sectionList[textIndex].lnnoPtr, sectionList[textIndex].numLnnoEntries * LnnoEntrySize];
textOffset ← sectionList[textIndex].dataPtr;
textRelocSeg ← [sectionList[textIndex].relocPtr, sectionList[textIndex].numRelocEntries * RelocEntrySize];
};
IF dataIndex # LAST[CARD] THEN {
iDataSeg.byteOffset ← sectionList[dataIndex].dataPtr;
dataRelocSeg ← [sectionList[dataIndex].relocPtr, sectionList[dataIndex].numRelocEntries * RelocEntrySize];
};
header ← NEW[HeaderBody ← [
dynamic: FALSE,
toolversion: BYTE[0],
machtype: BYTE[0],
magic: magic,
flags: flags,
text: [textOffset, textSize],
iData: iDataSeg,
textReloc: textRelocSeg,
dataReloc: dataRelocSeg,
syms: symsSeg,
debug: debugSeg,
linno: linnoSeg,
textLoadOffset: textLoadOffset,
bssSize: Basics.Card32FromF[wAuxHeader.bssSize],
entryPoint: Basics.Card32FromF[wAuxHeader.entryPtr],
nPageSize: nPageSize,
nEntries: Basics.Card32FromF[wHeader.numSyms],
stringOffset: symsSeg.byteOffset+symsSeg.byteLength
]];
RETURN[header];
};
EmbeddedDotOs
We assume that there are dbx stabs located in the dotO in the symbol table entry range [firstX..limitX).
We assume that the first stab is a SO stab whose value is the next module symbol table entry of the embedded dotO. We assume that this stab is a SO stab containing the file name.
The first and limitX have presumably been obtained by other means. For example, at this writing the CirioNub will provide access to a table of pc ranges located in the target world. (In addition, we provide a rather crude mechanism below to use when the CirioNub approach is not available. (e.g., when examining a file that is not part of a PCedar load state.)
We are unable to define a legitimate limitPC. We assume that when, later, an attempt is made to look up a pc in the bracket structure, we have already determined that that pc definitely belongs to this EmbeddedDotO. Thus, we can use header.textSize as a (bogus) limitPC.
XCOFFModuleFromParsedAndPC: ObjectFilesPrivate.ModuleFromParsedAndPCProcType ~ {
PROC[whole: Parsed, spc: FileSegmentPC, moduleRope: ROPENIL] RETURNS[Module] = {
IF whole = NIL THEN RETURN[NIL] ELSE
{
stabRange, pcRange: StabRange;
firstX, limitX: CARD;
stream: IO.STREAM;
[stabRange, pcRange] ← AlternativeFindModuleStabRange[whole, spc.relPC, moduleRope];
IF stabRange.count = 0 THEN RETURN[NIL];
firstX ← stabRange.first;
limitX ← stabRange.first+stabRange.count;
stream ← SystemInterface.GetStreamForFile[whole.file];
nest so that we can catch unwinds and release the stream
{
ENABLE UNWIND => {
SystemInterface.ReleaseStreamForFile[whole.file, stream];
};
firstSO: Stab;
firstPC: CARD32 ← pcRange.first;
lag: LIST OF Module ← NIL;
[firstSO, ] ← BasicReadStab[whole, stream, firstX];
first, see if we already have it
FOR modules: LIST OF Module ← whole.modules, modules.rest WHILE modules # NIL DO
IF modules.first.firstPC = firstPC THEN -- we already have it
{
SystemInterface.ReleaseStreamForFile[whole.file, stream];
RETURN[modules.first]
};
IF firstPC < modules.first.firstPC THEN -- we should have found it by now
{
newModule: Module ← CreateModule[whole, stream, stabRange, pcRange];
cell: LIST OF Module ← LIST[newModule];
cell.rest ← modules;
IF lag # NIL THEN lag.rest ← cell ELSE whole.modules ← cell;
SystemInterface.ReleaseStreamForFile[whole.file, stream];
RETURN[newModule];
};
lag ← modules;
ENDLOOP;
{
newModule: Module ← CreateModule[whole, stream, stabRange, pcRange];
cell: LIST OF Module ← LIST[newModule];
IF lag # NIL THEN lag.rest ← cell ELSE whole.modules ← cell;
SystemInterface.ReleaseStreamForFile[whole.file, stream];
RETURN[newModule];
};
};
};
};
CreateModule: PROC[parsed: Parsed, stream: IO.STREAM, stabRange, pcRange: StabRange ← [0, 0]] RETURNS[Module] = {
module: Module ← NEW[ModuleBody];
rctw: RCTW.RCTWData ← NEW[RCTW.RCTWDataBody];
sizes: REF KnownSizeList ← NEW[KnownSizeList ← CopyList[KnownSizes]];
firstSO: Stab;
nStabs: CARD;
[firstSO, ] ← BasicReadStab[parsed, stream, stabRange.first, sizes];
nStabs ← stabRange.count;
prepare the embeddedDotO for a call on BuildBrackets
module.whole ← parsed;
module.firstStabX ← 0;
module.limitStabX ← stabRange.count;
module.firstPC ← pcRange.first; -- firstSO.value;
module.limitPC ← parsed.header.text.byteLength; -- (bogus, but it will suffice for reasons given earlier)
lets read the all stabs now (without their ropes)
(I did some timing tests on a Dorado for RopeImpl. It took about 6 seconds to perform this loop, including the allocates. On the other hand, it takes about 4 seconds to spin through the entries without allocating the stab records. So, since at some point I have to traverse all the entries to find the Fun stabs, I might as well allocate them.)
(we also relocate the pc value of bracket stabs.)
module.stabs ← NEW[StabSet[nStabs]];
rctw.module ← module;
{I: CARD ← 0;
current: CARD ← 0;
tmp: REF StabSet ← NIL;
auxCount: CARD ← 0;
funFlag: BOOLEANFALSE;
WHILE I<nStabs DO
stab: Stab;
nAux: CARD;
[module.stabs[current], nAux] ← BasicReadStab[parsed, stream, stabRange.first+I, sizes];
stab ← module.stabs[current];
stab.module ← module;
IF funFlag THEN {
module.stabs[current - 1].rope ← stab.rope;
funFlag ← FALSE;
};
IF stab.stabType = Fun THEN
funFlag ← TRUE;
IF stab.stabType = LBrac OR stab.stabType = RBrac THEN
stab.value ← stab.value + module.firstPC;
stab.stabX ← stab.stabX - auxCount - stabRange.first + GlobalPreDefinedTypes.length;
current ← current+1;
I ← I+1+nAux;
auxCount ← auxCount + nAux;
ENDLOOP;
tmp ← module.stabs;
module.stabs ← NEW[StabSet[current + 30]];
FOR I IN [0..current) DO
module.stabs[I+GlobalPreDefinedTypes.length] ← tmp[I];
ENDLOOP;
FOR I IN [0..GlobalPreDefinedTypes.length) DO
module.stabs[I] ← NEW[StabBody ← [
module: module,
stabX: I,
stabType: LSym,
size: 0,
value: 0,
rope: GlobalPreDefinedTypes[I]
]];
ENDLOOP;
module.limitStabX ← module.stabs[current-1].stabX + GlobalPreDefinedTypes.length + 1;
nStabs ← current;
};
now lets finish up
module.firstSO ← module.stabs[0];
module.secondSO ← IF module.stabs[module.stabs[0].dx].stabType = SO THEN module.stabs[module.stabs[0].dx] ELSE NIL;
module.fileName ← PFS.PathFromRope[IF module.secondSO = NIL THEN ReadStabRope[module.firstSO] ELSE ReadStabRope[module.secondSO]];
module.fileName ← PFS.PathFromRope[ObjectFilesPrivate.ReadStabRope[module.stabs[0]]];
module.staticVarsInstalled ← FALSE;
module.versionStampInfo ← NIL;
module.outerBracket ← NEW[BracketPairBody←[
module: module,
kind: syntheticOuter,
firstX: 0,
limitX: module.whole.header.nEntries,
firstPC: module.firstPC,
pcLimit: module.limitPC,
funStab: NIL,
funIndex: 0,
symbols: NIL,
innerBrackets: NIL]];
InstallPCLineNumMaps[module, stabRange];
RETURN[module];
};
Finding Embedded dotOs
StabIndexTypeAndValue: TYPE = RECORD[stabX, stabXTo: CARD, stabType: StabType, value: CARD32];
NullStabIndexTypeAndValue: StabIndexTypeAndValue = [0, 0, Invalid, 0];
This procedure should only be used when examining a dotO which is not in the load state. If we are given a pc in a running PCR, it is far faster to use procedures in LoadStateAccess to obtain the stab range for an embedded dot O. Those procedures use a table available from the nub to find the stab range, then produce the embedded dotO through a call on CreateEmbeddedDotO.
AlternativeFindModuleStabRange: PROC[whole: ObjectFilesPrivate.Parsed, relativePC: CARD, moduleRope: Rope.ROPENIL] RETURNS[sr: StabRange, pcRange: StabRange ← [0, 0]] = {
stream: IO.STREAMNIL;
IF whole = NIL THEN RETURN[[0, 0]] ELSE {
ENABLE UNWIND => SystemInterface.ReleaseStreamForFile[whole.file, stream];
stream ← SystemInterface.GetStreamForFile[whole.file];
[sr, pcRange] ← AlternativeFindModuleStabRangeInner[whole, relativePC, moduleRope, stream];
SystemInterface.ReleaseStreamForFile[whole.file, stream];
};
};
LastStabX: CARD = 0ffffffffH;
AlternativeFindModuleStabRangeInner: PROC[whole: ObjectFilesPrivate.Parsed, relativePC: CARD, moduleRope: Rope.ROPE, stream: IO.STREAM] RETURNS[sr: StabRange, pcRange: StabRange ← [0, 0]] = {
firstStabX: CARD ← 0;
checkWholeFile: BOOL ← moduleRope.IsEmpty;
pattern: Rope.ROPE ← Rope.Concat[moduleRope, ".*"];
baseSO: Stab ← BasicReadStab[whole, stream, firstStabX].stab;
baseSO.value points the next baseSO on AIX.
nextStabX: CARDIF baseSO.value = 0 OR baseSO.value = LastStabX THEN whole.stabLimit ELSE baseSO.value;
name: Rope.ROPE ← baseSO.rope;
IF baseSO.stabType # SO THEN RETURN[[0, 0]];
WHILE firstStabX < whole.stabLimit DO
maxPC: CARD ← 0;
minPC: CARDLAST[CARD];
highDbx: CARD ← baseSO.stabX;
IF checkWholeFile OR (~name.IsEmpty AND Rope.Match[pattern, name]) THEN {
FOR x: CARD ← baseSO.stabX, highDbx WHILE x < nextStabX DO
info: StabIndexTypeAndValue ← CheckStab[whole, stream, x];
IF info.stabType = Fun OR info.stabType = SLine OR info.stabType = LBrac OR info.stabType = RBrac -- OR info.stabType = SO -- THEN {
maxPC ← MAX[maxPC, info.value];
minPC ← MIN[minPC, info.value];
};
highDbx ← info.stabXTo + 1;
ENDLOOP;
IF minPC <= relativePC AND relativePC <= maxPC THEN
RETURN[[baseSO.stabX, nextStabX-baseSO.stabX], [minPC, maxPC]];
};
firstStabX ← nextStabX;
baseSO ← BasicReadStab[whole, stream, firstStabX].stab;
baseSO.value points the next baseSO on AIX.
nextStabX ← IF baseSO.value = 0 OR baseSO.value = LastStabX THEN whole.stabLimit ELSE baseSO.value;
name ← baseSO.rope;
ENDLOOP;
SystemInterface.ReleaseStreamForFile[whole.file, stream];
RETURN[[0, 0]];
};
CheckStab: ENTRY PROC[whole: Parsed, stream: IO.STREAM, stabX: CARD] RETURNS[StabIndexTypeAndValue] = {
ENABLE UNWIND => NULL;
rope: Rope.ROPENIL;
GetRope: PROC [] RETURNS [rope: Rope.ROPE] ~ {
IF stream#previousStream THEN {
cloneStream←PFS.StreamFromOpenFile[openFile~PFS.OpenFileFromStream[stream]];
previousStream ← stream;
};
rope ← RopeFromData[whole, stream, wireStabBuffer.name, wireStabBuffer.sectionNumber, wireStabBuffer.storageClass, IF wireStabBuffer.numAuxEntries = 0 THEN wireStabBuffer.name ELSE wsAux.name];
};
GetSymTypeAndClass: PROC [] RETURNS [symType, class: CARD] ~ {
RETURN[wsAux.symbolType, wsAux.storageMappingClass]
};
IO.SetIndex[stream, whole.header.syms.byteOffset+stabX*StabSize];
TRUSTED{[] ← IO.UnsafeGetBlock[stream, [LOOPHOLE[@wireStabBuffer^], 0, StabSize]]};
IF wireStabBuffer.numAuxEntries # 0 THEN {
TRUSTED{[] ← IO.UnsafeGetBlock[stream, [LOOPHOLE[@wsAux^], 0, StabSize]]};
};
RETURN[[stabX, stabX + wireStabBuffer.numAuxEntries, TypeFromData[wireStabBuffer.storageClass, GetRope, GetSymTypeAndClass, wireStabBuffer.type], Basics.Card32FromF[wireStabBuffer.value]]];
};
XCOFFVarLocFromStab: ObjectFilesPrivate.VarLocFromStabProcType ~ {
PROC [stab: Stab] RETURNS [DotOAccess.VarLoc] ~ {
header: Header ~ stab.module.whole.header;
MkSegment: PROC [kind: ObjectFiles.SimpleSeg] RETURNS [ObjectFiles.VarLoc] ~ {
segRope:Rope.ROPE;
segBase: CARD ~ SELECT kind FROM
text => 0,
data => header.iData.byteOffset, --header.text.byteLength,--
bss => header.textReloc.byteOffset, --header.text.byteLength + header.iData.byteLength,--
ENDCASE => ERROR;
PJames 1/31/92: Doesn't look appropriate for XCoff...stab.value has a different type of value...
IF stab.value < segBase THEN ObjectFiles.UnreadableObjectFile[IO.PutFR["value (%xH) less than segment base (%xH) for symbol %g in %g", [cardinal[stab.value]], [cardinal[segBase]], [rope[ObjectFiles.CNameOfStab[stab]]], [rope[ObjectFiles.DescribeModule[stab.module]]] ]];
SELECT kind FROM
text => segRope ← "text";
data => segRope ← "data";
bss => segRope ← "bss";
ENDCASE => ERROR;
RETURN [NEW [ObjectFiles.VarLocBody ← [
bitSize: IF stab.size#LAST[CARD] AND stab.size#0 THEN stab.size*8 ELSE unspecdBitSize,
where: fSegment[
[0, segRope],
(stab.value-- - segBase--)*8,
stab.value + segBase] ]]]};
SELECT stab.stabType FROM
Fun => RETURN MkSegment[text];
STSym => RETURN MkSegment[data];
LCSym => RETURN MkSegment[bss];
LSym, PSym => {
byteOffset: INT ~ LOOPHOLE[stab.value];
vLB: ObjectFiles.VarLoc;
IF stab.stabType=PSym AND stab.size>4 AND stab.size#LAST[CARD]
THEN vLB ← NEW[ObjectFiles.VarLocBody ← [
bitSize: stab.size*8,
where: indirect[
base: NEW[ObjectFiles.VarLocBody ← [
bitSize: 32,
where: frame[bitOffset: 8*byteOffset]]],
offset: CirioTypes.zeroBA]]]
ELSE vLB ← NEW[ObjectFiles.VarLocBody ← [
bitSize: IF stab.size#LAST[CARD] AND stab.size#0 THEN stab.size*8 ELSE unspecdBitSize,
where: frame[bitOffset: 8*byteOffset]]];
RETURN[vLB]};
RSym => {
vLB: ObjectFiles.VarLoc;
IF stab.size>4 AND stab.size#LAST[CARD]
THEN vLB ← NEW[ObjectFiles.VarLocBody ← [
bitSize: stab.size*8,
where: indirect[
base: NEW[ObjectFiles.VarLocBody ← [
bitSize: 32,
where: register[regNum: stab.value]]],
offset: CirioTypes.zeroBA]]]
ELSE vLB ← NEW[ObjectFiles.VarLocBody ← [
bitSize: IF stab.size#LAST[CARD] AND stab.size#0 THEN stab.size*8 ELSE unspecdBitSize,
where: register[regNum: stab.value]]];
RETURN[vLB]
};
GSym => RETURN [NEW [ObjectFiles.VarLocBody ← [
bitSize: IF stab.size#LAST[CARD] AND stab.size#0 THEN stab.size*8 ELSE unspecdBitSize,
where: namedCommon[Rope.Concat["←", ObjectFiles.CNameOfStab[stab]], 0, 0, FALSE, FALSE] ]]];
ENDCASE => RETURN ObjectFiles.MakeUnknownVarLoc[IO.PutFR["unrecognized stabType (%02xH) for %g", [cardinal[stab.size]], [rope[ObjectFiles.CNameOfStab[stab]]] ]];
};
C Line-Number to Relative-PC maps
PCtoLineNumMap: TYPE = REF PCtoLineNumMapBody;
PCtoLineNumMapBody: TYPE = ObjectFilesPrivate.PCtoLineNumMapBody;
sorted by PC
LineNumToPCMap: TYPE = REF LineNumToPCMapBody;
LineNumToPCMapBody: TYPE = ObjectFilesPrivate.LineNumToPCMapBody;
sorted by Line Num
SLineData: TYPE = ObjectFilesPrivate.SLineData;
FMap: TYPE ~ LIST OF FMapData;
FMapBody: TYPE ~ RECORD [
seq: SEQUENCE len: CARD OF FMapData];
FMapData: TYPE ~ REF FMapDataBody;
FMapDataBody: TYPE ~ RECORD[
fName: Rope.ROPE,
stabOffset: CARD];
InstallPCLineNumMaps: PROC[module: Module, stabRange: StabRange] = {
fMap, fMapTmp: FMap ← NIL;
firstStabX: CARD ← stabRange.first;
limitStabX: CARD ← stabRange.first + stabRange.count;
CompareByCLineNum: PROC[ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison] =
BEGIN
info1: REF SLineData ← NARROW[ref1];
info2: REF SLineData ← NARROW[ref2];
IF info1.cLineNum < info2.cLineNum THEN RETURN[less];
IF info1.cLineNum > info2.cLineNum THEN RETURN[greater];
IF info1.parsedRelPC.relPC < info2.parsedRelPC.relPC THEN RETURN[less];
IF info1.parsedRelPC.relPC > info2.parsedRelPC.relPC THEN RETURN[greater];
RETURN[equal];
END;
CompareByPC: PROC[ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison] =
BEGIN
info1: REF SLineData ← NARROW[ref1];
info2: REF SLineData ← NARROW[ref2];
IF info1.parsedRelPC.relPC < info2.parsedRelPC.relPC THEN RETURN[less];
IF info1.parsedRelPC.relPC > info2.parsedRelPC.relPC THEN RETURN[greater];
IF info1.cLineNum < info2.cLineNum THEN RETURN[less];
IF info1.cLineNum > info2.cLineNum THEN RETURN[greater];
RETURN[equal];
END;
CompareBySI: PROC[ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison] =
BEGIN
info1: FMapData ← NARROW[ref1];
info2: FMapData ← NARROW[ref2];
IF info1.stabOffset < info2.stabOffset THEN RETURN[less];
IF info1.stabOffset > info2.stabOffset THEN RETURN[greater];
RETURN[equal];
END;
IF module.pcToLineNum # NIL OR module.lineNumToPC # NIL THEN {
IF module.pcToLineNum = NIL OR module.lineNumToPC = NIL THEN ERROR;
RETURN;
};
we have to create them
{
nItems: INTEGER ← 0;
list1: LIST OF REF ANYNIL; -- LIST OF REF SLineData
list2: LIST OF REF ANYNIL; -- LIST OF REF SLineData
sortedList: LIST OF REF ANYNIL;
linno, relPC, pOffset: CARD ← 0;
inCurrentModule: BOOLEANFALSE;
stream: IO.STREAM ← SystemInterface.GetStreamForFile[module.whole.file];
IO.SetIndex[stream, module.whole.header.linno.byteOffset];
FOR I: CARD IN [0..module.whole.header.linno.byteLength / 6) DO
IO.SetIndex[stream, module.whole.header.linno.byteOffset + I*6];
relPC ← Basics.Card32FromF[IO.GetFWord[stream]];
linno ← Basics.Card16FromH[IO.GetHWord[stream]];
IF linno = 0 THEN
IF relPC <= limitStabX AND relPC >= firstStabX THEN {
inCurrentModule ← TRUE;
IO.SetIndex[stream, module.whole.header.syms.byteOffset + 18 * relPC + 18 * 5 + 4];
pOffset ← Basics.Card16FromH[IO.GetHWord[stream]];
}
ELSE
inCurrentModule ← FALSE
ELSE
IF inCurrentModule THEN {
info: REF SLineData ← NEW[SLineData ← [cLineNum: linno + pOffset - 1, parsedRelPC: [[0 --??--, PFS.RopeFromPath[module.fileName]], relPC]]];
list1 ← CONS[info, list1];
list2 ← CONS[info, list2];
nItems←nItems + 1;
};
ENDLOOP;
SystemInterface.ReleaseStreamForFile[module.whole.file, stream];
FOR I: CARD IN [module.firstStabX..module.limitStabX) DO
stab: Stab ← ReadStab[module, I];
IF stab.stabType = SLine THEN {
info: REF SLineData ← NEW[SLineData←[cLineNum: --pj fill this in! stab.desc-- 0, parsedRelPC: [[2, ".text"], stab.value]]];
list1 ← CONS[info, list1];
list2 ← CONS[info, list2];
nItems ← nItems+1};
ENDLOOP;
sortedList ← List.Sort[list1, CompareByCLineNum];
module.lineNumToPC ← NEW[LineNumToPCMapBody[nItems]];
FOR I: INTEGER IN [0..nItems) DO
info: REF SLineData ← NARROW[sortedList.first];
module.lineNumToPC[I] ← info^;
sortedList ← sortedList.rest;
ENDLOOP;
sortedList ← List.Sort[list2, CompareByPC];
module.pcToLineNum ← NEW[PCtoLineNumMapBody[nItems]];
FOR I: INTEGER IN [0..nItems) DO
info: REF SLineData ← NARROW[sortedList.first];
module.pcToLineNum[I] ← info^;
sortedList ← sortedList.rest;
ENDLOOP;
};
};
Stabs
This routine treats stabX as relative to the entire containing dotO file.
warning: does NOT relocate LBrac or RBrac stabs.
warning: does NOT read the associated rope
Also: the edo field is set to NIL;
BasicReadStab: ENTRY PROC[parsed: Parsed, stream: IO.STREAM, stabX: CARD, sizes: REF KnownSizeList ← NIL] RETURNS[stab: Stab, nAux: CARD ← 0] ~ {
ENABLE UNWIND => NULL;
stab ← NEW[StabBody];
[stab^, nAux] ← ReadStabBody[parsed, stream, stabX, sizes];
RETURN
};
TypeFromData: PROC [data: BYTE, getName: PROC RETURNS [Rope.ROPE], getSymTypeAndClass: PROC RETURNS [symType, class: CARD], typ: BYTE ← 0] RETURNS [stabType: StabType] ~ {
dataVal: CARD ← data;
typeVal: CARD ← typ;
IF typeVal = 20H THEN
stabType ← Fun
ELSE
SELECT dataVal FROM
Block =>
{
name: Rope.ROPE ← getName[];
SELECT TRUE FROM
Rope.Equal[".bb", name] => stabType ← LBrac;
Rope.Equal[".eb", name] => stabType ← RBrac;
ENDCASE => ERROR;
};
Brac =>
{
name: Rope.ROPE ← getName[];
SELECT TRUE FROM
Rope.Equal[".bf", name] => stabType ← LBrac;
Rope.Equal[".ef", name] => stabType ← RBrac;
ENDCASE => ERROR;
};
00000H => SLine; -- no SLine s in XCOFF
Fun => stabType ← Fun; -- gets in the way.
PSym => stabType ← PSym;
LSym => stabType ← LSym;
RSym => stabType ← RSym;
STSym => stabType ← STSym;
00000H => LCSym; -- ?
GSym => stabType ← GSym;
HidEnt =>
{
symType, class: CARD;
[symType, class] ← getSymTypeAndClass[];
IF symType = XTYSD AND class = XMCRW THEN stabType ← Invalid ELSE stabType ← Unspecified;
};
00000H => Main; -- not used?
SO => stabType ← SO;
00000H => BIncl;
00000H => EIncl;
00000H => SOL;
ENDCASE => stabType ← Unspecified
};
DebugStab: CARD = 0fffeH;
previousStream: IO.STREAMNIL;
cloneStream: IO.STREAMNIL;
ReadStabBody: PROC [parsed: Parsed, stream: IO.STREAM, stabX: CARD, sizes: REF KnownSizeList] RETURNS [stab: StabBody, nAux: CARD ← 0] ~ {
size: CARD;
rope: Rope.ROPENIL;
GetRope: PROC [] RETURNS [Rope.ROPE] ~ {
RETURN[rope]
};
GetSymTypeAndClass: PROC [] RETURNS [symType, class: CARD] ~ {
RETURN[wsAux.symbolType, wsAux.storageMappingClass]
};
IO.SetIndex[stream, parsed.header.syms.byteOffset+stabX*StabSize];
TRUSTED{[] ← IO.UnsafeGetBlock[stream, [LOOPHOLE[@wireStabBuffer^], 0, StabSize]]};
IF wireStabBuffer.numAuxEntries # 0 THEN {
TRUSTED{[] ← IO.UnsafeGetBlock[stream, [LOOPHOLE[@wsAux^], 0, StabSize]]};
};
nAux ← wireStabBuffer.numAuxEntries;
IF stream#previousStream THEN {
cloneStream←PFS.StreamFromOpenFile[openFile~PFS.OpenFileFromStream[stream]];
previousStream ← stream;
};
rope ← RopeFromData[parsed, cloneStream, wireStabBuffer.name, wireStabBuffer.sectionNumber, wireStabBuffer.storageClass, IF wireStabBuffer.numAuxEntries = 0 THEN wireStabBuffer.name ELSE wsAux.name];
IF sizes # NIL THEN
[size, sizes^] ← SizeFromName[rope, sizes^]
ELSE
size ← 0;
stab ← [
module: NIL,
stabX: stabX,
stabType: TypeFromData[wireStabBuffer.storageClass, GetRope, GetSymTypeAndClass, wireStabBuffer.type], --wireStabBuffer.type,
value: Basics.Card32FromF[wireStabBuffer.value],
size: size,
rope: rope
];
RETURN};
dataChar: ByteEntry ← NEW[ByteEntryBody];
RopeFromData: PROC [parsed: Parsed, stream: IO.STREAM, data: PACKED ARRAY [0..7] OF BYTE, sn: Basics.HWORD, storeClass: BYTE, auxData: PACKED ARRAY [0..7] OF BYTE] RETURNS [name: Rope.ROPENIL] ~ TRUSTED {
len: TwoByteEntry ← NEW[TwoByteEntryBody];
dbg: BOOLIF Basics.Card16FromH[sn] = DebugStab THEN TRUE ELSE FALSE;
sum, offset: CARD ← 0;
IF dbg AND storeClass = 67H THEN {
data ← auxData;
dbg ← FALSE;
};
offset ← IF dbg THEN parsed.header.debug.byteOffset ELSE parsed.header.stringOffset;
FOR i: CARD IN [0..4) DO
n: CARD ← data[i];
sum ← sum + n;
ENDLOOP;
IF sum = 0 THEN {
FOR i: CARD IN [4..8) DO
n: CARD ← data[i];
sum ← sum * 256 + n;
ENDLOOP;
IO.SetIndex[stream, offset + sum];
IF IO.EndOf[stream] THEN RETURN[""];
DO
c: CHAR ← ' ;
c ← IO.GetChar[stream];
TRUSTED{[] ← IO.UnsafeGetBlock[stream, [LOOPHOLE[@dataChar^], 0, 1]]};
c ← dataChar.val;
IF c = VAL[0] THEN EXIT;
name ← name.Concat[Convert.RopeFromChar[from: c, quote: FALSE]];
ENDLOOP;
}
ELSE
FOR i: CARD IN [0..8) DO
num: CARD ← data[i];
IF num = 0 THEN EXIT;
name ← name.Concat[Convert.RopeFromChar[from: VAL[num], quote: FALSE]];
ENDLOOP;
RETURN
};
wireStabBuffer: WireSTEntry ← NEW[WireSTEntryBody];
wsAux: WireSTAuxEntry ← NEW[WireSTAuxEntryBody];
all users should be in the monitor
RandomTestFindStabRange: Commander.CommandProc =
{
args: CommanderOps.ArgumentVector ← CommanderOps.Parse[cmd];
path: PATHPFS.PathFromRope[args[1]];
fileSet: SystemInterface.FileSet ← SystemInterface.CreateFileSet[];
{ ENABLE UNWIND => SystemInterface.CloseFileSet[fileSet];
file: SystemInterface.CirioFile ← SystemInterface.GetCirioFile[fileSet, path];
nextSeed: INTIF args.argc < 3 THEN 4466 ELSE Convert.IntFromRope[args[2]];
start: BasicTime.GMT ← BasicTime.Now[];
WHILE TRUE DO
rs: Random.RandomStream ← Random.Create[seed: nextSeed];
whole: Parsed ← ObjectFiles.CreateParsed[file, "XCOFF"];
IO.PutF[cmd.out, "beginning seed = %g at %g\N", IO.card[nextSeed], IO.time[start]];
FOR I: INT IN [0..100) DO
range: StabRange;
pc: CARD ← Random.ChooseInt[rs, 0, whole.header.text.byteLength];
[range, ] ← AlternativeFindModuleStabRange[whole, pc];
IO.PutF[cmd.out, "\Tpc = %g gives firstX: %g count: %g\N", IO.card[pc], IO.card[range.first], IO.card[range.count]];
ENDLOOP;
ENDLOOP;
};
SystemInterface.CloseFileSet[fileSet];
};
SizeFromName: PROC[name:ROPE, sizes: KnownSizeList ← NIL] RETURNS [size: CARD ← 0, newSizes: KnownSizeList] ~ {
c: CHAR;
newID: CARD;
where: CARD ← 0;
IF sizes = NIL THEN RETURN;
newSizes ← sizes;
where ← Rope.Index[name, 0, ":"] + 1;
IF where = ABS[name.Length[]]+ 1 THEN RETURN;
IF (c ← name.Fetch[where]) # 't AND c # 'T THEN {
typeID: INT;
IF IsDigit[c] OR c = '- THEN
[typeID, ] ← GetNum[name, where]
ELSE
[typeID, ] ← GetNum[name, where+1];
typeID: INT ← -1;
IF IsDigit[c] THEN
typeID ← Convert.IntFromRope[name.Substr[where]]
ELSE
typeID ← Convert.IntFromRope[name.Substr[where + 1]];
RETURN[FindInList[typeID, sizes], newSizes];
};
[newID, where] ← GetNum[name, where+ 1];
IF name.Fetch[where] # '= THEN ERROR;
where ← where + 1;
SELECT name.Fetch[where] FROM
'f => {
type: INT;
[type, where] ← GetNum[name, where+ 1];
size ← FindInList[type, sizes];
};
'*, 'e => size ← 4;
'u, 's => {
[size, where] ← GetNum[name, where +1]};
'a => {
garb, start, end, type: INT;
baseSize: CARD;
c: CHAR ← name.Fetch[where + 1];
IF c# 'r THEN ERROR;
[garb, where] ← GetNum[name, where+2];
IF name.Fetch[where] # '; THEN ERROR;
[start, where] ← GetNum[name, where+1];
IF name.Fetch[where] # '; THEN ERROR;
[end, where] ← GetNum[name, where+1];
IF name.Fetch[where] # '; THEN ERROR;
[type, where] ← GetNum[name, where+1];
baseSize ← FindInList[type, sizes];
size ← (end - start + 1) * baseSize;
};
ENDCASE => size ← 0;
newSizes ← CONS[[newID, size], newSizes];
size ← 0;
};
GetNum: PROC [rope: ROPE, index: CARD] RETURNS [num: INT ← 0, newIndex: CARD ← 0] ~ {
len: CARD ← rope.Length[];
i: CARD;
negFlag: BOOLFALSE;
FOR i ← index, i + 1 UNTIL i = len DO
c: CHAR ← rope.Fetch[i];
IF ~IsDigit[c] AND c # '- THEN {
newIndex ← i;
EXIT;
};
IF c = '- THEN
negFlag ← TRUE
ELSE
num ← num * 10 + (c - '0);
ENDLOOP;
IF i = len THEN
newIndex ← i;
IF negFlag THEN
num ← -num;
};
KnownSize: TYPE ~ RECORD[typeID: INT, size: CARD];
KnownSizeList: TYPE ~ LIST OF KnownSize;
These sizes were determined from /usr/include/dbxstclass.h (no guarantees though)
KnownSizes: KnownSizeList ← LIST[[-1, 4], [-2, 1], [-3, 2], [-4, 4], [-5, 1], [-6, 1], [-7, 2], [-8, 4], [-9, 4], [-10, 4], [-11, 0], [-12, 4], [-13, 8], [-14, 10], [-15, 4], [-16, 1], [-17, 4], [-18, 8], [-19, 1], [-20, 1], [-21, 1], [-22, 2], [-23, 1], [-24, 1], [-25, 8], [-26, 16], [-27, 1], [-28, 2], [-29, 4], [-30, 2]];
CopyList: PROC [l: KnownSizeList] RETURNS [n: KnownSizeList ← NIL] ~ {
n ← NIL;
FOR i: KnownSizeList ← l, i.rest WHILE i # NIL DO
n ← CONS[i.first, n];
ENDLOOP;
};
FindInList: PROC [i: INT, l: KnownSizeList] RETURNS [s: CARDLAST[CARD]] ~ {
FOR loop: KnownSizeList ← l, loop.rest WHILE loop # NIL DO
IF loop.first.typeID = i THEN RETURN[loop.first.size];
ENDLOOP;
ERROR;
};
IsDigit: PROC [c: CHAR] RETURNS [BOOL]
= INLINE { RETURN [c IN ['0 .. '9]] };
XCOFFGetTypeRef: ObjectFilesPrivate.GetTypeRefProcType ~ {
PROC [sourceStream:IO.STREAM] RETURNS [Rope.ROPE]
typeRef: Rope.ROPE;
lastChar: CHAR;
IF IsDigit[lastChar ← IO.PeekChar[sourceStream]] OR lastChar = '- THEN {
typeRef ← RCTW.GetTokenRope[sourceStream];
RETURN[typeRef]}
ELSE DO
lastChar ← IO.GetChar[sourceStream];
typeRef ← Rope.Concat[typeRef, Rope.FromChar[lastChar]];
IF lastChar = ') THEN RETURN[typeRef]
ENDLOOP
};
The function stab is module.funStabs[funStabIndex].stab
XCOFFInstallBracketPairsForOneFunStab: PROC[module: Module, funStabIndex: CARD] =
{
funStab: Stab ← module.funStabs[funStabIndex].stab;
paramNames, lastParamName: LIST OF Rope.ROPENIL;
paramStabs, lastParamStab: LIST OF Stab ← NIL; -- includes param mod stabs
x: CARD ← funStab.stabX+1;
good: BOOLTRUE;
module.funStabs[funStabIndex].brackets ← NEW[BracketPairBody←[
module: module,
kind: syntheticFun,
firstX: funStab.stabX,
limitX: 0,
firstPC: funStab.value,
pcLimit: 0,
funStab: funStab,
funIndex: funStabIndex,
symbols: NIL,
innerBrackets: NIL]];
The following loop finds the limitX and pcLimit. These might be at different X values if there are global symbols between procedures (which can happen with Sun's cc). good goes false once we've found the limitX and are just looking for the pcLimit. The loop also accumulates innerBrackets, if any. We make a feeble attempt to distinguish parameters from local variables; this attempt will fail for Gnu cc; it it's important to get this right, look at the `symbol descriptor' in a variable's stab.
We assume the following structure:
some param stabs
some stabs that modify the param stabs
symbol stabs for the first inner block (if any)
Note that both C compilers feels free to omit the LBrac and RBrac of a procedure with no local variables. Gnu cc further feels free to omit the L&RBrac of procedures with no non-trivial nested scopes.
WHILE x < module.limitStabX DO
stab: Stab ← ObjectFilesPrivate.ReadStab[module, x];
TakeBP: PROC ~ {
finalRBracX: CARD;
[finalRBracX, module.funStabs[funStabIndex].brackets.innerBrackets, paramStabs] ← ScanOneBracketPairSequence[module, funStab, funStabIndex, x, paramStabs, lastParamStab];
IF good THEN module.funStabs[funStabIndex].brackets.limitX ← finalRBracX+1;
module.funStabs[funStabIndex].brackets.pcLimit ←
IF finalRBracX<module.limitStabX
THEN ObjectFilesPrivate.ReadStab[module, finalRBracX].value
ELSE module.limitPC;
RETURN};
IF stab = NIL THEN LOOP;
SELECT stab.stabType FROM
PSym => {
stabCell: LIST OF Stab ← LIST[stab];
name: Rope.ROPE ← ObjectFiles.CNameOfStab[stab];
nameCell: LIST OF Rope.ROPELIST[name];
IF paramStabs#NIL AND stab=paramStabs.first THEN ERROR;
IF paramNames = NIL THEN paramNames ← nameCell ELSE lastParamName.rest ← nameCell;
lastParamName ← nameCell;
IF paramStabs = NIL THEN paramStabs ← stabCell ELSE lastParamStab.rest ← stabCell;
lastParamStab ← stabCell};
RSym, LSym => {
stabCell: LIST OF Stab ← LIST[stab];
name: Rope.ROPE ← ObjectFiles.CNameOfStab[stab];
isAParam: BOOLEANFALSE; -- initial value
FOR pNames: LIST OF Rope.ROPE ← paramNames, pNames.rest WHILE pNames # NIL DO
IF Rope.Equal[name, pNames.first] THEN
{isAParam ← TRUE; EXIT};
ENDLOOP;
IF isAParam THEN {
IF paramStabs = NIL THEN paramStabs ← stabCell ELSE lastParamStab.rest ← stabCell;
lastParamStab ← stabCell}
ELSE -- we are facing the first symbol of a nested block
{TakeBP[]; EXIT};
};
GSym, LCSym, STSym => IF good THEN {
module.funStabs[funStabIndex].brackets.limitX ← stab.stabX;
good ← FALSE};
SLine, Unspecified => NULL;
LBrac => {TakeBP[]; EXIT};
Fun => {
IF good THEN module.funStabs[funStabIndex].brackets.limitX ← stab.stabX;
module.funStabs[funStabIndex].brackets.pcLimit ← stab.value;
EXIT};
ENDCASE => ObjectFilesPrivate.RaiseUnreadableObjectFileForFun[
IO.PutFR["Unexpected stab (of stabType %02xH) in intro to %g",
[rope[ObjectFiles.RopeForStabType[stab.stabType]]],
[rope[ObjectFiles.CNameOfStab[funStab]]] ],
module, funStab];
x ← x + 1;
REPEAT
FINISHED => {
IF good THEN module.funStabs[funStabIndex].brackets.limitX ← module.limitStabX;
module.funStabs[funStabIndex].brackets.pcLimit ← module.limitPC;
};
ENDLOOP;
module.funStabs[funStabIndex].brackets.symbols ← paramStabs;
RETURN};
this is coded to expect the symbols before the LBrac.
We have discovered that the Sun C compiler places local variables symbols in the symbol table before their enclosing left bracket.!! This is confirmed by the example in "Dbx and Dbxtool Interfaces, Second Edition, Revised, 7 January 1988. See lines 46 and 47 on page 28. It is also confirmed by some comments in /import/gdb/dbxread.c. See source position 90236. This is in the vicinity of "case N←RBRAC". On the positive side, the code in /import/gdb/dbxread.c confirms that the local variables symbols are expected to be properly sorted in some way with respect to the RBrac/LBrac stabs.
The comment in /import/gdb/dbxread.c also suggests that there are some C compilers that place the symbols inside the LBrac/RBrac pair. It shouldn't be difficult to recode this to handle the second case. One will have to collect the symbols during the scan. For full generality, one might expect to receive the symbols anytime one is outside an inner pair.
We assume that firstX is pointing at the first symbol of the first bracket pair. (If the enclosing bracket pair is an innermost pair, then firstX is pointing at either an RBrac, a Fun, or is an attempt to go past the end of the symbol table.)
Upon return, nextX will point to an unmatched RBrac, a Fun, or will be an attempt to go past the end of the symbol table.
BracketPair: TYPE ~ ObjectFilesPrivate.BracketPair;
BracketPairBody: TYPE ~ ObjectFilesPrivate.BracketPairBody;
ScanOneBracketPairSequence: PROC[module: Module, fun: Stab, funIndex: CARD, firstX: CARD, symHead, symTail: StabList] RETURNS[--finalRBracX-- CARD, --innerBrackets-- LIST OF BracketPair, --symbols-- StabList] =
{
innerBrackets: LIST OF BracketPair ← NIL;
lastInnerBracket: LIST OF BracketPair ← NIL;
x: CARD ← firstX;
DO
xAfterSymbs: CARD;
stabAfterSymbs: Stab;
[xAfterSymbs, symHead, symTail] ← ObjectFilesPrivate.ScanSymbolStabs[module, x, symHead, symTail];
IF xAfterSymbs >= module.limitStabX THEN
RETURN[xAfterSymbs, innerBrackets, symHead];
stabAfterSymbs ← ObjectFilesPrivate.ReadStab[module, xAfterSymbs];
SELECT stabAfterSymbs.stabType FROM
LBrac =>
{
finalRBracX: CARD;
nestedInnerBrackets: LIST OF BracketPair;
bracket: BracketPair;
bracketCell: LIST OF BracketPair;
[finalRBracX, nestedInnerBrackets, symHead] ← ScanOneBracketPairSequence[module, fun, funIndex, xAfterSymbs+1, symHead, symTail];
bracket ← NEW[BracketPairBody←[
module: module,
kind: actual,
firstX: stabAfterSymbs.stabX,
limitX: finalRBracX+1,
firstPC: stabAfterSymbs.value,
pcLimit: IF finalRBracX<module.limitStabX THEN ObjectFilesPrivate.ReadStab[module, finalRBracX].value ELSE module.limitPC,
funIndex: funIndex,
symbols: symHead,
innerBrackets: nestedInnerBrackets]];
bracketCell ← LIST[bracket];
IF innerBrackets = NIL THEN innerBrackets ← bracketCell ELSE lastInnerBracket.rest ← bracketCell;
lastInnerBracket ← bracketCell;
symHead ← symTail ← NIL;
x ← finalRBracX+1;
};
RBrac, Fun, GSym, STSym, LCSym => RETURN[xAfterSymbs, innerBrackets, symHead];
ENDCASE => ERROR; -- can't happen
ENDLOOP};
GlobalPreDefinedTypes: REF PDTType ← NIL;
GPTDs: LIST OF Rope.ROPELIST [
"int:t-1=r-1;-2147483648;2147483647",
"char:t-2=@s8;r-2;0;255",
"short:t-3=@s16;r-3;-32768;32767",
"long:t-4=-1",
"unsigned char:t-5=@s8;r-5;0;255",
"signed char:t-6=@s8;r-6;-128;127",
"unsigned short:t-7=@s16;r-7;0;65535",
"unsigned int:t-8=r-8;0;4294967295",
"unsigned:t-9=-8",
"unsigned long:t-10=-8",
"void:t-11=r-11;0;0",
"float:t-12=g-12;4",
"double:t-13=g-12;8",
"long double:t-14=g-12;10",
"integer:t-15=-1",
"boolean:t-16=efalse:0,true:1,",
"shortreal:t-17=g-12;4",
"real:t-18=g-12;8",
"stringptr:t-19=N-19",
"character:t-20=@s8;r-20;0;255",
"logical*1:t-21=@s8;r-21;0;255",
"logical*2:t-22=@s16;r-22;0;65535",
"logical*4:t-23=r-23;0;4294967295",
"logical:t-24=-23",
"complex:t-25=c-25;8",
"double complex:t-26=c-25;16",
"integer*1:t-27=-6",
"integer*2:t-28=-3",
"integer*4:t-29=-1",
"wchar:t-30=@s16;r-30;0;65535"];
PDTType: TYPE ~ RECORD [body: SEQUENCE length: CARD OF Rope.ROPE];
IntallPreDefinedTypes: PROC ~ {
count: CARD ← 0;
l: LIST OF Rope.ROPENIL;
FOR l ← GPTDs, l.rest UNTIL l = NIL DO
count ← count + 1;
ENDLOOP;
GlobalPreDefinedTypes ← NEW[PDTType[count]];
l ← GPTDs;
FOR index: CARD IN [0..count) DO
GlobalPreDefinedTypes[index] ← l.first;
l ← l.rest;
ENDLOOP;
};
It seems AIX ld doesn't relocate static symbol (STSym) offset. So, we get data section's base offset and add the offset to local symbol offset.
GetDataSectionOffset: PROC[module: Module] RETURNS[CARD] ~ {
IsDotO: PROC RETURNS[BOOL] ~ {
RETURN[Basics.BITAND[module.whole.header.flags, (FLdMinusR+FShrObj+FDynLoad+FExec)] = 0];
};
Section's base offset doesn't correct if file is .o.
IF IsDotO[] THEN RETURN [0];
FOR x: CARD DECREASING IN [module.firstStabX..module.limitStabX) DO
stab: Stab ← ObjectFilesPrivate.ReadStab[module, x];
IF stab.stabType = Invalid THEN RETURN [stab.value];
ENDLOOP;
RETURN [0];
};
XCOFFInstallStaticVars: ObjectFilesPrivate.InstallStaticVarsType ~
PROC[module: Module] =
this procedure examines all stabs up to the first Fun stab, looking for certain expected stabs. These are recorded in the whole.
{
numFuns: INT ← 1;
dataStabOffset: CARD ← 0; -- globalFrame stab seems to be offset from versionStringStab, which has to be searched for at times...
IF NOT module.staticVarsInstalled THEN
FOR y: CARD IN [module.firstStabX..module.limitStabX)
DO
x: CARD ← module.firstStabX + module.limitStabX - 1 - y;
stab: Stab ← ObjectFilesPrivate.ReadStab[module, x];
IF stab = NIL THEN LOOP;
IF stab.stabType = Fun THEN {
IF numFuns = 0 THEN EXIT;
numFuns ← numFuns - 1;
};
IF stab.stabType = STSym OR stab.stabType = LCSym THEN
{
IF module.versionStampStab = NIL THEN
{
gvi: ObjectFiles.GlobalVarLoc ← NIL;
gvi ← NARROW[ObjectFilesPrivate.CheckStaticVar[stab, "versionStamp", TRUE]];
IF gvi # NIL THEN
{
stream: IO.STREAMNIL;
dataRope: Rope.ROPE;
module.versionStampStab ← stab;
dataStabOffset ← GetDataSectionOffset[module];
dataRope ← ObjectFilesPrivate.ReadInitialDataAsRope[module, gvi.fileByteOffset + dataStabOffset];
IF dataRope.IsEmpty[] THEN {
ENABLE UNWIND => SystemInterface.ReleaseStreamForFile [module.whole.file, stream];
Force find the version stamp mob
Before ld, the version stamp mob is further into the
.data section than usual, and the stab entry shows
no indication of this, so if no rope was found at
the correct location, walk through until @(#) is found.
searchFor: Rope.ROPE ← "@(#)";
c: CHAR;
searchForIndex: INT ← 0;
found: BOOLFALSE;
stream ← SystemInterface.GetStreamForFile[module.whole.file];
IO.SetIndex[stream, gvi.fileByteOffset + dataStabOffset];
dataRope ← "";
WHILE ~found DO
tmpOffset: CARD ← 0;
WHILE ((c ← IO.GetChar[stream]) # searchFor.Fetch[searchForIndex]) DO
dataStabOffset ← dataStabOffset + 1;
ENDLOOP;
tmpOffset ← tmpOffset + 1;
searchForIndex ← searchForIndex + 1;
WHILE (searchForIndex < searchFor.Length[] AND (c ← IO.GetChar[stream]) = searchFor.Fetch[searchForIndex]) DO
tmpOffset ← tmpOffset + 1;
searchForIndex ← searchForIndex + 1;
ENDLOOP;
tmpOffset ← tmpOffset + 1;
IF searchForIndex = searchFor.Length[] THEN
found ← TRUE
ELSE
dataStabOffset ← dataStabOffset + tmpOffset;
ENDLOOP;
dataRope ← searchFor;
WHILE ((c ← IO.GetChar[stream]) # '\000) DO
dataRope ← dataRope.Concat[Rope.FromChar[c]];
ENDLOOP;
SystemInterface.ReleaseStreamForFile [module.whole.file, stream];
};
stab.value ← stab.value+dataStabOffset;
IF module.globalFrameStab # NIL THEN
module.globalFrameStab.value ← module.globalFrameStab.value + dataStabOffset;
IF module.globalFrameStab # NIL THEN {
module.globalFrameGvl.bitOffset ← module.globalFrameGvl.bitOffset + dataStabOffset * 8;
module.globalFrameGvl.fileByteOffset ← module.globalFrameGvl.fileByteOffset + dataStabOffset;
};
module.versionStampInfo ← NEW[ObjectFiles.VersionStampInfo←[gvi, dataRope]];
LOOP;
};
};
IF module.globalFrameStab = NIL THEN
{
gvi: ObjectFiles.GlobalVarLoc ← NIL;
gvi ← NARROW[ObjectFilesPrivate.CheckStaticVar[stab, "globalframe", TRUE]];
IF gvi # NIL THEN
{
module.globalFrameStab ← stab;
module.globalFrameGvl ← gvi;
LOOP;
};
};
};
ENDLOOP;
module.staticVarsInstalled ← TRUE;
};
XCOFFAlterFunStab: ObjectFilesPrivate.AlterFunStabType ~ {
rope assignement has been done in CreateModule.
module.funStabs[funStabX].stab.rope ← module.stabs[module.funStabs[funStabX].firstX+1].rope;
RETURN [module.funStabs[funStabX].stab]; };
XCOFFGetSPOffset: ObjectFilesPrivate.GetSPOffsetType ~ {
On AIX, SP offset from FP is always 0 (SP = FP).
RETURN [0];
};
Main code
transTable: ObjectFilesPrivate.TranslationTable;
IntallPreDefinedTypes[];
transTable ←NEW[ObjectFilesPrivate.TranslationTableBody[1]];
Commander.Register["xcoffRandomTestFindStabRange", RandomTestFindStabRange];
transTable[0] ← [0, 2, 479, "XCOFF"];
ObjectFilesPrivate.RegisterObjectFileFlavor[NEW [ObjectFilesPrivate.ObjectFileFlavorBody ← [
"XCOFF",
XCOFF,
ReadHeader,
XCOFFModuleFromParsedAndPC,
XCOFFVarLocFromStab,
XCOFFGetTypeRef,
XCOFFInstallBracketPairsForOneFunStab,
NIL, NIL, NIL,
FALSE,
XCOFFInstallStaticVars,
XCOFFAlterFunStab,
XCOFFGetSPOffset
]],
transTable];
END.