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