MapParseImpl.mesa
Last modified: L. Stewart, December 22, 1983 1:56 pm
A program to build up a symbol table, given a C map file
DIRECTORY
Basics USING [CompareINT, LowHalf],
Commander USING [CommandProc, Register],
CommandTool USING [ArgumentVector, Failed, Parse],
Convert USING [CardFromRope, Error],
FS USING [Error, StreamOpen],
IO,
LarkPrograms,
List USING [Reverse],
OrderedSymbolTableRef,
Rope;
MapParseImpl:
CEDAR
PROGRAM
IMPORTS Basics, Commander, CommandTool, Convert, FS, IO, List, OrderedSymbolTableRef, Rope =
BEGIN
BadHex: ERROR = CODE;
BadID: ERROR = CODE;
BadAddress: ERROR = CODE;
Hex4:
PROC [r: Rope.
ROPE, pos:
INT]
RETURNS [res:
INT] = {
res ← (Hex1[r: r, pos: pos] * 4096) + (Hex1[r: r, pos: pos + 1] * 256);
res ← res + (Hex1[r: r, pos: pos + 2] * 16) + Hex1[r: r, pos: pos + 3];
};
Hex2:
PROC [r: Rope.
ROPE, pos:
INT]
RETURNS [res:
INT] = {
res ← (Hex1[r: r, pos: pos] * 16) + Hex1[r: r, pos: pos + 1];
};
Hex1:
PROC [r: Rope.
ROPE, pos:
INT]
RETURNS [
INT] = {
c: CHAR ← r.Fetch[pos];
SELECT
TRUE
FROM
Digit[c] => RETURN [c - '0];
c IN ['A..'F] => RETURN [(c - 'A) + 10];
ENDCASE => ERROR BadHex;
};
Digit: PROC [c: CHAR] RETURNS [BOOL] = INLINE { RETURN [c IN ['0..'9]]; };
Letter: PROC [c: CHAR] RETURNS [BOOL] = INLINE { RETURN [c IN ['a..'z] OR c IN ['A..'Z]]; };
LowWord:
PROC [i:
INT]
RETURNS [
INT] = {
c: CARDINAL ← Basics.LowHalf[i];
RETURN [c];
};
Convert from CARDINAL to INTEGER (both kept in an INT)
GetID:
PROC [r: Rope.
ROPE, pos:
INT]
RETURNS [Rope.
ROPE] = {
end: INT;
c: CHAR;
FOR i:
INT
IN [pos..r.Length[])
DO
c ← r.Fetch[i];
IF
NOT Letter[c]
AND
NOT Digit[c]
THEN {
IF c # '\n THEN ERROR BadID;
end ← i;
EXIT;
};
REPEAT
FINISHED => end ← r.Length[];
ENDLOOP;
RETURN[Rope.Substr[base: r, start: pos, len: end - pos]];
};
GetMidID:
PROC [r: Rope.
ROPE, pos:
INT]
RETURNS [Rope.
ROPE] = {
end: INT;
c: CHAR;
FOR i:
INT
IN [pos..r.Length[])
DO
c ← r.Fetch[i];
IF
NOT Letter[c]
AND
NOT Digit[c]
THEN {
IF c # ' THEN ERROR BadID;
end ← i;
EXIT;
};
REPEAT
FINISHED => end ← r.Length[];
ENDLOOP;
RETURN[Rope.Substr[base: r, start: pos, len: end - pos]];
};
GetVarAddress:
PROC [r: Rope.
ROPE, pos:
INT]
RETURNS [idAddr:
INT] = {
address is the last two bytes before the "\t;"
DO
IF pos <= 15 THEN ERROR BadAddress;
IF HexChar[r.Fetch[pos]]
THEN {
idAddr ← Hex2[r: r, pos: pos - 1 ! BadHex => ERROR BadAddress] * 256;
IF r.Fetch[pos - 2] # ' THEN ERROR BadAddress;
idAddr ← idAddr + Hex2[r: r, pos: pos - 4 ! BadHex => ERROR BadAddress];
RETURN[LowWord[idAddr]];
};
pos ← pos - 1;
ENDLOOP;
};
HexChar:
PROC [ch:
CHAR]
RETURNS [
BOOL] =
INLINE {
RETURN [ch IN ['0..'9] OR ch IN ['A..'F]];
};
An interestng line must start with a '[ and must contain a ;, a ←, and "CALL" or "MOV"
ScanFile:
PROC [map, out:
IO.
STREAM] = {
id: Rope.ROPE;
idAddr: INT;
l: Rope.ROPE;
len: INT;
semiColon: INT;
address: INT;
offset: INT;
idPos: INT;
fileName: Rope.ROPE;
DO
{
l ← IO.GetLineRope[map];
len ← l.Length[];
IF map.EndOf[] THEN EXIT;
IF len > 0
AND l.Fetch[0] = ';
THEN {
IF l.Find[";File "] = 0 THEN fileName ← Rope.Substr[base: l, start: 6, len: len - 6];
};
IF len < 10 THEN GOTO TryNextLine;
first 6 characters must be "[0000]"
IF l.Find["[0000]"] # 0 THEN GOTO TryNextLine;
IF fileName #
NIL
THEN {
idAddr ← Convert.CardFromRope[Rope.Substr[base: l, start: 6, len: 4], 16 ! Convert.Error => {
out.PutF["Can't find address for file %g\n", IO.rope[fileName]];
idAddr ← 0;
CONTINUE}];
EnterFile[id: fileName, idAddr: Convert.CardFromRope[Rope.Substr[base: l, start: 6, len: 4], 16]];
fileName ← NIL;
};
The line must contain a C identifier
IF l.Find["←"] = -1 THEN GOTO TryNextLine;
The line must be a CALL or MOV or LEA instruction
semiColon ← l.Find[";"];
IF semiColon = -1 THEN GOTO TryNextLine;
idPos ← l.Find["CALL", semiColon];
IF idPos # -1
THEN {
IF l.Fetch[idPos + 4] # ' AND l.Fetch[idPos + 4] # '\t THEN GOTO TryNextLine;
IF l.Fetch[idPos + 5] # '← THEN GOTO TryNextLine;
IF l.Find[": E8 ", 10] # 10 THEN GOTO TryNextLine;
address = four chars starting at pos 6
address ← Hex4[r: l, pos: 6 ! BadHex => GOTO TryNextLine];
offset ← Hex2[r: l, pos: 16 ! BadHex => GOTO TryNextLine];
IF l.Fetch[18] # ' THEN GOTO TryNextLine;
offset ← offset + (Hex2[r: l, pos: 19 ! BadHex => GOTO TryNextLine] * 256);
idAddr ← address + offset + 3;
idAddr ← LowWord[idAddr];
id token starting at idPos + 6
id ← GetID[r: l, pos: idPos + 6 ! BadID => GOTO TryNextLine];
EnterProc[id: id, idAddr: idAddr];
GOTO TryNextLine;
};
idPos ← l.Find["MOV", semiColon];
IF idPos# -1
THEN {
{
IF l.Find[": 8B ", 10] = 10
THEN {
id follows the ",←" or ",WORD PTR ←"
idPos ← l.Find[",←", semiColon + 3];
IF idPos # -1
THEN {
id ← GetID[r: l, pos: idPos + 2 ! BadID => GOTO TryNextLine];
GOTO FoundID;
};
idPos ← l.Find[",WORD PTR ←", semiColon + 3];
IF idPos # -1
THEN {
id ← GetID[r: l, pos: idPos + 11 ! BadID => GOTO TryNextLine];
GOTO FoundID;
};
};
IF l.Find[": BB ", 10] = 10
OR l.Find[": B9 ", 10] = 10
THEN {
id follows the ",OFFSET ←"
idPos ← l.Find[",OFFSET ←", semiColon + 3];
IF idPos # -1
THEN {
id ← GetID[r: l, pos: idPos + 9 ! BadID => GOTO TryNextLine];
GOTO FoundID;
};
};
GOTO TryNextLine;
EXITS
FoundID => NULL;
};
address is the last two bytes before the "\t;"
idAddr ← GetVarAddress[r: l, pos: semiColon - 1 ! BadAddress => GOTO TryNextLine];
EnterVar[id: id, idAddr: idAddr];
GOTO TryNextLine;
};
IF l.Find["LEA", semiColon] = semiColon + 1
THEN {
id follows the ",←"
idPos ← l.Find[",←", semiColon];
IF idPos = -1 THEN GOTO TryNextLine;
id ← GetID[r: l, pos: idPos + 2 ! BadID => GOTO TryNextLine];
address is the last two bytes before the "\t;"
idAddr ← GetVarAddress[r: l, pos: semiColon - 1 ! BadAddress => GOTO TryNextLine];
EnterVar[id: id, idAddr: idAddr];
GOTO TryNextLine;
};
IF l.Find["INC", semiColon] = semiColon + 1
THEN {
id follows the "←"
idPos ← l.Find["←", semiColon];
IF idPos = -1 THEN GOTO TryNextLine;
id ← GetID[r: l, pos: idPos + 1 ! BadID => GOTO TryNextLine];
address is the last two bytes before the "\t;"
idAddr ← GetVarAddress[r: l, pos: semiColon - 1 ! BadAddress => GOTO TryNextLine];
EnterVar[id: id, idAddr: idAddr];
GOTO TryNextLine;
};
EXITS
TryNextLine => NULL;
};
ENDLOOP;
};
TryForPublics:
PROC [map, out:
IO.
STREAM] = {
id: Rope.ROPE;
idAddr: INT;
l: Rope.ROPE;
len: INT;
addrPos: INT;
DO
{
l ← IO.GetLineRope[map];
len ← l.Length[];
IF map.EndOf[] THEN EXIT;
IF len > 0 AND l.Fetch[0] = '; THEN EXIT;
IF len < 10 THEN GOTO TryNextLine;
There must be an "Offset = "
addrPos ← l.Find["Offset = "];
IF addrPos > 0
THEN {
idAddr ← Hex4[r: l, pos: addrPos + 9 ! BadHex => GOTO TryNextLine];
IF l.Fetch[0] = '← THEN id ← GetMidID[r: l, pos: 1 ! BadID => GOTO TryNextLine]
ELSE id ← GetID[r: l, pos: 0 ! BadID => GOTO TryNextLine];
IF l.Find["C𡤌ODE"] # -1 THEN EnterProc[id: id, idAddr: idAddr]
ELSE EnterVar[id: id, idAddr: idAddr];
};
EXITS
TryNextLine => NULL;
};
ENDLOOP;
};
Here is the symbol table part
nameTable: OrderedSymbolTableRef.Table ← NIL;
addrTable: OrderedSymbolTableRef.Table ← NIL;
fileTable: OrderedSymbolTableRef.Table ← NIL;
CompareSTItemsByName: OrderedSymbolTableRef.CompareProc = {
x: LarkPrograms.STItem ← NARROW[r1];
y: LarkPrograms.STItem ← NARROW[r2];
RETURN[Rope.Compare[s1: x.id, s2: y.id, case: FALSE]];
};
CompareSTItemsByAddr: OrderedSymbolTableRef.CompareProc = {
x: LarkPrograms.STItem ← NARROW[r1];
y: LarkPrograms.STItem ← NARROW[r2];
RETURN[Basics.CompareINT[x.addr, y.addr]];
};
FileDataObject:
TYPE =
RECORD [
id: Rope.ROPE ← NIL,
addr: INT ← 0,
size: INT ← 0,
codeAddr: INT ← 0,
codeSize: INT ← 0,
dataAddr: INT ← 0,
dataSize: INT ← 0
];
FileData: TYPE = REF FileDataObject;
CompareFileItemsByAddr: OrderedSymbolTableRef.CompareProc = {
x: FileData ← NARROW[r1];
y: FileData ← NARROW[r2];
RETURN[Basics.CompareINT[x.addr, y.addr]];
};
EnterProc:
PROC [id: Rope.
ROPE, idAddr:
INT] = {
item: LarkPrograms.STItem ← NEW[LarkPrograms.STObject ← [id: id, addr: idAddr, type: procedure]];
nameTable.Insert[item ! OrderedSymbolTableRef.DuplicateKey => CONTINUE];
out.PutF["proc: %04x %g\n", IO.int[idAddr], IO.rope[id]];
};
EnterVar:
PROC [id: Rope.
ROPE, idAddr:
INT] = {
item: LarkPrograms.STItem ← NEW[LarkPrograms.STObject ← [id: id, addr: idAddr, type: variable]];
nameTable.Insert[item ! OrderedSymbolTableRef.DuplicateKey => CONTINUE];
out.PutF["var: %04x %g\n", IO.int[idAddr], IO.rope[id]];
};
EnterFile:
PROC [id: Rope.
ROPE, idAddr:
INT] = {
item: FileData ← NEW[FileDataObject ← [id: id, addr: idAddr]];
fileTable.Insert[item ! OrderedSymbolTableRef.DuplicateKey => CONTINUE];
};
MapParse: Commander.CommandProc = {
map: IO.STREAM;
extension: INT;
scanFileSymbols: INT;
argv: CommandTool.ArgumentVector;
out: IO.STREAM ← cmd.out;
PrintSTItem:
PROC [a:
REF
ANY]
RETURNS [
BOOL ←
FALSE] = {
c: CHAR;
item: LarkPrograms.STItem ← NARROW[a];
c ←
SELECT item.type
FROM
procedure => 'P,
variable => 'V,
ENDCASE => ERROR;
out.PutF["%c %04x %g\n", IO.char[c], IO.int[item.addr], IO.rope[item.id]];
};
EnterIntoAddrTable:
PROC [a:
REF
ANY]
RETURNS [
BOOL ←
FALSE] = {
addrTable.Insert[a ! OrderedSymbolTableRef.DuplicateKey => {
out.PutF["Duplicate:\n\t"];
[] ← PrintSTItem[a];
appears to be no way to get the other one, monitors locked
out.PutF["\t"];
[] ← PrintSTItem[addrTable.Lookup[a]];
CONTINUE;
}];
};
argv ← CommandTool.Parse[cmd ! CommandTool.Failed => {msg ← errorMsg; CONTINUE; }];
IF argv = NIL THEN RETURN[$Failure, msg];
IF argv.argc # 2
THEN {
out.PutF["Usage: MapParse MapFileName (without extension)\n"];
RETURN;
};
extension ← Rope.Find[s1: argv[1], s2: ".map", case: FALSE];
IF extension # -1 THEN argv[1] ← Rope.Substr[base: argv[1], start: 0, len: extension];
map ←
FS.StreamOpen[fileName: Rope.Cat[argv[1], ".map"] !
FS.Error => {
out.PutRope[error.explanation];
out.PutRope["\n"];
GOTO Die;
}];
nameTable ← OrderedSymbolTableRef.CreateTable[compareProc: CompareSTItemsByName];
addrTable ← OrderedSymbolTableRef.CreateTable[compareProc: CompareSTItemsByAddr];
fileTable ← OrderedSymbolTableRef.CreateTable[compareProc: CompareFileItemsByAddr];
ScanFile[map: map, out: out];
scanFileSymbols ← nameTable.Size[];
map.SetIndex[0];
TryForPublics[map: map, out: out];
out.PutF["TryForPublics found %d more symbols\n", IO.card[nameTable.Size[] - scanFileSymbols]];
nameTable.EnumerateIncreasing[procToApply: EnterIntoAddrTable];
map.Close[];
out.PutF["\nIdentifiers: (%d items)\n", IO.int[nameTable.Size[]]];
{
symsFileName: Rope.ROPE ← Rope.Cat[argv[1], ".syms"];
symsFile: IO.STREAM;
PrintFileItem:
PROC [a:
REF
ANY]
RETURNS [
BOOL ←
FALSE] = {
item: LarkPrograms.STItem ← NARROW[a];
symsFile.PutF["%04x %g\n", IO.int[item.addr], IO.rope[item.id]];
};
out.PutF["Writing symbol file: %g.\n", IO.rope[symsFileName]];
symsFile ←
FS.StreamOpen[fileName: symsFileName, accessOptions: $create !
FS.Error => {
out.PutRope[error.explanation];
out.PutRope["\n"];
GOTO Die;
}];
WriteSortedFile[errors: out, tabFile: symsFile, byName: TRUE];
WriteSortedFile[errors: out, tabFile: symsFile, byName: FALSE];
symsFile.Close[];
symsFileName ← Rope.Cat[argv[1], ".files"];
out.PutF["Writing files file: %g.\n", IO.rope[symsFileName]];
symsFile ←
FS.StreamOpen[fileName: symsFileName, accessOptions: $create !
FS.Error => {
out.PutRope[error.explanation];
out.PutRope["\n"];
GOTO Die;
}];
PrintFileData[tab: fileTable, syms: symsFile, log: out];
symsFile.Close[];
EXITS
Die => NULL;
};
out.PutF["Releasing RedBlackTrees.\n"];
IF nameTable #
NIL
THEN {
nameTable.DestroyTable[];
nameTable ← NIL;
};
IF addrTable #
NIL
THEN {
addrTable.DestroyTable[];
addrTable ← NIL;
};
IF fileTable #
NIL
THEN {
fileTable.DestroyTable[];
fileTable ← NIL;
};
EXITS
Die => NULL;
};
PrintFileData:
PROC [tab: OrderedSymbolTableRef.Table, syms, log:
IO.
STREAM] = {
fileList: LIST OF REF ANY;
previous: FileData ← NIL;
totalCode: INT ← 0;
totalData: INT ← 0;
GetSizes:
PROC [a:
REF
ANY]
RETURNS [
BOOL ←
FALSE] = {
item: FileData ← NARROW[a];
IF previous # NIL THEN previous.size ← item.addr - previous.addr;
previous ← item;
};
CollectFileData:
PROC [a:
REF
ANY]
RETURNS [
BOOL ←
FALSE] = {
item: FileData ← NARROW[a];
FOR l:
LIST
OF
REF
ANY ← fileList, l.rest
WHILE l #
NIL
DO
file: FileData ← NARROW[l.first];
IF Rope.Equal[item.id, file.id,
FALSE]
THEN {
file.dataAddr ← item.addr;
file.dataSize ← item.size;
RETURN;
};
ENDLOOP;
item.codeAddr ← item.addr;
item.codeSize ← item.size;
fileList ← CONS[item, fileList];
};
tab.EnumerateIncreasing[procToApply: GetSizes];
tab.EnumerateIncreasing[procToApply: CollectFileData];
fileList ← List.Reverse[fileList];
syms.PutF["Module code size data size total\n"];
FOR l:
LIST
OF
REF
ANY ← fileList, l.rest
WHILE l #
NIL
DO
item: FileData ← NARROW[l.first];
totalCode ← totalCode + item.codeSize;
totalData ← totalData + item.dataSize;
syms.PutF["%-20g %04x %5d", IO.rope[item.id], IO.card[item.codeAddr], IO.card[item.codeSize]];
syms.PutF[" %04x %5d %5d\n", IO.card[item.dataAddr], IO.card[item.dataSize], IO.card[item.codeSize + item.dataSize]];
ENDLOOP;
syms.PutF["Totals: Code %d, Data %d, Everything %d\n", IO.card[totalCode], IO.card[totalData], IO.card[totalCode + totalData]];
};
WriteSortedFile: PROC [errors: IO.STREAM, tabFile: IO.STREAM, byName: BOOL] = {
tab: OrderedSymbolTableRef.Table ←
IF byName
THEN nameTable
ELSE addrTable;
PrintSTItem:
PROC [a:
REF
ANY]
RETURNS [
BOOL ←
FALSE] = {
c: CHAR;
item: LarkPrograms.STItem ← NARROW[a];
c ←
SELECT item.type
FROM
procedure => 'P,
variable => 'V,
ENDCASE => ERROR;
tabFile.PutF["%c %04x %g\n", IO.char[c], IO.int[item.addr], IO.rope[item.id]];
};
tabFile.PutF[IF byName THEN "Names %d\n" ELSE "Addresses %d\n", IO.int[tab.Size[]]];
tab.EnumerateIncreasing[procToApply: PrintSTItem];
};
Commander.Register[key: "MapParse", proc: MapParse, doc: "MapParse usage: MapParse Lark for Lark.map"];
END.
November 15, 1982 9:51 pm, L. Stewart, Created
November 16, 1982 8:46 am, L. Stewart, added RedBlackTree stuff
November 16, 1982 2:39 pm, L. Stewart, integration with Teleload
November 19, 1982 4:59 pm, L. Stewart
December 6, 1982 4:41 pm, L. Stewart, Cedar 3.5
December 18, 1982 4:38 pm, L. Stewart, Stand alone service
May 2, 1983 12:47 pm, L. Stewart, LarkPrograms
December 22, 1983 1:56 pm, L. Stewart, Cedar 5