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