Subversion Repositories Kolibri OS

Rev

Rev 5222 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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