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