
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.
382 lines
6.9 KiB
Go
382 lines
6.9 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"
|
|
)
|
|
|
|
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
|
|
}
|
|
switch s {
|
|
case 8:
|
|
op += "B"
|
|
case 16:
|
|
op += "W"
|
|
case 32:
|
|
op += "L"
|
|
case 64:
|
|
op += "Q"
|
|
}
|
|
}
|
|
|
|
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,
|
|
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",
|
|
}
|