From: Peter Johnson Date: Mon, 18 Mar 2002 07:16:52 +0000 (-0000) Subject: Restructure bc_calc_len() family into bc_resolve() family. Many many many X-Git-Tag: v0.1.0~12 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=fe8c2a009f177fc2dddd1ffe192e3b933ae0d32b;p=yasm Restructure bc_calc_len() family into bc_resolve() family. Many many many bugfixes to the bc_tobytes() family and other functions. Binary object output is now close to complete: just a few more bugs to kill! svn path=/trunk/yasm/; revision=528 --- diff --git a/libyasm/arch.h b/libyasm/arch.h index 4fb5aa58..86779b04 100644 --- a/libyasm/arch.h +++ b/libyasm/arch.h @@ -39,14 +39,13 @@ struct arch { void (*bc_delete) (bytecode *bc); void (*bc_print) (FILE *f, const bytecode *bc); - /* See bytecode.h comments on bc_calc_len() */ - int (*bc_calc_len) (bytecode *bc, const section *sect, - resolve_label_func resolve_label); + /* See bytecode.h comments on bc_resolve() */ + int (*bc_resolve) (bytecode *bc, int save, const section *sect, + resolve_label_func resolve_label); /* See bytecode.h comments on bc_tobytes() */ int (*bc_tobytes) (bytecode *bc, unsigned char **bufp, const section *sect, void *d, - output_expr_func output_expr, - resolve_label_func resolve_label); + output_expr_func output_expr); } bc; }; diff --git a/libyasm/bytecode.c b/libyasm/bytecode.c index 6b78ebe5..dc13e510 100644 --- a/libyasm/bytecode.c +++ b/libyasm/bytecode.c @@ -293,7 +293,7 @@ bc_print(FILE *f, const bytecode *bc) } static int -bc_calc_len_data(bytecode_data *bc_data, unsigned long *len) +bc_resolve_data(bytecode_data *bc_data, unsigned long *len) { dataval *dv; size_t slen; @@ -319,17 +319,24 @@ bc_calc_len_data(bytecode_data *bc_data, unsigned long *len) } static int -bc_calc_len_reserve(bytecode_reserve *reserve, unsigned long *len, - const section *sect, resolve_label_func resolve_label) +bc_resolve_reserve(bytecode_reserve *reserve, unsigned long *len, int save, + const section *sect, resolve_label_func resolve_label) { int retval = 1; /*@null@*/ expr *temp; + expr **tempp; /*@dependent@*/ /*@null@*/ const intnum *num; - temp = expr_copy(reserve->numitems); - assert(temp != NULL); - expr_expand_labelequ(temp, sect, 1, resolve_label); - num = expr_get_intnum(&temp); + if (save) { + temp = NULL; + tempp = &reserve->numitems; + } else { + temp = expr_copy(reserve->numitems); + assert(temp != NULL); + tempp = &temp; + } + expr_expand_labelequ(*tempp, sect, 1, resolve_label); + num = expr_get_intnum(tempp); if (!num) retval = -1; else @@ -339,21 +346,28 @@ bc_calc_len_reserve(bytecode_reserve *reserve, unsigned long *len, } static int -bc_calc_len_incbin(bytecode_incbin *incbin, unsigned long *len, - unsigned long line, const section *sect, - resolve_label_func resolve_label) +bc_resolve_incbin(bytecode_incbin *incbin, unsigned long *len, int save, + unsigned long line, const section *sect, + resolve_label_func resolve_label) { FILE *f; /*@null@*/ expr *temp; + expr **tempp; /*@dependent@*/ /*@null@*/ const intnum *num; unsigned long start = 0, maxlen = 0xFFFFFFFFUL, flen; /* Try to convert start to integer value */ if (incbin->start) { - temp = expr_copy(incbin->start); - assert(temp != NULL); - expr_expand_labelequ(temp, sect, 1, resolve_label); - num = expr_get_intnum(&temp); + if (save) { + temp = NULL; + tempp = &incbin->start; + } else { + temp = expr_copy(incbin->start); + assert(temp != NULL); + tempp = &temp; + } + expr_expand_labelequ(*tempp, sect, 1, resolve_label); + num = expr_get_intnum(tempp); if (num) start = intnum_get_uint(num); expr_delete(temp); @@ -363,10 +377,16 @@ bc_calc_len_incbin(bytecode_incbin *incbin, unsigned long *len, /* Try to convert maxlen to integer value */ if (incbin->maxlen) { - temp = expr_copy(incbin->maxlen); - assert(temp != NULL); - expr_expand_labelequ(temp, sect, 1, resolve_label); - num = expr_get_intnum(&temp); + if (save) { + temp = NULL; + tempp = &incbin->maxlen; + } else { + temp = expr_copy(incbin->maxlen); + assert(temp != NULL); + tempp = &temp; + } + expr_expand_labelequ(*tempp, sect, 1, resolve_label); + num = expr_get_intnum(tempp); if (num) maxlen = intnum_get_uint(num); expr_delete(temp); @@ -374,7 +394,9 @@ bc_calc_len_incbin(bytecode_incbin *incbin, unsigned long *len, return -1; } - /* FIXME: Search include path for filename */ + /* FIXME: Search include path for filename. Save full path back into + * filename if save is true. + */ /* Open file and determine its length */ f = fopen(incbin->filename, "rb"); @@ -406,14 +428,15 @@ bc_calc_len_incbin(bytecode_incbin *incbin, unsigned long *len, } int -bc_calc_len(bytecode *bc, const section *sect, - resolve_label_func resolve_label) +bc_resolve(bytecode *bc, int save, const section *sect, + resolve_label_func resolve_label) { int retval = 1; bytecode_data *bc_data; bytecode_reserve *reserve; bytecode_incbin *incbin; /*@null@*/ expr *temp; + expr **tempp; /*@dependent@*/ /*@null@*/ const intnum *num; bc->len = 0; /* start at 0 */ @@ -423,31 +446,38 @@ bc_calc_len(bytecode *bc, const section *sect, InternalError(_("got empty bytecode in bc_calc_len")); case BC_DATA: bc_data = bc_get_data(bc); - retval = bc_calc_len_data(bc_data, &bc->len); + retval = bc_resolve_data(bc_data, &bc->len); break; case BC_RESERVE: reserve = bc_get_data(bc); - retval = bc_calc_len_reserve(reserve, &bc->len, sect, - resolve_label); + retval = bc_resolve_reserve(reserve, &bc->len, save, sect, + resolve_label); break; case BC_INCBIN: incbin = bc_get_data(bc); - retval = bc_calc_len_incbin(incbin, &bc->len, bc->line, sect, - resolve_label); + retval = bc_resolve_incbin(incbin, &bc->len, save, bc->line, sect, + resolve_label); break; default: if (bc->type < cur_arch->bc.type_max) - retval = cur_arch->bc.bc_calc_len(bc, sect, resolve_label); + retval = cur_arch->bc.bc_resolve(bc, save, sect, + resolve_label); else InternalError(_("Unknown bytecode type")); } /* Multiply len by number of multiples */ if (bc->multiple) { - temp = expr_copy(bc->multiple); - assert(temp != NULL); - expr_expand_labelequ(temp, sect, 1, resolve_label); - num = expr_get_intnum(&temp); + if (save) { + temp = NULL; + tempp = &bc->multiple; + } else { + temp = expr_copy(bc->multiple); + assert(temp != NULL); + tempp = &temp; + } + expr_expand_labelequ(*tempp, sect, 1, resolve_label); + num = expr_get_intnum(tempp); if (!num) retval = -1; else @@ -496,21 +526,14 @@ bc_tobytes_data(bytecode_data *bc_data, unsigned char **bufp, static int bc_tobytes_reserve(bytecode_reserve *reserve, unsigned char **bufp, - const section *sect, resolve_label_func resolve_label) + unsigned long len) { - /*@dependent@*/ /*@null@*/ const intnum *num; - unsigned long numitems, i; - - expr_expand_labelequ(reserve->numitems, sect, 1, resolve_label); - num = expr_get_intnum(&reserve->numitems); - if (!num) - InternalError(_("could not determine number of items in bc_tobytes_reserve")); - numitems = intnum_get_uint(num)*reserve->itemsize; + unsigned long i; /* Go ahead and zero the bytes. Probably most objfmts will want it * zero'd if they're actually going to output it. */ - for (i=0; istart) { - expr_expand_labelequ(incbin->start, sect, 1, resolve_label); num = expr_get_intnum(&incbin->start); if (!num) InternalError(_("could not determine start in bc_tobytes_incbin")); start = intnum_get_uint(num); } - /* FIXME: Search include path for filename */ - /* Open file */ f = fopen(incbin->filename, "rb"); if (!f) { @@ -552,15 +571,15 @@ bc_tobytes_incbin(bytecode_incbin *incbin, unsigned char **bufp, return 1; } - /* Read buflen bytes */ - if (fread(*bufp, buflen, 1, f) < buflen) { + /* Read len bytes */ + if (fread(*bufp, len, 1, f) < len) { ErrorAt(line, _("`incbin': unable to read %lu bytes from file `%s'"), - buflen, incbin->filename); + len, incbin->filename); fclose(f); return 1; } - *bufp += buflen; + *bufp += len; fclose(f); return 0; } @@ -568,22 +587,36 @@ bc_tobytes_incbin(bytecode_incbin *incbin, unsigned char **bufp, /*@null@*/ /*@only@*/ unsigned char * bc_tobytes(bytecode *bc, unsigned char *buf, unsigned long *bufsize, /*@out@*/ unsigned long *multiple, /*@out@*/ int *gap, - const section *sect, void *d, output_expr_func output_expr, - resolve_label_func resolve_label) + const section *sect, void *d, output_expr_func output_expr) { /*@only@*/ /*@null@*/ unsigned char *mybuf = NULL; - unsigned char *destbuf; + unsigned char *origbuf, *destbuf; /*@dependent@*/ /*@null@*/ const intnum *num; bytecode_data *bc_data; bytecode_reserve *reserve; bytecode_incbin *incbin; + unsigned long datasize; int error = 0; - if (*bufsize < bc->len) { + if (bc->multiple) { + num = expr_get_intnum(&bc->multiple); + if (!num) + InternalError(_("could not determine multiple in bc_tobytes")); + *multiple = intnum_get_uint(num); + } else + *multiple = 1; + + datasize = bc->len / (*multiple); + + if (*bufsize < datasize) { mybuf = xmalloc(sizeof(bc->len)); + origbuf = mybuf; destbuf = mybuf; - } else + } else { + origbuf = buf; destbuf = buf; + } + *bufsize = datasize; *gap = 0; @@ -597,33 +630,23 @@ bc_tobytes(bytecode *bc, unsigned char *buf, unsigned long *bufsize, break; case BC_RESERVE: reserve = bc_get_data(bc); - error = bc_tobytes_reserve(reserve, &destbuf, sect, resolve_label); + error = bc_tobytes_reserve(reserve, &destbuf, bc->len); *gap = 1; break; case BC_INCBIN: incbin = bc_get_data(bc); - error = bc_tobytes_incbin(incbin, &destbuf, bc->len, bc->line, - sect, resolve_label); + error = bc_tobytes_incbin(incbin, &destbuf, bc->len, bc->line); break; default: if (bc->type < cur_arch->bc.type_max) error = cur_arch->bc.bc_tobytes(bc, &destbuf, sect, d, - output_expr, resolve_label); + output_expr); else InternalError(_("Unknown bytecode type")); } - if (bc->multiple) { - expr_expand_labelequ(bc->multiple, sect, 1, resolve_label); - num = expr_get_intnum(&bc->multiple); - if (!num) - InternalError(_("could not determine multiple in bc_tobytes")); - *multiple = intnum_get_uint(num); - } else - *multiple = 1; - if (!error && ((destbuf - buf) != bc->len)) + if (!error && ((destbuf - origbuf) != datasize)) InternalError(_("written length does not match optimized length")); - *bufsize = bc->len; return mybuf; } diff --git a/libyasm/bytecode.h b/libyasm/bytecode.h index f011b6ec..0dbf5c4d 100644 --- a/libyasm/bytecode.h +++ b/libyasm/bytecode.h @@ -58,18 +58,22 @@ void bc_delete(/*@only@*/ /*@null@*/ bytecode *bc); void bc_print(FILE *f, const bytecode *bc); -/* Calculates length of bytecode, saving in bc structure. +/* Resolves labels in bytecode, and calculates its length. + * Tries to minimize the length as much as possible. * Returns whether the length is the minimum possible (1=yes, 0=no). * Returns -1 if the length was indeterminate. + * Note: sometimes it's impossible to determine if a length is the minimum + * possible. In this case, this function returns that the length is NOT + * the minimum. * resolve_label is the function used to determine the value (offset) of a * in-file label (eg, not an EXTERN variable, which is indeterminate). - * This function does *not* modify bc other than the length/size values (eg - * it doesn't keep the values returned by resolve_label except temporarily to - * try to minimize the length). - * sect is passed along to resolve_label. + * When save is zero, this function does *not* modify bc other than the + * length/size values (i.e. it doesn't keep the values returned by + * resolve_label except temporarily to try to minimize the length). + * When save is nonzero, all fields in bc may be modified by this function. */ -int bc_calc_len(bytecode *bc, const section *sect, - resolve_label_func resolve_label); +int bc_resolve(bytecode *bc, int save, const section *sect, + resolve_label_func resolve_label); /* Converts the bytecode bc into its byte representation. * Inputs: @@ -78,17 +82,6 @@ int bc_calc_len(bytecode *bc, const section *sect, * bufsize - the size of buf * d - the data to pass to each call to output_expr() * output_expr - the function to call to convert expressions to byte rep - * output_expr inputs: - * bc - the bytecode containing the expr that is being output - * ep - a pointer to the expression to output - * bufp - pointer to pointer to buffer to contain byte representation - * valsize - the size (in bytes) to be used for the byte rep - * d - the data passed into bc_tobytes - * output_expr returns nonzero if an error occurred, 0 otherwise - * resolve_label - the function to call to determine the values of - * expressions that are *not* output to the file - * resolve_label inputs: - * sym - the symbol to resolve * Outputs: * bufsize - the size of the generated data. * multiple - the number of times the data should be dup'ed when output @@ -102,7 +95,7 @@ int bc_calc_len(bytecode *bc, const section *sect, /*@null@*/ /*@only@*/ unsigned char *bc_tobytes(bytecode *bc, unsigned char *buf, unsigned long *bufsize, /*@out@*/ unsigned long *multiple, /*@out@*/ int *gap, const section *sect, - void *d, output_expr_func output_expr, resolve_label_func resolve_label); + void *d, output_expr_func output_expr); /* void bcs_initialize(bytecodehead *headp); */ #define bcs_initialize(headp) STAILQ_INIT(headp) diff --git a/modules/arch/x86/x86-int.h b/modules/arch/x86/x86-int.h index ce867271..f027c3a6 100644 --- a/modules/arch/x86/x86-int.h +++ b/modules/arch/x86/x86-int.h @@ -96,11 +96,10 @@ typedef struct x86_jmprel { void x86_bc_delete(bytecode *bc); void x86_bc_print(FILE *f, const bytecode *bc); -int x86_bc_calc_len(bytecode *bc, const section *sect, - resolve_label_func resolve_label); -int x86_bc_tobytes(bytecode *bc, unsigned char **bufp, const section *sect, - void *d, output_expr_func output_expr, +int x86_bc_resolve(bytecode *bc, int save, const section *sect, resolve_label_func resolve_label); +int x86_bc_tobytes(bytecode *bc, unsigned char **bufp, const section *sect, + void *d, output_expr_func output_expr); int x86_expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, unsigned char nosplit, unsigned char *displen, diff --git a/modules/arch/x86/x86arch.c b/modules/arch/x86/x86arch.c index 3d8bec49..755e8bed 100644 --- a/modules/arch/x86/x86arch.c +++ b/modules/arch/x86/x86arch.c @@ -38,7 +38,7 @@ arch x86_arch = { X86_BYTECODE_TYPE_MAX, x86_bc_delete, x86_bc_print, - x86_bc_calc_len, + x86_bc_resolve, x86_bc_tobytes } }; diff --git a/modules/arch/x86/x86bc.c b/modules/arch/x86/x86bc.c index 939b8999..7a0f06fd 100644 --- a/modules/arch/x86/x86bc.c +++ b/modules/arch/x86/x86bc.c @@ -463,8 +463,8 @@ x86_bc_print(FILE *f, const bytecode *bc) } static int -x86_bc_calc_len_insn(x86_insn *insn, unsigned long *len, const section *sect, - resolve_label_func resolve_label) +x86_bc_resolve_insn(x86_insn *insn, unsigned long *len, int save, + const section *sect, resolve_label_func resolve_label) { /*@null@*/ expr *temp; effaddr *ea = insn->ea; @@ -499,35 +499,25 @@ x86_bc_calc_len_insn(x86_insn *insn, unsigned long *len, const section *sect, return -1; /* failed, don't bother checking rest of insn */ } - if (!temp) { - /* If the expression was deleted (temp=NULL), then make the - * temp info permanent. - */ + expr_delete(temp); - /* Delete the "real" expression */ - expr_delete(ea->disp); - ea->disp = NULL; - *ead = ead_t; /* structure copy */ - ea->len = displen; - } else if (displen == 1) { - /* Fits into a byte. We'll assume it never gets bigger, so - * make temp info permanent, but NOT the expr itself (as that - * may change). - */ - expr_delete(temp); - *ead = ead_t; /* structure copy */ - ea->len = displen; - } else { - /* Fits into a word/dword, or unknown. As this /may/ change in - * a future pass, so discard temp info. - */ - expr_delete(temp); + if (displen != 1) { + /* Fits into a word/dword, or unknown. */ retval = 0; /* may not be smallest size */ /* Handle unknown case, make displen word-sized */ if (displen == 0xff) displen = (insn->addrsize == 32) ? 4 : 2; } + + if (save) { + *ead = ead_t; /* structure copy */ + ea->len = displen; + if (displen == 0 && ea->disp) { + expr_delete(ea->disp); + ea->disp = NULL; + } + } } /* Compute length of ea and add to total */ @@ -552,8 +542,21 @@ x86_bc_calc_len_insn(x86_insn *insn, unsigned long *len, const section *sect, * (as we add it back in below). */ *len -= imm->len; + + if (save) { + /* Make the ,1 form permanent. */ + insn->opcode[0] = insn->opcode[1]; + /* Delete imm, as it's not needed. */ + expr_delete(imm->val); + xfree(imm); + insn->imm = (immval *)NULL; + } } else retval = 0; /* we could still get ,1 */ + + /* Not really necessary, but saves confusion over it. */ + if (save) + insn->shift_op = 0; } expr_delete(temp); @@ -571,9 +574,9 @@ x86_bc_calc_len_insn(x86_insn *insn, unsigned long *len, const section *sect, } static int -x86_bc_calc_len_jmprel(x86_jmprel *jmprel, unsigned long *len, - unsigned long offset, const section *sect, - resolve_label_func resolve_label) +x86_bc_resolve_jmprel(x86_jmprel *jmprel, unsigned long *len, int save, + const bytecode *bc, const section *sect, + resolve_label_func resolve_label) { int retval = 1; /*@null@*/ expr *temp; @@ -587,17 +590,44 @@ x86_bc_calc_len_jmprel(x86_jmprel *jmprel, unsigned long *len, opersize = (jmprel->opersize == 0) ? jmprel->mode_bits : jmprel->opersize; - /* We don't check here to see if forced forms are actually legal; we - * assume that they are, and only check it in x86_bc_tobytes_jmprel(). + /* We only check to see if forced forms are actually legal if we're in + * save mode. Otherwise we assume that they are legal. */ switch (jmprel->op_sel) { case JR_SHORT_FORCED: /* 1 byte relative displacement */ jrshort = 1; + if (save) { + if (!num) { + ErrorAt(bc->line, + _("short jump target external or out of segment")); + return -1; + } else { + target = intnum_get_uint(num); + rel = (long)(target - + (bc->offset+jmprel->shortop.opcode_len+1)); + /* does a short form exist? */ + if (jmprel->shortop.opcode_len == 0) { + ErrorAt(bc->line, _("short jump does not exist")); + return -1; + } + /* short displacement must fit in -128 <= rel <= +127 */ + if (rel < -128 || rel > 127) { + ErrorAt(bc->line, _("short jump out of range")); + return -1; + } + } + } break; case JR_NEAR_FORCED: /* 2/4 byte relative displacement (depending on operand size) */ jrshort = 0; + if (save) { + if (jmprel->nearop.opcode_len == 0) { + ErrorAt(bc->line, _("near jump does not exist")); + return -1; + } + } break; default: /* Try to find shortest displacement based on difference between @@ -611,42 +641,63 @@ x86_bc_calc_len_jmprel(x86_jmprel *jmprel, unsigned long *len, num = expr_get_intnum(&temp); if (num) { target = intnum_get_uint(num); - rel = (long)(target-(offset+jmprel->shortop.opcode_len+1)); + rel = (long)(target-(bc->offset+jmprel->shortop.opcode_len+1)); /* short displacement must fit within -128 <= rel <= +127 */ if (jmprel->shortop.opcode_len != 0 && rel >= -128 && rel <= 127) { /* It fits into a short displacement. */ jrshort = 1; - } else { + } else if (jmprel->nearop.opcode_len != 0) { /* Near for now, but could get shorter in the future if * there's a short form available. */ jrshort = 0; if (jmprel->shortop.opcode_len != 0) retval = 0; + } else { + /* Doesn't fit into short, and there's no near opcode. + * Error out if saving, otherwise just make it a short + * (in the hopes that a short might make it possible for + * it to actually be within short range). + */ + if (save) { + ErrorAt(bc->line, _("short jump out of range")); + return -1; + } + jrshort = 1; } } else { - /* It's unknown (e.g. out of this segment or external). - * Thus, assume near displacement. If a near opcode is not - * available, use a short opcode instead. + /* It's unknown. Thus, assume near displacement. If a near + * opcode is not available, use a short opcode instead. + * If we're saving, error if a near opcode is not available. */ if (jmprel->nearop.opcode_len != 0) { if (jmprel->shortop.opcode_len != 0) retval = 0; jrshort = 0; - } else + } else { + if (save) { + ErrorAt(bc->line, + _("short jump target or out of segment")); + return -1; + } jrshort = 1; + } } expr_delete(temp); break; } if (jrshort) { + if (save) + jmprel->op_sel = JR_SHORT; if (jmprel->shortop.opcode_len == 0) return -1; /* uh-oh, that size not available */ *len += jmprel->shortop.opcode_len + 1; } else { + if (save) + jmprel->op_sel = JR_NEAR; if (jmprel->nearop.opcode_len == 0) return -1; /* uh-oh, that size not available */ @@ -663,8 +714,8 @@ x86_bc_calc_len_jmprel(x86_jmprel *jmprel, unsigned long *len, } int -x86_bc_calc_len(bytecode *bc, const section *sect, - resolve_label_func resolve_label) +x86_bc_resolve(bytecode *bc, int save, const section *sect, + resolve_label_func resolve_label) { x86_insn *insn; x86_jmprel *jmprel; @@ -672,11 +723,12 @@ x86_bc_calc_len(bytecode *bc, const section *sect, switch ((x86_bytecode_type)bc->type) { case X86_BC_INSN: insn = bc_get_data(bc); - return x86_bc_calc_len_insn(insn, &bc->len, sect, resolve_label); + return x86_bc_resolve_insn(insn, &bc->len, save, sect, + resolve_label); case X86_BC_JMPREL: jmprel = bc_get_data(bc); - return x86_bc_calc_len_jmprel(jmprel, &bc->len, bc->offset, sect, - resolve_label); + return x86_bc_resolve_jmprel(jmprel, &bc->len, save, bc, sect, + resolve_label); default: break; } @@ -685,57 +737,13 @@ x86_bc_calc_len(bytecode *bc, const section *sect, static int x86_bc_tobytes_insn(x86_insn *insn, unsigned char **bufp, const section *sect, - const bytecode *bc, void *d, output_expr_func output_expr, - resolve_label_func resolve_label) + const bytecode *bc, void *d, output_expr_func output_expr) { /*@null@*/ effaddr *ea = insn->ea; x86_effaddr_data *ead = ea_get_data(ea); immval *imm = insn->imm; unsigned int i; - /* We need to figure out the EA first to determine the addrsize. - * Of course, the ModR/M, SIB, and displacement are not output until later. - */ - if (ea) { - if ((ea->disp) && ((!ead->valid_sib && ead->need_sib) || - (!ead->valid_modrm && ead->need_modrm))) { - /* Expand equ's and labels */ - expr_expand_labelequ(ea->disp, sect, 1, resolve_label); - - /* Check validity of effective address and calc R/M bits of - * Mod/RM byte and SIB byte. We won't know the Mod field - * of the Mod/RM byte until we know more about the - * displacement. - */ - if (!x86_expr_checkea(&ea->disp, &insn->addrsize, insn->mode_bits, - ea->nosplit, &ea->len, &ead->modrm, - &ead->valid_modrm, &ead->need_modrm, - &ead->sib, &ead->valid_sib, - &ead->need_sib)) - InternalError(_("expr_checkea failed from x86 tobytes_insn")); - } - } - - /* Also check for shift_op special-casing (affects imm). */ - if (insn->shift_op && imm && imm->val) { - /*@dependent@*/ /*@null@*/ const intnum *num; - - expr_expand_labelequ(imm->val, sect, 1, resolve_label); - - num = expr_get_intnum(&imm->val); - if (num) { - if (intnum_get_uint(num) == 1) { - /* Use ,1 form: first copy ,1 opcode. */ - insn->opcode[0] = insn->opcode[1]; - /* Delete imm, as it's not needed. */ - expr_delete(imm->val); - xfree(imm); - insn->imm = (immval *)NULL; - } - insn->shift_op = 0; - } - } - /* Prefixes */ if (insn->lockrep_pre != 0) WRITE_BYTE(*bufp, insn->lockrep_pre); @@ -766,9 +774,35 @@ x86_bc_tobytes_insn(x86_insn *insn, unsigned char **bufp, const section *sect, WRITE_BYTE(*bufp, ead->sib); } - if (ea->disp) - if (output_expr(&ea->disp, bufp, ea->len, sect, bc, 0, d)) - return 1; + if (ea->disp) { + x86_effaddr_data ead_t = *ead; /* structure copy */ + unsigned char displen = ea->len; + unsigned char addrsize = insn->addrsize; + + ead_t.valid_modrm = 0; /* force checkea to actually run */ + + /* Call checkea() to simplify the registers out of the + * displacement. Throw away all of the return values except for + * the modified expr. + */ + if (!x86_expr_checkea(&ea->disp, &addrsize, insn->mode_bits, + ea->nosplit, &displen, &ead_t.modrm, + &ead_t.valid_modrm, &ead_t.need_modrm, + &ead_t.sib, &ead_t.valid_sib, + &ead_t.need_sib)) + InternalError(_("checkea failed")); + + if (ea->disp) { + if (output_expr(&ea->disp, bufp, ea->len, sect, bc, 0, d)) + return 1; + } else { + /* 0 displacement, but we didn't know it before, so we have to + * write out 0 value. + */ + for (i=0; ilen; i++) + WRITE_BYTE(*bufp, 0); + } + } } /* Immediate (if required) */ @@ -784,14 +818,9 @@ x86_bc_tobytes_insn(x86_insn *insn, unsigned char **bufp, const section *sect, static int x86_bc_tobytes_jmprel(x86_jmprel *jmprel, unsigned char **bufp, const section *sect, const bytecode *bc, void *d, - output_expr_func output_expr, - resolve_label_func resolve_label) + output_expr_func output_expr) { - /*@dependent@*/ /*@null@*/ const intnum *num; - unsigned long target; - long rel; unsigned char opersize; - int jrshort = 0; unsigned int i; /* Prefixes */ @@ -807,100 +836,48 @@ x86_bc_tobytes_jmprel(x86_jmprel *jmprel, unsigned char **bufp, opersize = (jmprel->opersize == 0) ? jmprel->mode_bits : jmprel->opersize; - /* Get displacement value here so that forced forms can be checked. */ - expr_expand_labelequ(jmprel->target, sect, 0, resolve_label); - num = expr_get_intnum(&jmprel->target); - /* Check here to see if forced forms are actually legal. */ switch (jmprel->op_sel) { case JR_SHORT_FORCED: + case JR_SHORT: /* 1 byte relative displacement */ - jrshort = 1; - if (!num) { - ErrorAt(bc->line, - _("short jump target external or out of segment")); + if (jmprel->shortop.opcode_len == 0) + InternalError(_("short jump does not exist")); + + /* Opcode */ + for (i=0; ishortop.opcode_len; i++) + WRITE_BYTE(*bufp, jmprel->shortop.opcode[i]); + + /* Relative displacement */ + if (output_expr(&jmprel->target, bufp, 1, sect, bc, 1, d)) return 1; - } else { - target = intnum_get_uint(num); - rel = (long)(target-(bc->offset+jmprel->shortop.opcode_len+1)); - /* does a short form exist? */ - if (jmprel->shortop.opcode_len == 0) { - ErrorAt(bc->line, _("short jump does not exist")); - return 1; - } - /* short displacement must fit within -128 <= rel <= +127 */ - if (rel < -128 || rel > 127) { - ErrorAt(bc->line, _("short jump out of range")); - return 1; - } - } break; case JR_NEAR_FORCED: + case JR_NEAR: /* 2/4 byte relative displacement (depending on operand size) */ - jrshort = 0; if (jmprel->nearop.opcode_len == 0) { ErrorAt(bc->line, _("near jump does not exist")); return 1; } - break; - default: - /* Try to find shortest displacement based on difference between - * target expr value and our (this bytecode's) offset. - */ - if (num) { - target = intnum_get_uint(num); - rel = (long)(target-(bc->offset+jmprel->shortop.opcode_len+1)); - /* short displacement must fit within -128 <= rel <= +127 */ - if (jmprel->shortop.opcode_len != 0 && rel >= -128 && - rel <= 127) { - /* It fits into a short displacement. */ - jrshort = 1; - } else { - /* It's near. */ - jrshort = 0; - if (jmprel->nearop.opcode_len == 0) { - InternalError(_("near jump does not exist")); - return 1; - } - } - } else { - /* It's unknown (e.g. out of this segment or external). - * Thus, assume near displacement. If a near opcode is not - * available, error out. - */ - jrshort = 0; - if (jmprel->nearop.opcode_len == 0) { - ErrorAt(bc->line, - _("short jump target or out of segment")); - return 1; - } - } - break; - } - if (jrshort) { - /* Opcode */ - for (i=0; ishortop.opcode_len; i++) - WRITE_BYTE(*bufp, jmprel->shortop.opcode[i]); + /* Opcode */ + for (i=0; inearop.opcode_len; i++) + WRITE_BYTE(*bufp, jmprel->nearop.opcode[i]); - /* Relative displacement */ - output_expr(&jmprel->target, bufp, 1, sect, bc, 1, d); - } else { - /* Opcode */ - for (i=0; inearop.opcode_len; i++) - WRITE_BYTE(*bufp, jmprel->nearop.opcode[i]); - - /* Relative displacement */ - output_expr(&jmprel->target, bufp, (opersize == 32) ? 4 : 2, sect, bc, - 1, d); + /* Relative displacement */ + if (output_expr(&jmprel->target, bufp, (opersize == 32) ? 4 : 2, + sect, bc, 1, d)) + return 1; + break; + default: + InternalError(_("unrecognized relative jump op_sel")); } return 0; } int x86_bc_tobytes(bytecode *bc, unsigned char **bufp, const section *sect, - void *d, output_expr_func output_expr, - resolve_label_func resolve_label) + void *d, output_expr_func output_expr) { x86_insn *insn; x86_jmprel *jmprel; @@ -908,16 +885,15 @@ x86_bc_tobytes(bytecode *bc, unsigned char **bufp, const section *sect, switch ((x86_bytecode_type)bc->type) { case X86_BC_INSN: insn = bc_get_data(bc); - return x86_bc_tobytes_insn(insn, bufp, sect, bc, d, output_expr, - resolve_label); + return x86_bc_tobytes_insn(insn, bufp, sect, bc, d, output_expr); break; case X86_BC_JMPREL: jmprel = bc_get_data(bc); return x86_bc_tobytes_jmprel(jmprel, bufp, sect, bc, d, - output_expr, resolve_label); + output_expr); default: break; } - return 1; + return 0; } diff --git a/modules/objfmts/bin/bin-objfmt.c b/modules/objfmts/bin/bin-objfmt.c index 5af6dc49..1e8a06cd 100644 --- a/modules/objfmts/bin/bin-objfmt.c +++ b/modules/objfmts/bin/bin-objfmt.c @@ -127,9 +127,6 @@ bin_objfmt_resolve_label2(symrec *sym, /*@null@*/ const section *cursect, startval -= cursectstart; } - /* If a section is done, the following will always succeed. If it's in- - * progress, this will fail if the bytecode comes AFTER the current one. - */ if (precbc) return intnum_new_int(startval + precbc->offset + precbc->len); else @@ -249,8 +246,7 @@ bin_objfmt_output_bytecode(bytecode *bc, /*@null@*/ void *d) int gap; bigbuf = bc_tobytes(bc, info->buf, &size, &multiple, &gap, info->sect, - info, bin_objfmt_output_expr, - bin_objfmt_resolve_label); + info, bin_objfmt_output_expr); /* Warn that gaps are converted to 0. The 0 bytes are generated by * bc_tobytes() so no special handling is needed. diff --git a/modules/optimizers/basic/basic-optimizer.c b/modules/optimizers/basic/basic-optimizer.c index a12ad292..381d44a4 100644 --- a/modules/optimizers/basic/basic-optimizer.c +++ b/modules/optimizers/basic/basic-optimizer.c @@ -94,6 +94,48 @@ basic_optimize_resolve_label(symrec *sym, int withstart) return NULL; } +static /*@only@*/ /*@null@*/ intnum * +basic_optimize_resolve_label_2(symrec *sym, int withstart) +{ + /*@dependent@*/ section *sect; + /*@dependent@*/ /*@null@*/ bytecode *precbc; + /*@null@*/ bytecode *bc; + /*@null@*/ expr *startexpr; + /*@dependent@*/ /*@null@*/ const intnum *start; + unsigned long startval = 0; + + if (!symrec_get_label(sym, §, &precbc)) + return NULL; + + /* determine actual bc from preceding bc (how labels are stored) */ + if (!precbc) + bc = bcs_first(section_get_bytecodes(sect)); + else + bc = bcs_next(precbc); + assert(bc != NULL); + + /* Figure out the starting offset of the entire section */ + if (withstart || section_is_absolute(sect)) { + startexpr = expr_copy(section_get_start(sect)); + assert(startexpr != NULL); + expr_expand_labelequ(startexpr, sect, 1, + basic_optimize_resolve_label_2); + start = expr_get_intnum(&startexpr); + if (!start) + return NULL; + startval = intnum_get_uint(start); + expr_delete(startexpr); + } + + /* If a section is done, the following will always succeed. If it's in- + * progress, this will fail if the bytecode comes AFTER the current one. + */ + if (precbc) + return intnum_new_int(startval + precbc->offset + precbc->len); + else + return intnum_new_int(startval + bc->offset); +} + typedef struct basic_optimize_data { bytecode *precbc; const section *sect; @@ -122,7 +164,7 @@ basic_optimize_bytecode_1(bytecode *bc, void *d) * is minimum or not, and just check for indeterminate length (indicative * of circular reference). */ - if (bc_calc_len(bc, data->sect, basic_optimize_resolve_label) < 0) { + if (bc_resolve(bc, 0, data->sect, basic_optimize_resolve_label) < 0) { ErrorAt(bc->line, _("Circular reference detected.")); return -1; } @@ -162,23 +204,41 @@ basic_optimize_section_1(section *sect, /*@unused@*/ /*@null@*/ void *d) } static int -basic_optimize_bytecode_2(bytecode *bc, /*@unused@*/ /*@null@*/ void *d) +basic_optimize_bytecode_2(bytecode *bc, /*@null@*/ void *d) { + basic_optimize_data *data = (basic_optimize_data *)d; + + assert(data != NULL); + if (bc->opt_flags != BCFLAG_DONE) { InternalError(_("Optimizer pass 1 missed a bytecode!")); return -1; } + + if (!data->precbc) + bc->offset = 0; + else + bc->offset = data->precbc->offset + data->precbc->len; + data->precbc = bc; + + if (bc_resolve(bc, 1, data->sect, basic_optimize_resolve_label_2) < 0) + return -1; return 0; } static int basic_optimize_section_2(section *sect, /*@unused@*/ /*@null@*/ void *d) { + basic_optimize_data data; + + data.precbc = NULL; + data.sect = sect; + if (section_get_opt_flags(sect) != SECTFLAG_DONE) { InternalError(_("Optimizer pass 1 missed a section!")); return -1; } - return bcs_traverse(section_get_bytecodes(sect), NULL, + return bcs_traverse(section_get_bytecodes(sect), &data, basic_optimize_bytecode_2); } @@ -200,7 +260,7 @@ basic_optimize(sectionhead *sections) if (sections_traverse(sections, NULL, basic_optimize_section_1) < 0) return; - /* FIXME: Really necessary? Check completion of all sections */ + /* Check completion of all sections and save bytecode changes */ sections_traverse(sections, NULL, basic_optimize_section_2); } diff --git a/src/arch.h b/src/arch.h index 4fb5aa58..86779b04 100644 --- a/src/arch.h +++ b/src/arch.h @@ -39,14 +39,13 @@ struct arch { void (*bc_delete) (bytecode *bc); void (*bc_print) (FILE *f, const bytecode *bc); - /* See bytecode.h comments on bc_calc_len() */ - int (*bc_calc_len) (bytecode *bc, const section *sect, - resolve_label_func resolve_label); + /* See bytecode.h comments on bc_resolve() */ + int (*bc_resolve) (bytecode *bc, int save, const section *sect, + resolve_label_func resolve_label); /* See bytecode.h comments on bc_tobytes() */ int (*bc_tobytes) (bytecode *bc, unsigned char **bufp, const section *sect, void *d, - output_expr_func output_expr, - resolve_label_func resolve_label); + output_expr_func output_expr); } bc; }; diff --git a/src/arch/x86/x86-int.h b/src/arch/x86/x86-int.h index ce867271..f027c3a6 100644 --- a/src/arch/x86/x86-int.h +++ b/src/arch/x86/x86-int.h @@ -96,11 +96,10 @@ typedef struct x86_jmprel { void x86_bc_delete(bytecode *bc); void x86_bc_print(FILE *f, const bytecode *bc); -int x86_bc_calc_len(bytecode *bc, const section *sect, - resolve_label_func resolve_label); -int x86_bc_tobytes(bytecode *bc, unsigned char **bufp, const section *sect, - void *d, output_expr_func output_expr, +int x86_bc_resolve(bytecode *bc, int save, const section *sect, resolve_label_func resolve_label); +int x86_bc_tobytes(bytecode *bc, unsigned char **bufp, const section *sect, + void *d, output_expr_func output_expr); int x86_expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits, unsigned char nosplit, unsigned char *displen, diff --git a/src/arch/x86/x86arch.c b/src/arch/x86/x86arch.c index 3d8bec49..755e8bed 100644 --- a/src/arch/x86/x86arch.c +++ b/src/arch/x86/x86arch.c @@ -38,7 +38,7 @@ arch x86_arch = { X86_BYTECODE_TYPE_MAX, x86_bc_delete, x86_bc_print, - x86_bc_calc_len, + x86_bc_resolve, x86_bc_tobytes } }; diff --git a/src/arch/x86/x86bc.c b/src/arch/x86/x86bc.c index 939b8999..7a0f06fd 100644 --- a/src/arch/x86/x86bc.c +++ b/src/arch/x86/x86bc.c @@ -463,8 +463,8 @@ x86_bc_print(FILE *f, const bytecode *bc) } static int -x86_bc_calc_len_insn(x86_insn *insn, unsigned long *len, const section *sect, - resolve_label_func resolve_label) +x86_bc_resolve_insn(x86_insn *insn, unsigned long *len, int save, + const section *sect, resolve_label_func resolve_label) { /*@null@*/ expr *temp; effaddr *ea = insn->ea; @@ -499,35 +499,25 @@ x86_bc_calc_len_insn(x86_insn *insn, unsigned long *len, const section *sect, return -1; /* failed, don't bother checking rest of insn */ } - if (!temp) { - /* If the expression was deleted (temp=NULL), then make the - * temp info permanent. - */ + expr_delete(temp); - /* Delete the "real" expression */ - expr_delete(ea->disp); - ea->disp = NULL; - *ead = ead_t; /* structure copy */ - ea->len = displen; - } else if (displen == 1) { - /* Fits into a byte. We'll assume it never gets bigger, so - * make temp info permanent, but NOT the expr itself (as that - * may change). - */ - expr_delete(temp); - *ead = ead_t; /* structure copy */ - ea->len = displen; - } else { - /* Fits into a word/dword, or unknown. As this /may/ change in - * a future pass, so discard temp info. - */ - expr_delete(temp); + if (displen != 1) { + /* Fits into a word/dword, or unknown. */ retval = 0; /* may not be smallest size */ /* Handle unknown case, make displen word-sized */ if (displen == 0xff) displen = (insn->addrsize == 32) ? 4 : 2; } + + if (save) { + *ead = ead_t; /* structure copy */ + ea->len = displen; + if (displen == 0 && ea->disp) { + expr_delete(ea->disp); + ea->disp = NULL; + } + } } /* Compute length of ea and add to total */ @@ -552,8 +542,21 @@ x86_bc_calc_len_insn(x86_insn *insn, unsigned long *len, const section *sect, * (as we add it back in below). */ *len -= imm->len; + + if (save) { + /* Make the ,1 form permanent. */ + insn->opcode[0] = insn->opcode[1]; + /* Delete imm, as it's not needed. */ + expr_delete(imm->val); + xfree(imm); + insn->imm = (immval *)NULL; + } } else retval = 0; /* we could still get ,1 */ + + /* Not really necessary, but saves confusion over it. */ + if (save) + insn->shift_op = 0; } expr_delete(temp); @@ -571,9 +574,9 @@ x86_bc_calc_len_insn(x86_insn *insn, unsigned long *len, const section *sect, } static int -x86_bc_calc_len_jmprel(x86_jmprel *jmprel, unsigned long *len, - unsigned long offset, const section *sect, - resolve_label_func resolve_label) +x86_bc_resolve_jmprel(x86_jmprel *jmprel, unsigned long *len, int save, + const bytecode *bc, const section *sect, + resolve_label_func resolve_label) { int retval = 1; /*@null@*/ expr *temp; @@ -587,17 +590,44 @@ x86_bc_calc_len_jmprel(x86_jmprel *jmprel, unsigned long *len, opersize = (jmprel->opersize == 0) ? jmprel->mode_bits : jmprel->opersize; - /* We don't check here to see if forced forms are actually legal; we - * assume that they are, and only check it in x86_bc_tobytes_jmprel(). + /* We only check to see if forced forms are actually legal if we're in + * save mode. Otherwise we assume that they are legal. */ switch (jmprel->op_sel) { case JR_SHORT_FORCED: /* 1 byte relative displacement */ jrshort = 1; + if (save) { + if (!num) { + ErrorAt(bc->line, + _("short jump target external or out of segment")); + return -1; + } else { + target = intnum_get_uint(num); + rel = (long)(target - + (bc->offset+jmprel->shortop.opcode_len+1)); + /* does a short form exist? */ + if (jmprel->shortop.opcode_len == 0) { + ErrorAt(bc->line, _("short jump does not exist")); + return -1; + } + /* short displacement must fit in -128 <= rel <= +127 */ + if (rel < -128 || rel > 127) { + ErrorAt(bc->line, _("short jump out of range")); + return -1; + } + } + } break; case JR_NEAR_FORCED: /* 2/4 byte relative displacement (depending on operand size) */ jrshort = 0; + if (save) { + if (jmprel->nearop.opcode_len == 0) { + ErrorAt(bc->line, _("near jump does not exist")); + return -1; + } + } break; default: /* Try to find shortest displacement based on difference between @@ -611,42 +641,63 @@ x86_bc_calc_len_jmprel(x86_jmprel *jmprel, unsigned long *len, num = expr_get_intnum(&temp); if (num) { target = intnum_get_uint(num); - rel = (long)(target-(offset+jmprel->shortop.opcode_len+1)); + rel = (long)(target-(bc->offset+jmprel->shortop.opcode_len+1)); /* short displacement must fit within -128 <= rel <= +127 */ if (jmprel->shortop.opcode_len != 0 && rel >= -128 && rel <= 127) { /* It fits into a short displacement. */ jrshort = 1; - } else { + } else if (jmprel->nearop.opcode_len != 0) { /* Near for now, but could get shorter in the future if * there's a short form available. */ jrshort = 0; if (jmprel->shortop.opcode_len != 0) retval = 0; + } else { + /* Doesn't fit into short, and there's no near opcode. + * Error out if saving, otherwise just make it a short + * (in the hopes that a short might make it possible for + * it to actually be within short range). + */ + if (save) { + ErrorAt(bc->line, _("short jump out of range")); + return -1; + } + jrshort = 1; } } else { - /* It's unknown (e.g. out of this segment or external). - * Thus, assume near displacement. If a near opcode is not - * available, use a short opcode instead. + /* It's unknown. Thus, assume near displacement. If a near + * opcode is not available, use a short opcode instead. + * If we're saving, error if a near opcode is not available. */ if (jmprel->nearop.opcode_len != 0) { if (jmprel->shortop.opcode_len != 0) retval = 0; jrshort = 0; - } else + } else { + if (save) { + ErrorAt(bc->line, + _("short jump target or out of segment")); + return -1; + } jrshort = 1; + } } expr_delete(temp); break; } if (jrshort) { + if (save) + jmprel->op_sel = JR_SHORT; if (jmprel->shortop.opcode_len == 0) return -1; /* uh-oh, that size not available */ *len += jmprel->shortop.opcode_len + 1; } else { + if (save) + jmprel->op_sel = JR_NEAR; if (jmprel->nearop.opcode_len == 0) return -1; /* uh-oh, that size not available */ @@ -663,8 +714,8 @@ x86_bc_calc_len_jmprel(x86_jmprel *jmprel, unsigned long *len, } int -x86_bc_calc_len(bytecode *bc, const section *sect, - resolve_label_func resolve_label) +x86_bc_resolve(bytecode *bc, int save, const section *sect, + resolve_label_func resolve_label) { x86_insn *insn; x86_jmprel *jmprel; @@ -672,11 +723,12 @@ x86_bc_calc_len(bytecode *bc, const section *sect, switch ((x86_bytecode_type)bc->type) { case X86_BC_INSN: insn = bc_get_data(bc); - return x86_bc_calc_len_insn(insn, &bc->len, sect, resolve_label); + return x86_bc_resolve_insn(insn, &bc->len, save, sect, + resolve_label); case X86_BC_JMPREL: jmprel = bc_get_data(bc); - return x86_bc_calc_len_jmprel(jmprel, &bc->len, bc->offset, sect, - resolve_label); + return x86_bc_resolve_jmprel(jmprel, &bc->len, save, bc, sect, + resolve_label); default: break; } @@ -685,57 +737,13 @@ x86_bc_calc_len(bytecode *bc, const section *sect, static int x86_bc_tobytes_insn(x86_insn *insn, unsigned char **bufp, const section *sect, - const bytecode *bc, void *d, output_expr_func output_expr, - resolve_label_func resolve_label) + const bytecode *bc, void *d, output_expr_func output_expr) { /*@null@*/ effaddr *ea = insn->ea; x86_effaddr_data *ead = ea_get_data(ea); immval *imm = insn->imm; unsigned int i; - /* We need to figure out the EA first to determine the addrsize. - * Of course, the ModR/M, SIB, and displacement are not output until later. - */ - if (ea) { - if ((ea->disp) && ((!ead->valid_sib && ead->need_sib) || - (!ead->valid_modrm && ead->need_modrm))) { - /* Expand equ's and labels */ - expr_expand_labelequ(ea->disp, sect, 1, resolve_label); - - /* Check validity of effective address and calc R/M bits of - * Mod/RM byte and SIB byte. We won't know the Mod field - * of the Mod/RM byte until we know more about the - * displacement. - */ - if (!x86_expr_checkea(&ea->disp, &insn->addrsize, insn->mode_bits, - ea->nosplit, &ea->len, &ead->modrm, - &ead->valid_modrm, &ead->need_modrm, - &ead->sib, &ead->valid_sib, - &ead->need_sib)) - InternalError(_("expr_checkea failed from x86 tobytes_insn")); - } - } - - /* Also check for shift_op special-casing (affects imm). */ - if (insn->shift_op && imm && imm->val) { - /*@dependent@*/ /*@null@*/ const intnum *num; - - expr_expand_labelequ(imm->val, sect, 1, resolve_label); - - num = expr_get_intnum(&imm->val); - if (num) { - if (intnum_get_uint(num) == 1) { - /* Use ,1 form: first copy ,1 opcode. */ - insn->opcode[0] = insn->opcode[1]; - /* Delete imm, as it's not needed. */ - expr_delete(imm->val); - xfree(imm); - insn->imm = (immval *)NULL; - } - insn->shift_op = 0; - } - } - /* Prefixes */ if (insn->lockrep_pre != 0) WRITE_BYTE(*bufp, insn->lockrep_pre); @@ -766,9 +774,35 @@ x86_bc_tobytes_insn(x86_insn *insn, unsigned char **bufp, const section *sect, WRITE_BYTE(*bufp, ead->sib); } - if (ea->disp) - if (output_expr(&ea->disp, bufp, ea->len, sect, bc, 0, d)) - return 1; + if (ea->disp) { + x86_effaddr_data ead_t = *ead; /* structure copy */ + unsigned char displen = ea->len; + unsigned char addrsize = insn->addrsize; + + ead_t.valid_modrm = 0; /* force checkea to actually run */ + + /* Call checkea() to simplify the registers out of the + * displacement. Throw away all of the return values except for + * the modified expr. + */ + if (!x86_expr_checkea(&ea->disp, &addrsize, insn->mode_bits, + ea->nosplit, &displen, &ead_t.modrm, + &ead_t.valid_modrm, &ead_t.need_modrm, + &ead_t.sib, &ead_t.valid_sib, + &ead_t.need_sib)) + InternalError(_("checkea failed")); + + if (ea->disp) { + if (output_expr(&ea->disp, bufp, ea->len, sect, bc, 0, d)) + return 1; + } else { + /* 0 displacement, but we didn't know it before, so we have to + * write out 0 value. + */ + for (i=0; ilen; i++) + WRITE_BYTE(*bufp, 0); + } + } } /* Immediate (if required) */ @@ -784,14 +818,9 @@ x86_bc_tobytes_insn(x86_insn *insn, unsigned char **bufp, const section *sect, static int x86_bc_tobytes_jmprel(x86_jmprel *jmprel, unsigned char **bufp, const section *sect, const bytecode *bc, void *d, - output_expr_func output_expr, - resolve_label_func resolve_label) + output_expr_func output_expr) { - /*@dependent@*/ /*@null@*/ const intnum *num; - unsigned long target; - long rel; unsigned char opersize; - int jrshort = 0; unsigned int i; /* Prefixes */ @@ -807,100 +836,48 @@ x86_bc_tobytes_jmprel(x86_jmprel *jmprel, unsigned char **bufp, opersize = (jmprel->opersize == 0) ? jmprel->mode_bits : jmprel->opersize; - /* Get displacement value here so that forced forms can be checked. */ - expr_expand_labelequ(jmprel->target, sect, 0, resolve_label); - num = expr_get_intnum(&jmprel->target); - /* Check here to see if forced forms are actually legal. */ switch (jmprel->op_sel) { case JR_SHORT_FORCED: + case JR_SHORT: /* 1 byte relative displacement */ - jrshort = 1; - if (!num) { - ErrorAt(bc->line, - _("short jump target external or out of segment")); + if (jmprel->shortop.opcode_len == 0) + InternalError(_("short jump does not exist")); + + /* Opcode */ + for (i=0; ishortop.opcode_len; i++) + WRITE_BYTE(*bufp, jmprel->shortop.opcode[i]); + + /* Relative displacement */ + if (output_expr(&jmprel->target, bufp, 1, sect, bc, 1, d)) return 1; - } else { - target = intnum_get_uint(num); - rel = (long)(target-(bc->offset+jmprel->shortop.opcode_len+1)); - /* does a short form exist? */ - if (jmprel->shortop.opcode_len == 0) { - ErrorAt(bc->line, _("short jump does not exist")); - return 1; - } - /* short displacement must fit within -128 <= rel <= +127 */ - if (rel < -128 || rel > 127) { - ErrorAt(bc->line, _("short jump out of range")); - return 1; - } - } break; case JR_NEAR_FORCED: + case JR_NEAR: /* 2/4 byte relative displacement (depending on operand size) */ - jrshort = 0; if (jmprel->nearop.opcode_len == 0) { ErrorAt(bc->line, _("near jump does not exist")); return 1; } - break; - default: - /* Try to find shortest displacement based on difference between - * target expr value and our (this bytecode's) offset. - */ - if (num) { - target = intnum_get_uint(num); - rel = (long)(target-(bc->offset+jmprel->shortop.opcode_len+1)); - /* short displacement must fit within -128 <= rel <= +127 */ - if (jmprel->shortop.opcode_len != 0 && rel >= -128 && - rel <= 127) { - /* It fits into a short displacement. */ - jrshort = 1; - } else { - /* It's near. */ - jrshort = 0; - if (jmprel->nearop.opcode_len == 0) { - InternalError(_("near jump does not exist")); - return 1; - } - } - } else { - /* It's unknown (e.g. out of this segment or external). - * Thus, assume near displacement. If a near opcode is not - * available, error out. - */ - jrshort = 0; - if (jmprel->nearop.opcode_len == 0) { - ErrorAt(bc->line, - _("short jump target or out of segment")); - return 1; - } - } - break; - } - if (jrshort) { - /* Opcode */ - for (i=0; ishortop.opcode_len; i++) - WRITE_BYTE(*bufp, jmprel->shortop.opcode[i]); + /* Opcode */ + for (i=0; inearop.opcode_len; i++) + WRITE_BYTE(*bufp, jmprel->nearop.opcode[i]); - /* Relative displacement */ - output_expr(&jmprel->target, bufp, 1, sect, bc, 1, d); - } else { - /* Opcode */ - for (i=0; inearop.opcode_len; i++) - WRITE_BYTE(*bufp, jmprel->nearop.opcode[i]); - - /* Relative displacement */ - output_expr(&jmprel->target, bufp, (opersize == 32) ? 4 : 2, sect, bc, - 1, d); + /* Relative displacement */ + if (output_expr(&jmprel->target, bufp, (opersize == 32) ? 4 : 2, + sect, bc, 1, d)) + return 1; + break; + default: + InternalError(_("unrecognized relative jump op_sel")); } return 0; } int x86_bc_tobytes(bytecode *bc, unsigned char **bufp, const section *sect, - void *d, output_expr_func output_expr, - resolve_label_func resolve_label) + void *d, output_expr_func output_expr) { x86_insn *insn; x86_jmprel *jmprel; @@ -908,16 +885,15 @@ x86_bc_tobytes(bytecode *bc, unsigned char **bufp, const section *sect, switch ((x86_bytecode_type)bc->type) { case X86_BC_INSN: insn = bc_get_data(bc); - return x86_bc_tobytes_insn(insn, bufp, sect, bc, d, output_expr, - resolve_label); + return x86_bc_tobytes_insn(insn, bufp, sect, bc, d, output_expr); break; case X86_BC_JMPREL: jmprel = bc_get_data(bc); return x86_bc_tobytes_jmprel(jmprel, bufp, sect, bc, d, - output_expr, resolve_label); + output_expr); default: break; } - return 1; + return 0; } diff --git a/src/bytecode.c b/src/bytecode.c index 6b78ebe5..dc13e510 100644 --- a/src/bytecode.c +++ b/src/bytecode.c @@ -293,7 +293,7 @@ bc_print(FILE *f, const bytecode *bc) } static int -bc_calc_len_data(bytecode_data *bc_data, unsigned long *len) +bc_resolve_data(bytecode_data *bc_data, unsigned long *len) { dataval *dv; size_t slen; @@ -319,17 +319,24 @@ bc_calc_len_data(bytecode_data *bc_data, unsigned long *len) } static int -bc_calc_len_reserve(bytecode_reserve *reserve, unsigned long *len, - const section *sect, resolve_label_func resolve_label) +bc_resolve_reserve(bytecode_reserve *reserve, unsigned long *len, int save, + const section *sect, resolve_label_func resolve_label) { int retval = 1; /*@null@*/ expr *temp; + expr **tempp; /*@dependent@*/ /*@null@*/ const intnum *num; - temp = expr_copy(reserve->numitems); - assert(temp != NULL); - expr_expand_labelequ(temp, sect, 1, resolve_label); - num = expr_get_intnum(&temp); + if (save) { + temp = NULL; + tempp = &reserve->numitems; + } else { + temp = expr_copy(reserve->numitems); + assert(temp != NULL); + tempp = &temp; + } + expr_expand_labelequ(*tempp, sect, 1, resolve_label); + num = expr_get_intnum(tempp); if (!num) retval = -1; else @@ -339,21 +346,28 @@ bc_calc_len_reserve(bytecode_reserve *reserve, unsigned long *len, } static int -bc_calc_len_incbin(bytecode_incbin *incbin, unsigned long *len, - unsigned long line, const section *sect, - resolve_label_func resolve_label) +bc_resolve_incbin(bytecode_incbin *incbin, unsigned long *len, int save, + unsigned long line, const section *sect, + resolve_label_func resolve_label) { FILE *f; /*@null@*/ expr *temp; + expr **tempp; /*@dependent@*/ /*@null@*/ const intnum *num; unsigned long start = 0, maxlen = 0xFFFFFFFFUL, flen; /* Try to convert start to integer value */ if (incbin->start) { - temp = expr_copy(incbin->start); - assert(temp != NULL); - expr_expand_labelequ(temp, sect, 1, resolve_label); - num = expr_get_intnum(&temp); + if (save) { + temp = NULL; + tempp = &incbin->start; + } else { + temp = expr_copy(incbin->start); + assert(temp != NULL); + tempp = &temp; + } + expr_expand_labelequ(*tempp, sect, 1, resolve_label); + num = expr_get_intnum(tempp); if (num) start = intnum_get_uint(num); expr_delete(temp); @@ -363,10 +377,16 @@ bc_calc_len_incbin(bytecode_incbin *incbin, unsigned long *len, /* Try to convert maxlen to integer value */ if (incbin->maxlen) { - temp = expr_copy(incbin->maxlen); - assert(temp != NULL); - expr_expand_labelequ(temp, sect, 1, resolve_label); - num = expr_get_intnum(&temp); + if (save) { + temp = NULL; + tempp = &incbin->maxlen; + } else { + temp = expr_copy(incbin->maxlen); + assert(temp != NULL); + tempp = &temp; + } + expr_expand_labelequ(*tempp, sect, 1, resolve_label); + num = expr_get_intnum(tempp); if (num) maxlen = intnum_get_uint(num); expr_delete(temp); @@ -374,7 +394,9 @@ bc_calc_len_incbin(bytecode_incbin *incbin, unsigned long *len, return -1; } - /* FIXME: Search include path for filename */ + /* FIXME: Search include path for filename. Save full path back into + * filename if save is true. + */ /* Open file and determine its length */ f = fopen(incbin->filename, "rb"); @@ -406,14 +428,15 @@ bc_calc_len_incbin(bytecode_incbin *incbin, unsigned long *len, } int -bc_calc_len(bytecode *bc, const section *sect, - resolve_label_func resolve_label) +bc_resolve(bytecode *bc, int save, const section *sect, + resolve_label_func resolve_label) { int retval = 1; bytecode_data *bc_data; bytecode_reserve *reserve; bytecode_incbin *incbin; /*@null@*/ expr *temp; + expr **tempp; /*@dependent@*/ /*@null@*/ const intnum *num; bc->len = 0; /* start at 0 */ @@ -423,31 +446,38 @@ bc_calc_len(bytecode *bc, const section *sect, InternalError(_("got empty bytecode in bc_calc_len")); case BC_DATA: bc_data = bc_get_data(bc); - retval = bc_calc_len_data(bc_data, &bc->len); + retval = bc_resolve_data(bc_data, &bc->len); break; case BC_RESERVE: reserve = bc_get_data(bc); - retval = bc_calc_len_reserve(reserve, &bc->len, sect, - resolve_label); + retval = bc_resolve_reserve(reserve, &bc->len, save, sect, + resolve_label); break; case BC_INCBIN: incbin = bc_get_data(bc); - retval = bc_calc_len_incbin(incbin, &bc->len, bc->line, sect, - resolve_label); + retval = bc_resolve_incbin(incbin, &bc->len, save, bc->line, sect, + resolve_label); break; default: if (bc->type < cur_arch->bc.type_max) - retval = cur_arch->bc.bc_calc_len(bc, sect, resolve_label); + retval = cur_arch->bc.bc_resolve(bc, save, sect, + resolve_label); else InternalError(_("Unknown bytecode type")); } /* Multiply len by number of multiples */ if (bc->multiple) { - temp = expr_copy(bc->multiple); - assert(temp != NULL); - expr_expand_labelequ(temp, sect, 1, resolve_label); - num = expr_get_intnum(&temp); + if (save) { + temp = NULL; + tempp = &bc->multiple; + } else { + temp = expr_copy(bc->multiple); + assert(temp != NULL); + tempp = &temp; + } + expr_expand_labelequ(*tempp, sect, 1, resolve_label); + num = expr_get_intnum(tempp); if (!num) retval = -1; else @@ -496,21 +526,14 @@ bc_tobytes_data(bytecode_data *bc_data, unsigned char **bufp, static int bc_tobytes_reserve(bytecode_reserve *reserve, unsigned char **bufp, - const section *sect, resolve_label_func resolve_label) + unsigned long len) { - /*@dependent@*/ /*@null@*/ const intnum *num; - unsigned long numitems, i; - - expr_expand_labelequ(reserve->numitems, sect, 1, resolve_label); - num = expr_get_intnum(&reserve->numitems); - if (!num) - InternalError(_("could not determine number of items in bc_tobytes_reserve")); - numitems = intnum_get_uint(num)*reserve->itemsize; + unsigned long i; /* Go ahead and zero the bytes. Probably most objfmts will want it * zero'd if they're actually going to output it. */ - for (i=0; istart) { - expr_expand_labelequ(incbin->start, sect, 1, resolve_label); num = expr_get_intnum(&incbin->start); if (!num) InternalError(_("could not determine start in bc_tobytes_incbin")); start = intnum_get_uint(num); } - /* FIXME: Search include path for filename */ - /* Open file */ f = fopen(incbin->filename, "rb"); if (!f) { @@ -552,15 +571,15 @@ bc_tobytes_incbin(bytecode_incbin *incbin, unsigned char **bufp, return 1; } - /* Read buflen bytes */ - if (fread(*bufp, buflen, 1, f) < buflen) { + /* Read len bytes */ + if (fread(*bufp, len, 1, f) < len) { ErrorAt(line, _("`incbin': unable to read %lu bytes from file `%s'"), - buflen, incbin->filename); + len, incbin->filename); fclose(f); return 1; } - *bufp += buflen; + *bufp += len; fclose(f); return 0; } @@ -568,22 +587,36 @@ bc_tobytes_incbin(bytecode_incbin *incbin, unsigned char **bufp, /*@null@*/ /*@only@*/ unsigned char * bc_tobytes(bytecode *bc, unsigned char *buf, unsigned long *bufsize, /*@out@*/ unsigned long *multiple, /*@out@*/ int *gap, - const section *sect, void *d, output_expr_func output_expr, - resolve_label_func resolve_label) + const section *sect, void *d, output_expr_func output_expr) { /*@only@*/ /*@null@*/ unsigned char *mybuf = NULL; - unsigned char *destbuf; + unsigned char *origbuf, *destbuf; /*@dependent@*/ /*@null@*/ const intnum *num; bytecode_data *bc_data; bytecode_reserve *reserve; bytecode_incbin *incbin; + unsigned long datasize; int error = 0; - if (*bufsize < bc->len) { + if (bc->multiple) { + num = expr_get_intnum(&bc->multiple); + if (!num) + InternalError(_("could not determine multiple in bc_tobytes")); + *multiple = intnum_get_uint(num); + } else + *multiple = 1; + + datasize = bc->len / (*multiple); + + if (*bufsize < datasize) { mybuf = xmalloc(sizeof(bc->len)); + origbuf = mybuf; destbuf = mybuf; - } else + } else { + origbuf = buf; destbuf = buf; + } + *bufsize = datasize; *gap = 0; @@ -597,33 +630,23 @@ bc_tobytes(bytecode *bc, unsigned char *buf, unsigned long *bufsize, break; case BC_RESERVE: reserve = bc_get_data(bc); - error = bc_tobytes_reserve(reserve, &destbuf, sect, resolve_label); + error = bc_tobytes_reserve(reserve, &destbuf, bc->len); *gap = 1; break; case BC_INCBIN: incbin = bc_get_data(bc); - error = bc_tobytes_incbin(incbin, &destbuf, bc->len, bc->line, - sect, resolve_label); + error = bc_tobytes_incbin(incbin, &destbuf, bc->len, bc->line); break; default: if (bc->type < cur_arch->bc.type_max) error = cur_arch->bc.bc_tobytes(bc, &destbuf, sect, d, - output_expr, resolve_label); + output_expr); else InternalError(_("Unknown bytecode type")); } - if (bc->multiple) { - expr_expand_labelequ(bc->multiple, sect, 1, resolve_label); - num = expr_get_intnum(&bc->multiple); - if (!num) - InternalError(_("could not determine multiple in bc_tobytes")); - *multiple = intnum_get_uint(num); - } else - *multiple = 1; - if (!error && ((destbuf - buf) != bc->len)) + if (!error && ((destbuf - origbuf) != datasize)) InternalError(_("written length does not match optimized length")); - *bufsize = bc->len; return mybuf; } diff --git a/src/bytecode.h b/src/bytecode.h index f011b6ec..0dbf5c4d 100644 --- a/src/bytecode.h +++ b/src/bytecode.h @@ -58,18 +58,22 @@ void bc_delete(/*@only@*/ /*@null@*/ bytecode *bc); void bc_print(FILE *f, const bytecode *bc); -/* Calculates length of bytecode, saving in bc structure. +/* Resolves labels in bytecode, and calculates its length. + * Tries to minimize the length as much as possible. * Returns whether the length is the minimum possible (1=yes, 0=no). * Returns -1 if the length was indeterminate. + * Note: sometimes it's impossible to determine if a length is the minimum + * possible. In this case, this function returns that the length is NOT + * the minimum. * resolve_label is the function used to determine the value (offset) of a * in-file label (eg, not an EXTERN variable, which is indeterminate). - * This function does *not* modify bc other than the length/size values (eg - * it doesn't keep the values returned by resolve_label except temporarily to - * try to minimize the length). - * sect is passed along to resolve_label. + * When save is zero, this function does *not* modify bc other than the + * length/size values (i.e. it doesn't keep the values returned by + * resolve_label except temporarily to try to minimize the length). + * When save is nonzero, all fields in bc may be modified by this function. */ -int bc_calc_len(bytecode *bc, const section *sect, - resolve_label_func resolve_label); +int bc_resolve(bytecode *bc, int save, const section *sect, + resolve_label_func resolve_label); /* Converts the bytecode bc into its byte representation. * Inputs: @@ -78,17 +82,6 @@ int bc_calc_len(bytecode *bc, const section *sect, * bufsize - the size of buf * d - the data to pass to each call to output_expr() * output_expr - the function to call to convert expressions to byte rep - * output_expr inputs: - * bc - the bytecode containing the expr that is being output - * ep - a pointer to the expression to output - * bufp - pointer to pointer to buffer to contain byte representation - * valsize - the size (in bytes) to be used for the byte rep - * d - the data passed into bc_tobytes - * output_expr returns nonzero if an error occurred, 0 otherwise - * resolve_label - the function to call to determine the values of - * expressions that are *not* output to the file - * resolve_label inputs: - * sym - the symbol to resolve * Outputs: * bufsize - the size of the generated data. * multiple - the number of times the data should be dup'ed when output @@ -102,7 +95,7 @@ int bc_calc_len(bytecode *bc, const section *sect, /*@null@*/ /*@only@*/ unsigned char *bc_tobytes(bytecode *bc, unsigned char *buf, unsigned long *bufsize, /*@out@*/ unsigned long *multiple, /*@out@*/ int *gap, const section *sect, - void *d, output_expr_func output_expr, resolve_label_func resolve_label); + void *d, output_expr_func output_expr); /* void bcs_initialize(bytecodehead *headp); */ #define bcs_initialize(headp) STAILQ_INIT(headp) diff --git a/src/objfmts/bin/bin-objfmt.c b/src/objfmts/bin/bin-objfmt.c index 5af6dc49..1e8a06cd 100644 --- a/src/objfmts/bin/bin-objfmt.c +++ b/src/objfmts/bin/bin-objfmt.c @@ -127,9 +127,6 @@ bin_objfmt_resolve_label2(symrec *sym, /*@null@*/ const section *cursect, startval -= cursectstart; } - /* If a section is done, the following will always succeed. If it's in- - * progress, this will fail if the bytecode comes AFTER the current one. - */ if (precbc) return intnum_new_int(startval + precbc->offset + precbc->len); else @@ -249,8 +246,7 @@ bin_objfmt_output_bytecode(bytecode *bc, /*@null@*/ void *d) int gap; bigbuf = bc_tobytes(bc, info->buf, &size, &multiple, &gap, info->sect, - info, bin_objfmt_output_expr, - bin_objfmt_resolve_label); + info, bin_objfmt_output_expr); /* Warn that gaps are converted to 0. The 0 bytes are generated by * bc_tobytes() so no special handling is needed. diff --git a/src/optimizers/basic/basic-optimizer.c b/src/optimizers/basic/basic-optimizer.c index a12ad292..381d44a4 100644 --- a/src/optimizers/basic/basic-optimizer.c +++ b/src/optimizers/basic/basic-optimizer.c @@ -94,6 +94,48 @@ basic_optimize_resolve_label(symrec *sym, int withstart) return NULL; } +static /*@only@*/ /*@null@*/ intnum * +basic_optimize_resolve_label_2(symrec *sym, int withstart) +{ + /*@dependent@*/ section *sect; + /*@dependent@*/ /*@null@*/ bytecode *precbc; + /*@null@*/ bytecode *bc; + /*@null@*/ expr *startexpr; + /*@dependent@*/ /*@null@*/ const intnum *start; + unsigned long startval = 0; + + if (!symrec_get_label(sym, §, &precbc)) + return NULL; + + /* determine actual bc from preceding bc (how labels are stored) */ + if (!precbc) + bc = bcs_first(section_get_bytecodes(sect)); + else + bc = bcs_next(precbc); + assert(bc != NULL); + + /* Figure out the starting offset of the entire section */ + if (withstart || section_is_absolute(sect)) { + startexpr = expr_copy(section_get_start(sect)); + assert(startexpr != NULL); + expr_expand_labelequ(startexpr, sect, 1, + basic_optimize_resolve_label_2); + start = expr_get_intnum(&startexpr); + if (!start) + return NULL; + startval = intnum_get_uint(start); + expr_delete(startexpr); + } + + /* If a section is done, the following will always succeed. If it's in- + * progress, this will fail if the bytecode comes AFTER the current one. + */ + if (precbc) + return intnum_new_int(startval + precbc->offset + precbc->len); + else + return intnum_new_int(startval + bc->offset); +} + typedef struct basic_optimize_data { bytecode *precbc; const section *sect; @@ -122,7 +164,7 @@ basic_optimize_bytecode_1(bytecode *bc, void *d) * is minimum or not, and just check for indeterminate length (indicative * of circular reference). */ - if (bc_calc_len(bc, data->sect, basic_optimize_resolve_label) < 0) { + if (bc_resolve(bc, 0, data->sect, basic_optimize_resolve_label) < 0) { ErrorAt(bc->line, _("Circular reference detected.")); return -1; } @@ -162,23 +204,41 @@ basic_optimize_section_1(section *sect, /*@unused@*/ /*@null@*/ void *d) } static int -basic_optimize_bytecode_2(bytecode *bc, /*@unused@*/ /*@null@*/ void *d) +basic_optimize_bytecode_2(bytecode *bc, /*@null@*/ void *d) { + basic_optimize_data *data = (basic_optimize_data *)d; + + assert(data != NULL); + if (bc->opt_flags != BCFLAG_DONE) { InternalError(_("Optimizer pass 1 missed a bytecode!")); return -1; } + + if (!data->precbc) + bc->offset = 0; + else + bc->offset = data->precbc->offset + data->precbc->len; + data->precbc = bc; + + if (bc_resolve(bc, 1, data->sect, basic_optimize_resolve_label_2) < 0) + return -1; return 0; } static int basic_optimize_section_2(section *sect, /*@unused@*/ /*@null@*/ void *d) { + basic_optimize_data data; + + data.precbc = NULL; + data.sect = sect; + if (section_get_opt_flags(sect) != SECTFLAG_DONE) { InternalError(_("Optimizer pass 1 missed a section!")); return -1; } - return bcs_traverse(section_get_bytecodes(sect), NULL, + return bcs_traverse(section_get_bytecodes(sect), &data, basic_optimize_bytecode_2); } @@ -200,7 +260,7 @@ basic_optimize(sectionhead *sections) if (sections_traverse(sections, NULL, basic_optimize_section_1) < 0) return; - /* FIXME: Really necessary? Check completion of all sections */ + /* Check completion of all sections and save bytecode changes */ sections_traverse(sections, NULL, basic_optimize_section_2); }