123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424 |
- #!/usr/bin/env node
- "use strict";
- // number of tests per instruction
- const NO_TESTS = 1;
- const assert = require("assert").strict;
- const fs = require("fs");
- const encodings = require("../../gen/x86_table.js");
- const Prand = require("./prand.js");
- generate_tests();
- function generate_tests()
- {
- const build_folder = __dirname + "/build/";
- try
- {
- fs.mkdirSync(build_folder);
- }
- catch(e)
- {
- if(e.code !== "EEXIST")
- {
- throw e;
- }
- }
- for(const op of encodings)
- {
- const configurations = [
- { mem: 0, size: 16, },
- { mem: 0, size: 32, },
- { mem: 1, size: 16, },
- { mem: 1, size: 32, },
- ];
- let i = 0;
- for(const config of configurations)
- {
- for(let nth_test = 0; nth_test < NO_TESTS; nth_test++)
- {
- if(nth_test > 0 && op.opcode === 0x8D)
- {
- // is already tested exhaustively in first run
- continue;
- }
- for(const code of create_nasm(op, config, nth_test))
- {
- const filename = "gen_" + format_opcode(op.opcode) + "_" + (op.fixed_g || 0) + "_" + i + ".asm";
- const dirname = build_folder + filename;
- let old_code = undefined;
- try
- {
- old_code = fs.readFileSync(dirname, { encoding: "ascii" });
- }
- catch(e)
- {
- }
- if(old_code !== code)
- {
- console.log("Creating %s", filename);
- fs.writeFileSync(dirname, code);
- }
- i++;
- }
- }
- }
- }
- }
- function format_opcode(n)
- {
- let x = n.toString(16);
- return (x.length === 1 || x.length === 3) ? "0" + x : x;
- }
- function create_nasm_modrm_combinations_16()
- {
- let result = [];
- for(let modrm = 0; modrm < 0xC0; modrm++)
- {
- let mod = modrm >> 6;
- let rm = modrm & 7;
- let has_imm8 = mod === 1;
- let has_imm16 = mod === 2 || rm === 6 && mod === 0;
- assert(!has_imm8 || !has_imm16);
- let line = ["db " + modrm];
- if(has_imm8) line.push("db 9ah");
- if(has_imm16) line.push("dw 9a1fh");
- result.push(line);
- }
- return result;
- }
- function create_nasm_modrm_combinations_32()
- {
- let result = [];
- let sample_sib_bytes = [0x05, 0x65, 0xAD, 0xCD, 0x20, 0xFF];
- let exhaustive_sib_bytes = [];
- for(let sib = 0; sib < 0x100; sib++) exhaustive_sib_bytes.push(sib);
- for(let modrm = 0; modrm < 0xC0; modrm++)
- {
- let mod = modrm >> 6;
- let reg = modrm >> 3 & 7;
- let rm = modrm & 7;
- let has_imm8 = mod === 1;
- let has_imm32 = mod === 2 || rm === 5 && mod === 0;
- let has_sib = rm === 4;
- assert(!has_imm8 || !has_imm32);
- if(has_sib)
- {
- // avoid generating an excessive number of tests
- let sib_bytes = reg === 0 ? exhaustive_sib_bytes : sample_sib_bytes;
- for(let sib of sib_bytes)
- {
- let line = ["db " + modrm, "db " + sib];
- if(has_imm8) line.push("db 9ah");
- if(has_imm32 || mod === 0 && (sib & 7) === 5) line.push("dd 9a1fbcdeh");
- result.push(line);
- }
- }
- else
- {
- let line = ["db " + modrm];
- if(has_imm8) line.push("db 9ah");
- if(has_imm32) line.push("dd 9a1fbcdeh");
- result.push(line);
- }
- }
- return result;
- }
- function create_nasm(op, config, nth_test)
- {
- if(op.prefix || op.skip)
- {
- return [];
- }
- if(config.mem ? op.skip_mem : op.skip_reg)
- {
- // Not supported by test
- return [];
- }
- if(!op.e)
- {
- if(config.mem)
- {
- // doesn't use memory, don't test both
- return [];
- }
- }
- if(!op.os)
- {
- if(config.size === 16)
- {
- // equivalent to 32-bit version, don't test both
- return [];
- }
- }
- const op_rand = new Prand(op.opcode + nth_test * 0x10000);
- const size = (op.os || op.opcode % 2 === 1) ? config.size : 8;
- const is_modrm = op.e || op.fixed_g !== undefined;
- const codes = [];
- for(let reg of ["eax", "ecx", "edx", "ebx", "ebp", "esi", "edi"])
- {
- let rand = op_rand.next();
- codes.push("mov " + reg + ", " + rand);
- }
- if(!op.is_fpu) // generate random mmx registers
- {
- codes.push("sub esp, 8");
- for(let i = 0; i < 8; i++)
- {
- codes.push("mov dword [esp], " + op_rand.next());
- codes.push("mov dword [esp + 4], " + op_rand.next());
- codes.push("movq mm" + i + ", [esp]");
- }
- codes.push("add esp, 8");
- }
- else // generate random fpu registers
- {
- codes.push("finit");
- codes.push("sub esp, 8");
- for(let i = 0; i < 8; i++)
- {
- codes.push("mov dword [esp], " + op_rand.next());
- codes.push("mov dword [esp + 4], " + op_rand.next());
- codes.push("fld qword [esp]");
- }
- for(let i = 0; i < 4; i++) // half full stack
- {
- codes.push("fstp qword [esp]");
- }
- codes.push("add esp, 8");
- }
- if(true) // generate random xmm registers
- {
- codes.push("sub esp, 16");
- for(let i = 0; i < 8; i++)
- {
- codes.push("mov dword [esp], " + op_rand.next());
- codes.push("mov dword [esp + 4], " + op_rand.next());
- codes.push("mov dword [esp + 8], " + op_rand.next());
- codes.push("mov dword [esp + 12], " + op_rand.next());
- codes.push("movdqu xmm" + i + ", [esp]");
- }
- codes.push("add esp, 16");
- }
- if(true) // generate random stack memory
- {
- for(let i = 0; i < 8; i++)
- {
- codes.push("sub esp, 4");
- codes.push("mov dword [esp], " + op_rand.next());
- }
- }
- codes.push("push dword " + (op_rand.next() & ~(1 << 8 | 1 << 9)));
- codes.push("popf");
- if(true)
- {
- // generate random flags using arithmatic instruction
- // not well-distributed, but can trigger bugs in lazy flag calculation
- if(true)
- {
- // rarely sets zero flag, other flags mostly well-distributed
- codes.push("add al, ah");
- }
- else
- {
- // always sets zero flag
- codes.push("sub al, al");
- }
- }
- if(op.is_string)
- {
- codes.push("mov ecx, 3");
- codes.push("mov edi, (120000h-16)");
- codes.push("mov esi, (120000h-20)");
- }
- if(size === 16)
- {
- codes.push("db 66h ; 16 bit");
- }
- let opcode = op.opcode;
- if(opcode === 0x8D)
- {
- // special case: lea: generate 16-bit addressing and all modrm combinations
- assert(is_modrm);
- codes.push([].concat(
- create_nasm_modrm_combinations_16().map(lines => ["db 67h", "db 8dh"].concat(lines).join("\n")),
- create_nasm_modrm_combinations_32().map(lines => ["db 8dh"].concat(lines).join("\n"))
- ));
- }
- else
- {
- assert(opcode < 0x1000000);
- if(opcode >= 0x10000)
- {
- let c = opcode >> 16;
- assert(c === 0x66 || c === 0xF3 || c === 0xF2);
- codes.push("db " + c);
- opcode &= ~0xFF0000;
- }
- if(opcode >= 0x100)
- {
- let c = opcode >> 8;
- assert(c === 0x0F || c === 0xF2 || c === 0xF3, "Expected 0F, F2, or F3 prefix, got " + c.toString(16));
- codes.push("db " + c);
- opcode &= ~0xFF00;
- }
- codes.push("db " + opcode);
- if(is_modrm)
- {
- let g = 7; // edi / di / bh
- if(op.fixed_g !== undefined)
- {
- g = op.fixed_g;
- }
- if(config.mem)
- {
- const e = 0x04; // [esp]
- const sib = 0x24;
- codes.push("db " + (e | g << 3));
- codes.push("db " + sib);
- }
- else
- {
- const es = op.is_fpu ? [0, 1, 2, 3, 4, 5, 6, 7] : [
- 2 // edx
- ];
- const modrm_bytes = es.map(e => "db " + (0xC0 | g << 3 | e));
- codes.push(modrm_bytes);
- }
- }
- }
- if(op.opcode === 0xC8) // special case: enter
- {
- codes.push("dw 8h");
- codes.push("db 0h");
- }
- else if(op.imm8 || op.imm8s || op.imm16 || op.imm1632 || op.imm32 || op.immaddr)
- {
- if(op.imm8 || op.imm8s)
- {
- codes.push("db 12h");
- }
- else
- {
- if(op.immaddr)
- {
- // immaddr: depends on address size
- // generate valid pointer into bss section
- codes.push("dd (120000h-16)");
- }
- else
- {
- assert(op.imm1632 || op.imm16 || op.imm32);
- if(op.imm1632 && size === 16 || op.imm16)
- {
- codes.push("dw 34cdh");
- }
- else
- {
- assert(op.imm1632 && size === 32 || op.imm32);
- codes.push("dd 1234abcdh");
- }
- }
- }
- }
- if(op.mask_flags)
- {
- codes.push(
- "pushf",
- "and dword [esp], ~" + op.mask_flags,
- "popf"
- );
- }
- return all_combinations(codes).map(c => {
- return (
- "global _start\n" +
- '%include "header.inc"\n\n' +
- c.join("\n") + "\n" +
- '%include "footer.inc"\n'
- );
- });
- }
- function all_combinations(xs)
- {
- let result = [xs];
- for(let i = 0; i < xs.length; i++)
- {
- let x = xs[i];
- if(x instanceof Array)
- {
- let new_result = [];
- for(let r of result)
- {
- for(let x_ of x)
- {
- r = r.slice();
- r[i] = x_;
- new_result.push(r);
- }
- }
- result = new_result;
- }
- }
- return result;
- }
|