golang.org/x/arch@v0.17.0/x86/x86asm/intel.go (about) 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 (inst.Op == MOV || inst.Op == PUSH) && inst.DataSize == 32 { // See comment in plan9x.go. 345 if s, base := symname(uint64(a)); s != "" { 346 suffix := "" 347 if uint64(a) != base { 348 suffix = fmt.Sprintf("%+d", uint64(a)-base) 349 } 350 return fmt.Sprintf("$%s%s", s, suffix) 351 } 352 } 353 if inst.Mode == 32 { 354 return fmt.Sprintf("%#x", uint32(a)) 355 } 356 if Imm(int32(a)) == a { 357 return fmt.Sprintf("%#x", int64(a)) 358 } 359 return fmt.Sprintf("%#x", uint64(a)) 360 case Mem: 361 if a.Base == EIP { 362 a.Base = RIP 363 } 364 prefix := "" 365 switch inst.MemBytes { 366 case 1: 367 prefix = "byte " 368 case 2: 369 prefix = "word " 370 case 4: 371 prefix = "dword " 372 case 8: 373 prefix = "qword " 374 case 16: 375 prefix = "xmmword " 376 case 32: 377 prefix = "ymmword " 378 } 379 switch inst.Op { 380 case INVLPG: 381 prefix = "byte " 382 case STOSB, MOVSB, CMPSB, LODSB, SCASB: 383 prefix = "byte " 384 case STOSW, MOVSW, CMPSW, LODSW, SCASW: 385 prefix = "word " 386 case STOSD, MOVSD, CMPSD, LODSD, SCASD: 387 prefix = "dword " 388 case STOSQ, MOVSQ, CMPSQ, LODSQ, SCASQ: 389 prefix = "qword " 390 case LAR: 391 prefix = "word " 392 case BOUND: 393 if inst.Mode == 32 { 394 prefix = "qword " 395 } else { 396 prefix = "dword " 397 } 398 case PREFETCHW, PREFETCHNTA, PREFETCHT0, PREFETCHT1, PREFETCHT2, CLFLUSH: 399 prefix = "zmmword " 400 } 401 switch inst.Op { 402 case MOVSB, MOVSW, MOVSD, MOVSQ, CMPSB, CMPSW, CMPSD, CMPSQ, STOSB, STOSW, STOSD, STOSQ, SCASB, SCASW, SCASD, SCASQ, LODSB, LODSW, LODSD, LODSQ: 403 switch a.Base { 404 case DI, EDI, RDI: 405 if a.Segment == ES { 406 a.Segment = 0 407 } 408 case SI, ESI, RSI: 409 if a.Segment == DS { 410 a.Segment = 0 411 } 412 } 413 case LEA: 414 a.Segment = 0 415 default: 416 switch a.Base { 417 case SP, ESP, RSP, BP, EBP, RBP: 418 if a.Segment == SS { 419 a.Segment = 0 420 } 421 default: 422 if a.Segment == DS { 423 a.Segment = 0 424 } 425 } 426 } 427 428 if inst.Mode == 64 && a.Segment != FS && a.Segment != GS { 429 a.Segment = 0 430 } 431 432 prefix += "ptr " 433 if s, disp := memArgToSymbol(a, pc, inst.Len, symname); s != "" { 434 suffix := "" 435 if disp != 0 { 436 suffix = fmt.Sprintf("%+d", disp) 437 } 438 return prefix + fmt.Sprintf("[%s%s]", s, suffix) 439 } 440 if a.Segment != 0 { 441 prefix += strings.ToLower(a.Segment.String()) + ":" 442 } 443 prefix += "[" 444 if a.Base != 0 { 445 prefix += intelArg(inst, pc, symname, a.Base) 446 } 447 if a.Scale != 0 && a.Index != 0 { 448 if a.Base != 0 { 449 prefix += "+" 450 } 451 prefix += fmt.Sprintf("%s*%d", intelArg(inst, pc, symname, a.Index), a.Scale) 452 } 453 if a.Disp != 0 { 454 if prefix[len(prefix)-1] == '[' && (a.Disp >= 0 || int64(int32(a.Disp)) != a.Disp) { 455 prefix += fmt.Sprintf("%#x", uint64(a.Disp)) 456 } else { 457 prefix += fmt.Sprintf("%+#x", a.Disp) 458 } 459 } 460 prefix += "]" 461 return prefix 462 case Rel: 463 if pc == 0 { 464 return fmt.Sprintf(".%+#x", int64(a)) 465 } else { 466 addr := pc + uint64(inst.Len) + uint64(a) 467 if s, base := symname(addr); s != "" && addr == base { 468 return fmt.Sprintf("%s", s) 469 } else { 470 addr := pc + uint64(inst.Len) + uint64(a) 471 return fmt.Sprintf("%#x", addr) 472 } 473 } 474 case Reg: 475 if int(a) < len(intelReg) && intelReg[a] != "" { 476 switch inst.Op { 477 case VMOVDQA, VMOVDQU, VMOVNTDQA, VMOVNTDQ: 478 return strings.Replace(intelReg[a], "xmm", "ymm", -1) 479 default: 480 return intelReg[a] 481 } 482 } 483 } 484 return strings.ToLower(arg.String()) 485 } 486 487 var intelOp = map[Op]string{ 488 JAE: "jnb", 489 JA: "jnbe", 490 JGE: "jnl", 491 JNE: "jnz", 492 JG: "jnle", 493 JE: "jz", 494 SETAE: "setnb", 495 SETA: "setnbe", 496 SETGE: "setnl", 497 SETNE: "setnz", 498 SETG: "setnle", 499 SETE: "setz", 500 CMOVAE: "cmovnb", 501 CMOVA: "cmovnbe", 502 CMOVGE: "cmovnl", 503 CMOVNE: "cmovnz", 504 CMOVG: "cmovnle", 505 CMOVE: "cmovz", 506 LCALL: "call far", 507 LJMP: "jmp far", 508 LRET: "ret far", 509 ICEBP: "int1", 510 MOVSD_XMM: "movsd", 511 XLATB: "xlat", 512 } 513 514 var intelReg = [...]string{ 515 F0: "st0", 516 F1: "st1", 517 F2: "st2", 518 F3: "st3", 519 F4: "st4", 520 F5: "st5", 521 F6: "st6", 522 F7: "st7", 523 M0: "mmx0", 524 M1: "mmx1", 525 M2: "mmx2", 526 M3: "mmx3", 527 M4: "mmx4", 528 M5: "mmx5", 529 M6: "mmx6", 530 M7: "mmx7", 531 X0: "xmm0", 532 X1: "xmm1", 533 X2: "xmm2", 534 X3: "xmm3", 535 X4: "xmm4", 536 X5: "xmm5", 537 X6: "xmm6", 538 X7: "xmm7", 539 X8: "xmm8", 540 X9: "xmm9", 541 X10: "xmm10", 542 X11: "xmm11", 543 X12: "xmm12", 544 X13: "xmm13", 545 X14: "xmm14", 546 X15: "xmm15", 547 548 // TODO: Maybe the constants are named wrong. 549 SPB: "spl", 550 BPB: "bpl", 551 SIB: "sil", 552 DIB: "dil", 553 554 R8L: "r8d", 555 R9L: "r9d", 556 R10L: "r10d", 557 R11L: "r11d", 558 R12L: "r12d", 559 R13L: "r13d", 560 R14L: "r14d", 561 R15L: "r15d", 562 }