123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- // 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 (inst.Op == MOV || inst.Op == PUSH) && inst.DataSize == 32 {
- // Only try to convert an immediate to a symbol in certain
- // special circumstances. See issue 72942.
- //
- // On 64-bit, symbol addresses always hit the Mem case below.
- // Particularly, we use LEAQ to materialize the address of
- // a global or function.
- //
- // On 32-bit, we sometimes use MOVL. Still try to symbolize
- // those immediates.
- 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",
- }
|