diff options
Diffstat (limited to 'opcodes/i386-dis.c')
-rw-r--r-- | opcodes/i386-dis.c | 467 |
1 files changed, 281 insertions, 186 deletions
diff --git a/opcodes/i386-dis.c b/opcodes/i386-dis.c index a8bfa0f..4d71a00 100644 --- a/opcodes/i386-dis.c +++ b/opcodes/i386-dis.c @@ -42,9 +42,6 @@ #include <setjmp.h> -static int fetch_data (struct disassemble_info *, bfd_byte *); -static void ckprefix (void); -static const char *prefix_name (int, int); static int print_insn (bfd_vma, disassemble_info *); static void dofloat (int); static void OP_ST (int, int); @@ -1992,12 +1989,17 @@ static char scratchbuf[100]; static unsigned char *start_codep; static unsigned char *insn_codep; static unsigned char *codep; -static const char *lock_prefix; -static const char *data_prefix; -static const char *addr_prefix; -static const char *repz_prefix; -static const char *repnz_prefix; -static const char **all_prefixes[5]; +static int last_lock_prefix; +static int last_repz_prefix; +static int last_repnz_prefix; +static int last_data_prefix; +static int last_addr_prefix; +static int last_rex_prefix; +static int last_seg_prefix; +#define MAX_CODE_LENGTH 15 +/* We can up to 14 prefixes since the maximum instruction length is + 15bytes. */ +static int all_prefixes[MAX_CODE_LENGTH - 1]; static disassemble_info *the_info; static struct { @@ -9570,20 +9572,37 @@ static const struct dis386 rm_table[][8] = { #define INTERNAL_DISASSEMBLER_ERROR _("<internal disassembler error>") -static void +/* We use the high bit to indicate different name for the same + prefix. */ +#define ADDR16_PREFIX (0x67 | 0x100) +#define ADDR32_PREFIX (0x67 | 0x200) +#define DATA16_PREFIX (0x66 | 0x100) +#define DATA32_PREFIX (0x66 | 0x200) +#define REP_PREFIX (0xf3 | 0x100) + +static int ckprefix (void) { - int newrex, i; + int newrex, i, length; rex = 0; rex_original = 0; rex_ignored = 0; prefixes = 0; used_prefixes = 0; rex_used = 0; + last_lock_prefix = -1; + last_repz_prefix = -1; + last_repnz_prefix = -1; + last_data_prefix = -1; + last_addr_prefix = -1; + last_rex_prefix = -1; + last_seg_prefix = -1; for (i = 0; i < (int) ARRAY_SIZE (all_prefixes); i++) all_prefixes[i] = 0; i = 0; - while (1) + length = 0; + /* The maximum instruction length is 15bytes. */ + while (length < MAX_CODE_LENGTH - 1) { FETCH_DATA (the_info, codep + 1); newrex = 0; @@ -9606,68 +9625,55 @@ ckprefix (void) case 0x4d: case 0x4e: case 0x4f: - if (address_mode == mode_64bit) - newrex = *codep; - else - return; + if (address_mode == mode_64bit) + newrex = *codep; + else + return 1; + last_rex_prefix = i; break; case 0xf3: - if ((prefixes & PREFIX_REPZ) == 0) - { - all_prefixes[i] = &repz_prefix; - i++; - } prefixes |= PREFIX_REPZ; + last_repz_prefix = i; break; case 0xf2: - if ((prefixes & PREFIX_REPNZ) == 0) - { - all_prefixes[i] = &repnz_prefix; - i++; - } prefixes |= PREFIX_REPNZ; + last_repnz_prefix = i; break; case 0xf0: - if ((prefixes & PREFIX_LOCK) == 0) - { - all_prefixes[i] = &lock_prefix; - i++; - } prefixes |= PREFIX_LOCK; + last_lock_prefix = i; break; case 0x2e: prefixes |= PREFIX_CS; + last_seg_prefix = i; break; case 0x36: prefixes |= PREFIX_SS; + last_seg_prefix = i; break; case 0x3e: prefixes |= PREFIX_DS; + last_seg_prefix = i; break; case 0x26: prefixes |= PREFIX_ES; + last_seg_prefix = i; break; case 0x64: prefixes |= PREFIX_FS; + last_seg_prefix = i; break; case 0x65: prefixes |= PREFIX_GS; + last_seg_prefix = i; break; case 0x66: - if ((prefixes & PREFIX_DATA) == 0) - { - all_prefixes[i] = &data_prefix; - i++; - } prefixes |= PREFIX_DATA; + last_data_prefix = i; break; case 0x67: - if ((prefixes & PREFIX_ADDR) == 0) - { - all_prefixes[i] = &addr_prefix; - i++; - } prefixes |= PREFIX_ADDR; + last_addr_prefix = i; break; case FWAIT_OPCODE: /* fwait is really an instruction. If there are prefixes @@ -9677,22 +9683,48 @@ ckprefix (void) { prefixes |= PREFIX_FWAIT; codep++; - return; + return 1; } prefixes = PREFIX_FWAIT; break; default: - return; + return 1; } /* Rex is ignored when followed by another prefix. */ if (rex) { rex_used = rex; - return; + return 1; } + if (*codep != FWAIT_OPCODE) + all_prefixes[i++] = *codep; rex = newrex; rex_original = rex; codep++; + length++; + } + return 0; +} + +static int +seg_prefix (int pref) +{ + switch (pref) + { + case 0x2e: + return PREFIX_CS; + case 0x36: + return PREFIX_SS; + case 0x3e: + return PREFIX_DS; + case 0x26: + return PREFIX_ES; + case 0x64: + return PREFIX_FS; + case 0x65: + return PREFIX_GS; + default: + return 0; } } @@ -9769,6 +9801,16 @@ prefix_name (int pref, int sizeflag) return (sizeflag & AFLAG) ? "addr16" : "addr32"; case FWAIT_OPCODE: return "fwait"; + case ADDR16_PREFIX: + return "addr16"; + case ADDR32_PREFIX: + return "addr32"; + case DATA16_PREFIX: + return "data16"; + case DATA32_PREFIX: + return "data32"; + case REP_PREFIX: + return "rep"; default: return NULL; } @@ -9903,7 +9945,7 @@ get_valid_dis386 (const struct dis386 *dp, disassemble_info *info) if (prefixes & PREFIX_REPZ) { index = 1; - repz_prefix = NULL; + all_prefixes[last_repz_prefix] = 0; } else { @@ -9913,7 +9955,7 @@ get_valid_dis386 (const struct dis386 *dp, disassemble_info *info) if (prefixes & PREFIX_REPNZ) { index = 3; - repnz_prefix = NULL; + all_prefixes[last_repnz_prefix] = 0; } else { @@ -9921,7 +9963,7 @@ get_valid_dis386 (const struct dis386 *dp, disassemble_info *info) if (prefixes & PREFIX_DATA) { index = 2; - data_prefix = NULL; + all_prefixes[last_data_prefix] = 0; } } } @@ -10142,8 +10184,8 @@ print_insn (bfd_vma pc, disassemble_info *info) const char *p; struct dis_private priv; unsigned char op; - char prefix_obuf[32]; - char *prefix_obufp; + int prefix_length; + int default_prefixes; if (info->mach == bfd_mach_x86_64_intel_syntax || info->mach == bfd_mach_x86_64 @@ -10315,26 +10357,28 @@ print_insn (bfd_vma pc, disassemble_info *info) } obufp = obuf; - ckprefix (); + sizeflag = priv.orig_sizeflag; + + if (!ckprefix () || rex_used) + { + /* Too many prefixes or unused REX prefixes. */ + for (i = 0; + all_prefixes[i] && i < (int) ARRAY_SIZE (all_prefixes); + i++) + (*info->fprintf_func) (info->stream, "%s", + prefix_name (all_prefixes[i], sizeflag)); + return 1; + } insn_codep = codep; - sizeflag = priv.orig_sizeflag; FETCH_DATA (info, codep + 1); two_source_ops = (*codep == 0x62) || (*codep == 0xc8); if (((prefixes & PREFIX_FWAIT) - && ((*codep < 0xd8) || (*codep > 0xdf))) - || (rex && rex_used)) + && ((*codep < 0xd8) || (*codep > 0xdf)))) { - const char *name; - - /* fwait not followed by floating point instruction, or rex followed - by other prefixes. Print the first prefix. */ - name = prefix_name (priv.the_buffer[0], priv.orig_sizeflag); - if (name == NULL) - name = INTERNAL_DISASSEMBLER_ERROR; - (*info->fprintf_func) (info->stream, "%s", name); + (*info->fprintf_func) (info->stream, "fwait"); return 1; } @@ -10357,44 +10401,26 @@ print_insn (bfd_vma pc, disassemble_info *info) } if ((prefixes & PREFIX_REPZ)) - { - repz_prefix = "repz "; - used_prefixes |= PREFIX_REPZ; - } - else - repz_prefix = NULL; - + used_prefixes |= PREFIX_REPZ; if ((prefixes & PREFIX_REPNZ)) - { - repnz_prefix = "repnz "; - used_prefixes |= PREFIX_REPNZ; - } - else - repnz_prefix = NULL; - + used_prefixes |= PREFIX_REPNZ; if ((prefixes & PREFIX_LOCK)) - { - lock_prefix = "lock "; - used_prefixes |= PREFIX_LOCK; - } - else - lock_prefix = NULL; + used_prefixes |= PREFIX_LOCK; - addr_prefix = NULL; + default_prefixes = 0; if (prefixes & PREFIX_ADDR) { sizeflag ^= AFLAG; if (dp->op[2].bytemode != loop_jcxz_mode || intel_syntax) { if ((sizeflag & AFLAG) || address_mode == mode_64bit) - addr_prefix = "addr32 "; + all_prefixes[last_addr_prefix] = ADDR32_PREFIX; else - addr_prefix = "addr16 "; - used_prefixes |= PREFIX_ADDR; + all_prefixes[last_addr_prefix] = ADDR16_PREFIX; + default_prefixes |= PREFIX_ADDR; } } - data_prefix = NULL; if ((prefixes & PREFIX_DATA)) { sizeflag ^= DFLAG; @@ -10403,10 +10429,15 @@ print_insn (bfd_vma pc, disassemble_info *info) && !intel_syntax) { if (sizeflag & DFLAG) - data_prefix = "data32 "; + all_prefixes[last_data_prefix] = DATA32_PREFIX; else - data_prefix = "data16 "; - used_prefixes |= PREFIX_DATA; + all_prefixes[last_data_prefix] = DATA16_PREFIX; + default_prefixes |= PREFIX_DATA; + } + else if (rex & REX_W) + { + /* REX_W will override PREFIX_DATA. */ + default_prefixes |= PREFIX_DATA; } } @@ -10445,36 +10476,62 @@ print_insn (bfd_vma pc, disassemble_info *info) separately. If we don't do this, we'll wind up printing an instruction stream which does not precisely correspond to the bytes we are disassembling. */ - if ((prefixes & ~used_prefixes) != 0) + if ((prefixes & ~(used_prefixes | default_prefixes)) != 0) { - const char *name; - - name = prefix_name (priv.the_buffer[0], priv.orig_sizeflag); - if (name == NULL) - name = INTERNAL_DISASSEMBLER_ERROR; - (*info->fprintf_func) (info->stream, "%s", name); - return 1; - } - if ((rex_original & ~rex_used) || rex_ignored) - { - const char *name; - name = prefix_name (rex_original, priv.orig_sizeflag); - if (name == NULL) - name = INTERNAL_DISASSEMBLER_ERROR; - (*info->fprintf_func) (info->stream, "%s ", name); + for (i = 0; i < (int) ARRAY_SIZE (all_prefixes); i++) + if (all_prefixes[i]) + { + const char *name; + name = prefix_name (all_prefixes[i], priv.orig_sizeflag); + if (name == NULL) + name = INTERNAL_DISASSEMBLER_ERROR; + (*info->fprintf_func) (info->stream, "%s", name); + return 1; + } } - prefix_obuf[0] = 0; - prefix_obufp = prefix_obuf; + /* Check if the REX prefix used. */ + if ((rex ^ rex_used) == 0) + all_prefixes[last_rex_prefix] = 0; + + /* Check if the SEG prefix used. */ + if ((prefixes & (PREFIX_CS | PREFIX_SS | PREFIX_DS | PREFIX_ES + | PREFIX_FS | PREFIX_GS)) != 0 + && (used_prefixes + & seg_prefix (all_prefixes[last_seg_prefix])) != 0) + all_prefixes[last_seg_prefix] = 0; + + /* Check if the ADDR prefix used. */ + if ((prefixes & PREFIX_ADDR) != 0 + && (used_prefixes & PREFIX_ADDR) != 0) + all_prefixes[last_addr_prefix] = 0; + + /* Check if the DATA prefix used. */ + if ((prefixes & PREFIX_DATA) != 0 + && (used_prefixes & PREFIX_DATA) != 0) + all_prefixes[last_data_prefix] = 0; + + prefix_length = 0; for (i = 0; i < (int) ARRAY_SIZE (all_prefixes); i++) - if (all_prefixes[i] && *all_prefixes[i]) - prefix_obufp = stpcpy (prefix_obufp, *all_prefixes[i]); + if (all_prefixes[i]) + { + const char *name; + name = prefix_name (all_prefixes[i], sizeflag); + if (name == NULL) + abort (); + prefix_length += strlen (name) + 1; + (*info->fprintf_func) (info->stream, "%s ", name); + } - if (prefix_obuf[0] != 0) - (*info->fprintf_func) (info->stream, "%s", prefix_obuf); + /* Check maximum code length. */ + if ((codep - start_codep) > MAX_CODE_LENGTH) + { + (*info->fprintf_func) (info->stream, "(bad)"); + return MAX_CODE_LENGTH; + } obufp = mnemonicendp; - for (i = strlen (obuf) + strlen (prefix_obuf); i < 6; i++) + for (i = strlen (obuf) + prefix_length; i < 6; i++) oappend (" "); oappend (" "); (*info->fprintf_func) (info->stream, "%s", obuf); @@ -11013,11 +11070,14 @@ case_B: { if (rex & REX_W) *obufp++ = 'q'; - else if (sizeflag & DFLAG) - *obufp++ = intel_syntax ? 'd' : 'l'; else - *obufp++ = 'w'; - used_prefixes |= (prefixes & PREFIX_DATA); + { + if (sizeflag & DFLAG) + *obufp++ = intel_syntax ? 'd' : 'l'; + else + *obufp++ = 'w'; + used_prefixes |= (prefixes & PREFIX_DATA); + } } else *obufp++ = 'w'; @@ -11152,8 +11212,8 @@ case_L: *obufp++ = 'l'; else *obufp++ = 'w'; + used_prefixes |= (prefixes & PREFIX_DATA); } - used_prefixes |= (prefixes & PREFIX_DATA); } break; case 'U': @@ -11184,8 +11244,8 @@ case_Q: *obufp++ = intel_syntax ? 'd' : 'l'; else *obufp++ = 'w'; + used_prefixes |= (prefixes & PREFIX_DATA); } - used_prefixes |= (prefixes & PREFIX_DATA); } } else @@ -11311,11 +11371,14 @@ case_S: else *obufp++ = 's'; } - else if (prefixes & PREFIX_DATA) - *obufp++ = 'd'; else - *obufp++ = 's'; - used_prefixes |= (prefixes & PREFIX_DATA); + { + if (prefixes & PREFIX_DATA) + *obufp++ = 'd'; + else + *obufp++ = 's'; + used_prefixes |= (prefixes & PREFIX_DATA); + } break; case 'Y': if (l == 0 && len == 1) @@ -11561,7 +11624,6 @@ intel_operand_size (int bytemode, int sizeflag) if (address_mode == mode_64bit && (sizeflag & DFLAG)) { oappend ("QWORD PTR "); - used_prefixes |= (prefixes & PREFIX_DATA); break; } /* FALLTHRU */ @@ -11571,11 +11633,14 @@ intel_operand_size (int bytemode, int sizeflag) USED_REX (REX_W); if (rex & REX_W) oappend ("QWORD PTR "); - else if ((sizeflag & DFLAG) || bytemode == dq_mode) - oappend ("DWORD PTR "); else - oappend ("WORD PTR "); - used_prefixes |= (prefixes & PREFIX_DATA); + { + if ((sizeflag & DFLAG) || bytemode == dq_mode) + oappend ("DWORD PTR "); + else + oappend ("WORD PTR "); + used_prefixes |= (prefixes & PREFIX_DATA); + } break; case z_mode: if ((rex & REX_W) || (sizeflag & DFLAG)) @@ -11727,7 +11792,6 @@ OP_E_register (int bytemode, int sizeflag) if (address_mode == mode_64bit && (sizeflag & DFLAG)) { names = names64; - used_prefixes |= (prefixes & PREFIX_DATA); break; } bytemode = v_mode; @@ -11741,13 +11805,16 @@ OP_E_register (int bytemode, int sizeflag) USED_REX (REX_W); if (rex & REX_W) names = names64; - else if ((sizeflag & DFLAG) - || (bytemode != v_mode - && bytemode != v_swap_mode)) - names = names32; else - names = names16; - used_prefixes |= (prefixes & PREFIX_DATA); + { + if ((sizeflag & DFLAG) + || (bytemode != v_mode + && bytemode != v_swap_mode)) + names = names32; + else + names = names16; + used_prefixes |= (prefixes & PREFIX_DATA); + } break; case 0: return; @@ -11936,7 +12003,9 @@ OP_E_memory (int bytemode, int sizeflag) } } else - { /* 16 bit address mode */ + { + /* 16 bit address mode */ + used_prefixes |= prefixes & PREFIX_ADDR; switch (modrm.mod) { case 0: @@ -12063,11 +12132,14 @@ OP_G (int bytemode, int sizeflag) USED_REX (REX_W); if (rex & REX_W) oappend (names64[modrm.reg + add]); - else if ((sizeflag & DFLAG) || bytemode != v_mode) - oappend (names32[modrm.reg + add]); else - oappend (names16[modrm.reg + add]); - used_prefixes |= (prefixes & PREFIX_DATA); + { + if ((sizeflag & DFLAG) || bytemode != v_mode) + oappend (names32[modrm.reg + add]); + else + oappend (names16[modrm.reg + add]); + used_prefixes |= (prefixes & PREFIX_DATA); + } break; case m_mode: if (address_mode == mode_64bit) @@ -12206,11 +12278,14 @@ OP_REG (int code, int sizeflag) USED_REX (REX_W); if (rex & REX_W) s = names64[code - eAX_reg + add]; - else if (sizeflag & DFLAG) - s = names32[code - eAX_reg + add]; else - s = names16[code - eAX_reg + add]; - used_prefixes |= (prefixes & PREFIX_DATA); + { + if (sizeflag & DFLAG) + s = names32[code - eAX_reg + add]; + else + s = names16[code - eAX_reg + add]; + used_prefixes |= (prefixes & PREFIX_DATA); + } break; default: s = INTERNAL_DISASSEMBLER_ERROR; @@ -12253,11 +12328,14 @@ OP_IMREG (int code, int sizeflag) USED_REX (REX_W); if (rex & REX_W) s = names64[code - eAX_reg]; - else if (sizeflag & DFLAG) - s = names32[code - eAX_reg]; else - s = names16[code - eAX_reg]; - used_prefixes |= (prefixes & PREFIX_DATA); + { + if (sizeflag & DFLAG) + s = names32[code - eAX_reg]; + else + s = names16[code - eAX_reg]; + used_prefixes |= (prefixes & PREFIX_DATA); + } break; case z_mode_ax_reg: if ((rex & REX_W) || (sizeflag & DFLAG)) @@ -12298,17 +12376,20 @@ OP_I (int bytemode, int sizeflag) USED_REX (REX_W); if (rex & REX_W) op = get32s (); - else if (sizeflag & DFLAG) - { - op = get32 (); - mask = 0xffffffff; - } else { - op = get16 (); - mask = 0xfffff; + if (sizeflag & DFLAG) + { + op = get32 (); + mask = 0xffffffff; + } + else + { + op = get16 (); + mask = 0xfffff; + } + used_prefixes |= (prefixes & PREFIX_DATA); } - used_prefixes |= (prefixes & PREFIX_DATA); break; case w_mode: mask = 0xfffff; @@ -12353,17 +12434,20 @@ OP_I64 (int bytemode, int sizeflag) USED_REX (REX_W); if (rex & REX_W) op = get64 (); - else if (sizeflag & DFLAG) - { - op = get32 (); - mask = 0xffffffff; - } else { - op = get16 (); - mask = 0xfffff; + if (sizeflag & DFLAG) + { + op = get32 (); + mask = 0xffffffff; + } + else + { + op = get16 (); + mask = 0xfffff; + } + used_prefixes |= (prefixes & PREFIX_DATA); } - used_prefixes |= (prefixes & PREFIX_DATA); break; case w_mode: mask = 0xfffff; @@ -12400,19 +12484,22 @@ OP_sI (int bytemode, int sizeflag) USED_REX (REX_W); if (rex & REX_W) op = get32s (); - else if (sizeflag & DFLAG) - { - op = get32s (); - mask = 0xffffffff; - } else { - mask = 0xffffffff; - op = get16 (); - if ((op & 0x8000) != 0) - op -= 0x10000; + if (sizeflag & DFLAG) + { + op = get32s (); + mask = 0xffffffff; + } + else + { + mask = 0xffffffff; + op = get16 (); + if ((op & 0x8000) != 0) + op -= 0x10000; + } + used_prefixes |= (prefixes & PREFIX_DATA); } - used_prefixes |= (prefixes & PREFIX_DATA); break; case w_mode: op = get16 (); @@ -12446,6 +12533,7 @@ OP_J (int bytemode, int sizeflag) disp -= 0x100; break; case v_mode: + USED_REX (REX_W); if ((sizeflag & DFLAG) || (rex & REX_W)) disp = get32s (); else @@ -12462,7 +12550,8 @@ OP_J (int bytemode, int sizeflag) segment = ((start_pc + codep - start_codep) & ~((bfd_vma) 0xffff)); } - used_prefixes |= (prefixes & PREFIX_DATA); + if (!(rex & REX_W)) + used_prefixes |= (prefixes & PREFIX_DATA); break; default: oappend (INTERNAL_DISASSEMBLER_ERROR); @@ -12653,7 +12742,7 @@ OP_C (int dummy ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED) } else if (address_mode != mode_64bit && (prefixes & PREFIX_LOCK)) { - lock_prefix = NULL; + all_prefixes[last_lock_prefix] = 0; used_prefixes |= PREFIX_LOCK; add = 8; } @@ -13092,7 +13181,7 @@ OP_Monitor (int bytemode ATTRIBUTE_UNUSED, else { /* Remove "addr16/addr32". */ - addr_prefix = NULL; + all_prefixes[last_addr_prefix] = 0; op1_names = (address_mode != mode_32bit ? names32 : names16); used_prefixes |= PREFIX_ADDR; @@ -13121,7 +13210,7 @@ REP_Fixup (int bytemode, int sizeflag) /* The 0xf3 prefix should be displayed as "rep" for ins, outs, movs, lods and stos. */ if (prefixes & PREFIX_REPZ) - repz_prefix = "rep "; + all_prefixes[last_repz_prefix] = REP_PREFIX; switch (bytemode) { @@ -13199,11 +13288,14 @@ CRC32_Fixup (int bytemode, int sizeflag) USED_REX (REX_W); if (rex & REX_W) *p++ = 'q'; - else if (sizeflag & DFLAG) - *p++ = 'l'; - else - *p++ = 'w'; - used_prefixes |= (prefixes & PREFIX_DATA); + else + { + if (sizeflag & DFLAG) + *p++ = 'l'; + else + *p++ = 'w'; + used_prefixes |= (prefixes & PREFIX_DATA); + } break; default: oappend (INTERNAL_DISASSEMBLER_ERROR); @@ -13636,12 +13728,15 @@ MOVBE_Fixup (int bytemode, int sizeflag) { if (rex & REX_W) *p++ = 'q'; - else if (sizeflag & DFLAG) - *p++ = 'l'; else - *p++ = 'w'; + { + if (sizeflag & DFLAG) + *p++ = 'l'; + else + *p++ = 'w'; + used_prefixes |= (prefixes & PREFIX_DATA); + } } - used_prefixes |= (prefixes & PREFIX_DATA); break; default: oappend (INTERNAL_DISASSEMBLER_ERROR); |