FoxDisassembler.Mod 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. MODULE FoxDisassembler; (** AUTHOR ""; PURPOSE ""; *)
  2. IMPORT Streams, ObjectFile, Scanner := FoxScanner, Basic := FoxBasic, BitSets, D := Debugging, Files, Commands;
  3. CONST Trace = FALSE;
  4. TYPE
  5. Block*= POINTER TO RECORD (ObjectFile.Section)
  6. to-: LONGINT;
  7. next-: Block;
  8. END;
  9. Disassembler* = OBJECT
  10. VAR
  11. first, block: Block; w: Streams.Writer; stringWriter: Streams.StringWriter; code,data: BitSets.BitSet; codeUnit, dataUnit: LONGINT;
  12. codeDisplacement-, dataDisplacement: LONGINT;
  13. PROCEDURE & Init*(w: Streams.Writer);
  14. BEGIN SELF.w := w; NEW(stringWriter, 256);
  15. codeDisplacement := 0; dataDisplacement := 0;
  16. END Init;
  17. PROCEDURE GetLogFile*(CONST binaryFileName: ARRAY OF CHAR): Files.File;
  18. VAR fileName,extension: Files.FileName;
  19. BEGIN
  20. Files.SplitExtension(binaryFileName, fileName, extension);
  21. Files.JoinExtension(fileName, "log", fileName);
  22. RETURN Files.Old(fileName)
  23. END GetLogFile;
  24. PROCEDURE BlockHeader(block: Block);
  25. VAR name: ObjectFile.SectionName; offset: LONGINT;
  26. BEGIN
  27. Basic.SegmentedNameToString(block.identifier.name, name);
  28. w.String("-------- "); w.Char(Scanner.TAB);
  29. IF ObjectFile.IsCode(block.type) THEN w.String("code");
  30. ELSE w.String("data");
  31. END;
  32. w.Char(Scanner.TAB);
  33. w.String(name);
  34. w.String(" @");
  35. w.Hex(block.alignment,-8); (*w.String("-"); w.Hex(block.to+offset,-8);*)
  36. w.String(" ---");
  37. w.Ln;
  38. END BlockHeader;
  39. PROCEDURE WriteReference*(adr: LONGINT; isCode: BOOLEAN; w: Streams.Writer);
  40. VAR b: Block; name: ObjectFile.SectionName; offset: LONGINT;
  41. BEGIN
  42. w.String(" --> "); w.Hex(adr+codeDisplacement,-8);
  43. b := first; IF b = NIL THEN RETURN END;
  44. IF isCode THEN offset := codeDisplacement ELSE offset := dataDisplacement END;
  45. WHILE (b # NIL) & ((adr < b.alignment-offset) OR (adr > b.to - offset) OR (ObjectFile.IsCode(b.type)#isCode)) DO
  46. b := b.next;
  47. END;
  48. IF b = NIL THEN
  49. (* try to find any matching section *)
  50. WHILE (b # NIL) & ((adr < b.alignment-offset) OR (adr > b.to - offset)) DO
  51. b := b.next;
  52. END;
  53. END;
  54. IF (b # NIL) & (b # block) THEN
  55. Basic.SegmentedNameToString(b.identifier.name, name);
  56. w.String(" [");
  57. w.String(name);
  58. IF adr # b.alignment THEN
  59. w.String("+"); w.Int(adr-b.alignment+offset,1)
  60. END;
  61. w.String("]");
  62. END;
  63. END WriteReference;
  64. PROCEDURE DisassembleInstruction*(bitset: BitSets.BitSet; VAR adrInUnits: LONGINT; maxInstructionSize: LONGINT; w: Streams.Writer);
  65. BEGIN
  66. END DisassembleInstruction;
  67. PROCEDURE DisassembleBlock(from, to: LONGINT);
  68. VAR adr, prevadr, max, digit,value: LONGINT; string: ARRAY 256 OF CHAR;
  69. BEGIN
  70. IF code = NIL THEN RETURN END;
  71. adr := from;
  72. max := MIN(to+1, code.GetSize() DIV codeUnit);
  73. WHILE adr < max DO
  74. (* adr *)
  75. w.Hex(adr+codeDisplacement,-8); w.String(": ");
  76. prevadr := adr;
  77. DisassembleInstruction(code, adr, max-adr, stringWriter);
  78. IF prevadr = adr THEN w.String("decoder error: address must increase"); w.Ln; RETURN END;
  79. stringWriter.Update;
  80. stringWriter.Get(string);
  81. (* value *)
  82. WHILE prevadr < adr DO
  83. value := code.GetBits(prevadr*codeUnit, codeUnit);
  84. w.Hex(value,-((codeUnit-1) DIV 4 +1)); w.String(" ");
  85. INC(prevadr);
  86. END;
  87. (* instruction string *)
  88. w.Char(Scanner.TAB); w.String(string); w.Ln;
  89. END;
  90. END DisassembleBlock;
  91. PROCEDURE DataBlock(from, to: LONGINT);
  92. VAR adr,width,max,value: LONGINT;
  93. BEGIN
  94. IF data = NIL THEN RETURN END;
  95. adr := from;
  96. max := MIN(to+1, data.GetSize() DIV dataUnit);
  97. WHILE adr < max DO
  98. w.Hex(adr+dataDisplacement,-8); w.String(": ");
  99. width := 8;
  100. WHILE (adr < max) & (width > 0) DO
  101. value := data.GetBits(adr*dataUnit, dataUnit);
  102. w.Hex(value,-((dataUnit-1) DIV 4 +1)); w.String(" ");
  103. INC(adr); DEC(width);
  104. END;
  105. w.Ln;
  106. END;
  107. END DataBlock;
  108. PROCEDURE ParseLogFile*(file: Files.File): Block;
  109. VAR reader: Files.Reader; newline: BOOLEAN; sectionName: ObjectFile.SectionName; scanner: Scanner.AssemblerScanner;
  110. symbol: Scanner.Symbol; b: BOOLEAN; block: Block; first: Block; last: Block;
  111. PROCEDURE GetNextSymbol;
  112. VAR b: BOOLEAN;
  113. BEGIN b := scanner.GetNextSymbol(symbol)
  114. END GetNextSymbol;
  115. PROCEDURE ExpectToken(token: LONGINT): BOOLEAN;
  116. BEGIN IF symbol.token = token THEN GetNextSymbol; RETURN TRUE ELSE RETURN FALSE END;
  117. END ExpectToken;
  118. PROCEDURE ExpectIdentifier(VAR name: ARRAY OF CHAR): BOOLEAN;
  119. BEGIN
  120. IF (symbol.token = Scanner.Identifier) THEN COPY(symbol.identifierString,name); GetNextSymbol; RETURN TRUE
  121. ELSE RETURN FALSE
  122. END;
  123. END ExpectIdentifier;
  124. PROCEDURE ExpectThisIdentifier(CONST name: ARRAY OF CHAR): BOOLEAN;
  125. BEGIN
  126. IF (symbol.token = Scanner.Identifier) & (symbol.identifierString = name) THEN GetNextSymbol; RETURN TRUE
  127. ELSE RETURN FALSE
  128. END;
  129. END ExpectThisIdentifier;
  130. PROCEDURE ExpectNumber(VAR int: LONGINT):BOOLEAN;
  131. BEGIN
  132. IF (symbol.token = Scanner.Number) THEN
  133. int := SHORT(symbol.hugeint); GetNextSymbol; RETURN TRUE
  134. ELSE RETURN FALSE
  135. END;
  136. END ExpectNumber;
  137. PROCEDURE ParseLine(): Block;
  138. VAR from,to: LONGINT; block: Block; displacement: LONGINT;
  139. BEGIN
  140. block := NIL;
  141. IF ExpectNumber(from) & ExpectToken(Scanner.Colon) THEN
  142. IF ExpectThisIdentifier("code") & ExpectIdentifier(sectionName) & ExpectThisIdentifier("to") & ExpectNumber(to) THEN
  143. NEW(block); block.type := ObjectFile.Code; Basic.ToSegmentedName(sectionName, block.identifier.name); block.alignment := from; block.fixed := TRUE; block.to := to;
  144. ELSIF ExpectThisIdentifier("data") & ExpectIdentifier(sectionName) & ExpectThisIdentifier("to") & ExpectNumber(to) THEN
  145. NEW(block); block.type := ObjectFile.Data; Basic.ToSegmentedName(sectionName, block.identifier.name); block.alignment := from; block.fixed := TRUE; block.to := to;
  146. END;
  147. ELSIF ExpectThisIdentifier("code") & ExpectThisIdentifier("displacement") & ExpectNumber(displacement) THEN
  148. codeDisplacement := displacement; dataDisplacement := displacement
  149. ELSIF ExpectThisIdentifier("data") & ExpectThisIdentifier("displacement") & ExpectNumber(displacement) THEN
  150. dataDisplacement := displacement;
  151. ELSE scanner.SkipToEndOfLine; GetNextSymbol; scanner.ResetError;
  152. END;
  153. IF (block # NIL) & Trace THEN
  154. D.String("found section ");
  155. D.String(sectionName);
  156. IF ObjectFile.IsCode(block.type) THEN D.String(" (code) ") ELSE D.String(" (data) ") END;
  157. D.Int(block.alignment,1); D.String(" "); D.Int(block.to,1);
  158. D.Ln;
  159. END;
  160. RETURN block
  161. END ParseLine;
  162. BEGIN
  163. first := NIL; last := NIL;
  164. IF file = NIL THEN RETURN NIL END;
  165. NEW(reader, file, 0);
  166. scanner := Scanner.NewAssemblerScanner("",reader,0,NIL);
  167. b := scanner.GetNextSymbol(symbol);
  168. REPEAT
  169. block := ParseLine();
  170. IF block # NIL THEN
  171. IF first = NIL THEN first := block; last := block ELSE last.next := block; last := block END;
  172. END;
  173. GetNextSymbol
  174. UNTIL symbol.token = Scanner.EndOfText;
  175. RETURN first;
  176. END ParseLogFile;
  177. PROCEDURE Disassemble*(code, data: BitSets.BitSet; codeUnit, dataUnit: LONGINT; logFile: Files.File; address: ADDRESS);
  178. BEGIN
  179. SELF.code := code;SELF.data := data; SELF.codeUnit := codeUnit; SELF.dataUnit := dataUnit;
  180. first := ParseLogFile(logFile); block := first;
  181. IF block = NIL THEN
  182. w.String("------ code ------"); w.Ln;
  183. DisassembleBlock(0, MAX(LONGINT)-1);
  184. IF code # data THEN
  185. w.String("------ data ------"); w.Ln;
  186. DataBlock(0, MAX(LONGINT)-1);
  187. END;
  188. ELSE
  189. WHILE block # NIL DO
  190. IF (address = 0) OR (block.alignment <= address) & (block.to > address) THEN
  191. BlockHeader(block);
  192. IF ObjectFile.IsCode(block.type) & (code # NIL) THEN
  193. DisassembleBlock(block.alignment-codeDisplacement, block.to-codeDisplacement);
  194. ELSE
  195. DataBlock(block.alignment-dataDisplacement, block.to-dataDisplacement);
  196. END;
  197. END;
  198. block := block.next
  199. END;
  200. END;
  201. END Disassemble;
  202. PROCEDURE SetDisplacements*(code, data: LONGINT);
  203. BEGIN
  204. codeDisplacement := code;
  205. dataDisplacement := data;
  206. END SetDisplacements;
  207. END Disassembler;
  208. PROCEDURE FindPC*(context: Commands.Context);
  209. VAR file: Files.File; logFile: Files.FileName; adr,maxadr: LONGINT; disassembler: Disassembler; block, found: Block; name: ObjectFile.SectionName;
  210. BEGIN
  211. IF context.arg.GetString(logFile) & context.arg.GetInteger(adr, TRUE) THEN
  212. file := Files.Old(logFile);
  213. IF file = NIL THEN Files.JoinExtension(logFile,".log",logFile); file := Files.Old(logFile) END;
  214. IF file = NIL THEN
  215. context.error.String("file not found "); context.error.String(logFile); context.error.Ln
  216. ELSE
  217. NEW(disassembler, context.out);
  218. maxadr := 0;
  219. block := disassembler.ParseLogFile(file);
  220. WHILE (block # NIL ) DO
  221. IF (block.alignment < adr) & (block.alignment > maxadr) & ObjectFile.IsCode(block.type) THEN
  222. found := block;
  223. maxadr := block.alignment
  224. END;
  225. block := block.next
  226. END;
  227. Basic.SegmentedNameToString(found.identifier.name, name);
  228. context.out.String(name); context.out.String(":"); context.out.Int(adr-found.alignment,1); context.out.Ln;
  229. END;
  230. END;
  231. END FindPC;
  232. (*
  233. PROCEDURE Test*(context: Commands.Context);
  234. VAR filename: Files.FileName; name: ObjectFile.SectionName; block: Block;
  235. BEGIN
  236. IF context.arg.GetString(filename) THEN
  237. file := Files.Old(filename);
  238. IF file = NIL THEN (* error *) HALT(100) END;
  239. block := ParseLogFile(filename);
  240. WHILE block # NIL DO
  241. Basic.SegmentedNameToString(block.identifier.name, name);
  242. context.out.String(name); context.out.String(" at "); context.out.Int(block.alignment,1); context.out.Ln;
  243. block := block.next;
  244. END;
  245. END;
  246. END Test;
  247. *)
  248. END FoxDisassembler.
  249. SystemTools.FreeDownTo FoxDisassembler ~
  250. FoxDisassembler.Test ins.log ~