/* * 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); }