summaryrefslogtreecommitdiff
path: root/translate-i386.c
diff options
context:
space:
mode:
Diffstat (limited to 'translate-i386.c')
-rw-r--r--translate-i386.c244
1 files changed, 180 insertions, 64 deletions
diff --git a/translate-i386.c b/translate-i386.c
index 0dbaa99d92..5146242c6c 100644
--- a/translate-i386.c
+++ b/translate-i386.c
@@ -34,6 +34,10 @@
#include "dis-asm.h"
#endif
+#ifndef offsetof
+#define offsetof(type, field) ((size_t) &((type *)0)->field)
+#endif
+
static uint8_t *gen_code_ptr;
int __op_param1, __op_param2, __op_param3;
@@ -71,8 +75,13 @@ typedef struct DisasContext {
int prefix;
int aflag, dflag;
uint8_t *pc; /* current pc */
- int cc_op; /* current CC operation */
- int f_st;
+ int is_jmp; /* 1 = means jump (stop translation), 2 means CPU
+ static state change (stop translation) */
+ /* current block context */
+ int code32; /* 32 bit code segment */
+ int cc_op; /* current CC operation */
+ int addseg; /* non zero if either DS/ES/SS have a non zero base */
+ int f_st; /* currently unused */
} DisasContext;
/* i386 arith/logic operations */
@@ -763,12 +772,32 @@ static void gen_shifti(DisasContext *s1, int op, int ot, int d, int c)
static void gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_ptr)
{
int havesib;
- int havebase;
int base, disp;
- int index = 0;
- int scale = 0;
- int reg1, reg2, opreg;
- int mod, rm, code;
+ int index;
+ int scale;
+ int opreg;
+ int mod, rm, code, override, must_add_seg;
+
+ /* XXX: add a generation time variable to tell if base == 0 in DS/ES/SS */
+ /* XXX: fix lea case */
+ override = -1;
+ must_add_seg = s->addseg;
+ if (s->prefix & (PREFIX_CS | PREFIX_SS | PREFIX_DS |
+ PREFIX_ES | PREFIX_FS | PREFIX_GS)) {
+ if (s->prefix & PREFIX_ES)
+ override = R_ES;
+ else if (s->prefix & PREFIX_CS)
+ override = R_CS;
+ else if (s->prefix & PREFIX_SS)
+ override = R_SS;
+ else if (s->prefix & PREFIX_DS)
+ override = R_DS;
+ else if (s->prefix & PREFIX_FS)
+ override = R_FS;
+ else
+ override = R_GS;
+ must_add_seg = 1;
+ }
mod = (modrm >> 6) & 3;
rm = modrm & 7;
@@ -776,8 +805,9 @@ static void gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_
if (s->aflag) {
havesib = 0;
- havebase = 1;
base = rm;
+ index = 0;
+ scale = 0;
if (base == 4) {
havesib = 1;
@@ -790,7 +820,7 @@ static void gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_
switch (mod) {
case 0:
if (base == 5) {
- havebase = 0;
+ base = -1;
disp = ldl(s->pc);
s->pc += 4;
} else {
@@ -806,40 +836,25 @@ static void gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_
s->pc += 4;
break;
}
-
- reg1 = OR_ZERO;
- reg2 = OR_ZERO;
-
- if (havebase || (havesib && (index != 4 || scale != 0))) {
- if (havebase)
- reg1 = OR_EAX + base;
- if (havesib && index != 4) {
- if (havebase)
- reg2 = index + OR_EAX;
- else
- reg1 = index + OR_EAX;
- }
- }
- /* XXX: disp only ? */
- if (reg2 == OR_ZERO) {
- /* op: disp + (reg1 << scale) */
- if (reg1 == OR_ZERO) {
- gen_op_movl_A0_im(disp);
- } else if (scale == 0 && disp == 0) {
- gen_op_movl_A0_reg[reg1]();
- } else {
- gen_op_movl_A0_im(disp);
- gen_op_addl_A0_reg_sN[scale][reg1]();
- }
+
+ if (base >= 0) {
+ gen_op_movl_A0_reg[base]();
+ if (disp != 0)
+ gen_op_addl_A0_im(disp);
} else {
- /* op: disp + reg1 + (reg2 << scale) */
- if (disp != 0) {
- gen_op_movl_A0_im(disp);
- gen_op_addl_A0_reg_sN[0][reg1]();
- } else {
- gen_op_movl_A0_reg[reg1]();
+ gen_op_movl_A0_im(disp);
+ }
+ if (havesib && (index != 4 || scale != 0)) {
+ gen_op_addl_A0_reg_sN[scale][index]();
+ }
+ if (must_add_seg) {
+ if (override < 0) {
+ if (base == R_EBP || base == R_ESP)
+ override = R_SS;
+ else
+ override = R_DS;
}
- gen_op_addl_A0_reg_sN[scale][reg2]();
+ gen_op_addl_A0_seg(offsetof(CPUX86State,seg_cache[override].base));
}
} else {
switch (mod) {
@@ -848,6 +863,7 @@ static void gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_
disp = lduw(s->pc);
s->pc += 2;
gen_op_movl_A0_im(disp);
+ rm = 0; /* avoid SS override */
goto no_rm;
} else {
disp = 0;
@@ -896,8 +912,18 @@ static void gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_
if (disp != 0)
gen_op_addl_A0_im(disp);
gen_op_andl_A0_ffff();
- no_rm: ;
+ no_rm:
+ if (must_add_seg) {
+ if (override < 0) {
+ if (rm == 2 || rm == 3 || rm == 6)
+ override = R_SS;
+ else
+ override = R_DS;
+ }
+ gen_op_addl_A0_seg(offsetof(CPUX86State,seg_cache[override].base));
+ }
}
+
opreg = OR_A0;
disp = 0;
*reg_ptr = opreg;
@@ -1082,10 +1108,19 @@ static void gen_setcc(DisasContext *s, int b)
}
}
+/* move T0 to seg_reg and compute if the CPU state may change */
+void gen_movl_seg_T0(DisasContext *s, int seg_reg)
+{
+ gen_op_movl_seg_T0(seg_reg);
+ if (!s->addseg && seg_reg < R_FS)
+ s->is_jmp = 2; /* abort translation because the register may
+ have a non zero base */
+}
+
/* return the next pc address. Return -1 if no insn found. *is_jmp_ptr
is set to true if the instruction sets the PC (last instruction of
a basic block) */
-long disas_insn(DisasContext *s, uint8_t *pc_start, int *is_jmp_ptr)
+long disas_insn(DisasContext *s, uint8_t *pc_start)
{
int b, prefixes, aflag, dflag;
int shift, ot;
@@ -1093,8 +1128,8 @@ long disas_insn(DisasContext *s, uint8_t *pc_start, int *is_jmp_ptr)
s->pc = pc_start;
prefixes = 0;
- aflag = 1;
- dflag = 1;
+ aflag = s->code32;
+ dflag = s->code32;
// cur_pc = s->pc; /* for insn generation */
next_byte:
b = ldub(s->pc);
@@ -1416,11 +1451,11 @@ long disas_insn(DisasContext *s, uint8_t *pc_start, int *is_jmp_ptr)
gen_op_movl_T1_im((long)s->pc);
gen_op_pushl_T1();
gen_op_jmp_T0();
- *is_jmp_ptr = 1;
+ s->is_jmp = 1;
break;
case 4: /* jmp Ev */
gen_op_jmp_T0();
- *is_jmp_ptr = 1;
+ s->is_jmp = 1;
break;
case 6: /* push Ev */
gen_op_pushl_T0();
@@ -1555,6 +1590,30 @@ long disas_insn(DisasContext *s, uint8_t *pc_start, int *is_jmp_ptr)
gen_op_popl_T0();
gen_op_mov_reg_T0[OT_LONG][R_EBP]();
break;
+ case 0x06: /* push es */
+ case 0x0e: /* push cs */
+ case 0x16: /* push ss */
+ case 0x1e: /* push ds */
+ gen_op_movl_T0_seg(b >> 3);
+ gen_op_pushl_T0();
+ break;
+ case 0x1a0: /* push fs */
+ case 0x1a8: /* push gs */
+ gen_op_movl_T0_seg(((b >> 3) & 7) + R_FS);
+ gen_op_pushl_T0();
+ break;
+ case 0x07: /* pop es */
+ case 0x17: /* pop ss */
+ case 0x1f: /* pop ds */
+ gen_op_popl_T0();
+ gen_movl_seg_T0(s, b >> 3);
+ break;
+ case 0x1a1: /* pop fs */
+ case 0x1a9: /* pop gs */
+ gen_op_popl_T0();
+ gen_movl_seg_T0(s, ((b >> 3) & 7) + R_FS);
+ break;
+
/**************************/
/* mov */
case 0x88:
@@ -1598,6 +1657,24 @@ long disas_insn(DisasContext *s, uint8_t *pc_start, int *is_jmp_ptr)
gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0);
gen_op_mov_reg_T0[ot][reg]();
break;
+ case 0x8e: /* mov seg, Gv */
+ ot = dflag ? OT_LONG : OT_WORD;
+ modrm = ldub(s->pc++);
+ reg = (modrm >> 3) & 7;
+ gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0);
+ if (reg >= 6)
+ goto illegal_op;
+ gen_movl_seg_T0(s, reg);
+ break;
+ case 0x8c: /* mov Gv, seg */
+ ot = dflag ? OT_LONG : OT_WORD;
+ modrm = ldub(s->pc++);
+ reg = (modrm >> 3) & 7;
+ if (reg >= 6)
+ goto illegal_op;
+ gen_op_movl_T0_seg(reg);
+ gen_ldst_modrm(s, modrm, ot, OR_TMP0, 1);
+ break;
case 0x1b6: /* movzbS Gv, Eb */
case 0x1b7: /* movzwS Gv, Eb */
@@ -1648,8 +1725,13 @@ long disas_insn(DisasContext *s, uint8_t *pc_start, int *is_jmp_ptr)
ot = dflag ? OT_LONG : OT_WORD;
modrm = ldub(s->pc++);
reg = (modrm >> 3) & 7;
-
+ /* we must ensure that no segment is added */
+ s->prefix &= ~(PREFIX_CS | PREFIX_SS | PREFIX_DS |
+ PREFIX_ES | PREFIX_FS | PREFIX_GS);
+ val = s->addseg;
+ s->addseg = 0;
gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ s->addseg = val;
gen_op_mov_reg_A0[ot - OT_WORD][reg]();
break;
@@ -1711,6 +1793,35 @@ long disas_insn(DisasContext *s, uint8_t *pc_start, int *is_jmp_ptr)
gen_op_st_T0_A0[ot]();
gen_op_mov_reg_T1[ot][reg]();
break;
+ case 0xc4: /* les Gv */
+ op = R_ES;
+ goto do_lxx;
+ case 0xc5: /* lds Gv */
+ op = R_DS;
+ goto do_lxx;
+ case 0x1b2: /* lss Gv */
+ op = R_SS;
+ goto do_lxx;
+ case 0x1b4: /* lfs Gv */
+ op = R_FS;
+ goto do_lxx;
+ case 0x1b5: /* lgs Gv */
+ op = R_GS;
+ do_lxx:
+ ot = dflag ? OT_LONG : OT_WORD;
+ modrm = ldub(s->pc++);
+ reg = (modrm >> 3) & 7;
+ mod = (modrm >> 6) & 3;
+ if (mod == 3)
+ goto illegal_op;
+ gen_op_ld_T1_A0[ot]();
+ op_addl_A0_im(1 << (ot - OT_WORD + 1));
+ /* load the segment first to handle exceptions properly */
+ gen_op_lduw_T0_A0();
+ gen_movl_seg_T0(s, op);
+ /* then put the data */
+ gen_op_mov_reg_T1[ot][reg]();
+ break;
/************************/
/* shifts */
@@ -2327,12 +2438,12 @@ long disas_insn(DisasContext *s, uint8_t *pc_start, int *is_jmp_ptr)
gen_op_popl_T0();
gen_op_addl_ESP_im(val);
gen_op_jmp_T0();
- *is_jmp_ptr = 1;
+ s->is_jmp = 1;
break;
case 0xc3: /* ret */
gen_op_popl_T0();
gen_op_jmp_T0();
- *is_jmp_ptr = 1;
+ s->is_jmp = 1;
break;
case 0xe8: /* call */
val = insn_get(s, OT_LONG);
@@ -2340,19 +2451,19 @@ long disas_insn(DisasContext *s, uint8_t *pc_start, int *is_jmp_ptr)
gen_op_movl_T1_im((long)s->pc);
gen_op_pushl_T1();
gen_op_jmp_im(val);
- *is_jmp_ptr = 1;
+ s->is_jmp = 1;
break;
case 0xe9: /* jmp */
val = insn_get(s, OT_LONG);
val += (long)s->pc;
gen_op_jmp_im(val);
- *is_jmp_ptr = 1;
+ s->is_jmp = 1;
break;
case 0xeb: /* jmp Jb */
val = (int8_t)insn_get(s, OT_BYTE);
val += (long)s->pc;
gen_op_jmp_im(val);
- *is_jmp_ptr = 1;
+ s->is_jmp = 1;
break;
case 0x70 ... 0x7f: /* jcc Jb */
val = (int8_t)insn_get(s, OT_BYTE);
@@ -2367,7 +2478,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start, int *is_jmp_ptr)
val += (long)s->pc; /* XXX: fix 16 bit wrap */
do_jcc:
gen_jcc(s, b, val);
- *is_jmp_ptr = 1;
+ s->is_jmp = 1;
break;
case 0x190 ... 0x19f:
@@ -2548,19 +2659,19 @@ long disas_insn(DisasContext *s, uint8_t *pc_start, int *is_jmp_ptr)
break;
case 0xcc: /* int3 */
gen_op_int3((long)pc_start);
- *is_jmp_ptr = 1;
+ s->is_jmp = 1;
break;
case 0xcd: /* int N */
val = ldub(s->pc++);
/* XXX: currently we ignore the interrupt number */
gen_op_int_im((long)pc_start);
- *is_jmp_ptr = 1;
+ s->is_jmp = 1;
break;
case 0xce: /* into */
if (s->cc_op != CC_OP_DYNAMIC)
gen_op_set_cc_op(s->cc_op);
gen_op_into((long)pc_start, (long)s->pc);
- *is_jmp_ptr = 1;
+ s->is_jmp = 1;
break;
case 0x1c8 ... 0x1cf: /* bswap reg */
reg = b & 7;
@@ -2586,38 +2697,43 @@ long disas_insn(DisasContext *s, uint8_t *pc_start, int *is_jmp_ptr)
return -1;
}
return (long)s->pc;
+ illegal_op:
+ error("illegal opcode pc=0x%08Lx", (long)pc_start);
+ return -1;
}
/* return the next pc */
int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size,
- int *gen_code_size_ptr, uint8_t *pc_start)
+ int *gen_code_size_ptr, uint8_t *pc_start,
+ int flags)
{
DisasContext dc1, *dc = &dc1;
uint8_t *gen_code_end, *pc_ptr;
- int is_jmp;
long ret;
#ifdef DEBUG_DISAS
struct disassemble_info disasm_info;
#endif
-
+ dc->code32 = (flags >> GEN_FLAG_CODE32_SHIFT) & 1;
+ dc->addseg = (flags >> GEN_FLAG_ADDSEG_SHIFT) & 1;
+ dc->f_st = (flags >> GEN_FLAG_ST_SHIFT) & 7;
dc->cc_op = CC_OP_DYNAMIC;
gen_code_ptr = gen_code_buf;
gen_code_end = gen_code_buf + max_code_size - 4096;
gen_start();
- is_jmp = 0;
+ dc->is_jmp = 0;
pc_ptr = pc_start;
do {
- ret = disas_insn(dc, pc_ptr, &is_jmp);
+ ret = disas_insn(dc, pc_ptr);
if (ret == -1)
error("unknown instruction at PC=0x%x B=%02x %02x",
pc_ptr, pc_ptr[0], pc_ptr[1]);
pc_ptr = (void *)ret;
- } while (!is_jmp && gen_code_ptr < gen_code_end);
+ } while (!dc->is_jmp && gen_code_ptr < gen_code_end);
/* we must store the eflags state if it is not already done */
if (dc->cc_op != CC_OP_DYNAMIC)
gen_op_set_cc_op(dc->cc_op);
- if (!is_jmp) {
+ if (dc->is_jmp != 1) {
/* we add an additionnal jmp to update the simulated PC */
gen_op_jmp_im(ret);
}