diff options
author | H. Peter Anvin <hpa@zytor.com> | 2007-11-12 21:57:00 -0800 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2007-11-12 21:57:00 -0800 |
commit | e8cdcdcc37d9a701b47a9fc129c912c3b0f9ba68 (patch) | |
tree | db88f3b6933cf2dc567b16d25a66c33e0e867b7b | |
parent | 2344010d26266705c541809f7d73f25bf3bae643 (diff) | |
download | nasm-e8cdcdcc37d9a701b47a9fc129c912c3b0f9ba68.tar.gz nasm-e8cdcdcc37d9a701b47a9fc129c912c3b0f9ba68.tar.bz2 nasm-e8cdcdcc37d9a701b47a9fc129c912c3b0f9ba68.zip |
Better (but not *good!*) handling of 64-bit addressing in ndisasm
More correct -- but not fully correct -- handing of 64-bit addressing
in ndisasm. In particular, we need to generate "a32" versus "dword"
where applicable.
-rw-r--r-- | disasm.c | 366 |
1 files changed, 269 insertions, 97 deletions
@@ -335,7 +335,7 @@ static uint8_t *do_ea(uint8_t *data, int modrm, int asize, else op->basereg = rd_reg32[base | ((rex & REX_B) ? 8 : 0)]; - if (segsize != 32) + if (segsize == 16) op->disp_size = 32; } @@ -362,6 +362,8 @@ static uint8_t *do_ea(uint8_t *data, int modrm, int asize, * Determine whether the instruction template in t corresponds to the data * stream in data. Return the number of bytes matched if so. */ +#define case4(x) case (x): case (x)+1: case (x)+2: case (x)+3 + static int matches(const struct itemplate *t, uint8_t *data, const struct prefix_info *prefix, int segsize, insn *ins) { @@ -372,7 +374,7 @@ static int matches(const struct itemplate *t, uint8_t *data, uint8_t lock = prefix->lock; int osize = prefix->osize; int asize = prefix->asize; - int i; + int i, c; for (i = 0; i < MAX_OPERANDS; i++) { ins->oprs[i].segment = ins->oprs[i].disp_size = @@ -389,15 +391,17 @@ static int matches(const struct itemplate *t, uint8_t *data, else if (prefix->rep == 0xF3) drep = P_REP; - while (*r) { - int c = *r++; - - /* FIX: change this into a switch */ - if (c >= 01 && c <= 03) { + while ((c = *r++) != 0) { + switch (c) { + case 01: + case 02: + case 03: while (c--) if (*r++ != *data++) return false; - } else if (c == 04) { + break; + + case 04: switch (*data++) { case 0x07: ins->oprs[0].basereg = 0; @@ -411,7 +415,9 @@ static int matches(const struct itemplate *t, uint8_t *data, default: return false; } - } else if (c == 05) { + break; + + case 05: switch (*data++) { case 0xA1: ins->oprs[0].basereg = 4; @@ -422,7 +428,9 @@ static int matches(const struct itemplate *t, uint8_t *data, default: return false; } - } else if (c == 06) { + break; + + case 06: switch (*data++) { case 0x06: ins->oprs[0].basereg = 0; @@ -439,7 +447,9 @@ static int matches(const struct itemplate *t, uint8_t *data, default: return false; } - } else if (c == 07) { + break; + + case 07: switch (*data++) { case 0xA0: ins->oprs[0].basereg = 4; @@ -450,7 +460,10 @@ static int matches(const struct itemplate *t, uint8_t *data, default: return false; } - } else if (c >= 010 && c <= 013) { + break; + + case4(010): + { int t = *r++, d = *data++; if (d < t || d > t + 7) return false; @@ -459,17 +472,28 @@ static int matches(const struct itemplate *t, uint8_t *data, (ins->rex & REX_B ? 8 : 0); ins->oprs[c - 010].segment |= SEG_RMREG; } - } else if (c >= 014 && c <= 017) { + break; + } + + case4(014): ins->oprs[c - 014].offset = (int8_t)*data++; ins->oprs[c - 014].segment |= SEG_SIGNED; - } else if (c >= 020 && c <= 023) { + break; + + case4(020): ins->oprs[c - 020].offset = *data++; - } else if (c >= 024 && c <= 027) { + break; + + case4(024): ins->oprs[c - 024].offset = *data++; - } else if (c >= 030 && c <= 033) { + break; + + case4(030): ins->oprs[c - 030].offset = getu16(data); data += 2; - } else if (c >= 034 && c <= 037) { + break; + + case4(034): if (osize == 32) { ins->oprs[c - 034].offset = getu32(data); data += 4; @@ -479,38 +503,53 @@ static int matches(const struct itemplate *t, uint8_t *data, } if (segsize != asize) ins->oprs[c - 034].disp_size = asize; - } else if (c >= 040 && c <= 043) { + break; + + case4(040): ins->oprs[c - 040].offset = getu32(data); data += 4; - } else if (c >= 044 && c <= 047) { + break; + + case4(044): switch (asize) { case 16: ins->oprs[c - 044].offset = getu16(data); data += 2; + if (segsize != 16) + ins->oprs[c - 044].disp_size = 16; break; case 32: ins->oprs[c - 044].offset = getu32(data); data += 4; + if (segsize == 16) + ins->oprs[c - 044].disp_size = 32; break; case 64: ins->oprs[c - 044].offset = getu64(data); + ins->oprs[c - 044].disp_size = 64; data += 8; break; } - if (segsize != asize) - ins->oprs[c - 044].disp_size = asize; - } else if (c >= 050 && c <= 053) { + break; + + case4(050): ins->oprs[c - 050].offset = gets8(data++); ins->oprs[c - 050].segment |= SEG_RELATIVE; - } else if (c >= 054 && c <= 057) { + break; + + case4(054): ins->oprs[c - 054].offset = getu64(data); data += 8; - } else if (c >= 060 && c <= 063) { + break; + + case4(060): ins->oprs[c - 060].offset = gets16(data); data += 2; ins->oprs[c - 060].segment |= SEG_RELATIVE; ins->oprs[c - 060].segment &= ~SEG_32BIT; - } else if (c >= 064 && c <= 067) { + break; + + case4(064): ins->oprs[c - 064].segment |= SEG_RELATIVE; if (osize == 16) { ins->oprs[c - 064].offset = getu16(data); @@ -527,12 +566,20 @@ static int matches(const struct itemplate *t, uint8_t *data, (ins->oprs[c - 064].type & ~SIZE_MASK) | ((osize == 16) ? BITS16 : BITS32); } - } else if (c >= 070 && c <= 073) { + break; + + case4(070): ins->oprs[c - 070].offset = getu32(data); data += 4; ins->oprs[c - 070].segment |= SEG_32BIT | SEG_RELATIVE; - } else if (c >= 0100 && c < 0140) { - int modrm = *data++; + break; + + case4(0100): + case4(0110): + case4(0120): + case4(0130): + { + int modrm = *data++; ins->oprs[c & 07].segment |= SEG_RMREG; data = do_ea(data, modrm, asize, segsize, &ins->oprs[(c >> 3) & 07], ins); @@ -540,23 +587,49 @@ static int matches(const struct itemplate *t, uint8_t *data, return false; ins->oprs[c & 07].basereg = ((modrm >> 3)&7)+ (ins->rex & REX_R ? 8 : 0); - } else if (c >= 0140 && c <= 0143) { + break; + } + + case4(0140): ins->oprs[c - 0140].offset = getu16(data); data += 2; - } else if (c >= 0150 && c <= 0153) { + break; + + case4(0150): ins->oprs[c - 0150].offset = getu32(data); data += 4; - } else if (c >= 0160 && c <= 0167) { - ins->rex |= (c & 4) ? REX_D|REX_OC : REX_D; + break; + + case4(0160): + ins->rex |= REX_D; + ins->drexdst = c & 3; + break; + + case4(0164): + ins->rex |= REX_D|REX_OC; ins->drexdst = c & 3; - } else if (c == 0170) { + break; + + case 0170: if (*data++) return false; - } else if (c == 0171) { + break; + + case 0171: data = do_drex(data, ins); if (!data) return false; - } else if (c >= 0200 && c <= 0277) { + break; + + case4(0200): + case4(0204): + case4(0210): + case4(0214): + case4(0220): + case4(0224): + case4(0230): + case4(0234): + { int modrm = *data++; if (((modrm >> 3) & 07) != (c & 07)) return false; /* spare field doesn't match up */ @@ -564,97 +637,153 @@ static int matches(const struct itemplate *t, uint8_t *data, &ins->oprs[(c >> 3) & 07], ins); if (!data) return false; - } else if (c == 0310) { + break; + } + + case 0310: if (asize != 16) return false; else a_used = true; - } else if (c == 0311) { + break; + + case 0311: if (asize == 16) return false; else a_used = true; - } else if (c == 0312) { + break; + + case 0312: if (asize != segsize) return false; else a_used = true; - } else if (c == 0313) { + break; + + case 0313: if (asize != 64) return false; else a_used = true; - } else if (c == 0314) { + break; + + case 0314: if (prefix->rex & REX_B) return false; - } else if (c == 0315) { + break; + + case 0315: if (prefix->rex & REX_X) return false; - } else if (c == 0316) { + break; + + case 0316: if (prefix->rex & REX_R) return false; - } else if (c == 0317) { + break; + + case 0317: if (prefix->rex & REX_W) return false; - } else if (c == 0320) { + break; + + case 0320: if (osize != 16) return false; else o_used = true; - } else if (c == 0321) { + break; + + case 0321: if (osize != 32) return false; else o_used = true; - } else if (c == 0322) { + break; + + case 0322: if (osize != (segsize == 16) ? 16 : 32) return false; else o_used = true; - } else if (c == 0323) { + break; + + case 0323: ins->rex |= REX_W; /* 64-bit only instruction */ osize = 64; - } else if (c == 0324) { + break; + + case 0324: if (!(ins->rex & (REX_P|REX_W)) || osize != 64) return false; - } else if (c == 0330) { + break; + + case 0330: + { int t = *r++, d = *data++; if (d < t || d > t + 15) return false; else ins->condition = d - t; - } else if (c == 0331) { + break; + } + + case 0331: if (prefix->rep) return false; - } else if (c == 0332) { + break; + + case 0332: if (prefix->rep != 0xF2) return false; - } else if (c == 0333) { + break; + + case 0333: if (prefix->rep != 0xF3) return false; drep = 0; - } else if (c == 0334) { + break; + + case 0334: if (lock) { ins->rex |= REX_R; lock = 0; } - } else if (c == 0335) { + break; + + case 0335: if (drep == P_REP) drep = P_REPE; - } else if (c == 0364) { + break; + + case 0340: + return false; + + case 0364: if (prefix->osp) return false; - } else if (c == 0365) { + break; + + case 0365: if (prefix->asp) return false; - } else if (c == 0366) { + break; + + case 0366: if (!prefix->osp) return false; o_used = true; - } else if (c == 0367) { + break; + + case 0367: if (!prefix->asp) return false; o_used = true; + break; + + default: + return false; /* Unknown code */ } } @@ -711,6 +840,7 @@ int32_t disasm(uint8_t *data, char *output, int outbufsize, int segsize, uint32_t goodness, best; int best_pref; struct prefix_info prefix; + bool end_prefix; memset(&ins, 0, sizeof ins); @@ -722,36 +852,51 @@ int32_t disasm(uint8_t *data, char *output, int outbufsize, int segsize, prefix.osize = (segsize == 64) ? 32 : segsize; segover = NULL; origdata = data; - for (;;) { - if (*data == 0xF3 || *data == 0xF2) + + for (end_prefix = false; !end_prefix; ) { + switch (*data) { + case 0xF2: + case 0xF3: prefix.rep = *data++; - else if (*data == 0xF0) + break; + case 0xF0: prefix.lock = *data++; - else if (*data == 0x2E) + break; + case 0x2E: segover = "cs", prefix.seg = *data++; - else if (*data == 0x36) + break; + case 0x36: segover = "ss", prefix.seg = *data++; - else if (*data == 0x3E) + break; + case 0x3E: segover = "ds", prefix.seg = *data++; - else if (*data == 0x26) + break; + case 0x26: segover = "es", prefix.seg = *data++; - else if (*data == 0x64) + break; + case 0x64: segover = "fs", prefix.seg = *data++; - else if (*data == 0x65) + break; + case 0x65: segover = "gs", prefix.seg = *data++; - else if (*data == 0x66) { + break; + case 0x66: prefix.osize = (segsize == 16) ? 32 : 16; prefix.osp = *data++; - } else if (*data == 0x67) { + break; + case 0x67: prefix.asize = (segsize == 32) ? 16 : 32; prefix.asp = *data++; - } else if (segsize == 64 && (*data & 0xf0) == REX_P) { - prefix.rex = *data++; - if (prefix.rex & REX_W) - prefix.osize = 64; - break; /* REX is always the last prefix */ - } else { - break; + break; + default: + if (segsize == 64 && (*data & 0xf0) == REX_P) { + prefix.rex = *data++; + if (prefix.rex & REX_W) + prefix.osize = 64; + end_prefix = true; + } else { + end_prefix = true; + } } } @@ -776,14 +921,17 @@ int32_t disasm(uint8_t *data, char *output, int outbufsize, int segsize, for (i = 0; i < (*p)->operands; i++) { if (!((*p)->opd[i] & SAME_AS) && ( - /* If it's a mem-only EA but we have a register, die. */ + /* If it's a mem-only EA but we have a + register, die. */ ((tmp_ins.oprs[i].segment & SEG_RMREG) && !(MEMORY & ~(*p)->opd[i])) || - /* If it's a reg-only EA but we have a memory ref, die. */ + /* If it's a reg-only EA but we have a memory + ref, die. */ (!(tmp_ins.oprs[i].segment & SEG_RMREG) && !(REG_EA & ~(*p)->opd[i]) && !((*p)->opd[i] & REG_SMASK)) || - /* Register type mismatch (eg FS vs REG_DESS): die. */ + /* Register type mismatch (eg FS vs REG_DESS): + die. */ ((((*p)->opd[i] & (REGISTER | FPUREG)) || (tmp_ins.oprs[i].segment & SEG_RMREG)) && !whichreg((*p)->opd[i], @@ -955,12 +1103,13 @@ int32_t disasm(uint8_t *data, char *output, int outbufsize, int segsize, offs); } else if (!(MEM_OFFS & ~t)) { slen += - snprintf(output + slen, outbufsize - slen, "[%s%s%s0x%"PRIx64"]", + snprintf(output + slen, outbufsize - slen, + "[%s%s%s0x%"PRIx64"]", (segover ? segover : ""), (segover ? ":" : ""), - (o->disp_size == - 32 ? "dword " : o->disp_size == - 16 ? "word " : ""), offs); + (o->disp_size == 64 ? "qword " : + o->disp_size == 32 ? "dword " : + o->disp_size == 16 ? "word " : ""), offs); segover = NULL; } else if (!(REGMEM & ~t)) { int started = false; @@ -979,6 +1128,9 @@ int32_t disasm(uint8_t *data, char *output, int outbufsize, int segsize, if (t & BITS80) slen += snprintf(output + slen, outbufsize - slen, "tword "); + if (t & BITS128) + slen += + snprintf(output + slen, outbufsize - slen, "oword "); if (t & FAR) slen += snprintf(output + slen, outbufsize - slen, "far "); if (t & NEAR) @@ -1017,30 +1169,49 @@ int32_t disasm(uint8_t *data, char *output, int outbufsize, int segsize, o->scale); started = true; } + + if (o->segment & SEG_DISP8) { - int minus = 0; - int8_t offset = offs; - if (offset < 0) { - minus = 1; + const char *prefix; + uint8_t offset = offs; + if ((int8_t)offset < 0) { + prefix = "-"; offset = -offset; + } else { + prefix = "+"; } slen += snprintf(output + slen, outbufsize - slen, "%s0x%"PRIx8"", - minus ? "-" : "+", offset); + prefix, offset); } else if (o->segment & SEG_DISP16) { - int minus = 0; - int16_t offset = offs; - if (offset < 0) { - minus = 1; + const char *prefix; + uint16_t offset = offs; + if ((int16_t)offset < 0 && started) { offset = -offset; + prefix = "-"; + } else { + prefix = started ? "+" : ""; } slen += - snprintf(output + slen, outbufsize - slen, "%s0x%"PRIx16"", - minus ? "-" : started ? "+" : "", offset); + snprintf(output + slen, outbufsize - slen, + "%s0x%"PRIx16"", prefix, offset); } else if (o->segment & SEG_DISP32) { - char *prefix = ""; - int32_t offset = offs; - if (offset < 0) { + if (prefix.asize == 64) { + const char *prefix; + uint64_t offset = (int64_t)(int32_t)offs; + if ((int32_t)offs < 0 && started) { + offset = -offset; + prefix = "-"; + } else { + prefix = started ? "+" : ""; + } + slen += + snprintf(output + slen, outbufsize - slen, + "%s0x%"PRIx64"", prefix, offset); + } else { + const char *prefix; + uint32_t offset = offs; + if ((int32_t) offset < 0 && started) { offset = -offset; prefix = "-"; } else { @@ -1049,6 +1220,7 @@ int32_t disasm(uint8_t *data, char *output, int outbufsize, int segsize, slen += snprintf(output + slen, outbufsize - slen, "%s0x%"PRIx32"", prefix, offset); + } } output[slen++] = ']'; } else { |