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