1/* seh pdata/xdata coff object file format 2 Copyright (C) 2009-2020 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. */ 25struct seh_seg_list { 26 segT seg; 27 int subseg; 28 char *seg_name; 29}; 30 31/* Local data. */ 32static seh_context *seh_ctx_cur = NULL; 33 34static htab_t seh_hash; 35 36static struct seh_seg_list *x_segcur = NULL; 37static struct seh_seg_list *p_segcur = NULL; 38 39static void write_function_xdata (seh_context *); 40static 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. */ 45static char * 46get_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. */ 73static struct seh_seg_list * 74alloc_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. */ 88static segT 89make_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 116static void 117seh_hash_insert (const char *name, struct seh_seg_list *item) 118{ 119 str_hash_insert (seh_hash, name, item, 1); 120} 121 122static struct seh_seg_list * 123seh_hash_find (char *name) 124{ 125 return (struct seh_seg_list *) str_hash_find (seh_hash, name); 126} 127 128static struct seh_seg_list * 129seh_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. */ 154static int 155seh_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. */ 169static void 170obj_coff_seh_code (int ignored ATTRIBUTE_UNUSED) 171{ 172 subseg_set (seh_ctx_cur->code_seg, 0); 173} 174 175static void 176switch_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 183static void 184switch_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 195static seh_kind 196seh_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 229static int 230verify_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 243static int 244verify_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 257static int 258skip_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 279static void 280obj_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 292static void 293obj_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 308static void 309obj_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 382static void 383obj_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 394static void 395do_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 404static void 405obj_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 419static void 420obj_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 462static void 463obj_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 478void 479obj_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 487static void 488seh_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 511static int 512seh_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 562static void 563obj_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 582static void 583obj_coff_seh_pushframe (int what ATTRIBUTE_UNUSED) 584{ 585 if (!verify_context_and_target (".seh_pushframe", seh_kind_x64) 586 || !seh_validate_seg (".seh_pushframe")) 587 return; 588 demand_empty_rest_of_line (); 589 590 seh_x64_make_prologue_element (UWOP_PUSH_MACHFRAME, 0, 0); 591} 592 593/* Add a register save-unwind token to current context. */ 594 595static void 596obj_coff_seh_save (int what) 597{ 598 const char *directive = (what == 1 ? ".seh_savereg" : ".seh_savexmm"); 599 int code, reg, scale; 600 offsetT off; 601 602 if (!verify_context_and_target (directive, seh_kind_x64) 603 || !seh_validate_seg (directive)) 604 return; 605 606 reg = seh_x64_read_reg (directive, what); 607 608 if (!skip_whitespace_and_comma (1)) 609 return; 610 611 off = get_absolute_expression (); 612 demand_empty_rest_of_line (); 613 614 if (reg < 0) 615 return; 616 if (off < 0) 617 { 618 as_bad (_("%s offset is negative"), directive); 619 return; 620 } 621 622 scale = (what == 1 ? 8 : 16); 623 624 if ((off & (scale - 1)) == 0 && off <= (offsetT) (0xffff * scale)) 625 { 626 code = (what == 1 ? UWOP_SAVE_NONVOL : UWOP_SAVE_XMM128); 627 off /= scale; 628 } 629 else if (off < (offsetT) 0xffffffff) 630 code = (what == 1 ? UWOP_SAVE_NONVOL_FAR : UWOP_SAVE_XMM128_FAR); 631 else 632 { 633 as_bad (_("%s offset out of range"), directive); 634 return; 635 } 636 637 seh_x64_make_prologue_element (code, reg, off); 638} 639 640/* Add a stack-allocation token to current context. */ 641 642static void 643obj_coff_seh_stackalloc (int what ATTRIBUTE_UNUSED) 644{ 645 offsetT off; 646 int code, info; 647 648 if (!verify_context_and_target (".seh_stackalloc", seh_kind_x64) 649 || !seh_validate_seg (".seh_stackalloc")) 650 return; 651 652 off = get_absolute_expression (); 653 demand_empty_rest_of_line (); 654 655 if (off == 0) 656 return; 657 if (off < 0) 658 { 659 as_bad (_(".seh_stackalloc offset is negative")); 660 return; 661 } 662 663 if ((off & 7) == 0 && off <= 128) 664 code = UWOP_ALLOC_SMALL, info = (off - 8) >> 3, off = 0; 665 else if ((off & 7) == 0 && off <= (offsetT) (0xffff * 8)) 666 code = UWOP_ALLOC_LARGE, info = 0, off >>= 3; 667 else if (off <= (offsetT) 0xffffffff) 668 code = UWOP_ALLOC_LARGE, info = 1; 669 else 670 { 671 as_bad (_(".seh_stackalloc offset out of range")); 672 return; 673 } 674 675 seh_x64_make_prologue_element (code, info, off); 676} 677 678/* Add a frame-pointer token to current context. */ 679 680static void 681obj_coff_seh_setframe (int what ATTRIBUTE_UNUSED) 682{ 683 offsetT off; 684 int reg; 685 686 if (!verify_context_and_target (".seh_setframe", seh_kind_x64) 687 || !seh_validate_seg (".seh_setframe")) 688 return; 689 690 reg = seh_x64_read_reg (".seh_setframe", 0); 691 692 if (!skip_whitespace_and_comma (1)) 693 return; 694 695 off = get_absolute_expression (); 696 demand_empty_rest_of_line (); 697 698 if (reg < 0) 699 return; 700 if (off < 0) 701 as_bad (_(".seh_setframe offset is negative")); 702 else if (off > 240) 703 as_bad (_(".seh_setframe offset out of range")); 704 else if (off & 15) 705 as_bad (_(".seh_setframe offset not a multiple of 16")); 706 else if (seh_ctx_cur->framereg != 0) 707 as_bad (_("duplicate .seh_setframe in current .seh_proc")); 708 else 709 { 710 seh_ctx_cur->framereg = reg; 711 seh_ctx_cur->frameoff = off; 712 seh_x64_make_prologue_element (UWOP_SET_FPREG, 0, 0); 713 } 714} 715 716/* Data writing routines. */ 717 718/* Output raw integers in 1, 2, or 4 bytes. */ 719 720static inline void 721out_one (int byte) 722{ 723 FRAG_APPEND_1_CHAR (byte); 724} 725 726static inline void 727out_two (int data) 728{ 729 md_number_to_chars (frag_more (2), data, 2); 730} 731 732static inline void 733out_four (int data) 734{ 735 md_number_to_chars (frag_more (4), data, 4); 736} 737 738/* Write out prologue data for x64. */ 739 740static void 741seh_x64_write_prologue_data (const seh_context *c) 742{ 743 int i; 744 745 /* We have to store in reverse order. */ 746 for (i = c->elems_count - 1; i >= 0; --i) 747 { 748 const seh_prologue_element *e = c->elems + i; 749 expressionS exp; 750 751 /* First comes byte offset in code. */ 752 exp.X_op = O_subtract; 753 exp.X_add_symbol = e->pc_addr; 754 exp.X_op_symbol = c->start_addr; 755 exp.X_add_number = 0; 756 emit_expr (&exp, 1); 757 758 /* Second comes code+info packed into a byte. */ 759 out_one ((e->info << 4) | e->code); 760 761 switch (e->code) 762 { 763 case UWOP_PUSH_NONVOL: 764 case UWOP_ALLOC_SMALL: 765 case UWOP_SET_FPREG: 766 case UWOP_PUSH_MACHFRAME: 767 /* These have no extra data. */ 768 break; 769 770 case UWOP_ALLOC_LARGE: 771 if (e->info) 772 { 773 case UWOP_SAVE_NONVOL_FAR: 774 case UWOP_SAVE_XMM128_FAR: 775 /* An unscaled 4 byte offset. */ 776 out_four (e->off); 777 break; 778 } 779 /* FALLTHRU */ 780 781 case UWOP_SAVE_NONVOL: 782 case UWOP_SAVE_XMM128: 783 /* A scaled 2 byte offset. */ 784 out_two (e->off); 785 break; 786 787 default: 788 abort (); 789 } 790 } 791} 792 793static int 794seh_x64_size_prologue_data (const seh_context *c) 795{ 796 int i, ret = 0; 797 798 for (i = c->elems_count - 1; i >= 0; --i) 799 switch (c->elems[i].code) 800 { 801 case UWOP_PUSH_NONVOL: 802 case UWOP_ALLOC_SMALL: 803 case UWOP_SET_FPREG: 804 case UWOP_PUSH_MACHFRAME: 805 ret += 1; 806 break; 807 808 case UWOP_SAVE_NONVOL: 809 case UWOP_SAVE_XMM128: 810 ret += 2; 811 break; 812 813 case UWOP_SAVE_NONVOL_FAR: 814 case UWOP_SAVE_XMM128_FAR: 815 ret += 3; 816 break; 817 818 case UWOP_ALLOC_LARGE: 819 ret += (c->elems[i].info ? 3 : 2); 820 break; 821 822 default: 823 abort (); 824 } 825 826 return ret; 827} 828 829/* Write out the xdata information for one function (x64). */ 830 831static void 832seh_x64_write_function_xdata (seh_context *c) 833{ 834 int flags, count_unwind_codes; 835 expressionS exp; 836 837 /* Set 4-byte alignment. */ 838 frag_align (2, 0, 0); 839 840 c->xdata_addr = symbol_temp_new_now (); 841 flags = c->handler_flags; 842 count_unwind_codes = seh_x64_size_prologue_data (c); 843 844 /* ubyte:3 version, ubyte:5 flags. */ 845 out_one ((flags << 3) | 1); 846 847 /* Size of prologue. */ 848 if (c->endprologue_addr) 849 { 850 exp.X_op = O_subtract; 851 exp.X_add_symbol = c->endprologue_addr; 852 exp.X_op_symbol = c->start_addr; 853 exp.X_add_number = 0; 854 emit_expr (&exp, 1); 855 } 856 else 857 out_one (0); 858 859 /* Number of slots (i.e. shorts) in the unwind codes array. */ 860 if (count_unwind_codes > 255) 861 as_fatal (_("too much unwind data in this .seh_proc")); 862 out_one (count_unwind_codes); 863 864 /* ubyte:4 frame-reg, ubyte:4 frame-reg-offset. */ 865 /* Note that frameoff is already a multiple of 16, and therefore 866 the offset is already both scaled and shifted into place. */ 867 out_one (c->frameoff | c->framereg); 868 869 seh_x64_write_prologue_data (c); 870 871 /* We need to align prologue data. */ 872 if (count_unwind_codes & 1) 873 out_two (0); 874 875 if (flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER)) 876 { 877 /* Force the use of segment-relative relocations instead of absolute 878 valued expressions. Don't adjust for constants (e.g. NULL). */ 879 if (c->handler.X_op == O_symbol) 880 c->handler.X_op = O_symbol_rva; 881 emit_expr (&c->handler, 4); 882 } 883 884 /* Handler data will be tacked in here by subsections. */ 885} 886 887/* Write out xdata for one function. */ 888 889static void 890write_function_xdata (seh_context *c) 891{ 892 segT save_seg = now_seg; 893 int save_subseg = now_subseg; 894 895 /* MIPS, SH, ARM don't have xdata. */ 896 if (seh_get_target_kind () != seh_kind_x64) 897 return; 898 899 switch_xdata (c->subsection, c->code_seg); 900 901 seh_x64_write_function_xdata (c); 902 903 subseg_set (save_seg, save_subseg); 904} 905 906/* Write pdata section data for one function (arm). */ 907 908static void 909seh_arm_write_function_pdata (seh_context *c) 910{ 911 expressionS exp; 912 unsigned int prol_len = 0, func_len = 0; 913 unsigned int val; 914 915 /* Start address of the function. */ 916 exp.X_op = O_symbol; 917 exp.X_add_symbol = c->start_addr; 918 exp.X_add_number = 0; 919 emit_expr (&exp, 4); 920 921 exp.X_op = O_subtract; 922 exp.X_add_symbol = c->end_addr; 923 exp.X_op_symbol = c->start_addr; 924 exp.X_add_number = 0; 925 if (resolve_expression (&exp) && exp.X_op == O_constant) 926 func_len = exp.X_add_number; 927 else 928 as_bad (_(".seh_endproc in a different section from .seh_proc")); 929 930 if (c->endprologue_addr) 931 { 932 exp.X_op = O_subtract; 933 exp.X_add_symbol = c->endprologue_addr; 934 exp.X_op_symbol = c->start_addr; 935 exp.X_add_number = 0; 936 937 if (resolve_expression (&exp) && exp.X_op == O_constant) 938 prol_len = exp.X_add_number; 939 else 940 as_bad (_(".seh_endprologue in a different section from .seh_proc")); 941 } 942 943 /* Both function and prologue are in units of instructions. */ 944 func_len >>= (c->use_instruction_32 ? 2 : 1); 945 prol_len >>= (c->use_instruction_32 ? 2 : 1); 946 947 /* Assemble the second word of the pdata. */ 948 val = prol_len & 0xff; 949 val |= (func_len & 0x3fffff) << 8; 950 if (c->use_instruction_32) 951 val |= 0x40000000U; 952 if (c->handler_written) 953 val |= 0x80000000U; 954 out_four (val); 955} 956 957/* Write out pdata for one function. */ 958 959static void 960write_function_pdata (seh_context *c) 961{ 962 expressionS exp; 963 segT save_seg = now_seg; 964 int save_subseg = now_subseg; 965 memset (&exp, 0, sizeof (expressionS)); 966 switch_pdata (c->code_seg); 967 968 switch (seh_get_target_kind ()) 969 { 970 case seh_kind_x64: 971 exp.X_op = O_symbol_rva; 972 exp.X_add_number = 0; 973 974 exp.X_add_symbol = c->start_addr; 975 emit_expr (&exp, 4); 976 exp.X_op = O_symbol_rva; 977 exp.X_add_number = 0; 978 exp.X_add_symbol = c->end_addr; 979 emit_expr (&exp, 4); 980 exp.X_op = O_symbol_rva; 981 exp.X_add_number = 0; 982 exp.X_add_symbol = c->xdata_addr; 983 emit_expr (&exp, 4); 984 break; 985 986 case seh_kind_mips: 987 exp.X_op = O_symbol; 988 exp.X_add_number = 0; 989 990 exp.X_add_symbol = c->start_addr; 991 emit_expr (&exp, 4); 992 exp.X_add_symbol = c->end_addr; 993 emit_expr (&exp, 4); 994 995 emit_expr (&c->handler, 4); 996 emit_expr (&c->handler_data, 4); 997 998 exp.X_add_symbol = (c->endprologue_addr 999 ? c->endprologue_addr 1000 : c->start_addr); 1001 emit_expr (&exp, 4); 1002 break; 1003 1004 case seh_kind_arm: 1005 seh_arm_write_function_pdata (c); 1006 break; 1007 1008 default: 1009 abort (); 1010 } 1011 1012 subseg_set (save_seg, save_subseg); 1013} 1014