ChangeLog gas
[deliverable/binutils-gdb.git] / gas / config / obj-coff-seh.c
CommitLineData
284e0531 1/* seh pdata/xdata coff object file format
681418c2 2 Copyright 2009, 2010
284e0531
KT
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
987499b2
KT
22#include "obj-coff-seh.h"
23
987499b2 24
2d7f4929
KT
25/* Private segment collection list. */
26struct seh_seg_list {
27 segT seg;
28 int subseg;
29 char *seg_name;
30};
31
987499b2 32/* Local data. */
987499b2
KT
33static seh_context *seh_ctx_cur = NULL;
34
2d7f4929
KT
35static struct hash_control *seh_hash;
36
37static struct seh_seg_list *x_segcur = NULL;
38static struct seh_seg_list *p_segcur = NULL;
681418c2
RH
39
40static void write_function_xdata (seh_context *);
41static void write_function_pdata (seh_context *);
604ab327 42
681418c2 43\f
2d7f4929
KT
44/* Build based on segment the derived .pdata/.xdata
45 segment name containing origin segment's postfix name part. */
46static char *
47get_pxdata_name (segT seg, const char *base_name)
987499b2 48{
2d7f4929
KT
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. */
74static struct seh_seg_list *
75alloc_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. */
89static segT
90make_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;
987499b2
KT
115}
116
987499b2 117static void
2d7f4929
KT
118seh_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
127static struct seh_seg_list *
128seh_hash_find (char *name)
987499b2 129{
2d7f4929
KT
130 return (struct seh_seg_list *) hash_find (seh_hash, name);
131}
132
133static struct seh_seg_list *
134seh_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)
681418c2 147 {
2d7f4929
KT
148 item = alloc_pxdata_item (make_pxdata_seg (cseg, name), 0, name);
149
150 seh_hash_insert (item->seg_name, item);
681418c2
RH
151 }
152 else
2d7f4929
KT
153 free (name);
154
155 return item;
156}
157
158static void
159switch_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
166static void
167switch_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);
681418c2
RH
172}
173\f
174/* Parsing routines. */
987499b2 175
681418c2
RH
176/* Return the style of SEH unwind info to generate. */
177
178static seh_kind
179seh_get_target_kind (void)
180{
181 if (!stdoutput)
182 return seh_kind_unknown;
183 switch (bfd_get_arch (stdoutput))
987499b2 184 {
681418c2
RH
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))
987499b2 191 {
681418c2
RH
192 case bfd_mach_x86_64:
193 case bfd_mach_x86_64_intel_syntax:
194 return seh_kind_x64;
195 default:
196 break;
987499b2 197 }
681418c2
RH
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;
987499b2
KT
204 default:
205 break;
206 }
681418c2 207 return seh_kind_unknown;
987499b2
KT
208}
209
681418c2
RH
210/* Verify that we're in the context of a seh_proc. */
211
212static int
213verify_context (const char *directive)
987499b2 214{
681418c2 215 if (seh_ctx_cur == NULL)
987499b2 216 {
681418c2
RH
217 as_bad (_("%s used outside of .seh_proc block"), directive);
218 ignore_rest_of_line ();
219 return 0;
987499b2 220 }
681418c2
RH
221 return 1;
222}
987499b2 223
681418c2 224/* Similar, except we also verify the appropriate target. */
987499b2 225
681418c2
RH
226static int
227verify_context_and_target (const char *directive, seh_kind target)
228{
229 if (seh_get_target_kind () != target)
987499b2 230 {
681418c2
RH
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
240static int
241skip_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;
987499b2 249 }
681418c2
RH
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;
987499b2
KT
258}
259
681418c2 260/* Mark current context to use 32-bit instruction (arm). */
1e17085d 261
987499b2 262static void
681418c2 263obj_coff_seh_32 (int what)
987499b2 264{
681418c2
RH
265 if (!verify_context_and_target ((what ? ".seh_32" : ".seh_no32"),
266 seh_kind_arm))
267 return;
604ab327 268
681418c2
RH
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
275static void
276obj_coff_seh_eh (int what ATTRIBUTE_UNUSED)
277{
278 if (!verify_context_and_target (".seh_eh", seh_kind_arm))
987499b2 279 return;
681418c2
RH
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 ();
987499b2
KT
287}
288
681418c2
RH
289/* Set for current context the default handler (x64). */
290
987499b2 291static void
681418c2 292obj_coff_seh_handler (int what ATTRIBUTE_UNUSED)
987499b2 293{
681418c2
RH
294 char *symbol_name;
295 char name_end;
296
297 if (!verify_context (".seh_handler"))
987499b2 298 return;
681418c2
RH
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 == '@')
987499b2 310 {
681418c2
RH
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;
987499b2 326 }
681418c2
RH
327 else
328 expression (&seh_ctx_cur->handler);
987499b2 329
681418c2
RH
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))
987499b2
KT
335 return;
336
681418c2 337 if (seh_get_target_kind () == seh_kind_x64)
987499b2 338 {
681418c2 339 do
987499b2 340 {
681418c2
RH
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;
987499b2 352 }
681418c2
RH
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
367static void
368obj_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
2d7f4929 374 switch_xdata (seh_ctx_cur->subsection + 1, seh_ctx_cur->code_seg);
681418c2
RH
375}
376
377/* Mark end of current context. */
378
379static void
380do_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
389static void
390obj_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
404static void
405obj_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
2d7f4929
KT
425 seh_ctx_cur->code_seg = now_seg;
426
681418c2
RH
427 if (seh_get_target_kind () == seh_kind_x64)
428 {
2d7f4929
KT
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;
987499b2 432 }
681418c2
RH
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 ();
987499b2
KT
444}
445
681418c2
RH
446/* Mark end of prologue for current context. */
447
448static void
449obj_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
987499b2
KT
463void
464obj_coff_seh_do_final (void)
465{
681418c2 466 if (seh_ctx_cur != NULL)
987499b2 467 {
681418c2
RH
468 as_bad (_("open SEH entry at end of file (missing .cfi_endproc)"));
469 do_seh_endproc ();
987499b2
KT
470 }
471}
472
681418c2
RH
473/* Enter a prologue element into current context (x64). */
474
987499b2 475static void
681418c2 476seh_x64_make_prologue_element (int code, int info, offsetT off)
987499b2
KT
477{
478 seh_prologue_element *n;
604ab327 479
987499b2
KT
480 if (seh_ctx_cur == NULL)
481 return;
482 if (seh_ctx_cur->elems_count == seh_ctx_cur->elems_max)
483 {
987499b2 484 seh_ctx_cur->elems_max += 8;
681418c2
RH
485 seh_ctx_cur->elems = XRESIZEVEC (seh_prologue_element,
486 seh_ctx_cur->elems,
487 seh_ctx_cur->elems_max);
987499b2 488 }
681418c2
RH
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 ();
987499b2
KT
495}
496
681418c2
RH
497/* Helper to read a register name from input stream (x64). */
498
987499b2 499static int
681418c2 500seh_x64_read_reg (const char *directive, int kind)
987499b2 501{
681418c2 502 static const char * const int_regs[16] =
604ab327
NC
503 { "rax", "rcx", "rdx", "rbx", "rsp", "rbp","rsi","rdi",
504 "r8","r9","r10","r11","r12","r13","r14","r15" };
681418c2 505 static const char * const xmm_regs[16] =
604ab327
NC
506 { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
507 "xmm8", "xmm9", "xmm10","xmm11","xmm12","xmm13","xmm14","xmm15" };
681418c2
RH
508
509 const char * const *regs = NULL;
987499b2
KT
510 char name_end;
511 char *symbol_name = NULL;
512 int i;
513
987499b2 514 switch (kind)
604ab327
NC
515 {
516 case 0:
604ab327 517 case 1:
681418c2 518 regs = int_regs;
604ab327
NC
519 break;
520 case 2:
681418c2 521 regs = xmm_regs;
604ab327
NC
522 break;
523 default:
524 abort ();
525 }
526
681418c2 527 SKIP_WHITESPACE ();
987499b2
KT
528 if (*input_line_pointer == '%')
529 ++input_line_pointer;
530 symbol_name = input_line_pointer;
531 name_end = get_symbol_end ();
604ab327 532
987499b2 533 for (i = 0; i < 16; i++)
681418c2 534 if (! strcasecmp (regs[i], symbol_name))
987499b2 535 break;
604ab327 536
987499b2 537 *input_line_pointer = name_end;
987499b2 538
681418c2
RH
539 /* Error if register not found, or EAX used as a frame pointer. */
540 if (i == 16 || (kind == 0 && i == 0))
604ab327 541 {
681418c2
RH
542 as_bad (_("invalid register for %s"), directive);
543 return -1;
604ab327 544 }
1e17085d 545
681418c2 546 return i;
987499b2
KT
547}
548
681418c2
RH
549/* Add a register push-unwind token to the current context. */
550
987499b2 551static void
681418c2 552obj_coff_seh_pushreg (int what ATTRIBUTE_UNUSED)
987499b2 553{
681418c2
RH
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);
987499b2 560 demand_empty_rest_of_line ();
681418c2
RH
561
562 if (reg < 0)
563 return;
564
565 seh_x64_make_prologue_element (UWOP_PUSH_NONVOL, reg, 0);
987499b2
KT
566}
567
681418c2
RH
568/* Add a register frame-unwind token to the current context. */
569
987499b2 570static void
681418c2 571obj_coff_seh_pushframe (int what ATTRIBUTE_UNUSED)
987499b2 572{
681418c2
RH
573 if (!verify_context_and_target (".seh_pushframe", seh_kind_x64))
574 return;
987499b2 575 demand_empty_rest_of_line ();
987499b2 576
681418c2 577 seh_x64_make_prologue_element (UWOP_PUSH_MACHFRAME, 0, 0);
987499b2
KT
578}
579
681418c2
RH
580/* Add a register save-unwind token to current context. */
581
987499b2 582static void
681418c2 583obj_coff_seh_save (int what)
987499b2 584{
681418c2
RH
585 const char *directive = (what == 1 ? ".seh_savereg" : ".seh_savexmm");
586 int code, reg, scale;
587 offsetT off;
987499b2 588
681418c2
RH
589 if (!verify_context_and_target (directive, seh_kind_x64))
590 return;
987499b2 591
681418c2 592 reg = seh_x64_read_reg (directive, what);
987499b2 593
681418c2
RH
594 if (!skip_whitespace_and_comma (1))
595 return;
987499b2 596
681418c2 597 off = get_absolute_expression ();
987499b2 598 demand_empty_rest_of_line ();
987499b2 599
681418c2
RH
600 if (reg < 0)
601 return;
602 if (off < 0)
987499b2 603 {
681418c2 604 as_bad (_("%s offset is negative"), directive);
987499b2
KT
605 return;
606 }
607
681418c2 608 scale = (what == 1 ? 8 : 16);
987499b2 609
6e0973c0 610 if ((off & (scale - 1)) == 0 && off <= (offsetT) (0xffff * scale))
987499b2 611 {
681418c2
RH
612 code = (what == 1 ? UWOP_SAVE_NONVOL : UWOP_SAVE_XMM128);
613 off /= scale;
987499b2 614 }
6e0973c0 615 else if (off < (offsetT) 0xffffffff)
681418c2
RH
616 code = (what == 1 ? UWOP_SAVE_NONVOL_FAR : UWOP_SAVE_XMM128_FAR);
617 else
604ab327 618 {
681418c2 619 as_bad (_("%s offset out of range"), directive);
604ab327
NC
620 return;
621 }
681418c2
RH
622
623 seh_x64_make_prologue_element (code, reg, off);
987499b2
KT
624}
625
681418c2
RH
626/* Add a stack-allocation token to current context. */
627
987499b2 628static void
681418c2 629obj_coff_seh_stackalloc (int what ATTRIBUTE_UNUSED)
987499b2 630{
681418c2
RH
631 offsetT off;
632 int code, info;
604ab327 633
681418c2
RH
634 if (!verify_context_and_target (".seh_stackalloc", seh_kind_x64))
635 return;
1e17085d 636
681418c2 637 off = get_absolute_expression ();
987499b2 638 demand_empty_rest_of_line ();
987499b2 639
681418c2
RH
640 if (off == 0)
641 return;
642 if (off < 0)
604ab327 643 {
681418c2 644 as_bad (_(".seh_stackalloc offset is negative"));
604ab327
NC
645 return;
646 }
987499b2 647
681418c2
RH
648 if ((off & 7) == 0 && off <= 128)
649 code = UWOP_ALLOC_SMALL, info = (off - 8) >> 3, off = 0;
6e0973c0 650 else if ((off & 7) == 0 && off <= (offsetT) (0xffff * 8))
681418c2 651 code = UWOP_ALLOC_LARGE, info = 0, off >>= 3;
6e0973c0 652 else if (off <= (offsetT) 0xffffffff)
681418c2 653 code = UWOP_ALLOC_LARGE, info = 1;
987499b2 654 else
604ab327 655 {
681418c2 656 as_bad (_(".seh_stackalloc offset out of range"));
604ab327
NC
657 return;
658 }
681418c2
RH
659
660 seh_x64_make_prologue_element (code, info, off);
987499b2
KT
661}
662
681418c2
RH
663/* Add a frame-pointer token to current context. */
664
987499b2
KT
665static void
666obj_coff_seh_setframe (int what ATTRIBUTE_UNUSED)
667{
681418c2 668 offsetT off;
987499b2 669 int reg;
987499b2 670
681418c2
RH
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
604ab327
NC
693 {
694 seh_ctx_cur->framereg = reg;
695 seh_ctx_cur->frameoff = off;
681418c2 696 seh_x64_make_prologue_element (UWOP_SET_FPREG, 0, 0);
604ab327 697 }
987499b2 698}
681418c2
RH
699\f
700/* Data writing routines. */
987499b2 701
681418c2 702/* Output raw integers in 1, 2, or 4 bytes. */
987499b2 703
681418c2
RH
704static inline void
705out_one (int byte)
987499b2 706{
681418c2 707 FRAG_APPEND_1_CHAR (byte);
987499b2
KT
708}
709
681418c2
RH
710static inline void
711out_two (int data)
987499b2 712{
681418c2 713 md_number_to_chars (frag_more (2), data, 2);
987499b2
KT
714}
715
681418c2
RH
716static inline void
717out_four (int data)
987499b2 718{
681418c2 719 md_number_to_chars (frag_more (4), data, 4);
987499b2
KT
720}
721
681418c2
RH
722/* Write out prologue data for x64. */
723
724static void
725seh_x64_write_prologue_data (const seh_context *c)
987499b2 726{
681418c2 727 int i;
604ab327 728
681418c2
RH
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;
987499b2 734
681418c2
RH
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);
987499b2 741
681418c2
RH
742 /* Second comes code+info packed into a byte. */
743 out_one ((e->info << 4) | e->code);
987499b2 744
681418c2 745 switch (e->code)
987499b2 746 {
681418c2
RH
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. */
987499b2 752 break;
681418c2
RH
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 ();
987499b2 773 }
987499b2 774 }
987499b2
KT
775}
776
681418c2
RH
777static int
778seh_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;
987499b2 791
681418c2
RH
792 case UWOP_SAVE_NONVOL:
793 case UWOP_SAVE_XMM128:
794 ret += 2;
795 break;
987499b2 796
681418c2
RH
797 case UWOP_SAVE_NONVOL_FAR:
798 case UWOP_SAVE_XMM128_FAR:
799 ret += 3;
800 break;
987499b2 801
681418c2
RH
802 case UWOP_ALLOC_LARGE:
803 ret += (c->elems[i].info ? 3 : 2);
804 break;
987499b2 805
681418c2
RH
806 default:
807 abort ();
808 }
987499b2 809
681418c2 810 return ret;
987499b2
KT
811}
812
681418c2
RH
813/* Write out the xdata information for one function (x64). */
814
815static void
816seh_x64_write_function_xdata (seh_context *c)
987499b2 817{
681418c2
RH
818 int flags, count_unwind_codes;
819 expressionS exp;
987499b2 820
681418c2
RH
821 /* Set 4-byte alignment. */
822 frag_align (2, 0, 0);
987499b2 823
681418c2
RH
824 c->xdata_addr = symbol_temp_new_now ();
825 flags = c->handler_flags;
826 count_unwind_codes = seh_x64_size_prologue_data (c);
987499b2 827
681418c2
RH
828 /* ubyte:3 version, ubyte:5 flags. */
829 out_one ((flags << 3) | 1);
987499b2 830
681418c2
RH
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);
987499b2 842
681418c2
RH
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);
987499b2 847
681418c2
RH
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);
604ab327 852
681418c2 853 seh_x64_write_prologue_data (c);
987499b2 854
681418c2
RH
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))
604ab327 860 {
681418c2
RH
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);
604ab327 866 }
681418c2
RH
867
868 /* Handler data will be tacked in here by subsections. */
987499b2
KT
869}
870
681418c2 871/* Write out xdata for one function. */
987499b2
KT
872
873static void
681418c2 874write_function_xdata (seh_context *c)
987499b2 875{
681418c2
RH
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)
987499b2 881 return;
987499b2 882
2d7f4929 883 switch_xdata (c->subsection, c->code_seg);
987499b2 884
681418c2 885 seh_x64_write_function_xdata (c);
1e17085d 886
681418c2 887 subseg_set (save_seg, save_subseg);
1e17085d
KT
888}
889
681418c2 890/* Write pdata section data for one function (arm). */
987499b2 891
681418c2
RH
892static void
893seh_arm_write_function_pdata (seh_context *c)
987499b2 894{
681418c2
RH
895 expressionS exp;
896 unsigned int prol_len = 0, func_len = 0;
897 unsigned int val;
604ab327 898
681418c2
RH
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;
987499b2 911 else
681418c2
RH
912 as_bad (_(".seh_endproc in a different section from .seh_proc"));
913
914 if (c->endprologue_addr)
987499b2 915 {
681418c2
RH
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"));
987499b2 925 }
681418c2
RH
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);
987499b2
KT
939}
940
681418c2
RH
941/* Write out pdata for one function. */
942
987499b2 943static void
681418c2 944write_function_pdata (seh_context *c)
987499b2 945{
681418c2
RH
946 expressionS exp;
947 segT save_seg = now_seg;
948 int save_subseg = now_subseg;
2d7f4929
KT
949 memset (&exp, 0, sizeof (expressionS));
950 switch_pdata (c->code_seg);
681418c2
RH
951
952 switch (seh_get_target_kind ())
987499b2 953 {
681418c2
RH
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);
2d7f4929
KT
960 exp.X_op = O_symbol_rva;
961 exp.X_add_number = 0;
681418c2
RH
962 exp.X_add_symbol = c->end_addr;
963 emit_expr (&exp, 4);
2d7f4929
KT
964 exp.X_op = O_symbol_rva;
965 exp.X_add_number = 0;
681418c2
RH
966 exp.X_add_symbol = c->xdata_addr;
967 emit_expr (&exp, 4);
968 break;
987499b2 969
681418c2
RH
970 case seh_kind_mips:
971 exp.X_op = O_symbol;
972 exp.X_add_number = 0;
987499b2 973
681418c2
RH
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);
987499b2 978
681418c2
RH
979 emit_expr (&c->handler, 4);
980 emit_expr (&c->handler_data, 4);
604ab327 981
681418c2
RH
982 exp.X_add_symbol = (c->endprologue_addr
983 ? c->endprologue_addr
984 : c->start_addr);
985 emit_expr (&exp, 4);
986 break;
987499b2 987
681418c2
RH
988 case seh_kind_arm:
989 seh_arm_write_function_pdata (c);
990 break;
604ab327 991
681418c2
RH
992 default:
993 abort ();
987499b2 994 }
681418c2
RH
995
996 subseg_set (save_seg, save_subseg);
987499b2 997}
This page took 0.142251 seconds and 4 git commands to generate.