📄 x86expr.c
字号:
/* * x86 expression handling * * Copyright (C) 2001 Peter Johnson * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */#include <util.h>/*@unused@*/ RCSID("$Id: x86expr.c 1168 2004-10-31 01:07:52Z peter $");#define YASM_LIB_INTERNAL#define YASM_EXPR_INTERNAL#include <libyasm.h>#include "x86arch.h"typedef struct x86_checkea_reg3264_data { int *regs; /* total multiplier for each reg */ unsigned char bits; unsigned char addrsize;} x86_checkea_reg3264_data;/* Only works if ei->type == EXPR_REG (doesn't check). * Overwrites ei with intnum of 0 (to eliminate regs from the final expr). */static /*@null@*/ /*@dependent@*/ int *x86_expr_checkea_get_reg3264(yasm_expr__item *ei, int *regnum, /*returned*/ void *d){ x86_checkea_reg3264_data *data = d; switch ((x86_expritem_reg_size)(ei->data.reg & ~0xFUL)) { case X86_REG32: if (data->addrsize != 32) return 0; *regnum = ei->data.reg & 0xF; break; case X86_REG64: if (data->addrsize != 64) return 0; *regnum = ei->data.reg & 0xF; break; case X86_RIP: if (data->bits != 64) return 0; *regnum = 16; break; default: return 0; } /* overwrite with 0 to eliminate register from displacement expr */ ei->type = YASM_EXPR_INT; ei->data.intn = yasm_intnum_create_uint(0); /* we're okay */ return &data->regs[*regnum];}typedef struct x86_checkea_reg16_data { int bx, si, di, bp; /* total multiplier for each reg */} x86_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). */static /*@null@*/ int *x86_expr_checkea_get_reg16(yasm_expr__item *ei, int *regnum, void *d){ x86_checkea_reg16_data *data = d; /* in order: ax,cx,dx,bx,sp,bp,si,di */ /*@-nullassign@*/ static int *reg16[8] = {0,0,0,0,0,0,0,0}; /*@=nullassign@*/ reg16[3] = &data->bx; reg16[5] = &data->bp; reg16[6] = &data->si; reg16[7] = &data->di; /* don't allow 32-bit registers */ if ((ei->data.reg & ~0xFUL) != X86_REG16) return 0; /* & 7 for sanity check */ *regnum = ei->data.reg & 0x7; /* only allow BX, SI, DI, BP */ if (!reg16[*regnum]) return 0; /* overwrite with 0 to eliminate register from displacement expr */ ei->type = YASM_EXPR_INT; ei->data.intn = yasm_intnum_create_uint(0); /* we're okay */ return reg16[*regnum];}/* Distribute over registers to help bring them to the topmost level of e. * Also check for illegal operations against registers. * Returns 0 if something was illegal, 1 if legal and nothing in e changed, * and 2 if legal and e needs to be simplified. * * Only half joking: Someday make this/checkea able to accept crazy things * like: (bx+di)*(bx+di)-bx*bx-2*bx*di-di*di+di? Probably not: NASM never * accepted such things, and it's doubtful such an expn is valid anyway * (even though the above one is). But even macros would be hard-pressed * to generate something like this. * * e must already have been simplified for this function to work properly * (as it doesn't think things like SUB are valid). * * IMPLEMENTATION NOTE: About the only thing this function really needs to * "distribute" is: (non-float-expn or intnum) * (sum expn of registers). * * TODO: Clean up this code, make it easier to understand. */static intx86_expr_checkea_distcheck_reg(yasm_expr **ep, unsigned int bits){ yasm_expr *e = *ep; int i; int havereg = -1, havereg_expr = -1; int retval = 1; /* default to legal, no changes */ for (i=0; i<e->numterms; i++) { switch (e->terms[i].type) { case YASM_EXPR_REG: /* Check op to make sure it's valid to use w/register. */ switch (e->op) { case YASM_EXPR_MUL: /* Check for reg*reg */ if (havereg != -1) return 0; break; case YASM_EXPR_ADD: case YASM_EXPR_IDENT: break; default: return 0; } havereg = i; break; case YASM_EXPR_FLOAT: /* Floats not allowed. */ return 0; case YASM_EXPR_EXPR: if (yasm_expr__contains(e->terms[i].data.expn, YASM_EXPR_REG)) { int ret2; /* Check op to make sure it's valid to use w/register. */ if (e->op != YASM_EXPR_ADD && e->op != YASM_EXPR_MUL) return 0; /* Check for reg*reg */ if (e->op == YASM_EXPR_MUL && havereg != -1) return 0; havereg = i; havereg_expr = i; /* Recurse to check lower levels */ ret2 = x86_expr_checkea_distcheck_reg(&e->terms[i].data.expn, bits); if (ret2 == 0) return 0; if (ret2 == 2) retval = 2; } else if (yasm_expr__contains(e->terms[i].data.expn, YASM_EXPR_FLOAT)) return 0; /* Disallow floats */ break; default: break; } } /* just exit if no registers were used */ if (havereg == -1) return retval; /* Distribute */ if (e->op == YASM_EXPR_MUL && havereg_expr != -1) { yasm_expr *ne; retval = 2; /* we're going to change it */ /* The reg expn *must* be EXPR_ADD at this point. Sanity check. */ if (e->terms[havereg_expr].type != YASM_EXPR_EXPR || e->terms[havereg_expr].data.expn->op != YASM_EXPR_ADD) yasm_internal_error(N_("Register expression not ADD or EXPN")); /* Iterate over each term in reg expn */ for (i=0; i<e->terms[havereg_expr].data.expn->numterms; i++) { /* Copy everything EXCEPT havereg_expr term into new expression */ ne = yasm_expr__copy_except(e, havereg_expr); assert(ne != NULL); /* Copy reg expr term into uncopied (empty) term in new expn */ ne->terms[havereg_expr] = e->terms[havereg_expr].data.expn->terms[i]; /* struct copy */ /* Overwrite old reg expr term with new expn */ e->terms[havereg_expr].data.expn->terms[i].type = YASM_EXPR_EXPR; e->terms[havereg_expr].data.expn->terms[i].data.expn = ne; } /* Replace e with expanded reg expn */ ne = e->terms[havereg_expr].data.expn; e->terms[havereg_expr].type = YASM_EXPR_NONE; /* don't delete it! */ yasm_expr_destroy(e); /* but everything else */ e = ne; /*@-onlytrans@*/ *ep = ne; /*@=onlytrans@*/ } return retval;}/* Simplify and determine if expression is superficially valid: * Valid expr should be [(int-equiv expn)]+[reg*(int-equiv expn)+...] * where the [...] parts are optional. * * Don't simplify out constant identities if we're looking for an indexreg: we * may need the multiplier for determining what the indexreg is! * * Returns 1 if invalid register usage, 2 if unable to determine all values, * and 0 if all values successfully determined and saved in data. */static intx86_expr_checkea_getregusage(yasm_expr **ep, /*@null@*/ yasm_expr **wrt, /*@null@*/ int *indexreg, unsigned char *pcrel, unsigned int bits, void *data, int *(*get_reg)(yasm_expr__item *ei, int *regnum, void *d), yasm_calc_bc_dist_func calc_bc_dist){ int i; int *reg; int regnum; int indexval = 0; int indexmult = 0; yasm_expr *e; /*@-unqualifiedtrans@*/ *ep = yasm_expr__level_tree(*ep, 1, indexreg == 0, calc_bc_dist, NULL, NULL, NULL); if (*wrt) *wrt = yasm_expr__level_tree(*wrt, 1, indexreg == 0, calc_bc_dist, NULL, NULL, NULL); /*@=unqualifiedtrans@*/ assert(*ep != NULL); e = *ep; switch (x86_expr_checkea_distcheck_reg(ep, bits)) { case 0: return 1; case 2: /* Need to simplify again */ *ep = yasm_expr__level_tree(*ep, 1, indexreg == 0, NULL, NULL, NULL, NULL); e = *ep; break; default: break; } if (*wrt && (*wrt)->op == YASM_EXPR_IDENT && (*wrt)->terms[0].type == YASM_EXPR_REG) { /* Handle xx WRT rip. */ if (bits != 64) /* only valid in 64-bit mode */ return 1; reg = get_reg(&(*wrt)->terms[0], ®num, data); if (!reg || regnum != 16) /* only accept rip */ return 1; (*reg)++; /* Delete WRT. Set pcrel to 1 to indicate to x86 * bytecode code to do PC-relative displacement transform. */ *pcrel = 1; yasm_expr_destroy(*wrt); /* Drill down to next WRT and recurse if there was one. */ *wrt = yasm_expr_extract_wrt(ep); if (*wrt) return x86_expr_checkea_getregusage(ep, wrt, indexreg, pcrel, bits, data, get_reg, calc_bc_dist); } switch (e->op) { case YASM_EXPR_ADD: /* Prescan for non-int multipliers against a reg. * This is because if any of the terms is a more complex * expr (eg, undetermined value), we don't want to try to * figure out *any* of the expression, because each register * lookup overwrites the register with a 0 value! And storing * the state of this routine from one excution to the next * would be a major chore. */ for (i=0; i<e->numterms; i++) if (e->terms[i].type == YASM_EXPR_EXPR) { yasm_expr__order_terms(e->terms[i].data.expn); if (e->terms[i].data.expn->terms[0].type == YASM_EXPR_REG) { if (e->terms[i].data.expn->numterms > 2) return 2; if (e->terms[i].data.expn->terms[1].type != YASM_EXPR_INT) return 2; } } /*@fallthrough@*/ case YASM_EXPR_IDENT: /* Check each term for register (and possible multiplier). */ for (i=0; i<e->numterms; i++) { if (e->terms[i].type == YASM_EXPR_REG) { reg = get_reg(&e->terms[i], ®num, data); if (!reg) return 1; (*reg)++; /* Let last, largest multipler win indexreg */ if (indexreg && *reg > 0 && indexval <= *reg && !indexmult) { *indexreg = regnum; indexval = *reg; } } else if (e->terms[i].type == YASM_EXPR_EXPR) { /* Already ordered from ADD above, just grab the value. * Sanity check for EXPR_INT. */ if (e->terms[i].data.expn->terms[0].type == YASM_EXPR_REG) { if (e->terms[i].data.expn->terms[1].type != YASM_EXPR_INT) yasm_internal_error( N_("Non-integer value in reg expn")); reg = get_reg(&e->terms[i].data.expn->terms[0], ®num, data); if (!reg) return 1; (*reg) += yasm_intnum_get_int( e->terms[i].data.expn->terms[1].data.intn); /* Let last, largest multipler win indexreg */ if (indexreg && *reg > 0 && indexval <= *reg) { *indexreg = regnum; indexval = *reg; indexmult = 1; } } } } break; case YASM_EXPR_MUL: /* Here, too, check for non-int multipliers against a reg. */ yasm_expr__order_terms(e); if (e->terms[0].type == YASM_EXPR_REG) { if (e->numterms > 2) return 2; if (e->terms[1].type != YASM_EXPR_INT) return 2; reg = get_reg(&e->terms[0], ®num, data); if (!reg) return 1; (*reg) += yasm_intnum_get_int(e->terms[1].data.intn); if (indexreg) *indexreg = regnum; } break; default: /* Should never get here! */ yasm_internal_error(N_("unexpected expr op")); } /* Simplify expr, which is now really just the displacement. This * should get rid of the 0's we put in for registers in the callback. */ *ep = yasm_expr_simplify(*ep, NULL); /* e = *ep; */ return 0;}/* 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. * dispreq=1 if a displacement value is *required* (even if =0). * Returns 0 if successfully calculated, 1 if not. *//*@-nullstate@*/static intx86_checkea_calc_displen(yasm_expr **ep, unsigned int wordsize, int noreg, int dispreq, unsigned char *displen, unsigned char *modrm, unsigned char *v_modrm){ yasm_expr *e = *ep; const yasm_intnum *intn; long dispval; *v_modrm = 0; /* default to not yet valid */ switch (*displen) { case 0: break; /* If not 0, the displacement length was forced; set the Mod bits * appropriately and we're done with the ModRM byte. */ case 1: /* Byte is not valid override in noreg case; fix it. */ if (noreg) { *displen = 0; yasm__warning(YASM_WARN_GENERAL, e->line, N_("invalid displacement size; fixed")); } else *modrm |= 0100; *v_modrm = 1; break; case 2: case 4: if (wordsize != *displen) { yasm__error(e->line, N_("invalid effective address (displacement size)")); return 1; } /* 2/4 is not valid override in noreg case; fix it. */ if (noreg) { if (wordsize != *displen) yasm__warning(YASM_WARN_GENERAL, e->line, N_("invalid displacement size; fixed")); *displen = 0; } else *modrm |= 0200; *v_modrm = 1; break; default: /* we shouldn't ever get any other size! */ yasm_internal_error(N_("strange EA displacement size")); } if (*displen == 0) { /* the displacement length hasn't been forced (or the forcing wasn't * valid), 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; return 0; } else if (dispreq) { /* 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 = yasm_expr_get_intnum(ep, NULL); if (!intn) { /* expr still has unknown values: assume 16/32-bit disp */ *displen = wordsize; *modrm |= 0200; *v_modrm = 1; return 0; } /* don't try to find out what size displacement we have if * displen is known. */ if (*displen != 0 && *displen != 0xff) { if (*displen == 1) *modrm |= 0100; else *modrm |= 0200; *v_modrm = 1; return 0; } dispval = yasm_intnum_get_int(intn);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -