plan9x.go raw

   1  // Copyright 2014 The Go Authors.  All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  package x86asm
   6  
   7  import (
   8  	"fmt"
   9  	"strings"
  10  )
  11  
  12  type SymLookup func(uint64) (string, uint64)
  13  
  14  // GoSyntax returns the Go assembler syntax for the instruction.
  15  // The syntax was originally defined by Plan 9.
  16  // The pc is the program counter of the instruction, used for expanding
  17  // PC-relative addresses into absolute ones.
  18  // The symname function queries the symbol table for the program
  19  // being disassembled. Given a target address it returns the name and base
  20  // address of the symbol containing the target, if any; otherwise it returns "", 0.
  21  func GoSyntax(inst Inst, pc uint64, symname SymLookup) string {
  22  	if symname == nil {
  23  		symname = func(uint64) (string, uint64) { return "", 0 }
  24  	}
  25  	var args []string
  26  	for i := len(inst.Args) - 1; i >= 0; i-- {
  27  		a := inst.Args[i]
  28  		if a == nil {
  29  			continue
  30  		}
  31  		args = append(args, plan9Arg(&inst, pc, symname, a))
  32  	}
  33  
  34  	var rep string
  35  	var last Prefix
  36  	for _, p := range inst.Prefix {
  37  		if p == 0 || p.IsREX() || p.IsVEX() {
  38  			break
  39  		}
  40  
  41  		switch {
  42  		// Don't show prefixes implied by the instruction text.
  43  		case p&0xFF00 == PrefixImplicit:
  44  			continue
  45  		// Only REP and REPN are recognized repeaters. Plan 9 syntax
  46  		// treats them as separate opcodes.
  47  		case p&0xFF == PrefixREP:
  48  			rep = "REP; "
  49  		case p&0xFF == PrefixREPN:
  50  			rep = "REPNE; "
  51  		default:
  52  			last = p
  53  		}
  54  	}
  55  
  56  	prefix := ""
  57  	switch last & 0xFF {
  58  	case 0, 0x66, 0x67:
  59  		// ignore
  60  	default:
  61  		prefix += last.String() + " "
  62  	}
  63  
  64  	op := inst.Op.String()
  65  	if plan9Suffix[inst.Op] {
  66  		s := inst.DataSize
  67  		if inst.MemBytes != 0 {
  68  			s = inst.MemBytes * 8
  69  		} else if inst.Args[1] == nil { // look for register-only 64-bit instruction, like PUSHQ AX
  70  			if r, ok := inst.Args[0].(Reg); ok && RAX <= r && r <= R15 {
  71  				s = 64
  72  			}
  73  		}
  74  		switch s {
  75  		case 8:
  76  			op += "B"
  77  		case 16:
  78  			op += "W"
  79  		case 32:
  80  			op += "L"
  81  		case 64:
  82  			op += "Q"
  83  		}
  84  	}
  85  
  86  	if inst.Op == CMP {
  87  		// Use reads-left-to-right ordering for comparisons.
  88  		// See issue 60920.
  89  		args[0], args[1] = args[1], args[0]
  90  	}
  91  
  92  	if args != nil {
  93  		op += " " + strings.Join(args, ", ")
  94  	}
  95  
  96  	return rep + prefix + op
  97  }
  98  
  99  func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg Arg) string {
 100  	switch a := arg.(type) {
 101  	case Reg:
 102  		return plan9Reg[a]
 103  	case Rel:
 104  		if pc == 0 {
 105  			break
 106  		}
 107  		// If the absolute address is the start of a symbol, use the name.
 108  		// Otherwise use the raw address, so that things like relative
 109  		// jumps show up as JMP 0x123 instead of JMP f+10(SB).
 110  		// It is usually easier to search for 0x123 than to do the mental
 111  		// arithmetic to find f+10.
 112  		addr := pc + uint64(inst.Len) + uint64(a)
 113  		if s, base := symname(addr); s != "" && addr == base {
 114  			return fmt.Sprintf("%s(SB)", s)
 115  		}
 116  		return fmt.Sprintf("%#x", addr)
 117  
 118  	case Imm:
 119  		if s, base := symname(uint64(a)); s != "" {
 120  			suffix := ""
 121  			if uint64(a) != base {
 122  				suffix = fmt.Sprintf("%+d", uint64(a)-base)
 123  			}
 124  			return fmt.Sprintf("$%s%s(SB)", s, suffix)
 125  		}
 126  		if inst.Mode == 32 {
 127  			return fmt.Sprintf("$%#x", uint32(a))
 128  		}
 129  		if Imm(int32(a)) == a {
 130  			return fmt.Sprintf("$%#x", int64(a))
 131  		}
 132  		return fmt.Sprintf("$%#x", uint64(a))
 133  	case Mem:
 134  		if s, disp := memArgToSymbol(a, pc, inst.Len, symname); s != "" {
 135  			suffix := ""
 136  			if disp != 0 {
 137  				suffix = fmt.Sprintf("%+d", disp)
 138  			}
 139  			return fmt.Sprintf("%s%s(SB)", s, suffix)
 140  		}
 141  		s := ""
 142  		if a.Segment != 0 {
 143  			s += fmt.Sprintf("%s:", plan9Reg[a.Segment])
 144  		}
 145  		if a.Disp != 0 {
 146  			s += fmt.Sprintf("%#x", a.Disp)
 147  		} else {
 148  			s += "0"
 149  		}
 150  		if a.Base != 0 {
 151  			s += fmt.Sprintf("(%s)", plan9Reg[a.Base])
 152  		}
 153  		if a.Index != 0 && a.Scale != 0 {
 154  			s += fmt.Sprintf("(%s*%d)", plan9Reg[a.Index], a.Scale)
 155  		}
 156  		return s
 157  	}
 158  	return arg.String()
 159  }
 160  
 161  func memArgToSymbol(a Mem, pc uint64, instrLen int, symname SymLookup) (string, int64) {
 162  	if a.Segment != 0 || a.Disp == 0 || a.Index != 0 || a.Scale != 0 {
 163  		return "", 0
 164  	}
 165  
 166  	var disp uint64
 167  	switch a.Base {
 168  	case IP, EIP, RIP:
 169  		disp = uint64(a.Disp + int64(pc) + int64(instrLen))
 170  	case 0:
 171  		disp = uint64(a.Disp)
 172  	default:
 173  		return "", 0
 174  	}
 175  
 176  	s, base := symname(disp)
 177  	return s, int64(disp) - int64(base)
 178  }
 179  
 180  var plan9Suffix = [maxOp + 1]bool{
 181  	ADC:       true,
 182  	ADD:       true,
 183  	AND:       true,
 184  	BSF:       true,
 185  	BSR:       true,
 186  	BT:        true,
 187  	BTC:       true,
 188  	BTR:       true,
 189  	BTS:       true,
 190  	CMP:       true,
 191  	CMPXCHG:   true,
 192  	CVTSI2SD:  true,
 193  	CVTSI2SS:  true,
 194  	CVTSD2SI:  true,
 195  	CVTSS2SI:  true,
 196  	CVTTSD2SI: true,
 197  	CVTTSS2SI: true,
 198  	DEC:       true,
 199  	DIV:       true,
 200  	FLDENV:    true,
 201  	FRSTOR:    true,
 202  	IDIV:      true,
 203  	IMUL:      true,
 204  	IN:        true,
 205  	INC:       true,
 206  	LEA:       true,
 207  	MOV:       true,
 208  	MOVNTI:    true,
 209  	MUL:       true,
 210  	NEG:       true,
 211  	NOP:       true,
 212  	NOT:       true,
 213  	OR:        true,
 214  	OUT:       true,
 215  	POP:       true,
 216  	POPA:      true,
 217  	POPCNT:    true,
 218  	PUSH:      true,
 219  	PUSHA:     true,
 220  	RCL:       true,
 221  	RCR:       true,
 222  	ROL:       true,
 223  	ROR:       true,
 224  	SAR:       true,
 225  	SBB:       true,
 226  	SHL:       true,
 227  	SHLD:      true,
 228  	SHR:       true,
 229  	SHRD:      true,
 230  	SUB:       true,
 231  	TEST:      true,
 232  	XADD:      true,
 233  	XCHG:      true,
 234  	XOR:       true,
 235  }
 236  
 237  var plan9Reg = [...]string{
 238  	AL:   "AL",
 239  	CL:   "CL",
 240  	BL:   "BL",
 241  	DL:   "DL",
 242  	AH:   "AH",
 243  	CH:   "CH",
 244  	BH:   "BH",
 245  	DH:   "DH",
 246  	SPB:  "SP",
 247  	BPB:  "BP",
 248  	SIB:  "SI",
 249  	DIB:  "DI",
 250  	R8B:  "R8",
 251  	R9B:  "R9",
 252  	R10B: "R10",
 253  	R11B: "R11",
 254  	R12B: "R12",
 255  	R13B: "R13",
 256  	R14B: "R14",
 257  	R15B: "R15",
 258  	AX:   "AX",
 259  	CX:   "CX",
 260  	BX:   "BX",
 261  	DX:   "DX",
 262  	SP:   "SP",
 263  	BP:   "BP",
 264  	SI:   "SI",
 265  	DI:   "DI",
 266  	R8W:  "R8",
 267  	R9W:  "R9",
 268  	R10W: "R10",
 269  	R11W: "R11",
 270  	R12W: "R12",
 271  	R13W: "R13",
 272  	R14W: "R14",
 273  	R15W: "R15",
 274  	EAX:  "AX",
 275  	ECX:  "CX",
 276  	EDX:  "DX",
 277  	EBX:  "BX",
 278  	ESP:  "SP",
 279  	EBP:  "BP",
 280  	ESI:  "SI",
 281  	EDI:  "DI",
 282  	R8L:  "R8",
 283  	R9L:  "R9",
 284  	R10L: "R10",
 285  	R11L: "R11",
 286  	R12L: "R12",
 287  	R13L: "R13",
 288  	R14L: "R14",
 289  	R15L: "R15",
 290  	RAX:  "AX",
 291  	RCX:  "CX",
 292  	RDX:  "DX",
 293  	RBX:  "BX",
 294  	RSP:  "SP",
 295  	RBP:  "BP",
 296  	RSI:  "SI",
 297  	RDI:  "DI",
 298  	R8:   "R8",
 299  	R9:   "R9",
 300  	R10:  "R10",
 301  	R11:  "R11",
 302  	R12:  "R12",
 303  	R13:  "R13",
 304  	R14:  "R14",
 305  	R15:  "R15",
 306  	IP:   "IP",
 307  	EIP:  "IP",
 308  	RIP:  "IP",
 309  	F0:   "F0",
 310  	F1:   "F1",
 311  	F2:   "F2",
 312  	F3:   "F3",
 313  	F4:   "F4",
 314  	F5:   "F5",
 315  	F6:   "F6",
 316  	F7:   "F7",
 317  	M0:   "M0",
 318  	M1:   "M1",
 319  	M2:   "M2",
 320  	M3:   "M3",
 321  	M4:   "M4",
 322  	M5:   "M5",
 323  	M6:   "M6",
 324  	M7:   "M7",
 325  	X0:   "X0",
 326  	X1:   "X1",
 327  	X2:   "X2",
 328  	X3:   "X3",
 329  	X4:   "X4",
 330  	X5:   "X5",
 331  	X6:   "X6",
 332  	X7:   "X7",
 333  	X8:   "X8",
 334  	X9:   "X9",
 335  	X10:  "X10",
 336  	X11:  "X11",
 337  	X12:  "X12",
 338  	X13:  "X13",
 339  	X14:  "X14",
 340  	X15:  "X15",
 341  	CS:   "CS",
 342  	SS:   "SS",
 343  	DS:   "DS",
 344  	ES:   "ES",
 345  	FS:   "FS",
 346  	GS:   "GS",
 347  	GDTR: "GDTR",
 348  	IDTR: "IDTR",
 349  	LDTR: "LDTR",
 350  	MSW:  "MSW",
 351  	TASK: "TASK",
 352  	CR0:  "CR0",
 353  	CR1:  "CR1",
 354  	CR2:  "CR2",
 355  	CR3:  "CR3",
 356  	CR4:  "CR4",
 357  	CR5:  "CR5",
 358  	CR6:  "CR6",
 359  	CR7:  "CR7",
 360  	CR8:  "CR8",
 361  	CR9:  "CR9",
 362  	CR10: "CR10",
 363  	CR11: "CR11",
 364  	CR12: "CR12",
 365  	CR13: "CR13",
 366  	CR14: "CR14",
 367  	CR15: "CR15",
 368  	DR0:  "DR0",
 369  	DR1:  "DR1",
 370  	DR2:  "DR2",
 371  	DR3:  "DR3",
 372  	DR4:  "DR4",
 373  	DR5:  "DR5",
 374  	DR6:  "DR6",
 375  	DR7:  "DR7",
 376  	DR8:  "DR8",
 377  	DR9:  "DR9",
 378  	DR10: "DR10",
 379  	DR11: "DR11",
 380  	DR12: "DR12",
 381  	DR13: "DR13",
 382  	DR14: "DR14",
 383  	DR15: "DR15",
 384  	TR0:  "TR0",
 385  	TR1:  "TR1",
 386  	TR2:  "TR2",
 387  	TR3:  "TR3",
 388  	TR4:  "TR4",
 389  	TR5:  "TR5",
 390  	TR6:  "TR6",
 391  	TR7:  "TR7",
 392  }
 393