mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
For Go 1.3 these external packages were collapsed into large single-file implementations stored in the cmd/objdump directory. For Go 1.4 we want pprof to be able to link against them too, so move them into cmd/internal, where they can be shared. The new files are copied from the repo in the file path (rsc.io/...). Those repos were code reviewed during development (mainly by crawshaw and minux), because we knew the main repo would use them. Update #8798 LGTM=bradfitz R=crawshaw, bradfitz CC=golang-codereviews https://golang.org/cl/153750044
383 lines
9.4 KiB
Go
383 lines
9.4 KiB
Go
// 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 = `
|
|
F390
|
|
`
|
|
|
|
// 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
|
|
`)
|