Home | History | Annotate | Download | only in coff
      1 /*
      2  * Win64 structured exception handling support
      3  *
      4  *  Copyright (C) 2007  Peter Johnson
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS''
     16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE
     19  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     20  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     21  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     23  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     24  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     25  * POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 #include <util.h>
     28 
     29 #include <libyasm.h>
     30 
     31 #include "coff-objfmt.h"
     32 
     33 
     34 #define UNW_FLAG_EHANDLER   0x01
     35 #define UNW_FLAG_UHANDLER   0x02
     36 #define UNW_FLAG_CHAININFO  0x04
     37 
     38 /* Bytecode callback function prototypes */
     39 static void win64_uwinfo_bc_destroy(void *contents);
     40 static void win64_uwinfo_bc_print(const void *contents, FILE *f,
     41                                   int indent_level);
     42 static void win64_uwinfo_bc_finalize(yasm_bytecode *bc,
     43                                      yasm_bytecode *prev_bc);
     44 static int win64_uwinfo_bc_calc_len
     45     (yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data);
     46 static int win64_uwinfo_bc_expand(yasm_bytecode *bc, int span, long old_val,
     47                                   long new_val, /*@out@*/ long *neg_thres,
     48                                   /*@out@*/ long *pos_thres);
     49 static int win64_uwinfo_bc_tobytes
     50     (yasm_bytecode *bc, unsigned char **bufp, unsigned char *bufstart, void *d,
     51      yasm_output_value_func output_value,
     52      /*@null@*/ yasm_output_reloc_func output_reloc);
     53 
     54 static void win64_uwcode_bc_destroy(void *contents);
     55 static void win64_uwcode_bc_print(const void *contents, FILE *f,
     56                                   int indent_level);
     57 static void win64_uwcode_bc_finalize(yasm_bytecode *bc,
     58                                      yasm_bytecode *prev_bc);
     59 static int win64_uwcode_bc_calc_len
     60     (yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data);
     61 static int win64_uwcode_bc_expand(yasm_bytecode *bc, int span, long old_val,
     62                                   long new_val, /*@out@*/ long *neg_thres,
     63                                   /*@out@*/ long *pos_thres);
     64 static int win64_uwcode_bc_tobytes
     65     (yasm_bytecode *bc, unsigned char **bufp, unsigned char *bufstart, void *d,
     66      yasm_output_value_func output_value,
     67      /*@null@*/ yasm_output_reloc_func output_reloc);
     68 
     69 /* Bytecode callback structures */
     70 static const yasm_bytecode_callback win64_uwinfo_bc_callback = {
     71     win64_uwinfo_bc_destroy,
     72     win64_uwinfo_bc_print,
     73     win64_uwinfo_bc_finalize,
     74     NULL,
     75     win64_uwinfo_bc_calc_len,
     76     win64_uwinfo_bc_expand,
     77     win64_uwinfo_bc_tobytes,
     78     0
     79 };
     80 
     81 static const yasm_bytecode_callback win64_uwcode_bc_callback = {
     82     win64_uwcode_bc_destroy,
     83     win64_uwcode_bc_print,
     84     win64_uwcode_bc_finalize,
     85     NULL,
     86     win64_uwcode_bc_calc_len,
     87     win64_uwcode_bc_expand,
     88     win64_uwcode_bc_tobytes,
     89     0
     90 };
     91 
     92 
     93 coff_unwind_info *
     94 yasm_win64__uwinfo_create(void)
     95 {
     96     coff_unwind_info *info = yasm_xmalloc(sizeof(coff_unwind_info));
     97     info->proc = NULL;
     98     info->prolog = NULL;
     99     info->ehandler = NULL;
    100     info->framereg = 0;
    101     /* Frameoff is really a 4-bit value, scaled by 16 */
    102     yasm_value_initialize(&info->frameoff, NULL, 8);
    103     SLIST_INIT(&info->codes);
    104     yasm_value_initialize(&info->prolog_size, NULL, 8);
    105     yasm_value_initialize(&info->codes_count, NULL, 8);
    106     return info;
    107 }
    108 
    109 void
    110 yasm_win64__uwinfo_destroy(coff_unwind_info *info)
    111 {
    112     coff_unwind_code *code;
    113 
    114     yasm_value_delete(&info->frameoff);
    115     yasm_value_delete(&info->prolog_size);
    116     yasm_value_delete(&info->codes_count);
    117 
    118     while (!SLIST_EMPTY(&info->codes)) {
    119         code = SLIST_FIRST(&info->codes);
    120         SLIST_REMOVE_HEAD(&info->codes, link);
    121         yasm_value_delete(&code->off);
    122         yasm_xfree(code);
    123     }
    124     yasm_xfree(info);
    125 }
    126 
    127 void
    128 yasm_win64__unwind_generate(yasm_section *xdata, coff_unwind_info *info,
    129                             unsigned long line)
    130 {
    131     yasm_bytecode *infobc, *codebc = NULL;
    132     coff_unwind_code *code;
    133 
    134     /* 4-byte align the start of unwind info */
    135     yasm_section_bcs_append(xdata, yasm_bc_create_align(
    136         yasm_expr_create_ident(yasm_expr_int(yasm_intnum_create_uint(4)),
    137                                line),
    138         NULL, NULL, NULL, line));
    139 
    140     /* Prolog size = end of prolog - start of procedure */
    141     yasm_value_initialize(&info->prolog_size,
    142         yasm_expr_create(YASM_EXPR_SUB, yasm_expr_sym(info->prolog),
    143                          yasm_expr_sym(info->proc), line),
    144         8);
    145 
    146     /* Unwind info */
    147     infobc = yasm_bc_create_common(&win64_uwinfo_bc_callback, info, line);
    148     yasm_section_bcs_append(xdata, infobc);
    149 
    150     /* Code array */
    151     SLIST_FOREACH(code, &info->codes, link) {
    152         codebc = yasm_bc_create_common(&win64_uwcode_bc_callback, code,
    153                                        yasm_symrec_get_def_line(code->loc));
    154         yasm_section_bcs_append(xdata, codebc);
    155     }
    156 
    157     /* Avoid double-free (by code destroy and uwinfo destroy). */
    158     SLIST_INIT(&info->codes);
    159 
    160     /* Number of codes = (Last code - end of info) >> 1 */
    161     if (!codebc) {
    162         yasm_value_initialize(&info->codes_count,
    163             yasm_expr_create_ident(yasm_expr_int(yasm_intnum_create_uint(0)),
    164                                    line),
    165             8);
    166     } else {
    167         yasm_value_initialize(&info->codes_count,
    168             yasm_expr_create(YASM_EXPR_SHR, yasm_expr_expr(
    169                 yasm_expr_create(YASM_EXPR_SUB, yasm_expr_precbc(codebc),
    170                                  yasm_expr_precbc(infobc), line)),
    171                 yasm_expr_int(yasm_intnum_create_uint(1)), line),
    172             8);
    173     }
    174 
    175     /* 4-byte align */
    176     yasm_section_bcs_append(xdata, yasm_bc_create_align(
    177         yasm_expr_create_ident(yasm_expr_int(yasm_intnum_create_uint(4)),
    178                                line),
    179         NULL, NULL, NULL, line));
    180 
    181     /* Exception handler, if present.  Use data bytecode. */
    182     if (info->ehandler) {
    183         yasm_datavalhead dvs;
    184 
    185         yasm_dvs_initialize(&dvs);
    186         yasm_dvs_append(&dvs, yasm_dv_create_expr(
    187             yasm_expr_create_ident(yasm_expr_sym(info->ehandler), line)));
    188         yasm_section_bcs_append(xdata,
    189                                 yasm_bc_create_data(&dvs, 4, 0, NULL, line));
    190     }
    191 }
    192 
    193 static void
    194 win64_uwinfo_bc_destroy(void *contents)
    195 {
    196     yasm_win64__uwinfo_destroy((coff_unwind_info *)contents);
    197 }
    198 
    199 static void
    200 win64_uwinfo_bc_print(const void *contents, FILE *f, int indent_level)
    201 {
    202     /* TODO */
    203 }
    204 
    205 static void
    206 win64_uwinfo_bc_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc)
    207 {
    208     coff_unwind_info *info = (coff_unwind_info *)bc->contents;
    209 
    210     if (yasm_value_finalize(&info->prolog_size, prev_bc))
    211         yasm_internal_error(N_("prolog size expression too complex"));
    212 
    213     if (yasm_value_finalize(&info->codes_count, prev_bc))
    214         yasm_internal_error(N_("codes count expression too complex"));
    215 
    216     if (yasm_value_finalize(&info->frameoff, prev_bc))
    217         yasm_error_set(YASM_ERROR_VALUE,
    218                        N_("frame offset expression too complex"));
    219 }
    220 
    221 static int
    222 win64_uwinfo_bc_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
    223                          void *add_span_data)
    224 {
    225     coff_unwind_info *info = (coff_unwind_info *)bc->contents;
    226     /*@only@*/ /*@null@*/ yasm_intnum *intn;
    227     long intv;
    228 
    229     /* Want to make sure prolog size and codes count doesn't exceed
    230      * byte-size, and scaled frame offset doesn't exceed 4 bits.
    231      */
    232     add_span(add_span_data, bc, 1, &info->prolog_size, 0, 255);
    233     add_span(add_span_data, bc, 2, &info->codes_count, 0, 255);
    234 
    235     intn = yasm_value_get_intnum(&info->frameoff, bc, 0);
    236     if (intn) {
    237         intv = yasm_intnum_get_int(intn);
    238         if (intv < 0 || intv > 240)
    239             yasm_error_set(YASM_ERROR_VALUE,
    240                 N_("frame offset of %ld bytes, must be between 0 and 240"),
    241                 intv);
    242         else if ((intv & 0xF) != 0)
    243             yasm_error_set(YASM_ERROR_VALUE,
    244                 N_("frame offset of %ld is not a multiple of 16"), intv);
    245         yasm_intnum_destroy(intn);
    246     } else
    247         add_span(add_span_data, bc, 3, &info->frameoff, 0, 240);
    248 
    249     bc->len += 4;
    250     return 0;
    251 }
    252 
    253 static int
    254 win64_uwinfo_bc_expand(yasm_bytecode *bc, int span, long old_val, long new_val,
    255                        /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres)
    256 {
    257     coff_unwind_info *info = (coff_unwind_info *)bc->contents;
    258     switch (span) {
    259         case 1:
    260             yasm_error_set_xref(yasm_symrec_get_def_line(info->prolog),
    261                                 N_("prologue ended here"));
    262             yasm_error_set(YASM_ERROR_VALUE,
    263                            N_("prologue %ld bytes, must be <256"), new_val);
    264             return -1;
    265         case 2:
    266             yasm_error_set(YASM_ERROR_VALUE,
    267                            N_("%ld unwind codes, maximum of 255"), new_val);
    268             return -1;
    269         case 3:
    270             yasm_error_set(YASM_ERROR_VALUE,
    271                 N_("frame offset of %ld bytes, must be between 0 and 240"),
    272                 new_val);
    273             return -1;
    274         default:
    275             yasm_internal_error(N_("unrecognized span id"));
    276     }
    277     return 0;
    278 }
    279 
    280 static int
    281 win64_uwinfo_bc_tobytes(yasm_bytecode *bc, unsigned char **bufp,
    282                         unsigned char *bufstart, void *d,
    283                         yasm_output_value_func output_value,
    284                         yasm_output_reloc_func output_reloc)
    285 {
    286     coff_unwind_info *info = (coff_unwind_info *)bc->contents;
    287     unsigned char *buf = *bufp;
    288     /*@only@*/ /*@null@*/ yasm_intnum *frameoff;
    289     long intv;
    290 
    291     /* Version and flags */
    292     if (info->ehandler)
    293         YASM_WRITE_8(buf, 1 | (UNW_FLAG_EHANDLER << 3));
    294     else
    295         YASM_WRITE_8(buf, 1);
    296 
    297     /* Size of prolog */
    298     output_value(&info->prolog_size, buf, 1, (unsigned long)(buf-bufstart),
    299                  bc, 1, d);
    300     buf += 1;
    301 
    302     /* Count of codes */
    303     output_value(&info->codes_count, buf, 1, (unsigned long)(buf-bufstart),
    304                  bc, 1, d);
    305     buf += 1;
    306 
    307     /* Frame register and offset */
    308     frameoff = yasm_value_get_intnum(&info->frameoff, bc, 1);
    309     if (!frameoff) {
    310         yasm_error_set(YASM_ERROR_VALUE,
    311                        N_("frame offset expression too complex"));
    312         return 1;
    313     }
    314     intv = yasm_intnum_get_int(frameoff);
    315     if (intv < 0 || intv > 240)
    316         yasm_error_set(YASM_ERROR_VALUE,
    317             N_("frame offset of %ld bytes, must be between 0 and 240"), intv);
    318     else if ((intv & 0xF) != 0)
    319         yasm_error_set(YASM_ERROR_VALUE,
    320             N_("frame offset of %ld is not a multiple of 16"), intv);
    321 
    322     YASM_WRITE_8(buf, ((unsigned long)intv & 0xF0) | (info->framereg & 0x0F));
    323     yasm_intnum_destroy(frameoff);
    324 
    325     *bufp = buf;
    326     return 0;
    327 }
    328 
    329 static void
    330 win64_uwcode_bc_destroy(void *contents)
    331 {
    332     coff_unwind_code *code = (coff_unwind_code *)contents;
    333     yasm_value_delete(&code->off);
    334     yasm_xfree(contents);
    335 }
    336 
    337 static void
    338 win64_uwcode_bc_print(const void *contents, FILE *f, int indent_level)
    339 {
    340     /* TODO */
    341 }
    342 
    343 static void
    344 win64_uwcode_bc_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc)
    345 {
    346     coff_unwind_code *code = (coff_unwind_code *)bc->contents;
    347     if (yasm_value_finalize(&code->off, prev_bc))
    348         yasm_error_set(YASM_ERROR_VALUE, N_("offset expression too complex"));
    349 }
    350 
    351 static int
    352 win64_uwcode_bc_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
    353                          void *add_span_data)
    354 {
    355     coff_unwind_code *code = (coff_unwind_code *)bc->contents;
    356     int span = 0;
    357     /*@only@*/ /*@null@*/ yasm_intnum *intn;
    358     long intv;
    359     long low, high, mask;
    360 
    361     bc->len += 2;   /* Prolog offset, code, and info */
    362 
    363     switch (code->opcode) {
    364         case UWOP_PUSH_NONVOL:
    365         case UWOP_SET_FPREG:
    366         case UWOP_PUSH_MACHFRAME:
    367             /* always 1 node */
    368             return 0;
    369         case UWOP_ALLOC_SMALL:
    370         case UWOP_ALLOC_LARGE:
    371             /* Start with smallest, then work our way up as necessary */
    372             code->opcode = UWOP_ALLOC_SMALL;
    373             code->info = 0;
    374             span = 1; low = 8; high = 128; mask = 0x7;
    375             break;
    376         case UWOP_SAVE_NONVOL:
    377         case UWOP_SAVE_NONVOL_FAR:
    378             /* Start with smallest, then work our way up as necessary */
    379             code->opcode = UWOP_SAVE_NONVOL;
    380             bc->len += 2;   /* Scaled offset */
    381             span = 2;
    382             low = 0;
    383             high = 8*64*1024-8;         /* 16-bit field, *8 scaling */
    384             mask = 0x7;
    385             break;
    386         case UWOP_SAVE_XMM128:
    387         case UWOP_SAVE_XMM128_FAR:
    388             /* Start with smallest, then work our way up as necessary */
    389             code->opcode = UWOP_SAVE_XMM128;
    390             bc->len += 2;   /* Scaled offset */
    391             span = 3;
    392             low = 0;
    393             high = 16*64*1024-16;       /* 16-bit field, *16 scaling */
    394             mask = 0xF;
    395             break;
    396         default:
    397             yasm_internal_error(N_("unrecognied unwind opcode"));
    398             /*@unreached@*/
    399             return 0;
    400     }
    401 
    402     intn = yasm_value_get_intnum(&code->off, bc, 0);
    403     if (intn) {
    404         intv = yasm_intnum_get_int(intn);
    405         if (intv > high) {
    406             /* Expand it ourselves here if we can and we're already larger */
    407             if (win64_uwcode_bc_expand(bc, span, intv, intv, &low, &high) > 0)
    408                 add_span(add_span_data, bc, span, &code->off, low, high);
    409         }
    410         if (intv < low)
    411             yasm_error_set(YASM_ERROR_VALUE,
    412                            N_("negative offset not allowed"));
    413         if ((intv & mask) != 0)
    414             yasm_error_set(YASM_ERROR_VALUE,
    415                 N_("offset of %ld is not a multiple of %ld"), intv, mask+1);
    416         yasm_intnum_destroy(intn);
    417     } else
    418         add_span(add_span_data, bc, span, &code->off, low, high);
    419     return 0;
    420 }
    421 
    422 static int
    423 win64_uwcode_bc_expand(yasm_bytecode *bc, int span, long old_val, long new_val,
    424                        /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres)
    425 {
    426     coff_unwind_code *code = (coff_unwind_code *)bc->contents;
    427 
    428     if (new_val < 0) {
    429         yasm_error_set(YASM_ERROR_VALUE, N_("negative offset not allowed"));
    430         return -1;
    431     }
    432 
    433     if (span == 1) {
    434         /* 3 stages: SMALL, LARGE and info=0, LARGE and info=1 */
    435         if (code->opcode == UWOP_ALLOC_LARGE && code->info == 1)
    436             yasm_internal_error(N_("expansion on already largest alloc"));
    437 
    438         if (code->opcode == UWOP_ALLOC_SMALL && new_val > 128) {
    439             /* Overflowed small size */
    440             code->opcode = UWOP_ALLOC_LARGE;
    441             bc->len += 2;
    442         }
    443         if (new_val <= 8*64*1024-8) {
    444             /* Still can grow one more size */
    445             *pos_thres = 8*64*1024-8;
    446             return 1;
    447         }
    448         /* We're into the largest size */
    449         code->info = 1;
    450         bc->len += 2;
    451     } else if (code->opcode == UWOP_SAVE_NONVOL && span == 2) {
    452         code->opcode = UWOP_SAVE_NONVOL_FAR;
    453         bc->len += 2;
    454     } else if (code->opcode == UWOP_SAVE_XMM128 && span == 3) {
    455         code->opcode = UWOP_SAVE_XMM128_FAR;
    456         bc->len += 2;
    457     }
    458     return 0;
    459 }
    460 
    461 static int
    462 win64_uwcode_bc_tobytes(yasm_bytecode *bc, unsigned char **bufp,
    463                         unsigned char *bufstart, void *d,
    464                         yasm_output_value_func output_value,
    465                         yasm_output_reloc_func output_reloc)
    466 {
    467     coff_unwind_code *code = (coff_unwind_code *)bc->contents;
    468     unsigned char *buf = *bufp;
    469     yasm_value val;
    470     unsigned int size;
    471     int shift;
    472     long intv, low, high, mask;
    473     yasm_intnum *intn;
    474 
    475     /* Offset in prolog */
    476     yasm_value_initialize(&val,
    477         yasm_expr_create(YASM_EXPR_SUB, yasm_expr_sym(code->loc),
    478                          yasm_expr_sym(code->proc), bc->line),
    479         8);
    480     output_value(&val, buf, 1, (unsigned long)(buf-bufstart), bc, 1, d);
    481     buf += 1;
    482     yasm_value_delete(&val);
    483 
    484     /* Offset value */
    485     switch (code->opcode) {
    486         case UWOP_PUSH_NONVOL:
    487         case UWOP_SET_FPREG:
    488         case UWOP_PUSH_MACHFRAME:
    489             /* just 1 node, no offset; write opcode and info and we're done */
    490             YASM_WRITE_8(buf, (code->info << 4) | (code->opcode & 0xF));
    491             *bufp = buf;
    492             return 0;
    493         case UWOP_ALLOC_SMALL:
    494             /* 1 node, but offset stored in info */
    495             size = 0; low = 8; high = 128; shift = 3; mask = 0x7;
    496             break;
    497         case UWOP_ALLOC_LARGE:
    498             if (code->info == 0) {
    499                 size = 2; low = 136; high = 8*64*1024-8; shift = 3;
    500             } else {
    501                 size = 4; low = high = 0; shift = 0;
    502             }
    503             mask = 0x7;
    504             break;
    505         case UWOP_SAVE_NONVOL:
    506             size = 2; low = 0; high = 8*64*1024-8; shift = 3; mask = 0x7;
    507             break;
    508         case UWOP_SAVE_XMM128:
    509             size = 2; low = 0; high = 16*64*1024-16; shift = 4; mask = 0xF;
    510             break;
    511         case UWOP_SAVE_NONVOL_FAR:
    512             size = 4; low = high = 0; shift = 0; mask = 0x7;
    513             break;
    514         case UWOP_SAVE_XMM128_FAR:
    515             size = 4; low = high = 0; shift = 0; mask = 0xF;
    516             break;
    517         default:
    518             yasm_internal_error(N_("unrecognied unwind opcode"));
    519             /*@unreached@*/
    520             return 1;
    521     }
    522 
    523     /* Check for overflow */
    524     intn = yasm_value_get_intnum(&code->off, bc, 1);
    525     if (!intn) {
    526         yasm_error_set(YASM_ERROR_VALUE, N_("offset expression too complex"));
    527         return 1;
    528     }
    529     intv = yasm_intnum_get_int(intn);
    530     if (size != 4 && (intv < low || intv > high)) {
    531         yasm_error_set(YASM_ERROR_VALUE,
    532             N_("offset of %ld bytes, must be between %ld and %ld"),
    533             intv, low, high);
    534         return 1;
    535     }
    536     if ((intv & mask) != 0) {
    537         yasm_error_set(YASM_ERROR_VALUE,
    538                        N_("offset of %ld is not a multiple of %ld"),
    539                        intv, mask+1);
    540         return 1;
    541     }
    542 
    543     /* Stored value in info instead of extra code space */
    544     if (size == 0)
    545         code->info = (yasm_intnum_get_uint(intn) >> shift)-1;
    546 
    547     /* Opcode and info */
    548     YASM_WRITE_8(buf, (code->info << 4) | (code->opcode & 0xF));
    549 
    550     if (size != 0) {
    551         yasm_intnum_get_sized(intn, buf, size, size*8, -shift, 0, 1);
    552         buf += size;
    553     }
    554 
    555     yasm_intnum_destroy(intn);
    556 
    557     *bufp = buf;
    558     return 0;
    559 }
    560