blob: 3d4e1460f8ccdfdb41554b5fb36e591ace5a8f34 [file] [log] [blame]
// 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
`)