plan9port/src/libmach/mach386.c
Dan Cross fa325e9b42 Trivial changes: whitespace and modes.
Remote whitespace at the ends of lines.
Remove blank lines from the ends of files.
Change modes on source files so that they
are not executable.

Signed-off-by: Dan Cross <cross@gajendra.net>
2020-01-10 14:54:30 +00:00

2422 lines
61 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 386 definition
*/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
#include "ureg386.h"
#define REGOFF(x) (ulong)(&((struct Ureg *) 0)->x)
#define REGSIZE sizeof(struct Ureg)
#define FP_CTL(x) (REGSIZE+4*(x))
#define FP_REG(x) (FP_CTL(7)+10*(x))
#define FPREGSIZE (6*4+8*10)
/*
* i386-specific debugger interface
*/
char *i386excep(Map*, Regs*);
/*
static int i386trace(Map*, ulong, ulong, ulong, Tracer);
static ulong i386frame(Map*, ulong, ulong, ulong, ulong);
*/
int i386foll(Map*, Regs*, u64int, u64int*);
int i386hexinst(Map*, u64int, char*, int);
int i386das(Map*, u64int, char, char*, int);
int i386instlen(Map*, u64int);
int i386unwind(Map*, Regs*, u64int*, Symbol*);
static char *i386windregs[] = {
"PC",
"SP",
"BP",
"AX",
"CX",
"DX",
"BX",
"SI",
"DI",
0,
};
static Regdesc i386reglist[] = {
{"DI", REGOFF(di), RINT, 'X'},
{"SI", REGOFF(si), RINT, 'X'},
{"BP", REGOFF(bp), RINT, 'X'},
{"BX", REGOFF(bx), RINT, 'X'},
{"DX", REGOFF(dx), RINT, 'X'},
{"CX", REGOFF(cx), RINT, 'X'},
{"AX", REGOFF(ax), RINT, 'X'},
{"GS", REGOFF(gs), RINT, 'X'},
{"FS", REGOFF(fs), RINT, 'X'},
{"ES", REGOFF(es), RINT, 'X'},
{"DS", REGOFF(ds), RINT, 'X'},
{"TRAP", REGOFF(trap), RINT, 'X'},
{"ECODE", REGOFF(ecode), RINT, 'X'},
{"PC", REGOFF(pc), RINT, 'X'},
{"CS", REGOFF(cs), RINT, 'X'},
{"EFLAGS", REGOFF(flags), RINT, 'X'},
{"SP", REGOFF(sp), RINT, 'X'},
{"SS", REGOFF(ss), RINT, 'X'},
{"E0", FP_CTL(0), RFLT, 'X'},
{"E1", FP_CTL(1), RFLT, 'X'},
{"E2", FP_CTL(2), RFLT, 'X'},
{"E3", FP_CTL(3), RFLT, 'X'},
{"E4", FP_CTL(4), RFLT, 'X'},
{"E5", FP_CTL(5), RFLT, 'X'},
{"E6", FP_CTL(6), RFLT, 'X'},
{"F0", FP_REG(7), RFLT, '3'},
{"F1", FP_REG(6), RFLT, '3'},
{"F2", FP_REG(5), RFLT, '3'},
{"F3", FP_REG(4), RFLT, '3'},
{"F4", FP_REG(3), RFLT, '3'},
{"F5", FP_REG(2), RFLT, '3'},
{"F6", FP_REG(1), RFLT, '3'},
{"F7", FP_REG(0), RFLT, '3'},
{ 0 }
};
Mach mach386 =
{
"386",
M386, /* machine type */
i386reglist, /* register list */
REGSIZE, /* size of registers in bytes */
FPREGSIZE, /* size of fp registers in bytes */
"PC", /* name of PC */
"SP", /* name of SP */
"BP", /* name of FP */
0, /* link register */
"setSB", /* static base register name (bogus anyways) */
0, /* static base register value */
0x1000, /* page size */
0x80100000, /* kernel base */
0, /* kernel text mask */
1, /* quantization of pc */
4, /* szaddr */
4, /* szreg */
4, /* szfloat */
8, /* szdouble */
i386windregs, /* locations unwound in stack trace */
9,
{0xCC, 0, 0, 0}, /* break point: INT 3 */
1, /* break point size */
i386foll, /* following addresses */
i386excep, /* print exception */
i386unwind, /* stack unwind */
leswap2, /* convert short to local byte order */
leswap4, /* convert long to local byte order */
leswap8, /* convert vlong to local byte order */
leieeeftoa32, /* single precision float pointer */
leieeeftoa64, /* double precision float pointer */
leieeeftoa80, /* long double precision floating point */
i386das, /* dissembler */
i386das, /* plan9-format disassembler */
0, /* commercial disassembler */
i386hexinst, /* print instruction */
i386instlen, /* instruction size calculation */
};
/*
* The wrapper code around Linux system calls
* saves AX on the stack before calling some calls
* (at least, __libc_nanosleep), when running in
* threaded programs.
*/
static void
syscallhack(Map *map, Regs *regs, int *spoff)
{
u64int pc;
char buf[60];
rget(regs, "PC", &pc);
if(i386das(map, pc-2, 0, buf, sizeof buf) != 2 || strncmp(buf, "INTB\t$", 6) != 0)
return;
if(i386das(map, pc, 0, buf, sizeof buf) != 2 || strcmp(buf, "MOVL\tDX,BX") != 0)
return;
if(i386das(map, pc+2, 0, buf, sizeof buf) != 3 || strcmp(buf, "XCHGL\tAX,0(SP)") != 0)
return;
*spoff += 4;
}
int
i386unwind(Map *map, Regs *regs, u64int *next, Symbol *sym)
{
int i, isp, ipc, ibp, havebp, n, spoff, off[9];
ulong pc;
u32int v;
char buf[60], *p;
/*print("i386unwind %s\n", sym ? sym->name : nil); */
isp = windindex("SP");
ipc = windindex("PC");
ibp = windindex("BP");
if(isp < 0 || ipc < 0 || ibp < 0){
werrstr("i386unwind: cannot happen");
return -1;
}
/*
* Disassemble entry to figure out
* where values have been saved.
* Perhaps should disassemble exit path
* instead -- a random walk on the code
* should suffice to get us to a RET.
*/
if(sym){
pc = sym->loc.addr;
/*print("startpc %lux\n", pc); */
memset(off, 0xff, sizeof off);
spoff = 0;
havebp = 0;
for(;;){
if((n = i386das(map, pc, 0, buf, sizeof buf)) < 0)
break;
/*print("%s\n", buf); */
pc += n;
if(strncmp(buf, "PUSHL\t", 6) == 0){
spoff += 4;
if((i = windindex(buf+6)) >= 0)
off[i] = spoff;
}else if(strcmp(buf, "MOVL\tSP,BP") == 0 && spoff == 4 && off[ibp] == 4){
havebp = 1;
}else if(strncmp(buf, "SUBL\t$", 6) == 0){
if((p = strrchr(buf, ',')) && strcmp(p, ",SP") == 0){
/*print("spoff %s\n", buf+6); */
spoff += strtol(buf+6, 0, 16);
}
break;
}else if(strncmp(buf, "XORL\t", 5) == 0 || strncmp(buf, "MOVL\t", 5) == 0){
/*
* Hope these are rescheduled non-prologue instructions
* like XORL AX, AX or MOVL $0x3, AX and thus ignorable.
*/
}else
break;
}
syscallhack(map, regs, &spoff);
if(havebp){
/*print("havebp\n"); */
rget(regs, "BP", &next[isp]);
get4(map, next[isp], &v);
next[ibp] = v;
next[isp] += 4;
}else{
rget(regs, "SP", &next[isp]);
/*print("old sp %lux + %d\n", next[isp], spoff); */
next[isp] += spoff;
}
for(i=0; i<nelem(off); i++)
if(off[i] != -1){
get4(map, next[isp]-off[i], &v);
next[i] = v;
}
if(get4(map, next[isp], &v) < 0)
return -1;
/*print("new pc %lux => %lux\n", next[isp], v); */
next[ipc] = v;
next[isp] += 4;
return 0;
}
/*
* Rely on bp chaining
*/
if(rget(regs, "BP", &next[isp]) < 0
|| get4(map, next[isp], &v) < 0)
return -1;
next[ibp] = v;
next[isp] += 4;
if(get4(map, next[isp], &v) < 0)
return -1;
next[ipc] = v;
next[isp] += 4;
return 0;
}
/*static char STARTSYM[] = "_main"; */
/*static char PROFSYM[] = "_mainp"; */
static char FRAMENAME[] = ".frame";
static char *excname[] =
{
"divide error", /* 0 */
"debug exception", /* 1 */
0,0, /* 2, 3 */
"overflow", /* 4 */
"bounds check", /* 5 */
"invalid opcode", /* 6 */
"math coprocessor emulation", /* 7 */
"double fault", /* 8 */
"math coprocessor overrun", /* 9 */
"invalid TSS", /* 10 */
"segment not present", /* 11 */
"stack exception", /* 12 */
"general protection violation", /* 13 */
"page fault", /* 14 */
0, /* 15 */
"math coprocessor error", /* 16 */
0,0,0,0,0,0,0, /* 17-23 */
"clock", /* 24 */
"keyboard", /* 25 */
0, /* 26 */
"modem status", /* 27 */
"serial line status", /* 28 */
0, /* 29 */
"floppy disk", /* 30 */
0,0,0,0,0, /* 31-35 */
"mouse", /* 36 */
"math coprocessor", /* 37 */
"hard disk", /* 38 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,/* 39-54 */
0,0,0,0,0,0,0,0,0, /* 55-63 */
"system call", /* 64 */
};
char*
i386excep(Map *map, Regs *regs)
{
u64int c;
u64int pc;
static char buf[16];
if(rget(regs, "TRAP", &c) < 0)
return "no trap register";
if(c > 64 || excname[c] == 0) {
if (c == 3) {
if (rget(regs, "PC", &pc) >= 0)
if (get1(map, pc, (uchar*)buf, mach->bpsize) > 0)
if (memcmp(buf, mach->bpinst, mach->bpsize) == 0)
return "breakpoint";
}
sprint(buf, "exception %ld", c);
return buf;
} else
return excname[c];
}
/* I386/486 - Disassembler and related functions */
/*
* an instruction
*/
typedef struct Instr Instr;
struct Instr
{
uchar mem[1+1+1+1+2+1+1+4+4]; /* raw instruction */
uvlong addr; /* address of start of instruction */
int n; /* number of bytes in instruction */
char *prefix; /* instr prefix */
char *segment; /* segment override */
uchar jumptype; /* set to the operand type for jump/ret/call */
uchar amd64;
uchar rex; /* REX prefix (or zero) */
char osize; /* 'W' or 'L' (or 'Q' on amd64) */
char asize; /* address size 'W' or 'L' (or 'Q' or amd64) */
uchar mod; /* bits 6-7 of mod r/m field */
uchar reg; /* bits 3-5 of mod r/m field */
char ss; /* bits 6-7 of SIB */
schar index; /* bits 3-5 of SIB */
schar base; /* bits 0-2 of SIB */
char rip; /* RIP-relative in amd64 mode */
uchar opre; /* f2/f3 could introduce media */
short seg; /* segment of far address */
uint32 disp; /* displacement */
uint32 imm; /* immediate */
uint32 imm2; /* second immediate operand */
uvlong imm64; /* big immediate */
char *curr; /* fill level in output buffer */
char *end; /* end of output buffer */
char *err; /* error message */
};
/* 386 register (ha!) set */
enum{
AX=0,
CX,
DX,
BX,
SP,
BP,
SI,
DI,
/* amd64 */
/* be careful: some unix system headers #define R8, R9, etc */
AMD64_R8,
AMD64_R9,
AMD64_R10,
AMD64_R11,
AMD64_R12,
AMD64_R13,
AMD64_R14,
AMD64_R15
};
/* amd64 rex extension byte */
enum{
REXW = 1<<3, /* =1, 64-bit operand size */
REXR = 1<<2, /* extend modrm reg */
REXX = 1<<1, /* extend sib index */
REXB = 1<<0 /* extend modrm r/m, sib base, or opcode reg */
};
/* Operand Format codes */
/*
%A - address size register modifier (!asize -> 'E')
%C - Control register CR0/CR1/CR2
%D - Debug register DR0/DR1/DR2/DR3/DR6/DR7
%I - second immediate operand
%O - Operand size register modifier (!osize -> 'E')
%T - Test register TR6/TR7
%S - size code ('W' or 'L')
%W - Weird opcode: OSIZE == 'W' => "CBW"; else => "CWDE"
%d - displacement 16-32 bits
%e - effective address - Mod R/M value
%f - floating point register F0-F7 - from Mod R/M register
%g - segment register
%i - immediate operand 8-32 bits
%p - PC-relative - signed displacement in immediate field
%r - Reg from Mod R/M
%w - Weird opcode: OSIZE == 'W' => "CWD"; else => "CDQ"
*/
typedef struct Optable Optable;
struct Optable
{
char operand[2];
void *proto; /* actually either (char*) or (Optable*) */
};
/* Operand decoding codes */
enum {
Ib = 1, /* 8-bit immediate - (no sign extension)*/
Ibs, /* 8-bit immediate (sign extended) */
Jbs, /* 8-bit sign-extended immediate in jump or call */
Iw, /* 16-bit immediate -> imm */
Iw2, /* 16-bit immediate -> imm2 */
Iwd, /* Operand-sized immediate (no sign extension)*/
Iwdq, /* Operand-sized immediate, possibly 64 bits */
Awd, /* Address offset */
Iwds, /* Operand-sized immediate (sign extended) */
RM, /* Word or int32 R/M field with register (/r) */
RMB, /* Byte R/M field with register (/r) */
RMOP, /* Word or int32 R/M field with op code (/digit) */
RMOPB, /* Byte R/M field with op code (/digit) */
RMR, /* R/M register only (mod = 11) */
RMM, /* R/M memory only (mod = 0/1/2) */
Op_R0, /* Base reg of Mod R/M is literal 0x00 */
Op_R1, /* Base reg of Mod R/M is literal 0x01 */
FRMOP, /* Floating point R/M field with opcode */
FRMEX, /* Extended floating point R/M field with opcode */
JUMP, /* Jump or Call flag - no operand */
RET, /* Return flag - no operand */
OA, /* literal 0x0a byte */
PTR, /* Seg:Displacement addr (ptr16:16 or ptr16:32) */
AUX, /* Multi-byte op code - Auxiliary table */
AUXMM, /* multi-byte op code - auxiliary table chosen by prefix */
PRE, /* Instr Prefix */
OPRE, /* Instr Prefix or media op extension */
SEG, /* Segment Prefix */
OPOVER, /* Operand size override */
ADDOVER, /* Address size override */
};
static Optable optab0F00[8]=
{
[0x00] = { 0,0, "MOVW LDT,%e" },
[0x01] = { 0,0, "MOVW TR,%e" },
[0x02] = { 0,0, "MOVW %e,LDT" },
[0x03] = { 0,0, "MOVW %e,TR" },
[0x04] = { 0,0, "VERR %e" },
[0x05] = { 0,0, "VERW %e" },
};
static Optable optab0F01[8]=
{
[0x00] = { 0,0, "MOVL GDTR,%e" },
[0x01] = { 0,0, "MOVL IDTR,%e" },
[0x02] = { 0,0, "MOVL %e,GDTR" },
[0x03] = { 0,0, "MOVL %e,IDTR" },
[0x04] = { 0,0, "MOVW MSW,%e" }, /* word */
[0x06] = { 0,0, "MOVW %e,MSW" }, /* word */
[0x07] = { 0,0, "INVLPG %e" }, /* or SWAPGS */
};
static Optable optab0F01F8[1]=
{
[0x00] = { 0,0, "SWAPGS" },
};
/* 0F71 */
/* 0F72 */
/* 0F73 */
static Optable optab0FAE[8]=
{
[0x00] = { 0,0, "FXSAVE %e" },
[0x01] = { 0,0, "FXRSTOR %e" },
[0x02] = { 0,0, "LDMXCSR %e" },
[0x03] = { 0,0, "STMXCSR %e" },
[0x05] = { 0,0, "LFENCE" },
[0x06] = { 0,0, "MFENCE" },
[0x07] = { 0,0, "SFENCE" },
};
/* 0F18 */
/* 0F0D */
static Optable optab0FBA[8]=
{
[0x04] = { Ib,0, "BT%S %i,%e" },
[0x05] = { Ib,0, "BTS%S %i,%e" },
[0x06] = { Ib,0, "BTR%S %i,%e" },
[0x07] = { Ib,0, "BTC%S %i,%e" },
};
static Optable optab0F0F[256]=
{
[0x0c] = { 0,0, "PI2FW %m,%M" },
[0x0d] = { 0,0, "PI2L %m,%M" },
[0x1c] = { 0,0, "PF2IW %m,%M" },
[0x1d] = { 0,0, "PF2IL %m,%M" },
[0x8a] = { 0,0, "PFNACC %m,%M" },
[0x8e] = { 0,0, "PFPNACC %m,%M" },
[0x90] = { 0,0, "PFCMPGE %m,%M" },
[0x94] = { 0,0, "PFMIN %m,%M" },
[0x96] = { 0,0, "PFRCP %m,%M" },
[0x97] = { 0,0, "PFRSQRT %m,%M" },
[0x9a] = { 0,0, "PFSUB %m,%M" },
[0x9e] = { 0,0, "PFADD %m,%M" },
[0xa0] = { 0,0, "PFCMPGT %m,%M" },
[0xa4] = { 0,0, "PFMAX %m,%M" },
[0xa6] = { 0,0, "PFRCPIT1 %m,%M" },
[0xa7] = { 0,0, "PFRSQIT1 %m,%M" },
[0xaa] = { 0,0, "PFSUBR %m,%M" },
[0xae] = { 0,0, "PFACC %m,%M" },
[0xb0] = { 0,0, "PFCMPEQ %m,%M" },
[0xb4] = { 0,0, "PFMUL %m,%M" },
[0xb6] = { 0,0, "PFRCPI2T %m,%M" },
[0xb7] = { 0,0, "PMULHRW %m,%M" },
[0xbb] = { 0,0, "PSWAPL %m,%M" },
};
static Optable optab0FC7[8]=
{
[0x01] = { 0,0, "CMPXCHG8B %e" },
};
static Optable optab660F71[8]=
{
[0x02] = { Ib,0, "PSRLW %i,%X" },
[0x04] = { Ib,0, "PSRAW %i,%X" },
[0x06] = { Ib,0, "PSLLW %i,%X" },
};
static Optable optab660F72[8]=
{
[0x02] = { Ib,0, "PSRLL %i,%X" },
[0x04] = { Ib,0, "PSRAL %i,%X" },
[0x06] = { Ib,0, "PSLLL %i,%X" },
};
static Optable optab660F73[8]=
{
[0x02] = { Ib,0, "PSRLQ %i,%X" },
[0x03] = { Ib,0, "PSRLO %i,%X" },
[0x06] = { Ib,0, "PSLLQ %i,%X" },
[0x07] = { Ib,0, "PSLLO %i,%X" },
};
static Optable optab660F[256]=
{
[0x2B] = { RM,0, "MOVNTPD %x,%e" },
[0x2E] = { RM,0, "UCOMISD %x,%X" },
[0x2F] = { RM,0, "COMISD %x,%X" },
[0x5A] = { RM,0, "CVTPD2PS %x,%X" },
[0x5B] = { RM,0, "CVTPS2PL %x,%X" },
[0x6A] = { RM,0, "PUNPCKHLQ %x,%X" },
[0x6B] = { RM,0, "PACKSSLW %x,%X" },
[0x6C] = { RM,0, "PUNPCKLQDQ %x,%X" },
[0x6D] = { RM,0, "PUNPCKHQDQ %x,%X" },
[0x6E] = { RM,0, "MOV%S %e,%X" },
[0x6F] = { RM,0, "MOVO %x,%X" }, /* MOVDQA */
[0x70] = { RM,Ib, "PSHUFL %i,%x,%X" },
[0x71] = { RMOP,0, optab660F71 },
[0x72] = { RMOP,0, optab660F72 },
[0x73] = { RMOP,0, optab660F73 },
[0x7E] = { RM,0, "MOV%S %X,%e" },
[0x7F] = { RM,0, "MOVO %X,%x" },
[0xC4] = { RM,Ib, "PINSRW %i,%e,%X" },
[0xC5] = { RMR,Ib, "PEXTRW %i,%X,%e" },
[0xD4] = { RM,0, "PADDQ %x,%X" },
[0xD5] = { RM,0, "PMULLW %x,%X" },
[0xD6] = { RM,0, "MOVQ %X,%x" },
[0xE6] = { RM,0, "CVTTPD2PL %x,%X" },
[0xE7] = { RM,0, "MOVNTO %X,%e" },
[0xF7] = { RM,0, "MASKMOVOU %x,%X" },
};
static Optable optabF20F[256]=
{
[0x10] = { RM,0, "MOVSD %x,%X" },
[0x11] = { RM,0, "MOVSD %X,%x" },
[0x2A] = { RM,0, "CVTS%S2SD %e,%X" },
[0x2C] = { RM,0, "CVTTSD2S%S %x,%r" },
[0x2D] = { RM,0, "CVTSD2S%S %x,%r" },
[0x5A] = { RM,0, "CVTSD2SS %x,%X" },
[0x6F] = { RM,0, "MOVOU %x,%X" },
[0x70] = { RM,Ib, "PSHUFLW %i,%x,%X" },
[0x7F] = { RM,0, "MOVOU %X,%x" },
[0xD6] = { RM,0, "MOVQOZX %M,%X" },
[0xE6] = { RM,0, "CVTPD2PL %x,%X" },
};
static Optable optabF30F[256]=
{
[0x10] = { RM,0, "MOVSS %x,%X" },
[0x11] = { RM,0, "MOVSS %X,%x" },
[0x2A] = { RM,0, "CVTS%S2SS %e,%X" },
[0x2C] = { RM,0, "CVTTSS2S%S %x,%r" },
[0x2D] = { RM,0, "CVTSS2S%S %x,%r" },
[0x5A] = { RM,0, "CVTSS2SD %x,%X" },
[0x5B] = { RM,0, "CVTTPS2PL %x,%X" },
[0x6F] = { RM,0, "MOVOU %x,%X" },
[0x70] = { RM,Ib, "PSHUFHW %i,%x,%X" },
[0x7E] = { RM,0, "MOVQOZX %x,%X" },
[0x7F] = { RM,0, "MOVOU %X,%x" },
[0xD6] = { RM,0, "MOVQOZX %m*,%X" },
[0xE6] = { RM,0, "CVTPL2PD %x,%X" },
};
static Optable optab0F[256]=
{
[0x00] = { RMOP,0, optab0F00 },
[0x01] = { RMOP,0, optab0F01 },
[0x02] = { RM,0, "LAR %e,%r" },
[0x03] = { RM,0, "LSL %e,%r" },
[0x05] = { 0,0, "SYSCALL" },
[0x06] = { 0,0, "CLTS" },
[0x07] = { 0,0, "SYSRET" },
[0x08] = { 0,0, "INVD" },
[0x09] = { 0,0, "WBINVD" },
[0x0B] = { 0,0, "UD2" },
[0x0F] = { RM,AUX, optab0F0F }, /* 3DNow! */
[0x10] = { RM,0, "MOVU%s %x,%X" },
[0x11] = { RM,0, "MOVU%s %X,%x" },
[0x12] = { RM,0, "MOV[H]L%s %x,%X" }, /* TO DO: H if source is XMM */
[0x13] = { RM,0, "MOVL%s %X,%e" },
[0x14] = { RM,0, "UNPCKL%s %x,%X" },
[0x15] = { RM,0, "UNPCKH%s %x,%X" },
[0x16] = { RM,0, "MOV[L]H%s %x,%X" }, /* TO DO: L if source is XMM */
[0x17] = { RM,0, "MOVH%s %X,%x" },
[0x20] = { RMR,0, "MOVL %C,%e" },
[0x21] = { RMR,0, "MOVL %D,%e" },
[0x22] = { RMR,0, "MOVL %e,%C" },
[0x23] = { RMR,0, "MOVL %e,%D" },
[0x24] = { RMR,0, "MOVL %T,%e" },
[0x26] = { RMR,0, "MOVL %e,%T" },
[0x28] = { RM,0, "MOVA%s %x,%X" },
[0x29] = { RM,0, "MOVA%s %X,%x" },
[0x2A] = { RM,0, "CVTPL2%s %m*,%X" },
[0x2B] = { RM,0, "MOVNT%s %X,%e" },
[0x2C] = { RM,0, "CVTT%s2PL %x,%M" },
[0x2D] = { RM,0, "CVT%s2PL %x,%M" },
[0x2E] = { RM,0, "UCOMISS %x,%X" },
[0x2F] = { RM,0, "COMISS %x,%X" },
[0x30] = { 0,0, "WRMSR" },
[0x31] = { 0,0, "RDTSC" },
[0x32] = { 0,0, "RDMSR" },
[0x33] = { 0,0, "RDPMC" },
[0x42] = { RM,0, "CMOVC %e,%r" }, /* CF */
[0x43] = { RM,0, "CMOVNC %e,%r" }, /* ¬ CF */
[0x44] = { RM,0, "CMOVZ %e,%r" }, /* ZF */
[0x45] = { RM,0, "CMOVNZ %e,%r" }, /* ¬ ZF */
[0x46] = { RM,0, "CMOVBE %e,%r" }, /* CF ZF */
[0x47] = { RM,0, "CMOVA %e,%r" }, /* ¬CF ∧ ¬ZF */
[0x48] = { RM,0, "CMOVS %e,%r" }, /* SF */
[0x49] = { RM,0, "CMOVNS %e,%r" }, /* ¬ SF */
[0x4A] = { RM,0, "CMOVP %e,%r" }, /* PF */
[0x4B] = { RM,0, "CMOVNP %e,%r" }, /* ¬ PF */
[0x4C] = { RM,0, "CMOVLT %e,%r" }, /* LT ≡ OF ≠ SF */
[0x4D] = { RM,0, "CMOVGE %e,%r" }, /* GE ≡ ZF SF */
[0x4E] = { RM,0, "CMOVLE %e,%r" }, /* LE ≡ ZF LT */
[0x4F] = { RM,0, "CMOVGT %e,%r" }, /* GT ≡ ¬ZF ∧ GE */
[0x50] = { RM,0, "MOVMSK%s %X,%r" }, /* TO DO: check */
[0x51] = { RM,0, "SQRT%s %x,%X" },
[0x52] = { RM,0, "RSQRT%s %x,%X" },
[0x53] = { RM,0, "RCP%s %x,%X" },
[0x54] = { RM,0, "AND%s %x,%X" },
[0x55] = { RM,0, "ANDN%s %x,%X" },
[0x56] = { RM,0, "OR%s %x,%X" }, /* TO DO: S/D */
[0x57] = { RM,0, "XOR%s %x,%X" }, /* S/D */
[0x58] = { RM,0, "ADD%s %x,%X" }, /* S/P S/D */
[0x59] = { RM,0, "MUL%s %x,%X" },
[0x5A] = { RM,0, "CVTPS2PD %x,%X" },
[0x5B] = { RM,0, "CVTPL2PS %x,%X" },
[0x5C] = { RM,0, "SUB%s %x,%X" },
[0x5D] = { RM,0, "MIN%s %x,%X" },
[0x5E] = { RM,0, "DIV%s %x,%X" }, /* TO DO: S/P S/D */
[0x5F] = { RM,0, "MAX%s %x,%X" },
[0x60] = { RM,0, "PUNPCKLBW %m,%M" },
[0x61] = { RM,0, "PUNPCKLWL %m,%M" },
[0x62] = { RM,0, "PUNPCKLLQ %m,%M" },
[0x63] = { RM,0, "PACKSSWB %m,%M" },
[0x64] = { RM,0, "PCMPGTB %m,%M" },
[0x65] = { RM,0, "PCMPGTW %m,%M" },
[0x66] = { RM,0, "PCMPGTL %m,%M" },
[0x67] = { RM,0, "PACKUSWB %m,%M" },
[0x68] = { RM,0, "PUNPCKHBW %m,%M" },
[0x69] = { RM,0, "PUNPCKHWL %m,%M" },
[0x6A] = { RM,0, "PUNPCKHLQ %m,%M" },
[0x6B] = { RM,0, "PACKSSLW %m,%M" },
[0x6E] = { RM,0, "MOV%S %e,%M" },
[0x6F] = { RM,0, "MOVQ %m,%M" },
[0x70] = { RM,Ib, "PSHUFW %i,%m,%M" },
[0x74] = { RM,0, "PCMPEQB %m,%M" },
[0x75] = { RM,0, "PCMPEQW %m,%M" },
[0x76] = { RM,0, "PCMPEQL %m,%M" },
[0x77] = { 0,0, "EMMS" },
[0x7E] = { RM,0, "MOV%S %M,%e" },
[0x7F] = { RM,0, "MOVQ %M,%m" },
[0xAE] = { RMOP,0, optab0FAE },
[0xAA] = { 0,0, "RSM" },
[0xB0] = { RM,0, "CMPXCHGB %r,%e" },
[0xB1] = { RM,0, "CMPXCHG%S %r,%e" },
[0xC0] = { RMB,0, "XADDB %r,%e" },
[0xC1] = { RM,0, "XADD%S %r,%e" },
[0xC2] = { RM,Ib, "CMP%s %x,%X,%#i" },
[0xC3] = { RM,0, "MOVNTI%S %r,%e" },
[0xC6] = { RM,Ib, "SHUF%s %i,%x,%X" },
[0xC8] = { 0,0, "BSWAP AX" },
[0xC9] = { 0,0, "BSWAP CX" },
[0xCA] = { 0,0, "BSWAP DX" },
[0xCB] = { 0,0, "BSWAP BX" },
[0xCC] = { 0,0, "BSWAP SP" },
[0xCD] = { 0,0, "BSWAP BP" },
[0xCE] = { 0,0, "BSWAP SI" },
[0xCF] = { 0,0, "BSWAP DI" },
[0xD1] = { RM,0, "PSRLW %m,%M" },
[0xD2] = { RM,0, "PSRLL %m,%M" },
[0xD3] = { RM,0, "PSRLQ %m,%M" },
[0xD5] = { RM,0, "PMULLW %m,%M" },
[0xD6] = { RM,0, "MOVQOZX %m*,%X" },
[0xD7] = { RM,0, "PMOVMSKB %m,%r" },
[0xD8] = { RM,0, "PSUBUSB %m,%M" },
[0xD9] = { RM,0, "PSUBUSW %m,%M" },
[0xDA] = { RM,0, "PMINUB %m,%M" },
[0xDB] = { RM,0, "PAND %m,%M" },
[0xDC] = { RM,0, "PADDUSB %m,%M" },
[0xDD] = { RM,0, "PADDUSW %m,%M" },
[0xDE] = { RM,0, "PMAXUB %m,%M" },
[0xDF] = { RM,0, "PANDN %m,%M" },
[0xE0] = { RM,0, "PAVGB %m,%M" },
[0xE1] = { RM,0, "PSRAW %m,%M" },
[0xE2] = { RM,0, "PSRAL %m,%M" },
[0xE3] = { RM,0, "PAVGW %m,%M" },
[0xE4] = { RM,0, "PMULHUW %m,%M" },
[0xE5] = { RM,0, "PMULHW %m,%M" },
[0xE7] = { RM,0, "MOVNTQ %M,%e" },
[0xE8] = { RM,0, "PSUBSB %m,%M" },
[0xE9] = { RM,0, "PSUBSW %m,%M" },
[0xEA] = { RM,0, "PMINSW %m,%M" },
[0xEB] = { RM,0, "POR %m,%M" },
[0xEC] = { RM,0, "PADDSB %m,%M" },
[0xED] = { RM,0, "PADDSW %m,%M" },
[0xEE] = { RM,0, "PMAXSW %m,%M" },
[0xEF] = { RM,0, "PXOR %m,%M" },
[0xF1] = { RM,0, "PSLLW %m,%M" },
[0xF2] = { RM,0, "PSLLL %m,%M" },
[0xF3] = { RM,0, "PSLLQ %m,%M" },
[0xF4] = { RM,0, "PMULULQ %m,%M" },
[0xF5] = { RM,0, "PMADDWL %m,%M" },
[0xF6] = { RM,0, "PSADBW %m,%M" },
[0xF7] = { RMR,0, "MASKMOVQ %m,%M" },
[0xF8] = { RM,0, "PSUBB %m,%M" },
[0xF9] = { RM,0, "PSUBW %m,%M" },
[0xFA] = { RM,0, "PSUBL %m,%M" },
[0xFC] = { RM,0, "PADDB %m,%M" },
[0xFD] = { RM,0, "PADDW %m,%M" },
[0xFE] = { RM,0, "PADDL %m,%M" },
[0x80] = { Iwds,0, "JOS %p" },
[0x81] = { Iwds,0, "JOC %p" },
[0x82] = { Iwds,0, "JCS %p" },
[0x83] = { Iwds,0, "JCC %p" },
[0x84] = { Iwds,0, "JEQ %p" },
[0x85] = { Iwds,0, "JNE %p" },
[0x86] = { Iwds,0, "JLS %p" },
[0x87] = { Iwds,0, "JHI %p" },
[0x88] = { Iwds,0, "JMI %p" },
[0x89] = { Iwds,0, "JPL %p" },
[0x8a] = { Iwds,0, "JPS %p" },
[0x8b] = { Iwds,0, "JPC %p" },
[0x8c] = { Iwds,0, "JLT %p" },
[0x8d] = { Iwds,0, "JGE %p" },
[0x8e] = { Iwds,0, "JLE %p" },
[0x8f] = { Iwds,0, "JGT %p" },
[0x90] = { RMB,0, "SETOS %e" },
[0x91] = { RMB,0, "SETOC %e" },
[0x92] = { RMB,0, "SETCS %e" },
[0x93] = { RMB,0, "SETCC %e" },
[0x94] = { RMB,0, "SETEQ %e" },
[0x95] = { RMB,0, "SETNE %e" },
[0x96] = { RMB,0, "SETLS %e" },
[0x97] = { RMB,0, "SETHI %e" },
[0x98] = { RMB,0, "SETMI %e" },
[0x99] = { RMB,0, "SETPL %e" },
[0x9a] = { RMB,0, "SETPS %e" },
[0x9b] = { RMB,0, "SETPC %e" },
[0x9c] = { RMB,0, "SETLT %e" },
[0x9d] = { RMB,0, "SETGE %e" },
[0x9e] = { RMB,0, "SETLE %e" },
[0x9f] = { RMB,0, "SETGT %e" },
[0xa0] = { 0,0, "PUSHL FS" },
[0xa1] = { 0,0, "POPL FS" },
[0xa2] = { 0,0, "CPUID" },
[0xa3] = { RM,0, "BT%S %r,%e" },
[0xa4] = { RM,Ib, "SHLD%S %r,%i,%e" },
[0xa5] = { RM,0, "SHLD%S %r,CL,%e" },
[0xa8] = { 0,0, "PUSHL GS" },
[0xa9] = { 0,0, "POPL GS" },
[0xab] = { RM,0, "BTS%S %r,%e" },
[0xac] = { RM,Ib, "SHRD%S %r,%i,%e" },
[0xad] = { RM,0, "SHRD%S %r,CL,%e" },
[0xaf] = { RM,0, "IMUL%S %e,%r" },
[0xb2] = { RMM,0, "LSS %e,%r" },
[0xb3] = { RM,0, "BTR%S %r,%e" },
[0xb4] = { RMM,0, "LFS %e,%r" },
[0xb5] = { RMM,0, "LGS %e,%r" },
[0xb6] = { RMB,0, "MOVBZX %e,%R" },
[0xb7] = { RM,0, "MOVWZX %e,%R" },
[0xba] = { RMOP,0, optab0FBA },
[0xbb] = { RM,0, "BTC%S %e,%r" },
[0xbc] = { RM,0, "BSF%S %e,%r" },
[0xbd] = { RM,0, "BSR%S %e,%r" },
[0xbe] = { RMB,0, "MOVBSX %e,%R" },
[0xbf] = { RM,0, "MOVWSX %e,%R" },
[0xc7] = { RMOP,0, optab0FC7 },
};
static Optable optab80[8]=
{
[0x00] = { Ib,0, "ADDB %i,%e" },
[0x01] = { Ib,0, "ORB %i,%e" },
[0x02] = { Ib,0, "ADCB %i,%e" },
[0x03] = { Ib,0, "SBBB %i,%e" },
[0x04] = { Ib,0, "ANDB %i,%e" },
[0x05] = { Ib,0, "SUBB %i,%e" },
[0x06] = { Ib,0, "XORB %i,%e" },
[0x07] = { Ib,0, "CMPB %e,%i" },
};
static Optable optab81[8]=
{
[0x00] = { Iwd,0, "ADD%S %i,%e" },
[0x01] = { Iwd,0, "OR%S %i,%e" },
[0x02] = { Iwd,0, "ADC%S %i,%e" },
[0x03] = { Iwd,0, "SBB%S %i,%e" },
[0x04] = { Iwd,0, "AND%S %i,%e" },
[0x05] = { Iwd,0, "SUB%S %i,%e" },
[0x06] = { Iwd,0, "XOR%S %i,%e" },
[0x07] = { Iwd,0, "CMP%S %e,%i" },
};
static Optable optab83[8]=
{
[0x00] = { Ibs,0, "ADD%S %i,%e" },
[0x01] = { Ibs,0, "OR%S %i,%e" },
[0x02] = { Ibs,0, "ADC%S %i,%e" },
[0x03] = { Ibs,0, "SBB%S %i,%e" },
[0x04] = { Ibs,0, "AND%S %i,%e" },
[0x05] = { Ibs,0, "SUB%S %i,%e" },
[0x06] = { Ibs,0, "XOR%S %i,%e" },
[0x07] = { Ibs,0, "CMP%S %e,%i" },
};
static Optable optabC0[8] =
{
[0x00] = { Ib,0, "ROLB %i,%e" },
[0x01] = { Ib,0, "RORB %i,%e" },
[0x02] = { Ib,0, "RCLB %i,%e" },
[0x03] = { Ib,0, "RCRB %i,%e" },
[0x04] = { Ib,0, "SHLB %i,%e" },
[0x05] = { Ib,0, "SHRB %i,%e" },
[0x07] = { Ib,0, "SARB %i,%e" },
};
static Optable optabC1[8] =
{
[0x00] = { Ib,0, "ROL%S %i,%e" },
[0x01] = { Ib,0, "ROR%S %i,%e" },
[0x02] = { Ib,0, "RCL%S %i,%e" },
[0x03] = { Ib,0, "RCR%S %i,%e" },
[0x04] = { Ib,0, "SHL%S %i,%e" },
[0x05] = { Ib,0, "SHR%S %i,%e" },
[0x07] = { Ib,0, "SAR%S %i,%e" },
};
static Optable optabD0[8] =
{
[0x00] = { 0,0, "ROLB %e" },
[0x01] = { 0,0, "RORB %e" },
[0x02] = { 0,0, "RCLB %e" },
[0x03] = { 0,0, "RCRB %e" },
[0x04] = { 0,0, "SHLB %e" },
[0x05] = { 0,0, "SHRB %e" },
[0x07] = { 0,0, "SARB %e" },
};
static Optable optabD1[8] =
{
[0x00] = { 0,0, "ROL%S %e" },
[0x01] = { 0,0, "ROR%S %e" },
[0x02] = { 0,0, "RCL%S %e" },
[0x03] = { 0,0, "RCR%S %e" },
[0x04] = { 0,0, "SHL%S %e" },
[0x05] = { 0,0, "SHR%S %e" },
[0x07] = { 0,0, "SAR%S %e" },
};
static Optable optabD2[8] =
{
[0x00] = { 0,0, "ROLB CL,%e" },
[0x01] = { 0,0, "RORB CL,%e" },
[0x02] = { 0,0, "RCLB CL,%e" },
[0x03] = { 0,0, "RCRB CL,%e" },
[0x04] = { 0,0, "SHLB CL,%e" },
[0x05] = { 0,0, "SHRB CL,%e" },
[0x07] = { 0,0, "SARB CL,%e" },
};
static Optable optabD3[8] =
{
[0x00] = { 0,0, "ROL%S CL,%e" },
[0x01] = { 0,0, "ROR%S CL,%e" },
[0x02] = { 0,0, "RCL%S CL,%e" },
[0x03] = { 0,0, "RCR%S CL,%e" },
[0x04] = { 0,0, "SHL%S CL,%e" },
[0x05] = { 0,0, "SHR%S CL,%e" },
[0x07] = { 0,0, "SAR%S CL,%e" },
};
static Optable optabD8[8+8] =
{
[0x00] = { 0,0, "FADDF %e,F0" },
[0x01] = { 0,0, "FMULF %e,F0" },
[0x02] = { 0,0, "FCOMF %e,F0" },
[0x03] = { 0,0, "FCOMFP %e,F0" },
[0x04] = { 0,0, "FSUBF %e,F0" },
[0x05] = { 0,0, "FSUBRF %e,F0" },
[0x06] = { 0,0, "FDIVF %e,F0" },
[0x07] = { 0,0, "FDIVRF %e,F0" },
[0x08] = { 0,0, "FADDD %f,F0" },
[0x09] = { 0,0, "FMULD %f,F0" },
[0x0a] = { 0,0, "FCOMD %f,F0" },
[0x0b] = { 0,0, "FCOMPD %f,F0" },
[0x0c] = { 0,0, "FSUBD %f,F0" },
[0x0d] = { 0,0, "FSUBRD %f,F0" },
[0x0e] = { 0,0, "FDIVD %f,F0" },
[0x0f] = { 0,0, "FDIVRD %f,F0" },
};
/*
* optabD9 and optabDB use the following encoding:
* if (0 <= modrm <= 2) instruction = optabDx[modrm&0x07];
* else instruction = optabDx[(modrm&0x3f)+8];
*
* the instructions for MOD == 3, follow the 8 instructions
* for the other MOD values stored at the front of the table.
*/
static Optable optabD9[64+8] =
{
[0x00] = { 0,0, "FMOVF %e,F0" },
[0x02] = { 0,0, "FMOVF F0,%e" },
[0x03] = { 0,0, "FMOVFP F0,%e" },
[0x04] = { 0,0, "FLDENV%S %e" },
[0x05] = { 0,0, "FLDCW %e" },
[0x06] = { 0,0, "FSTENV%S %e" },
[0x07] = { 0,0, "FSTCW %e" },
[0x08] = { 0,0, "FMOVD F0,F0" }, /* Mod R/M = 11xx xxxx*/
[0x09] = { 0,0, "FMOVD F1,F0" },
[0x0a] = { 0,0, "FMOVD F2,F0" },
[0x0b] = { 0,0, "FMOVD F3,F0" },
[0x0c] = { 0,0, "FMOVD F4,F0" },
[0x0d] = { 0,0, "FMOVD F5,F0" },
[0x0e] = { 0,0, "FMOVD F6,F0" },
[0x0f] = { 0,0, "FMOVD F7,F0" },
[0x10] = { 0,0, "FXCHD F0,F0" },
[0x11] = { 0,0, "FXCHD F1,F0" },
[0x12] = { 0,0, "FXCHD F2,F0" },
[0x13] = { 0,0, "FXCHD F3,F0" },
[0x14] = { 0,0, "FXCHD F4,F0" },
[0x15] = { 0,0, "FXCHD F5,F0" },
[0x16] = { 0,0, "FXCHD F6,F0" },
[0x17] = { 0,0, "FXCHD F7,F0" },
[0x18] = { 0,0, "FNOP" },
[0x28] = { 0,0, "FCHS" },
[0x29] = { 0,0, "FABS" },
[0x2c] = { 0,0, "FTST" },
[0x2d] = { 0,0, "FXAM" },
[0x30] = { 0,0, "FLD1" },
[0x31] = { 0,0, "FLDL2T" },
[0x32] = { 0,0, "FLDL2E" },
[0x33] = { 0,0, "FLDPI" },
[0x34] = { 0,0, "FLDLG2" },
[0x35] = { 0,0, "FLDLN2" },
[0x36] = { 0,0, "FLDZ" },
[0x38] = { 0,0, "F2XM1" },
[0x39] = { 0,0, "FYL2X" },
[0x3a] = { 0,0, "FPTAN" },
[0x3b] = { 0,0, "FPATAN" },
[0x3c] = { 0,0, "FXTRACT" },
[0x3d] = { 0,0, "FPREM1" },
[0x3e] = { 0,0, "FDECSTP" },
[0x3f] = { 0,0, "FNCSTP" },
[0x40] = { 0,0, "FPREM" },
[0x41] = { 0,0, "FYL2XP1" },
[0x42] = { 0,0, "FSQRT" },
[0x43] = { 0,0, "FSINCOS" },
[0x44] = { 0,0, "FRNDINT" },
[0x45] = { 0,0, "FSCALE" },
[0x46] = { 0,0, "FSIN" },
[0x47] = { 0,0, "FCOS" },
};
static Optable optabDA[8+8] =
{
[0x00] = { 0,0, "FADDL %e,F0" },
[0x01] = { 0,0, "FMULL %e,F0" },
[0x02] = { 0,0, "FCOML %e,F0" },
[0x03] = { 0,0, "FCOMLP %e,F0" },
[0x04] = { 0,0, "FSUBL %e,F0" },
[0x05] = { 0,0, "FSUBRL %e,F0" },
[0x06] = { 0,0, "FDIVL %e,F0" },
[0x07] = { 0,0, "FDIVRL %e,F0" },
[0x08] = { 0,0, "FCMOVCS %f,F0" },
[0x09] = { 0,0, "FCMOVEQ %f,F0" },
[0x0a] = { 0,0, "FCMOVLS %f,F0" },
[0x0b] = { 0,0, "FCMOVUN %f,F0" },
[0x0d] = { Op_R1,0, "FUCOMPP" },
};
static Optable optabDB[8+64] =
{
[0x00] = { 0,0, "FMOVL %e,F0" },
[0x02] = { 0,0, "FMOVL F0,%e" },
[0x03] = { 0,0, "FMOVLP F0,%e" },
[0x05] = { 0,0, "FMOVX %e,F0" },
[0x07] = { 0,0, "FMOVXP F0,%e" },
[0x08] = { 0,0, "FCMOVCC F0,F0" }, /* Mod R/M = 11xx xxxx*/
[0x09] = { 0,0, "FCMOVCC F1,F0" },
[0x0a] = { 0,0, "FCMOVCC F2,F0" },
[0x0b] = { 0,0, "FCMOVCC F3,F0" },
[0x0c] = { 0,0, "FCMOVCC F4,F0" },
[0x0d] = { 0,0, "FCMOVCC F5,F0" },
[0x0e] = { 0,0, "FCMOVCC F6,F0" },
[0x0f] = { 0,0, "FCMOVCC F7,F0" },
[0x10] = { 0,0, "FCMOVNE F0,F0" },
[0x11] = { 0,0, "FCMOVNE F1,F0" },
[0x12] = { 0,0, "FCMOVNE F2,F0" },
[0x13] = { 0,0, "FCMOVNE F3,F0" },
[0x14] = { 0,0, "FCMOVNE F4,F0" },
[0x15] = { 0,0, "FCMOVNE F5,F0" },
[0x16] = { 0,0, "FCMOVNE F6,F0" },
[0x17] = { 0,0, "FCMOVNE F7,F0" },
[0x18] = { 0,0, "FCMOVHI F0,F0" },
[0x19] = { 0,0, "FCMOVHI F1,F0" },
[0x1a] = { 0,0, "FCMOVHI F2,F0" },
[0x1b] = { 0,0, "FCMOVHI F3,F0" },
[0x1c] = { 0,0, "FCMOVHI F4,F0" },
[0x1d] = { 0,0, "FCMOVHI F5,F0" },
[0x1e] = { 0,0, "FCMOVHI F6,F0" },
[0x1f] = { 0,0, "FCMOVHI F7,F0" },
[0x20] = { 0,0, "FCMOVNU F0,F0" },
[0x21] = { 0,0, "FCMOVNU F1,F0" },
[0x22] = { 0,0, "FCMOVNU F2,F0" },
[0x23] = { 0,0, "FCMOVNU F3,F0" },
[0x24] = { 0,0, "FCMOVNU F4,F0" },
[0x25] = { 0,0, "FCMOVNU F5,F0" },
[0x26] = { 0,0, "FCMOVNU F6,F0" },
[0x27] = { 0,0, "FCMOVNU F7,F0" },
[0x2a] = { 0,0, "FCLEX" },
[0x2b] = { 0,0, "FINIT" },
[0x30] = { 0,0, "FUCOMI F0,F0" },
[0x31] = { 0,0, "FUCOMI F1,F0" },
[0x32] = { 0,0, "FUCOMI F2,F0" },
[0x33] = { 0,0, "FUCOMI F3,F0" },
[0x34] = { 0,0, "FUCOMI F4,F0" },
[0x35] = { 0,0, "FUCOMI F5,F0" },
[0x36] = { 0,0, "FUCOMI F6,F0" },
[0x37] = { 0,0, "FUCOMI F7,F0" },
[0x38] = { 0,0, "FCOMI F0,F0" },
[0x39] = { 0,0, "FCOMI F1,F0" },
[0x3a] = { 0,0, "FCOMI F2,F0" },
[0x3b] = { 0,0, "FCOMI F3,F0" },
[0x3c] = { 0,0, "FCOMI F4,F0" },
[0x3d] = { 0,0, "FCOMI F5,F0" },
[0x3e] = { 0,0, "FCOMI F6,F0" },
[0x3f] = { 0,0, "FCOMI F7,F0" },
};
static Optable optabDC[8+8] =
{
[0x00] = { 0,0, "FADDD %e,F0" },
[0x01] = { 0,0, "FMULD %e,F0" },
[0x02] = { 0,0, "FCOMD %e,F0" },
[0x03] = { 0,0, "FCOMDP %e,F0" },
[0x04] = { 0,0, "FSUBD %e,F0" },
[0x05] = { 0,0, "FSUBRD %e,F0" },
[0x06] = { 0,0, "FDIVD %e,F0" },
[0x07] = { 0,0, "FDIVRD %e,F0" },
[0x08] = { 0,0, "FADDD F0,%f" },
[0x09] = { 0,0, "FMULD F0,%f" },
[0x0c] = { 0,0, "FSUBRD F0,%f" },
[0x0d] = { 0,0, "FSUBD F0,%f" },
[0x0e] = { 0,0, "FDIVRD F0,%f" },
[0x0f] = { 0,0, "FDIVD F0,%f" },
};
static Optable optabDD[8+8] =
{
[0x00] = { 0,0, "FMOVD %e,F0" },
[0x02] = { 0,0, "FMOVD F0,%e" },
[0x03] = { 0,0, "FMOVDP F0,%e" },
[0x04] = { 0,0, "FRSTOR%S %e" },
[0x06] = { 0,0, "FSAVE%S %e" },
[0x07] = { 0,0, "FSTSW %e" },
[0x08] = { 0,0, "FFREED %f" },
[0x0a] = { 0,0, "FMOVD %f,F0" },
[0x0b] = { 0,0, "FMOVDP %f,F0" },
[0x0c] = { 0,0, "FUCOMD %f,F0" },
[0x0d] = { 0,0, "FUCOMDP %f,F0" },
};
static Optable optabDE[8+8] =
{
[0x00] = { 0,0, "FADDW %e,F0" },
[0x01] = { 0,0, "FMULW %e,F0" },
[0x02] = { 0,0, "FCOMW %e,F0" },
[0x03] = { 0,0, "FCOMWP %e,F0" },
[0x04] = { 0,0, "FSUBW %e,F0" },
[0x05] = { 0,0, "FSUBRW %e,F0" },
[0x06] = { 0,0, "FDIVW %e,F0" },
[0x07] = { 0,0, "FDIVRW %e,F0" },
[0x08] = { 0,0, "FADDDP F0,%f" },
[0x09] = { 0,0, "FMULDP F0,%f" },
[0x0b] = { Op_R1,0, "FCOMPDP" },
[0x0c] = { 0,0, "FSUBRDP F0,%f" },
[0x0d] = { 0,0, "FSUBDP F0,%f" },
[0x0e] = { 0,0, "FDIVRDP F0,%f" },
[0x0f] = { 0,0, "FDIVDP F0,%f" },
};
static Optable optabDF[8+8] =
{
[0x00] = { 0,0, "FMOVW %e,F0" },
[0x02] = { 0,0, "FMOVW F0,%e" },
[0x03] = { 0,0, "FMOVWP F0,%e" },
[0x04] = { 0,0, "FBLD %e" },
[0x05] = { 0,0, "FMOVL %e,F0" },
[0x06] = { 0,0, "FBSTP %e" },
[0x07] = { 0,0, "FMOVLP F0,%e" },
[0x0c] = { Op_R0,0, "FSTSW %OAX" },
[0x0d] = { 0,0, "FUCOMIP F0,%f" },
[0x0e] = { 0,0, "FCOMIP F0,%f" },
};
static Optable optabF6[8] =
{
[0x00] = { Ib,0, "TESTB %i,%e" },
[0x02] = { 0,0, "NOTB %e" },
[0x03] = { 0,0, "NEGB %e" },
[0x04] = { 0,0, "MULB AL,%e" },
[0x05] = { 0,0, "IMULB AL,%e" },
[0x06] = { 0,0, "DIVB AL,%e" },
[0x07] = { 0,0, "IDIVB AL,%e" },
};
static Optable optabF7[8] =
{
[0x00] = { Iwd,0, "TEST%S %i,%e" },
[0x02] = { 0,0, "NOT%S %e" },
[0x03] = { 0,0, "NEG%S %e" },
[0x04] = { 0,0, "MUL%S %OAX,%e" },
[0x05] = { 0,0, "IMUL%S %OAX,%e" },
[0x06] = { 0,0, "DIV%S %OAX,%e" },
[0x07] = { 0,0, "IDIV%S %OAX,%e" },
};
static Optable optabFE[8] =
{
[0x00] = { 0,0, "INCB %e" },
[0x01] = { 0,0, "DECB %e" },
};
static Optable optabFF[8] =
{
[0x00] = { 0,0, "INC%S %e" },
[0x01] = { 0,0, "DEC%S %e" },
[0x02] = { JUMP,0, "CALL* %e" },
[0x03] = { JUMP,0, "CALLF* %e" },
[0x04] = { JUMP,0, "JMP* %e" },
[0x05] = { JUMP,0, "JMPF* %e" },
[0x06] = { 0,0, "PUSHL %e" },
};
static Optable optable[256+2] =
{
[0x00] = { RMB,0, "ADDB %r,%e" },
[0x01] = { RM,0, "ADD%S %r,%e" },
[0x02] = { RMB,0, "ADDB %e,%r" },
[0x03] = { RM,0, "ADD%S %e,%r" },
[0x04] = { Ib,0, "ADDB %i,AL" },
[0x05] = { Iwd,0, "ADD%S %i,%OAX" },
[0x06] = { 0,0, "PUSHL ES" },
[0x07] = { 0,0, "POPL ES" },
[0x08] = { RMB,0, "ORB %r,%e" },
[0x09] = { RM,0, "OR%S %r,%e" },
[0x0a] = { RMB,0, "ORB %e,%r" },
[0x0b] = { RM,0, "OR%S %e,%r" },
[0x0c] = { Ib,0, "ORB %i,AL" },
[0x0d] = { Iwd,0, "OR%S %i,%OAX" },
[0x0e] = { 0,0, "PUSHL CS" },
[0x0f] = { AUXMM,0, optab0F },
[0x10] = { RMB,0, "ADCB %r,%e" },
[0x11] = { RM,0, "ADC%S %r,%e" },
[0x12] = { RMB,0, "ADCB %e,%r" },
[0x13] = { RM,0, "ADC%S %e,%r" },
[0x14] = { Ib,0, "ADCB %i,AL" },
[0x15] = { Iwd,0, "ADC%S %i,%OAX" },
[0x16] = { 0,0, "PUSHL SS" },
[0x17] = { 0,0, "POPL SS" },
[0x18] = { RMB,0, "SBBB %r,%e" },
[0x19] = { RM,0, "SBB%S %r,%e" },
[0x1a] = { RMB,0, "SBBB %e,%r" },
[0x1b] = { RM,0, "SBB%S %e,%r" },
[0x1c] = { Ib,0, "SBBB %i,AL" },
[0x1d] = { Iwd,0, "SBB%S %i,%OAX" },
[0x1e] = { 0,0, "PUSHL DS" },
[0x1f] = { 0,0, "POPL DS" },
[0x20] = { RMB,0, "ANDB %r,%e" },
[0x21] = { RM,0, "AND%S %r,%e" },
[0x22] = { RMB,0, "ANDB %e,%r" },
[0x23] = { RM,0, "AND%S %e,%r" },
[0x24] = { Ib,0, "ANDB %i,AL" },
[0x25] = { Iwd,0, "AND%S %i,%OAX" },
[0x26] = { SEG,0, "ES:" },
[0x27] = { 0,0, "DAA" },
[0x28] = { RMB,0, "SUBB %r,%e" },
[0x29] = { RM,0, "SUB%S %r,%e" },
[0x2a] = { RMB,0, "SUBB %e,%r" },
[0x2b] = { RM,0, "SUB%S %e,%r" },
[0x2c] = { Ib,0, "SUBB %i,AL" },
[0x2d] = { Iwd,0, "SUB%S %i,%OAX" },
[0x2e] = { SEG,0, "CS:" },
[0x2f] = { 0,0, "DAS" },
[0x30] = { RMB,0, "XORB %r,%e" },
[0x31] = { RM,0, "XOR%S %r,%e" },
[0x32] = { RMB,0, "XORB %e,%r" },
[0x33] = { RM,0, "XOR%S %e,%r" },
[0x34] = { Ib,0, "XORB %i,AL" },
[0x35] = { Iwd,0, "XOR%S %i,%OAX" },
[0x36] = { SEG,0, "SS:" },
[0x37] = { 0,0, "AAA" },
[0x38] = { RMB,0, "CMPB %r,%e" },
[0x39] = { RM,0, "CMP%S %r,%e" },
[0x3a] = { RMB,0, "CMPB %e,%r" },
[0x3b] = { RM,0, "CMP%S %e,%r" },
[0x3c] = { Ib,0, "CMPB %i,AL" },
[0x3d] = { Iwd,0, "CMP%S %i,%OAX" },
[0x3e] = { SEG,0, "DS:" },
[0x3f] = { 0,0, "AAS" },
[0x40] = { 0,0, "INC%S %OAX" },
[0x41] = { 0,0, "INC%S %OCX" },
[0x42] = { 0,0, "INC%S %ODX" },
[0x43] = { 0,0, "INC%S %OBX" },
[0x44] = { 0,0, "INC%S %OSP" },
[0x45] = { 0,0, "INC%S %OBP" },
[0x46] = { 0,0, "INC%S %OSI" },
[0x47] = { 0,0, "INC%S %ODI" },
[0x48] = { 0,0, "DEC%S %OAX" },
[0x49] = { 0,0, "DEC%S %OCX" },
[0x4a] = { 0,0, "DEC%S %ODX" },
[0x4b] = { 0,0, "DEC%S %OBX" },
[0x4c] = { 0,0, "DEC%S %OSP" },
[0x4d] = { 0,0, "DEC%S %OBP" },
[0x4e] = { 0,0, "DEC%S %OSI" },
[0x4f] = { 0,0, "DEC%S %ODI" },
[0x50] = { 0,0, "PUSH%S %OAX" },
[0x51] = { 0,0, "PUSH%S %OCX" },
[0x52] = { 0,0, "PUSH%S %ODX" },
[0x53] = { 0,0, "PUSH%S %OBX" },
[0x54] = { 0,0, "PUSH%S %OSP" },
[0x55] = { 0,0, "PUSH%S %OBP" },
[0x56] = { 0,0, "PUSH%S %OSI" },
[0x57] = { 0,0, "PUSH%S %ODI" },
[0x58] = { 0,0, "POP%S %OAX" },
[0x59] = { 0,0, "POP%S %OCX" },
[0x5a] = { 0,0, "POP%S %ODX" },
[0x5b] = { 0,0, "POP%S %OBX" },
[0x5c] = { 0,0, "POP%S %OSP" },
[0x5d] = { 0,0, "POP%S %OBP" },
[0x5e] = { 0,0, "POP%S %OSI" },
[0x5f] = { 0,0, "POP%S %ODI" },
[0x60] = { 0,0, "PUSHA%S" },
[0x61] = { 0,0, "POPA%S" },
[0x62] = { RMM,0, "BOUND %e,%r" },
[0x63] = { RM,0, "ARPL %r,%e" },
[0x64] = { SEG,0, "FS:" },
[0x65] = { SEG,0, "GS:" },
[0x66] = { OPOVER,0, "" },
[0x67] = { ADDOVER,0, "" },
[0x68] = { Iwd,0, "PUSH%S %i" },
[0x69] = { RM,Iwd, "IMUL%S %e,%i,%r" },
[0x6a] = { Ib,0, "PUSH%S %i" },
[0x6b] = { RM,Ibs, "IMUL%S %e,%i,%r" },
[0x6c] = { 0,0, "INSB DX,(%ODI)" },
[0x6d] = { 0,0, "INS%S DX,(%ODI)" },
[0x6e] = { 0,0, "OUTSB (%ASI),DX" },
[0x6f] = { 0,0, "OUTS%S (%ASI),DX" },
[0x70] = { Jbs,0, "JOS %p" },
[0x71] = { Jbs,0, "JOC %p" },
[0x72] = { Jbs,0, "JCS %p" },
[0x73] = { Jbs,0, "JCC %p" },
[0x74] = { Jbs,0, "JEQ %p" },
[0x75] = { Jbs,0, "JNE %p" },
[0x76] = { Jbs,0, "JLS %p" },
[0x77] = { Jbs,0, "JHI %p" },
[0x78] = { Jbs,0, "JMI %p" },
[0x79] = { Jbs,0, "JPL %p" },
[0x7a] = { Jbs,0, "JPS %p" },
[0x7b] = { Jbs,0, "JPC %p" },
[0x7c] = { Jbs,0, "JLT %p" },
[0x7d] = { Jbs,0, "JGE %p" },
[0x7e] = { Jbs,0, "JLE %p" },
[0x7f] = { Jbs,0, "JGT %p" },
[0x80] = { RMOPB,0, optab80 },
[0x81] = { RMOP,0, optab81 },
[0x83] = { RMOP,0, optab83 },
[0x84] = { RMB,0, "TESTB %r,%e" },
[0x85] = { RM,0, "TEST%S %r,%e" },
[0x86] = { RMB,0, "XCHGB %r,%e" },
[0x87] = { RM,0, "XCHG%S %r,%e" },
[0x88] = { RMB,0, "MOVB %r,%e" },
[0x89] = { RM,0, "MOV%S %r,%e" },
[0x8a] = { RMB,0, "MOVB %e,%r" },
[0x8b] = { RM,0, "MOV%S %e,%r" },
[0x8c] = { RM,0, "MOVW %g,%e" },
[0x8d] = { RM,0, "LEA%S %e,%r" },
[0x8e] = { RM,0, "MOVW %e,%g" },
[0x8f] = { RM,0, "POP%S %e" },
[0x90] = { 0,0, "NOP" },
[0x91] = { 0,0, "XCHG %OCX,%OAX" },
[0x92] = { 0,0, "XCHG %ODX,%OAX" },
[0x93] = { 0,0, "XCHG %OBX,%OAX" },
[0x94] = { 0,0, "XCHG %OSP,%OAX" },
[0x95] = { 0,0, "XCHG %OBP,%OAX" },
[0x96] = { 0,0, "XCHG %OSI,%OAX" },
[0x97] = { 0,0, "XCHG %ODI,%OAX" },
[0x98] = { 0,0, "%W" }, /* miserable CBW or CWDE */
[0x99] = { 0,0, "%w" }, /* idiotic CWD or CDQ */
[0x9a] = { PTR,0, "CALL%S %d" },
[0x9b] = { 0,0, "WAIT" },
[0x9c] = { 0,0, "PUSHF" },
[0x9d] = { 0,0, "POPF" },
[0x9e] = { 0,0, "SAHF" },
[0x9f] = { 0,0, "LAHF" },
[0xa0] = { Awd,0, "MOVB %i,AL" },
[0xa1] = { Awd,0, "MOV%S %i,%OAX" },
[0xa2] = { Awd,0, "MOVB AL,%i" },
[0xa3] = { Awd,0, "MOV%S %OAX,%i" },
[0xa4] = { 0,0, "MOVSB (%ASI),(%ADI)" },
[0xa5] = { 0,0, "MOVS%S (%ASI),(%ADI)" },
[0xa6] = { 0,0, "CMPSB (%ASI),(%ADI)" },
[0xa7] = { 0,0, "CMPS%S (%ASI),(%ADI)" },
[0xa8] = { Ib,0, "TESTB %i,AL" },
[0xa9] = { Iwd,0, "TEST%S %i,%OAX" },
[0xaa] = { 0,0, "STOSB AL,(%ADI)" },
[0xab] = { 0,0, "STOS%S %OAX,(%ADI)" },
[0xac] = { 0,0, "LODSB (%ASI),AL" },
[0xad] = { 0,0, "LODS%S (%ASI),%OAX" },
[0xae] = { 0,0, "SCASB (%ADI),AL" },
[0xaf] = { 0,0, "SCAS%S (%ADI),%OAX" },
[0xb0] = { Ib,0, "MOVB %i,AL" },
[0xb1] = { Ib,0, "MOVB %i,CL" },
[0xb2] = { Ib,0, "MOVB %i,DL" },
[0xb3] = { Ib,0, "MOVB %i,BL" },
[0xb4] = { Ib,0, "MOVB %i,AH" },
[0xb5] = { Ib,0, "MOVB %i,CH" },
[0xb6] = { Ib,0, "MOVB %i,DH" },
[0xb7] = { Ib,0, "MOVB %i,BH" },
[0xb8] = { Iwdq,0, "MOV%S %i,%OAX" },
[0xb9] = { Iwdq,0, "MOV%S %i,%OCX" },
[0xba] = { Iwdq,0, "MOV%S %i,%ODX" },
[0xbb] = { Iwdq,0, "MOV%S %i,%OBX" },
[0xbc] = { Iwdq,0, "MOV%S %i,%OSP" },
[0xbd] = { Iwdq,0, "MOV%S %i,%OBP" },
[0xbe] = { Iwdq,0, "MOV%S %i,%OSI" },
[0xbf] = { Iwdq,0, "MOV%S %i,%ODI" },
[0xc0] = { RMOPB,0, optabC0 },
[0xc1] = { RMOP,0, optabC1 },
[0xc2] = { Iw,0, "RET %i" },
[0xc3] = { RET,0, "RET" },
[0xc4] = { RM,0, "LES %e,%r" },
[0xc5] = { RM,0, "LDS %e,%r" },
[0xc6] = { RMB,Ib, "MOVB %i,%e" },
[0xc7] = { RM,Iwd, "MOV%S %i,%e" },
[0xc8] = { Iw2,Ib, "ENTER %i,%I" }, /* loony ENTER */
[0xc9] = { RET,0, "LEAVE" }, /* bizarre LEAVE */
[0xca] = { Iw,0, "RETF %i" },
[0xcb] = { RET,0, "RETF" },
[0xcc] = { 0,0, "INT 3" },
[0xcd] = { Ib,0, "INTB %i" },
[0xce] = { 0,0, "INTO" },
[0xcf] = { 0,0, "IRET" },
[0xd0] = { RMOPB,0, optabD0 },
[0xd1] = { RMOP,0, optabD1 },
[0xd2] = { RMOPB,0, optabD2 },
[0xd3] = { RMOP,0, optabD3 },
[0xd4] = { OA,0, "AAM" },
[0xd5] = { OA,0, "AAD" },
[0xd7] = { 0,0, "XLAT" },
[0xd8] = { FRMOP,0, optabD8 },
[0xd9] = { FRMEX,0, optabD9 },
[0xda] = { FRMOP,0, optabDA },
[0xdb] = { FRMEX,0, optabDB },
[0xdc] = { FRMOP,0, optabDC },
[0xdd] = { FRMOP,0, optabDD },
[0xde] = { FRMOP,0, optabDE },
[0xdf] = { FRMOP,0, optabDF },
[0xe0] = { Jbs,0, "LOOPNE %p" },
[0xe1] = { Jbs,0, "LOOPE %p" },
[0xe2] = { Jbs,0, "LOOP %p" },
[0xe3] = { Jbs,0, "JCXZ %p" },
[0xe4] = { Ib,0, "INB %i,AL" },
[0xe5] = { Ib,0, "IN%S %i,%OAX" },
[0xe6] = { Ib,0, "OUTB AL,%i" },
[0xe7] = { Ib,0, "OUT%S %OAX,%i" },
[0xe8] = { Iwds,0, "CALL %p" },
[0xe9] = { Iwds,0, "JMP %p" },
[0xea] = { PTR,0, "JMP %d" },
[0xeb] = { Jbs,0, "JMP %p" },
[0xec] = { 0,0, "INB DX,AL" },
[0xed] = { 0,0, "IN%S DX,%OAX" },
[0xee] = { 0,0, "OUTB AL,DX" },
[0xef] = { 0,0, "OUT%S %OAX,DX" },
[0xf0] = { PRE,0, "LOCK" },
[0xf2] = { OPRE,0, "REPNE" },
[0xf3] = { OPRE,0, "REP" },
[0xf4] = { 0,0, "HLT" },
[0xf5] = { 0,0, "CMC" },
[0xf6] = { RMOPB,0, optabF6 },
[0xf7] = { RMOP,0, optabF7 },
[0xf8] = { 0,0, "CLC" },
[0xf9] = { 0,0, "STC" },
[0xfa] = { 0,0, "CLI" },
[0xfb] = { 0,0, "STI" },
[0xfc] = { 0,0, "CLD" },
[0xfd] = { 0,0, "STD" },
[0xfe] = { RMOPB,0, optabFE },
[0xff] = { RMOP,0, optabFF },
[0x100] = { RM,0, "MOVLQSX %e,%r" },
[0x101] = { RM,0, "MOVLQZX %e,%r" },
};
/*
* get a byte of the instruction
*/
static int
igetc(Map *map, Instr *ip, uchar *c)
{
if(ip->n+1 > sizeof(ip->mem)){
werrstr("instruction too long");
return -1;
}
if (get1(map, ip->addr+ip->n, c, 1) < 0) {
werrstr("can't read instruction: %r");
return -1;
}
ip->mem[ip->n++] = *c;
return 1;
}
/*
* get two bytes of the instruction
*/
static int
igets(Map *map, Instr *ip, ushort *sp)
{
uchar c;
ushort s;
if (igetc(map, ip, &c) < 0)
return -1;
s = c;
if (igetc(map, ip, &c) < 0)
return -1;
s |= (c<<8);
*sp = s;
return 1;
}
/*
* get 4 bytes of the instruction
*/
static int
igetl(Map *map, Instr *ip, uint32 *lp)
{
ushort s;
int32 l;
if (igets(map, ip, &s) < 0)
return -1;
l = s;
if (igets(map, ip, &s) < 0)
return -1;
l |= (s<<16);
*lp = l;
return 1;
}
/*
* get 8 bytes of the instruction
*
static int
igetq(Map *map, Instr *ip, vlong *qp)
{
uint32 l;
uvlong q;
if (igetl(map, ip, &l) < 0)
return -1;
q = l;
if (igetl(map, ip, &l) < 0)
return -1;
q |= ((uvlong)l<<32);
*qp = q;
return 1;
}
*/
static int
getdisp(Map *map, Instr *ip, int mod, int rm, int code, int pcrel)
{
uchar c;
ushort s;
if (mod > 2)
return 1;
if (mod == 1) {
if (igetc(map, ip, &c) < 0)
return -1;
if (c&0x80)
ip->disp = c|0xffffff00;
else
ip->disp = c&0xff;
} else if (mod == 2 || rm == code) {
if (ip->asize == 'E') {
if (igetl(map, ip, &ip->disp) < 0)
return -1;
if (mod == 0)
ip->rip = pcrel;
} else {
if (igets(map, ip, &s) < 0)
return -1;
if (s&0x8000)
ip->disp = s|0xffff0000;
else
ip->disp = s;
}
if (mod == 0)
ip->base = -1;
}
return 1;
}
static int
modrm(Map *map, Instr *ip, uchar c)
{
uchar rm, mod;
mod = (c>>6)&3;
rm = c&7;
ip->mod = mod;
ip->base = rm;
ip->reg = (c>>3)&7;
ip->rip = 0;
if (mod == 3) /* register */
return 1;
if (ip->asize == 0) { /* 16-bit mode */
switch(rm) {
case 0:
ip->base = BX; ip->index = SI;
break;
case 1:
ip->base = BX; ip->index = DI;
break;
case 2:
ip->base = BP; ip->index = SI;
break;
case 3:
ip->base = BP; ip->index = DI;
break;
case 4:
ip->base = SI;
break;
case 5:
ip->base = DI;
break;
case 6:
ip->base = BP;
break;
case 7:
ip->base = BX;
break;
default:
break;
}
return getdisp(map, ip, mod, rm, 6, 0);
}
if (rm == 4) { /* scummy sib byte */
if (igetc(map, ip, &c) < 0)
return -1;
ip->ss = (c>>6)&0x03;
ip->index = (c>>3)&0x07;
if (ip->index == 4)
ip->index = -1;
ip->base = c&0x07;
return getdisp(map, ip, mod, ip->base, 5, 0);
}
return getdisp(map, ip, mod, rm, 5, ip->amd64);
}
static Optable *
mkinstr(Map *map, Instr *ip, uvlong pc)
{
int i, n, norex;
uchar c;
ushort s;
Optable *op, *obase;
char buf[128];
memset(ip, 0, sizeof(*ip));
norex = 1;
ip->base = -1;
ip->index = -1;
// if(asstype == AI8086)
// ip->osize = 'W';
// else {
ip->osize = 'L';
ip->asize = 'E';
ip->amd64 = (machcpu == &machamd64);
norex = 0;
// }
ip->addr = pc;
if (igetc(map, ip, &c) < 0)
return 0;
obase = optable;
newop:
if(ip->amd64 && !norex){
if(c >= 0x40 && c <= 0x4f) {
ip->rex = c;
if(igetc(map, ip, &c) < 0)
return 0;
}
if(c == 0x63){
if(ip->rex&REXW)
op = &obase[0x100]; /* MOVLQSX */
else
op = &obase[0x101]; /* MOVLQZX */
goto hack;
}
}
op = &obase[c];
hack:
if (op->proto == 0) {
badop:
n = snprint(buf, sizeof(buf), "opcode: ??");
for (i = 0; i < ip->n && n < sizeof(buf)-3; i++, n+=2)
_hexify(buf+n, ip->mem[i], 1);
strcpy(buf+n, "??");
werrstr(buf);
return 0;
}
for(i = 0; i < 2 && op->operand[i]; i++) {
switch(op->operand[i]) {
case Ib: /* 8-bit immediate - (no sign extension)*/
if (igetc(map, ip, &c) < 0)
return 0;
ip->imm = c&0xff;
ip->imm64 = ip->imm;
break;
case Jbs: /* 8-bit jump immediate (sign extended) */
if (igetc(map, ip, &c) < 0)
return 0;
if (c&0x80)
ip->imm = c|0xffffff00;
else
ip->imm = c&0xff;
ip->imm64 = (int32)ip->imm;
ip->jumptype = Jbs;
break;
case Ibs: /* 8-bit immediate (sign extended) */
if (igetc(map, ip, &c) < 0)
return 0;
if (c&0x80)
if (ip->osize == 'L')
ip->imm = c|0xffffff00;
else
ip->imm = c|0xff00;
else
ip->imm = c&0xff;
ip->imm64 = (int32)ip->imm;
break;
case Iw: /* 16-bit immediate -> imm */
if (igets(map, ip, &s) < 0)
return 0;
ip->imm = s&0xffff;
ip->imm64 = ip->imm;
ip->jumptype = Iw;
break;
case Iw2: /* 16-bit immediate -> in imm2*/
if (igets(map, ip, &s) < 0)
return 0;
ip->imm2 = s&0xffff;
break;
case Iwd: /* Operand-sized immediate (no sign extension unless 64 bits)*/
if (ip->osize == 'L') {
if (igetl(map, ip, &ip->imm) < 0)
return 0;
ip->imm64 = ip->imm;
if(ip->rex&REXW && (ip->imm & (1<<31)) != 0)
ip->imm64 |= (vlong)(~0ULL << 32);
} else {
if (igets(map, ip, &s)< 0)
return 0;
ip->imm = s&0xffff;
ip->imm64 = ip->imm;
}
break;
case Iwdq: /* Operand-sized immediate, possibly big */
if (ip->osize == 'L') {
if (igetl(map, ip, &ip->imm) < 0)
return 0;
ip->imm64 = ip->imm;
if (ip->rex & REXW) {
uint32 l;
if (igetl(map, ip, &l) < 0)
return 0;
ip->imm64 |= (uvlong)l << 32;
}
} else {
if (igets(map, ip, &s)< 0)
return 0;
ip->imm = s&0xffff;
}
break;
case Awd: /* Address-sized immediate (no sign extension)*/
if (ip->asize == 'E') {
if (igetl(map, ip, &ip->imm) < 0)
return 0;
/* TO DO: REX */
} else {
if (igets(map, ip, &s)< 0)
return 0;
ip->imm = s&0xffff;
}
break;
case Iwds: /* Operand-sized immediate (sign extended) */
if (ip->osize == 'L') {
if (igetl(map, ip, &ip->imm) < 0)
return 0;
} else {
if (igets(map, ip, &s)< 0)
return 0;
if (s&0x8000)
ip->imm = s|0xffff0000;
else
ip->imm = s&0xffff;
}
ip->jumptype = Iwds;
break;
case OA: /* literal 0x0a byte */
if (igetc(map, ip, &c) < 0)
return 0;
if (c != 0x0a)
goto badop;
break;
case Op_R0: /* base register must be R0 */
if (ip->base != 0)
goto badop;
break;
case Op_R1: /* base register must be R1 */
if (ip->base != 1)
goto badop;
break;
case RMB: /* R/M field with byte register (/r)*/
if (igetc(map, ip, &c) < 0)
return 0;
if (modrm(map, ip, c) < 0)
return 0;
ip->osize = 'B';
break;
case RM: /* R/M field with register (/r) */
if (igetc(map, ip, &c) < 0)
return 0;
if (modrm(map, ip, c) < 0)
return 0;
break;
case RMOPB: /* R/M field with op code (/digit) */
if (igetc(map, ip, &c) < 0)
return 0;
if (modrm(map, ip, c) < 0)
return 0;
c = ip->reg; /* secondary op code */
obase = (Optable*)op->proto;
ip->osize = 'B';
goto newop;
case RMOP: /* R/M field with op code (/digit) */
if (igetc(map, ip, &c) < 0)
return 0;
if (modrm(map, ip, c) < 0)
return 0;
obase = (Optable*)op->proto;
if(ip->amd64 && obase == optab0F01 && c == 0xF8)
return optab0F01F8;
c = ip->reg;
goto newop;
case FRMOP: /* FP R/M field with op code (/digit) */
if (igetc(map, ip, &c) < 0)
return 0;
if (modrm(map, ip, c) < 0)
return 0;
if ((c&0xc0) == 0xc0)
c = ip->reg+8; /* 16 entry table */
else
c = ip->reg;
obase = (Optable*)op->proto;
goto newop;
case FRMEX: /* Extended FP R/M field with op code (/digit) */
if (igetc(map, ip, &c) < 0)
return 0;
if (modrm(map, ip, c) < 0)
return 0;
if ((c&0xc0) == 0xc0)
c = (c&0x3f)+8; /* 64-entry table */
else
c = ip->reg;
obase = (Optable*)op->proto;
goto newop;
case RMR: /* R/M register only (mod = 11) */
if (igetc(map, ip, &c) < 0)
return 0;
if ((c&0xc0) != 0xc0) {
werrstr("invalid R/M register: %x", c);
return 0;
}
if (modrm(map, ip, c) < 0)
return 0;
break;
case RMM: /* R/M register only (mod = 11) */
if (igetc(map, ip, &c) < 0)
return 0;
if ((c&0xc0) == 0xc0) {
werrstr("invalid R/M memory mode: %x", c);
return 0;
}
if (modrm(map, ip, c) < 0)
return 0;
break;
case PTR: /* Seg:Displacement addr (ptr16:16 or ptr16:32) */
if (ip->osize == 'L') {
if (igetl(map, ip, &ip->disp) < 0)
return 0;
} else {
if (igets(map, ip, &s)< 0)
return 0;
ip->disp = s&0xffff;
}
if (igets(map, ip, (ushort*)&ip->seg) < 0)
return 0;
ip->jumptype = PTR;
break;
case AUXMM: /* Multi-byte op code; prefix determines table selection */
if (igetc(map, ip, &c) < 0)
return 0;
obase = (Optable*)op->proto;
switch (ip->opre) {
case 0x66: op = optab660F; break;
case 0xF2: op = optabF20F; break;
case 0xF3: op = optabF30F; break;
default: op = nil; break;
}
if(op != nil && op[c].proto != nil)
obase = op;
norex = 1; /* no more rex prefixes */
/* otherwise the optab entry captures it */
goto newop;
case AUX: /* Multi-byte op code - Auxiliary table */
obase = (Optable*)op->proto;
if (igetc(map, ip, &c) < 0)
return 0;
goto newop;
case OPRE: /* Instr Prefix or media op */
ip->opre = c;
/* fall through */
case PRE: /* Instr Prefix */
ip->prefix = (char*)op->proto;
if (igetc(map, ip, &c) < 0)
return 0;
if (ip->opre && c == 0x0F)
ip->prefix = 0;
goto newop;
case SEG: /* Segment Prefix */
ip->segment = (char*)op->proto;
if (igetc(map, ip, &c) < 0)
return 0;
goto newop;
case OPOVER: /* Operand size override */
ip->opre = c;
ip->osize = 'W';
if (igetc(map, ip, &c) < 0)
return 0;
if (c == 0x0F)
ip->osize = 'L';
else if (ip->amd64 && (c&0xF0) == 0x40)
ip->osize = 'Q';
goto newop;
case ADDOVER: /* Address size override */
ip->asize = 0;
if (igetc(map, ip, &c) < 0)
return 0;
goto newop;
case JUMP: /* mark instruction as JUMP or RET */
case RET:
ip->jumptype = op->operand[i];
break;
default:
werrstr("bad operand type %d", op->operand[i]);
return 0;
}
}
return op;
}
#pragma varargck argpos bprint 2
static void
bprint(Instr *ip, char *fmt, ...)
{
va_list arg;
va_start(arg, fmt);
ip->curr = vseprint(ip->curr, ip->end, fmt, arg);
va_end(arg);
}
/*
* if we want to call 16 bit regs AX,BX,CX,...
* and 32 bit regs EAX,EBX,ECX,... then
* change the defs of ANAME and ONAME to:
* #define ANAME(ip) ((ip->asize == 'E' ? "E" : "")
* #define ONAME(ip) ((ip)->osize == 'L' ? "E" : "")
*/
#define ANAME(ip) ""
#define ONAME(ip) ""
static char *reg[] = {
[AX] = "AX",
[CX] = "CX",
[DX] = "DX",
[BX] = "BX",
[SP] = "SP",
[BP] = "BP",
[SI] = "SI",
[DI] = "DI",
/* amd64 */
[AMD64_R8] = "R8",
[AMD64_R9] = "R9",
[AMD64_R10] = "R10",
[AMD64_R11] = "R11",
[AMD64_R12] = "R12",
[AMD64_R13] = "R13",
[AMD64_R14] = "R14",
[AMD64_R15] = "R15",
};
static char *breg[] = { "AL", "CL", "DL", "BL", "AH", "CH", "DH", "BH" };
static char *breg64[] = { "AL", "CL", "DL", "BL", "SPB", "BPB", "SIB", "DIB",
"R8B", "R9B", "R10B", "R11B", "R12B", "R13B", "R14B", "R15B" };
static char *sreg[] = { "ES", "CS", "SS", "DS", "FS", "GS" };
static void
plocal(Instr *ip)
{
Symbol s;
char *name;
Loc l, li;
l.type = LOFFSET;
l.offset = ip->disp;
if(ip->base == SP)
l.reg = "SP";
else
l.reg = "BP";
li.type = LADDR;
li.addr = ip->addr;
if(findsym(li, CTEXT, &s) < 0)
goto raw;
name = nil;
if(ip->base==SP && lookuplsym(&s, FRAMENAME, &s) >= 0) {
/* translate stack offset to offset from plan 9 frame pointer*/
/* XXX not sure how to do this */
}
if(name==nil && findlsym(&s, l, &s) >= 0)
name = s.name;
if(name)
bprint(ip, "%s+", name);
raw:
bprint(ip, "%#lx(%s)", l.offset, l.reg);
/*
if (s.value > ip->disp) {
ret = getauto(&s, s.value-ip->disp-mach->szaddr, CAUTO, &s);
reg = "(SP)";
} else {
offset -= s.value;
ret = getauto(&s, offset, CPARAM, &s);
reg = "(FP)";
}
if (ret)
bprint(ip, "%s+", s.name);
else
offset = ip->disp;
bprint(ip, "%ux%s", offset, reg);
*/
}
static int
isjmp(Instr *ip)
{
switch(ip->jumptype){
case Iwds:
case Jbs:
case JUMP:
return 1;
default:
return 0;
}
}
/*
* This is too smart for its own good, but it really is nice
* to have accurate translations when debugging, and it
* helps us identify which code is different in binaries that
* are changed on sources.
*/
static int
issymref(Instr *ip, Symbol *s, int32 w, int32 val)
{
Symbol next, tmp;
int32 isstring, size;
if (isjmp(ip))
return 1;
if (s->class==CTEXT && w==0)
return 1;
if (s->class==CDATA) {
/* use first bss symbol (or "end") rather than edata */
if (s->name[0]=='e' && strcmp(s->name, "edata") == 0){
if((indexsym(s->index+1, &tmp) && loccmp(&tmp.loc, &s->loc) == 0)
|| (indexsym(s->index-1, &tmp) && loccmp(&tmp.loc, &s->loc) == 0))
*s = tmp;
}
if (w == 0)
return 1;
for (next=*s; next.loc.addr==s->loc.addr; next=tmp)
if (!indexsym(next.index+1, &tmp))
break;
size = next.loc.addr - s->loc.addr;
if (w >= size)
return 0;
if (w > size-w)
w = size-w;
/* huge distances are usually wrong except in .string */
isstring = (s->name[0]=='.' && strcmp(s->name, ".string") == 0);
if (w > 8192 && !isstring)
return 0;
/* medium distances are tricky - look for constants */
/* near powers of two */
if ((val&(val-1)) == 0 || (val&(val+1)) == 0)
return 0;
return 1;
}
return 0;
}
static void
immediate(Instr *ip, vlong val)
{
Symbol s;
long w;
Loc l;
l.type = LADDR;
l.addr = val;
if (findsym(l, CANY, &s) >= 0) { /* TO DO */
w = val - s.loc.addr;
if (w < 0)
w = -w;
if (issymref(ip, &s, w, val)) {
if (w)
bprint(ip, "%s+%#lux(SB)", s.name, w);
else
bprint(ip, "%s(SB)", s.name);
return;
}
/*
if (s.class==CDATA && globalsym(&s, s.index+1)) {
w = s.value - val;
if (w < 0)
w = -w;
if (w < 4096) {
bprint(ip, "%s-%#lux(SB)", s.name, w);
return;
}
}
*/
}
if((ip->rex & REXW) == 0)
bprint(ip, "%lux", (long)val);
else
bprint(ip, "%llux", val);
}
static void
pea(Instr *ip)
{
if (ip->mod == 3) {
if (ip->osize == 'B')
bprint(ip, (ip->rex & REXB? breg64: breg)[(uchar)ip->base]);
else if(ip->rex & REXB)
bprint(ip, "%s%s", ANAME(ip), reg[ip->base+8]);
else
bprint(ip, "%s%s", ANAME(ip), reg[(uchar)ip->base]);
return;
}
if (ip->segment)
bprint(ip, ip->segment);
if (ip->asize == 'E' && ip->base == SP)
plocal(ip);
else {
if (ip->base < 0)
immediate(ip, ip->disp);
else {
bprint(ip, "%ux", ip->disp);
if(ip->rip)
bprint(ip, "(RIP)");
bprint(ip,"(%s%s)", ANAME(ip), reg[ip->rex&REXB? ip->base+8: ip->base]);
}
}
if (ip->index >= 0)
bprint(ip,"(%s%s*%d)", ANAME(ip), reg[ip->rex&REXX? ip->index+8: ip->index], 1<<ip->ss);
}
static void
prinstr(Instr *ip, char *fmt)
{
int sharp;
vlong v;
if (ip->prefix)
bprint(ip, "%s ", ip->prefix);
for (; *fmt && ip->curr < ip->end; fmt++) {
if (*fmt != '%'){
*ip->curr++ = *fmt;
continue;
}
sharp = 0;
if(*++fmt == '#') {
sharp = 1;
++fmt;
}
switch(*fmt){
case '%':
*ip->curr++ = '%';
break;
case 'A':
bprint(ip, "%s", ANAME(ip));
break;
case 'C':
bprint(ip, "CR%d", ip->reg);
break;
case 'D':
if (ip->reg < 4 || ip->reg == 6 || ip->reg == 7)
bprint(ip, "DR%d",ip->reg);
else
bprint(ip, "???");
break;
case 'I':
bprint(ip, "$");
immediate(ip, ip->imm2);
break;
case 'O':
bprint(ip,"%s", ONAME(ip));
break;
case 'i':
if(!sharp)
bprint(ip, "$");
v = ip->imm;
if(ip->rex & REXW)
v = ip->imm64;
immediate(ip, v);
break;
case 'R':
bprint(ip, "%s%s", ONAME(ip), reg[ip->rex&REXR? ip->reg+8: ip->reg]);
break;
case 'S':
if(ip->osize == 'Q' || ip->osize == 'L' && ip->rex & REXW)
bprint(ip, "Q");
else
bprint(ip, "%c", ip->osize);
break;
case 's':
if(ip->opre == 0 || ip->opre == 0x66)
bprint(ip, "P");
else
bprint(ip, "S");
if(ip->opre == 0xf2 || ip->opre == 0x66)
bprint(ip, "D");
else
bprint(ip, "S");
break;
case 'T':
if (ip->reg == 6 || ip->reg == 7)
bprint(ip, "TR%d",ip->reg);
else
bprint(ip, "???");
break;
case 'W':
if (ip->osize == 'Q' || ip->osize == 'L' && ip->rex & REXW)
bprint(ip, "CDQE");
else if (ip->osize == 'L')
bprint(ip,"CWDE");
else
bprint(ip, "CBW");
break;
case 'd':
bprint(ip,"%ux:%ux", ip->seg, ip->disp);
break;
case 'm':
if (ip->mod == 3 && ip->osize != 'B') {
if(fmt[1] != '*'){
if(ip->opre != 0) {
bprint(ip, "X%d", ip->rex&REXB? ip->base+8: ip->base);
break;
}
} else
fmt++;
bprint(ip, "M%d", ip->base);
break;
}
pea(ip);
break;
case 'e':
pea(ip);
break;
case 'f':
bprint(ip, "F%d", ip->base);
break;
case 'g':
if (ip->reg < 6)
bprint(ip,"%s",sreg[ip->reg]);
else
bprint(ip,"???");
break;
case 'p':
/*
* signed immediate in the uint32 ip->imm.
*/
v = (int32)ip->imm;
immediate(ip, v+ip->addr+ip->n);
break;
case 'r':
if (ip->osize == 'B')
bprint(ip,"%s", (ip->rex? breg64: breg)[ip->rex&REXR? ip->reg+8: ip->reg]);
else
bprint(ip, reg[ip->rex&REXR? ip->reg+8: ip->reg]);
break;
case 'w':
if (ip->osize == 'Q' || ip->rex & REXW)
bprint(ip, "CQO");
else if (ip->osize == 'L')
bprint(ip,"CDQ");
else
bprint(ip, "CWD");
break;
case 'M':
if(ip->opre != 0)
bprint(ip, "X%d", ip->rex&REXR? ip->reg+8: ip->reg);
else
bprint(ip, "M%d", ip->reg);
break;
case 'x':
if (ip->mod == 3 && ip->osize != 'B') {
bprint(ip, "X%d", ip->rex&REXB? ip->base+8: ip->base);
break;
}
pea(ip);
break;
case 'X':
bprint(ip, "X%d", ip->rex&REXR? ip->reg+8: ip->reg);
break;
default:
bprint(ip, "%%%c", *fmt);
break;
}
}
*ip->curr = 0; /* there's always room for 1 byte */
}
int
i386das(Map *map, uvlong pc, char modifier, char *buf, int n)
{
Instr instr;
Optable *op;
USED(modifier);
op = mkinstr(map, &instr, pc);
if (op == 0) {
errstr(buf, n);
return -1;
}
instr.curr = buf;
instr.end = buf+n-1;
prinstr(&instr, op->proto);
return instr.n;
}
int
i386hexinst(Map *map, u64int pc, char *buf, int n)
{
Instr instr;
int i;
if (mkinstr(map, &instr, pc) == 0) {
errstr(buf, n);
return -1;
}
for(i = 0; i < instr.n && n > 2; i++) {
_hexify(buf, instr.mem[i], 1);
buf += 2;
n -= 2;
}
*buf = 0;
return instr.n;
}
int
i386instlen(Map *map, u64int pc)
{
Instr i;
if (mkinstr(map, &i, pc))
return i.n;
return -1;
}
int
i386foll(Map *map, Regs *regs, u64int pc, u64int *foll)
{
Instr i;
Optable *op;
ushort s;
u64int l, addr;
vlong v;
int n;
op = mkinstr(map, &i, pc);
if (!op)
return -1;
n = 0;
switch(i.jumptype) {
case RET: /* RETURN or LEAVE */
case Iw: /* RETURN */
if (strcmp(op->proto, "LEAVE") == 0) {
if (lgeta(map, regs, locindir("BP", 0), &l) < 0)
return -1;
} else if (lgeta(map, regs, locindir(mach->sp, 0), &l) < 0)
return -1;
foll[0] = l;
return 1;
case Iwds: /* pc relative JUMP or CALL*/
case Jbs: /* pc relative JUMP or CALL */
v = (int32)i.imm;
foll[0] = pc+v+i.n;
n = 1;
break;
case PTR: /* seg:displacement JUMP or CALL */
foll[0] = (i.seg<<4)+i.disp;
return 1;
case JUMP: /* JUMP or CALL EA */
if(i.mod == 3) {
if (rget(regs, reg[i.rex&REXB? i.base+8: i.base], &foll[0]) < 0)
return -1;
return 1;
}
/* calculate the effective address */
addr = i.disp;
if (i.base >= 0) {
if (lgeta(map, regs, locindir(reg[i.rex&REXB? i.base+8: i.base], 0), &l) < 0)
return -1;
addr += l;
}
if (i.index >= 0) {
if (lgeta(map, regs, locindir(reg[i.rex&REXX? i.index+8: i.index], 0), &l) < 0)
return -1;
addr += l*(1<<i.ss);
}
/* now retrieve a seg:disp value at that address */
if (get2(map, addr, &s) < 0) /* seg */
return -1;
foll[0] = s<<4;
addr += 2;
if (i.asize == 'L') {
if (geta(map, addr, &l) < 0) /* disp32 */
return -1;
foll[0] += l;
} else { /* disp16 */
if (get2(map, addr, &s) < 0)
return -1;
foll[0] += s;
}
return 1;
default:
break;
}
if (strncmp(op->proto,"JMP", 3) == 0 || strncmp(op->proto,"CALL", 4) == 0)
return 1;
foll[n++] = pc+i.n;
return n;
}