testdisa.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  1. /*++
  2. Copyright (c) 2012 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. testdisa.c
  9. Abstract:
  10. This program tests the disassembler by feeding it instructions as input.
  11. Author:
  12. Evan Green 21-Jun-2012
  13. Environment:
  14. Development
  15. --*/
  16. //
  17. // ------------------------------------------------------------------- Includes
  18. //
  19. #include <minoca/lib/types.h>
  20. #include <minoca/lib/status.h>
  21. #include <minoca/lib/im.h>
  22. #include "../disasm.h"
  23. #include <assert.h>
  24. #include <stdarg.h>
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <string.h>
  28. //
  29. // ---------------------------------------------------------------- Definitions
  30. //
  31. #define MALLOC malloc
  32. #define FREE free
  33. //
  34. // ----------------------------------------------- Internal Function Prototypes
  35. //
  36. ULONG
  37. DbgpPrintAddress (
  38. PDISASSEMBLED_INSTRUCTION Instruction,
  39. BOOL Print
  40. );
  41. LONG
  42. DbgpGetFileSize (
  43. FILE *File
  44. );
  45. //
  46. // -------------------------------------------------------------------- Globals
  47. //
  48. //
  49. // ------------------------------------------------------ Data Type Definitions
  50. //
  51. //
  52. // ------------------------------------------------------------------ Functions
  53. //
  54. INT
  55. main (
  56. INT ArgumentCount,
  57. CHAR **Arguments
  58. )
  59. /*++
  60. Routine Description:
  61. This routine is the main entry point for the program. It collects the
  62. options passed to it, and invokes the disassembler.
  63. Arguments:
  64. ArgumentCount - Supplies the number of command line arguments the program
  65. was invoked with.
  66. Arguments - Supplies a tokenized array of command line arguments.
  67. Return Value:
  68. Returns an integer exit code. 0 for success, nonzero otherwise.
  69. --*/
  70. {
  71. ULONG ArmInstruction;
  72. ULONG BytesDisassembled;
  73. ULONG BytesRead;
  74. PUCHAR CurrentInstruction;
  75. DISASSEMBLED_INSTRUCTION Disassembly;
  76. CHAR DisassemblyBuffer[1024];
  77. ULONG Failures;
  78. FILE *File;
  79. PVOID FileBuffer;
  80. PSTR Filename;
  81. LONG FileSize;
  82. BOOL ForceThumb;
  83. IMAGE_BUFFER ImageBuffer;
  84. IMAGE_INFORMATION ImageInformation;
  85. PUCHAR InstructionStream;
  86. MACHINE_LANGUAGE Language;
  87. PSTR LanguageString;
  88. BOOL PrintDisassembly;
  89. BOOL Result;
  90. KSTATUS Status;
  91. ULONG TextSize;
  92. ULONGLONG TextVirtualAddress;
  93. Failures = 0;
  94. FileBuffer = NULL;
  95. ForceThumb = FALSE;
  96. memset(&ImageBuffer, 0, sizeof(IMAGE_BUFFER));
  97. InstructionStream = NULL;
  98. PrintDisassembly = TRUE;
  99. if (ArgumentCount < 2) {
  100. printf("Usage: testdisa [-q] [-t] <file>\n"
  101. "Options:\n"
  102. " -q Quiet. Don't print disassembly, only errors.\n"
  103. " -t Force thumb mode. Only applies to ARM images.\n");
  104. return 1;
  105. }
  106. while (TRUE) {
  107. if (strcasecmp(Arguments[1], "-q") == 0) {
  108. PrintDisassembly = FALSE;
  109. Arguments += 1;
  110. } else if (strcasecmp(Arguments[1], "-t") == 0) {
  111. ForceThumb = TRUE;
  112. Arguments += 1;
  113. } else {
  114. break;
  115. }
  116. }
  117. //
  118. // Determine the file size and load the file into memory.
  119. //
  120. Filename = Arguments[1];
  121. File = fopen(Filename, "rb");
  122. if (File == NULL) {
  123. Result = FALSE;
  124. Failures += 1;
  125. goto MainEnd;
  126. }
  127. FileSize = DbgpGetFileSize(File);
  128. if (FileSize <= 0) {
  129. Result = FALSE;
  130. Failures += 1;
  131. goto MainEnd;
  132. }
  133. FileBuffer = MALLOC(FileSize);
  134. if (FileBuffer == NULL) {
  135. Result = FALSE;
  136. Failures += 1;
  137. goto MainEnd;
  138. }
  139. BytesRead = fread(FileBuffer, 1, FileSize, File);
  140. if (BytesRead != FileSize) {
  141. Result = FALSE;
  142. Failures += 1;
  143. goto MainEnd;
  144. }
  145. ImageBuffer.Data = FileBuffer;
  146. ImageBuffer.Size = FileSize;
  147. Status = ImGetImageInformation(&ImageBuffer, &ImageInformation);
  148. if (!KSUCCESS(Status)) {
  149. Result = FALSE;
  150. Failures += 1;
  151. goto MainEnd;
  152. }
  153. //
  154. // Get the text section.
  155. //
  156. Result = ImGetImageSection(&ImageBuffer,
  157. ".text",
  158. (PVOID *)&InstructionStream,
  159. &TextVirtualAddress,
  160. &TextSize,
  161. NULL);
  162. if (Result == FALSE) {
  163. printf("Error: Could not load text section for file %s.\n", Filename);
  164. Failures += 1;
  165. goto MainEnd;
  166. }
  167. //
  168. // Determine the machine language.
  169. //
  170. Language = MachineLanguageInvalid;
  171. LanguageString = "Unknown";
  172. switch (ImageInformation.Machine) {
  173. case ImageMachineTypeX86:
  174. Language = MachineLanguageX86;
  175. LanguageString = "x86";
  176. break;
  177. case ImageMachineTypeX64:
  178. Language = MachineLanguageX64;
  179. LanguageString = "x64";
  180. break;
  181. case ImageMachineTypeArm32:
  182. Language = MachineLanguageArm;
  183. LanguageString = "ARM";
  184. if (((ImageInformation.EntryPoint & 0x1) != 0) ||
  185. (ForceThumb != FALSE)) {
  186. Language = MachineLanguageThumb2;
  187. LanguageString = "Thumb2";
  188. }
  189. break;
  190. default:
  191. printf("Unknown machine type %d!\n", ImageInformation.Machine);
  192. Failures += 1;
  193. goto MainEnd;
  194. }
  195. if (PrintDisassembly != FALSE) {
  196. printf("Disassembling %s (%s), VA 0x%llx, 0x%x bytes.\n",
  197. Filename,
  198. LanguageString,
  199. TextVirtualAddress,
  200. TextSize);
  201. }
  202. //
  203. // Disassemble the file contents.
  204. //
  205. BytesDisassembled = 0;
  206. CurrentInstruction = InstructionStream;
  207. while (BytesDisassembled < TextSize) {
  208. //
  209. // Print the offset from the start of disassembly and disassemble the
  210. // instruction.
  211. //
  212. if (PrintDisassembly != FALSE) {
  213. printf("\n%08llx: ", TextVirtualAddress + BytesDisassembled);
  214. }
  215. Result = DbgDisassemble(TextVirtualAddress + BytesDisassembled,
  216. CurrentInstruction,
  217. DisassemblyBuffer,
  218. sizeof(DisassemblyBuffer),
  219. &Disassembly,
  220. Language);
  221. if (Result == FALSE) {
  222. Failures += 1;
  223. printf("ERROR decoding instruction, partial string: ");
  224. DisassemblyBuffer[99] = '\0';
  225. printf("%s", DisassemblyBuffer);
  226. goto MainEnd;
  227. }
  228. //
  229. // For ARM, print the binary code first, since it's always a pretty
  230. // consistent size.
  231. //
  232. if (Language == MachineLanguageArm) {
  233. if (Disassembly.BinaryLength != 4) {
  234. printf("Error: got %d byte ARM disassembly.\n",
  235. Disassembly.BinaryLength);
  236. Failures += 1;
  237. }
  238. ArmInstruction = *((PULONG)CurrentInstruction);
  239. CurrentInstruction += Disassembly.BinaryLength;
  240. BytesDisassembled += Disassembly.BinaryLength;
  241. if (PrintDisassembly != FALSE) {
  242. printf("%08x ", ArmInstruction);
  243. }
  244. } else if (Language == MachineLanguageThumb2) {
  245. ArmInstruction = *((PUSHORT)CurrentInstruction);
  246. if (PrintDisassembly != FALSE) {
  247. printf(" %04x", ArmInstruction);
  248. }
  249. if (Disassembly.BinaryLength == 4) {
  250. ArmInstruction = *(((PUSHORT)CurrentInstruction) + 1);
  251. if (PrintDisassembly != FALSE) {
  252. printf("%04x ", ArmInstruction);
  253. }
  254. } else if (Disassembly.BinaryLength == 2) {
  255. if (PrintDisassembly != FALSE) {
  256. printf(" ");
  257. }
  258. } else if (Disassembly.BinaryLength != 2) {
  259. printf("Error: Got %d byte Thumb-2 disassembly.\n",
  260. Disassembly.BinaryLength);
  261. Failures += 1;
  262. ArmInstruction = *((PULONG)CurrentInstruction);
  263. }
  264. CurrentInstruction += Disassembly.BinaryLength;
  265. BytesDisassembled += Disassembly.BinaryLength;
  266. }
  267. //
  268. // Print the mnemonic, which should exist in any case.
  269. //
  270. if (Disassembly.Mnemonic == NULL) {
  271. printf("Error: NULL opcode mnemonic.\n");
  272. Failures += 1;
  273. }
  274. if (PrintDisassembly != FALSE) {
  275. printf("%s\t", Disassembly.Mnemonic);
  276. }
  277. //
  278. // Attempt to print the first (destination) operand. If the operand
  279. // is an address, print that as well.
  280. //
  281. if (Disassembly.DestinationOperand != NULL) {
  282. if (strcasecmp(Disassembly.DestinationOperand, "err") == 0) {
  283. printf("Error: got ERR destination operand!\n");
  284. Failures += 1;
  285. }
  286. if (PrintDisassembly != FALSE) {
  287. printf("%s", Disassembly.DestinationOperand);
  288. }
  289. if (Disassembly.AddressIsDestination != FALSE) {
  290. if (DbgpPrintAddress(&Disassembly, PrintDisassembly) != 0) {
  291. printf("Error: Invalid operand address.\n");
  292. Failures += 1;
  293. }
  294. }
  295. //
  296. // Attempt to print the second (source) operand. If the operand is
  297. // an address, print that as well.
  298. //
  299. if (Disassembly.SourceOperand != NULL) {
  300. if (strcasecmp(Disassembly.DestinationOperand, "err") == 0) {
  301. printf("Error: got ERR source operand!\n");
  302. Failures += 1;
  303. }
  304. if (PrintDisassembly != FALSE) {
  305. printf(", %s", Disassembly.SourceOperand);
  306. }
  307. if (Disassembly.AddressIsDestination == FALSE) {
  308. if (DbgpPrintAddress(&Disassembly, PrintDisassembly) != 0) {
  309. printf("Error: Invalid operand address.\n");
  310. Failures += 1;
  311. }
  312. }
  313. //
  314. // Attempt to print the third operand. This operand only exists
  315. // in rare circumstances on x86, and can never be an address.
  316. // On ARM, third and fourth operands are the norm.
  317. //
  318. if (Disassembly.ThirdOperand != NULL) {
  319. if (strcasecmp(Disassembly.ThirdOperand, "err") == 0) {
  320. printf("Error: got ERR source operand!\n");
  321. Failures += 1;
  322. }
  323. if (PrintDisassembly != FALSE) {
  324. printf(", %s", Disassembly.ThirdOperand);
  325. }
  326. //
  327. // Print the fourth operand, which will only ever be set
  328. // on ARM.
  329. //
  330. if ((Disassembly.FourthOperand != NULL) &&
  331. (PrintDisassembly != FALSE)) {
  332. printf(", %s", Disassembly.FourthOperand);
  333. }
  334. //
  335. // If the third operand wasn't present, a fourth better not be
  336. // either.
  337. //
  338. } else if (Disassembly.FourthOperand != NULL) {
  339. printf("Error: Got fourth operand but no third!\n");
  340. Failures += 1;
  341. }
  342. } else {
  343. //
  344. // If there was no second operand, there should definitely be
  345. // no third or fourth operand.
  346. //
  347. if ((Disassembly.ThirdOperand != NULL) ||
  348. (Disassembly.FourthOperand != NULL)) {
  349. printf("Error: Got third/fourth operands but no second "
  350. "operand!\n");
  351. Failures += 1;
  352. }
  353. }
  354. } else {
  355. //
  356. // If there was no first operand, there should definitely be no
  357. // second, third, or fourth operand.
  358. //
  359. if ((Disassembly.SourceOperand != NULL) ||
  360. (Disassembly.ThirdOperand != NULL) ||
  361. (Disassembly.FourthOperand != NULL)) {
  362. printf("Error: Got second/third/fourth operand, but no "
  363. "first!\n");
  364. Failures += 1;
  365. }
  366. }
  367. //
  368. // Print the binary contents for x86 disassembly.
  369. //
  370. if ((Language == MachineLanguageX86) ||
  371. (Language == MachineLanguageX64)) {
  372. if (Disassembly.BinaryLength == 0) {
  373. printf("Error: got a zero length instruction\n");
  374. Failures += 1;
  375. goto MainEnd;
  376. }
  377. if (PrintDisassembly != FALSE) {
  378. printf(" \t; ");
  379. }
  380. while (Disassembly.BinaryLength != 0) {
  381. if (PrintDisassembly != FALSE) {
  382. printf("%02x", *CurrentInstruction);
  383. }
  384. CurrentInstruction += 1;
  385. BytesDisassembled += 1;
  386. Disassembly.BinaryLength -= 1;
  387. }
  388. }
  389. }
  390. if (PrintDisassembly != FALSE) {
  391. printf("\n");
  392. }
  393. MainEnd:
  394. if (FileBuffer != NULL) {
  395. FREE(FileBuffer);
  396. }
  397. if (Failures != 0) {
  398. printf("\n*** %d Failures in disassembly test for file %s! ***\n",
  399. Failures,
  400. Filename);
  401. return 1;
  402. } else {
  403. printf("All disassembler tests passed for file %s.\n", Filename);
  404. }
  405. return 0;
  406. }
  407. //
  408. // --------------------------------------------------------- Internal Functions
  409. //
  410. ULONG
  411. DbgpPrintAddress (
  412. PDISASSEMBLED_INSTRUCTION Instruction,
  413. BOOL Print
  414. )
  415. /*++
  416. Routine Description:
  417. This routine prints an address encoded in a disassembled instruction.
  418. Arguments:
  419. Instruction - Supplies a pointer to the instruction containing the address
  420. to decode.
  421. Print - Supplies a boolean indicating if the value should
  422. actually be printed.
  423. Return Value:
  424. Returns 0 on success, or 1 on failure.
  425. --*/
  426. {
  427. if (Instruction->AddressIsValid == FALSE) {
  428. return 0;
  429. }
  430. if (Print != FALSE) {
  431. printf(" (0x%08llx)", Instruction->OperandAddress);
  432. }
  433. return 0;
  434. }
  435. LONG
  436. DbgpGetFileSize (
  437. FILE *File
  438. )
  439. /*++
  440. Routine Description:
  441. This routine determines the size of an opened file.
  442. Arguments:
  443. File - Supplies the file handle.
  444. Return Value:
  445. Returns the file length.
  446. --*/
  447. {
  448. INT CurrentPosition;
  449. LONG FileSize;
  450. CurrentPosition = ftell(File);
  451. fseek(File, 0, SEEK_END);
  452. FileSize = ftell(File);
  453. fseek(File, CurrentPosition, SEEK_SET);
  454. return FileSize;
  455. }