create_tests.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. #!/usr/bin/env node
  2. "use strict";
  3. // TODO
  4. // - multiple random tests
  5. const assert = require("assert").strict;
  6. const fs = require("fs");
  7. const encodings = require("../../gen/x86_table.js");
  8. const Prand = require("./prand.js");
  9. generate_tests();
  10. function generate_tests()
  11. {
  12. const build_folder = __dirname + "/build/";
  13. try
  14. {
  15. fs.mkdirSync(build_folder);
  16. }
  17. catch(e)
  18. {
  19. if(e.code !== "EEXIST")
  20. {
  21. throw e;
  22. }
  23. }
  24. for(const op of encodings)
  25. {
  26. const configurations = [
  27. { mem: 0, size: 16, },
  28. { mem: 0, size: 32, },
  29. { mem: 1, size: 16, },
  30. { mem: 1, size: 32, },
  31. ];
  32. let i = 0;
  33. for(const config of configurations)
  34. {
  35. for(const code of create_nasm(op, config))
  36. {
  37. const filename = "gen_" + format_opcode(op.opcode) + "_" + (op.fixed_g || 0) + "_" + i + ".asm";
  38. const dirname = build_folder + filename;
  39. let old_code = undefined;
  40. try
  41. {
  42. old_code = fs.readFileSync(dirname, { encoding: "ascii" });
  43. }
  44. catch(e)
  45. {
  46. }
  47. if(old_code !== code)
  48. {
  49. console.log("Creating %s", filename);
  50. fs.writeFileSync(dirname, code);
  51. }
  52. i++;
  53. }
  54. }
  55. }
  56. }
  57. function format_opcode(n)
  58. {
  59. let x = n.toString(16);
  60. return (x.length === 1 || x.length === 3) ? "0" + x : x;
  61. }
  62. function create_nasm_modrm_combinations_16()
  63. {
  64. let result = [];
  65. for(let modrm = 0; modrm < 0xC0; modrm++)
  66. {
  67. let mod = modrm >> 6;
  68. let rm = modrm & 7;
  69. let has_imm8 = mod === 1;
  70. let has_imm16 = mod === 2 || rm === 6 && mod === 0;
  71. assert(!has_imm8 || !has_imm16);
  72. let line = ["db " + modrm];
  73. if(has_imm8) line.push("db 9ah");
  74. if(has_imm16) line.push("dw 9a1fh");
  75. result.push(line);
  76. }
  77. return result;
  78. }
  79. function create_nasm_modrm_combinations_32()
  80. {
  81. let result = [];
  82. let sample_sib_bytes = [0x05, 0x65, 0xAD, 0xCD, 0x20, 0xFF];
  83. let exhaustive_sib_bytes = [];
  84. for(let sib = 0; sib < 0x100; sib++) exhaustive_sib_bytes.push(sib);
  85. for(let modrm = 0; modrm < 0xC0; modrm++)
  86. {
  87. let mod = modrm >> 6;
  88. let reg = modrm >> 3 & 7;
  89. let rm = modrm & 7;
  90. let has_imm8 = mod === 1;
  91. let has_imm32 = mod === 2 || rm === 5 && mod === 0;
  92. let has_sib = rm === 4;
  93. assert(!has_imm8 || !has_imm32);
  94. if(has_sib)
  95. {
  96. // avoid generating an excessive number of tests
  97. let sib_bytes = reg === 0 ? exhaustive_sib_bytes : sample_sib_bytes;
  98. for(let sib of sib_bytes)
  99. {
  100. let line = ["db " + modrm, "db " + sib];
  101. if(has_imm8) line.push("db 9ah");
  102. if(has_imm32 || mod === 0 && (sib & 7) === 5) line.push("dd 9a1fbcdeh");
  103. result.push(line);
  104. }
  105. }
  106. else
  107. {
  108. let line = ["db " + modrm];
  109. if(has_imm8) line.push("db 9ah");
  110. if(has_imm32) line.push("dd 9a1fbcdeh");
  111. result.push(line);
  112. }
  113. }
  114. return result;
  115. }
  116. function create_nasm(op, config)
  117. {
  118. const op_rand = new Prand(op.opcode);
  119. if(op.prefix || op.skip)
  120. {
  121. return [];
  122. }
  123. if(config.mem ? op.skip_mem : op.skip_reg)
  124. {
  125. // Not supported by test
  126. return [];
  127. }
  128. if(!op.e)
  129. {
  130. if(config.mem)
  131. {
  132. // doesn't use memory, don't test both
  133. return [];
  134. }
  135. }
  136. if(!op.os)
  137. {
  138. if(config.size === 16)
  139. {
  140. // equivalent to 32-bit version, don't test both
  141. return [];
  142. }
  143. }
  144. const size = (op.os || op.opcode % 2 === 1) ? config.size : 8;
  145. const is_modrm = op.e || op.fixed_g !== undefined;
  146. const codes = [];
  147. for(let reg of ["eax", "ecx", "edx", "ebx", "ebp", "esi", "edi"])
  148. {
  149. let rand = op_rand.next();
  150. codes.push("mov " + reg + ", " + rand);
  151. }
  152. if(!op.is_fpu) // generate random mmx registers
  153. {
  154. codes.push("sub esp, 8");
  155. for(let i = 0; i < 8; i++)
  156. {
  157. codes.push("mov dword [esp], " + op_rand.next());
  158. codes.push("mov dword [esp + 4], " + op_rand.next());
  159. codes.push("movq mm" + i + ", [esp]");
  160. }
  161. codes.push("add esp, 8");
  162. }
  163. else // generate random fpu registers
  164. {
  165. codes.push("finit");
  166. codes.push("sub esp, 8");
  167. for(let i = 0; i < 8; i++)
  168. {
  169. codes.push("mov dword [esp], " + op_rand.next());
  170. codes.push("mov dword [esp + 4], " + op_rand.next());
  171. codes.push("fld qword [esp]");
  172. }
  173. for(let i = 0; i < 4; i++) // half full stack
  174. {
  175. codes.push("fstp qword [esp]");
  176. }
  177. codes.push("add esp, 8");
  178. }
  179. if(true) // generate random xmm registers
  180. {
  181. codes.push("sub esp, 16");
  182. for(let i = 0; i < 8; i++)
  183. {
  184. codes.push("mov dword [esp], " + op_rand.next());
  185. codes.push("mov dword [esp + 4], " + op_rand.next());
  186. codes.push("mov dword [esp + 8], " + op_rand.next());
  187. codes.push("mov dword [esp + 12], " + op_rand.next());
  188. codes.push("movdqu xmm" + i + ", [esp]");
  189. }
  190. codes.push("add esp, 16");
  191. }
  192. if(true) // generate random stack memory
  193. {
  194. for(let i = 0; i < 8; i++)
  195. {
  196. codes.push("sub esp, 4");
  197. codes.push("mov dword [esp], " + op_rand.next());
  198. }
  199. }
  200. codes.push("push dword " + (op_rand.next() & ~(1 << 8 | 1 << 9)));
  201. codes.push("popf");
  202. if(true)
  203. {
  204. // generate random flags using arithmatic instruction
  205. // not well-distributed, but can trigger bugs in lazy flag calculation
  206. if(true)
  207. {
  208. // rarely sets zero flag, other flags mostly well-distributed
  209. codes.push("add al, ah");
  210. }
  211. else
  212. {
  213. // always sets zero flag
  214. codes.push("sub al, al");
  215. }
  216. }
  217. if(op.is_string)
  218. {
  219. codes.push("mov ecx, 3");
  220. codes.push("mov edi, (120000h-16)");
  221. codes.push("mov esi, (120000h-20)");
  222. }
  223. if(size === 16)
  224. {
  225. codes.push("db 66h ; 16 bit");
  226. }
  227. let opcode = op.opcode;
  228. if(opcode === 0x8D)
  229. {
  230. // special case: lea: generate 16-bit addressing and all modrm combinations
  231. assert(is_modrm);
  232. codes.push([].concat(
  233. create_nasm_modrm_combinations_16().map(lines => ["db 67h", "db 8dh"].concat(lines).join("\n")),
  234. create_nasm_modrm_combinations_32().map(lines => ["db 8dh"].concat(lines).join("\n"))
  235. ));
  236. }
  237. else
  238. {
  239. assert(opcode < 0x1000000);
  240. if(opcode >= 0x10000)
  241. {
  242. let c = opcode >> 16;
  243. assert(c === 0x66 || c === 0xF3 || c === 0xF2);
  244. codes.push("db " + c);
  245. opcode &= ~0xFF0000;
  246. }
  247. if(opcode >= 0x100)
  248. {
  249. let c = opcode >> 8;
  250. assert(c === 0x0F || c === 0xF2 || c === 0xF3, "Expected 0F, F2, or F3 prefix, got " + c.toString(16));
  251. codes.push("db " + c);
  252. opcode &= ~0xFF00;
  253. }
  254. codes.push("db " + opcode);
  255. if(is_modrm)
  256. {
  257. let g = 7; // edi / di / bh
  258. if(op.fixed_g !== undefined)
  259. {
  260. g = op.fixed_g;
  261. }
  262. if(config.mem)
  263. {
  264. const e = 0x04; // [esp]
  265. const sib = 0x24;
  266. codes.push("db " + (e | g << 3));
  267. codes.push("db " + sib);
  268. }
  269. else
  270. {
  271. const es = op.is_fpu ? [0, 1, 2, 3, 4, 5, 6, 7] : [
  272. 2 // edx
  273. ];
  274. const modrm_bytes = es.map(e => "db " + (0xC0 | g << 3 | e));
  275. codes.push(modrm_bytes);
  276. }
  277. }
  278. }
  279. if(op.opcode === 0xC8) // special case: enter
  280. {
  281. codes.push("dw 8h");
  282. codes.push("db 0h");
  283. }
  284. else if(op.imm8 || op.imm8s || op.imm16 || op.imm1632 || op.imm32 || op.immaddr)
  285. {
  286. if(op.imm8 || op.imm8s)
  287. {
  288. codes.push("db 12h");
  289. }
  290. else
  291. {
  292. if(op.immaddr)
  293. {
  294. // immaddr: depends on address size
  295. // generate valid pointer into bss section
  296. codes.push("dd (120000h-16)");
  297. }
  298. else
  299. {
  300. assert(op.imm1632 || op.imm16 || op.imm32);
  301. if(op.imm1632 && size === 16 || op.imm16)
  302. {
  303. codes.push("dw 34cdh");
  304. }
  305. else
  306. {
  307. assert(op.imm1632 && size === 32 || op.imm32);
  308. codes.push("dd 1234abcdh");
  309. }
  310. }
  311. }
  312. }
  313. if(op.mask_flags)
  314. {
  315. codes.push(
  316. "pushf",
  317. "and dword [esp], ~" + op.mask_flags,
  318. "popf"
  319. );
  320. }
  321. return all_combinations(codes).map(c => {
  322. return (
  323. "global _start\n" +
  324. '%include "header.inc"\n\n' +
  325. c.join("\n") + "\n" +
  326. '%include "footer.inc"\n'
  327. );
  328. });
  329. }
  330. function all_combinations(xs)
  331. {
  332. let result = [xs];
  333. for(let i = 0; i < xs.length; i++)
  334. {
  335. let x = xs[i];
  336. if(x instanceof Array)
  337. {
  338. let new_result = [];
  339. for(let r of result)
  340. {
  341. for(let x_ of x)
  342. {
  343. r = r.slice();
  344. r[i] = x_;
  345. new_result.push(r);
  346. }
  347. }
  348. result = new_result;
  349. }
  350. }
  351. return result;
  352. }