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