Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

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