// 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" ) type SymLookup func(uint64) (string, uint64) // GoSyntax returns the Go assembler syntax for the instruction. // The syntax was originally defined by Plan 9. // The pc is the program counter of the instruction, used for expanding // PC-relative addresses into absolute ones. // The symname function queries the symbol table for the program // being disassembled. Given a target address it returns the name and base // address of the symbol containing the target, if any; otherwise it returns "", 0. func GoSyntax(inst Inst, pc uint64, symname SymLookup) string { if symname == nil { symname = func(uint64) (string, uint64) { return "", 0 } } var args []string for i := len(inst.Args) - 1; i >= 0; i-- { a := inst.Args[i] if a == nil { continue } args = append(args, plan9Arg(&inst, pc, symname, a)) } var rep string var last Prefix for _, p := range inst.Prefix { if p == 0 || p.IsREX() || p.IsVEX() { break } switch { // Don't show prefixes implied by the instruction text. case p&0xFF00 == PrefixImplicit: continue // Only REP and REPN are recognized repeaters. Plan 9 syntax // treats them as separate opcodes. case p&0xFF == PrefixREP: rep = "REP; " case p&0xFF == PrefixREPN: rep = "REPNE; " default: last = p } } prefix := "" switch last & 0xFF { case 0, 0x66, 0x67: // ignore default: prefix += last.String() + " " } op := inst.Op.String() if plan9Suffix[inst.Op] { s := inst.DataSize if inst.MemBytes != 0 { s = inst.MemBytes * 8 } else if inst.Args[1] == nil { // look for register-only 64-bit instruction, like PUSHQ AX if r, ok := inst.Args[0].(Reg); ok && RAX <= r && r <= R15 { s = 64 } } switch s { case 8: op += "B" case 16: op += "W" case 32: op += "L" case 64: op += "Q" } } if inst.Op == CMP { // Use reads-left-to-right ordering for comparisons. // See issue 60920. args[0], args[1] = args[1], args[0] } if args != nil { op += " " + strings.Join(args, ", ") } return rep + prefix + op } func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg Arg) string { switch a := arg.(type) { case Reg: return plan9Reg[a] case Rel: if pc == 0 { break } // If the absolute address is the start of a symbol, use the name. // Otherwise use the raw address, so that things like relative // jumps show up as JMP 0x123 instead of JMP f+10(SB). // It is usually easier to search for 0x123 than to do the mental // arithmetic to find f+10. addr := pc + uint64(inst.Len) + uint64(a) if s, base := symname(addr); s != "" && addr == base { return fmt.Sprintf("%s(SB)", s) } return fmt.Sprintf("%#x", addr) 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(SB)", 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 s, disp := memArgToSymbol(a, pc, inst.Len, symname); s != "" { suffix := "" if disp != 0 { suffix = fmt.Sprintf("%+d", disp) } return fmt.Sprintf("%s%s(SB)", s, suffix) } s := "" if a.Segment != 0 { s += fmt.Sprintf("%s:", plan9Reg[a.Segment]) } if a.Disp != 0 { s += fmt.Sprintf("%#x", a.Disp) } else { s += "0" } if a.Base != 0 { s += fmt.Sprintf("(%s)", plan9Reg[a.Base]) } if a.Index != 0 && a.Scale != 0 { s += fmt.Sprintf("(%s*%d)", plan9Reg[a.Index], a.Scale) } return s } return arg.String() } func memArgToSymbol(a Mem, pc uint64, instrLen int, symname SymLookup) (string, int64) { if a.Segment != 0 || a.Disp == 0 || a.Index != 0 || a.Scale != 0 { return "", 0 } var disp uint64 switch a.Base { case IP, EIP, RIP: disp = uint64(a.Disp + int64(pc) + int64(instrLen)) case 0: disp = uint64(a.Disp) default: return "", 0 } s, base := symname(disp) return s, int64(disp) - int64(base) } var plan9Suffix = [maxOp + 1]bool{ ADC: true, ADD: true, AND: true, BSF: true, BSR: true, BT: true, BTC: true, BTR: true, BTS: true, CMP: true, CMPXCHG: true, CVTSI2SD: true, CVTSI2SS: true, CVTSD2SI: true, CVTSS2SI: true, CVTTSD2SI: true, CVTTSS2SI: true, DEC: true, DIV: true, FLDENV: true, FRSTOR: true, IDIV: true, IMUL: true, IN: true, INC: true, LEA: true, MOV: true, MOVNTI: true, MUL: true, NEG: true, NOP: true, NOT: true, OR: true, OUT: true, POP: true, POPA: true, POPCNT: true, PUSH: true, PUSHA: true, RCL: true, RCR: true, ROL: true, ROR: true, SAR: true, SBB: true, SHL: true, SHLD: true, SHR: true, SHRD: true, SUB: true, TEST: true, XADD: true, XCHG: true, XOR: true, } var plan9Reg = [...]string{ AL: "AL", CL: "CL", BL: "BL", DL: "DL", AH: "AH", CH: "CH", BH: "BH", DH: "DH", SPB: "SP", BPB: "BP", SIB: "SI", DIB: "DI", R8B: "R8", R9B: "R9", R10B: "R10", R11B: "R11", R12B: "R12", R13B: "R13", R14B: "R14", R15B: "R15", AX: "AX", CX: "CX", BX: "BX", DX: "DX", SP: "SP", BP: "BP", SI: "SI", DI: "DI", R8W: "R8", R9W: "R9", R10W: "R10", R11W: "R11", R12W: "R12", R13W: "R13", R14W: "R14", R15W: "R15", EAX: "AX", ECX: "CX", EDX: "DX", EBX: "BX", ESP: "SP", EBP: "BP", ESI: "SI", EDI: "DI", R8L: "R8", R9L: "R9", R10L: "R10", R11L: "R11", R12L: "R12", R13L: "R13", R14L: "R14", R15L: "R15", RAX: "AX", RCX: "CX", RDX: "DX", RBX: "BX", RSP: "SP", RBP: "BP", RSI: "SI", RDI: "DI", R8: "R8", R9: "R9", R10: "R10", R11: "R11", R12: "R12", R13: "R13", R14: "R14", R15: "R15", IP: "IP", EIP: "IP", RIP: "IP", F0: "F0", F1: "F1", F2: "F2", F3: "F3", F4: "F4", F5: "F5", F6: "F6", F7: "F7", M0: "M0", M1: "M1", M2: "M2", M3: "M3", M4: "M4", M5: "M5", M6: "M6", M7: "M7", X0: "X0", X1: "X1", X2: "X2", X3: "X3", X4: "X4", X5: "X5", X6: "X6", X7: "X7", X8: "X8", X9: "X9", X10: "X10", X11: "X11", X12: "X12", X13: "X13", X14: "X14", X15: "X15", CS: "CS", SS: "SS", DS: "DS", ES: "ES", FS: "FS", GS: "GS", GDTR: "GDTR", IDTR: "IDTR", LDTR: "LDTR", MSW: "MSW", TASK: "TASK", CR0: "CR0", CR1: "CR1", CR2: "CR2", CR3: "CR3", CR4: "CR4", CR5: "CR5", CR6: "CR6", CR7: "CR7", CR8: "CR8", CR9: "CR9", CR10: "CR10", CR11: "CR11", CR12: "CR12", CR13: "CR13", CR14: "CR14", CR15: "CR15", DR0: "DR0", DR1: "DR1", DR2: "DR2", DR3: "DR3", DR4: "DR4", DR5: "DR5", DR6: "DR6", DR7: "DR7", DR8: "DR8", DR9: "DR9", DR10: "DR10", DR11: "DR11", DR12: "DR12", DR13: "DR13", DR14: "DR14", DR15: "DR15", TR0: "TR0", TR1: "TR1", TR2: "TR2", TR3: "TR3", TR4: "TR4", TR5: "TR5", TR6: "TR6", TR7: "TR7", }