
updates vendored version of x86asm, adds a symbol lookup function to pass to the disassembler. This will show global symbol names in the disassembly like go tool objdump does.
561 lines
12 KiB
Go
561 lines
12 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 (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// IntelSyntax returns the Intel assembler syntax for the instruction, as defined by Intel's XED tool.
|
|
func IntelSyntax(inst Inst, pc uint64, symname SymLookup) string {
|
|
if symname == nil {
|
|
symname = func(uint64) (string, uint64) { return "", 0 }
|
|
}
|
|
|
|
var iargs []Arg
|
|
for _, a := range inst.Args {
|
|
if a == nil {
|
|
break
|
|
}
|
|
iargs = append(iargs, a)
|
|
}
|
|
|
|
switch inst.Op {
|
|
case INSB, INSD, INSW, OUTSB, OUTSD, OUTSW, LOOPNE, JCXZ, JECXZ, JRCXZ, LOOP, LOOPE, MOV, XLATB:
|
|
if inst.Op == MOV && (inst.Opcode>>16)&0xFFFC != 0x0F20 {
|
|
break
|
|
}
|
|
for i, p := range inst.Prefix {
|
|
if p&0xFF == PrefixAddrSize {
|
|
inst.Prefix[i] &^= PrefixImplicit
|
|
}
|
|
}
|
|
}
|
|
|
|
switch inst.Op {
|
|
case MOV:
|
|
dst, _ := inst.Args[0].(Reg)
|
|
src, _ := inst.Args[1].(Reg)
|
|
if ES <= dst && dst <= GS && EAX <= src && src <= R15L {
|
|
src -= EAX - AX
|
|
iargs[1] = src
|
|
}
|
|
if ES <= dst && dst <= GS && RAX <= src && src <= R15 {
|
|
src -= RAX - AX
|
|
iargs[1] = src
|
|
}
|
|
|
|
if inst.Opcode>>24&^3 == 0xA0 {
|
|
for i, p := range inst.Prefix {
|
|
if p&0xFF == PrefixAddrSize {
|
|
inst.Prefix[i] |= PrefixImplicit
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
switch inst.Op {
|
|
case AAM, AAD:
|
|
if imm, ok := iargs[0].(Imm); ok {
|
|
if inst.DataSize == 32 {
|
|
iargs[0] = Imm(uint32(int8(imm)))
|
|
} else if inst.DataSize == 16 {
|
|
iargs[0] = Imm(uint16(int8(imm)))
|
|
}
|
|
}
|
|
|
|
case PUSH:
|
|
if imm, ok := iargs[0].(Imm); ok {
|
|
iargs[0] = Imm(uint32(imm))
|
|
}
|
|
}
|
|
|
|
for _, p := range inst.Prefix {
|
|
if p&PrefixImplicit != 0 {
|
|
for j, pj := range inst.Prefix {
|
|
if pj&0xFF == p&0xFF {
|
|
inst.Prefix[j] |= PrefixImplicit
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if inst.Op != 0 {
|
|
for i, p := range inst.Prefix {
|
|
switch p &^ PrefixIgnored {
|
|
case PrefixData16, PrefixData32, PrefixCS, PrefixDS, PrefixES, PrefixSS:
|
|
inst.Prefix[i] |= PrefixImplicit
|
|
}
|
|
if p.IsREX() {
|
|
inst.Prefix[i] |= PrefixImplicit
|
|
}
|
|
if p.IsVEX() {
|
|
if p == PrefixVEX3Bytes {
|
|
inst.Prefix[i+2] |= PrefixImplicit
|
|
}
|
|
inst.Prefix[i] |= PrefixImplicit
|
|
inst.Prefix[i+1] |= PrefixImplicit
|
|
}
|
|
}
|
|
}
|
|
|
|
if isLoop[inst.Op] || inst.Op == JCXZ || inst.Op == JECXZ || inst.Op == JRCXZ {
|
|
for i, p := range inst.Prefix {
|
|
if p == PrefixPT || p == PrefixPN {
|
|
inst.Prefix[i] |= PrefixImplicit
|
|
}
|
|
}
|
|
}
|
|
|
|
switch inst.Op {
|
|
case AAA, AAS, CBW, CDQE, CLC, CLD, CLI, CLTS, CMC, CPUID, CQO, CWD, DAA, DAS,
|
|
FDECSTP, FINCSTP, FNCLEX, FNINIT, FNOP, FWAIT, HLT,
|
|
ICEBP, INSB, INSD, INSW, INT, INTO, INVD, IRET, IRETQ,
|
|
LAHF, LEAVE, LRET, MONITOR, MWAIT, NOP, OUTSB, OUTSD, OUTSW,
|
|
PAUSE, POPA, POPF, POPFQ, PUSHA, PUSHF, PUSHFQ,
|
|
RDMSR, RDPMC, RDTSC, RDTSCP, RET, RSM,
|
|
SAHF, STC, STD, STI, SYSENTER, SYSEXIT, SYSRET,
|
|
UD2, WBINVD, WRMSR, XEND, XLATB, XTEST:
|
|
|
|
if inst.Op == NOP && inst.Opcode>>24 != 0x90 {
|
|
break
|
|
}
|
|
if inst.Op == RET && inst.Opcode>>24 != 0xC3 {
|
|
break
|
|
}
|
|
if inst.Op == INT && inst.Opcode>>24 != 0xCC {
|
|
break
|
|
}
|
|
if inst.Op == LRET && inst.Opcode>>24 != 0xcb {
|
|
break
|
|
}
|
|
for i, p := range inst.Prefix {
|
|
if p&0xFF == PrefixDataSize {
|
|
inst.Prefix[i] &^= PrefixImplicit | PrefixIgnored
|
|
}
|
|
}
|
|
|
|
case 0:
|
|
// ok
|
|
}
|
|
|
|
switch inst.Op {
|
|
case INSB, INSD, INSW, OUTSB, OUTSD, OUTSW, MONITOR, MWAIT, XLATB:
|
|
iargs = nil
|
|
|
|
case STOSB, STOSW, STOSD, STOSQ:
|
|
iargs = iargs[:1]
|
|
|
|
case LODSB, LODSW, LODSD, LODSQ, SCASB, SCASW, SCASD, SCASQ:
|
|
iargs = iargs[1:]
|
|
}
|
|
|
|
const (
|
|
haveData16 = 1 << iota
|
|
haveData32
|
|
haveAddr16
|
|
haveAddr32
|
|
haveXacquire
|
|
haveXrelease
|
|
haveLock
|
|
haveHintTaken
|
|
haveHintNotTaken
|
|
haveBnd
|
|
)
|
|
var prefixBits uint32
|
|
prefix := ""
|
|
for _, p := range inst.Prefix {
|
|
if p == 0 {
|
|
break
|
|
}
|
|
if p&0xFF == 0xF3 {
|
|
prefixBits &^= haveBnd
|
|
}
|
|
if p&(PrefixImplicit|PrefixIgnored) != 0 {
|
|
continue
|
|
}
|
|
switch p {
|
|
default:
|
|
prefix += strings.ToLower(p.String()) + " "
|
|
case PrefixCS, PrefixDS, PrefixES, PrefixFS, PrefixGS, PrefixSS:
|
|
if inst.Op == 0 {
|
|
prefix += strings.ToLower(p.String()) + " "
|
|
}
|
|
case PrefixREPN:
|
|
prefix += "repne "
|
|
case PrefixLOCK:
|
|
prefixBits |= haveLock
|
|
case PrefixData16, PrefixDataSize:
|
|
prefixBits |= haveData16
|
|
case PrefixData32:
|
|
prefixBits |= haveData32
|
|
case PrefixAddrSize, PrefixAddr16:
|
|
prefixBits |= haveAddr16
|
|
case PrefixAddr32:
|
|
prefixBits |= haveAddr32
|
|
case PrefixXACQUIRE:
|
|
prefixBits |= haveXacquire
|
|
case PrefixXRELEASE:
|
|
prefixBits |= haveXrelease
|
|
case PrefixPT:
|
|
prefixBits |= haveHintTaken
|
|
case PrefixPN:
|
|
prefixBits |= haveHintNotTaken
|
|
case PrefixBND:
|
|
prefixBits |= haveBnd
|
|
}
|
|
}
|
|
switch inst.Op {
|
|
case JMP:
|
|
if inst.Opcode>>24 == 0xEB {
|
|
prefixBits &^= haveBnd
|
|
}
|
|
case RET, LRET:
|
|
prefixBits &^= haveData16 | haveData32
|
|
}
|
|
|
|
if prefixBits&haveXacquire != 0 {
|
|
prefix += "xacquire "
|
|
}
|
|
if prefixBits&haveXrelease != 0 {
|
|
prefix += "xrelease "
|
|
}
|
|
if prefixBits&haveLock != 0 {
|
|
prefix += "lock "
|
|
}
|
|
if prefixBits&haveBnd != 0 {
|
|
prefix += "bnd "
|
|
}
|
|
if prefixBits&haveHintTaken != 0 {
|
|
prefix += "hint-taken "
|
|
}
|
|
if prefixBits&haveHintNotTaken != 0 {
|
|
prefix += "hint-not-taken "
|
|
}
|
|
if prefixBits&haveAddr16 != 0 {
|
|
prefix += "addr16 "
|
|
}
|
|
if prefixBits&haveAddr32 != 0 {
|
|
prefix += "addr32 "
|
|
}
|
|
if prefixBits&haveData16 != 0 {
|
|
prefix += "data16 "
|
|
}
|
|
if prefixBits&haveData32 != 0 {
|
|
prefix += "data32 "
|
|
}
|
|
|
|
if inst.Op == 0 {
|
|
if prefix == "" {
|
|
return "<no instruction>"
|
|
}
|
|
return prefix[:len(prefix)-1]
|
|
}
|
|
|
|
var args []string
|
|
for _, a := range iargs {
|
|
if a == nil {
|
|
break
|
|
}
|
|
args = append(args, intelArg(&inst, pc, symname, a))
|
|
}
|
|
|
|
var op string
|
|
switch inst.Op {
|
|
case NOP:
|
|
if inst.Opcode>>24 == 0x0F {
|
|
if inst.DataSize == 16 {
|
|
args = append(args, "ax")
|
|
} else {
|
|
args = append(args, "eax")
|
|
}
|
|
}
|
|
|
|
case BLENDVPD, BLENDVPS, PBLENDVB:
|
|
args = args[:2]
|
|
|
|
case INT:
|
|
if inst.Opcode>>24 == 0xCC {
|
|
args = nil
|
|
op = "int3"
|
|
}
|
|
|
|
case LCALL, LJMP:
|
|
if len(args) == 2 {
|
|
args[0], args[1] = args[1], args[0]
|
|
}
|
|
|
|
case FCHS, FABS, FTST, FLDPI, FLDL2E, FLDLG2, F2XM1, FXAM, FLD1, FLDL2T, FSQRT, FRNDINT, FCOS, FSIN:
|
|
if len(args) == 0 {
|
|
args = append(args, "st0")
|
|
}
|
|
|
|
case FPTAN, FSINCOS, FUCOMPP, FCOMPP, FYL2X, FPATAN, FXTRACT, FPREM1, FPREM, FYL2XP1, FSCALE:
|
|
if len(args) == 0 {
|
|
args = []string{"st0", "st1"}
|
|
}
|
|
|
|
case FST, FSTP, FISTTP, FIST, FISTP, FBSTP:
|
|
if len(args) == 1 {
|
|
args = append(args, "st0")
|
|
}
|
|
|
|
case FLD, FXCH, FCOM, FCOMP, FIADD, FIMUL, FICOM, FICOMP, FISUBR, FIDIV, FUCOM, FUCOMP, FILD, FBLD, FADD, FMUL, FSUB, FSUBR, FISUB, FDIV, FDIVR, FIDIVR:
|
|
if len(args) == 1 {
|
|
args = []string{"st0", args[0]}
|
|
}
|
|
|
|
case MASKMOVDQU, MASKMOVQ, XLATB, OUTSB, OUTSW, OUTSD:
|
|
FixSegment:
|
|
for i := len(inst.Prefix) - 1; i >= 0; i-- {
|
|
p := inst.Prefix[i] & 0xFF
|
|
switch p {
|
|
case PrefixCS, PrefixES, PrefixFS, PrefixGS, PrefixSS:
|
|
if inst.Mode != 64 || p == PrefixFS || p == PrefixGS {
|
|
args = append(args, strings.ToLower((inst.Prefix[i] & 0xFF).String()))
|
|
break FixSegment
|
|
}
|
|
case PrefixDS:
|
|
if inst.Mode != 64 {
|
|
break FixSegment
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if op == "" {
|
|
op = intelOp[inst.Op]
|
|
}
|
|
if op == "" {
|
|
op = strings.ToLower(inst.Op.String())
|
|
}
|
|
if args != nil {
|
|
op += " " + strings.Join(args, ", ")
|
|
}
|
|
return prefix + op
|
|
}
|
|
|
|
func intelArg(inst *Inst, pc uint64, symname SymLookup, arg Arg) string {
|
|
switch a := arg.(type) {
|
|
case Imm:
|
|
if s, base := symname(uint64(a)); s != "" {
|
|
suffix := ""
|
|
if uint64(a) != base {
|
|
suffix = fmt.Sprintf("%+d", uint64(a)-base)
|
|
}
|
|
return fmt.Sprintf("$%s%s", s, suffix)
|
|
}
|
|
if inst.Mode == 32 {
|
|
return fmt.Sprintf("%#x", uint32(a))
|
|
}
|
|
if Imm(int32(a)) == a {
|
|
return fmt.Sprintf("%#x", int64(a))
|
|
}
|
|
return fmt.Sprintf("%#x", uint64(a))
|
|
case Mem:
|
|
if a.Base == EIP {
|
|
a.Base = RIP
|
|
}
|
|
prefix := ""
|
|
switch inst.MemBytes {
|
|
case 1:
|
|
prefix = "byte "
|
|
case 2:
|
|
prefix = "word "
|
|
case 4:
|
|
prefix = "dword "
|
|
case 8:
|
|
prefix = "qword "
|
|
case 16:
|
|
prefix = "xmmword "
|
|
case 32:
|
|
prefix = "ymmword "
|
|
}
|
|
switch inst.Op {
|
|
case INVLPG:
|
|
prefix = "byte "
|
|
case STOSB, MOVSB, CMPSB, LODSB, SCASB:
|
|
prefix = "byte "
|
|
case STOSW, MOVSW, CMPSW, LODSW, SCASW:
|
|
prefix = "word "
|
|
case STOSD, MOVSD, CMPSD, LODSD, SCASD:
|
|
prefix = "dword "
|
|
case STOSQ, MOVSQ, CMPSQ, LODSQ, SCASQ:
|
|
prefix = "qword "
|
|
case LAR:
|
|
prefix = "word "
|
|
case BOUND:
|
|
if inst.Mode == 32 {
|
|
prefix = "qword "
|
|
} else {
|
|
prefix = "dword "
|
|
}
|
|
case PREFETCHW, PREFETCHNTA, PREFETCHT0, PREFETCHT1, PREFETCHT2, CLFLUSH:
|
|
prefix = "zmmword "
|
|
}
|
|
switch inst.Op {
|
|
case MOVSB, MOVSW, MOVSD, MOVSQ, CMPSB, CMPSW, CMPSD, CMPSQ, STOSB, STOSW, STOSD, STOSQ, SCASB, SCASW, SCASD, SCASQ, LODSB, LODSW, LODSD, LODSQ:
|
|
switch a.Base {
|
|
case DI, EDI, RDI:
|
|
if a.Segment == ES {
|
|
a.Segment = 0
|
|
}
|
|
case SI, ESI, RSI:
|
|
if a.Segment == DS {
|
|
a.Segment = 0
|
|
}
|
|
}
|
|
case LEA:
|
|
a.Segment = 0
|
|
default:
|
|
switch a.Base {
|
|
case SP, ESP, RSP, BP, EBP, RBP:
|
|
if a.Segment == SS {
|
|
a.Segment = 0
|
|
}
|
|
default:
|
|
if a.Segment == DS {
|
|
a.Segment = 0
|
|
}
|
|
}
|
|
}
|
|
|
|
if inst.Mode == 64 && a.Segment != FS && a.Segment != GS {
|
|
a.Segment = 0
|
|
}
|
|
|
|
prefix += "ptr "
|
|
if s, disp := memArgToSymbol(a, pc, inst.Len, symname); s != "" {
|
|
suffix := ""
|
|
if disp != 0 {
|
|
suffix = fmt.Sprintf("%+d", disp)
|
|
}
|
|
return prefix + fmt.Sprintf("[%s%s]", s, suffix)
|
|
}
|
|
if a.Segment != 0 {
|
|
prefix += strings.ToLower(a.Segment.String()) + ":"
|
|
}
|
|
prefix += "["
|
|
if a.Base != 0 {
|
|
prefix += intelArg(inst, pc, symname, a.Base)
|
|
}
|
|
if a.Scale != 0 && a.Index != 0 {
|
|
if a.Base != 0 {
|
|
prefix += "+"
|
|
}
|
|
prefix += fmt.Sprintf("%s*%d", intelArg(inst, pc, symname, a.Index), a.Scale)
|
|
}
|
|
if a.Disp != 0 {
|
|
if prefix[len(prefix)-1] == '[' && (a.Disp >= 0 || int64(int32(a.Disp)) != a.Disp) {
|
|
prefix += fmt.Sprintf("%#x", uint64(a.Disp))
|
|
} else {
|
|
prefix += fmt.Sprintf("%+#x", a.Disp)
|
|
}
|
|
}
|
|
prefix += "]"
|
|
return prefix
|
|
case Rel:
|
|
if pc == 0 {
|
|
return fmt.Sprintf(".%+#x", int64(a))
|
|
} else {
|
|
addr := pc + uint64(inst.Len) + uint64(a)
|
|
if s, base := symname(addr); s != "" && addr == base {
|
|
return fmt.Sprintf("%s", s)
|
|
} else {
|
|
addr := pc + uint64(inst.Len) + uint64(a)
|
|
return fmt.Sprintf("%#x", addr)
|
|
}
|
|
}
|
|
case Reg:
|
|
if int(a) < len(intelReg) && intelReg[a] != "" {
|
|
switch inst.Op {
|
|
case VMOVDQA, VMOVDQU, VMOVNTDQA, VMOVNTDQ:
|
|
return strings.Replace(intelReg[a], "xmm", "ymm", -1)
|
|
default:
|
|
return intelReg[a]
|
|
}
|
|
}
|
|
}
|
|
return strings.ToLower(arg.String())
|
|
}
|
|
|
|
var intelOp = map[Op]string{
|
|
JAE: "jnb",
|
|
JA: "jnbe",
|
|
JGE: "jnl",
|
|
JNE: "jnz",
|
|
JG: "jnle",
|
|
JE: "jz",
|
|
SETAE: "setnb",
|
|
SETA: "setnbe",
|
|
SETGE: "setnl",
|
|
SETNE: "setnz",
|
|
SETG: "setnle",
|
|
SETE: "setz",
|
|
CMOVAE: "cmovnb",
|
|
CMOVA: "cmovnbe",
|
|
CMOVGE: "cmovnl",
|
|
CMOVNE: "cmovnz",
|
|
CMOVG: "cmovnle",
|
|
CMOVE: "cmovz",
|
|
LCALL: "call far",
|
|
LJMP: "jmp far",
|
|
LRET: "ret far",
|
|
ICEBP: "int1",
|
|
MOVSD_XMM: "movsd",
|
|
XLATB: "xlat",
|
|
}
|
|
|
|
var intelReg = [...]string{
|
|
F0: "st0",
|
|
F1: "st1",
|
|
F2: "st2",
|
|
F3: "st3",
|
|
F4: "st4",
|
|
F5: "st5",
|
|
F6: "st6",
|
|
F7: "st7",
|
|
M0: "mmx0",
|
|
M1: "mmx1",
|
|
M2: "mmx2",
|
|
M3: "mmx3",
|
|
M4: "mmx4",
|
|
M5: "mmx5",
|
|
M6: "mmx6",
|
|
M7: "mmx7",
|
|
X0: "xmm0",
|
|
X1: "xmm1",
|
|
X2: "xmm2",
|
|
X3: "xmm3",
|
|
X4: "xmm4",
|
|
X5: "xmm5",
|
|
X6: "xmm6",
|
|
X7: "xmm7",
|
|
X8: "xmm8",
|
|
X9: "xmm9",
|
|
X10: "xmm10",
|
|
X11: "xmm11",
|
|
X12: "xmm12",
|
|
X13: "xmm13",
|
|
X14: "xmm14",
|
|
X15: "xmm15",
|
|
|
|
// TODO: Maybe the constants are named wrong.
|
|
SPB: "spl",
|
|
BPB: "bpl",
|
|
SIB: "sil",
|
|
DIB: "dil",
|
|
|
|
R8L: "r8d",
|
|
R9L: "r9d",
|
|
R10L: "r10d",
|
|
R11L: "r11d",
|
|
R12L: "r12d",
|
|
R13L: "r13d",
|
|
R14L: "r14d",
|
|
R15L: "r15d",
|
|
}
|