MoberyObjectFileImpl.mesa
Copyright Ó 1992, 1993 by Xerox Corporation. All rights reserved.
Mna, November 11, 1991 2:58 pm PST
Willie-s, March 2, 1993 2:11 pm PST
DIRECTORY
Basics USING [BITLSHIFT, Word16, LongNumber],
Commander,
CommanderOps,
Convert,
MoberyPrivate,
IO USING [EndOf, GetBlock, GetByte, GetIndex, GetLength, SetIndex, STREAM],
IO,
Rope,
UserProfile;
MoberyObjectFileImpl: CEDAR PROGRAM
IMPORTS Basics, Commander, CommanderOps, Convert, IO, MoberyPrivate, Rope, UserProfile
~ BEGIN
ROPE: TYPE = Rope.ROPE;
a.out portions of this file created from MoberyImpl.mesa.
a.out know-how (thanks to Michael Plass)
aoutHeaderBytes: INT = 4+4*7; -- size of header for a.out .o files
aoutTextSizeOffset: INT = 4; -- byte offset of text size field in a.out .o files
aoutMagicOffset: INT = 2; -- byte offset of magic number in a.out .o files
XCOFF know-how (thanks to IBM documentation)
xcoffMagicOffset: INT = 0; -- byte offset of magic number in xcoff files
xcoffHeaderBytes: INT = 4+4*3+4; -- size of header for xcoff filehdr
xcoffOptionalHdrSizeOffset: INT = 16; -- offset of optional header (aouthdr) size in filehdr
xcoffScnHdrSectionNameOffset: INT = 0; -- byte offset of section name field within xcoff scn
xcoffScnHdrSectionStartOffset: INT = 8; -- byte offset of section start offset within xcoff scn
xcoffScnHdrSize: INT = 40; -- size of xcoff section header
xcoffScnNameLen: INT = 8; -- size of xcoff section name
xcoffDataSectionName: ROPE = ".data";
ELF know-how (SunOS 5.x and IRIX 4.x)
elfMagicOffset: INT = 0; -- byte offset of magic number in elf files
elfHeaderBytes: INT = 4+4*3+4; -- size of header for elf filehdr
elfOptionalHdrSizeOffset: INT = 16; -- offset of optional header (aouthdr) size in filehdr
elfScnHdrSectionNameOffset: INT = 0; -- byte offset of section name field within elf scn
elfScnHdrSectionStartOffset: INT = 8; -- byte offset of section start offset within elf scn
elfScnHdrSize: INT = 40; -- size of elf section header
elfScnNameLen: INT = 8; -- size of elf section name
elfDataSectionName: ROPE = ".data";
ReadBinaryShort: PROC [s: IO.STREAM] RETURNS [INT16] ~ {
sn: Basics.Word16 ¬ [int[0]];
sn.hi ¬ IO.GetByte[s];
sn.lo ¬ IO.GetByte[s];
RETURN [sn.card];
};
ReadThreeBytes: PROC [stream: IO.STREAM] RETURNS [CARD] ~ {
ln: Basics.LongNumber ← [lc[0]];
ln.hl ← IO.GetByte[stream];
ln.lh ← IO.GetByte[stream];
ln.ll ← IO.GetByte[stream];
RETURN [ln.lc]
};
ReadBinaryINT: PROC [stream: IO.STREAM] RETURNS [INT] ~ {
ln: Basics.LongNumber ¬ [li[0]];
ln.hh ¬ IO.GetByte[stream];
ln.hl ¬ IO.GetByte[stream];
ln.lh ¬ IO.GetByte[stream];
ln.ll ¬ IO.GetByte[stream];
RETURN [ln.li]
};
ReadBinaryUINT: PROC [stream: IO.STREAM] RETURNS [CARD] ~ {
ln: Basics.LongNumber ¬ [lc[0]];
ln.hh ¬ IO.GetByte[stream];
ln.hl ¬ IO.GetByte[stream];
ln.lh ¬ IO.GetByte[stream];
ln.ll ¬ IO.GetByte[stream];
RETURN [ln.lc]
};
This routine handles ELF files for both SunOS 5.x and IRIX 4.x as the differences are not large enough to affect the stamp-finding algorithm.
MoberyELFTester: MoberyPrivate.ObjectFileTypeMatcherProc ~ {
PROC[stream: IO.STREAM] RETURNS [matches: BOOLEAN ← FALSE, start, end: CARD]
magicNumber, numOfSections: INT;
magicNumber, numOfSections, optHdrLength: INT;
ElfMagic: CARD = (Basics.BITLSHIFT[177B, 24] + Basics.BITLSHIFT['E.ORD, 16] + Basics.BITLSHIFT['L.ORD, 8] + 'F.ORD);
isELF: PROC[num: CARD] RETURNS [BOOLEAN] = INLINE {
isELF checks if the binary is a valid SunOS 5.x or IRIX 4.0x ELF executable.
RETURN [num = ElfMagic];
};
readSectionName: PROC [s: IO.STREAM] RETURNS [REF TEXT] ~ {
b: REF TEXT = NEW[TEXT[elfScnNameLen]];
[] ← IO.GetBlock[self: s, block: b, count: elfScnNameLen];
RETURN [b];
};
IF NOT stream.EndOf[] THEN {
cardLen: CARD;
IO.SetIndex[self: stream, index: elfMagicOffset];
magicNumber ← ReadBinaryUINT[stream];
IF isELF[magicNumber] THEN {
an object file.
matches ← TRUE;
numOfSections ← ReadBinaryShort[stream]; -- read number of sections
IO.SetIndex[self: stream, index: elfOptionalHdrSizeOffset];
optHdrLength ← ReadBinaryShort[stream]; -- get length of optional header (a.out)
seek to section headers at end of optional a.out header
IO.SetIndex[stream, IO.GetIndex[self: stream] - 2 + optHdrLength + (elfHeaderBytes - elfOptionalHdrSizeOffset)];
find data section section and determine where to search for stamps
WHILE (numOfSections > 0) DO
scnName: REF TEXT;
scnName ← readSectionName[stream];
IF Rope.EqualSubstrs[s1: elfDataSectionName, len1: elfDataSectionName.Length[], s2: Rope.FromRefText[s: scnName], len2: elfDataSectionName.Length[]] THEN {
size: CARD;
[] ← ReadBinaryUINT[stream]; -- skip physical address field
[] ← ReadBinaryUINT[stream]; -- skip virtual address field
size ← ReadBinaryUINT[stream];
start ← ReadBinaryUINT[stream];
end ← start + size;
EXIT;
};
numOfSections ← numOfSections - 1;
IO.SetIndex[stream, IO.GetIndex[stream] - elfScnNameLen + elfScnHdrSize]; -- advance to next section header
ENDLOOP;
Kludge for testing -
numOfSections ← -1; start ← 0; end ← 0;
IF (numOfSections <= 0) OR (NOT start IN [0..end) AND end <= (cardLen ¬ IO.GetLength[stream]) ) THEN {
confusion! search whole file.
start ← 0;
end ← IO.GetLength[stream];
};
};
};
};
This routine handles both XCOFF (AIX) and ECOFF (DEC/MIPS) as the differences in the object file formats between ECOFF and XCOFF are not large enough to affect the stamp-finding algorithm.
MoberyXCoffTester: MoberyPrivate.ObjectFileTypeMatcherProc ~ {
PROC[stream: IO.STREAM] RETURNS [matches: BOOLEAN ← FALSE, start, end: CARD]
magicNumber, numOfSections, optHdrLength: INT;
isXCoff: PROC[num: INT] RETURNS [BOOLEAN] = INLINE {
isXCoff also checks if the binary is a valid dec ECOFF executable.
RETURN [num = 0730B OR num = 0735B OR num = 0737B OR num = 6201H OR num = 0160H];
};
readSectionName: PROC [s: IO.STREAM] RETURNS [REF TEXT] ~ {
b: REF TEXT = NEW[TEXT[xcoffScnNameLen]];
[] ¬ IO.GetBlock[self: s, block: b, count: xcoffScnNameLen];
RETURN [b];
};
IF NOT stream.EndOf[] THEN {
cardLen: CARD;
IO.SetIndex[self: stream, index: xcoffMagicOffset];
magicNumber ¬ ReadBinaryShort[stream];
IF isXCoff[magicNumber] THEN {
an object file.
matches ¬ TRUE;
numOfSections ¬ ReadBinaryShort[stream]; -- read number of sections
IO.SetIndex[self: stream, index: xcoffOptionalHdrSizeOffset];
optHdrLength ¬ ReadBinaryShort[stream]; -- get length of optional header (a.out)
seek to section headers at end of optional a.out header
IO.SetIndex[stream, IO.GetIndex[self: stream] - 2 + optHdrLength + (xcoffHeaderBytes - xcoffOptionalHdrSizeOffset)];
find data section section and determine where to search for stamps
WHILE (numOfSections > 0) DO
scnName: REF TEXT;
scnName ¬ readSectionName[stream];
IF Rope.EqualSubstrs[s1: xcoffDataSectionName, len1: xcoffDataSectionName.Length[], s2: Rope.FromRefText[s: scnName], len2: xcoffDataSectionName.Length[]] THEN {
size: CARD;
[] ¬ ReadBinaryUINT[stream]; -- skip physical address field
[] ¬ ReadBinaryUINT[stream]; -- skip virtual address field
size ¬ ReadBinaryUINT[stream];
start ¬ ReadBinaryUINT[stream];
end ¬ start + size;
EXIT;
};
numOfSections ¬ numOfSections - 1;
IO.SetIndex[stream, IO.GetIndex[stream] - xcoffScnNameLen + xcoffScnHdrSize]; -- advance to next section header
ENDLOOP;
IF (numOfSections <= 0) OR (NOT start IN [0..end) AND end <= (cardLen ¬ IO.GetLength[stream]) ) THEN {
confusion! search whole file.
start ¬ 0;
end ¬ cardLen;
};
};
};
};
MoberyADotOutTester: MoberyPrivate.ObjectFileTypeMatcherProc ~ {
PROC[stream: IO.STREAM] RETURNS [matches: BOOLEAN ← FALSE, start, end: CARD]
magicNumber: INT;
isADotOut: PROC[num: INT] RETURNS [BOOLEAN] = INLINE {
RETURN [num = 0407B OR num = 0410B OR num = 0413B];
};
IO.SetIndex[self: stream, index: aoutMagicOffset];
IF NOT stream.EndOf[] THEN {
magicNumber ¬ ReadBinaryShort[stream];
IF isADotOut[magicNumber] THEN {
an object file.
cardLen: CARD;
matches ¬ TRUE;
IO.SetIndex[self: stream, index: aoutTextSizeOffset];
start ¬ aoutHeaderBytes + ReadBinaryINT[stream];
end ¬ start + ReadBinaryINT[stream];
IF NOT start IN [0..end) AND end <= (cardLen ¬ IO.GetLength[stream]) THEN {
confusion! search whole file.
start ¬ 0;
end ¬ cardLen;
};
};
};
};
UserDefinedCheckers: MoberyPrivate.ObjectFileTypeMatcherProc ~ {
PROC[stream: IO.STREAM] RETURNS [matches: BOOLEAN ← FALSE, start, end: CARD]
Look: PROC[cL: LIST OF ConsistencyChecker]
RETURNS[doesMatch: BOOL ¬ FALSE] ~ {
num: CARD;
FOR uL: LIST OF ConsistencyChecker ¬ cL, uL.rest UNTIL uL = NIL DO
IO.SetIndex[self: stream, index: uL.first.offset];
SELECT uL.first.numBytes FROM
1 => num ¬ LOOPHOLE[IO.GetByte[stream]];
2 => num ¬ ReadBinaryShort[stream];
3 => num ¬ ReadThreeBytes[stream];
4 => num ¬ ReadBinaryUINT[stream];
ENDCASE => LOOP;
IF num = uL.first.value THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
};
IF Look[cmdCheckersList] OR Look[userCheckersList] THEN
RETURN[TRUE, 0, IO.GetLength[stream]];
RETURN[FALSE, 0, 0];
};
OnProfileChange: UserProfile.ProfileChangedProc ~ {
ENABLE Convert.Error => GOTO fail;
checkList: LIST OF ROPE ~ UserProfile.ListOfTokens["MobStuff.Checkers", NIL];
cL: LIST OF ROPE ¬ checkList;
userCheckersList ¬ NIL;
UNTIL cL = NIL DO
arg2, arg3: ROPE;
offset, numBytes, value: CARD;
arg1: ROPE ¬ cL.first;
IF Rope.Equal[arg1, "{"] OR Rope.Equal[arg1, "}"] THEN
IF ( cL ¬ cL.rest ) = NIL THEN EXIT ELSE LOOP;
arg1 ¬ cL.first;
IF ( cL ¬ cL.rest ) = NIL THEN EXIT;
arg2 ¬ cL.first;
IF ( cL ¬ cL.rest ) = NIL THEN EXIT;
arg3 ¬ cL.first;
cL ¬ cL.rest;
numBytes ¬ Convert.CardFromRope[arg2];
IF numBytes = 0 OR numBytes > 4 THEN LOOP; -- ignore if numBytes > 4
offset ¬ Convert.CardFromRope[arg1];
value ¬ Convert.CardFromRope[arg3];
userCheckersList ¬ CONS[[offset, numBytes, value], userCheckersList];
ENDLOOP;
EXITS fail => NULL;
};
MobStuffCheckersCmd: Commander.CommandProc ~ {
ENABLE Convert.Error => GOTO fail;
arg1: ROPE;
UNTIL ( arg1 ¬ CommanderOps.NextArgument[cmd] ) = NIL DO
arg2, arg3: ROPE;
offset, numBytes, value: CARD;
IF Rope.Equal[arg1, "{"] OR Rope.Equal[arg1, "}"] THEN LOOP;
IF ( arg2 ¬ CommanderOps.NextArgument[cmd] ) = NIL THEN EXIT;
IF ( arg3 ¬ CommanderOps.NextArgument[cmd] ) = NIL THEN EXIT;
numBytes ¬ Convert.CardFromRope[arg2];
IF numBytes = 0 OR numBytes > 4 THEN LOOP; -- ignore if numBytes > 4
offset ¬ Convert.CardFromRope[arg1];
value ¬ Convert.CardFromRope[arg3];
cmdCheckersList ¬ CONS[[offset, numBytes, value], cmdCheckersList];
ENDLOOP;
EXITS fail => NULL;
};
PrintMobStuffCheckersCmd: Commander.CommandProc ~ {
FOR list: LIST OF ConsistencyChecker ¬ cmdCheckersList, list.rest UNTIL list = NIL DO
cmd.out.PutFL[template, LIST[[integer[list.first.offset]], [integer[list.first.numBytes]], [integer[list.first.value]], [integer[list.first.value]]] ];
ENDLOOP;
FOR list: LIST OF ConsistencyChecker ¬ userCheckersList, list.rest UNTIL list = NIL DO
cmd.out.PutFL[template, LIST[[integer[list.first.offset]], [integer[list.first.numBytes]], [integer[list.first.value]], [integer[list.first.value]]] ];
ENDLOOP;
};
template: ROPE ~ "[offset: %g, numBytes: %g, value: %g (%xH)]\n";
cmdCheckersList: LIST OF ConsistencyChecker ¬ NIL;
userCheckersList: LIST OF ConsistencyChecker ¬ NIL;
ConsistencyChecker: TYPE ~ RECORD[offset, numBytes, value: CARD];
UserProfile.CallWhenProfileChanges[OnProfileChange];
MoberyPrivate.RegisterObjectFileType[MoberyADotOutTester];
MoberyPrivate.RegisterObjectFileType[MoberyXCoffTester];
MoberyPrivate.RegisterObjectFileType[MoberyELFTester];
MoberyPrivate.RegisterObjectFileType[UserDefinedCheckers];
Commander.Register["MobStuff.Checkers", MobStuffCheckersCmd, "add consistency checkers: syntax is { offset numBytes value }*; braces optional; 0<numBytes<5"];
Commander.Register["MobStuff.PrintCheckers", PrintMobStuffCheckersCmd, "prints the mob checkers added by commands and from the user profile"];
END.