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.ROPENIL,
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 [BOOLFALSE] = {
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 [BOOLFALSE] = {
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 [BOOLFALSE] = {
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 [BOOLFALSE] = {
item: FileData ← NARROW[a];
IF previous # NIL THEN previous.size ← item.addr - previous.addr;
previous ← item;
};
CollectFileData: PROC [a: REF ANY] RETURNS [BOOLFALSE] = {
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 [BOOLFALSE] = {
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