aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/tools/yasm/modules/objfmts/coff/win64-except.c
diff options
context:
space:
mode:
authorsomov <somov@yandex-team.ru>2022-02-10 16:45:47 +0300
committerDaniil Cherednik <dcherednik@yandex-team.ru>2022-02-10 16:45:47 +0300
commita5950576e397b1909261050b8c7da16db58f10b1 (patch)
tree7ba7677f6a4c3e19e2cefab34d16df2c8963b4d4 /contrib/tools/yasm/modules/objfmts/coff/win64-except.c
parent81eddc8c0b55990194e112b02d127b87d54164a9 (diff)
downloadydb-a5950576e397b1909261050b8c7da16db58f10b1.tar.gz
Restoring authorship annotation for <somov@yandex-team.ru>. Commit 1 of 2.
Diffstat (limited to 'contrib/tools/yasm/modules/objfmts/coff/win64-except.c')
-rw-r--r--contrib/tools/yasm/modules/objfmts/coff/win64-except.c1118
1 files changed, 559 insertions, 559 deletions
diff --git a/contrib/tools/yasm/modules/objfmts/coff/win64-except.c b/contrib/tools/yasm/modules/objfmts/coff/win64-except.c
index 8c8ecbbc49..8fe585df8c 100644
--- a/contrib/tools/yasm/modules/objfmts/coff/win64-except.c
+++ b/contrib/tools/yasm/modules/objfmts/coff/win64-except.c
@@ -1,559 +1,559 @@
-/*
- * Win64 structured exception handling support
- *
- * Copyright (C) 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.h>
-
-#include "coff-objfmt.h"
-
-
-#define UNW_FLAG_EHANDLER 0x01
-#define UNW_FLAG_UHANDLER 0x02
-#define UNW_FLAG_CHAININFO 0x04
-
-/* Bytecode callback function prototypes */
-static void win64_uwinfo_bc_destroy(void *contents);
-static void win64_uwinfo_bc_print(const void *contents, FILE *f,
- int indent_level);
-static void win64_uwinfo_bc_finalize(yasm_bytecode *bc,
- yasm_bytecode *prev_bc);
-static int win64_uwinfo_bc_calc_len
- (yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data);
-static int win64_uwinfo_bc_expand(yasm_bytecode *bc, int span, long old_val,
- long new_val, /*@out@*/ long *neg_thres,
- /*@out@*/ long *pos_thres);
-static int win64_uwinfo_bc_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 void win64_uwcode_bc_destroy(void *contents);
-static void win64_uwcode_bc_print(const void *contents, FILE *f,
- int indent_level);
-static void win64_uwcode_bc_finalize(yasm_bytecode *bc,
- yasm_bytecode *prev_bc);
-static int win64_uwcode_bc_calc_len
- (yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data);
-static int win64_uwcode_bc_expand(yasm_bytecode *bc, int span, long old_val,
- long new_val, /*@out@*/ long *neg_thres,
- /*@out@*/ long *pos_thres);
-static int win64_uwcode_bc_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);
-
-/* Bytecode callback structures */
-static const yasm_bytecode_callback win64_uwinfo_bc_callback = {
- win64_uwinfo_bc_destroy,
- win64_uwinfo_bc_print,
- win64_uwinfo_bc_finalize,
- NULL,
- win64_uwinfo_bc_calc_len,
- win64_uwinfo_bc_expand,
- win64_uwinfo_bc_tobytes,
- 0
-};
-
-static const yasm_bytecode_callback win64_uwcode_bc_callback = {
- win64_uwcode_bc_destroy,
- win64_uwcode_bc_print,
- win64_uwcode_bc_finalize,
- NULL,
- win64_uwcode_bc_calc_len,
- win64_uwcode_bc_expand,
- win64_uwcode_bc_tobytes,
- 0
-};
-
-
-coff_unwind_info *
-yasm_win64__uwinfo_create(void)
-{
- coff_unwind_info *info = yasm_xmalloc(sizeof(coff_unwind_info));
- info->proc = NULL;
- info->prolog = NULL;
- info->ehandler = NULL;
- info->framereg = 0;
- /* Frameoff is really a 4-bit value, scaled by 16 */
- yasm_value_initialize(&info->frameoff, NULL, 8);
- SLIST_INIT(&info->codes);
- yasm_value_initialize(&info->prolog_size, NULL, 8);
- yasm_value_initialize(&info->codes_count, NULL, 8);
- return info;
-}
-
-void
-yasm_win64__uwinfo_destroy(coff_unwind_info *info)
-{
- coff_unwind_code *code;
-
- yasm_value_delete(&info->frameoff);
- yasm_value_delete(&info->prolog_size);
- yasm_value_delete(&info->codes_count);
-
- while (!SLIST_EMPTY(&info->codes)) {
- code = SLIST_FIRST(&info->codes);
- SLIST_REMOVE_HEAD(&info->codes, link);
- yasm_value_delete(&code->off);
- yasm_xfree(code);
- }
- yasm_xfree(info);
-}
-
-void
-yasm_win64__unwind_generate(yasm_section *xdata, coff_unwind_info *info,
- unsigned long line)
-{
- yasm_bytecode *infobc, *codebc = NULL;
- coff_unwind_code *code;
-
- /* 4-byte align the start of unwind info */
- yasm_section_bcs_append(xdata, yasm_bc_create_align(
- yasm_expr_create_ident(yasm_expr_int(yasm_intnum_create_uint(4)),
- line),
- NULL, NULL, NULL, line));
-
- /* Prolog size = end of prolog - start of procedure */
- yasm_value_initialize(&info->prolog_size,
- yasm_expr_create(YASM_EXPR_SUB, yasm_expr_sym(info->prolog),
- yasm_expr_sym(info->proc), line),
- 8);
-
- /* Unwind info */
- infobc = yasm_bc_create_common(&win64_uwinfo_bc_callback, info, line);
- yasm_section_bcs_append(xdata, infobc);
-
- /* Code array */
- SLIST_FOREACH(code, &info->codes, link) {
- codebc = yasm_bc_create_common(&win64_uwcode_bc_callback, code,
- yasm_symrec_get_def_line(code->loc));
- yasm_section_bcs_append(xdata, codebc);
- }
-
- /* Avoid double-free (by code destroy and uwinfo destroy). */
- SLIST_INIT(&info->codes);
-
- /* Number of codes = (Last code - end of info) >> 1 */
- if (!codebc) {
- yasm_value_initialize(&info->codes_count,
- yasm_expr_create_ident(yasm_expr_int(yasm_intnum_create_uint(0)),
- line),
- 8);
- } else {
- yasm_value_initialize(&info->codes_count,
- yasm_expr_create(YASM_EXPR_SHR, yasm_expr_expr(
- yasm_expr_create(YASM_EXPR_SUB, yasm_expr_precbc(codebc),
- yasm_expr_precbc(infobc), line)),
- yasm_expr_int(yasm_intnum_create_uint(1)), line),
- 8);
- }
-
- /* 4-byte align */
- yasm_section_bcs_append(xdata, yasm_bc_create_align(
- yasm_expr_create_ident(yasm_expr_int(yasm_intnum_create_uint(4)),
- line),
- NULL, NULL, NULL, line));
-
- /* Exception handler, if present. Use data bytecode. */
- if (info->ehandler) {
- yasm_datavalhead dvs;
-
- yasm_dvs_initialize(&dvs);
- yasm_dvs_append(&dvs, yasm_dv_create_expr(
- yasm_expr_create_ident(yasm_expr_sym(info->ehandler), line)));
- yasm_section_bcs_append(xdata,
- yasm_bc_create_data(&dvs, 4, 0, NULL, line));
- }
-}
-
-static void
-win64_uwinfo_bc_destroy(void *contents)
-{
- yasm_win64__uwinfo_destroy((coff_unwind_info *)contents);
-}
-
-static void
-win64_uwinfo_bc_print(const void *contents, FILE *f, int indent_level)
-{
- /* TODO */
-}
-
-static void
-win64_uwinfo_bc_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc)
-{
- coff_unwind_info *info = (coff_unwind_info *)bc->contents;
-
- if (yasm_value_finalize(&info->prolog_size, prev_bc))
- yasm_internal_error(N_("prolog size expression too complex"));
-
- if (yasm_value_finalize(&info->codes_count, prev_bc))
- yasm_internal_error(N_("codes count expression too complex"));
-
- if (yasm_value_finalize(&info->frameoff, prev_bc))
- yasm_error_set(YASM_ERROR_VALUE,
- N_("frame offset expression too complex"));
-}
-
-static int
-win64_uwinfo_bc_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
- void *add_span_data)
-{
- coff_unwind_info *info = (coff_unwind_info *)bc->contents;
- /*@only@*/ /*@null@*/ yasm_intnum *intn;
- long intv;
-
- /* Want to make sure prolog size and codes count doesn't exceed
- * byte-size, and scaled frame offset doesn't exceed 4 bits.
- */
- add_span(add_span_data, bc, 1, &info->prolog_size, 0, 255);
- add_span(add_span_data, bc, 2, &info->codes_count, 0, 255);
-
- intn = yasm_value_get_intnum(&info->frameoff, bc, 0);
- if (intn) {
- intv = yasm_intnum_get_int(intn);
- if (intv < 0 || intv > 240)
- yasm_error_set(YASM_ERROR_VALUE,
- N_("frame offset of %ld bytes, must be between 0 and 240"),
- intv);
- else if ((intv & 0xF) != 0)
- yasm_error_set(YASM_ERROR_VALUE,
- N_("frame offset of %ld is not a multiple of 16"), intv);
- yasm_intnum_destroy(intn);
- } else
- add_span(add_span_data, bc, 3, &info->frameoff, 0, 240);
-
- bc->len += 4;
- return 0;
-}
-
-static int
-win64_uwinfo_bc_expand(yasm_bytecode *bc, int span, long old_val, long new_val,
- /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres)
-{
- coff_unwind_info *info = (coff_unwind_info *)bc->contents;
- switch (span) {
- case 1:
- yasm_error_set_xref(yasm_symrec_get_def_line(info->prolog),
- N_("prologue ended here"));
- yasm_error_set(YASM_ERROR_VALUE,
- N_("prologue %ld bytes, must be <256"), new_val);
- return -1;
- case 2:
- yasm_error_set(YASM_ERROR_VALUE,
- N_("%ld unwind codes, maximum of 255"), new_val);
- return -1;
- case 3:
- yasm_error_set(YASM_ERROR_VALUE,
- N_("frame offset of %ld bytes, must be between 0 and 240"),
- new_val);
- return -1;
- default:
- yasm_internal_error(N_("unrecognized span id"));
- }
- return 0;
-}
-
-static int
-win64_uwinfo_bc_tobytes(yasm_bytecode *bc, unsigned char **bufp,
- unsigned char *bufstart, void *d,
- yasm_output_value_func output_value,
- yasm_output_reloc_func output_reloc)
-{
- coff_unwind_info *info = (coff_unwind_info *)bc->contents;
- unsigned char *buf = *bufp;
- /*@only@*/ /*@null@*/ yasm_intnum *frameoff;
- long intv;
-
- /* Version and flags */
- if (info->ehandler)
- YASM_WRITE_8(buf, 1 | (UNW_FLAG_EHANDLER << 3));
- else
- YASM_WRITE_8(buf, 1);
-
- /* Size of prolog */
- output_value(&info->prolog_size, buf, 1, (unsigned long)(buf-bufstart),
- bc, 1, d);
- buf += 1;
-
- /* Count of codes */
- output_value(&info->codes_count, buf, 1, (unsigned long)(buf-bufstart),
- bc, 1, d);
- buf += 1;
-
- /* Frame register and offset */
- frameoff = yasm_value_get_intnum(&info->frameoff, bc, 1);
- if (!frameoff) {
- yasm_error_set(YASM_ERROR_VALUE,
- N_("frame offset expression too complex"));
- return 1;
- }
- intv = yasm_intnum_get_int(frameoff);
- if (intv < 0 || intv > 240)
- yasm_error_set(YASM_ERROR_VALUE,
- N_("frame offset of %ld bytes, must be between 0 and 240"), intv);
- else if ((intv & 0xF) != 0)
- yasm_error_set(YASM_ERROR_VALUE,
- N_("frame offset of %ld is not a multiple of 16"), intv);
-
- YASM_WRITE_8(buf, ((unsigned long)intv & 0xF0) | (info->framereg & 0x0F));
- yasm_intnum_destroy(frameoff);
-
- *bufp = buf;
- return 0;
-}
-
-static void
-win64_uwcode_bc_destroy(void *contents)
-{
- coff_unwind_code *code = (coff_unwind_code *)contents;
- yasm_value_delete(&code->off);
- yasm_xfree(contents);
-}
-
-static void
-win64_uwcode_bc_print(const void *contents, FILE *f, int indent_level)
-{
- /* TODO */
-}
-
-static void
-win64_uwcode_bc_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc)
-{
- coff_unwind_code *code = (coff_unwind_code *)bc->contents;
- if (yasm_value_finalize(&code->off, prev_bc))
- yasm_error_set(YASM_ERROR_VALUE, N_("offset expression too complex"));
-}
-
-static int
-win64_uwcode_bc_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
- void *add_span_data)
-{
- coff_unwind_code *code = (coff_unwind_code *)bc->contents;
- int span = 0;
- /*@only@*/ /*@null@*/ yasm_intnum *intn;
- long intv;
- long low, high, mask;
-
- bc->len += 2; /* Prolog offset, code, and info */
-
- switch (code->opcode) {
- case UWOP_PUSH_NONVOL:
- case UWOP_SET_FPREG:
- case UWOP_PUSH_MACHFRAME:
- /* always 1 node */
- return 0;
- case UWOP_ALLOC_SMALL:
- case UWOP_ALLOC_LARGE:
- /* Start with smallest, then work our way up as necessary */
- code->opcode = UWOP_ALLOC_SMALL;
- code->info = 0;
- span = 1; low = 8; high = 128; mask = 0x7;
- break;
- case UWOP_SAVE_NONVOL:
- case UWOP_SAVE_NONVOL_FAR:
- /* Start with smallest, then work our way up as necessary */
- code->opcode = UWOP_SAVE_NONVOL;
- bc->len += 2; /* Scaled offset */
- span = 2;
- low = 0;
- high = 8*64*1024-8; /* 16-bit field, *8 scaling */
- mask = 0x7;
- break;
- case UWOP_SAVE_XMM128:
- case UWOP_SAVE_XMM128_FAR:
- /* Start with smallest, then work our way up as necessary */
- code->opcode = UWOP_SAVE_XMM128;
- bc->len += 2; /* Scaled offset */
- span = 3;
- low = 0;
- high = 16*64*1024-16; /* 16-bit field, *16 scaling */
- mask = 0xF;
- break;
- default:
- yasm_internal_error(N_("unrecognied unwind opcode"));
- /*@unreached@*/
- return 0;
- }
-
- intn = yasm_value_get_intnum(&code->off, bc, 0);
- if (intn) {
- intv = yasm_intnum_get_int(intn);
- if (intv > high) {
- /* Expand it ourselves here if we can and we're already larger */
- if (win64_uwcode_bc_expand(bc, span, intv, intv, &low, &high) > 0)
- add_span(add_span_data, bc, span, &code->off, low, high);
- }
- if (intv < low)
- yasm_error_set(YASM_ERROR_VALUE,
- N_("negative offset not allowed"));
- if ((intv & mask) != 0)
- yasm_error_set(YASM_ERROR_VALUE,
- N_("offset of %ld is not a multiple of %ld"), intv, mask+1);
- yasm_intnum_destroy(intn);
- } else
- add_span(add_span_data, bc, span, &code->off, low, high);
- return 0;
-}
-
-static int
-win64_uwcode_bc_expand(yasm_bytecode *bc, int span, long old_val, long new_val,
- /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres)
-{
- coff_unwind_code *code = (coff_unwind_code *)bc->contents;
-
- if (new_val < 0) {
- yasm_error_set(YASM_ERROR_VALUE, N_("negative offset not allowed"));
- return -1;
- }
-
- if (span == 1) {
- /* 3 stages: SMALL, LARGE and info=0, LARGE and info=1 */
- if (code->opcode == UWOP_ALLOC_LARGE && code->info == 1)
- yasm_internal_error(N_("expansion on already largest alloc"));
-
- if (code->opcode == UWOP_ALLOC_SMALL && new_val > 128) {
- /* Overflowed small size */
- code->opcode = UWOP_ALLOC_LARGE;
- bc->len += 2;
- }
- if (new_val <= 8*64*1024-8) {
- /* Still can grow one more size */
- *pos_thres = 8*64*1024-8;
- return 1;
- }
- /* We're into the largest size */
- code->info = 1;
- bc->len += 2;
- } else if (code->opcode == UWOP_SAVE_NONVOL && span == 2) {
- code->opcode = UWOP_SAVE_NONVOL_FAR;
- bc->len += 2;
- } else if (code->opcode == UWOP_SAVE_XMM128 && span == 3) {
- code->opcode = UWOP_SAVE_XMM128_FAR;
- bc->len += 2;
- }
- return 0;
-}
-
-static int
-win64_uwcode_bc_tobytes(yasm_bytecode *bc, unsigned char **bufp,
- unsigned char *bufstart, void *d,
- yasm_output_value_func output_value,
- yasm_output_reloc_func output_reloc)
-{
- coff_unwind_code *code = (coff_unwind_code *)bc->contents;
- unsigned char *buf = *bufp;
- yasm_value val;
- unsigned int size;
- int shift;
- long intv, low, high, mask;
- yasm_intnum *intn;
-
- /* Offset in prolog */
- yasm_value_initialize(&val,
- yasm_expr_create(YASM_EXPR_SUB, yasm_expr_sym(code->loc),
- yasm_expr_sym(code->proc), bc->line),
- 8);
- output_value(&val, buf, 1, (unsigned long)(buf-bufstart), bc, 1, d);
- buf += 1;
- yasm_value_delete(&val);
-
- /* Offset value */
- switch (code->opcode) {
- case UWOP_PUSH_NONVOL:
- case UWOP_SET_FPREG:
- case UWOP_PUSH_MACHFRAME:
- /* just 1 node, no offset; write opcode and info and we're done */
- YASM_WRITE_8(buf, (code->info << 4) | (code->opcode & 0xF));
- *bufp = buf;
- return 0;
- case UWOP_ALLOC_SMALL:
- /* 1 node, but offset stored in info */
- size = 0; low = 8; high = 128; shift = 3; mask = 0x7;
- break;
- case UWOP_ALLOC_LARGE:
- if (code->info == 0) {
- size = 2; low = 136; high = 8*64*1024-8; shift = 3;
- } else {
- size = 4; low = high = 0; shift = 0;
- }
- mask = 0x7;
- break;
- case UWOP_SAVE_NONVOL:
- size = 2; low = 0; high = 8*64*1024-8; shift = 3; mask = 0x7;
- break;
- case UWOP_SAVE_XMM128:
- size = 2; low = 0; high = 16*64*1024-16; shift = 4; mask = 0xF;
- break;
- case UWOP_SAVE_NONVOL_FAR:
- size = 4; low = high = 0; shift = 0; mask = 0x7;
- break;
- case UWOP_SAVE_XMM128_FAR:
- size = 4; low = high = 0; shift = 0; mask = 0xF;
- break;
- default:
- yasm_internal_error(N_("unrecognied unwind opcode"));
- /*@unreached@*/
- return 1;
- }
-
- /* Check for overflow */
- intn = yasm_value_get_intnum(&code->off, bc, 1);
- if (!intn) {
- yasm_error_set(YASM_ERROR_VALUE, N_("offset expression too complex"));
- return 1;
- }
- intv = yasm_intnum_get_int(intn);
- if (size != 4 && (intv < low || intv > high)) {
- yasm_error_set(YASM_ERROR_VALUE,
- N_("offset of %ld bytes, must be between %ld and %ld"),
- intv, low, high);
- return 1;
- }
- if ((intv & mask) != 0) {
- yasm_error_set(YASM_ERROR_VALUE,
- N_("offset of %ld is not a multiple of %ld"),
- intv, mask+1);
- return 1;
- }
-
- /* Stored value in info instead of extra code space */
- if (size == 0)
- code->info = (yasm_intnum_get_uint(intn) >> shift)-1;
-
- /* Opcode and info */
- YASM_WRITE_8(buf, (code->info << 4) | (code->opcode & 0xF));
-
- if (size != 0) {
- yasm_intnum_get_sized(intn, buf, size, size*8, -shift, 0, 1);
- buf += size;
- }
-
- yasm_intnum_destroy(intn);
-
- *bufp = buf;
- return 0;
-}
+/*
+ * Win64 structured exception handling support
+ *
+ * Copyright (C) 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.h>
+
+#include "coff-objfmt.h"
+
+
+#define UNW_FLAG_EHANDLER 0x01
+#define UNW_FLAG_UHANDLER 0x02
+#define UNW_FLAG_CHAININFO 0x04
+
+/* Bytecode callback function prototypes */
+static void win64_uwinfo_bc_destroy(void *contents);
+static void win64_uwinfo_bc_print(const void *contents, FILE *f,
+ int indent_level);
+static void win64_uwinfo_bc_finalize(yasm_bytecode *bc,
+ yasm_bytecode *prev_bc);
+static int win64_uwinfo_bc_calc_len
+ (yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data);
+static int win64_uwinfo_bc_expand(yasm_bytecode *bc, int span, long old_val,
+ long new_val, /*@out@*/ long *neg_thres,
+ /*@out@*/ long *pos_thres);
+static int win64_uwinfo_bc_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 void win64_uwcode_bc_destroy(void *contents);
+static void win64_uwcode_bc_print(const void *contents, FILE *f,
+ int indent_level);
+static void win64_uwcode_bc_finalize(yasm_bytecode *bc,
+ yasm_bytecode *prev_bc);
+static int win64_uwcode_bc_calc_len
+ (yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data);
+static int win64_uwcode_bc_expand(yasm_bytecode *bc, int span, long old_val,
+ long new_val, /*@out@*/ long *neg_thres,
+ /*@out@*/ long *pos_thres);
+static int win64_uwcode_bc_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);
+
+/* Bytecode callback structures */
+static const yasm_bytecode_callback win64_uwinfo_bc_callback = {
+ win64_uwinfo_bc_destroy,
+ win64_uwinfo_bc_print,
+ win64_uwinfo_bc_finalize,
+ NULL,
+ win64_uwinfo_bc_calc_len,
+ win64_uwinfo_bc_expand,
+ win64_uwinfo_bc_tobytes,
+ 0
+};
+
+static const yasm_bytecode_callback win64_uwcode_bc_callback = {
+ win64_uwcode_bc_destroy,
+ win64_uwcode_bc_print,
+ win64_uwcode_bc_finalize,
+ NULL,
+ win64_uwcode_bc_calc_len,
+ win64_uwcode_bc_expand,
+ win64_uwcode_bc_tobytes,
+ 0
+};
+
+
+coff_unwind_info *
+yasm_win64__uwinfo_create(void)
+{
+ coff_unwind_info *info = yasm_xmalloc(sizeof(coff_unwind_info));
+ info->proc = NULL;
+ info->prolog = NULL;
+ info->ehandler = NULL;
+ info->framereg = 0;
+ /* Frameoff is really a 4-bit value, scaled by 16 */
+ yasm_value_initialize(&info->frameoff, NULL, 8);
+ SLIST_INIT(&info->codes);
+ yasm_value_initialize(&info->prolog_size, NULL, 8);
+ yasm_value_initialize(&info->codes_count, NULL, 8);
+ return info;
+}
+
+void
+yasm_win64__uwinfo_destroy(coff_unwind_info *info)
+{
+ coff_unwind_code *code;
+
+ yasm_value_delete(&info->frameoff);
+ yasm_value_delete(&info->prolog_size);
+ yasm_value_delete(&info->codes_count);
+
+ while (!SLIST_EMPTY(&info->codes)) {
+ code = SLIST_FIRST(&info->codes);
+ SLIST_REMOVE_HEAD(&info->codes, link);
+ yasm_value_delete(&code->off);
+ yasm_xfree(code);
+ }
+ yasm_xfree(info);
+}
+
+void
+yasm_win64__unwind_generate(yasm_section *xdata, coff_unwind_info *info,
+ unsigned long line)
+{
+ yasm_bytecode *infobc, *codebc = NULL;
+ coff_unwind_code *code;
+
+ /* 4-byte align the start of unwind info */
+ yasm_section_bcs_append(xdata, yasm_bc_create_align(
+ yasm_expr_create_ident(yasm_expr_int(yasm_intnum_create_uint(4)),
+ line),
+ NULL, NULL, NULL, line));
+
+ /* Prolog size = end of prolog - start of procedure */
+ yasm_value_initialize(&info->prolog_size,
+ yasm_expr_create(YASM_EXPR_SUB, yasm_expr_sym(info->prolog),
+ yasm_expr_sym(info->proc), line),
+ 8);
+
+ /* Unwind info */
+ infobc = yasm_bc_create_common(&win64_uwinfo_bc_callback, info, line);
+ yasm_section_bcs_append(xdata, infobc);
+
+ /* Code array */
+ SLIST_FOREACH(code, &info->codes, link) {
+ codebc = yasm_bc_create_common(&win64_uwcode_bc_callback, code,
+ yasm_symrec_get_def_line(code->loc));
+ yasm_section_bcs_append(xdata, codebc);
+ }
+
+ /* Avoid double-free (by code destroy and uwinfo destroy). */
+ SLIST_INIT(&info->codes);
+
+ /* Number of codes = (Last code - end of info) >> 1 */
+ if (!codebc) {
+ yasm_value_initialize(&info->codes_count,
+ yasm_expr_create_ident(yasm_expr_int(yasm_intnum_create_uint(0)),
+ line),
+ 8);
+ } else {
+ yasm_value_initialize(&info->codes_count,
+ yasm_expr_create(YASM_EXPR_SHR, yasm_expr_expr(
+ yasm_expr_create(YASM_EXPR_SUB, yasm_expr_precbc(codebc),
+ yasm_expr_precbc(infobc), line)),
+ yasm_expr_int(yasm_intnum_create_uint(1)), line),
+ 8);
+ }
+
+ /* 4-byte align */
+ yasm_section_bcs_append(xdata, yasm_bc_create_align(
+ yasm_expr_create_ident(yasm_expr_int(yasm_intnum_create_uint(4)),
+ line),
+ NULL, NULL, NULL, line));
+
+ /* Exception handler, if present. Use data bytecode. */
+ if (info->ehandler) {
+ yasm_datavalhead dvs;
+
+ yasm_dvs_initialize(&dvs);
+ yasm_dvs_append(&dvs, yasm_dv_create_expr(
+ yasm_expr_create_ident(yasm_expr_sym(info->ehandler), line)));
+ yasm_section_bcs_append(xdata,
+ yasm_bc_create_data(&dvs, 4, 0, NULL, line));
+ }
+}
+
+static void
+win64_uwinfo_bc_destroy(void *contents)
+{
+ yasm_win64__uwinfo_destroy((coff_unwind_info *)contents);
+}
+
+static void
+win64_uwinfo_bc_print(const void *contents, FILE *f, int indent_level)
+{
+ /* TODO */
+}
+
+static void
+win64_uwinfo_bc_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc)
+{
+ coff_unwind_info *info = (coff_unwind_info *)bc->contents;
+
+ if (yasm_value_finalize(&info->prolog_size, prev_bc))
+ yasm_internal_error(N_("prolog size expression too complex"));
+
+ if (yasm_value_finalize(&info->codes_count, prev_bc))
+ yasm_internal_error(N_("codes count expression too complex"));
+
+ if (yasm_value_finalize(&info->frameoff, prev_bc))
+ yasm_error_set(YASM_ERROR_VALUE,
+ N_("frame offset expression too complex"));
+}
+
+static int
+win64_uwinfo_bc_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
+ void *add_span_data)
+{
+ coff_unwind_info *info = (coff_unwind_info *)bc->contents;
+ /*@only@*/ /*@null@*/ yasm_intnum *intn;
+ long intv;
+
+ /* Want to make sure prolog size and codes count doesn't exceed
+ * byte-size, and scaled frame offset doesn't exceed 4 bits.
+ */
+ add_span(add_span_data, bc, 1, &info->prolog_size, 0, 255);
+ add_span(add_span_data, bc, 2, &info->codes_count, 0, 255);
+
+ intn = yasm_value_get_intnum(&info->frameoff, bc, 0);
+ if (intn) {
+ intv = yasm_intnum_get_int(intn);
+ if (intv < 0 || intv > 240)
+ yasm_error_set(YASM_ERROR_VALUE,
+ N_("frame offset of %ld bytes, must be between 0 and 240"),
+ intv);
+ else if ((intv & 0xF) != 0)
+ yasm_error_set(YASM_ERROR_VALUE,
+ N_("frame offset of %ld is not a multiple of 16"), intv);
+ yasm_intnum_destroy(intn);
+ } else
+ add_span(add_span_data, bc, 3, &info->frameoff, 0, 240);
+
+ bc->len += 4;
+ return 0;
+}
+
+static int
+win64_uwinfo_bc_expand(yasm_bytecode *bc, int span, long old_val, long new_val,
+ /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres)
+{
+ coff_unwind_info *info = (coff_unwind_info *)bc->contents;
+ switch (span) {
+ case 1:
+ yasm_error_set_xref(yasm_symrec_get_def_line(info->prolog),
+ N_("prologue ended here"));
+ yasm_error_set(YASM_ERROR_VALUE,
+ N_("prologue %ld bytes, must be <256"), new_val);
+ return -1;
+ case 2:
+ yasm_error_set(YASM_ERROR_VALUE,
+ N_("%ld unwind codes, maximum of 255"), new_val);
+ return -1;
+ case 3:
+ yasm_error_set(YASM_ERROR_VALUE,
+ N_("frame offset of %ld bytes, must be between 0 and 240"),
+ new_val);
+ return -1;
+ default:
+ yasm_internal_error(N_("unrecognized span id"));
+ }
+ return 0;
+}
+
+static int
+win64_uwinfo_bc_tobytes(yasm_bytecode *bc, unsigned char **bufp,
+ unsigned char *bufstart, void *d,
+ yasm_output_value_func output_value,
+ yasm_output_reloc_func output_reloc)
+{
+ coff_unwind_info *info = (coff_unwind_info *)bc->contents;
+ unsigned char *buf = *bufp;
+ /*@only@*/ /*@null@*/ yasm_intnum *frameoff;
+ long intv;
+
+ /* Version and flags */
+ if (info->ehandler)
+ YASM_WRITE_8(buf, 1 | (UNW_FLAG_EHANDLER << 3));
+ else
+ YASM_WRITE_8(buf, 1);
+
+ /* Size of prolog */
+ output_value(&info->prolog_size, buf, 1, (unsigned long)(buf-bufstart),
+ bc, 1, d);
+ buf += 1;
+
+ /* Count of codes */
+ output_value(&info->codes_count, buf, 1, (unsigned long)(buf-bufstart),
+ bc, 1, d);
+ buf += 1;
+
+ /* Frame register and offset */
+ frameoff = yasm_value_get_intnum(&info->frameoff, bc, 1);
+ if (!frameoff) {
+ yasm_error_set(YASM_ERROR_VALUE,
+ N_("frame offset expression too complex"));
+ return 1;
+ }
+ intv = yasm_intnum_get_int(frameoff);
+ if (intv < 0 || intv > 240)
+ yasm_error_set(YASM_ERROR_VALUE,
+ N_("frame offset of %ld bytes, must be between 0 and 240"), intv);
+ else if ((intv & 0xF) != 0)
+ yasm_error_set(YASM_ERROR_VALUE,
+ N_("frame offset of %ld is not a multiple of 16"), intv);
+
+ YASM_WRITE_8(buf, ((unsigned long)intv & 0xF0) | (info->framereg & 0x0F));
+ yasm_intnum_destroy(frameoff);
+
+ *bufp = buf;
+ return 0;
+}
+
+static void
+win64_uwcode_bc_destroy(void *contents)
+{
+ coff_unwind_code *code = (coff_unwind_code *)contents;
+ yasm_value_delete(&code->off);
+ yasm_xfree(contents);
+}
+
+static void
+win64_uwcode_bc_print(const void *contents, FILE *f, int indent_level)
+{
+ /* TODO */
+}
+
+static void
+win64_uwcode_bc_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc)
+{
+ coff_unwind_code *code = (coff_unwind_code *)bc->contents;
+ if (yasm_value_finalize(&code->off, prev_bc))
+ yasm_error_set(YASM_ERROR_VALUE, N_("offset expression too complex"));
+}
+
+static int
+win64_uwcode_bc_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
+ void *add_span_data)
+{
+ coff_unwind_code *code = (coff_unwind_code *)bc->contents;
+ int span = 0;
+ /*@only@*/ /*@null@*/ yasm_intnum *intn;
+ long intv;
+ long low, high, mask;
+
+ bc->len += 2; /* Prolog offset, code, and info */
+
+ switch (code->opcode) {
+ case UWOP_PUSH_NONVOL:
+ case UWOP_SET_FPREG:
+ case UWOP_PUSH_MACHFRAME:
+ /* always 1 node */
+ return 0;
+ case UWOP_ALLOC_SMALL:
+ case UWOP_ALLOC_LARGE:
+ /* Start with smallest, then work our way up as necessary */
+ code->opcode = UWOP_ALLOC_SMALL;
+ code->info = 0;
+ span = 1; low = 8; high = 128; mask = 0x7;
+ break;
+ case UWOP_SAVE_NONVOL:
+ case UWOP_SAVE_NONVOL_FAR:
+ /* Start with smallest, then work our way up as necessary */
+ code->opcode = UWOP_SAVE_NONVOL;
+ bc->len += 2; /* Scaled offset */
+ span = 2;
+ low = 0;
+ high = 8*64*1024-8; /* 16-bit field, *8 scaling */
+ mask = 0x7;
+ break;
+ case UWOP_SAVE_XMM128:
+ case UWOP_SAVE_XMM128_FAR:
+ /* Start with smallest, then work our way up as necessary */
+ code->opcode = UWOP_SAVE_XMM128;
+ bc->len += 2; /* Scaled offset */
+ span = 3;
+ low = 0;
+ high = 16*64*1024-16; /* 16-bit field, *16 scaling */
+ mask = 0xF;
+ break;
+ default:
+ yasm_internal_error(N_("unrecognied unwind opcode"));
+ /*@unreached@*/
+ return 0;
+ }
+
+ intn = yasm_value_get_intnum(&code->off, bc, 0);
+ if (intn) {
+ intv = yasm_intnum_get_int(intn);
+ if (intv > high) {
+ /* Expand it ourselves here if we can and we're already larger */
+ if (win64_uwcode_bc_expand(bc, span, intv, intv, &low, &high) > 0)
+ add_span(add_span_data, bc, span, &code->off, low, high);
+ }
+ if (intv < low)
+ yasm_error_set(YASM_ERROR_VALUE,
+ N_("negative offset not allowed"));
+ if ((intv & mask) != 0)
+ yasm_error_set(YASM_ERROR_VALUE,
+ N_("offset of %ld is not a multiple of %ld"), intv, mask+1);
+ yasm_intnum_destroy(intn);
+ } else
+ add_span(add_span_data, bc, span, &code->off, low, high);
+ return 0;
+}
+
+static int
+win64_uwcode_bc_expand(yasm_bytecode *bc, int span, long old_val, long new_val,
+ /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres)
+{
+ coff_unwind_code *code = (coff_unwind_code *)bc->contents;
+
+ if (new_val < 0) {
+ yasm_error_set(YASM_ERROR_VALUE, N_("negative offset not allowed"));
+ return -1;
+ }
+
+ if (span == 1) {
+ /* 3 stages: SMALL, LARGE and info=0, LARGE and info=1 */
+ if (code->opcode == UWOP_ALLOC_LARGE && code->info == 1)
+ yasm_internal_error(N_("expansion on already largest alloc"));
+
+ if (code->opcode == UWOP_ALLOC_SMALL && new_val > 128) {
+ /* Overflowed small size */
+ code->opcode = UWOP_ALLOC_LARGE;
+ bc->len += 2;
+ }
+ if (new_val <= 8*64*1024-8) {
+ /* Still can grow one more size */
+ *pos_thres = 8*64*1024-8;
+ return 1;
+ }
+ /* We're into the largest size */
+ code->info = 1;
+ bc->len += 2;
+ } else if (code->opcode == UWOP_SAVE_NONVOL && span == 2) {
+ code->opcode = UWOP_SAVE_NONVOL_FAR;
+ bc->len += 2;
+ } else if (code->opcode == UWOP_SAVE_XMM128 && span == 3) {
+ code->opcode = UWOP_SAVE_XMM128_FAR;
+ bc->len += 2;
+ }
+ return 0;
+}
+
+static int
+win64_uwcode_bc_tobytes(yasm_bytecode *bc, unsigned char **bufp,
+ unsigned char *bufstart, void *d,
+ yasm_output_value_func output_value,
+ yasm_output_reloc_func output_reloc)
+{
+ coff_unwind_code *code = (coff_unwind_code *)bc->contents;
+ unsigned char *buf = *bufp;
+ yasm_value val;
+ unsigned int size;
+ int shift;
+ long intv, low, high, mask;
+ yasm_intnum *intn;
+
+ /* Offset in prolog */
+ yasm_value_initialize(&val,
+ yasm_expr_create(YASM_EXPR_SUB, yasm_expr_sym(code->loc),
+ yasm_expr_sym(code->proc), bc->line),
+ 8);
+ output_value(&val, buf, 1, (unsigned long)(buf-bufstart), bc, 1, d);
+ buf += 1;
+ yasm_value_delete(&val);
+
+ /* Offset value */
+ switch (code->opcode) {
+ case UWOP_PUSH_NONVOL:
+ case UWOP_SET_FPREG:
+ case UWOP_PUSH_MACHFRAME:
+ /* just 1 node, no offset; write opcode and info and we're done */
+ YASM_WRITE_8(buf, (code->info << 4) | (code->opcode & 0xF));
+ *bufp = buf;
+ return 0;
+ case UWOP_ALLOC_SMALL:
+ /* 1 node, but offset stored in info */
+ size = 0; low = 8; high = 128; shift = 3; mask = 0x7;
+ break;
+ case UWOP_ALLOC_LARGE:
+ if (code->info == 0) {
+ size = 2; low = 136; high = 8*64*1024-8; shift = 3;
+ } else {
+ size = 4; low = high = 0; shift = 0;
+ }
+ mask = 0x7;
+ break;
+ case UWOP_SAVE_NONVOL:
+ size = 2; low = 0; high = 8*64*1024-8; shift = 3; mask = 0x7;
+ break;
+ case UWOP_SAVE_XMM128:
+ size = 2; low = 0; high = 16*64*1024-16; shift = 4; mask = 0xF;
+ break;
+ case UWOP_SAVE_NONVOL_FAR:
+ size = 4; low = high = 0; shift = 0; mask = 0x7;
+ break;
+ case UWOP_SAVE_XMM128_FAR:
+ size = 4; low = high = 0; shift = 0; mask = 0xF;
+ break;
+ default:
+ yasm_internal_error(N_("unrecognied unwind opcode"));
+ /*@unreached@*/
+ return 1;
+ }
+
+ /* Check for overflow */
+ intn = yasm_value_get_intnum(&code->off, bc, 1);
+ if (!intn) {
+ yasm_error_set(YASM_ERROR_VALUE, N_("offset expression too complex"));
+ return 1;
+ }
+ intv = yasm_intnum_get_int(intn);
+ if (size != 4 && (intv < low || intv > high)) {
+ yasm_error_set(YASM_ERROR_VALUE,
+ N_("offset of %ld bytes, must be between %ld and %ld"),
+ intv, low, high);
+ return 1;
+ }
+ if ((intv & mask) != 0) {
+ yasm_error_set(YASM_ERROR_VALUE,
+ N_("offset of %ld is not a multiple of %ld"),
+ intv, mask+1);
+ return 1;
+ }
+
+ /* Stored value in info instead of extra code space */
+ if (size == 0)
+ code->info = (yasm_intnum_get_uint(intn) >> shift)-1;
+
+ /* Opcode and info */
+ YASM_WRITE_8(buf, (code->info << 4) | (code->opcode & 0xF));
+
+ if (size != 0) {
+ yasm_intnum_get_sized(intn, buf, size, size*8, -shift, 0, 1);
+ buf += size;
+ }
+
+ yasm_intnum_destroy(intn);
+
+ *bufp = buf;
+ return 0;
+}