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