fix(disasm): name the amd64 families x/arch decodes to the zero opcode
x/arch reports the ADCX, ADOX, RDSEED, RDPID, TPAUSE, UMONITOR, UMWAIT and ENDBR families with no error but the degenerate zero instruction, which GoSyntax renders as Op(0) under its prefix decoration and with a length of one. A supplementary naming table keyed by the opcode pattern restores the toolchain's own spellings and lengths; the parity fixtures pin all 41 corpus rows (ENDBR32 alone, which the toolchain cannot spell, pins as bytes and text in the focused naming test). Assisted-by: GLM 5.3 Flash
This commit is contained in:
1 parent
6c4932c4ec
commit
dd356b9e6a
4 files changed
+498
No files matched your search
@@ -70,6 +70,16 @@ func Decode(a arch.Arch, code []byte, addr uint64) (Instruction, error) {
|
||||
if err != nil {
|
||||
return Instruction{Addr: addr, Text: "???", Len: 1}, nil
|
||||
}
|
||||
if inst.Op == 0 {
|
||||
// x/arch reports a few opcode families with no error but the
|
||||
// degenerate zero instruction: no opcode, no operands and a
|
||||
// length of one, which GoSyntax renders as "Op(0)". The
|
||||
// supplementary naming table restores the families the Go
|
||||
// toolchain names; anything else keeps the placeholder.
|
||||
if text, n, ok := nameAMD64Degenerate(code); ok {
|
||||
return Instruction{Addr: addr, Text: text, Len: n}, nil
|
||||
}
|
||||
}
|
||||
return Instruction{Addr: addr, Text: x86asm.GoSyntax(inst, addr, nil), Len: inst.Len}, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,320 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package disasm
|
||||
|
||||
import "fmt"
|
||||
|
||||
// golang.org/x/arch decodes a handful of amd64 opcode families to no error
|
||||
// and the degenerate zero instruction: no opcode, no operands, one byte.
|
||||
// GoSyntax then renders the placeholder "Op(0)", decorated with whatever
|
||||
// prefixes it saw, and reports a length of one. The families are the ADCX
|
||||
// and ADOX carry-propagation pair, RDSEED, RDPID, the WAITPKG trio TPAUSE,
|
||||
// UMONITOR and UMWAIT, and the ENDBR pair.
|
||||
//
|
||||
// nameAMD64Degenerate restores the names the Go toolchain itself carries
|
||||
// for these families. The toolchain's assembler test corpus
|
||||
// machine-checks the encodings (amd64enc.s's "ADCXL DX, DX // 660f38f6d2"
|
||||
// and its kin), and the spellings below are the corpus's own; the parity
|
||||
// fixtures pin every corpus row the table names, so a decoder bump that
|
||||
// changes a length or a register reading fails there rather than silently
|
||||
// moving the text. The table is keyed by the opcode pattern: legacy
|
||||
// prefixes, opcode bytes and the ModR/M shape, so it names every encoding
|
||||
// of a family, not only the corpus rows.
|
||||
|
||||
// amd64GPRNames holds the Plan 9 spelling of the general-purpose registers
|
||||
// by index. The names are width-independent: the mnemonic's suffix carries
|
||||
// the size, so RDX reads DX in both ADCXL and ADCXQ.
|
||||
var amd64GPRNames = [16]string{
|
||||
"AX", "CX", "DX", "BX", "SP", "BP", "SI", "DI",
|
||||
"R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15",
|
||||
}
|
||||
|
||||
// amd64Prefixes is the legacy prefix reading of one instruction: the
|
||||
// operand-size override, the rep and repne not-really-prefixes, and REX.
|
||||
// n is the number of bytes the prefixes consumed.
|
||||
type amd64Prefixes struct {
|
||||
osz bool // 66
|
||||
rep bool // f3
|
||||
repne bool // f2
|
||||
rexW, rexR, rexX, rexB bool
|
||||
n int
|
||||
}
|
||||
|
||||
// scanAMD64Prefixes reads the legacy prefixes at the start of code. It
|
||||
// stops at the first byte that is not one, which is where the opcode
|
||||
// begins.
|
||||
func scanAMD64Prefixes(code []byte) (amd64Prefixes, bool) {
|
||||
var p amd64Prefixes
|
||||
for ; p.n < len(code); p.n++ {
|
||||
switch b := code[p.n]; {
|
||||
case b == 0x66:
|
||||
p.osz = true
|
||||
case b == 0xf3:
|
||||
p.rep = true
|
||||
case b == 0xf2:
|
||||
p.repne = true
|
||||
case b >= 0x40 && b <= 0x4f:
|
||||
p.rexW = b&0x8 != 0
|
||||
p.rexR = b&0x4 != 0
|
||||
p.rexX = b&0x2 != 0
|
||||
p.rexB = b&0x1 != 0
|
||||
default:
|
||||
return p, true
|
||||
}
|
||||
}
|
||||
return p, false // prefixes with no opcode behind them
|
||||
}
|
||||
|
||||
// amd64RM is the ModR/M (and SIB) reading of one operand, in the shape the
|
||||
// named families use: one register or one memory reference, never an
|
||||
// immediate. n counts the ModR/M byte, an SIB byte and the displacement.
|
||||
type amd64RM struct {
|
||||
regForm bool // mod == 11: the operand is the r/m register
|
||||
reg int // reg field with REX.R applied
|
||||
rm int // r/m field with REX.B applied, register form
|
||||
base int // memory base register, -1 when absent
|
||||
index int // memory index register, -1 when absent
|
||||
scale int
|
||||
disp int32
|
||||
rip bool // mod == 00, r/m == 101: RIP-relative
|
||||
n int
|
||||
}
|
||||
|
||||
// decodeAMD64RM reads the ModR/M byte at the start of code, with the SIB
|
||||
// byte and displacement that mod 00 and 01 may carry behind it. rexR,
|
||||
// rexX and rexB extend the register fields to R8 through R15.
|
||||
func decodeAMD64RM(code []byte, rexR, rexX, rexB bool) (amd64RM, bool) {
|
||||
if len(code) == 0 {
|
||||
return amd64RM{}, false
|
||||
}
|
||||
b := code[0]
|
||||
var rm amd64RM
|
||||
rm.n = 1
|
||||
rm.reg = int(b>>3) & 7
|
||||
if rexR {
|
||||
rm.reg += 8
|
||||
}
|
||||
mod := b >> 6
|
||||
rmb := int(b & 7)
|
||||
if mod == 3 {
|
||||
rm.regForm = true
|
||||
rm.rm = rmb
|
||||
if rexB {
|
||||
rm.rm += 8
|
||||
}
|
||||
return rm, true
|
||||
}
|
||||
rm.base, rm.index, rm.scale = -1, -1, 1
|
||||
switch rmb {
|
||||
case 4: // SIB byte follows
|
||||
if len(code) < rm.n+1 {
|
||||
return amd64RM{}, false
|
||||
}
|
||||
sib := code[rm.n]
|
||||
rm.n++
|
||||
rm.scale = 1 << (sib >> 6)
|
||||
idx := int(sib>>3) & 7
|
||||
if rexX {
|
||||
idx += 8
|
||||
}
|
||||
if idx%8 != 4 { // index 100 is the no-index encoding
|
||||
rm.index = idx
|
||||
}
|
||||
bs := int(sib & 7)
|
||||
if rexB {
|
||||
bs += 8
|
||||
}
|
||||
if mod == 0 && bs%8 == 5 {
|
||||
// base 101 with no displacement byte is disp32 alone
|
||||
} else {
|
||||
rm.base = bs
|
||||
}
|
||||
case 5:
|
||||
if mod == 0 {
|
||||
rm.rip = true
|
||||
} else {
|
||||
rm.base = rmb
|
||||
if rexB {
|
||||
rm.base += 8
|
||||
}
|
||||
}
|
||||
default:
|
||||
rm.base = rmb
|
||||
if rexB {
|
||||
rm.base += 8
|
||||
}
|
||||
}
|
||||
switch mod {
|
||||
case 1:
|
||||
if len(code) < rm.n+1 {
|
||||
return amd64RM{}, false
|
||||
}
|
||||
rm.disp = int32(int8(code[rm.n]))
|
||||
rm.n++
|
||||
case 2:
|
||||
if len(code) < rm.n+4 {
|
||||
return amd64RM{}, false
|
||||
}
|
||||
rm.disp = int32(uint32(code[rm.n]) | uint32(code[rm.n+1])<<8 |
|
||||
uint32(code[rm.n+2])<<16 | uint32(code[rm.n+3])<<24)
|
||||
rm.n += 4
|
||||
}
|
||||
return rm, true
|
||||
}
|
||||
|
||||
// text renders the operand the way the GoSyntax renderer prints a memory
|
||||
// reference: the displacement in hex (a zero displacement printed as 0),
|
||||
// then the base, then the scaled index.
|
||||
func (rm amd64RM) text() string {
|
||||
if rm.regForm {
|
||||
return amd64GPRNames[rm.rm]
|
||||
}
|
||||
s := "0"
|
||||
if rm.disp != 0 {
|
||||
s = fmt.Sprintf("%#x", rm.disp)
|
||||
}
|
||||
if rm.rip {
|
||||
// The renderer names the instruction pointer IP in a memory
|
||||
// reference.
|
||||
return s + "(IP)"
|
||||
}
|
||||
if rm.base >= 0 {
|
||||
s += "(" + amd64GPRNames[rm.base] + ")"
|
||||
}
|
||||
if rm.index >= 0 {
|
||||
s += fmt.Sprintf("(%s*%d)", amd64GPRNames[rm.index], rm.scale)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// nameAMD64Degenerate names an amd64 encoding the decoder returned as the
|
||||
// degenerate zero instruction. It reports the rendered text, the
|
||||
// instruction's length in bytes and whether the bytes matched a family.
|
||||
// Unmatched bytes keep the renderer's own placeholder output.
|
||||
func nameAMD64Degenerate(code []byte) (string, int, bool) {
|
||||
p, ok := scanAMD64Prefixes(code)
|
||||
if !ok || len(code) < p.n+3 || code[p.n] != 0x0f {
|
||||
return "", 0, false
|
||||
}
|
||||
tail := code[p.n+1:]
|
||||
switch tail[0] {
|
||||
case 0x38:
|
||||
return nameAMD64Carry(p, tail[1:])
|
||||
case 0xc7:
|
||||
return nameAMD64RNG(p, tail[1:])
|
||||
case 0xae:
|
||||
return nameAMD64Wait(p, tail[1:])
|
||||
case 0x1e:
|
||||
return nameAMD64Endbr(p, tail[1:])
|
||||
}
|
||||
return "", 0, false
|
||||
}
|
||||
|
||||
// nameAMD64Carry names the ADCX and ADOX pair, 0F 38 F6 /r. The operand
|
||||
// size override carries ADCX and rep carries ADOX; the length suffix
|
||||
// follows REX.W. The destination is the reg field and the source the
|
||||
// r/m operand, printed source first.
|
||||
func nameAMD64Carry(p amd64Prefixes, tail []byte) (string, int, bool) {
|
||||
var name string
|
||||
switch {
|
||||
case p.osz && !p.rep && !p.repne:
|
||||
name = "ADCX"
|
||||
case p.rep && !p.osz && !p.repne:
|
||||
name = "ADOX"
|
||||
default:
|
||||
return "", 0, false
|
||||
}
|
||||
if len(tail) < 2 || tail[0] != 0xf6 {
|
||||
return "", 0, false
|
||||
}
|
||||
rm, ok := decodeAMD64RM(tail[1:], p.rexR, p.rexX, p.rexB)
|
||||
if !ok {
|
||||
return "", 0, false
|
||||
}
|
||||
suffix := "L"
|
||||
if p.rexW {
|
||||
suffix = "Q"
|
||||
}
|
||||
text := name + suffix + " " + rm.text() + ", " + amd64GPRNames[rm.reg]
|
||||
return text, p.n + 3 + rm.n, true
|
||||
}
|
||||
|
||||
// nameAMD64RNG names RDSEED, 0F C7 /7, and its rep-prefixed sibling
|
||||
// RDPID. Both take the destination register in the r/m field and exist
|
||||
// only in the register form; the memory forms of the same opcode are
|
||||
// CLFLUSH and its descendants, which the decoder names itself. The size
|
||||
// suffix follows REX.W and the operand-size override.
|
||||
func nameAMD64RNG(p amd64Prefixes, tail []byte) (string, int, bool) {
|
||||
var name string
|
||||
switch {
|
||||
case p.rep && !p.osz && !p.repne:
|
||||
name = "RDPID"
|
||||
case !p.rep && !p.repne:
|
||||
name = "RDSEED"
|
||||
default:
|
||||
return "", 0, false
|
||||
}
|
||||
if len(tail) < 1 {
|
||||
return "", 0, false
|
||||
}
|
||||
rm, ok := decodeAMD64RM(tail, p.rexR, p.rexX, p.rexB)
|
||||
if !ok || !rm.regForm || rm.reg != 7 {
|
||||
return "", 0, false
|
||||
}
|
||||
if name == "RDPID" {
|
||||
return name + " " + amd64GPRNames[rm.rm], p.n + 2 + rm.n, true
|
||||
}
|
||||
suffix := "L"
|
||||
switch {
|
||||
case p.rexW:
|
||||
suffix = "Q"
|
||||
case p.osz:
|
||||
suffix = "W"
|
||||
}
|
||||
return name + suffix + " " + amd64GPRNames[rm.rm], p.n + 2 + rm.n, true
|
||||
}
|
||||
|
||||
// nameAMD64Wait names the WAITPKG and monitor trio on 0F AE /6: TPAUSE
|
||||
// under the operand-size override, UMONITOR under rep and UMWAIT under
|
||||
// repne. Each takes one 32-bit register in the r/m field.
|
||||
func nameAMD64Wait(p amd64Prefixes, tail []byte) (string, int, bool) {
|
||||
var name string
|
||||
switch {
|
||||
case p.osz && !p.rep && !p.repne:
|
||||
name = "TPAUSE"
|
||||
case p.rep && !p.osz && !p.repne:
|
||||
name = "UMONITOR"
|
||||
case p.repne && !p.osz && !p.rep:
|
||||
name = "UMWAIT"
|
||||
default:
|
||||
return "", 0, false
|
||||
}
|
||||
if len(tail) < 1 {
|
||||
return "", 0, false
|
||||
}
|
||||
rm, ok := decodeAMD64RM(tail, p.rexR, p.rexX, p.rexB)
|
||||
if !ok || !rm.regForm || rm.reg != 6 {
|
||||
return "", 0, false
|
||||
}
|
||||
return name + " " + amd64GPRNames[rm.rm], p.n + 2 + rm.n, true
|
||||
}
|
||||
|
||||
// nameAMD64Endbr names the ENDBR pair, F3 0F 1E with the fixed ModR/M
|
||||
// bytes FA for the 64-bit variant and FB for the 32-bit one. The
|
||||
// instructions take no operands.
|
||||
func nameAMD64Endbr(p amd64Prefixes, tail []byte) (string, int, bool) {
|
||||
if !p.rep || p.osz || p.repne {
|
||||
return "", 0, false
|
||||
}
|
||||
if len(tail) < 1 {
|
||||
return "", 0, false
|
||||
}
|
||||
switch tail[0] {
|
||||
case 0xfa:
|
||||
return "ENDBR64", p.n + 3, true
|
||||
case 0xfb:
|
||||
return "ENDBR32", p.n + 3, true
|
||||
}
|
||||
return "", 0, false
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package disasm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"sourcedock.dev/petrbalvin/gasm-sdk/arch"
|
||||
)
|
||||
|
||||
// TestDegenerateNaming pins the supplementary naming table over the whole
|
||||
// set of encodings the Go toolchain's assembler corpus carries for the
|
||||
// families x/arch decodes to the degenerate zero instruction. Every row's
|
||||
// bytes are the corpus's own expected-encoding comments (machine-checked
|
||||
// by the toolchain's assembler test suite); every text is the corpus's own
|
||||
// spelling of the instruction.
|
||||
//
|
||||
// The rows whose text the encoder carries are also in
|
||||
// testdata/parity_amd64_unlisted.txt, where the parity and round-trip
|
||||
// tests pin them; this table covers them too so a fixture edit cannot
|
||||
// move one without this one noticing. ENDBR32 is the exception: the
|
||||
// toolchain knows no spelling for it (its obj/x86 table carries ENDBR64
|
||||
// only), so its text is pinned here as bytes and text, with no
|
||||
// round-trip.
|
||||
func TestDegenerateNaming(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
code []byte
|
||||
text string
|
||||
}{
|
||||
// amd64enc.s: ADCXL (BX), DX // 660f38f613 and kin.
|
||||
{[]byte{0x66, 0x0f, 0x38, 0xf6, 0x13}, "ADCXL 0(BX), DX"},
|
||||
{[]byte{0x66, 0x41, 0x0f, 0x38, 0xf6, 0x13}, "ADCXL 0(R11), DX"},
|
||||
{[]byte{0x66, 0x0f, 0x38, 0xf6, 0xd2}, "ADCXL DX, DX"},
|
||||
{[]byte{0x66, 0x41, 0x0f, 0x38, 0xf6, 0xd3}, "ADCXL R11, DX"},
|
||||
{[]byte{0x66, 0x44, 0x0f, 0x38, 0xf6, 0x1b}, "ADCXL 0(BX), R11"},
|
||||
{[]byte{0x66, 0x45, 0x0f, 0x38, 0xf6, 0x1b}, "ADCXL 0(R11), R11"},
|
||||
{[]byte{0x66, 0x44, 0x0f, 0x38, 0xf6, 0xda}, "ADCXL DX, R11"},
|
||||
{[]byte{0x66, 0x45, 0x0f, 0x38, 0xf6, 0xdb}, "ADCXL R11, R11"},
|
||||
{[]byte{0x66, 0x48, 0x0f, 0x38, 0xf6, 0x13}, "ADCXQ 0(BX), DX"},
|
||||
{[]byte{0x66, 0x49, 0x0f, 0x38, 0xf6, 0x13}, "ADCXQ 0(R11), DX"},
|
||||
{[]byte{0x66, 0x48, 0x0f, 0x38, 0xf6, 0xd2}, "ADCXQ DX, DX"},
|
||||
{[]byte{0x66, 0x49, 0x0f, 0x38, 0xf6, 0xd3}, "ADCXQ R11, DX"},
|
||||
{[]byte{0x66, 0x4c, 0x0f, 0x38, 0xf6, 0x1b}, "ADCXQ 0(BX), R11"},
|
||||
{[]byte{0x66, 0x4d, 0x0f, 0x38, 0xf6, 0x1b}, "ADCXQ 0(R11), R11"},
|
||||
{[]byte{0x66, 0x4c, 0x0f, 0x38, 0xf6, 0xda}, "ADCXQ DX, R11"},
|
||||
{[]byte{0x66, 0x4d, 0x0f, 0x38, 0xf6, 0xdb}, "ADCXQ R11, R11"},
|
||||
// amd64enc.s: ADOXL (BX), DX // f30f38f613 and kin.
|
||||
{[]byte{0xf3, 0x0f, 0x38, 0xf6, 0x13}, "ADOXL 0(BX), DX"},
|
||||
{[]byte{0xf3, 0x41, 0x0f, 0x38, 0xf6, 0x13}, "ADOXL 0(R11), DX"},
|
||||
{[]byte{0xf3, 0x0f, 0x38, 0xf6, 0xd2}, "ADOXL DX, DX"},
|
||||
{[]byte{0xf3, 0x41, 0x0f, 0x38, 0xf6, 0xd3}, "ADOXL R11, DX"},
|
||||
{[]byte{0xf3, 0x44, 0x0f, 0x38, 0xf6, 0x1b}, "ADOXL 0(BX), R11"},
|
||||
{[]byte{0xf3, 0x45, 0x0f, 0x38, 0xf6, 0x1b}, "ADOXL 0(R11), R11"},
|
||||
{[]byte{0xf3, 0x44, 0x0f, 0x38, 0xf6, 0xda}, "ADOXL DX, R11"},
|
||||
{[]byte{0xf3, 0x45, 0x0f, 0x38, 0xf6, 0xdb}, "ADOXL R11, R11"},
|
||||
{[]byte{0xf3, 0x48, 0x0f, 0x38, 0xf6, 0x13}, "ADOXQ 0(BX), DX"},
|
||||
{[]byte{0xf3, 0x49, 0x0f, 0x38, 0xf6, 0x13}, "ADOXQ 0(R11), DX"},
|
||||
{[]byte{0xf3, 0x48, 0x0f, 0x38, 0xf6, 0xd2}, "ADOXQ DX, DX"},
|
||||
{[]byte{0xf3, 0x49, 0x0f, 0x38, 0xf6, 0xd3}, "ADOXQ R11, DX"},
|
||||
{[]byte{0xf3, 0x4c, 0x0f, 0x38, 0xf6, 0x1b}, "ADOXQ 0(BX), R11"},
|
||||
{[]byte{0xf3, 0x4d, 0x0f, 0x38, 0xf6, 0x1b}, "ADOXQ 0(R11), R11"},
|
||||
{[]byte{0xf3, 0x4c, 0x0f, 0x38, 0xf6, 0xda}, "ADOXQ DX, R11"},
|
||||
{[]byte{0xf3, 0x4d, 0x0f, 0x38, 0xf6, 0xdb}, "ADOXQ R11, R11"},
|
||||
// amd64enc.s: RDSEEDW DX // 660fc7fa.
|
||||
{[]byte{0x66, 0x0f, 0xc7, 0xfa}, "RDSEEDW DX"},
|
||||
{[]byte{0x66, 0x41, 0x0f, 0xc7, 0xfb}, "RDSEEDW R11"},
|
||||
// amd64enc_extra.s: RDPID DX // f30fc7fa.
|
||||
{[]byte{0xf3, 0x0f, 0xc7, 0xfa}, "RDPID DX"},
|
||||
{[]byte{0xf3, 0x41, 0x0f, 0xc7, 0xfb}, "RDPID R11"},
|
||||
// amd64enc_extra.s: TPAUSE BX // 660faef3 and kin.
|
||||
{[]byte{0x66, 0x0f, 0xae, 0xf3}, "TPAUSE BX"},
|
||||
{[]byte{0xf3, 0x0f, 0xae, 0xf3}, "UMONITOR BX"},
|
||||
{[]byte{0xf2, 0x0f, 0xae, 0xf3}, "UMWAIT BX"},
|
||||
// amd64enc_extra.s: ENDBR64 // f30f1efa; ENDBR32 has no
|
||||
// toolchain spelling.
|
||||
{[]byte{0xf3, 0x0f, 0x1e, 0xfa}, "ENDBR64"},
|
||||
{[]byte{0xf3, 0x0f, 0x1e, 0xfb}, "ENDBR32"},
|
||||
} {
|
||||
ins, err := Decode(arch.AMD64, tt.code, 0)
|
||||
if err != nil {
|
||||
t.Errorf("% x: %v", tt.code, err)
|
||||
continue
|
||||
}
|
||||
if ins.Text != tt.text || ins.Len != len(tt.code) {
|
||||
t.Errorf("% x: %q (%d bytes), want %q (%d)",
|
||||
tt.code, ins.Text, ins.Len, tt.text, len(tt.code))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestDegenerateNamingBoundaries guards the table's edges: bytes the
|
||||
// decoder rejects outright keep the placeholder, and the prefix
|
||||
// combinations no toolchain spelling carries stay unnamed even where the
|
||||
// decode is degenerate. The bare 0F 38 F6 form is the 32-bit ADCX of the
|
||||
// Intel manual; the toolchain's ADCXL spelling always carries the
|
||||
// operand-size override, so the bare form is left to the placeholder
|
||||
// rather than named to a spelling that would re-encode differently.
|
||||
func TestDegenerateNamingBoundaries(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
code []byte
|
||||
text string
|
||||
}{
|
||||
// Rejected outright: RDSEED without the operand-size override
|
||||
// (the bare 0F C7 /7 register form), MONITORX and MWAITX, the
|
||||
// hint NOP CLDEMOTE.
|
||||
{[]byte{0x0f, 0xc7, 0xfa}, "???"},
|
||||
{[]byte{0x0f, 0x01, 0xfa}, "???"},
|
||||
{[]byte{0x0f, 0x01, 0xfb}, "???"},
|
||||
{[]byte{0x0f, 0x1c, 0x03}, "???"},
|
||||
// Degenerate but outside the table's prefix gates: repne ADCX is
|
||||
// no instruction the corpus names.
|
||||
{[]byte{0xf2, 0x0f, 0x38, 0xf6, 0xd2}, "REPNE; Op(0)"},
|
||||
// MONITOR and MWAIT decode as named opcodes and never reach the
|
||||
// table.
|
||||
{[]byte{0x0f, 0x01, 0xc8}, "MONITOR"},
|
||||
{[]byte{0x0f, 0x01, 0xc9}, "MWAIT"},
|
||||
} {
|
||||
ins, err := Decode(arch.AMD64, tt.code, 0)
|
||||
if err != nil {
|
||||
t.Errorf("% x: %v", tt.code, err)
|
||||
continue
|
||||
}
|
||||
if ins.Text != tt.text {
|
||||
t.Errorf("% x: %q, want %q", tt.code, ins.Text, tt.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
+40
@@ -1188,3 +1188,43 @@ c4e17c77 VZEROALL
|
||||
c4e17877 VZEROUPPER
|
||||
0f09 WBINVD
|
||||
f30faed2 WRFSBASE DX
|
||||
660f38f613 ADCXL 0(BX), DX
|
||||
66410f38f613 ADCXL 0(R11), DX
|
||||
660f38f6d2 ADCXL DX, DX
|
||||
66410f38f6d3 ADCXL R11, DX
|
||||
66440f38f61b ADCXL 0(BX), R11
|
||||
66450f38f61b ADCXL 0(R11), R11
|
||||
66440f38f6da ADCXL DX, R11
|
||||
66450f38f6db ADCXL R11, R11
|
||||
66480f38f613 ADCXQ 0(BX), DX
|
||||
66490f38f613 ADCXQ 0(R11), DX
|
||||
66480f38f6d2 ADCXQ DX, DX
|
||||
66490f38f6d3 ADCXQ R11, DX
|
||||
664c0f38f61b ADCXQ 0(BX), R11
|
||||
664d0f38f61b ADCXQ 0(R11), R11
|
||||
664c0f38f6da ADCXQ DX, R11
|
||||
664d0f38f6db ADCXQ R11, R11
|
||||
f30f38f613 ADOXL 0(BX), DX
|
||||
f3410f38f613 ADOXL 0(R11), DX
|
||||
f30f38f6d2 ADOXL DX, DX
|
||||
f3410f38f6d3 ADOXL R11, DX
|
||||
f3440f38f61b ADOXL 0(BX), R11
|
||||
f3450f38f61b ADOXL 0(R11), R11
|
||||
f3440f38f6da ADOXL DX, R11
|
||||
f3450f38f6db ADOXL R11, R11
|
||||
f3480f38f613 ADOXQ 0(BX), DX
|
||||
f3490f38f613 ADOXQ 0(R11), DX
|
||||
f3480f38f6d2 ADOXQ DX, DX
|
||||
f3490f38f6d3 ADOXQ R11, DX
|
||||
f34c0f38f61b ADOXQ 0(BX), R11
|
||||
f34d0f38f61b ADOXQ 0(R11), R11
|
||||
f34c0f38f6da ADOXQ DX, R11
|
||||
f34d0f38f6db ADOXQ R11, R11
|
||||
660fc7fa RDSEEDW DX
|
||||
66410fc7fb RDSEEDW R11
|
||||
f30fc7fa RDPID DX
|
||||
f3410fc7fb RDPID R11
|
||||
660faef3 TPAUSE BX
|
||||
f30faef3 UMONITOR BX
|
||||
f20faef3 UMWAIT BX
|
||||
f30f1efa ENDBR64
|
||||
Reference in new issue
Block a user