1 /* seh pdata/xdata coff object file format 2 Copyright (C) 2009-2022 Free Software Foundation, Inc. 3 4 This file is part of GAS. 5 6 GAS is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 3, or (at your option) 9 any later version. 10 11 GAS is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GAS; see the file COPYING. If not, write to the Free 18 Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 19 02110-1301, USA. */ 20 21 #include "obj-coff-seh.h" 22 23 24 /* Private segment collection list. */ 25 struct seh_seg_list { 26 segT seg; 27 int subseg; 28 char *seg_name; 29 }; 30 31 /* Local data. */ 32 static seh_context *seh_ctx_cur = NULL; 33 34 static htab_t seh_hash; 35 36 static struct seh_seg_list *x_segcur = NULL; 37 static struct seh_seg_list *p_segcur = NULL; 38 39 static void write_function_xdata (seh_context *); 40 static void write_function_pdata (seh_context *); 41 42 43 /* Build based on segment the derived .pdata/.xdata 44 segment name containing origin segment's postfix name part. */ 45 static char * 46 get_pxdata_name (segT seg, const char *base_name) 47 { 48 const char *name,*dollar, *dot; 49 char *sname; 50 51 name = bfd_section_name (seg); 52 53 dollar = strchr (name, '$'); 54 dot = strchr (name + 1, '.'); 55 56 if (!dollar && !dot) 57 name = ""; 58 else if (!dollar) 59 name = dot; 60 else if (!dot) 61 name = dollar; 62 else if (dot < dollar) 63 name = dot; 64 else 65 name = dollar; 66 67 sname = concat (base_name, name, NULL); 68 69 return sname; 70 } 71 72 /* Allocate a seh_seg_list structure. */ 73 static struct seh_seg_list * 74 alloc_pxdata_item (segT seg, int subseg, char *name) 75 { 76 struct seh_seg_list *r; 77 78 r = (struct seh_seg_list *) 79 xmalloc (sizeof (struct seh_seg_list) + strlen (name)); 80 r->seg = seg; 81 r->subseg = subseg; 82 r->seg_name = name; 83 return r; 84 } 85 86 /* Generate pdata/xdata segment with same linkonce properties 87 of based segment. */ 88 static segT 89 make_pxdata_seg (segT cseg, char *name) 90 { 91 segT save_seg = now_seg; 92 int save_subseg = now_subseg; 93 segT r; 94 flagword flags; 95 96 r = subseg_new (name, 0); 97 /* Check if code segment is marked as linked once. */ 98 flags = (bfd_section_flags (cseg) 99 & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD 100 | SEC_LINK_DUPLICATES_ONE_ONLY | SEC_LINK_DUPLICATES_SAME_SIZE 101 | SEC_LINK_DUPLICATES_SAME_CONTENTS)); 102 103 /* Add standard section flags. */ 104 flags |= SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA; 105 106 /* Apply possibly linked once flags to new generated segment, too. */ 107 if (!bfd_set_section_flags (r, flags)) 108 as_bad (_("bfd_set_section_flags: %s"), 109 bfd_errmsg (bfd_get_error ())); 110 111 /* Restore to previous segment. */ 112 subseg_set (save_seg, save_subseg); 113 return r; 114 } 115 116 static void 117 seh_hash_insert (const char *name, struct seh_seg_list *item) 118 { 119 str_hash_insert (seh_hash, name, item, 1); 120 } 121 122 static struct seh_seg_list * 123 seh_hash_find (char *name) 124 { 125 return (struct seh_seg_list *) str_hash_find (seh_hash, name); 126 } 127 128 static struct seh_seg_list * 129 seh_hash_find_or_make (segT cseg, const char *base_name) 130 { 131 struct seh_seg_list *item; 132 char *name; 133 134 /* Initialize seh_hash once. */ 135 if (!seh_hash) 136 seh_hash = str_htab_create (); 137 138 name = get_pxdata_name (cseg, base_name); 139 140 item = seh_hash_find (name); 141 if (!item) 142 { 143 item = alloc_pxdata_item (make_pxdata_seg (cseg, name), 0, name); 144 145 seh_hash_insert (item->seg_name, item); 146 } 147 else 148 free (name); 149 150 return item; 151 } 152 153 /* Check if current segment has same name. */ 154 static int 155 seh_validate_seg (const char *directive) 156 { 157 const char *cseg_name, *nseg_name; 158 if (seh_ctx_cur->code_seg == now_seg) 159 return 1; 160 cseg_name = bfd_section_name (seh_ctx_cur->code_seg); 161 nseg_name = bfd_section_name (now_seg); 162 as_bad (_("%s used in segment '%s' instead of expected '%s'"), 163 directive, nseg_name, cseg_name); 164 ignore_rest_of_line (); 165 return 0; 166 } 167 168 /* Switch back to the code section, whatever that may be. */ 169 static void 170 obj_coff_seh_code (int ignored ATTRIBUTE_UNUSED) 171 { 172 subseg_set (seh_ctx_cur->code_seg, 0); 173 } 174 175 static void 176 switch_xdata (int subseg, segT code_seg) 177 { 178 x_segcur = seh_hash_find_or_make (code_seg, ".xdata"); 179 180 subseg_set (x_segcur->seg, subseg); 181 } 182 183 static void 184 switch_pdata (segT code_seg) 185 { 186 p_segcur = seh_hash_find_or_make (code_seg, ".pdata"); 187 188 subseg_set (p_segcur->seg, p_segcur->subseg); 189 } 190 191 /* Parsing routines. */ 192 193 /* Return the style of SEH unwind info to generate. */ 194 195 static seh_kind 196 seh_get_target_kind (void) 197 { 198 if (!stdoutput) 199 return seh_kind_unknown; 200 switch (bfd_get_arch (stdoutput)) 201 { 202 case bfd_arch_arm: 203 case bfd_arch_powerpc: 204 case bfd_arch_sh: 205 return seh_kind_arm; 206 case bfd_arch_i386: 207 switch (bfd_get_mach (stdoutput)) 208 { 209 case bfd_mach_x86_64: 210 case bfd_mach_x86_64_intel_syntax: 211 return seh_kind_x64; 212 default: 213 break; 214 } 215 /* FALL THROUGH. */ 216 case bfd_arch_mips: 217 return seh_kind_mips; 218 case bfd_arch_ia64: 219 /* Should return seh_kind_x64. But not implemented yet. */ 220 return seh_kind_unknown; 221 default: 222 break; 223 } 224 return seh_kind_unknown; 225 } 226 227 /* Verify that we're in the context of a seh_proc. */ 228 229 static int 230 verify_context (const char *directive) 231 { 232 if (seh_ctx_cur == NULL) 233 { 234 as_bad (_("%s used outside of .seh_proc block"), directive); 235 ignore_rest_of_line (); 236 return 0; 237 } 238 return 1; 239 } 240 241 /* Similar, except we also verify the appropriate target. */ 242 243 static int 244 verify_context_and_target (const char *directive, seh_kind target) 245 { 246 if (seh_get_target_kind () != target) 247 { 248 as_warn (_("%s ignored for this target"), directive); 249 ignore_rest_of_line (); 250 return 0; 251 } 252 return verify_context (directive); 253 } 254 255 /* Skip whitespace and a comma. Error if the comma is not seen. */ 256 257 static int 258 skip_whitespace_and_comma (int required) 259 { 260 SKIP_WHITESPACE (); 261 if (*input_line_pointer == ',') 262 { 263 input_line_pointer++; 264 SKIP_WHITESPACE (); 265 return 1; 266 } 267 else if (required) 268 { 269 as_bad (_("missing separator")); 270 ignore_rest_of_line (); 271 } 272 else 273 demand_empty_rest_of_line (); 274 return 0; 275 } 276 277 /* Mark current context to use 32-bit instruction (arm). */ 278 279 static void 280 obj_coff_seh_32 (int what) 281 { 282 if (!verify_context_and_target ((what ? ".seh_32" : ".seh_no32"), 283 seh_kind_arm)) 284 return; 285 286 seh_ctx_cur->use_instruction_32 = (what ? 1 : 0); 287 demand_empty_rest_of_line (); 288 } 289 290 /* Set for current context the handler and optional data (arm). */ 291 292 static void 293 obj_coff_seh_eh (int what ATTRIBUTE_UNUSED) 294 { 295 if (!verify_context_and_target (".seh_eh", seh_kind_arm)) 296 return; 297 298 /* Write block to .text if exception handler is set. */ 299 seh_ctx_cur->handler_written = 1; 300 emit_expr (&seh_ctx_cur->handler, 4); 301 emit_expr (&seh_ctx_cur->handler_data, 4); 302 303 demand_empty_rest_of_line (); 304 } 305 306 /* Set for current context the default handler (x64). */ 307 308 static void 309 obj_coff_seh_handler (int what ATTRIBUTE_UNUSED) 310 { 311 char *symbol_name; 312 char name_end; 313 314 if (!verify_context (".seh_handler")) 315 return; 316 317 if (*input_line_pointer == 0 || *input_line_pointer == '\n') 318 { 319 as_bad (_(".seh_handler requires a handler")); 320 demand_empty_rest_of_line (); 321 return; 322 } 323 324 SKIP_WHITESPACE (); 325 326 if (*input_line_pointer == '@') 327 { 328 name_end = get_symbol_name (&symbol_name); 329 330 seh_ctx_cur->handler.X_op = O_constant; 331 seh_ctx_cur->handler.X_add_number = 0; 332 333 if (strcasecmp (symbol_name, "@0") == 0 334 || strcasecmp (symbol_name, "@null") == 0) 335 ; 336 else if (strcasecmp (symbol_name, "@1") == 0) 337 seh_ctx_cur->handler.X_add_number = 1; 338 else 339 as_bad (_("unknown constant value '%s' for handler"), symbol_name); 340 341 (void) restore_line_pointer (name_end); 342 } 343 else 344 expression (&seh_ctx_cur->handler); 345 346 seh_ctx_cur->handler_data.X_op = O_constant; 347 seh_ctx_cur->handler_data.X_add_number = 0; 348 seh_ctx_cur->handler_flags = 0; 349 350 if (!skip_whitespace_and_comma (0)) 351 return; 352 353 if (seh_get_target_kind () == seh_kind_x64) 354 { 355 do 356 { 357 name_end = get_symbol_name (&symbol_name); 358 359 if (strcasecmp (symbol_name, "@unwind") == 0) 360 seh_ctx_cur->handler_flags |= UNW_FLAG_UHANDLER; 361 else if (strcasecmp (symbol_name, "@except") == 0) 362 seh_ctx_cur->handler_flags |= UNW_FLAG_EHANDLER; 363 else 364 as_bad (_(".seh_handler constant '%s' unknown"), symbol_name); 365 366 (void) restore_line_pointer (name_end); 367 } 368 while (skip_whitespace_and_comma (0)); 369 } 370 else 371 { 372 expression (&seh_ctx_cur->handler_data); 373 demand_empty_rest_of_line (); 374 375 if (seh_ctx_cur->handler_written) 376 as_warn (_(".seh_handler after .seh_eh is ignored")); 377 } 378 } 379 380 /* Switch to subsection for handler data for exception region (x64). */ 381 382 static void 383 obj_coff_seh_handlerdata (int what ATTRIBUTE_UNUSED) 384 { 385 if (!verify_context_and_target (".seh_handlerdata", seh_kind_x64)) 386 return; 387 demand_empty_rest_of_line (); 388 389 switch_xdata (seh_ctx_cur->subsection + 1, seh_ctx_cur->code_seg); 390 } 391 392 /* Mark end of current context. */ 393 394 static void 395 do_seh_endproc (void) 396 { 397 seh_ctx_cur->end_addr = symbol_temp_new_now (); 398 399 write_function_xdata (seh_ctx_cur); 400 write_function_pdata (seh_ctx_cur); 401 seh_ctx_cur = NULL; 402 } 403 404 static void 405 obj_coff_seh_endproc (int what ATTRIBUTE_UNUSED) 406 { 407 demand_empty_rest_of_line (); 408 if (seh_ctx_cur == NULL) 409 { 410 as_bad (_(".seh_endproc used without .seh_proc")); 411 return; 412 } 413 seh_validate_seg (".seh_endproc"); 414 do_seh_endproc (); 415 } 416 417 /* Mark begin of new context. */ 418 419 static void 420 obj_coff_seh_proc (int what ATTRIBUTE_UNUSED) 421 { 422 char *symbol_name; 423 char name_end; 424 425 if (seh_ctx_cur != NULL) 426 { 427 as_bad (_("previous SEH entry not closed (missing .seh_endproc)")); 428 do_seh_endproc (); 429 } 430 431 if (*input_line_pointer == 0 || *input_line_pointer == '\n') 432 { 433 as_bad (_(".seh_proc requires function label name")); 434 demand_empty_rest_of_line (); 435 return; 436 } 437 438 seh_ctx_cur = XCNEW (seh_context); 439 440 seh_ctx_cur->code_seg = now_seg; 441 442 if (seh_get_target_kind () == seh_kind_x64) 443 { 444 x_segcur = seh_hash_find_or_make (seh_ctx_cur->code_seg, ".xdata"); 445 seh_ctx_cur->subsection = x_segcur->subseg; 446 x_segcur->subseg += 2; 447 } 448 449 SKIP_WHITESPACE (); 450 451 name_end = get_symbol_name (&symbol_name); 452 seh_ctx_cur->func_name = xstrdup (symbol_name); 453 (void) restore_line_pointer (name_end); 454 455 demand_empty_rest_of_line (); 456 457 seh_ctx_cur->start_addr = symbol_temp_new_now (); 458 } 459 460 /* Mark end of prologue for current context. */ 461 462 static void 463 obj_coff_seh_endprologue (int what ATTRIBUTE_UNUSED) 464 { 465 if (!verify_context (".seh_endprologue") 466 || !seh_validate_seg (".seh_endprologue")) 467 return; 468 demand_empty_rest_of_line (); 469 470 if (seh_ctx_cur->endprologue_addr != NULL) 471 as_warn (_("duplicate .seh_endprologue in .seh_proc block")); 472 else 473 seh_ctx_cur->endprologue_addr = symbol_temp_new_now (); 474 } 475 476 /* End-of-file hook. */ 477 478 void 479 obj_coff_seh_do_final (void) 480 { 481 if (seh_ctx_cur != NULL) 482 as_bad (_("open SEH entry at end of file (missing .seh_endproc)")); 483 } 484 485 /* Enter a prologue element into current context (x64). */ 486 487 static void 488 seh_x64_make_prologue_element (int code, int info, offsetT off) 489 { 490 seh_prologue_element *n; 491 492 if (seh_ctx_cur == NULL) 493 return; 494 if (seh_ctx_cur->elems_count == seh_ctx_cur->elems_max) 495 { 496 seh_ctx_cur->elems_max += 8; 497 seh_ctx_cur->elems = XRESIZEVEC (seh_prologue_element, 498 seh_ctx_cur->elems, 499 seh_ctx_cur->elems_max); 500 } 501 502 n = &seh_ctx_cur->elems[seh_ctx_cur->elems_count++]; 503 n->code = code; 504 n->info = info; 505 n->off = off; 506 n->pc_addr = symbol_temp_new_now (); 507 } 508 509 /* Helper to read a register name from input stream (x64). */ 510 511 static int 512 seh_x64_read_reg (const char *directive, int kind) 513 { 514 static const char * const int_regs[16] = 515 { "rax", "rcx", "rdx", "rbx", "rsp", "rbp","rsi","rdi", 516 "r8","r9","r10","r11","r12","r13","r14","r15" }; 517 static const char * const xmm_regs[16] = 518 { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", 519 "xmm8", "xmm9", "xmm10","xmm11","xmm12","xmm13","xmm14","xmm15" }; 520 521 const char * const *regs = NULL; 522 char name_end; 523 char *symbol_name = NULL; 524 int i; 525 526 switch (kind) 527 { 528 case 0: 529 case 1: 530 regs = int_regs; 531 break; 532 case 2: 533 regs = xmm_regs; 534 break; 535 default: 536 abort (); 537 } 538 539 SKIP_WHITESPACE (); 540 if (*input_line_pointer == '%') 541 ++input_line_pointer; 542 name_end = get_symbol_name (& symbol_name); 543 544 for (i = 0; i < 16; i++) 545 if (! strcasecmp (regs[i], symbol_name)) 546 break; 547 548 (void) restore_line_pointer (name_end); 549 550 /* Error if register not found, or EAX used as a frame pointer. */ 551 if (i == 16 || (kind == 0 && i == 0)) 552 { 553 as_bad (_("invalid register for %s"), directive); 554 return -1; 555 } 556 557 return i; 558 } 559 560 /* Add a register push-unwind token to the current context. */ 561 562 static void 563 obj_coff_seh_pushreg (int what ATTRIBUTE_UNUSED) 564 { 565 int reg; 566 567 if (!verify_context_and_target (".seh_pushreg", seh_kind_x64) 568 || !seh_validate_seg (".seh_pushreg")) 569 return; 570 571 reg = seh_x64_read_reg (".seh_pushreg", 1); 572 demand_empty_rest_of_line (); 573 574 if (reg < 0) 575 return; 576 577 seh_x64_make_prologue_element (UWOP_PUSH_NONVOL, reg, 0); 578 } 579 580 /* Add a register frame-unwind token to the current context. */ 581 582 static void 583 obj_coff_seh_pushframe (int what ATTRIBUTE_UNUSED) 584 { 585 int code = 0; 586 587 if (!verify_context_and_target (".seh_pushframe", seh_kind_x64) 588 || !seh_validate_seg (".seh_pushframe")) 589 return; 590 591 SKIP_WHITESPACE(); 592 593 if (is_name_beginner (*input_line_pointer)) 594 { 595 char* identifier; 596 597 get_symbol_name (&identifier); 598 if (strcmp (identifier, "code") != 0) 599 { 600 as_bad(_("invalid argument \"%s\" for .seh_pushframe. Expected \"code\" or nothing"), 601 identifier); 602 return; 603 } 604 code = 1; 605 } 606 607 demand_empty_rest_of_line (); 608 609 seh_x64_make_prologue_element (UWOP_PUSH_MACHFRAME, code, 0); 610 } 611 612 /* Add a register save-unwind token to current context. */ 613 614 static void 615 obj_coff_seh_save (int what) 616 { 617 const char *directive = (what == 1 ? ".seh_savereg" : ".seh_savexmm"); 618 int code, reg, scale; 619 offsetT off; 620 621 if (!verify_context_and_target (directive, seh_kind_x64) 622 || !seh_validate_seg (directive)) 623 return; 624 625 reg = seh_x64_read_reg (directive, what); 626 627 if (!skip_whitespace_and_comma (1)) 628 return; 629 630 off = get_absolute_expression (); 631 demand_empty_rest_of_line (); 632 633 if (reg < 0) 634 return; 635 if (off < 0) 636 { 637 as_bad (_("%s offset is negative"), directive); 638 return; 639 } 640 641 scale = (what == 1 ? 8 : 16); 642 643 if ((off & (scale - 1)) == 0 && off <= (offsetT) (0xffff * scale)) 644 { 645 code = (what == 1 ? UWOP_SAVE_NONVOL : UWOP_SAVE_XMM128); 646 off /= scale; 647 } 648 else if (off < (offsetT) 0xffffffff) 649 code = (what == 1 ? UWOP_SAVE_NONVOL_FAR : UWOP_SAVE_XMM128_FAR); 650 else 651 { 652 as_bad (_("%s offset out of range"), directive); 653 return; 654 } 655 656 seh_x64_make_prologue_element (code, reg, off); 657 } 658 659 /* Add a stack-allocation token to current context. */ 660 661 static void 662 obj_coff_seh_stackalloc (int what ATTRIBUTE_UNUSED) 663 { 664 offsetT off; 665 int code, info; 666 667 if (!verify_context_and_target (".seh_stackalloc", seh_kind_x64) 668 || !seh_validate_seg (".seh_stackalloc")) 669 return; 670 671 off = get_absolute_expression (); 672 demand_empty_rest_of_line (); 673 674 if (off == 0) 675 return; 676 if (off < 0) 677 { 678 as_bad (_(".seh_stackalloc offset is negative")); 679 return; 680 } 681 682 if ((off & 7) == 0 && off <= 128) 683 code = UWOP_ALLOC_SMALL, info = (off - 8) >> 3, off = 0; 684 else if ((off & 7) == 0 && off <= (offsetT) (0xffff * 8)) 685 code = UWOP_ALLOC_LARGE, info = 0, off >>= 3; 686 else if (off <= (offsetT) 0xffffffff) 687 code = UWOP_ALLOC_LARGE, info = 1; 688 else 689 { 690 as_bad (_(".seh_stackalloc offset out of range")); 691 return; 692 } 693 694 seh_x64_make_prologue_element (code, info, off); 695 } 696 697 /* Add a frame-pointer token to current context. */ 698 699 static void 700 obj_coff_seh_setframe (int what ATTRIBUTE_UNUSED) 701 { 702 offsetT off; 703 int reg; 704 705 if (!verify_context_and_target (".seh_setframe", seh_kind_x64) 706 || !seh_validate_seg (".seh_setframe")) 707 return; 708 709 reg = seh_x64_read_reg (".seh_setframe", 0); 710 711 if (!skip_whitespace_and_comma (1)) 712 return; 713 714 off = get_absolute_expression (); 715 demand_empty_rest_of_line (); 716 717 if (reg < 0) 718 return; 719 if (off < 0) 720 as_bad (_(".seh_setframe offset is negative")); 721 else if (off > 240) 722 as_bad (_(".seh_setframe offset out of range")); 723 else if (off & 15) 724 as_bad (_(".seh_setframe offset not a multiple of 16")); 725 else if (seh_ctx_cur->framereg != 0) 726 as_bad (_("duplicate .seh_setframe in current .seh_proc")); 727 else 728 { 729 seh_ctx_cur->framereg = reg; 730 seh_ctx_cur->frameoff = off; 731 seh_x64_make_prologue_element (UWOP_SET_FPREG, 0, 0); 732 } 733 } 734 735 /* Data writing routines. */ 736 737 /* Output raw integers in 1, 2, or 4 bytes. */ 738 739 static inline void 740 out_one (int byte) 741 { 742 FRAG_APPEND_1_CHAR (byte); 743 } 744 745 static inline void 746 out_two (int data) 747 { 748 md_number_to_chars (frag_more (2), data, 2); 749 } 750 751 static inline void 752 out_four (int data) 753 { 754 md_number_to_chars (frag_more (4), data, 4); 755 } 756 757 /* Write out prologue data for x64. */ 758 759 static void 760 seh_x64_write_prologue_data (const seh_context *c) 761 { 762 int i; 763 764 /* We have to store in reverse order. */ 765 for (i = c->elems_count - 1; i >= 0; --i) 766 { 767 const seh_prologue_element *e = c->elems + i; 768 expressionS exp; 769 770 /* First comes byte offset in code. */ 771 exp.X_op = O_subtract; 772 exp.X_add_symbol = e->pc_addr; 773 exp.X_op_symbol = c->start_addr; 774 exp.X_add_number = 0; 775 emit_expr (&exp, 1); 776 777 /* Second comes code+info packed into a byte. */ 778 out_one ((e->info << 4) | e->code); 779 780 switch (e->code) 781 { 782 case UWOP_PUSH_NONVOL: 783 case UWOP_ALLOC_SMALL: 784 case UWOP_SET_FPREG: 785 case UWOP_PUSH_MACHFRAME: 786 /* These have no extra data. */ 787 break; 788 789 case UWOP_ALLOC_LARGE: 790 if (e->info) 791 { 792 case UWOP_SAVE_NONVOL_FAR: 793 case UWOP_SAVE_XMM128_FAR: 794 /* An unscaled 4 byte offset. */ 795 out_four (e->off); 796 break; 797 } 798 /* FALLTHRU */ 799 800 case UWOP_SAVE_NONVOL: 801 case UWOP_SAVE_XMM128: 802 /* A scaled 2 byte offset. */ 803 out_two (e->off); 804 break; 805 806 default: 807 abort (); 808 } 809 } 810 } 811 812 static int 813 seh_x64_size_prologue_data (const seh_context *c) 814 { 815 int i, ret = 0; 816 817 for (i = c->elems_count - 1; i >= 0; --i) 818 switch (c->elems[i].code) 819 { 820 case UWOP_PUSH_NONVOL: 821 case UWOP_ALLOC_SMALL: 822 case UWOP_SET_FPREG: 823 case UWOP_PUSH_MACHFRAME: 824 ret += 1; 825 break; 826 827 case UWOP_SAVE_NONVOL: 828 case UWOP_SAVE_XMM128: 829 ret += 2; 830 break; 831 832 case UWOP_SAVE_NONVOL_FAR: 833 case UWOP_SAVE_XMM128_FAR: 834 ret += 3; 835 break; 836 837 case UWOP_ALLOC_LARGE: 838 ret += (c->elems[i].info ? 3 : 2); 839 break; 840 841 default: 842 abort (); 843 } 844 845 return ret; 846 } 847 848 /* Write out the xdata information for one function (x64). */ 849 850 static void 851 seh_x64_write_function_xdata (seh_context *c) 852 { 853 int flags, count_unwind_codes; 854 expressionS exp; 855 856 /* Set 4-byte alignment. */ 857 frag_align (2, 0, 0); 858 859 c->xdata_addr = symbol_temp_new_now (); 860 flags = c->handler_flags; 861 count_unwind_codes = seh_x64_size_prologue_data (c); 862 863 /* ubyte:3 version, ubyte:5 flags. */ 864 out_one ((flags << 3) | 1); 865 866 /* Size of prologue. */ 867 if (c->endprologue_addr) 868 { 869 exp.X_op = O_subtract; 870 exp.X_add_symbol = c->endprologue_addr; 871 exp.X_op_symbol = c->start_addr; 872 exp.X_add_number = 0; 873 emit_expr (&exp, 1); 874 } 875 else 876 out_one (0); 877 878 /* Number of slots (i.e. shorts) in the unwind codes array. */ 879 if (count_unwind_codes > 255) 880 as_fatal (_("too much unwind data in this .seh_proc")); 881 out_one (count_unwind_codes); 882 883 /* ubyte:4 frame-reg, ubyte:4 frame-reg-offset. */ 884 /* Note that frameoff is already a multiple of 16, and therefore 885 the offset is already both scaled and shifted into place. */ 886 out_one (c->frameoff | c->framereg); 887 888 seh_x64_write_prologue_data (c); 889 890 /* We need to align prologue data. */ 891 if (count_unwind_codes & 1) 892 out_two (0); 893 894 if (flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER)) 895 { 896 /* Force the use of segment-relative relocations instead of absolute 897 valued expressions. Don't adjust for constants (e.g. NULL). */ 898 if (c->handler.X_op == O_symbol) 899 c->handler.X_op = O_symbol_rva; 900 emit_expr (&c->handler, 4); 901 } 902 903 /* Handler data will be tacked in here by subsections. */ 904 } 905 906 /* Write out xdata for one function. */ 907 908 static void 909 write_function_xdata (seh_context *c) 910 { 911 segT save_seg = now_seg; 912 int save_subseg = now_subseg; 913 914 /* MIPS, SH, ARM don't have xdata. */ 915 if (seh_get_target_kind () != seh_kind_x64) 916 return; 917 918 switch_xdata (c->subsection, c->code_seg); 919 920 seh_x64_write_function_xdata (c); 921 922 subseg_set (save_seg, save_subseg); 923 } 924 925 /* Write pdata section data for one function (arm). */ 926 927 static void 928 seh_arm_write_function_pdata (seh_context *c) 929 { 930 expressionS exp; 931 unsigned int prol_len = 0, func_len = 0; 932 unsigned int val; 933 934 /* Start address of the function. */ 935 exp.X_op = O_symbol; 936 exp.X_add_symbol = c->start_addr; 937 exp.X_add_number = 0; 938 emit_expr (&exp, 4); 939 940 exp.X_op = O_subtract; 941 exp.X_add_symbol = c->end_addr; 942 exp.X_op_symbol = c->start_addr; 943 exp.X_add_number = 0; 944 if (resolve_expression (&exp) && exp.X_op == O_constant) 945 func_len = exp.X_add_number; 946 else 947 as_bad (_(".seh_endproc in a different section from .seh_proc")); 948 949 if (c->endprologue_addr) 950 { 951 exp.X_op = O_subtract; 952 exp.X_add_symbol = c->endprologue_addr; 953 exp.X_op_symbol = c->start_addr; 954 exp.X_add_number = 0; 955 956 if (resolve_expression (&exp) && exp.X_op == O_constant) 957 prol_len = exp.X_add_number; 958 else 959 as_bad (_(".seh_endprologue in a different section from .seh_proc")); 960 } 961 962 /* Both function and prologue are in units of instructions. */ 963 func_len >>= (c->use_instruction_32 ? 2 : 1); 964 prol_len >>= (c->use_instruction_32 ? 2 : 1); 965 966 /* Assemble the second word of the pdata. */ 967 val = prol_len & 0xff; 968 val |= (func_len & 0x3fffff) << 8; 969 if (c->use_instruction_32) 970 val |= 0x40000000U; 971 if (c->handler_written) 972 val |= 0x80000000U; 973 out_four (val); 974 } 975 976 /* Write out pdata for one function. */ 977 978 static void 979 write_function_pdata (seh_context *c) 980 { 981 expressionS exp; 982 segT save_seg = now_seg; 983 int save_subseg = now_subseg; 984 memset (&exp, 0, sizeof (expressionS)); 985 switch_pdata (c->code_seg); 986 987 switch (seh_get_target_kind ()) 988 { 989 case seh_kind_x64: 990 exp.X_op = O_symbol_rva; 991 exp.X_add_number = 0; 992 993 exp.X_add_symbol = c->start_addr; 994 emit_expr (&exp, 4); 995 exp.X_op = O_symbol_rva; 996 exp.X_add_number = 0; 997 exp.X_add_symbol = c->end_addr; 998 emit_expr (&exp, 4); 999 exp.X_op = O_symbol_rva; 1000 exp.X_add_number = 0; 1001 exp.X_add_symbol = c->xdata_addr; 1002 emit_expr (&exp, 4); 1003 break; 1004 1005 case seh_kind_mips: 1006 exp.X_op = O_symbol; 1007 exp.X_add_number = 0; 1008 1009 exp.X_add_symbol = c->start_addr; 1010 emit_expr (&exp, 4); 1011 exp.X_add_symbol = c->end_addr; 1012 emit_expr (&exp, 4); 1013 1014 emit_expr (&c->handler, 4); 1015 emit_expr (&c->handler_data, 4); 1016 1017 exp.X_add_symbol = (c->endprologue_addr 1018 ? c->endprologue_addr 1019 : c->start_addr); 1020 emit_expr (&exp, 4); 1021 break; 1022 1023 case seh_kind_arm: 1024 seh_arm_write_function_pdata (c); 1025 break; 1026 1027 default: 1028 abort (); 1029 } 1030 1031 subseg_set (save_seg, save_subseg); 1032 } 1033