intel.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  // IntelSyntax returns the Intel assembler syntax for the instruction, as defined by Intel's XED tool.
  13  func IntelSyntax(inst Inst, pc uint64, symname SymLookup) string {
  14  	if symname == nil {
  15  		symname = func(uint64) (string, uint64) { return "", 0 }
  16  	}
  17  
  18  	var iargs []Arg
  19  	for _, a := range inst.Args {
  20  		if a == nil {
  21  			break
  22  		}
  23  		iargs = append(iargs, a)
  24  	}
  25  
  26  	switch inst.Op {
  27  	case INSB, INSD, INSW, OUTSB, OUTSD, OUTSW, LOOPNE, JCXZ, JECXZ, JRCXZ, LOOP, LOOPE, MOV, XLATB:
  28  		if inst.Op == MOV && (inst.Opcode>>16)&0xFFFC != 0x0F20 {
  29  			break
  30  		}
  31  		for i, p := range inst.Prefix {
  32  			if p&0xFF == PrefixAddrSize {
  33  				inst.Prefix[i] &^= PrefixImplicit
  34  			}
  35  		}
  36  	}
  37  
  38  	switch inst.Op {
  39  	case MOV:
  40  		dst, _ := inst.Args[0].(Reg)
  41  		src, _ := inst.Args[1].(Reg)
  42  		if ES <= dst && dst <= GS && EAX <= src && src <= R15L {
  43  			src -= EAX - AX
  44  			iargs[1] = src
  45  		}
  46  		if ES <= dst && dst <= GS && RAX <= src && src <= R15 {
  47  			src -= RAX - AX
  48  			iargs[1] = src
  49  		}
  50  
  51  		if inst.Opcode>>24&^3 == 0xA0 {
  52  			for i, p := range inst.Prefix {
  53  				if p&0xFF == PrefixAddrSize {
  54  					inst.Prefix[i] |= PrefixImplicit
  55  				}
  56  			}
  57  		}
  58  	}
  59  
  60  	switch inst.Op {
  61  	case AAM, AAD:
  62  		if imm, ok := iargs[0].(Imm); ok {
  63  			if inst.DataSize == 32 {
  64  				iargs[0] = Imm(uint32(int8(imm)))
  65  			} else if inst.DataSize == 16 {
  66  				iargs[0] = Imm(uint16(int8(imm)))
  67  			}
  68  		}
  69  
  70  	case PUSH:
  71  		if imm, ok := iargs[0].(Imm); ok {
  72  			iargs[0] = Imm(uint32(imm))
  73  		}
  74  	}
  75  
  76  	for _, p := range inst.Prefix {
  77  		if p&PrefixImplicit != 0 {
  78  			for j, pj := range inst.Prefix {
  79  				if pj&0xFF == p&0xFF {
  80  					inst.Prefix[j] |= PrefixImplicit
  81  				}
  82  			}
  83  		}
  84  	}
  85  
  86  	if inst.Op != 0 {
  87  		for i, p := range inst.Prefix {
  88  			switch p &^ PrefixIgnored {
  89  			case PrefixData16, PrefixData32, PrefixCS, PrefixDS, PrefixES, PrefixSS:
  90  				inst.Prefix[i] |= PrefixImplicit
  91  			}
  92  			if p.IsREX() {
  93  				inst.Prefix[i] |= PrefixImplicit
  94  			}
  95  			if p.IsVEX() {
  96  				if p == PrefixVEX3Bytes {
  97  					inst.Prefix[i+2] |= PrefixImplicit
  98  				}
  99  				inst.Prefix[i] |= PrefixImplicit
 100  				inst.Prefix[i+1] |= PrefixImplicit
 101  			}
 102  		}
 103  	}
 104  
 105  	if isLoop[inst.Op] || inst.Op == JCXZ || inst.Op == JECXZ || inst.Op == JRCXZ {
 106  		for i, p := range inst.Prefix {
 107  			if p == PrefixPT || p == PrefixPN {
 108  				inst.Prefix[i] |= PrefixImplicit
 109  			}
 110  		}
 111  	}
 112  
 113  	switch inst.Op {
 114  	case AAA, AAS, CBW, CDQE, CLC, CLD, CLI, CLTS, CMC, CPUID, CQO, CWD, DAA, DAS,
 115  		FDECSTP, FINCSTP, FNCLEX, FNINIT, FNOP, FWAIT, HLT,
 116  		ICEBP, INSB, INSD, INSW, INT, INTO, INVD, IRET, IRETQ,
 117  		LAHF, LEAVE, LRET, MONITOR, MWAIT, NOP, OUTSB, OUTSD, OUTSW,
 118  		PAUSE, POPA, POPF, POPFQ, PUSHA, PUSHF, PUSHFQ,
 119  		RDMSR, RDPMC, RDTSC, RDTSCP, RET, RSM,
 120  		SAHF, STC, STD, STI, SYSENTER, SYSEXIT, SYSRET,
 121  		UD2, WBINVD, WRMSR, XEND, XLATB, XTEST:
 122  
 123  		if inst.Op == NOP && inst.Opcode>>24 != 0x90 {
 124  			break
 125  		}
 126  		if inst.Op == RET && inst.Opcode>>24 != 0xC3 {
 127  			break
 128  		}
 129  		if inst.Op == INT && inst.Opcode>>24 != 0xCC {
 130  			break
 131  		}
 132  		if inst.Op == LRET && inst.Opcode>>24 != 0xcb {
 133  			break
 134  		}
 135  		for i, p := range inst.Prefix {
 136  			if p&0xFF == PrefixDataSize {
 137  				inst.Prefix[i] &^= PrefixImplicit | PrefixIgnored
 138  			}
 139  		}
 140  
 141  	case 0:
 142  		// ok
 143  	}
 144  
 145  	switch inst.Op {
 146  	case INSB, INSD, INSW, OUTSB, OUTSD, OUTSW, MONITOR, MWAIT, XLATB:
 147  		iargs = nil
 148  
 149  	case STOSB, STOSW, STOSD, STOSQ:
 150  		iargs = iargs[:1]
 151  
 152  	case LODSB, LODSW, LODSD, LODSQ, SCASB, SCASW, SCASD, SCASQ:
 153  		iargs = iargs[1:]
 154  	}
 155  
 156  	const (
 157  		haveData16 = 1 << iota
 158  		haveData32
 159  		haveAddr16
 160  		haveAddr32
 161  		haveXacquire
 162  		haveXrelease
 163  		haveLock
 164  		haveHintTaken
 165  		haveHintNotTaken
 166  		haveBnd
 167  	)
 168  	var prefixBits uint32
 169  	prefix := ""
 170  	for _, p := range inst.Prefix {
 171  		if p == 0 {
 172  			break
 173  		}
 174  		if p&0xFF == 0xF3 {
 175  			prefixBits &^= haveBnd
 176  		}
 177  		if p&(PrefixImplicit|PrefixIgnored) != 0 {
 178  			continue
 179  		}
 180  		switch p {
 181  		default:
 182  			prefix += strings.ToLower(p.String()) + " "
 183  		case PrefixCS, PrefixDS, PrefixES, PrefixFS, PrefixGS, PrefixSS:
 184  			if inst.Op == 0 {
 185  				prefix += strings.ToLower(p.String()) + " "
 186  			}
 187  		case PrefixREPN:
 188  			prefix += "repne "
 189  		case PrefixLOCK:
 190  			prefixBits |= haveLock
 191  		case PrefixData16, PrefixDataSize:
 192  			prefixBits |= haveData16
 193  		case PrefixData32:
 194  			prefixBits |= haveData32
 195  		case PrefixAddrSize, PrefixAddr16:
 196  			prefixBits |= haveAddr16
 197  		case PrefixAddr32:
 198  			prefixBits |= haveAddr32
 199  		case PrefixXACQUIRE:
 200  			prefixBits |= haveXacquire
 201  		case PrefixXRELEASE:
 202  			prefixBits |= haveXrelease
 203  		case PrefixPT:
 204  			prefixBits |= haveHintTaken
 205  		case PrefixPN:
 206  			prefixBits |= haveHintNotTaken
 207  		case PrefixBND:
 208  			prefixBits |= haveBnd
 209  		}
 210  	}
 211  	switch inst.Op {
 212  	case JMP:
 213  		if inst.Opcode>>24 == 0xEB {
 214  			prefixBits &^= haveBnd
 215  		}
 216  	case RET, LRET:
 217  		prefixBits &^= haveData16 | haveData32
 218  	}
 219  
 220  	if prefixBits&haveXacquire != 0 {
 221  		prefix += "xacquire "
 222  	}
 223  	if prefixBits&haveXrelease != 0 {
 224  		prefix += "xrelease "
 225  	}
 226  	if prefixBits&haveLock != 0 {
 227  		prefix += "lock "
 228  	}
 229  	if prefixBits&haveBnd != 0 {
 230  		prefix += "bnd "
 231  	}
 232  	if prefixBits&haveHintTaken != 0 {
 233  		prefix += "hint-taken "
 234  	}
 235  	if prefixBits&haveHintNotTaken != 0 {
 236  		prefix += "hint-not-taken "
 237  	}
 238  	if prefixBits&haveAddr16 != 0 {
 239  		prefix += "addr16 "
 240  	}
 241  	if prefixBits&haveAddr32 != 0 {
 242  		prefix += "addr32 "
 243  	}
 244  	if prefixBits&haveData16 != 0 {
 245  		prefix += "data16 "
 246  	}
 247  	if prefixBits&haveData32 != 0 {
 248  		prefix += "data32 "
 249  	}
 250  
 251  	if inst.Op == 0 {
 252  		if prefix == "" {
 253  			return "<no instruction>"
 254  		}
 255  		return prefix[:len(prefix)-1]
 256  	}
 257  
 258  	var args []string
 259  	for _, a := range iargs {
 260  		if a == nil {
 261  			break
 262  		}
 263  		args = append(args, intelArg(&inst, pc, symname, a))
 264  	}
 265  
 266  	var op string
 267  	switch inst.Op {
 268  	case NOP:
 269  		if inst.Opcode>>24 == 0x0F {
 270  			if inst.DataSize == 16 {
 271  				args = append(args, "ax")
 272  			} else {
 273  				args = append(args, "eax")
 274  			}
 275  		}
 276  
 277  	case BLENDVPD, BLENDVPS, PBLENDVB:
 278  		args = args[:2]
 279  
 280  	case INT:
 281  		if inst.Opcode>>24 == 0xCC {
 282  			args = nil
 283  			op = "int3"
 284  		}
 285  
 286  	case LCALL, LJMP:
 287  		if len(args) == 2 {
 288  			args[0], args[1] = args[1], args[0]
 289  		}
 290  
 291  	case FCHS, FABS, FTST, FLDPI, FLDL2E, FLDLG2, F2XM1, FXAM, FLD1, FLDL2T, FSQRT, FRNDINT, FCOS, FSIN:
 292  		if len(args) == 0 {
 293  			args = append(args, "st0")
 294  		}
 295  
 296  	case FPTAN, FSINCOS, FUCOMPP, FCOMPP, FYL2X, FPATAN, FXTRACT, FPREM1, FPREM, FYL2XP1, FSCALE:
 297  		if len(args) == 0 {
 298  			args = []string{"st0", "st1"}
 299  		}
 300  
 301  	case FST, FSTP, FISTTP, FIST, FISTP, FBSTP:
 302  		if len(args) == 1 {
 303  			args = append(args, "st0")
 304  		}
 305  
 306  	case FLD, FXCH, FCOM, FCOMP, FIADD, FIMUL, FICOM, FICOMP, FISUBR, FIDIV, FUCOM, FUCOMP, FILD, FBLD, FADD, FMUL, FSUB, FSUBR, FISUB, FDIV, FDIVR, FIDIVR:
 307  		if len(args) == 1 {
 308  			args = []string{"st0", args[0]}
 309  		}
 310  
 311  	case MASKMOVDQU, MASKMOVQ, XLATB, OUTSB, OUTSW, OUTSD:
 312  	FixSegment:
 313  		for i := len(inst.Prefix) - 1; i >= 0; i-- {
 314  			p := inst.Prefix[i] & 0xFF
 315  			switch p {
 316  			case PrefixCS, PrefixES, PrefixFS, PrefixGS, PrefixSS:
 317  				if inst.Mode != 64 || p == PrefixFS || p == PrefixGS {
 318  					args = append(args, strings.ToLower((inst.Prefix[i] & 0xFF).String()))
 319  					break FixSegment
 320  				}
 321  			case PrefixDS:
 322  				if inst.Mode != 64 {
 323  					break FixSegment
 324  				}
 325  			}
 326  		}
 327  	}
 328  
 329  	if op == "" {
 330  		op = intelOp[inst.Op]
 331  	}
 332  	if op == "" {
 333  		op = strings.ToLower(inst.Op.String())
 334  	}
 335  	if args != nil {
 336  		op += " " + strings.Join(args, ", ")
 337  	}
 338  	return prefix + op
 339  }
 340  
 341  func intelArg(inst *Inst, pc uint64, symname SymLookup, arg Arg) string {
 342  	switch a := arg.(type) {
 343  	case Imm:
 344  		if s, base := symname(uint64(a)); s != "" {
 345  			suffix := ""
 346  			if uint64(a) != base {
 347  				suffix = fmt.Sprintf("%+d", uint64(a)-base)
 348  			}
 349  			return fmt.Sprintf("$%s%s", s, suffix)
 350  		}
 351  		if inst.Mode == 32 {
 352  			return fmt.Sprintf("%#x", uint32(a))
 353  		}
 354  		if Imm(int32(a)) == a {
 355  			return fmt.Sprintf("%#x", int64(a))
 356  		}
 357  		return fmt.Sprintf("%#x", uint64(a))
 358  	case Mem:
 359  		if a.Base == EIP {
 360  			a.Base = RIP
 361  		}
 362  		prefix := ""
 363  		switch inst.MemBytes {
 364  		case 1:
 365  			prefix = "byte "
 366  		case 2:
 367  			prefix = "word "
 368  		case 4:
 369  			prefix = "dword "
 370  		case 8:
 371  			prefix = "qword "
 372  		case 16:
 373  			prefix = "xmmword "
 374  		case 32:
 375  			prefix = "ymmword "
 376  		}
 377  		switch inst.Op {
 378  		case INVLPG:
 379  			prefix = "byte "
 380  		case STOSB, MOVSB, CMPSB, LODSB, SCASB:
 381  			prefix = "byte "
 382  		case STOSW, MOVSW, CMPSW, LODSW, SCASW:
 383  			prefix = "word "
 384  		case STOSD, MOVSD, CMPSD, LODSD, SCASD:
 385  			prefix = "dword "
 386  		case STOSQ, MOVSQ, CMPSQ, LODSQ, SCASQ:
 387  			prefix = "qword "
 388  		case LAR:
 389  			prefix = "word "
 390  		case BOUND:
 391  			if inst.Mode == 32 {
 392  				prefix = "qword "
 393  			} else {
 394  				prefix = "dword "
 395  			}
 396  		case PREFETCHW, PREFETCHNTA, PREFETCHT0, PREFETCHT1, PREFETCHT2, CLFLUSH:
 397  			prefix = "zmmword "
 398  		}
 399  		switch inst.Op {
 400  		case MOVSB, MOVSW, MOVSD, MOVSQ, CMPSB, CMPSW, CMPSD, CMPSQ, STOSB, STOSW, STOSD, STOSQ, SCASB, SCASW, SCASD, SCASQ, LODSB, LODSW, LODSD, LODSQ:
 401  			switch a.Base {
 402  			case DI, EDI, RDI:
 403  				if a.Segment == ES {
 404  					a.Segment = 0
 405  				}
 406  			case SI, ESI, RSI:
 407  				if a.Segment == DS {
 408  					a.Segment = 0
 409  				}
 410  			}
 411  		case LEA:
 412  			a.Segment = 0
 413  		default:
 414  			switch a.Base {
 415  			case SP, ESP, RSP, BP, EBP, RBP:
 416  				if a.Segment == SS {
 417  					a.Segment = 0
 418  				}
 419  			default:
 420  				if a.Segment == DS {
 421  					a.Segment = 0
 422  				}
 423  			}
 424  		}
 425  
 426  		if inst.Mode == 64 && a.Segment != FS && a.Segment != GS {
 427  			a.Segment = 0
 428  		}
 429  
 430  		prefix += "ptr "
 431  		if s, disp := memArgToSymbol(a, pc, inst.Len, symname); s != "" {
 432  			suffix := ""
 433  			if disp != 0 {
 434  				suffix = fmt.Sprintf("%+d", disp)
 435  			}
 436  			return prefix + fmt.Sprintf("[%s%s]", s, suffix)
 437  		}
 438  		if a.Segment != 0 {
 439  			prefix += strings.ToLower(a.Segment.String()) + ":"
 440  		}
 441  		prefix += "["
 442  		if a.Base != 0 {
 443  			prefix += intelArg(inst, pc, symname, a.Base)
 444  		}
 445  		if a.Scale != 0 && a.Index != 0 {
 446  			if a.Base != 0 {
 447  				prefix += "+"
 448  			}
 449  			prefix += fmt.Sprintf("%s*%d", intelArg(inst, pc, symname, a.Index), a.Scale)
 450  		}
 451  		if a.Disp != 0 {
 452  			if prefix[len(prefix)-1] == '[' && (a.Disp >= 0 || int64(int32(a.Disp)) != a.Disp) {
 453  				prefix += fmt.Sprintf("%#x", uint64(a.Disp))
 454  			} else {
 455  				prefix += fmt.Sprintf("%+#x", a.Disp)
 456  			}
 457  		}
 458  		prefix += "]"
 459  		return prefix
 460  	case Rel:
 461  		if pc == 0 {
 462  			return fmt.Sprintf(".%+#x", int64(a))
 463  		} else {
 464  			addr := pc + uint64(inst.Len) + uint64(a)
 465  			if s, base := symname(addr); s != "" && addr == base {
 466  				return fmt.Sprintf("%s", s)
 467  			} else {
 468  				addr := pc + uint64(inst.Len) + uint64(a)
 469  				return fmt.Sprintf("%#x", addr)
 470  			}
 471  		}
 472  	case Reg:
 473  		if int(a) < len(intelReg) && intelReg[a] != "" {
 474  			switch inst.Op {
 475  			case VMOVDQA, VMOVDQU, VMOVNTDQA, VMOVNTDQ:
 476  				return strings.Replace(intelReg[a], "xmm", "ymm", -1)
 477  			default:
 478  				return intelReg[a]
 479  			}
 480  		}
 481  	}
 482  	return strings.ToLower(arg.String())
 483  }
 484  
 485  var intelOp = map[Op]string{
 486  	JAE:       "jnb",
 487  	JA:        "jnbe",
 488  	JGE:       "jnl",
 489  	JNE:       "jnz",
 490  	JG:        "jnle",
 491  	JE:        "jz",
 492  	SETAE:     "setnb",
 493  	SETA:      "setnbe",
 494  	SETGE:     "setnl",
 495  	SETNE:     "setnz",
 496  	SETG:      "setnle",
 497  	SETE:      "setz",
 498  	CMOVAE:    "cmovnb",
 499  	CMOVA:     "cmovnbe",
 500  	CMOVGE:    "cmovnl",
 501  	CMOVNE:    "cmovnz",
 502  	CMOVG:     "cmovnle",
 503  	CMOVE:     "cmovz",
 504  	LCALL:     "call far",
 505  	LJMP:      "jmp far",
 506  	LRET:      "ret far",
 507  	ICEBP:     "int1",
 508  	MOVSD_XMM: "movsd",
 509  	XLATB:     "xlat",
 510  }
 511  
 512  var intelReg = [...]string{
 513  	F0:  "st0",
 514  	F1:  "st1",
 515  	F2:  "st2",
 516  	F3:  "st3",
 517  	F4:  "st4",
 518  	F5:  "st5",
 519  	F6:  "st6",
 520  	F7:  "st7",
 521  	M0:  "mmx0",
 522  	M1:  "mmx1",
 523  	M2:  "mmx2",
 524  	M3:  "mmx3",
 525  	M4:  "mmx4",
 526  	M5:  "mmx5",
 527  	M6:  "mmx6",
 528  	M7:  "mmx7",
 529  	X0:  "xmm0",
 530  	X1:  "xmm1",
 531  	X2:  "xmm2",
 532  	X3:  "xmm3",
 533  	X4:  "xmm4",
 534  	X5:  "xmm5",
 535  	X6:  "xmm6",
 536  	X7:  "xmm7",
 537  	X8:  "xmm8",
 538  	X9:  "xmm9",
 539  	X10: "xmm10",
 540  	X11: "xmm11",
 541  	X12: "xmm12",
 542  	X13: "xmm13",
 543  	X14: "xmm14",
 544  	X15: "xmm15",
 545  
 546  	// TODO: Maybe the constants are named wrong.
 547  	SPB: "spl",
 548  	BPB: "bpl",
 549  	SIB: "sil",
 550  	DIB: "dil",
 551  
 552  	R8L:  "r8d",
 553  	R9L:  "r9d",
 554  	R10L: "r10d",
 555  	R11L: "r11d",
 556  	R12L: "r12d",
 557  	R13L: "r13d",
 558  	R14L: "r14d",
 559  	R15L: "r15d",
 560  }
 561