1
0
mirror of https://github.com/golang/go synced 2024-11-20 09:24:50 -07:00
go/src/libmach/8db.c
Rob Pike 00274a13cb rename libmach_amd64 libmach
R=rsc
OCL=33273
CL=33276
2009-08-14 10:46:04 -07:00

2332 lines
54 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.

// Inferno libmach/8db.c
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/8db.c
//
// Copyright © 1994-1999 Lucent Technologies Inc.
// Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
// Portions Copyright © 1997-1999 Vita Nuova Limited.
// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
// Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
// Portions Copyright © 2009 The Go Authors. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
#define Ureg UregAmd64
#include <ureg_amd64.h>
#undef Ureg
#define Ureg Ureg386
#include <ureg_x86.h>
#undef Ureg
typedef struct UregAmd64 UregAmd64;
typedef struct Ureg386 Ureg386;
/*
* i386-specific debugger interface
* also amd64 extensions
*/
static char *i386excep(Map*, Rgetter);
static int i386trace(Map*, uvlong, uvlong, uvlong, Tracer);
static uvlong i386frame(Map*, uvlong, uvlong, uvlong, uvlong);
static int i386foll(Map*, uvlong, Rgetter, uvlong*);
static int i386inst(Map*, uvlong, char, char*, int);
static int i386das(Map*, uvlong, char*, int);
static int i386instlen(Map*, uvlong);
static char STARTSYM[] = "_main";
static char GOSTARTSYM[] = "sys·goexit";
static char PROFSYM[] = "_mainp";
static char FRAMENAME[] = ".frame";
static char LESSSTACK[] = "sys·lessstack";
static char MORESTACK[] = "sys·morestack";
static char *excname[] =
{
[0] "divide error",
[1] "debug exception",
[4] "overflow",
[5] "bounds check",
[6] "invalid opcode",
[7] "math coprocessor emulation",
[8] "double fault",
[9] "math coprocessor overrun",
[10] "invalid TSS",
[11] "segment not present",
[12] "stack exception",
[13] "general protection violation",
[14] "page fault",
[16] "math coprocessor error",
[17] "alignment check",
[18] "machine check",
[19] "floating-point exception",
[24] "clock",
[25] "keyboard",
[27] "modem status",
[28] "serial line status",
[30] "floppy disk",
[36] "mouse",
[37] "math coprocessor",
[38] "hard disk",
[64] "system call",
};
Machdata i386mach =
{
{0xCC, 0, 0, 0}, /* break point: INT 3 */
1, /* break point size */
leswab, /* convert short to local byte order */
leswal, /* convert int32 to local byte order */
leswav, /* convert vlong to local byte order */
i386trace, /* C traceback */
i386frame, /* frame finder */
i386excep, /* print exception */
0, /* breakpoint fixup */
leieeesftos, /* single precision float printer */
leieeedftos, /* double precision float printer */
i386foll, /* following addresses */
i386inst, /* print instruction */
i386das, /* dissembler */
i386instlen, /* instruction size calculation */
};
static char*
i386excep(Map *map, Rgetter rget)
{
uint32 c;
uvlong pc;
static char buf[16];
c = (*rget)(map, "TRAP");
if(c > 64 || excname[c] == 0) {
if (c == 3) {
pc = (*rget)(map, "PC");
if (get1(map, pc, (uchar*)buf, machdata->bpsize) > 0)
if (memcmp(buf, machdata->bpinst, machdata->bpsize) == 0)
return "breakpoint";
}
snprint(buf, sizeof(buf), "exception %ld", c);
return buf;
} else
return excname[c];
}
static int
i386trace(Map *map, uvlong pc, uvlong sp, uvlong link, Tracer trace)
{
int i;
uvlong osp, pc1;
Symbol s, f, s1;
extern Mach mamd64;
int isamd64;
uvlong g, m, lessstack, morestack, stktop;
isamd64 = (mach == &mamd64);
// ../pkg/runtime/runtime.h
// G is
// byte* stackguard
// byte* stackbase (= Stktop*)
// Defer* defer
// Gobuf sched
// TODO(rsc): Need some way to get at the g for other threads.
// Probably need to pass it into the trace function.
g = 0;
if(isamd64)
geta(map, offsetof(struct UregAmd64, r15), &g);
else {
// TODO(rsc): How to fetch g on 386?
}
stktop = 0;
if(g != 0)
geta(map, g+1*mach->szaddr, &stktop);
lessstack = 0;
if(lookup(0, LESSSTACK, &s))
lessstack = s.value;
morestack = 0;
if(lookup(0, MORESTACK, &s))
morestack = s.value;
USED(link);
osp = 0;
i = 0;
for(;;) {
if(!findsym(pc, CTEXT, &s)) {
// check for closure return sequence
uchar buf[8], *p;
if(get1(map, pc, buf, 8) < 0)
break;
// ADDQ $xxx, SP; RET
p = buf;
if(mach == &mamd64) {
if(p[0] != 0x48)
break;
p++;
}
if(p[0] != 0x81 || p[1] != 0xc4 || p[6] != 0xc3)
break;
sp += p[2] | (p[3]<<8) | (p[4]<<16) | (p[5]<<24);
if(geta(map, sp, &pc) < 0)
break;
sp += mach->szaddr;
continue;
}
if (osp == sp)
break;
osp = sp;
if(strcmp(STARTSYM, s.name) == 0 ||
strcmp(GOSTARTSYM, s.name) == 0 ||
strcmp(PROFSYM, s.name) == 0)
break;
if(s.value == morestack) {
// In the middle of morestack.
// Caller is m->morepc.
// Caller's caller is in m->morearg.
// TODO(rsc): 386
geta(map, offsetof(struct UregAmd64, r14), &m);
pc = 0;
sp = 0;
pc1 = 0;
s1 = s;
memset(&s, 0, sizeof s);
geta(map, m+1*mach->szaddr, &pc1); // m->morepc
geta(map, m+2*mach->szaddr, &sp); // m->morebuf.sp
geta(map, m+3*mach->szaddr, &pc); // m->morebuf.pc
findsym(pc1, CTEXT, &s);
(*trace)(map, pc1, sp-mach->szaddr, &s1); // morestack symbol; caller's PC/SP
// caller's caller
s1 = s;
findsym(pc, CTEXT, &s);
(*trace)(map, pc, sp, &s1); // morestack's caller; caller's caller's PC/SP
continue;
}
if(pc == lessstack) {
// ../pkg/runtime/runtime.h
// Stktop is
// byte* stackguard
// byte* stackbase
// Gobuf gobuf
// byte* sp;
// byte* pc;
// G* g;
if(!isamd64)
fprint(2, "warning: cannot unwind stack split on 386\n");
if(stktop == 0)
break;
pc = 0;
sp = 0;
geta(map, stktop+2*mach->szaddr, &sp);
geta(map, stktop+3*mach->szaddr, &pc);
geta(map, stktop+1*mach->szaddr, &stktop);
(*trace)(map, pc, sp, &s1);
continue;
}
s1 = s;
pc1 = 0;
if(pc != s.value) { /* not at first instruction */
if(findlocal(&s, FRAMENAME, &f) == 0)
break;
geta(map, sp, &pc1);
sp += f.value-mach->szaddr;
}
if(geta(map, sp, &pc) < 0)
break;
// If PC is not valid, assume we caught the function
// before it moved the stack pointer down or perhaps
// after it moved the stack pointer back up.
// Try the PC we'd have gotten without the stack
// pointer adjustment above (pc != s.value).
// This only matters for the first frame, and it is only
// a heuristic, but it does help.
if(!findsym(pc, CTEXT, &s) || strcmp(s.name, "etext") == 0)
pc = pc1;
if(pc == 0)
break;
if(pc != lessstack)
(*trace)(map, pc, sp, &s1);
sp += mach->szaddr;
if(++i > 1000)
break;
}
return i;
}
static uvlong
i386frame(Map *map, uvlong addr, uvlong pc, uvlong sp, uvlong link)
{
Symbol s, f;
USED(link);
while (findsym(pc, CTEXT, &s)) {
if(strcmp(STARTSYM, s.name) == 0 || strcmp(PROFSYM, s.name) == 0)
break;
if(pc != s.value) { /* not first instruction */
if(findlocal(&s, FRAMENAME, &f) == 0)
break;
sp += f.value-mach->szaddr;
}
if (s.value == addr)
return sp;
if (geta(map, sp, &pc) < 0)
break;
sp += mach->szaddr;
}
return 0;
}
/* 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 */
char index; /* bits 3-5 of SIB */
char 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 */
R8,
R9,
R10,
R11,
R12,
R13,
R14,
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) */
R0, /* Base reg of Mod R/M is literal 0x00 */
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",
[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 %i,%x,%X",
[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",
[0x0d] 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",
[0x2a] 0,0, "FCLEX",
[0x2b] 0,0, "FINIT",
};
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] 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] R0,0, "FSTSW %OAX",
};
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+1] =
{
[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 %r,%e",
};
/*
* 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 = asstype != AI386;
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){
op = &obase[0x100]; /* MOVLQSX */
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)~0 << 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 R0: /* base register must be R0 */
if (ip->base != 0)
goto badop;
break;
case 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 */
[R8] "R8",
[R9] "R9",
[R10] "R10",
[R11] "R11",
[R12] "R12",
[R13] "R13",
[R14] "R14",
[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)
{
int ret;
int32 offset;
Symbol s;
char *reg;
offset = ip->disp;
if (!findsym(ip->addr, CTEXT, &s) || !findlocal(&s, FRAMENAME, &s)) {
bprint(ip, "%lux(SP)", offset);
return;
}
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, "%lux%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((s ->index >= 0 && globalsym(&tmp, s->index+1) && tmp.value==s->value)
|| (s->index > 0 && globalsym(&tmp, s->index-1) && tmp.value==s->value))
*s = tmp;
}
if (w == 0)
return 1;
for (next=*s; next.value==s->value; next=tmp)
if (!globalsym(&tmp, next.index+1))
break;
size = next.value - s->value;
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;
int32 w;
if (findsym(val, CANY, &s)) { /* TO DO */
w = val - s.value;
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, "%lux", 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)
{
vlong v;
if (ip->prefix)
bprint(ip, "%s ", ip->prefix);
for (; *fmt && ip->curr < ip->end; fmt++) {
if (*fmt != '%'){
*ip->curr++ = *fmt;
continue;
}
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':
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:%lux",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 */
}
static int
i386inst(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;
}
static int
i386das(Map *map, uvlong 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;
}
static int
i386instlen(Map *map, uvlong pc)
{
Instr i;
if (mkinstr(map, &i, pc))
return i.n;
return -1;
}
static int
i386foll(Map *map, uvlong pc, Rgetter rget, uvlong *foll)
{
Instr i;
Optable *op;
ushort s;
uvlong 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 (geta(map, (*rget)(map, "BP"), &l) < 0)
return -1;
} else if (geta(map, (*rget)(map, mach->sp), &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) {
foll[0] = (*rget)(map, reg[i.rex&REXB? i.base+8: i.base]);
return 1;
}
/* calculate the effective address */
addr = i.disp;
if (i.base >= 0) {
if (geta(map, (*rget)(map, reg[i.rex&REXB? i.base+8: i.base]), &l) < 0)
return -1;
addr += l;
}
if (i.index >= 0) {
if (geta(map, (*rget)(map, reg[i.rex&REXX? i.index+8: i.index]), &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;
}