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