diff options
Diffstat (limited to 'libyasm/bc-align.c')
-rw-r--r-- | libyasm/bc-align.c | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/libyasm/bc-align.c b/libyasm/bc-align.c new file mode 100644 index 0000000..2a47882 --- /dev/null +++ b/libyasm/bc-align.c @@ -0,0 +1,245 @@ +/* + * Align bytecode + * + * Copyright (C) 2005-2007 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" + +#include "libyasm-stdint.h" +#include "coretype.h" + +#include "errwarn.h" +#include "intnum.h" +#include "expr.h" + +#include "bytecode.h" + + +typedef struct bytecode_align { + /*@only@*/ yasm_expr *boundary; /* alignment boundary */ + + /* What to fill intervening locations with, NULL if using code_fill */ + /*@only@*/ /*@null@*/ yasm_expr *fill; + + /* Maximum number of bytes to skip, NULL if no maximum. */ + /*@only@*/ /*@null@*/ yasm_expr *maxskip; + + /* Code fill, NULL if using 0 fill */ + /*@null@*/ const unsigned char **code_fill; +} bytecode_align; + +static void bc_align_destroy(void *contents); +static void bc_align_print(const void *contents, FILE *f, int indent_level); +static void bc_align_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc); +static int bc_align_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, + void *add_span_data); +static int bc_align_expand(yasm_bytecode *bc, int span, long old_val, + long new_val, /*@out@*/ long *neg_thres, + /*@out@*/ long *pos_thres); +static int bc_align_tobytes(yasm_bytecode *bc, unsigned char **bufp, + unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + /*@null@*/ yasm_output_reloc_func output_reloc); + +static const yasm_bytecode_callback bc_align_callback = { + bc_align_destroy, + bc_align_print, + bc_align_finalize, + NULL, + bc_align_calc_len, + bc_align_expand, + bc_align_tobytes, + YASM_BC_SPECIAL_OFFSET +}; + + +static void +bc_align_destroy(void *contents) +{ + bytecode_align *align = (bytecode_align *)contents; + if (align->boundary) + yasm_expr_destroy(align->boundary); + if (align->fill) + yasm_expr_destroy(align->fill); + if (align->maxskip) + yasm_expr_destroy(align->maxskip); + yasm_xfree(contents); +} + +static void +bc_align_print(const void *contents, FILE *f, int indent_level) +{ + const bytecode_align *align = (const bytecode_align *)contents; + fprintf(f, "%*s_Align_\n", indent_level, ""); + fprintf(f, "%*sBoundary=", indent_level, ""); + yasm_expr_print(align->boundary, f); + fprintf(f, "\n%*sFill=", indent_level, ""); + yasm_expr_print(align->fill, f); + fprintf(f, "\n%*sMax Skip=", indent_level, ""); + yasm_expr_print(align->maxskip, f); + fprintf(f, "\n"); +} + +static void +bc_align_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) +{ + bytecode_align *align = (bytecode_align *)bc->contents; + if (!yasm_expr_get_intnum(&align->boundary, 0)) + yasm_error_set(YASM_ERROR_NOT_CONSTANT, + N_("align boundary must be a constant")); + if (align->fill && !yasm_expr_get_intnum(&align->fill, 0)) + yasm_error_set(YASM_ERROR_NOT_CONSTANT, + N_("align fill must be a constant")); + if (align->maxskip && !yasm_expr_get_intnum(&align->maxskip, 0)) + yasm_error_set(YASM_ERROR_NOT_CONSTANT, + N_("align maximum skip must be a constant")); +} + +static int +bc_align_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, + void *add_span_data) +{ + long neg_thres = 0; + long pos_thres = 0; + + if (bc_align_expand(bc, 0, 0, (long)bc->offset, &neg_thres, + &pos_thres) < 0) + return -1; + + return 0; +} + +static int +bc_align_expand(yasm_bytecode *bc, int span, long old_val, long new_val, + /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres) +{ + bytecode_align *align = (bytecode_align *)bc->contents; + unsigned long end; + unsigned long boundary = + yasm_intnum_get_uint(yasm_expr_get_intnum(&align->boundary, 0)); + + if (boundary == 0) { + bc->len = 0; + *pos_thres = new_val; + return 0; + } + + end = (unsigned long)new_val; + if ((unsigned long)new_val & (boundary-1)) + end = ((unsigned long)new_val & ~(boundary-1)) + boundary; + + *pos_thres = (long)end; + bc->len = end - (unsigned long)new_val; + + if (align->maxskip) { + unsigned long maxskip = + yasm_intnum_get_uint(yasm_expr_get_intnum(&align->maxskip, 0)); + if (bc->len > maxskip) { + *pos_thres = (long)end-maxskip-1; + bc->len = 0; + } + } + return 1; +} + +static int +bc_align_tobytes(yasm_bytecode *bc, unsigned char **bufp, + unsigned char *bufstart, void *d, + yasm_output_value_func output_value, + /*@unused@*/ yasm_output_reloc_func output_reloc) +{ + bytecode_align *align = (bytecode_align *)bc->contents; + unsigned long len; + unsigned long boundary = + yasm_intnum_get_uint(yasm_expr_get_intnum(&align->boundary, 0)); + + if (boundary == 0) + return 0; + else { + unsigned long end = bc->offset; + if (bc->offset & (boundary-1)) + end = (bc->offset & ~(boundary-1)) + boundary; + len = end - bc->offset; + if (len == 0) + return 0; + if (align->maxskip) { + unsigned long maxskip = + yasm_intnum_get_uint(yasm_expr_get_intnum(&align->maxskip, 0)); + if (len > maxskip) + return 0; + } + } + + if (align->fill) { + unsigned long v; + v = yasm_intnum_get_uint(yasm_expr_get_intnum(&align->fill, 0)); + memset(*bufp, (int)v, len); + *bufp += len; + } else if (align->code_fill) { + unsigned long maxlen = 15; + while (!align->code_fill[maxlen] && maxlen>0) + maxlen--; + if (maxlen == 0) { + yasm_error_set(YASM_ERROR_GENERAL, + N_("could not find any code alignment size")); + return 1; + } + + /* Fill with maximum code fill as much as possible */ + while (len > maxlen) { + memcpy(*bufp, align->code_fill[maxlen], maxlen); + *bufp += maxlen; + len -= maxlen; + } + + if (!align->code_fill[len]) { + yasm_error_set(YASM_ERROR_VALUE, + N_("invalid alignment size %d"), len); + return 1; + } + /* Handle rest of code fill */ + memcpy(*bufp, align->code_fill[len], len); + *bufp += len; + } else { + /* Just fill with 0 */ + memset(*bufp, 0, len); + *bufp += len; + } + return 0; +} + +yasm_bytecode * +yasm_bc_create_align(yasm_expr *boundary, yasm_expr *fill, + yasm_expr *maxskip, const unsigned char **code_fill, + unsigned long line) +{ + bytecode_align *align = yasm_xmalloc(sizeof(bytecode_align)); + + align->boundary = boundary; + align->fill = fill; + align->maxskip = maxskip; + align->code_fill = code_fill; + + return yasm_bc_create_common(&bc_align_callback, align, line); +} |