diff --git a/libyasm/expr.c b/libyasm/expr.c index 63604b90..4c2a6dc4 100644 --- a/libyasm/expr.c +++ b/libyasm/expr.c @@ -761,9 +761,9 @@ expr_checkea_get_reg32(ExprItem *ei, void *d) return ret; } -typedef struct checkea_invalid16_data { +typedef struct checkea_reg16_data { int bx, si, di, bp; /* total multiplier for each reg */ -} checkea_invalid16_data; +} checkea_reg16_data; /* Only works if ei->type == EXPR_REG (doesn't check). * Overwrites ei with intnum of 0 (to eliminate regs from the final expr). @@ -771,7 +771,7 @@ typedef struct checkea_invalid16_data { static int * expr_checkea_get_reg16(ExprItem *ei, void *d) { - checkea_invalid16_data *data = d; + checkea_reg16_data *data = d; /* in order: ax,cx,dx,bx,sp,bp,si,di */ static int *reg16[8] = {0,0,0,0,0,0,0,0}; int *ret; @@ -910,7 +910,7 @@ expr_checkea_distcheck_reg(expr **ep) * where the [...] parts are optional. * * Don't simplify out constant identities if we're looking for an indexreg: we - * need the multiplier for determining what the indexreg is! + * may need the multiplier for determining what the indexreg is! * * Returns 0 if invalid register usage, 1 if unable to determine all values, * and 2 if all values successfully determined and saved in data. @@ -928,7 +928,6 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, e = *ep; switch (expr_checkea_distcheck_reg(ep)) { case 0: - ErrorAt(e->filename, e->line, _("invalid effective address")); return 0; case 2: /* Need to simplify again */ @@ -968,6 +967,8 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, if (!reg) return 0; (*reg)++; + if (indexreg) + *indexreg = reg-(int *)data; } else if (e->terms[i].type == EXPR_EXPR) { /* Already ordered from ADD above, just grab the value. * Sanity check for EXPR_INT. @@ -982,8 +983,7 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, (*reg) += intnum_get_int(e->terms[i].data.expn->terms[1].data.intn); if (indexreg) - *indexreg = - e->terms[i].data.expn->terms[0].data.reg.num; + *indexreg = reg-(int *)data; } } break; @@ -998,6 +998,8 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, if (!reg) return 0; (*reg) += intnum_get_int(e->terms[1].data.intn); + if (indexreg) + *indexreg = reg-(int *)data; break; default: /* Should never get here! */ @@ -1013,6 +1015,123 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, return 2; } +/* Calculate the displacement length, if possible. + * Takes several extra inputs so it can be used by both 32-bit and 16-bit + * expressions: + * wordsize=2 for 16-bit, =4 for 32-bit. + * noreg=1 if the *ModRM byte* has no registers used. + * isbpreg=1 if BP/EBP is the *only* register used within the *ModRM byte*. + */ +static int +checkea_calc_displen(expr **ep, int wordsize, int noreg, int isbpreg, + unsigned char *displen, unsigned char *modrm, + unsigned char *v_modrm) +{ + expr *e = *ep; + const intnum *intn; + long dispval; + + *v_modrm = 0; /* default to not yet valid */ + + switch (*displen) { + case 0: + /* the displacement length hasn't been forced, try to + * determine what it is. + */ + if (noreg) { + /* no register in ModRM expression, so it must be disp16/32, + * and as the Mod bits are set to 0 by the caller, we're done + * with the ModRM byte. + */ + *displen = wordsize; + *v_modrm = 1; + } else if (isbpreg) { + /* for BP/EBP, there *must* be a displacement value, but we + * may not know the size (8 or 16/32) for sure right now. + * We can't leave displen at 0, because that just means + * unknown displacement, including none. + */ + *displen = 0xff; + } + + intn = expr_get_intnum(ep); + if (!intn) + break; /* expr still has unknown values */ + + /* make sure the displacement will fit in 16/32 bits if unsigned, + * and 8 bits if signed. + */ + if (!intnum_check_size(intn, wordsize, 0) && + !intnum_check_size(intn, 1, 1)) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + + /* don't try to find out what size displacement we have if + * displen is known. + */ + if (*displen != 0 && *displen != 0xff) + break; + + /* Don't worry about overflows here (it's already guaranteed + * to be 16/32 or 8 bits). + */ + dispval = intnum_get_int(intn); + + /* Figure out what size displacement we will have. */ + if (*displen != 0xff && dispval == 0) { + /* if we know that the displacement is 0 right now, + * go ahead and delete the expr (making it so no + * displacement value is included in the output). + * The Mod bits of ModRM are set to 0 above, and + * we're done with the ModRM byte! + * + * Don't do this if we came from isbpreg check above, so + * check *displen. + */ + expr_delete(e); + *ep = (expr *)NULL; + } else if (dispval >= -128 && dispval <= 127) { + /* It fits into a signed byte */ + *displen = 1; + *modrm |= 0100; + } else { + /* It's a 16/32-bit displacement */ + *displen = wordsize; + *modrm |= 0200; + } + *v_modrm = 1; /* We're done with ModRM */ + + break; + + /* If not 0, the displacement length was forced; set the Mod bits + * appropriately and we're done with the ModRM byte. We assume + * that the user knows what they're doing if they do an explicit + * override, so we don't check for overflow (we'll just truncate + * when we output). + */ + case 1: + *modrm |= 0100; + *v_modrm = 1; + break; + case 2: + case 4: + if (wordsize != *displen) { + ErrorAt(e->filename, e->line, + _("invalid effective address (displacement size)")); + return 0; + } + *modrm |= 0200; + *v_modrm = 1; + break; + default: + /* we shouldn't ever get any other size! */ + InternalError(_("strange EA displacement size")); + } + + return 1; +} + static int expr_checkea_getregsize_callback(ExprItem *ei, void *d) { @@ -1033,8 +1152,6 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, unsigned char *n_sib) { expr *e = *ep; - const intnum *intn; - long dispval; if (*addrsize == 0) { /* we need to figure out the address size from what we know about: @@ -1066,9 +1183,21 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, } if (*addrsize == 32 && ((*n_modrm && !*v_modrm) || (*n_sib && !*v_sib))) { + int i; + typedef enum { + REG32_NONE = -1, + REG32_EAX = 0, + REG32_ECX = 1, + REG32_EDX = 2, + REG32_EBX = 3, + REG32_ESP = 4, + REG32_EBP = 5, + REG32_ESI = 6, + REG32_EDI = 7 + } reg32type; int reg32mult[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - /*int basereg = 0;*/ /* "base" register (for SIB) */ - int indexreg = 0; /* "index" register (for SIB) */ + int basereg = REG32_NONE; /* "base" register (for SIB) */ + int indexreg = REG32_NONE; /* "index" register (for SIB) */ switch (expr_checkea_getregusage(ep, &indexreg, reg32mult, expr_checkea_get_reg32)) { @@ -1083,6 +1212,153 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, break; } + /* If indexreg mult is 0, discard it. + * This is possible because of the way indexreg is found in + * expr_checkea_getregusage(). + */ + if (indexreg != REG32_NONE && reg32mult[indexreg] == 0) + indexreg = REG32_NONE; + + /* Find a basereg (*1, but not indexreg), if there is one. + * Also, if an indexreg hasn't been assigned, try to find one. + * Meanwhile, check to make sure there's no negative register mults. + */ + for (i=0; i<8; i++) { + if (reg32mult[i] < 0) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + if (i != indexreg && reg32mult[i] == 1) + basereg = i; + else if (indexreg == REG32_NONE && reg32mult[i] > 0) + indexreg = i; + } + + /* Handle certain special cases of indexreg mults when basereg is + * empty. + */ + if (indexreg != REG32_NONE && basereg == REG32_NONE) + switch (reg32mult[indexreg]) { + case 1: + /* Only optimize this way if nosplit wasn't specified */ + if (!nosplit) { + basereg = indexreg; + indexreg = -1; + } + break; + case 2: + /* Only split if nosplit wasn't specified */ + if (!nosplit) { + basereg = indexreg; + reg32mult[indexreg] = 1; + } + break; + case 3: + case 5: + case 9: + basereg = indexreg; + reg32mult[indexreg]--; + break; + } + + /* Make sure there's no other registers than the basereg and indexreg + * we just found. + */ + for (i=0; i<8; i++) + if (i != basereg && i != indexreg && reg32mult[i] != 0) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + + /* Check the index multiplier value for validity if present. */ + if (indexreg != REG32_NONE && reg32mult[indexreg] != 1 && + reg32mult[indexreg] != 2 && reg32mult[indexreg] != 4 && + reg32mult[indexreg] != 8) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + + /* ESP is not a legal indexreg. */ + if (indexreg == REG32_ESP) { + /* If mult>1 or basereg is ESP also, there's no way to make it + * legal. + */ + if (reg32mult[REG32_ESP] > 1 || basereg == REG32_ESP) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + /* If mult==1 and basereg is not ESP, swap indexreg w/basereg. */ + indexreg = basereg; + basereg = REG32_ESP; + } + + /* At this point, we know the base and index registers and that the + * memory expression is (essentially) valid. Now build the ModRM and + * (optional) SIB bytes. + */ + + /* First determine R/M (Mod is later determined from disp size) */ + *n_modrm = 1; /* we always need ModRM */ + if (basereg == REG32_NONE) { + /* disp32[index] */ + *modrm |= 5; + /* we must have a SIB */ + *n_sib = 1; + } else if (indexreg == REG32_NONE) { + /* basereg only */ + *modrm |= basereg; + /* we don't need an SIB *unless* basereg is ESP */ + if (basereg == REG32_ESP) + *n_sib = 1; + else { + *sib = 0; + *v_sib = 0; + *n_sib = 0; + } + } else { + /* both base AND index */ + *modrm |= 4; + *n_sib = 1; + } + + /* Determine SIB if needed */ + if (*n_sib == 1) { + *sib = 0; /* start with 0 */ + + /* Special case: no basereg (only happens in disp32[index] case) */ + if (basereg == REG32_NONE) + *sib |= 5; + else + *sib |= basereg & 7; /* &7 to sanity check */ + + /* Put in indexreg, checking for none case */ + if (indexreg == REG32_NONE) + *sib |= 040; + /* Any scale field is valid, just leave at 0. */ + else { + *sib |= (indexreg & 7) << 3; /* &7 to sanity check */ + /* Set scale field, 1 case -> 0, so don't bother. */ + switch (reg32mult[indexreg]) { + case 2: + *sib |= 0100; + break; + case 4: + *sib |= 0200; + break; + case 8: + *sib |= 0300; + break; + } + } + + *v_sib = 1; /* Done with SIB */ + } + + /* Calculate displacement length (if possible) */ + return checkea_calc_displen(ep, 4, basereg == REG32_NONE, + basereg == REG32_EBP && + indexreg == REG32_NONE, displen, modrm, + v_modrm); } else if (*addrsize == 16 && *n_modrm && !*v_modrm) { static const unsigned char modrm16[16] = { 0006 /* disp16 */, 0007 /* [BX] */, 0004 /* [SI] */, @@ -1092,7 +1368,7 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, 0003 /* [BP+DI] */, 0377 /* invalid */, 0377 /* invalid */, 0377 /* invalid */ }; - checkea_invalid16_data reg16mult = {0, 0, 0, 0}; + checkea_reg16_data reg16mult = {0, 0, 0, 0}; enum { HAVE_NONE = 0, HAVE_BX = 1<<0, @@ -1142,107 +1418,13 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, return 0; } + /* Set ModRM byte for registers */ *modrm |= modrm16[havereg]; - *v_modrm = 0; /* default to not yet valid */ - - switch (*displen) { - case 0: - /* the displacement length hasn't been forced, try to - * determine what it is. - */ - switch (havereg) { - case HAVE_NONE: - /* no register in expression, so it must be disp16, and - * as the Mod bits are set to 0 above, we're done with - * the ModRM byte. - */ - *displen = 2; - *v_modrm = 1; - break; - case HAVE_BP: - /* for BP, there *must* be a displacement value, but we - * may not know the size (8 or 16) for sure right now. - * We can't leave displen at 0, because that just means - * unknown displacement, including none. - */ - *displen = 0xff; - break; - default: - break; - } - - intn = expr_get_intnum(ep); - if (!intn) - break; /* expr still has unknown values */ - - /* make sure the displacement will fit in 16 bits if unsigned, - * and 8 bits if signed. - */ - if (!intnum_check_size(intn, 2, 0) && - !intnum_check_size(intn, 1, 1)) { - ErrorAt(e->filename, e->line, - _("invalid effective address")); - return 0; - } - - /* don't try to find out what size displacement we have if - * displen is known. - */ - if (*displen != 0 && *displen != 0xff) - break; - - /* Don't worry about overflows here (it's already guaranteed - * to be 16 or 8 bits). - */ - dispval = intnum_get_int(intn); - - /* Figure out what size displacement we will have. */ - if (*displen != 0xff && dispval == 0) { - /* if we know that the displacement is 0 right now, - * go ahead and delete the expr (making it so no - * displacement value is included in the output). - * The Mod bits of ModRM are set to 0 above, and - * we're done with the ModRM byte! - * - * Don't do this if we came from HAVE_BP above, so - * check *displen. - */ - expr_delete(e); - *ep = (expr *)NULL; - } else if (dispval >= -128 && dispval <= 127) { - /* It fits into a signed byte */ - *displen = 1; - *modrm |= 0100; - } else { - /* It's a 16-bit displacement */ - *displen = 2; - *modrm |= 0200; - } - *v_modrm = 1; /* We're done with ModRM */ - - break; - - /* If not 0, the displacement length was forced; set the Mod bits - * appropriately and we're done with the ModRM byte. We assume - * that the user knows what they're doing if they do an explicit - * override, so we don't check for overflow (we'll just truncate - * when we output). - */ - case 1: - *modrm |= 0100; - *v_modrm = 1; - break; - case 2: - *modrm |= 0200; - *v_modrm = 1; - break; - default: - /* any other size is an error */ - ErrorAt(e->filename, e->line, - _("invalid effective address (displacement size)")); - return 0; - } + /* Calculate displacement length (if possible) */ + return checkea_calc_displen(ep, 2, havereg == HAVE_NONE, + havereg == HAVE_BP, displen, modrm, + v_modrm); } return 1; } diff --git a/modules/arch/x86/expr.c b/modules/arch/x86/expr.c index 63604b90..4c2a6dc4 100644 --- a/modules/arch/x86/expr.c +++ b/modules/arch/x86/expr.c @@ -761,9 +761,9 @@ expr_checkea_get_reg32(ExprItem *ei, void *d) return ret; } -typedef struct checkea_invalid16_data { +typedef struct checkea_reg16_data { int bx, si, di, bp; /* total multiplier for each reg */ -} checkea_invalid16_data; +} checkea_reg16_data; /* Only works if ei->type == EXPR_REG (doesn't check). * Overwrites ei with intnum of 0 (to eliminate regs from the final expr). @@ -771,7 +771,7 @@ typedef struct checkea_invalid16_data { static int * expr_checkea_get_reg16(ExprItem *ei, void *d) { - checkea_invalid16_data *data = d; + checkea_reg16_data *data = d; /* in order: ax,cx,dx,bx,sp,bp,si,di */ static int *reg16[8] = {0,0,0,0,0,0,0,0}; int *ret; @@ -910,7 +910,7 @@ expr_checkea_distcheck_reg(expr **ep) * where the [...] parts are optional. * * Don't simplify out constant identities if we're looking for an indexreg: we - * need the multiplier for determining what the indexreg is! + * may need the multiplier for determining what the indexreg is! * * Returns 0 if invalid register usage, 1 if unable to determine all values, * and 2 if all values successfully determined and saved in data. @@ -928,7 +928,6 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, e = *ep; switch (expr_checkea_distcheck_reg(ep)) { case 0: - ErrorAt(e->filename, e->line, _("invalid effective address")); return 0; case 2: /* Need to simplify again */ @@ -968,6 +967,8 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, if (!reg) return 0; (*reg)++; + if (indexreg) + *indexreg = reg-(int *)data; } else if (e->terms[i].type == EXPR_EXPR) { /* Already ordered from ADD above, just grab the value. * Sanity check for EXPR_INT. @@ -982,8 +983,7 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, (*reg) += intnum_get_int(e->terms[i].data.expn->terms[1].data.intn); if (indexreg) - *indexreg = - e->terms[i].data.expn->terms[0].data.reg.num; + *indexreg = reg-(int *)data; } } break; @@ -998,6 +998,8 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, if (!reg) return 0; (*reg) += intnum_get_int(e->terms[1].data.intn); + if (indexreg) + *indexreg = reg-(int *)data; break; default: /* Should never get here! */ @@ -1013,6 +1015,123 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, return 2; } +/* Calculate the displacement length, if possible. + * Takes several extra inputs so it can be used by both 32-bit and 16-bit + * expressions: + * wordsize=2 for 16-bit, =4 for 32-bit. + * noreg=1 if the *ModRM byte* has no registers used. + * isbpreg=1 if BP/EBP is the *only* register used within the *ModRM byte*. + */ +static int +checkea_calc_displen(expr **ep, int wordsize, int noreg, int isbpreg, + unsigned char *displen, unsigned char *modrm, + unsigned char *v_modrm) +{ + expr *e = *ep; + const intnum *intn; + long dispval; + + *v_modrm = 0; /* default to not yet valid */ + + switch (*displen) { + case 0: + /* the displacement length hasn't been forced, try to + * determine what it is. + */ + if (noreg) { + /* no register in ModRM expression, so it must be disp16/32, + * and as the Mod bits are set to 0 by the caller, we're done + * with the ModRM byte. + */ + *displen = wordsize; + *v_modrm = 1; + } else if (isbpreg) { + /* for BP/EBP, there *must* be a displacement value, but we + * may not know the size (8 or 16/32) for sure right now. + * We can't leave displen at 0, because that just means + * unknown displacement, including none. + */ + *displen = 0xff; + } + + intn = expr_get_intnum(ep); + if (!intn) + break; /* expr still has unknown values */ + + /* make sure the displacement will fit in 16/32 bits if unsigned, + * and 8 bits if signed. + */ + if (!intnum_check_size(intn, wordsize, 0) && + !intnum_check_size(intn, 1, 1)) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + + /* don't try to find out what size displacement we have if + * displen is known. + */ + if (*displen != 0 && *displen != 0xff) + break; + + /* Don't worry about overflows here (it's already guaranteed + * to be 16/32 or 8 bits). + */ + dispval = intnum_get_int(intn); + + /* Figure out what size displacement we will have. */ + if (*displen != 0xff && dispval == 0) { + /* if we know that the displacement is 0 right now, + * go ahead and delete the expr (making it so no + * displacement value is included in the output). + * The Mod bits of ModRM are set to 0 above, and + * we're done with the ModRM byte! + * + * Don't do this if we came from isbpreg check above, so + * check *displen. + */ + expr_delete(e); + *ep = (expr *)NULL; + } else if (dispval >= -128 && dispval <= 127) { + /* It fits into a signed byte */ + *displen = 1; + *modrm |= 0100; + } else { + /* It's a 16/32-bit displacement */ + *displen = wordsize; + *modrm |= 0200; + } + *v_modrm = 1; /* We're done with ModRM */ + + break; + + /* If not 0, the displacement length was forced; set the Mod bits + * appropriately and we're done with the ModRM byte. We assume + * that the user knows what they're doing if they do an explicit + * override, so we don't check for overflow (we'll just truncate + * when we output). + */ + case 1: + *modrm |= 0100; + *v_modrm = 1; + break; + case 2: + case 4: + if (wordsize != *displen) { + ErrorAt(e->filename, e->line, + _("invalid effective address (displacement size)")); + return 0; + } + *modrm |= 0200; + *v_modrm = 1; + break; + default: + /* we shouldn't ever get any other size! */ + InternalError(_("strange EA displacement size")); + } + + return 1; +} + static int expr_checkea_getregsize_callback(ExprItem *ei, void *d) { @@ -1033,8 +1152,6 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, unsigned char *n_sib) { expr *e = *ep; - const intnum *intn; - long dispval; if (*addrsize == 0) { /* we need to figure out the address size from what we know about: @@ -1066,9 +1183,21 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, } if (*addrsize == 32 && ((*n_modrm && !*v_modrm) || (*n_sib && !*v_sib))) { + int i; + typedef enum { + REG32_NONE = -1, + REG32_EAX = 0, + REG32_ECX = 1, + REG32_EDX = 2, + REG32_EBX = 3, + REG32_ESP = 4, + REG32_EBP = 5, + REG32_ESI = 6, + REG32_EDI = 7 + } reg32type; int reg32mult[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - /*int basereg = 0;*/ /* "base" register (for SIB) */ - int indexreg = 0; /* "index" register (for SIB) */ + int basereg = REG32_NONE; /* "base" register (for SIB) */ + int indexreg = REG32_NONE; /* "index" register (for SIB) */ switch (expr_checkea_getregusage(ep, &indexreg, reg32mult, expr_checkea_get_reg32)) { @@ -1083,6 +1212,153 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, break; } + /* If indexreg mult is 0, discard it. + * This is possible because of the way indexreg is found in + * expr_checkea_getregusage(). + */ + if (indexreg != REG32_NONE && reg32mult[indexreg] == 0) + indexreg = REG32_NONE; + + /* Find a basereg (*1, but not indexreg), if there is one. + * Also, if an indexreg hasn't been assigned, try to find one. + * Meanwhile, check to make sure there's no negative register mults. + */ + for (i=0; i<8; i++) { + if (reg32mult[i] < 0) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + if (i != indexreg && reg32mult[i] == 1) + basereg = i; + else if (indexreg == REG32_NONE && reg32mult[i] > 0) + indexreg = i; + } + + /* Handle certain special cases of indexreg mults when basereg is + * empty. + */ + if (indexreg != REG32_NONE && basereg == REG32_NONE) + switch (reg32mult[indexreg]) { + case 1: + /* Only optimize this way if nosplit wasn't specified */ + if (!nosplit) { + basereg = indexreg; + indexreg = -1; + } + break; + case 2: + /* Only split if nosplit wasn't specified */ + if (!nosplit) { + basereg = indexreg; + reg32mult[indexreg] = 1; + } + break; + case 3: + case 5: + case 9: + basereg = indexreg; + reg32mult[indexreg]--; + break; + } + + /* Make sure there's no other registers than the basereg and indexreg + * we just found. + */ + for (i=0; i<8; i++) + if (i != basereg && i != indexreg && reg32mult[i] != 0) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + + /* Check the index multiplier value for validity if present. */ + if (indexreg != REG32_NONE && reg32mult[indexreg] != 1 && + reg32mult[indexreg] != 2 && reg32mult[indexreg] != 4 && + reg32mult[indexreg] != 8) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + + /* ESP is not a legal indexreg. */ + if (indexreg == REG32_ESP) { + /* If mult>1 or basereg is ESP also, there's no way to make it + * legal. + */ + if (reg32mult[REG32_ESP] > 1 || basereg == REG32_ESP) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + /* If mult==1 and basereg is not ESP, swap indexreg w/basereg. */ + indexreg = basereg; + basereg = REG32_ESP; + } + + /* At this point, we know the base and index registers and that the + * memory expression is (essentially) valid. Now build the ModRM and + * (optional) SIB bytes. + */ + + /* First determine R/M (Mod is later determined from disp size) */ + *n_modrm = 1; /* we always need ModRM */ + if (basereg == REG32_NONE) { + /* disp32[index] */ + *modrm |= 5; + /* we must have a SIB */ + *n_sib = 1; + } else if (indexreg == REG32_NONE) { + /* basereg only */ + *modrm |= basereg; + /* we don't need an SIB *unless* basereg is ESP */ + if (basereg == REG32_ESP) + *n_sib = 1; + else { + *sib = 0; + *v_sib = 0; + *n_sib = 0; + } + } else { + /* both base AND index */ + *modrm |= 4; + *n_sib = 1; + } + + /* Determine SIB if needed */ + if (*n_sib == 1) { + *sib = 0; /* start with 0 */ + + /* Special case: no basereg (only happens in disp32[index] case) */ + if (basereg == REG32_NONE) + *sib |= 5; + else + *sib |= basereg & 7; /* &7 to sanity check */ + + /* Put in indexreg, checking for none case */ + if (indexreg == REG32_NONE) + *sib |= 040; + /* Any scale field is valid, just leave at 0. */ + else { + *sib |= (indexreg & 7) << 3; /* &7 to sanity check */ + /* Set scale field, 1 case -> 0, so don't bother. */ + switch (reg32mult[indexreg]) { + case 2: + *sib |= 0100; + break; + case 4: + *sib |= 0200; + break; + case 8: + *sib |= 0300; + break; + } + } + + *v_sib = 1; /* Done with SIB */ + } + + /* Calculate displacement length (if possible) */ + return checkea_calc_displen(ep, 4, basereg == REG32_NONE, + basereg == REG32_EBP && + indexreg == REG32_NONE, displen, modrm, + v_modrm); } else if (*addrsize == 16 && *n_modrm && !*v_modrm) { static const unsigned char modrm16[16] = { 0006 /* disp16 */, 0007 /* [BX] */, 0004 /* [SI] */, @@ -1092,7 +1368,7 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, 0003 /* [BP+DI] */, 0377 /* invalid */, 0377 /* invalid */, 0377 /* invalid */ }; - checkea_invalid16_data reg16mult = {0, 0, 0, 0}; + checkea_reg16_data reg16mult = {0, 0, 0, 0}; enum { HAVE_NONE = 0, HAVE_BX = 1<<0, @@ -1142,107 +1418,13 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, return 0; } + /* Set ModRM byte for registers */ *modrm |= modrm16[havereg]; - *v_modrm = 0; /* default to not yet valid */ - - switch (*displen) { - case 0: - /* the displacement length hasn't been forced, try to - * determine what it is. - */ - switch (havereg) { - case HAVE_NONE: - /* no register in expression, so it must be disp16, and - * as the Mod bits are set to 0 above, we're done with - * the ModRM byte. - */ - *displen = 2; - *v_modrm = 1; - break; - case HAVE_BP: - /* for BP, there *must* be a displacement value, but we - * may not know the size (8 or 16) for sure right now. - * We can't leave displen at 0, because that just means - * unknown displacement, including none. - */ - *displen = 0xff; - break; - default: - break; - } - - intn = expr_get_intnum(ep); - if (!intn) - break; /* expr still has unknown values */ - - /* make sure the displacement will fit in 16 bits if unsigned, - * and 8 bits if signed. - */ - if (!intnum_check_size(intn, 2, 0) && - !intnum_check_size(intn, 1, 1)) { - ErrorAt(e->filename, e->line, - _("invalid effective address")); - return 0; - } - - /* don't try to find out what size displacement we have if - * displen is known. - */ - if (*displen != 0 && *displen != 0xff) - break; - - /* Don't worry about overflows here (it's already guaranteed - * to be 16 or 8 bits). - */ - dispval = intnum_get_int(intn); - - /* Figure out what size displacement we will have. */ - if (*displen != 0xff && dispval == 0) { - /* if we know that the displacement is 0 right now, - * go ahead and delete the expr (making it so no - * displacement value is included in the output). - * The Mod bits of ModRM are set to 0 above, and - * we're done with the ModRM byte! - * - * Don't do this if we came from HAVE_BP above, so - * check *displen. - */ - expr_delete(e); - *ep = (expr *)NULL; - } else if (dispval >= -128 && dispval <= 127) { - /* It fits into a signed byte */ - *displen = 1; - *modrm |= 0100; - } else { - /* It's a 16-bit displacement */ - *displen = 2; - *modrm |= 0200; - } - *v_modrm = 1; /* We're done with ModRM */ - - break; - - /* If not 0, the displacement length was forced; set the Mod bits - * appropriately and we're done with the ModRM byte. We assume - * that the user knows what they're doing if they do an explicit - * override, so we don't check for overflow (we'll just truncate - * when we output). - */ - case 1: - *modrm |= 0100; - *v_modrm = 1; - break; - case 2: - *modrm |= 0200; - *v_modrm = 1; - break; - default: - /* any other size is an error */ - ErrorAt(e->filename, e->line, - _("invalid effective address (displacement size)")); - return 0; - } + /* Calculate displacement length (if possible) */ + return checkea_calc_displen(ep, 2, havereg == HAVE_NONE, + havereg == HAVE_BP, displen, modrm, + v_modrm); } return 1; } diff --git a/modules/arch/x86/x86expr.c b/modules/arch/x86/x86expr.c index 63604b90..4c2a6dc4 100644 --- a/modules/arch/x86/x86expr.c +++ b/modules/arch/x86/x86expr.c @@ -761,9 +761,9 @@ expr_checkea_get_reg32(ExprItem *ei, void *d) return ret; } -typedef struct checkea_invalid16_data { +typedef struct checkea_reg16_data { int bx, si, di, bp; /* total multiplier for each reg */ -} checkea_invalid16_data; +} checkea_reg16_data; /* Only works if ei->type == EXPR_REG (doesn't check). * Overwrites ei with intnum of 0 (to eliminate regs from the final expr). @@ -771,7 +771,7 @@ typedef struct checkea_invalid16_data { static int * expr_checkea_get_reg16(ExprItem *ei, void *d) { - checkea_invalid16_data *data = d; + checkea_reg16_data *data = d; /* in order: ax,cx,dx,bx,sp,bp,si,di */ static int *reg16[8] = {0,0,0,0,0,0,0,0}; int *ret; @@ -910,7 +910,7 @@ expr_checkea_distcheck_reg(expr **ep) * where the [...] parts are optional. * * Don't simplify out constant identities if we're looking for an indexreg: we - * need the multiplier for determining what the indexreg is! + * may need the multiplier for determining what the indexreg is! * * Returns 0 if invalid register usage, 1 if unable to determine all values, * and 2 if all values successfully determined and saved in data. @@ -928,7 +928,6 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, e = *ep; switch (expr_checkea_distcheck_reg(ep)) { case 0: - ErrorAt(e->filename, e->line, _("invalid effective address")); return 0; case 2: /* Need to simplify again */ @@ -968,6 +967,8 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, if (!reg) return 0; (*reg)++; + if (indexreg) + *indexreg = reg-(int *)data; } else if (e->terms[i].type == EXPR_EXPR) { /* Already ordered from ADD above, just grab the value. * Sanity check for EXPR_INT. @@ -982,8 +983,7 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, (*reg) += intnum_get_int(e->terms[i].data.expn->terms[1].data.intn); if (indexreg) - *indexreg = - e->terms[i].data.expn->terms[0].data.reg.num; + *indexreg = reg-(int *)data; } } break; @@ -998,6 +998,8 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, if (!reg) return 0; (*reg) += intnum_get_int(e->terms[1].data.intn); + if (indexreg) + *indexreg = reg-(int *)data; break; default: /* Should never get here! */ @@ -1013,6 +1015,123 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, return 2; } +/* Calculate the displacement length, if possible. + * Takes several extra inputs so it can be used by both 32-bit and 16-bit + * expressions: + * wordsize=2 for 16-bit, =4 for 32-bit. + * noreg=1 if the *ModRM byte* has no registers used. + * isbpreg=1 if BP/EBP is the *only* register used within the *ModRM byte*. + */ +static int +checkea_calc_displen(expr **ep, int wordsize, int noreg, int isbpreg, + unsigned char *displen, unsigned char *modrm, + unsigned char *v_modrm) +{ + expr *e = *ep; + const intnum *intn; + long dispval; + + *v_modrm = 0; /* default to not yet valid */ + + switch (*displen) { + case 0: + /* the displacement length hasn't been forced, try to + * determine what it is. + */ + if (noreg) { + /* no register in ModRM expression, so it must be disp16/32, + * and as the Mod bits are set to 0 by the caller, we're done + * with the ModRM byte. + */ + *displen = wordsize; + *v_modrm = 1; + } else if (isbpreg) { + /* for BP/EBP, there *must* be a displacement value, but we + * may not know the size (8 or 16/32) for sure right now. + * We can't leave displen at 0, because that just means + * unknown displacement, including none. + */ + *displen = 0xff; + } + + intn = expr_get_intnum(ep); + if (!intn) + break; /* expr still has unknown values */ + + /* make sure the displacement will fit in 16/32 bits if unsigned, + * and 8 bits if signed. + */ + if (!intnum_check_size(intn, wordsize, 0) && + !intnum_check_size(intn, 1, 1)) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + + /* don't try to find out what size displacement we have if + * displen is known. + */ + if (*displen != 0 && *displen != 0xff) + break; + + /* Don't worry about overflows here (it's already guaranteed + * to be 16/32 or 8 bits). + */ + dispval = intnum_get_int(intn); + + /* Figure out what size displacement we will have. */ + if (*displen != 0xff && dispval == 0) { + /* if we know that the displacement is 0 right now, + * go ahead and delete the expr (making it so no + * displacement value is included in the output). + * The Mod bits of ModRM are set to 0 above, and + * we're done with the ModRM byte! + * + * Don't do this if we came from isbpreg check above, so + * check *displen. + */ + expr_delete(e); + *ep = (expr *)NULL; + } else if (dispval >= -128 && dispval <= 127) { + /* It fits into a signed byte */ + *displen = 1; + *modrm |= 0100; + } else { + /* It's a 16/32-bit displacement */ + *displen = wordsize; + *modrm |= 0200; + } + *v_modrm = 1; /* We're done with ModRM */ + + break; + + /* If not 0, the displacement length was forced; set the Mod bits + * appropriately and we're done with the ModRM byte. We assume + * that the user knows what they're doing if they do an explicit + * override, so we don't check for overflow (we'll just truncate + * when we output). + */ + case 1: + *modrm |= 0100; + *v_modrm = 1; + break; + case 2: + case 4: + if (wordsize != *displen) { + ErrorAt(e->filename, e->line, + _("invalid effective address (displacement size)")); + return 0; + } + *modrm |= 0200; + *v_modrm = 1; + break; + default: + /* we shouldn't ever get any other size! */ + InternalError(_("strange EA displacement size")); + } + + return 1; +} + static int expr_checkea_getregsize_callback(ExprItem *ei, void *d) { @@ -1033,8 +1152,6 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, unsigned char *n_sib) { expr *e = *ep; - const intnum *intn; - long dispval; if (*addrsize == 0) { /* we need to figure out the address size from what we know about: @@ -1066,9 +1183,21 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, } if (*addrsize == 32 && ((*n_modrm && !*v_modrm) || (*n_sib && !*v_sib))) { + int i; + typedef enum { + REG32_NONE = -1, + REG32_EAX = 0, + REG32_ECX = 1, + REG32_EDX = 2, + REG32_EBX = 3, + REG32_ESP = 4, + REG32_EBP = 5, + REG32_ESI = 6, + REG32_EDI = 7 + } reg32type; int reg32mult[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - /*int basereg = 0;*/ /* "base" register (for SIB) */ - int indexreg = 0; /* "index" register (for SIB) */ + int basereg = REG32_NONE; /* "base" register (for SIB) */ + int indexreg = REG32_NONE; /* "index" register (for SIB) */ switch (expr_checkea_getregusage(ep, &indexreg, reg32mult, expr_checkea_get_reg32)) { @@ -1083,6 +1212,153 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, break; } + /* If indexreg mult is 0, discard it. + * This is possible because of the way indexreg is found in + * expr_checkea_getregusage(). + */ + if (indexreg != REG32_NONE && reg32mult[indexreg] == 0) + indexreg = REG32_NONE; + + /* Find a basereg (*1, but not indexreg), if there is one. + * Also, if an indexreg hasn't been assigned, try to find one. + * Meanwhile, check to make sure there's no negative register mults. + */ + for (i=0; i<8; i++) { + if (reg32mult[i] < 0) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + if (i != indexreg && reg32mult[i] == 1) + basereg = i; + else if (indexreg == REG32_NONE && reg32mult[i] > 0) + indexreg = i; + } + + /* Handle certain special cases of indexreg mults when basereg is + * empty. + */ + if (indexreg != REG32_NONE && basereg == REG32_NONE) + switch (reg32mult[indexreg]) { + case 1: + /* Only optimize this way if nosplit wasn't specified */ + if (!nosplit) { + basereg = indexreg; + indexreg = -1; + } + break; + case 2: + /* Only split if nosplit wasn't specified */ + if (!nosplit) { + basereg = indexreg; + reg32mult[indexreg] = 1; + } + break; + case 3: + case 5: + case 9: + basereg = indexreg; + reg32mult[indexreg]--; + break; + } + + /* Make sure there's no other registers than the basereg and indexreg + * we just found. + */ + for (i=0; i<8; i++) + if (i != basereg && i != indexreg && reg32mult[i] != 0) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + + /* Check the index multiplier value for validity if present. */ + if (indexreg != REG32_NONE && reg32mult[indexreg] != 1 && + reg32mult[indexreg] != 2 && reg32mult[indexreg] != 4 && + reg32mult[indexreg] != 8) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + + /* ESP is not a legal indexreg. */ + if (indexreg == REG32_ESP) { + /* If mult>1 or basereg is ESP also, there's no way to make it + * legal. + */ + if (reg32mult[REG32_ESP] > 1 || basereg == REG32_ESP) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + /* If mult==1 and basereg is not ESP, swap indexreg w/basereg. */ + indexreg = basereg; + basereg = REG32_ESP; + } + + /* At this point, we know the base and index registers and that the + * memory expression is (essentially) valid. Now build the ModRM and + * (optional) SIB bytes. + */ + + /* First determine R/M (Mod is later determined from disp size) */ + *n_modrm = 1; /* we always need ModRM */ + if (basereg == REG32_NONE) { + /* disp32[index] */ + *modrm |= 5; + /* we must have a SIB */ + *n_sib = 1; + } else if (indexreg == REG32_NONE) { + /* basereg only */ + *modrm |= basereg; + /* we don't need an SIB *unless* basereg is ESP */ + if (basereg == REG32_ESP) + *n_sib = 1; + else { + *sib = 0; + *v_sib = 0; + *n_sib = 0; + } + } else { + /* both base AND index */ + *modrm |= 4; + *n_sib = 1; + } + + /* Determine SIB if needed */ + if (*n_sib == 1) { + *sib = 0; /* start with 0 */ + + /* Special case: no basereg (only happens in disp32[index] case) */ + if (basereg == REG32_NONE) + *sib |= 5; + else + *sib |= basereg & 7; /* &7 to sanity check */ + + /* Put in indexreg, checking for none case */ + if (indexreg == REG32_NONE) + *sib |= 040; + /* Any scale field is valid, just leave at 0. */ + else { + *sib |= (indexreg & 7) << 3; /* &7 to sanity check */ + /* Set scale field, 1 case -> 0, so don't bother. */ + switch (reg32mult[indexreg]) { + case 2: + *sib |= 0100; + break; + case 4: + *sib |= 0200; + break; + case 8: + *sib |= 0300; + break; + } + } + + *v_sib = 1; /* Done with SIB */ + } + + /* Calculate displacement length (if possible) */ + return checkea_calc_displen(ep, 4, basereg == REG32_NONE, + basereg == REG32_EBP && + indexreg == REG32_NONE, displen, modrm, + v_modrm); } else if (*addrsize == 16 && *n_modrm && !*v_modrm) { static const unsigned char modrm16[16] = { 0006 /* disp16 */, 0007 /* [BX] */, 0004 /* [SI] */, @@ -1092,7 +1368,7 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, 0003 /* [BP+DI] */, 0377 /* invalid */, 0377 /* invalid */, 0377 /* invalid */ }; - checkea_invalid16_data reg16mult = {0, 0, 0, 0}; + checkea_reg16_data reg16mult = {0, 0, 0, 0}; enum { HAVE_NONE = 0, HAVE_BX = 1<<0, @@ -1142,107 +1418,13 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, return 0; } + /* Set ModRM byte for registers */ *modrm |= modrm16[havereg]; - *v_modrm = 0; /* default to not yet valid */ - - switch (*displen) { - case 0: - /* the displacement length hasn't been forced, try to - * determine what it is. - */ - switch (havereg) { - case HAVE_NONE: - /* no register in expression, so it must be disp16, and - * as the Mod bits are set to 0 above, we're done with - * the ModRM byte. - */ - *displen = 2; - *v_modrm = 1; - break; - case HAVE_BP: - /* for BP, there *must* be a displacement value, but we - * may not know the size (8 or 16) for sure right now. - * We can't leave displen at 0, because that just means - * unknown displacement, including none. - */ - *displen = 0xff; - break; - default: - break; - } - - intn = expr_get_intnum(ep); - if (!intn) - break; /* expr still has unknown values */ - - /* make sure the displacement will fit in 16 bits if unsigned, - * and 8 bits if signed. - */ - if (!intnum_check_size(intn, 2, 0) && - !intnum_check_size(intn, 1, 1)) { - ErrorAt(e->filename, e->line, - _("invalid effective address")); - return 0; - } - - /* don't try to find out what size displacement we have if - * displen is known. - */ - if (*displen != 0 && *displen != 0xff) - break; - - /* Don't worry about overflows here (it's already guaranteed - * to be 16 or 8 bits). - */ - dispval = intnum_get_int(intn); - - /* Figure out what size displacement we will have. */ - if (*displen != 0xff && dispval == 0) { - /* if we know that the displacement is 0 right now, - * go ahead and delete the expr (making it so no - * displacement value is included in the output). - * The Mod bits of ModRM are set to 0 above, and - * we're done with the ModRM byte! - * - * Don't do this if we came from HAVE_BP above, so - * check *displen. - */ - expr_delete(e); - *ep = (expr *)NULL; - } else if (dispval >= -128 && dispval <= 127) { - /* It fits into a signed byte */ - *displen = 1; - *modrm |= 0100; - } else { - /* It's a 16-bit displacement */ - *displen = 2; - *modrm |= 0200; - } - *v_modrm = 1; /* We're done with ModRM */ - - break; - - /* If not 0, the displacement length was forced; set the Mod bits - * appropriately and we're done with the ModRM byte. We assume - * that the user knows what they're doing if they do an explicit - * override, so we don't check for overflow (we'll just truncate - * when we output). - */ - case 1: - *modrm |= 0100; - *v_modrm = 1; - break; - case 2: - *modrm |= 0200; - *v_modrm = 1; - break; - default: - /* any other size is an error */ - ErrorAt(e->filename, e->line, - _("invalid effective address (displacement size)")); - return 0; - } + /* Calculate displacement length (if possible) */ + return checkea_calc_displen(ep, 2, havereg == HAVE_NONE, + havereg == HAVE_BP, displen, modrm, + v_modrm); } return 1; } diff --git a/src/arch/x86/expr.c b/src/arch/x86/expr.c index 63604b90..4c2a6dc4 100644 --- a/src/arch/x86/expr.c +++ b/src/arch/x86/expr.c @@ -761,9 +761,9 @@ expr_checkea_get_reg32(ExprItem *ei, void *d) return ret; } -typedef struct checkea_invalid16_data { +typedef struct checkea_reg16_data { int bx, si, di, bp; /* total multiplier for each reg */ -} checkea_invalid16_data; +} checkea_reg16_data; /* Only works if ei->type == EXPR_REG (doesn't check). * Overwrites ei with intnum of 0 (to eliminate regs from the final expr). @@ -771,7 +771,7 @@ typedef struct checkea_invalid16_data { static int * expr_checkea_get_reg16(ExprItem *ei, void *d) { - checkea_invalid16_data *data = d; + checkea_reg16_data *data = d; /* in order: ax,cx,dx,bx,sp,bp,si,di */ static int *reg16[8] = {0,0,0,0,0,0,0,0}; int *ret; @@ -910,7 +910,7 @@ expr_checkea_distcheck_reg(expr **ep) * where the [...] parts are optional. * * Don't simplify out constant identities if we're looking for an indexreg: we - * need the multiplier for determining what the indexreg is! + * may need the multiplier for determining what the indexreg is! * * Returns 0 if invalid register usage, 1 if unable to determine all values, * and 2 if all values successfully determined and saved in data. @@ -928,7 +928,6 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, e = *ep; switch (expr_checkea_distcheck_reg(ep)) { case 0: - ErrorAt(e->filename, e->line, _("invalid effective address")); return 0; case 2: /* Need to simplify again */ @@ -968,6 +967,8 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, if (!reg) return 0; (*reg)++; + if (indexreg) + *indexreg = reg-(int *)data; } else if (e->terms[i].type == EXPR_EXPR) { /* Already ordered from ADD above, just grab the value. * Sanity check for EXPR_INT. @@ -982,8 +983,7 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, (*reg) += intnum_get_int(e->terms[i].data.expn->terms[1].data.intn); if (indexreg) - *indexreg = - e->terms[i].data.expn->terms[0].data.reg.num; + *indexreg = reg-(int *)data; } } break; @@ -998,6 +998,8 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, if (!reg) return 0; (*reg) += intnum_get_int(e->terms[1].data.intn); + if (indexreg) + *indexreg = reg-(int *)data; break; default: /* Should never get here! */ @@ -1013,6 +1015,123 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, return 2; } +/* Calculate the displacement length, if possible. + * Takes several extra inputs so it can be used by both 32-bit and 16-bit + * expressions: + * wordsize=2 for 16-bit, =4 for 32-bit. + * noreg=1 if the *ModRM byte* has no registers used. + * isbpreg=1 if BP/EBP is the *only* register used within the *ModRM byte*. + */ +static int +checkea_calc_displen(expr **ep, int wordsize, int noreg, int isbpreg, + unsigned char *displen, unsigned char *modrm, + unsigned char *v_modrm) +{ + expr *e = *ep; + const intnum *intn; + long dispval; + + *v_modrm = 0; /* default to not yet valid */ + + switch (*displen) { + case 0: + /* the displacement length hasn't been forced, try to + * determine what it is. + */ + if (noreg) { + /* no register in ModRM expression, so it must be disp16/32, + * and as the Mod bits are set to 0 by the caller, we're done + * with the ModRM byte. + */ + *displen = wordsize; + *v_modrm = 1; + } else if (isbpreg) { + /* for BP/EBP, there *must* be a displacement value, but we + * may not know the size (8 or 16/32) for sure right now. + * We can't leave displen at 0, because that just means + * unknown displacement, including none. + */ + *displen = 0xff; + } + + intn = expr_get_intnum(ep); + if (!intn) + break; /* expr still has unknown values */ + + /* make sure the displacement will fit in 16/32 bits if unsigned, + * and 8 bits if signed. + */ + if (!intnum_check_size(intn, wordsize, 0) && + !intnum_check_size(intn, 1, 1)) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + + /* don't try to find out what size displacement we have if + * displen is known. + */ + if (*displen != 0 && *displen != 0xff) + break; + + /* Don't worry about overflows here (it's already guaranteed + * to be 16/32 or 8 bits). + */ + dispval = intnum_get_int(intn); + + /* Figure out what size displacement we will have. */ + if (*displen != 0xff && dispval == 0) { + /* if we know that the displacement is 0 right now, + * go ahead and delete the expr (making it so no + * displacement value is included in the output). + * The Mod bits of ModRM are set to 0 above, and + * we're done with the ModRM byte! + * + * Don't do this if we came from isbpreg check above, so + * check *displen. + */ + expr_delete(e); + *ep = (expr *)NULL; + } else if (dispval >= -128 && dispval <= 127) { + /* It fits into a signed byte */ + *displen = 1; + *modrm |= 0100; + } else { + /* It's a 16/32-bit displacement */ + *displen = wordsize; + *modrm |= 0200; + } + *v_modrm = 1; /* We're done with ModRM */ + + break; + + /* If not 0, the displacement length was forced; set the Mod bits + * appropriately and we're done with the ModRM byte. We assume + * that the user knows what they're doing if they do an explicit + * override, so we don't check for overflow (we'll just truncate + * when we output). + */ + case 1: + *modrm |= 0100; + *v_modrm = 1; + break; + case 2: + case 4: + if (wordsize != *displen) { + ErrorAt(e->filename, e->line, + _("invalid effective address (displacement size)")); + return 0; + } + *modrm |= 0200; + *v_modrm = 1; + break; + default: + /* we shouldn't ever get any other size! */ + InternalError(_("strange EA displacement size")); + } + + return 1; +} + static int expr_checkea_getregsize_callback(ExprItem *ei, void *d) { @@ -1033,8 +1152,6 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, unsigned char *n_sib) { expr *e = *ep; - const intnum *intn; - long dispval; if (*addrsize == 0) { /* we need to figure out the address size from what we know about: @@ -1066,9 +1183,21 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, } if (*addrsize == 32 && ((*n_modrm && !*v_modrm) || (*n_sib && !*v_sib))) { + int i; + typedef enum { + REG32_NONE = -1, + REG32_EAX = 0, + REG32_ECX = 1, + REG32_EDX = 2, + REG32_EBX = 3, + REG32_ESP = 4, + REG32_EBP = 5, + REG32_ESI = 6, + REG32_EDI = 7 + } reg32type; int reg32mult[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - /*int basereg = 0;*/ /* "base" register (for SIB) */ - int indexreg = 0; /* "index" register (for SIB) */ + int basereg = REG32_NONE; /* "base" register (for SIB) */ + int indexreg = REG32_NONE; /* "index" register (for SIB) */ switch (expr_checkea_getregusage(ep, &indexreg, reg32mult, expr_checkea_get_reg32)) { @@ -1083,6 +1212,153 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, break; } + /* If indexreg mult is 0, discard it. + * This is possible because of the way indexreg is found in + * expr_checkea_getregusage(). + */ + if (indexreg != REG32_NONE && reg32mult[indexreg] == 0) + indexreg = REG32_NONE; + + /* Find a basereg (*1, but not indexreg), if there is one. + * Also, if an indexreg hasn't been assigned, try to find one. + * Meanwhile, check to make sure there's no negative register mults. + */ + for (i=0; i<8; i++) { + if (reg32mult[i] < 0) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + if (i != indexreg && reg32mult[i] == 1) + basereg = i; + else if (indexreg == REG32_NONE && reg32mult[i] > 0) + indexreg = i; + } + + /* Handle certain special cases of indexreg mults when basereg is + * empty. + */ + if (indexreg != REG32_NONE && basereg == REG32_NONE) + switch (reg32mult[indexreg]) { + case 1: + /* Only optimize this way if nosplit wasn't specified */ + if (!nosplit) { + basereg = indexreg; + indexreg = -1; + } + break; + case 2: + /* Only split if nosplit wasn't specified */ + if (!nosplit) { + basereg = indexreg; + reg32mult[indexreg] = 1; + } + break; + case 3: + case 5: + case 9: + basereg = indexreg; + reg32mult[indexreg]--; + break; + } + + /* Make sure there's no other registers than the basereg and indexreg + * we just found. + */ + for (i=0; i<8; i++) + if (i != basereg && i != indexreg && reg32mult[i] != 0) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + + /* Check the index multiplier value for validity if present. */ + if (indexreg != REG32_NONE && reg32mult[indexreg] != 1 && + reg32mult[indexreg] != 2 && reg32mult[indexreg] != 4 && + reg32mult[indexreg] != 8) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + + /* ESP is not a legal indexreg. */ + if (indexreg == REG32_ESP) { + /* If mult>1 or basereg is ESP also, there's no way to make it + * legal. + */ + if (reg32mult[REG32_ESP] > 1 || basereg == REG32_ESP) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + /* If mult==1 and basereg is not ESP, swap indexreg w/basereg. */ + indexreg = basereg; + basereg = REG32_ESP; + } + + /* At this point, we know the base and index registers and that the + * memory expression is (essentially) valid. Now build the ModRM and + * (optional) SIB bytes. + */ + + /* First determine R/M (Mod is later determined from disp size) */ + *n_modrm = 1; /* we always need ModRM */ + if (basereg == REG32_NONE) { + /* disp32[index] */ + *modrm |= 5; + /* we must have a SIB */ + *n_sib = 1; + } else if (indexreg == REG32_NONE) { + /* basereg only */ + *modrm |= basereg; + /* we don't need an SIB *unless* basereg is ESP */ + if (basereg == REG32_ESP) + *n_sib = 1; + else { + *sib = 0; + *v_sib = 0; + *n_sib = 0; + } + } else { + /* both base AND index */ + *modrm |= 4; + *n_sib = 1; + } + + /* Determine SIB if needed */ + if (*n_sib == 1) { + *sib = 0; /* start with 0 */ + + /* Special case: no basereg (only happens in disp32[index] case) */ + if (basereg == REG32_NONE) + *sib |= 5; + else + *sib |= basereg & 7; /* &7 to sanity check */ + + /* Put in indexreg, checking for none case */ + if (indexreg == REG32_NONE) + *sib |= 040; + /* Any scale field is valid, just leave at 0. */ + else { + *sib |= (indexreg & 7) << 3; /* &7 to sanity check */ + /* Set scale field, 1 case -> 0, so don't bother. */ + switch (reg32mult[indexreg]) { + case 2: + *sib |= 0100; + break; + case 4: + *sib |= 0200; + break; + case 8: + *sib |= 0300; + break; + } + } + + *v_sib = 1; /* Done with SIB */ + } + + /* Calculate displacement length (if possible) */ + return checkea_calc_displen(ep, 4, basereg == REG32_NONE, + basereg == REG32_EBP && + indexreg == REG32_NONE, displen, modrm, + v_modrm); } else if (*addrsize == 16 && *n_modrm && !*v_modrm) { static const unsigned char modrm16[16] = { 0006 /* disp16 */, 0007 /* [BX] */, 0004 /* [SI] */, @@ -1092,7 +1368,7 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, 0003 /* [BP+DI] */, 0377 /* invalid */, 0377 /* invalid */, 0377 /* invalid */ }; - checkea_invalid16_data reg16mult = {0, 0, 0, 0}; + checkea_reg16_data reg16mult = {0, 0, 0, 0}; enum { HAVE_NONE = 0, HAVE_BX = 1<<0, @@ -1142,107 +1418,13 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, return 0; } + /* Set ModRM byte for registers */ *modrm |= modrm16[havereg]; - *v_modrm = 0; /* default to not yet valid */ - - switch (*displen) { - case 0: - /* the displacement length hasn't been forced, try to - * determine what it is. - */ - switch (havereg) { - case HAVE_NONE: - /* no register in expression, so it must be disp16, and - * as the Mod bits are set to 0 above, we're done with - * the ModRM byte. - */ - *displen = 2; - *v_modrm = 1; - break; - case HAVE_BP: - /* for BP, there *must* be a displacement value, but we - * may not know the size (8 or 16) for sure right now. - * We can't leave displen at 0, because that just means - * unknown displacement, including none. - */ - *displen = 0xff; - break; - default: - break; - } - - intn = expr_get_intnum(ep); - if (!intn) - break; /* expr still has unknown values */ - - /* make sure the displacement will fit in 16 bits if unsigned, - * and 8 bits if signed. - */ - if (!intnum_check_size(intn, 2, 0) && - !intnum_check_size(intn, 1, 1)) { - ErrorAt(e->filename, e->line, - _("invalid effective address")); - return 0; - } - - /* don't try to find out what size displacement we have if - * displen is known. - */ - if (*displen != 0 && *displen != 0xff) - break; - - /* Don't worry about overflows here (it's already guaranteed - * to be 16 or 8 bits). - */ - dispval = intnum_get_int(intn); - - /* Figure out what size displacement we will have. */ - if (*displen != 0xff && dispval == 0) { - /* if we know that the displacement is 0 right now, - * go ahead and delete the expr (making it so no - * displacement value is included in the output). - * The Mod bits of ModRM are set to 0 above, and - * we're done with the ModRM byte! - * - * Don't do this if we came from HAVE_BP above, so - * check *displen. - */ - expr_delete(e); - *ep = (expr *)NULL; - } else if (dispval >= -128 && dispval <= 127) { - /* It fits into a signed byte */ - *displen = 1; - *modrm |= 0100; - } else { - /* It's a 16-bit displacement */ - *displen = 2; - *modrm |= 0200; - } - *v_modrm = 1; /* We're done with ModRM */ - - break; - - /* If not 0, the displacement length was forced; set the Mod bits - * appropriately and we're done with the ModRM byte. We assume - * that the user knows what they're doing if they do an explicit - * override, so we don't check for overflow (we'll just truncate - * when we output). - */ - case 1: - *modrm |= 0100; - *v_modrm = 1; - break; - case 2: - *modrm |= 0200; - *v_modrm = 1; - break; - default: - /* any other size is an error */ - ErrorAt(e->filename, e->line, - _("invalid effective address (displacement size)")); - return 0; - } + /* Calculate displacement length (if possible) */ + return checkea_calc_displen(ep, 2, havereg == HAVE_NONE, + havereg == HAVE_BP, displen, modrm, + v_modrm); } return 1; } diff --git a/src/arch/x86/x86expr.c b/src/arch/x86/x86expr.c index 63604b90..4c2a6dc4 100644 --- a/src/arch/x86/x86expr.c +++ b/src/arch/x86/x86expr.c @@ -761,9 +761,9 @@ expr_checkea_get_reg32(ExprItem *ei, void *d) return ret; } -typedef struct checkea_invalid16_data { +typedef struct checkea_reg16_data { int bx, si, di, bp; /* total multiplier for each reg */ -} checkea_invalid16_data; +} checkea_reg16_data; /* Only works if ei->type == EXPR_REG (doesn't check). * Overwrites ei with intnum of 0 (to eliminate regs from the final expr). @@ -771,7 +771,7 @@ typedef struct checkea_invalid16_data { static int * expr_checkea_get_reg16(ExprItem *ei, void *d) { - checkea_invalid16_data *data = d; + checkea_reg16_data *data = d; /* in order: ax,cx,dx,bx,sp,bp,si,di */ static int *reg16[8] = {0,0,0,0,0,0,0,0}; int *ret; @@ -910,7 +910,7 @@ expr_checkea_distcheck_reg(expr **ep) * where the [...] parts are optional. * * Don't simplify out constant identities if we're looking for an indexreg: we - * need the multiplier for determining what the indexreg is! + * may need the multiplier for determining what the indexreg is! * * Returns 0 if invalid register usage, 1 if unable to determine all values, * and 2 if all values successfully determined and saved in data. @@ -928,7 +928,6 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, e = *ep; switch (expr_checkea_distcheck_reg(ep)) { case 0: - ErrorAt(e->filename, e->line, _("invalid effective address")); return 0; case 2: /* Need to simplify again */ @@ -968,6 +967,8 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, if (!reg) return 0; (*reg)++; + if (indexreg) + *indexreg = reg-(int *)data; } else if (e->terms[i].type == EXPR_EXPR) { /* Already ordered from ADD above, just grab the value. * Sanity check for EXPR_INT. @@ -982,8 +983,7 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, (*reg) += intnum_get_int(e->terms[i].data.expn->terms[1].data.intn); if (indexreg) - *indexreg = - e->terms[i].data.expn->terms[0].data.reg.num; + *indexreg = reg-(int *)data; } } break; @@ -998,6 +998,8 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, if (!reg) return 0; (*reg) += intnum_get_int(e->terms[1].data.intn); + if (indexreg) + *indexreg = reg-(int *)data; break; default: /* Should never get here! */ @@ -1013,6 +1015,123 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, return 2; } +/* Calculate the displacement length, if possible. + * Takes several extra inputs so it can be used by both 32-bit and 16-bit + * expressions: + * wordsize=2 for 16-bit, =4 for 32-bit. + * noreg=1 if the *ModRM byte* has no registers used. + * isbpreg=1 if BP/EBP is the *only* register used within the *ModRM byte*. + */ +static int +checkea_calc_displen(expr **ep, int wordsize, int noreg, int isbpreg, + unsigned char *displen, unsigned char *modrm, + unsigned char *v_modrm) +{ + expr *e = *ep; + const intnum *intn; + long dispval; + + *v_modrm = 0; /* default to not yet valid */ + + switch (*displen) { + case 0: + /* the displacement length hasn't been forced, try to + * determine what it is. + */ + if (noreg) { + /* no register in ModRM expression, so it must be disp16/32, + * and as the Mod bits are set to 0 by the caller, we're done + * with the ModRM byte. + */ + *displen = wordsize; + *v_modrm = 1; + } else if (isbpreg) { + /* for BP/EBP, there *must* be a displacement value, but we + * may not know the size (8 or 16/32) for sure right now. + * We can't leave displen at 0, because that just means + * unknown displacement, including none. + */ + *displen = 0xff; + } + + intn = expr_get_intnum(ep); + if (!intn) + break; /* expr still has unknown values */ + + /* make sure the displacement will fit in 16/32 bits if unsigned, + * and 8 bits if signed. + */ + if (!intnum_check_size(intn, wordsize, 0) && + !intnum_check_size(intn, 1, 1)) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + + /* don't try to find out what size displacement we have if + * displen is known. + */ + if (*displen != 0 && *displen != 0xff) + break; + + /* Don't worry about overflows here (it's already guaranteed + * to be 16/32 or 8 bits). + */ + dispval = intnum_get_int(intn); + + /* Figure out what size displacement we will have. */ + if (*displen != 0xff && dispval == 0) { + /* if we know that the displacement is 0 right now, + * go ahead and delete the expr (making it so no + * displacement value is included in the output). + * The Mod bits of ModRM are set to 0 above, and + * we're done with the ModRM byte! + * + * Don't do this if we came from isbpreg check above, so + * check *displen. + */ + expr_delete(e); + *ep = (expr *)NULL; + } else if (dispval >= -128 && dispval <= 127) { + /* It fits into a signed byte */ + *displen = 1; + *modrm |= 0100; + } else { + /* It's a 16/32-bit displacement */ + *displen = wordsize; + *modrm |= 0200; + } + *v_modrm = 1; /* We're done with ModRM */ + + break; + + /* If not 0, the displacement length was forced; set the Mod bits + * appropriately and we're done with the ModRM byte. We assume + * that the user knows what they're doing if they do an explicit + * override, so we don't check for overflow (we'll just truncate + * when we output). + */ + case 1: + *modrm |= 0100; + *v_modrm = 1; + break; + case 2: + case 4: + if (wordsize != *displen) { + ErrorAt(e->filename, e->line, + _("invalid effective address (displacement size)")); + return 0; + } + *modrm |= 0200; + *v_modrm = 1; + break; + default: + /* we shouldn't ever get any other size! */ + InternalError(_("strange EA displacement size")); + } + + return 1; +} + static int expr_checkea_getregsize_callback(ExprItem *ei, void *d) { @@ -1033,8 +1152,6 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, unsigned char *n_sib) { expr *e = *ep; - const intnum *intn; - long dispval; if (*addrsize == 0) { /* we need to figure out the address size from what we know about: @@ -1066,9 +1183,21 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, } if (*addrsize == 32 && ((*n_modrm && !*v_modrm) || (*n_sib && !*v_sib))) { + int i; + typedef enum { + REG32_NONE = -1, + REG32_EAX = 0, + REG32_ECX = 1, + REG32_EDX = 2, + REG32_EBX = 3, + REG32_ESP = 4, + REG32_EBP = 5, + REG32_ESI = 6, + REG32_EDI = 7 + } reg32type; int reg32mult[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - /*int basereg = 0;*/ /* "base" register (for SIB) */ - int indexreg = 0; /* "index" register (for SIB) */ + int basereg = REG32_NONE; /* "base" register (for SIB) */ + int indexreg = REG32_NONE; /* "index" register (for SIB) */ switch (expr_checkea_getregusage(ep, &indexreg, reg32mult, expr_checkea_get_reg32)) { @@ -1083,6 +1212,153 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, break; } + /* If indexreg mult is 0, discard it. + * This is possible because of the way indexreg is found in + * expr_checkea_getregusage(). + */ + if (indexreg != REG32_NONE && reg32mult[indexreg] == 0) + indexreg = REG32_NONE; + + /* Find a basereg (*1, but not indexreg), if there is one. + * Also, if an indexreg hasn't been assigned, try to find one. + * Meanwhile, check to make sure there's no negative register mults. + */ + for (i=0; i<8; i++) { + if (reg32mult[i] < 0) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + if (i != indexreg && reg32mult[i] == 1) + basereg = i; + else if (indexreg == REG32_NONE && reg32mult[i] > 0) + indexreg = i; + } + + /* Handle certain special cases of indexreg mults when basereg is + * empty. + */ + if (indexreg != REG32_NONE && basereg == REG32_NONE) + switch (reg32mult[indexreg]) { + case 1: + /* Only optimize this way if nosplit wasn't specified */ + if (!nosplit) { + basereg = indexreg; + indexreg = -1; + } + break; + case 2: + /* Only split if nosplit wasn't specified */ + if (!nosplit) { + basereg = indexreg; + reg32mult[indexreg] = 1; + } + break; + case 3: + case 5: + case 9: + basereg = indexreg; + reg32mult[indexreg]--; + break; + } + + /* Make sure there's no other registers than the basereg and indexreg + * we just found. + */ + for (i=0; i<8; i++) + if (i != basereg && i != indexreg && reg32mult[i] != 0) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + + /* Check the index multiplier value for validity if present. */ + if (indexreg != REG32_NONE && reg32mult[indexreg] != 1 && + reg32mult[indexreg] != 2 && reg32mult[indexreg] != 4 && + reg32mult[indexreg] != 8) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + + /* ESP is not a legal indexreg. */ + if (indexreg == REG32_ESP) { + /* If mult>1 or basereg is ESP also, there's no way to make it + * legal. + */ + if (reg32mult[REG32_ESP] > 1 || basereg == REG32_ESP) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + /* If mult==1 and basereg is not ESP, swap indexreg w/basereg. */ + indexreg = basereg; + basereg = REG32_ESP; + } + + /* At this point, we know the base and index registers and that the + * memory expression is (essentially) valid. Now build the ModRM and + * (optional) SIB bytes. + */ + + /* First determine R/M (Mod is later determined from disp size) */ + *n_modrm = 1; /* we always need ModRM */ + if (basereg == REG32_NONE) { + /* disp32[index] */ + *modrm |= 5; + /* we must have a SIB */ + *n_sib = 1; + } else if (indexreg == REG32_NONE) { + /* basereg only */ + *modrm |= basereg; + /* we don't need an SIB *unless* basereg is ESP */ + if (basereg == REG32_ESP) + *n_sib = 1; + else { + *sib = 0; + *v_sib = 0; + *n_sib = 0; + } + } else { + /* both base AND index */ + *modrm |= 4; + *n_sib = 1; + } + + /* Determine SIB if needed */ + if (*n_sib == 1) { + *sib = 0; /* start with 0 */ + + /* Special case: no basereg (only happens in disp32[index] case) */ + if (basereg == REG32_NONE) + *sib |= 5; + else + *sib |= basereg & 7; /* &7 to sanity check */ + + /* Put in indexreg, checking for none case */ + if (indexreg == REG32_NONE) + *sib |= 040; + /* Any scale field is valid, just leave at 0. */ + else { + *sib |= (indexreg & 7) << 3; /* &7 to sanity check */ + /* Set scale field, 1 case -> 0, so don't bother. */ + switch (reg32mult[indexreg]) { + case 2: + *sib |= 0100; + break; + case 4: + *sib |= 0200; + break; + case 8: + *sib |= 0300; + break; + } + } + + *v_sib = 1; /* Done with SIB */ + } + + /* Calculate displacement length (if possible) */ + return checkea_calc_displen(ep, 4, basereg == REG32_NONE, + basereg == REG32_EBP && + indexreg == REG32_NONE, displen, modrm, + v_modrm); } else if (*addrsize == 16 && *n_modrm && !*v_modrm) { static const unsigned char modrm16[16] = { 0006 /* disp16 */, 0007 /* [BX] */, 0004 /* [SI] */, @@ -1092,7 +1368,7 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, 0003 /* [BP+DI] */, 0377 /* invalid */, 0377 /* invalid */, 0377 /* invalid */ }; - checkea_invalid16_data reg16mult = {0, 0, 0, 0}; + checkea_reg16_data reg16mult = {0, 0, 0, 0}; enum { HAVE_NONE = 0, HAVE_BX = 1<<0, @@ -1142,107 +1418,13 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, return 0; } + /* Set ModRM byte for registers */ *modrm |= modrm16[havereg]; - *v_modrm = 0; /* default to not yet valid */ - - switch (*displen) { - case 0: - /* the displacement length hasn't been forced, try to - * determine what it is. - */ - switch (havereg) { - case HAVE_NONE: - /* no register in expression, so it must be disp16, and - * as the Mod bits are set to 0 above, we're done with - * the ModRM byte. - */ - *displen = 2; - *v_modrm = 1; - break; - case HAVE_BP: - /* for BP, there *must* be a displacement value, but we - * may not know the size (8 or 16) for sure right now. - * We can't leave displen at 0, because that just means - * unknown displacement, including none. - */ - *displen = 0xff; - break; - default: - break; - } - - intn = expr_get_intnum(ep); - if (!intn) - break; /* expr still has unknown values */ - - /* make sure the displacement will fit in 16 bits if unsigned, - * and 8 bits if signed. - */ - if (!intnum_check_size(intn, 2, 0) && - !intnum_check_size(intn, 1, 1)) { - ErrorAt(e->filename, e->line, - _("invalid effective address")); - return 0; - } - - /* don't try to find out what size displacement we have if - * displen is known. - */ - if (*displen != 0 && *displen != 0xff) - break; - - /* Don't worry about overflows here (it's already guaranteed - * to be 16 or 8 bits). - */ - dispval = intnum_get_int(intn); - - /* Figure out what size displacement we will have. */ - if (*displen != 0xff && dispval == 0) { - /* if we know that the displacement is 0 right now, - * go ahead and delete the expr (making it so no - * displacement value is included in the output). - * The Mod bits of ModRM are set to 0 above, and - * we're done with the ModRM byte! - * - * Don't do this if we came from HAVE_BP above, so - * check *displen. - */ - expr_delete(e); - *ep = (expr *)NULL; - } else if (dispval >= -128 && dispval <= 127) { - /* It fits into a signed byte */ - *displen = 1; - *modrm |= 0100; - } else { - /* It's a 16-bit displacement */ - *displen = 2; - *modrm |= 0200; - } - *v_modrm = 1; /* We're done with ModRM */ - - break; - - /* If not 0, the displacement length was forced; set the Mod bits - * appropriately and we're done with the ModRM byte. We assume - * that the user knows what they're doing if they do an explicit - * override, so we don't check for overflow (we'll just truncate - * when we output). - */ - case 1: - *modrm |= 0100; - *v_modrm = 1; - break; - case 2: - *modrm |= 0200; - *v_modrm = 1; - break; - default: - /* any other size is an error */ - ErrorAt(e->filename, e->line, - _("invalid effective address (displacement size)")); - return 0; - } + /* Calculate displacement length (if possible) */ + return checkea_calc_displen(ep, 2, havereg == HAVE_NONE, + havereg == HAVE_BP, displen, modrm, + v_modrm); } return 1; } diff --git a/src/expr.c b/src/expr.c index 63604b90..4c2a6dc4 100644 --- a/src/expr.c +++ b/src/expr.c @@ -761,9 +761,9 @@ expr_checkea_get_reg32(ExprItem *ei, void *d) return ret; } -typedef struct checkea_invalid16_data { +typedef struct checkea_reg16_data { int bx, si, di, bp; /* total multiplier for each reg */ -} checkea_invalid16_data; +} checkea_reg16_data; /* Only works if ei->type == EXPR_REG (doesn't check). * Overwrites ei with intnum of 0 (to eliminate regs from the final expr). @@ -771,7 +771,7 @@ typedef struct checkea_invalid16_data { static int * expr_checkea_get_reg16(ExprItem *ei, void *d) { - checkea_invalid16_data *data = d; + checkea_reg16_data *data = d; /* in order: ax,cx,dx,bx,sp,bp,si,di */ static int *reg16[8] = {0,0,0,0,0,0,0,0}; int *ret; @@ -910,7 +910,7 @@ expr_checkea_distcheck_reg(expr **ep) * where the [...] parts are optional. * * Don't simplify out constant identities if we're looking for an indexreg: we - * need the multiplier for determining what the indexreg is! + * may need the multiplier for determining what the indexreg is! * * Returns 0 if invalid register usage, 1 if unable to determine all values, * and 2 if all values successfully determined and saved in data. @@ -928,7 +928,6 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, e = *ep; switch (expr_checkea_distcheck_reg(ep)) { case 0: - ErrorAt(e->filename, e->line, _("invalid effective address")); return 0; case 2: /* Need to simplify again */ @@ -968,6 +967,8 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, if (!reg) return 0; (*reg)++; + if (indexreg) + *indexreg = reg-(int *)data; } else if (e->terms[i].type == EXPR_EXPR) { /* Already ordered from ADD above, just grab the value. * Sanity check for EXPR_INT. @@ -982,8 +983,7 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, (*reg) += intnum_get_int(e->terms[i].data.expn->terms[1].data.intn); if (indexreg) - *indexreg = - e->terms[i].data.expn->terms[0].data.reg.num; + *indexreg = reg-(int *)data; } } break; @@ -998,6 +998,8 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, if (!reg) return 0; (*reg) += intnum_get_int(e->terms[1].data.intn); + if (indexreg) + *indexreg = reg-(int *)data; break; default: /* Should never get here! */ @@ -1013,6 +1015,123 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data, return 2; } +/* Calculate the displacement length, if possible. + * Takes several extra inputs so it can be used by both 32-bit and 16-bit + * expressions: + * wordsize=2 for 16-bit, =4 for 32-bit. + * noreg=1 if the *ModRM byte* has no registers used. + * isbpreg=1 if BP/EBP is the *only* register used within the *ModRM byte*. + */ +static int +checkea_calc_displen(expr **ep, int wordsize, int noreg, int isbpreg, + unsigned char *displen, unsigned char *modrm, + unsigned char *v_modrm) +{ + expr *e = *ep; + const intnum *intn; + long dispval; + + *v_modrm = 0; /* default to not yet valid */ + + switch (*displen) { + case 0: + /* the displacement length hasn't been forced, try to + * determine what it is. + */ + if (noreg) { + /* no register in ModRM expression, so it must be disp16/32, + * and as the Mod bits are set to 0 by the caller, we're done + * with the ModRM byte. + */ + *displen = wordsize; + *v_modrm = 1; + } else if (isbpreg) { + /* for BP/EBP, there *must* be a displacement value, but we + * may not know the size (8 or 16/32) for sure right now. + * We can't leave displen at 0, because that just means + * unknown displacement, including none. + */ + *displen = 0xff; + } + + intn = expr_get_intnum(ep); + if (!intn) + break; /* expr still has unknown values */ + + /* make sure the displacement will fit in 16/32 bits if unsigned, + * and 8 bits if signed. + */ + if (!intnum_check_size(intn, wordsize, 0) && + !intnum_check_size(intn, 1, 1)) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + + /* don't try to find out what size displacement we have if + * displen is known. + */ + if (*displen != 0 && *displen != 0xff) + break; + + /* Don't worry about overflows here (it's already guaranteed + * to be 16/32 or 8 bits). + */ + dispval = intnum_get_int(intn); + + /* Figure out what size displacement we will have. */ + if (*displen != 0xff && dispval == 0) { + /* if we know that the displacement is 0 right now, + * go ahead and delete the expr (making it so no + * displacement value is included in the output). + * The Mod bits of ModRM are set to 0 above, and + * we're done with the ModRM byte! + * + * Don't do this if we came from isbpreg check above, so + * check *displen. + */ + expr_delete(e); + *ep = (expr *)NULL; + } else if (dispval >= -128 && dispval <= 127) { + /* It fits into a signed byte */ + *displen = 1; + *modrm |= 0100; + } else { + /* It's a 16/32-bit displacement */ + *displen = wordsize; + *modrm |= 0200; + } + *v_modrm = 1; /* We're done with ModRM */ + + break; + + /* If not 0, the displacement length was forced; set the Mod bits + * appropriately and we're done with the ModRM byte. We assume + * that the user knows what they're doing if they do an explicit + * override, so we don't check for overflow (we'll just truncate + * when we output). + */ + case 1: + *modrm |= 0100; + *v_modrm = 1; + break; + case 2: + case 4: + if (wordsize != *displen) { + ErrorAt(e->filename, e->line, + _("invalid effective address (displacement size)")); + return 0; + } + *modrm |= 0200; + *v_modrm = 1; + break; + default: + /* we shouldn't ever get any other size! */ + InternalError(_("strange EA displacement size")); + } + + return 1; +} + static int expr_checkea_getregsize_callback(ExprItem *ei, void *d) { @@ -1033,8 +1152,6 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, unsigned char *n_sib) { expr *e = *ep; - const intnum *intn; - long dispval; if (*addrsize == 0) { /* we need to figure out the address size from what we know about: @@ -1066,9 +1183,21 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, } if (*addrsize == 32 && ((*n_modrm && !*v_modrm) || (*n_sib && !*v_sib))) { + int i; + typedef enum { + REG32_NONE = -1, + REG32_EAX = 0, + REG32_ECX = 1, + REG32_EDX = 2, + REG32_EBX = 3, + REG32_ESP = 4, + REG32_EBP = 5, + REG32_ESI = 6, + REG32_EDI = 7 + } reg32type; int reg32mult[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - /*int basereg = 0;*/ /* "base" register (for SIB) */ - int indexreg = 0; /* "index" register (for SIB) */ + int basereg = REG32_NONE; /* "base" register (for SIB) */ + int indexreg = REG32_NONE; /* "index" register (for SIB) */ switch (expr_checkea_getregusage(ep, &indexreg, reg32mult, expr_checkea_get_reg32)) { @@ -1083,6 +1212,153 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, break; } + /* If indexreg mult is 0, discard it. + * This is possible because of the way indexreg is found in + * expr_checkea_getregusage(). + */ + if (indexreg != REG32_NONE && reg32mult[indexreg] == 0) + indexreg = REG32_NONE; + + /* Find a basereg (*1, but not indexreg), if there is one. + * Also, if an indexreg hasn't been assigned, try to find one. + * Meanwhile, check to make sure there's no negative register mults. + */ + for (i=0; i<8; i++) { + if (reg32mult[i] < 0) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + if (i != indexreg && reg32mult[i] == 1) + basereg = i; + else if (indexreg == REG32_NONE && reg32mult[i] > 0) + indexreg = i; + } + + /* Handle certain special cases of indexreg mults when basereg is + * empty. + */ + if (indexreg != REG32_NONE && basereg == REG32_NONE) + switch (reg32mult[indexreg]) { + case 1: + /* Only optimize this way if nosplit wasn't specified */ + if (!nosplit) { + basereg = indexreg; + indexreg = -1; + } + break; + case 2: + /* Only split if nosplit wasn't specified */ + if (!nosplit) { + basereg = indexreg; + reg32mult[indexreg] = 1; + } + break; + case 3: + case 5: + case 9: + basereg = indexreg; + reg32mult[indexreg]--; + break; + } + + /* Make sure there's no other registers than the basereg and indexreg + * we just found. + */ + for (i=0; i<8; i++) + if (i != basereg && i != indexreg && reg32mult[i] != 0) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + + /* Check the index multiplier value for validity if present. */ + if (indexreg != REG32_NONE && reg32mult[indexreg] != 1 && + reg32mult[indexreg] != 2 && reg32mult[indexreg] != 4 && + reg32mult[indexreg] != 8) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + + /* ESP is not a legal indexreg. */ + if (indexreg == REG32_ESP) { + /* If mult>1 or basereg is ESP also, there's no way to make it + * legal. + */ + if (reg32mult[REG32_ESP] > 1 || basereg == REG32_ESP) { + ErrorAt(e->filename, e->line, _("invalid effective address")); + return 0; + } + /* If mult==1 and basereg is not ESP, swap indexreg w/basereg. */ + indexreg = basereg; + basereg = REG32_ESP; + } + + /* At this point, we know the base and index registers and that the + * memory expression is (essentially) valid. Now build the ModRM and + * (optional) SIB bytes. + */ + + /* First determine R/M (Mod is later determined from disp size) */ + *n_modrm = 1; /* we always need ModRM */ + if (basereg == REG32_NONE) { + /* disp32[index] */ + *modrm |= 5; + /* we must have a SIB */ + *n_sib = 1; + } else if (indexreg == REG32_NONE) { + /* basereg only */ + *modrm |= basereg; + /* we don't need an SIB *unless* basereg is ESP */ + if (basereg == REG32_ESP) + *n_sib = 1; + else { + *sib = 0; + *v_sib = 0; + *n_sib = 0; + } + } else { + /* both base AND index */ + *modrm |= 4; + *n_sib = 1; + } + + /* Determine SIB if needed */ + if (*n_sib == 1) { + *sib = 0; /* start with 0 */ + + /* Special case: no basereg (only happens in disp32[index] case) */ + if (basereg == REG32_NONE) + *sib |= 5; + else + *sib |= basereg & 7; /* &7 to sanity check */ + + /* Put in indexreg, checking for none case */ + if (indexreg == REG32_NONE) + *sib |= 040; + /* Any scale field is valid, just leave at 0. */ + else { + *sib |= (indexreg & 7) << 3; /* &7 to sanity check */ + /* Set scale field, 1 case -> 0, so don't bother. */ + switch (reg32mult[indexreg]) { + case 2: + *sib |= 0100; + break; + case 4: + *sib |= 0200; + break; + case 8: + *sib |= 0300; + break; + } + } + + *v_sib = 1; /* Done with SIB */ + } + + /* Calculate displacement length (if possible) */ + return checkea_calc_displen(ep, 4, basereg == REG32_NONE, + basereg == REG32_EBP && + indexreg == REG32_NONE, displen, modrm, + v_modrm); } else if (*addrsize == 16 && *n_modrm && !*v_modrm) { static const unsigned char modrm16[16] = { 0006 /* disp16 */, 0007 /* [BX] */, 0004 /* [SI] */, @@ -1092,7 +1368,7 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, 0003 /* [BP+DI] */, 0377 /* invalid */, 0377 /* invalid */, 0377 /* invalid */ }; - checkea_invalid16_data reg16mult = {0, 0, 0, 0}; + checkea_reg16_data reg16mult = {0, 0, 0, 0}; enum { HAVE_NONE = 0, HAVE_BX = 1<<0, @@ -1142,107 +1418,13 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, return 0; } + /* Set ModRM byte for registers */ *modrm |= modrm16[havereg]; - *v_modrm = 0; /* default to not yet valid */ - - switch (*displen) { - case 0: - /* the displacement length hasn't been forced, try to - * determine what it is. - */ - switch (havereg) { - case HAVE_NONE: - /* no register in expression, so it must be disp16, and - * as the Mod bits are set to 0 above, we're done with - * the ModRM byte. - */ - *displen = 2; - *v_modrm = 1; - break; - case HAVE_BP: - /* for BP, there *must* be a displacement value, but we - * may not know the size (8 or 16) for sure right now. - * We can't leave displen at 0, because that just means - * unknown displacement, including none. - */ - *displen = 0xff; - break; - default: - break; - } - - intn = expr_get_intnum(ep); - if (!intn) - break; /* expr still has unknown values */ - - /* make sure the displacement will fit in 16 bits if unsigned, - * and 8 bits if signed. - */ - if (!intnum_check_size(intn, 2, 0) && - !intnum_check_size(intn, 1, 1)) { - ErrorAt(e->filename, e->line, - _("invalid effective address")); - return 0; - } - - /* don't try to find out what size displacement we have if - * displen is known. - */ - if (*displen != 0 && *displen != 0xff) - break; - - /* Don't worry about overflows here (it's already guaranteed - * to be 16 or 8 bits). - */ - dispval = intnum_get_int(intn); - - /* Figure out what size displacement we will have. */ - if (*displen != 0xff && dispval == 0) { - /* if we know that the displacement is 0 right now, - * go ahead and delete the expr (making it so no - * displacement value is included in the output). - * The Mod bits of ModRM are set to 0 above, and - * we're done with the ModRM byte! - * - * Don't do this if we came from HAVE_BP above, so - * check *displen. - */ - expr_delete(e); - *ep = (expr *)NULL; - } else if (dispval >= -128 && dispval <= 127) { - /* It fits into a signed byte */ - *displen = 1; - *modrm |= 0100; - } else { - /* It's a 16-bit displacement */ - *displen = 2; - *modrm |= 0200; - } - *v_modrm = 1; /* We're done with ModRM */ - - break; - - /* If not 0, the displacement length was forced; set the Mod bits - * appropriately and we're done with the ModRM byte. We assume - * that the user knows what they're doing if they do an explicit - * override, so we don't check for overflow (we'll just truncate - * when we output). - */ - case 1: - *modrm |= 0100; - *v_modrm = 1; - break; - case 2: - *modrm |= 0200; - *v_modrm = 1; - break; - default: - /* any other size is an error */ - ErrorAt(e->filename, e->line, - _("invalid effective address (displacement size)")); - return 0; - } + /* Calculate displacement length (if possible) */ + return checkea_calc_displen(ep, 2, havereg == HAVE_NONE, + havereg == HAVE_BP, displen, modrm, + v_modrm); } return 1; }