| // Copyright 2014 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package x86asm |
| |
| import ( |
| "bytes" |
| "strings" |
| "testing" |
| ) |
| |
| func TestObjdump32Manual(t *testing.T) { testObjdump32(t, hexCases(t, objdumpManualTests)) } |
| func TestObjdump32Testdata(t *testing.T) { testObjdump32(t, concat(basicPrefixes, testdataCases(t))) } |
| func TestObjdump32ModRM(t *testing.T) { testObjdump32(t, concat(basicPrefixes, enumModRM)) } |
| func TestObjdump32OneByte(t *testing.T) { testBasic(t, testObjdump32) } |
| func TestObjdump320F(t *testing.T) { testBasic(t, testObjdump32, 0x0F) } |
| func TestObjdump320F38(t *testing.T) { testBasic(t, testObjdump32, 0x0F, 0x38) } |
| func TestObjdump320F3A(t *testing.T) { testBasic(t, testObjdump32, 0x0F, 0x3A) } |
| func TestObjdump32Prefix(t *testing.T) { testPrefix(t, testObjdump32) } |
| |
| func TestObjdump64Manual(t *testing.T) { testObjdump64(t, hexCases(t, objdumpManualTests)) } |
| func TestObjdump64Testdata(t *testing.T) { testObjdump64(t, concat(basicPrefixes, testdataCases(t))) } |
| func TestObjdump64ModRM(t *testing.T) { testObjdump64(t, concat(basicPrefixes, enumModRM)) } |
| func TestObjdump64OneByte(t *testing.T) { testBasic(t, testObjdump64) } |
| func TestObjdump640F(t *testing.T) { testBasic(t, testObjdump64, 0x0F) } |
| func TestObjdump640F38(t *testing.T) { testBasic(t, testObjdump64, 0x0F, 0x38) } |
| func TestObjdump640F3A(t *testing.T) { testBasic(t, testObjdump64, 0x0F, 0x3A) } |
| func TestObjdump64Prefix(t *testing.T) { testPrefix(t, testObjdump64) } |
| |
| func TestObjdump64REXTestdata(t *testing.T) { |
| testObjdump64(t, filter(concat3(basicPrefixes, rexPrefixes, testdataCases(t)), isValidREX)) |
| } |
| func TestObjdump64REXModRM(t *testing.T) { |
| testObjdump64(t, concat3(basicPrefixes, rexPrefixes, enumModRM)) |
| } |
| func TestObjdump64REXOneByte(t *testing.T) { testBasicREX(t, testObjdump64) } |
| func TestObjdump64REX0F(t *testing.T) { testBasicREX(t, testObjdump64, 0x0F) } |
| func TestObjdump64REX0F38(t *testing.T) { testBasicREX(t, testObjdump64, 0x0F, 0x38) } |
| func TestObjdump64REX0F3A(t *testing.T) { testBasicREX(t, testObjdump64, 0x0F, 0x3A) } |
| func TestObjdump64REXPrefix(t *testing.T) { testPrefixREX(t, testObjdump64) } |
| |
| // objdumpManualTests holds test cases that will be run by TestObjdumpManual. |
| // If you are debugging a few cases that turned up in a longer run, it can be useful |
| // to list them here and then use -run=ObjdumpManual, particularly with tracing enabled. |
| var objdumpManualTests = ` |
| 4883FE017413 |
| 488DFC2500000000 |
| 488D3D00000000 |
| ` |
| |
| // allowedMismatchObjdump reports whether the mismatch between text and dec |
| // should be allowed by the test. |
| func allowedMismatchObjdump(text string, size int, inst *Inst, dec ExtInst) bool { |
| if size == 15 && dec.nenc == 15 && contains(text, "truncated") && contains(dec.text, "(bad)") { |
| return true |
| } |
| |
| if i := strings.LastIndex(dec.text, " "); isPrefix(dec.text[i+1:]) && size == 1 && isPrefix(text) { |
| return true |
| } |
| |
| if size == dec.nenc && contains(dec.text, "movupd") && contains(dec.text, "data32") { |
| s := strings.Replace(dec.text, "data32 ", "", -1) |
| if text == s { |
| return true |
| } |
| } |
| |
| // Simplify our invalid instruction text. |
| if text == "error: unrecognized instruction" { |
| text = "BAD" |
| } |
| |
| // Invalid instructions for which libopcodes prints %? register. |
| // FF E8 11 22 33 44: |
| // Invalid instructions for which libopcodes prints "internal disassembler error". |
| // Invalid instructions for which libopcodes prints 8087 only (e.g., DB E0) |
| // or prints 287 only (e.g., DB E4). |
| if contains(dec.text, "%?", "<internal disassembler error>", "(8087 only)", "(287 only)") { |
| dec.text = "(bad)" |
| } |
| |
| // 0F 19 11, 0F 1C 11, 0F 1D 11, 0F 1E 11, 0F 1F 11: libopcodes says nop, |
| // but the Intel manuals say that the only NOP there is 0F 1F /0. |
| // Perhaps libopcodes is reporting an older encoding. |
| i := bytes.IndexByte(dec.enc[:], 0x0F) |
| if contains(dec.text, "nop") && i >= 0 && i+2 < len(dec.enc) && dec.enc[i+1]&^7 == 0x18 && (dec.enc[i+1] != 0x1F || (dec.enc[i+2]>>3)&7 != 0) { |
| dec.text = "(bad)" |
| } |
| |
| // Any invalid instruction. |
| if text == "BAD" && contains(dec.text, "(bad)") { |
| return true |
| } |
| |
| // Instructions libopcodes knows but we do not (e.g., 0F 19 11). |
| if (text == "BAD" || size == 1 && isPrefix(text)) && hasPrefix(dec.text, unsupported...) { |
| return true |
| } |
| |
| // Instructions we know but libopcodes does not (e.g., 0F D0 11). |
| if (contains(dec.text, "(bad)") || dec.nenc == 1 && isPrefix(dec.text)) && hasPrefix(text, libopcodesUnsupported...) { |
| return true |
| } |
| |
| // Libopcodes rejects F2 90 as NOP. Not sure why. |
| if (contains(dec.text, "(bad)") || dec.nenc == 1 && isPrefix(dec.text)) && inst.Opcode>>24 == 0x90 && countPrefix(inst, 0xF2) > 0 { |
| return true |
| } |
| |
| // 0F 20 11, 0F 21 11, 0F 22 11, 0F 23 11, 0F 24 11: |
| // Moves into and out of some control registers seem to be unsupported by libopcodes. |
| // TODO(rsc): Are they invalid somehow? |
| if (contains(dec.text, "(bad)") || dec.nenc == 1 && isPrefix(dec.text)) && contains(text, "%cr", "%db", "%tr") { |
| return true |
| } |
| |
| if contains(dec.text, "fwait") && dec.nenc == 1 && dec.enc[0] != 0x9B { |
| return true |
| } |
| |
| // 9B D9 11: libopcodes reports FSTSW instead of FWAIT + FNSTSW. |
| // This is correct in that FSTSW is a pseudo-op for the pair, but it really |
| // is a pair of instructions: execution can stop between them. |
| // Our decoder chooses to separate them. |
| if (text == "fwait" || strings.HasSuffix(text, " fwait")) && dec.nenc >= len(strings.Fields(text)) && dec.enc[len(strings.Fields(text))-1] == 0x9B { |
| return true |
| } |
| |
| // 0F 18 77 11: |
| // Invalid instructions for which libopcodes prints "nop/reserved". |
| // Perhaps libopcodes is reporting an older encoding. |
| if text == "BAD" && contains(dec.text, "nop/reserved") { |
| return true |
| } |
| |
| // 0F C7 B0 11 22 33 44: libopcodes says vmptrld 0x44332211(%eax); we say rdrand %eax. |
| // TODO(rsc): Fix, since we are probably wrong, but we don't have vmptrld in the manual. |
| if contains(text, "rdrand") && contains(dec.text, "vmptrld", "vmxon", "vmclear") { |
| return true |
| } |
| |
| // DD C8: libopcodes says FNOP but the Intel manual is clear FNOP is only D9 D0. |
| // Perhaps libopcodes is reporting an older encoding. |
| if text == "BAD" && contains(dec.text, "fnop") && (dec.enc[0] != 0xD9 || dec.enc[1] != 0xD0) { |
| return true |
| } |
| |
| // 66 90: libopcodes says xchg %ax,%ax; we say 'data16 nop'. |
| // The 16-bit swap will preserve the high bits of the register, |
| // so they are the same. |
| if contains(text, "nop") && contains(dec.text, "xchg %ax,%ax") { |
| return true |
| } |
| |
| // If there are multiple prefixes, allow libopcodes to use an alternate name. |
| if size == 1 && dec.nenc == 1 && prefixByte[text] > 0 && prefixByte[text] == prefixByte[dec.text] { |
| return true |
| } |
| |
| // 26 9B: libopcodes reports "fwait"/1, ignoring segment prefix. |
| // https://sourceware.org/bugzilla/show_bug.cgi?id=16891 |
| // F0 82: Decode="lock"/1 but libopcodes="lock (bad)"/2. |
| if size == 1 && dec.nenc >= 1 && prefixByte[text] == dec.enc[0] && contains(dec.text, "(bad)", "fwait", "fnop") { |
| return true |
| } |
| |
| // libopcodes interprets 660f801122 as taking a rel16 but |
| // truncating the address at 16 bits. Not sure what is correct. |
| if contains(text, ".+0x2211", ".+0x11") && contains(dec.text, " .-") { |
| return true |
| } |
| |
| // 66 F3 0F D6 C5, 66 F2 0F D6 C0: libopcodes reports use of XMM register instead of MMX register, |
| // but only when the instruction has a 66 prefix. Maybe they know something we don't. |
| if countPrefix(inst, 0x66) > 0 && contains(dec.text, "movdq2q", "movq2dq") && !contains(dec.text, "%mm") { |
| return true |
| } |
| |
| // 0F 01 F8, 0F 05, 0F 07: these are 64-bit instructions but libopcodes accepts them. |
| if (text == "BAD" || size == 1 && isPrefix(text)) && contains(dec.text, "swapgs", "syscall", "sysret", "rdfsbase", "rdgsbase", "wrfsbase", "wrgsbase") { |
| return true |
| } |
| |
| return false |
| } |
| |
| // Instructions known to libopcodes (or xed) but not to us. |
| // Most of these come from supplementary manuals of one form or another. |
| var unsupported = strings.Fields(` |
| bndc |
| bndl |
| bndm |
| bnds |
| clac |
| clgi |
| femms |
| fldln |
| fldz |
| getsec |
| invlpga |
| kmov |
| montmul |
| pavg |
| pf2i |
| pfacc |
| pfadd |
| pfcmp |
| pfmax |
| pfmin |
| pfmul |
| pfna |
| pfpnac |
| pfrc |
| pfrs |
| pfsub |
| phadd |
| phsub |
| pi2f |
| pmulhr |
| prefetch |
| pswap |
| ptest |
| rdseed |
| sha1 |
| sha256 |
| skinit |
| stac |
| stgi |
| vadd |
| vand |
| vcmp |
| vcomis |
| vcvt |
| vcvt |
| vdiv |
| vhadd |
| vhsub |
| vld |
| vmax |
| vmcall |
| vmfunc |
| vmin |
| vmlaunch |
| vmload |
| vmmcall |
| vmov |
| vmov |
| vmov |
| vmptrld |
| vmptrst |
| vmread |
| vmresume |
| vmrun |
| vmsave |
| vmul |
| vmwrite |
| vmxoff |
| vor |
| vpack |
| vpadd |
| vpand |
| vpavg |
| vpcmp |
| vpcmp |
| vpins |
| vpmadd |
| vpmax |
| vpmin |
| vpmul |
| vpmul |
| vpor |
| vpsad |
| vpshuf |
| vpsll |
| vpsra |
| vpsrad |
| vpsrl |
| vpsub |
| vpunp |
| vpxor |
| vrcp |
| vrsqrt |
| vshuf |
| vsqrt |
| vsub |
| vucomis |
| vunp |
| vxor |
| vzero |
| xcrypt |
| xsha1 |
| xsha256 |
| xstore-rng |
| insertq |
| extrq |
| vmclear |
| invvpid |
| adox |
| vmxon |
| invept |
| adcx |
| vmclear |
| prefetchwt1 |
| enclu |
| encls |
| salc |
| fstpnce |
| fdisi8087_nop |
| fsetpm287_nop |
| feni8087_nop |
| syscall |
| sysret |
| `) |
| |
| // Instructions known to us but not to libopcodes (at least in binutils 2.24). |
| var libopcodesUnsupported = strings.Fields(` |
| addsubps |
| aes |
| blend |
| cvttpd2dq |
| dpp |
| extract |
| haddps |
| hsubps |
| insert |
| invpcid |
| lddqu |
| movmsk |
| movnt |
| movq2dq |
| mps |
| pack |
| pblend |
| pclmul |
| pcmp |
| pext |
| phmin |
| pins |
| pmax |
| pmin |
| pmov |
| pmovmsk |
| pmul |
| popcnt |
| pslld |
| psllq |
| psllw |
| psrad |
| psraw |
| psrl |
| ptest |
| punpck |
| round |
| xrstor |
| xsavec |
| xsaves |
| comis |
| ucomis |
| movhps |
| movntps |
| rsqrt |
| rcpp |
| puncpck |
| bsf |
| movq2dq |
| cvttpd2dq |
| movq |
| hsubpd |
| movdqa |
| movhpd |
| addsubpd |
| movd |
| haddpd |
| cvtps2dq |
| bsr |
| cvtdq2ps |
| rdrand |
| maskmov |
| movq2dq |
| movlhps |
| movbe |
| movlpd |
| `) |