OSDN Git Service

Add missed files.
[pf3gnuchains/sourceware.git] / gas / config / obj-coff-seh.c
1 #include "obj-coff-seh.h"
2
3 /* Forward declarations.  */
4 static seh_kind seh_get_target_kind (void);
5 static int seh_symbol (bfd *, const char *, const char *, const char *, asection *, int, int);
6 static void seh_reloc (bfd *, bfd_size_type, int, int);
7 static void save_relocs (asection *sec);
8 static asection *quick_section (bfd *abfd, const char *name, int flags, int align);
9 static void seh_symbol_init (bfd *abfd, unsigned int added);
10 static void seh_emit_rva (const char *);
11 static void seh_emit_long (const char *);
12 static void seh_make_globl (char *);
13 static segT seh_make_section (void);
14 static segT seh_make_section2 (const char *section_name, unsigned flags);
15 static char *seh_make_xlbl_name (seh_context *);
16 static char *make_seh_text_label (seh_context *c, symbolS **addr);
17
18 static void seh_write_text_eh_data (const char *hnd, const char *hnd_data);
19 static void seh_emit_rva (const char *name);
20 static int seh_needed_unwind_info (seh_context *);
21 static void seh_fill_pcsyms (const seh_context *c, char **, int *);
22 static size_t seh_getelm_data_size (const seh_context *, int, int);
23 static size_t seh_getsize_of_unwind_entry (seh_context *, int, int, int);
24 static void seh_make_unwind_entry (const seh_context *, char *, int, int, int, unsigned char *, size_t *, int);
25 static size_t seh_getsize_unwind_data (seh_context *);
26 static void seh_create_unwind_data (seh_context *, unsigned char *, size_t);
27 static void seh_make_function_entry_xdata (seh_context *, char *, char *, char *, unsigned char *, size_t *,int);
28 static seh_scope_elem *seh_x64_makescope_elem (seh_context *, const char *, const char *, const char *, const char *);
29
30 /* Local data.  */
31 static asymbol **symtab;
32 static int symptr;
33 static arelent *reltab = 0;
34 static int relcount = 0, relsize = 0;
35
36 static seh_context *seh_ctx_root = NULL;
37 static seh_context *seh_ctx = NULL;
38 static seh_context *seh_ctx_cur = NULL;
39
40 /* Write xdata for arm, sh3, sh4, and ppc.  */
41 static void
42 seh_write_text_eh_data (const char *hnd, const char *hnd_data)
43 {
44   if (!hnd || *hnd==0)
45     return;
46   if (hnd[0] == '@')
47     seh_emit_long ("0");
48   else
49     seh_emit_long (hnd);
50   if (!hnd_data || hnd_data[0] == '@')
51     seh_emit_long ("0");
52   else
53     seh_emit_long (hnd_data);
54 }
55
56 /* Generate initial pdata for x64 and mips.  */
57 static void
58 make_function_entry_pdata (seh_context *c)
59 {
60   segT sec = NULL;
61   segT current_seg = now_seg;
62   subsegT current_subseg = now_subseg;
63
64   sec = seh_make_section ();
65   switch (seh_get_target_kind ())
66     {
67     case seh_kind_x64:
68       subseg_set (sec, 0);
69       seh_emit_rva (c->func_name);
70       seh_emit_rva (c->end_symbol);
71       seh_emit_rva (c->xdata_first);
72       break;
73     case seh_kind_mips:
74       subseg_set (sec, 0);
75       seh_emit_long (c->func_name);
76       seh_emit_long (c->end_symbol);
77       if (c->handler_name == NULL)
78         seh_emit_long ("0");
79       else if (c->handler_name[0] == '@')
80         {
81           if (strcasecmp (c->handler_name, "@1") == 0)
82             seh_emit_long ("1");
83           else
84             seh_emit_long ("0");
85         }
86       else
87         seh_emit_long (c->handler_name);
88       if (c->handler_data_name == NULL || c->handler_data_name[0] == '@')
89         seh_emit_long ("0");
90       else
91         seh_emit_long (c->handler_data_name);
92       seh_emit_long (c->endprologue_symbol ? c->endprologue_symbol : c->func_name);
93       break;
94     default:
95       break;
96     }
97   subseg_set (current_seg, current_subseg);
98 }
99
100 static void
101 seh_x64_write_xdata (void)
102 {
103   seh_context *h;
104   size_t xdata_size = 0, count_syms = 0;
105   size_t xdata_offs = 0;
106   unsigned char *data;
107   segT seg_xdata;
108   bfd *abfd = stdoutput;
109
110   h = seh_ctx_root;
111   if (h->done)
112     return;
113   while (h != NULL)
114     {
115       h->xdata_offset = xdata_size;
116       xdata_size += seh_getsize_unwind_data (h);
117       count_syms += h->count_syms;
118       h = h->next;
119     }
120
121   if (xdata_size == 0)
122     return;
123
124   seh_symbol_init (abfd, count_syms);
125   data = xmalloc (xdata_size);
126   seg_xdata = quick_section (abfd, ".xdata", SEC_HAS_CONTENTS, 3);
127   seg_xdata->contents = data;
128   memset (data, 0, xdata_size);
129   bfd_set_section_size (abfd, seg_xdata, xdata_size);
130   h = seh_ctx_root;
131   while (h != NULL)
132     {
133       xdata_offs = h->xdata_offset;
134       h->section = seg_xdata;
135       h->abfd = abfd;
136       if (h->done == 0)
137         {
138           h->done = 1;
139           seh_create_unwind_data (h, data, xdata_offs);
140           h->done = 1;
141         }
142       h = h->next;
143     }
144   save_relocs (seg_xdata);
145   bfd_set_symtab (abfd, symtab, symptr);
146   bfd_set_section_contents (abfd, seg_xdata, data, 0, xdata_size);
147 }
148
149 static void
150 seh_arm_create_pdata (seh_context *c, unsigned char *data, size_t pdata_offs)
151 {
152   int idx;
153   unsigned int val;
154   valueT func_len = 0;
155   valueT prolog_len = 0;
156   valueT start_len = 0;
157   func_len = resolve_symbol_value (c->end_addr);
158   start_len = resolve_symbol_value (c->start_addr);
159   if (c->endprologue_addr)
160     prolog_len = resolve_symbol_value (c->endprologue_addr);
161   else
162     prolog_len = start_len;
163   func_len -= start_len;
164   prolog_len -= start_len;
165   if (!c || !data)
166     return;
167   /* $$$$ */
168   idx = seh_symbol (c->abfd, c->start_symbol, "", "", UNDSEC, BSF_GLOBAL, 0);
169   seh_reloc (c->abfd, pdata_offs, BFD_RELOC_32, idx);
170   val = (unsigned int) func_len;
171   val <<= 8;
172   val |= ((unsigned int) prolog_len & 0xffU);
173   if (c->use_instruction_32)
174     val |= 0x40000000U;
175   if (c->handler_written)
176     val |= 0x80000000U;
177   bfd_put_32 (c->abfd, (bfd_vma) val, data + pdata_offs + 4);
178 }
179
180 static void
181 seh_arm_write_pdata (void)
182 {
183   seh_context *h;
184   size_t pdata_size = 0, count_syms = 0;
185   size_t pdata_offs = 0;
186   unsigned char *data;
187   segT seg_pdata;
188   bfd *abfd = stdoutput;
189
190   h = seh_ctx_root;
191   if (h->done)
192     return;
193   while (h != NULL)
194     {
195       h->xdata_offset = pdata_size;
196       pdata_size += 8;
197       count_syms += 1;
198       h = h->next;
199     }
200
201   if (pdata_size == 0)
202     return;
203
204   seh_symbol_init (abfd, count_syms);
205   data = xmalloc (pdata_size);
206   seg_pdata = quick_section (abfd, ".pdata", SEC_HAS_CONTENTS, 3);
207   seg_pdata->contents = data;
208   memset (data, 0, pdata_size);
209   bfd_set_section_size (abfd, seg_pdata, pdata_size);
210   h = seh_ctx_root;
211   while (h != NULL)
212     {
213       pdata_offs = h->xdata_offset;
214       h->section = seg_pdata;
215       h->abfd = abfd;
216       if (h->done != 0)
217         {
218           seh_arm_create_pdata (h, data, pdata_offs);
219           h->done = 1;
220         }
221       h = h->next;
222     }
223   save_relocs (seg_pdata);
224   bfd_set_symtab (abfd, symtab, symptr);
225   bfd_set_section_contents (abfd, seg_pdata, data, 0, pdata_size);
226 }
227
228 void
229 obj_coff_seh_do_final (void)
230 {
231   switch (seh_get_target_kind ())
232     {
233     case seh_kind_mips:
234     default:
235       break;
236     case seh_kind_arm:
237       seh_arm_write_pdata ();
238       break;
239     case seh_kind_x64:
240       seh_x64_write_xdata ();
241       break;
242     }
243 }
244
245 static void
246 seh_x64_make_prologue_element (int kind, int reg, bfd_vma off)
247 {
248   seh_prologue_element *n;
249   if (seh_ctx_cur == NULL)
250     return;
251   if (seh_ctx_cur->elems_count == seh_ctx_cur->elems_max)
252     {
253       seh_ctx_cur->elems = (seh_prologue_element *)
254         xrealloc (seh_ctx_cur->elems,
255         ((seh_ctx_cur->elems_max + 8) * sizeof (seh_prologue_element)));
256       seh_ctx_cur->elems_max += 8;
257     }
258   n = &seh_ctx_cur->elems[seh_ctx_cur->elems_count];
259   memset (n, 0, sizeof (seh_prologue_element));
260   n->kind = kind;
261   n->reg = reg;
262   n->offset = off;
263   n->pc_symbol = make_seh_text_label (seh_ctx_cur, &(n->pc_addr));
264   seh_ctx_cur->elems_count += 1;
265 }
266
267 static int
268 seh_x64_read_reg (const char *tok, int kind, int *regno)
269 {
270   static const char *frame_regs[16] =
271   { "cfa", "rcx", "rdx", "rbx", "rsp", "rbp","rsi","rdi",
272   "r8","r9","r10","r11","r12","r13","r14","r15" };
273   static const char *int_regs[16] =
274   { "rax", "rcx", "rdx", "rbx", "rsp", "rbp","rsi","rdi",
275   "r8","r9","r10","r11","r12","r13","r14","r15" };
276   static const char *xmm_regs[16] =
277   { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
278   "xmm8", "xmm9", "xmm10","xmm11","xmm12","xmm13","xmm14","xmm15" };
279   static const char *mm_regs[16] =
280   { "xmm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
281   "xmm8", "mm9", "mm10","mm11","mm12","mm13","mm14","mm15" };
282   const char **p = NULL;
283   char name_end;
284   char *symbol_name = NULL;
285   int i;
286
287   while (*input_line_pointer == ' ' || *input_line_pointer == '\t')
288     input_line_pointer++;
289   while (*input_line_pointer == ' ' || *input_line_pointer == '\t')
290     input_line_pointer++;
291   switch (kind)
292   {
293   case 0:
294     p = frame_regs;
295     break;
296   case 1:
297     p = int_regs;
298     break;
299   case 2:
300     p = mm_regs;
301     break;
302   case 3:
303     p = xmm_regs;
304     break;
305   default:
306     abort ();
307   }
308   if (*input_line_pointer == 0 || *input_line_pointer == '\n')
309   {
310     return 0;
311   }
312   if (*input_line_pointer == '%')
313     ++input_line_pointer;
314   symbol_name = input_line_pointer;
315   name_end = get_symbol_end ();
316   for (i = 0; i < 16; i++)
317   {
318     if (! strcasecmp (p[i], symbol_name))
319       break;
320   }
321   if (i == 16)
322   {
323     as_warn (_("In %s we found the invalid register name %s.\n"),
324       tok, symbol_name);
325   }
326   *input_line_pointer = name_end;
327   while (*input_line_pointer == ' ' || *input_line_pointer == '\t')
328     input_line_pointer++;
329   if (*input_line_pointer == ',')
330     ++input_line_pointer;
331   while (*input_line_pointer == ' ' || *input_line_pointer == '\t')
332     input_line_pointer++;
333   *regno = i;
334   return i != 16;
335 }
336
337 static int
338 seh_read_offset (const char *tok, bfd_vma *off)
339 {
340   bfd_vma r, v = 0, base = 10;
341   int had_one = 0;
342
343   while (*input_line_pointer == ' ' || *input_line_pointer == '\t')
344     input_line_pointer++;
345   if (*input_line_pointer == '0')
346   {
347     ++input_line_pointer;
348     had_one = 1;
349     base = 8;
350     switch ((*input_line_pointer))
351     {
352     case 'x':
353     case 'X':
354       base = 16;
355       ++input_line_pointer;
356       break;
357     case 'd':
358     case 'D':
359       base = 10;
360       input_line_pointer++;
361       break;
362     case 'o':
363     case 'O':
364       base = 8;
365       input_line_pointer++;
366       break;
367     }
368   }
369   while (*input_line_pointer != 0)
370   {
371     if (input_line_pointer[0] >= '0' && input_line_pointer[0] <='9')
372       r = (bfd_vma) (input_line_pointer[0] - '0');
373     else if (base == 16 && input_line_pointer[0] >= 'a' && input_line_pointer[0] <='f')
374       r = (bfd_vma) ((input_line_pointer[0] - 'a') + 10);
375     else if (base == 16 && input_line_pointer[0] >= 'A' && input_line_pointer[0] <='F')
376       r = (bfd_vma) ((input_line_pointer[0] - 'A') + 10);
377     else
378       break;
379     input_line_pointer++;
380     v *= base;
381     v += r;
382     had_one = 1;
383   }
384   *off = v;
385   if (had_one == 0)
386   {
387     as_warn (_("In %s we expect a number.\n"),
388       tok);
389   }
390   while (*input_line_pointer == ' ' || *input_line_pointer == '\t')
391     input_line_pointer++;
392   if (*input_line_pointer == ',')
393     ++input_line_pointer;
394   while (*input_line_pointer == ' ' || *input_line_pointer == '\t')
395     input_line_pointer++;
396   return had_one != 0;
397 }
398
399 static void
400 obj_coff_seh_32 (int what)
401 {
402   if (seh_ctx_cur == NULL)
403     {
404       as_fatal (_(".seh_eh requires to be in .seh_proc/.seh_endproc block.\n"));
405       demand_empty_rest_of_line ();
406       return;
407     }
408   seh_ctx_cur->use_instruction_32 = (what ? 1 : 0);
409   if (seh_get_target_kind () == seh_kind_arm)
410     as_warn (_(".seh_%s32 is ignored for this target."), (what ? "" : "no"));
411   demand_empty_rest_of_line ();
412 }
413
414 static void
415 obj_coff_seh_eh (int what ATTRIBUTE_UNUSED)
416 {
417   if (seh_ctx_cur == NULL)
418     {
419       as_fatal (_(".seh_eh requires to be in .seh_proc/.seh_endproc block.\n"));
420       demand_empty_rest_of_line ();
421       return;
422     }
423   if (seh_get_target_kind () == seh_kind_arm)
424     {
425       seh_ctx_cur->handler_written = 1;
426       /* write block to .text if exception handler is set.  */
427       seh_write_text_eh_data (seh_ctx_cur->handler_name, seh_ctx_cur->handler_data_name);
428     }
429   demand_empty_rest_of_line ();
430 }
431
432 static void
433 obj_coff_seh_handler (int what ATTRIBUTE_UNUSED)
434 {
435   char *symbol_name;
436   char name_end;
437
438   if (seh_ctx_cur == NULL)
439     {
440       as_fatal (_(".seh_handler requires to be in .seh_proc/.seh_endproc block.\n"));
441       demand_empty_rest_of_line ();
442       return;
443     }
444   if (*input_line_pointer == 0 || *input_line_pointer == '\n')
445     {
446       as_fatal (_(".seh_handler requires a handler lable name.\n"));
447       demand_empty_rest_of_line ();
448       return;
449     }
450
451   while (*input_line_pointer == ' ' || *input_line_pointer == '\t' || *input_line_pointer == ',')
452     input_line_pointer++;
453   symbol_name = input_line_pointer;
454   name_end = get_symbol_end ();
455   seh_ctx->handler_name = xstrdup (symbol_name);
456   if (symbol_name[0] == '@')
457     {
458       if (strcasecmp (symbol_name, "@0") != 0 && strcasecmp (symbol_name, "@1") != 0
459           && strcasecmp (symbol_name, "@null") != 0)
460         as_warn (_("Unknown constant value ,%s' for handler."), symbol_name);
461     }
462   *input_line_pointer = name_end;
463   seh_ctx->handler_data_name = NULL;
464   while (*input_line_pointer == ' ' || *input_line_pointer == '\t' || *input_line_pointer == ',')
465     input_line_pointer++;
466   symbol_name = input_line_pointer;
467   if (*input_line_pointer != '\n' && *input_line_pointer != 0)
468     {
469       name_end = get_symbol_end ();
470       seh_ctx->handler_data_name = xstrdup (symbol_name);
471       if (symbol_name[0] == '@')
472         {
473           if (seh_get_target_kind () != seh_kind_x64)
474             as_fatal (_("For this target .seh_handler doesn't support constant user-data."));
475           else if (strcasecmp (symbol_name, "@unwind") != 0 &&
476                    strcasecmp (symbol_name, "@except") != 0)
477             as_warn (_("For .seh_handler the constant ,%s' is ignored."), symbol_name);
478         }
479       *input_line_pointer = name_end;
480     }
481   if (seh_ctx_cur->handler_written)
482     as_warn (_(".seh_handler is ignored as .seh_eh was seen before."));
483   demand_empty_rest_of_line ();
484 }
485
486 static void
487 obj_coff_seh_scope (int what ATTRIBUTE_UNUSED)
488 {
489   char *symbol_name,*beg = NULL,*end = NULL, *handl = NULL, *jmp = NULL;
490   char name_end;
491
492   if (seh_ctx_cur == NULL)
493     {
494       as_fatal (_(".seh_scope requires to be in .seh_proc/.seh_endproc block.\n"));
495       demand_empty_rest_of_line ();
496       return;
497     }
498
499   while (*input_line_pointer == ' ' || *input_line_pointer == '\t' || *input_line_pointer == ',')
500     input_line_pointer++;
501   if (*input_line_pointer == 0 || *input_line_pointer == '\n')
502     {
503       as_fatal (_(".seh_scope requires four symbol names.\n"));
504       demand_empty_rest_of_line ();
505       return;
506     }
507   symbol_name = input_line_pointer;
508   name_end = get_symbol_end ();
509   beg = xstrdup (symbol_name);
510   *input_line_pointer = name_end;
511   while (*input_line_pointer == ' ' || *input_line_pointer == '\t' || *input_line_pointer == ',')
512     input_line_pointer++;
513   if (*input_line_pointer == 0 || *input_line_pointer == '\n')
514     {
515       as_fatal (_(".seh_scope requires three more symbol names.\n"));
516       demand_empty_rest_of_line ();
517       return;
518     }
519   symbol_name = input_line_pointer;
520   name_end = get_symbol_end ();
521   end = xstrdup (symbol_name);
522   *input_line_pointer = name_end;
523   while (*input_line_pointer == ' ' || *input_line_pointer == '\t' || *input_line_pointer == ',')
524     input_line_pointer++;
525   if (*input_line_pointer == 0 || *input_line_pointer == '\n')
526     {
527       as_fatal (_(".seh_scope requires two more symbol names.\n"));
528       demand_empty_rest_of_line ();
529       return;
530     }
531   symbol_name = input_line_pointer;
532   name_end = get_symbol_end ();
533   handl = xstrdup (symbol_name);
534   *input_line_pointer = name_end;
535   if (*handl == '@')
536     {
537       if (strcasecmp (handl, "@0") != 0 && strcasecmp (handl, "@1") != 0
538           && strcasecmp (handl, "@null") != 0)
539         as_warn (_("Unknown constant for handler ,%s'."), handl);
540     }
541
542   while (*input_line_pointer == ' ' || *input_line_pointer == '\t' || *input_line_pointer == ',')
543     input_line_pointer++;
544   if (*input_line_pointer == 0 || *input_line_pointer == '\n')
545     {
546       as_fatal (_(".seh_scope requires one more symbol names.\n"));
547       demand_empty_rest_of_line ();
548       return;
549     }
550   symbol_name = input_line_pointer;
551   name_end = get_symbol_end ();
552   jmp = xstrdup (symbol_name);
553   *input_line_pointer = name_end;
554   if (*jmp == '@')
555     {
556       if (strcasecmp (jmp, "@0") != 0 && strcasecmp (handl, "@null") != 0)
557         as_warn (_("Unknown constant for jump ,%s'."), jmp);
558     }
559
560   if (seh_get_target_kind () != seh_kind_x64)
561     as_warn (_(".seh_scope is ignored for this target."));
562   else
563     seh_x64_makescope_elem (seh_ctx_cur, beg, end, handl, jmp);
564   if (beg)
565     free (beg);
566   if (end)
567     free (end);
568   if (handl)
569     free (handl);
570   if (jmp)
571     free (jmp);
572   demand_empty_rest_of_line ();
573 }
574
575 static void
576 obj_coff_seh_proc (int what ATTRIBUTE_UNUSED)
577 {
578   char *symbol_name;
579   char name_end;
580
581   if (seh_ctx_cur != NULL)
582   {
583     as_warn (_(".seh_proc has to be closed by .seh_endprog\n"));
584     obj_coff_seh_endproc (0);
585   }
586
587   if (*input_line_pointer == 0 || *input_line_pointer == '\n')
588     {
589       as_fatal (_(".seh_proc requires function lable name.\n"));
590       demand_empty_rest_of_line ();
591       return;
592     }
593
594   while (*input_line_pointer == ' ' || *input_line_pointer == '\t' || *input_line_pointer == ',')
595     input_line_pointer++;
596   symbol_name = input_line_pointer;
597   name_end = get_symbol_end ();
598
599   if (seh_ctx == NULL)
600     seh_ctx_root = seh_ctx = (seh_context *) xmalloc (sizeof (seh_context));
601   else
602     {
603       seh_ctx->next = (seh_context *) xmalloc (sizeof (seh_context));
604       seh_ctx = seh_ctx->next;
605     }
606   seh_ctx_cur = seh_ctx;
607   memset (seh_ctx, 0, sizeof (seh_context));
608
609   seh_ctx->func_name = xstrdup (symbol_name);
610   *input_line_pointer = name_end;
611   while (*input_line_pointer == ' ' || *input_line_pointer == '\t' || *input_line_pointer == ',')
612     input_line_pointer++;
613   seh_ctx->start_symbol = make_seh_text_label (seh_ctx_cur, &(seh_ctx_cur->start_addr));
614   demand_empty_rest_of_line ();
615 }
616
617 static void
618 obj_coff_seh_endproc  (int what ATTRIBUTE_UNUSED)
619 {
620   if (seh_ctx_cur == NULL)
621   {
622     as_warn (_(".seh_endprog without prior .seh_proc (ignored)\n"));
623     demand_empty_rest_of_line ();
624     return;
625   }
626   seh_ctx->end_symbol = make_seh_text_label (seh_ctx, &(seh_ctx->end_addr));
627   seh_ctx->xdata_first = seh_make_xlbl_name (seh_ctx);
628   make_function_entry_pdata (seh_ctx);
629   seh_ctx_cur = NULL;
630   demand_empty_rest_of_line ();
631 }
632
633 static void
634 obj_coff_seh_push  (int what)
635 {
636   int reg = 0;
637   int kind = -1;
638   if (seh_ctx_cur == NULL)
639   {
640     as_warn (_(".seh_push used outside of .seh_proc block.\n"));
641     demand_empty_rest_of_line ();
642     return;
643   }
644   /* what 0:reg, 1:pushframe.  */
645   switch (what)
646   {
647   case 0:
648     if (seh_x64_read_reg (".seh_push", 1, &reg))
649       kind = UWOP_PUSH_NONVOL;
650     else
651       as_warn (_(".seh_pushreg expects register argument."));
652     break;
653   case 1:
654     kind = UWOP_PUSH_MACHFRAME;
655     break;
656   default:
657     abort ();
658   }
659   if (seh_get_target_kind () != seh_kind_x64)
660     as_warn (_(".seh_save... is ignored for this target.\n"));
661   else if (kind != -1)
662     seh_x64_make_prologue_element (kind, reg, 0);
663   demand_empty_rest_of_line ();
664 }
665
666 static void
667 obj_coff_seh_save  (int what)
668 {
669   int reg;
670   bfd_vma off;
671   int kind;
672   int ok = 1;
673
674   /* what 0:reg, 1:mm, 2:xmm.  */
675   switch (what)
676   {
677   case 0:
678     ok &= seh_x64_read_reg (".seh_savereg", 1, &reg);
679     kind = UWOP_SAVE_NONVOL;
680     break;
681   case 1:
682     ok &= seh_x64_read_reg (".seh_savemm", 2, &reg);
683     kind = UWOP_SAVE_XMM;
684     break;
685   case 2:
686     ok &= seh_x64_read_reg (".seh_savexmm", 3, &reg);
687     kind = UWOP_SAVE_XMM128;
688     break;
689   default:
690     abort ();
691   }
692   ok &= seh_read_offset (".seh_save", &off);
693   if (seh_ctx_cur == NULL)
694   {
695     as_warn (_(".seh_save used outside of .seh_proc block.\n"));
696     demand_empty_rest_of_line ();
697     return;
698   }
699   if (seh_get_target_kind () != seh_kind_x64)
700     as_warn (_(".seh_save... is ignored for this target.\n"));
701   else
702     seh_x64_make_prologue_element (kind, reg, off);
703   demand_empty_rest_of_line ();
704 }
705
706 static void
707 obj_coff_seh_endprologue (int what ATTRIBUTE_UNUSED)
708 {
709   if (seh_ctx_cur == NULL)
710   {
711     as_warn (_(".seh_endprologue used outside of .seh_proc block.\n"));
712     demand_empty_rest_of_line ();
713     return;
714   }
715   if (seh_ctx_cur->endprologue_symbol != NULL)
716     as_warn (_(".seh_endprologue used more then once in .seh_proc block.\n"));
717   else
718     seh_ctx_cur->endprologue_symbol = make_seh_text_label (seh_ctx_cur, &seh_ctx_cur->endprologue_addr);
719 }
720
721 static void
722 obj_coff_seh_stack_alloc (int what ATTRIBUTE_UNUSED)
723 {
724   bfd_vma size;
725   if (seh_ctx_cur == NULL)
726   {
727     as_warn (_(".seh_stackalloc used outside of .seh_proc block.\n"));
728     demand_empty_rest_of_line ();
729     return;
730   }
731   if (seh_read_offset (".seh_stackalloc", &size))
732     {
733       if (seh_get_target_kind () != seh_kind_x64)
734         as_warn (_(".seh_stackalloc is ignored for this target.\n"));
735       else
736         seh_x64_make_prologue_element (UWOP_ALLOC_LARGE, 0, size);
737     }
738 }
739
740 static void
741 obj_coff_seh_setframe (int what ATTRIBUTE_UNUSED)
742 {
743   int reg;
744   int ok = 1;
745   bfd_vma off;
746
747   ok &= seh_x64_read_reg (".seh_setframe", 0, &reg);
748   ok &= seh_read_offset (".seh_setframe", &off);
749   if (seh_ctx_cur == NULL)
750   {
751     as_warn (_(".seh_setframe used outside of .seh_proc block.\n"));
752     demand_empty_rest_of_line ();
753     return;
754   }
755   if (ok)
756   {
757     seh_ctx_cur->framereg = reg;
758     seh_ctx_cur->frameoff = off;
759   }
760   if (seh_get_target_kind () != seh_kind_x64)
761     as_warn (_(".seh_setframe is ignored for this target.\n"));
762   demand_empty_rest_of_line ();
763 }
764
765 /* Misc function helpers.  */
766 static void
767 seh_reloc (bfd *abfd, bfd_size_type address, int which_howto, int symidx)
768 {
769   if (relcount >= relsize - 1)
770     {
771       relsize += 10;
772       if (reltab)
773         reltab = xrealloc (reltab, relsize * sizeof (arelent));
774       else
775         reltab = xmalloc (relsize * sizeof (arelent));
776     }
777   reltab[relcount].address = address;
778   reltab[relcount].addend = 0;
779   reltab[relcount].howto = bfd_reloc_type_lookup (abfd, which_howto);
780   reltab[relcount].sym_ptr_ptr = symtab + symidx;
781   relcount++;
782 }
783
784 static void
785 save_relocs (asection *sec)
786 {
787   int i;
788
789   sec->relocation = reltab;
790   sec->reloc_count = relcount;
791   sec->orelocation = xmalloc ((relcount + 1) * sizeof (arelent *));
792   for (i = 0; i < relcount; i++)
793     sec->orelocation[i] = sec->relocation + i;
794   sec->orelocation[relcount] = 0;
795   sec->flags |= SEC_RELOC;
796   reltab = 0;
797   relcount = relsize = 0;
798 }
799
800 static void
801 seh_symbol_init (bfd *abfd, unsigned int added)
802 {
803   unsigned int oldcount;
804   oldcount = bfd_get_symcount (abfd);
805   symptr = oldcount;
806   symtab = xmalloc ((oldcount + added + 6) * sizeof (asymbol *));
807   if (oldcount > 0)
808     memcpy (symtab, bfd_get_outsymbols (abfd), sizeof (asymbol *) * oldcount);
809 }
810
811 static int
812 seh_symbol (bfd *abfd, const char *n1, const char *n2, const char *n3,
813             asection *sec, int flags, int addr)
814 {
815   asymbol *sym;
816   char *name = xmalloc (strlen (n1) + strlen (n2) + strlen (n3) + 1);
817   int ret = symptr;
818   strcpy (name, n1);
819   strcat (name, n2);
820   strcat (name, n3);
821   sym = bfd_make_empty_symbol (abfd);
822   sym->name = name;
823   sym->section = sec;
824   sym->flags = flags;
825   sym->value = addr;
826   symtab[symptr++] = sym;
827   return ret;
828 }
829
830 static asection *
831 quick_section (bfd *abfd, const char *name, int flags, int align)
832 {
833   asection *sec;
834   asymbol *sym;
835   sec = seh_make_section2 (name, flags);
836   bfd_set_section_alignment (abfd, sec, align);
837   /* Remember to undo this before trying to link internally!  */
838
839   sym = bfd_make_empty_symbol (abfd);
840   symtab[symptr++] = sym;
841   sym->name = sec->name;
842   sym->section = sec;
843   sym->flags = BSF_LOCAL;
844   sym->value = 0;
845
846   return sec;
847 }
848
849 static seh_kind
850 seh_get_target_kind (void)
851 {
852   if (!stdoutput)
853     return seh_kind_unknown;
854   switch (bfd_get_arch (stdoutput))
855     {
856     case bfd_arch_arm:
857     case bfd_arch_powerpc:
858     case bfd_arch_sh:
859       return seh_kind_arm;
860     case bfd_arch_i386:
861       switch (bfd_get_mach (stdoutput))
862         {
863         case bfd_mach_x86_64:
864         case bfd_mach_x86_64_intel_syntax:
865           return seh_kind_x64;
866         default:
867           break;
868         }
869       /* FALL THROUGH.  */
870     case bfd_arch_mips:
871       return seh_kind_mips;
872     case bfd_arch_ia64:
873       /* Should return seh_kind_x64.  But not implemented yet.  */
874       return seh_kind_unknown;
875     default:
876       break;
877     }
878   return seh_kind_unknown;
879 }
880
881 static void
882 seh_emit_rva (const char *name)
883 {
884   char *p = (char *) xmalloc (strlen (name) + 1);
885   char *s = input_line_pointer;
886
887   strcpy (p, name);
888   input_line_pointer = p;
889   s_rva (4);
890   input_line_pointer = s;
891 }
892
893 static void
894 seh_emit_long (const char *name)
895 {
896   char *p = (char *) xmalloc (strlen (name) + 1);
897   char *s = input_line_pointer;
898
899   strcpy (p, name);
900   input_line_pointer = p;
901   cons (4);
902   input_line_pointer = s;
903 }
904
905 static void
906 seh_make_globl (char *sym_name)
907 {
908   char *s = input_line_pointer;
909
910   input_line_pointer = sym_name;
911   s_globl (4);
912   input_line_pointer = s;
913 }
914
915 static segT
916 seh_make_section2 (const char *section_name, unsigned flags)
917 {
918   char *name;
919   segT sec;
920
921   name = xmalloc (strlen (section_name) + 1);
922   strcpy (name, section_name);
923
924   sec = subseg_new (name, (subsegT) 0);
925   bfd_set_section_flags (stdoutput, sec,
926     ((SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA | flags)
927     & bfd_applicable_section_flags (stdoutput)));
928
929   return sec;
930 }
931
932 static segT
933 seh_make_section (void)
934 {
935   static segT seg_pdata = NULL;
936   segT sec = NULL;
937
938   if (!seg_pdata)
939     seg_pdata = seh_make_section2 (".pdata", 0);
940   sec = seg_pdata;
941   return sec;
942 }
943
944 static char *
945 seh_make_xlbl_name (seh_context *c)
946 {
947   size_t len = strlen (".seh_xlbl_") + strlen (c->func_name) + 9 + 1;
948   char *ret = (char*) xmalloc (len);
949   if (!ret)
950     as_fatal (_("Out of memory for xdata lable for %s"), c->func_name);
951   else
952     sprintf (ret, ".seh_xlbl_%s_%x", c->func_name, + c->xlbl_count);
953   c->xlbl_count += 1;
954   return ret;
955 }
956
957 static char *
958 make_seh_text_label (seh_context *c, symbolS **addr)
959 {
960   char *sym_name;
961   size_t len = strlen (".seh_tlbl_") + strlen (c->func_name) + 9 + 1;
962   sym_name = (char *) xmalloc (len);
963   if (!sym_name)
964     as_fatal (_("Allocating memory for SEH's text symbol for %s failed"), c->func_name);
965   sprintf (sym_name, ".seh_tlbl_%s_%x", c->func_name, c->tlbl_count);
966   c->tlbl_count += 1;
967   if (addr)
968   {
969     seh_make_globl (sym_name);
970     *addr = colon (sym_name);
971   }
972   return sym_name;
973 }
974
975 /* x64 secific functions.  */
976
977 static void
978 seh_fill_pcsyms (const seh_context *c, char **names, int *idx)
979 {
980   size_t i;
981   int count = 1;
982   valueT start_off = resolve_symbol_value (c->start_addr);
983   valueT un_off;
984   seh_prologue_element *e = c->elems;
985   names[0] = c->start_symbol;
986   idx[0] = 0;
987   if (c->elems_count == 0)
988     return;
989   for (i = 0; i < c->elems_count; i++)
990   {
991     un_off = resolve_symbol_value (e[i].pc_addr);
992     if ((un_off - start_off) > 255)
993     {
994       names[count] = e[i].pc_symbol;
995       idx[count] = (int) i;
996       count++;
997       start_off = un_off;
998     }
999   }
1000 }
1001
1002 static int
1003 seh_needed_unwind_info (seh_context *c)
1004 {
1005   size_t i;
1006   int count = 1;
1007   valueT start_off = resolve_symbol_value (c->start_addr);
1008   valueT un_off;
1009   seh_prologue_element *e = c->elems;
1010   if (c->elems_count == 0)
1011     return count;
1012   for (i = 0; i < c->elems_count; i++)
1013   {
1014     un_off = resolve_symbol_value (e[i].pc_addr);
1015     if ((un_off - start_off) > 255)
1016     {
1017       count++;
1018       start_off = un_off;
1019     }
1020   }
1021   return count;
1022 }
1023
1024 static size_t
1025 seh_getelm_data_size (const seh_context *c, int elm_start, int elm_end)
1026 {
1027   size_t ret = PEX64_UWI_SIZEOF_UWCODE_ARRAY (elm_end - elm_start);
1028   while (elm_start < elm_end)
1029     {
1030       switch (c->elems[elm_start].kind)
1031         {
1032         case UWOP_PUSH_NONVOL:
1033         case UWOP_PUSH_MACHFRAME:
1034           ret += 2;
1035           break;
1036         case UWOP_SAVE_NONVOL:
1037         case UWOP_SAVE_XMM:
1038         case UWOP_SAVE_XMM128:
1039           if ((c->elems[elm_start].offset & 7) != 0 ||
1040             ((c->elems[elm_start].offset / 8) > 0xffff))
1041             ret += 6;
1042           else
1043             ret += 4;
1044           break;
1045         case UWOP_ALLOC_LARGE:
1046           ret += 4;
1047           break;
1048         default:
1049           break;
1050         }
1051       elm_start++;
1052     }
1053   return ret;
1054 }
1055
1056 static size_t
1057 seh_getsize_of_unwind_entry (seh_context *c, int elm_start, int elm_end, int bechain)
1058 {
1059   size_t ret = seh_getelm_data_size(c, elm_start, elm_end);
1060   c->count_syms += 1;
1061   if (bechain)
1062     {
1063       ret += 4 + 4;
1064       c->count_syms += 1;
1065       c->count_reloc += 1;
1066     }
1067   else
1068     {
1069       ret += 4;
1070       if (c->handler_name != NULL)
1071         {
1072           if (c->handler_data_name != NULL
1073               && c->handler_data_name[0] != '@')
1074             {
1075               ret += 4;
1076               c->count_syms += 2;
1077               c->count_reloc += 2;
1078             }
1079           else
1080             {
1081               ret += 8 + (c->scope_count * 4) * 4;
1082               c->count_syms += (c->scope_count * 4) + 1;
1083               c->count_reloc += (c->scope_count * 4) + 1;
1084             }
1085         }
1086     }
1087   return ret;
1088 }
1089
1090 static void
1091 seh_make_unwind_entry (const seh_context *c, char *name, int elm_start, int elm_end, int bechain,
1092                        unsigned char *data, size_t *poffs, int no)
1093 {
1094   size_t off = *poffs;
1095   size_t it;
1096   valueT start_off = resolve_symbol_value (c->start_addr);
1097   valueT end_prologue;
1098   size_t uwcodes = seh_getelm_data_size(c, elm_start, elm_end);
1099   unsigned int flag = UNW_FLAG_NHANDLER;
1100   int idx;
1101
1102   if (c->handler_name != NULL)
1103     {
1104       flag = UNW_FLAG_EHANDLER;
1105       if (c->handler_data_name != NULL && c->handler_data_name[0] != '@')
1106         flag = UNW_FLAG_FHANDLER;
1107       else if (c->handler_data_name != NULL &&
1108                strcasecmp (c->handler_data_name, "@unwind") == 0)
1109         flag = UNW_FLAG_UHANDLER;
1110     }
1111   if (!c->endprologue_addr)
1112     end_prologue = start_off;
1113   else
1114     end_prologue = resolve_symbol_value (c->endprologue_addr);
1115   seh_symbol (c->abfd, name, "", "", c->section, BSF_GLOBAL, (int) off);
1116   data[off++] = (1 | ((bechain ? UNW_FLAG_CHAININFO : flag) << 3));
1117   if (elm_start != 0)
1118     start_off = (valueT) c->elems[elm_start].offset;
1119   end_prologue -= start_off;
1120   if (end_prologue > 255)
1121     end_prologue = 255;
1122   data[off++] = (unsigned char) end_prologue;
1123   data[off++] = (unsigned char) (uwcodes / 2);
1124   data[off] = (unsigned char) c->framereg;
1125   data[off++] |= (unsigned char) ((c->frameoff / 16) << 4);
1126   off += uwcodes;
1127   if (bechain)
1128     {
1129       char n[100];
1130       sprintf (n,"%x", no);
1131       idx = seh_symbol (c->abfd, ".xdata_fct", c->func_name, n, UNDSEC, BSF_GLOBAL, (int) off);
1132       seh_reloc (c->abfd, off, BFD_RELOC_RVA, idx);
1133       off += 4;
1134     }
1135   else if (c->handler_name != NULL)
1136     {
1137       if (flag == UNW_FLAG_FHANDLER)
1138         {
1139           if (strcasecmp (c->handler_name, "@1") == 0)
1140             bfd_put_32 (c->abfd, (bfd_vma) 1, &data[off]);
1141           else if (c->handler_name[0] != '@')
1142             {
1143               idx = seh_symbol (c->abfd, c->handler_name, "", "", UNDSEC, BSF_GLOBAL, 0);
1144               seh_reloc (c->abfd, off, BFD_RELOC_RVA, idx);
1145             }
1146           off += 4;
1147           idx = seh_symbol (c->abfd, c->handler_data_name, "", "", UNDSEC, BSF_GLOBAL, 0);
1148           seh_reloc (c->abfd, off, BFD_RELOC_RVA, idx);
1149           off += 4;
1150         }
1151       else if (flag == UNW_FLAG_UHANDLER || flag == UNW_FLAG_EHANDLER)
1152         {
1153           if (strcasecmp (c->handler_name, "@1") == 0)
1154             bfd_put_32 (c->abfd, (bfd_vma) 1, &data[off]);
1155           else if (c->handler_name[0] != '@')
1156             {
1157               idx = seh_symbol (c->abfd, c->handler_name, "", "", UNDSEC, BSF_GLOBAL, 0);
1158               seh_reloc (c->abfd, off, BFD_RELOC_RVA, idx);
1159             }
1160           off += 4;
1161           bfd_put_32 (c->abfd, (bfd_vma) c->scope_count, &data[off]);
1162           off += 4;
1163           for (it = 0; it < c->scope_count; it++)
1164             {
1165               idx = seh_symbol (c->abfd, c->scopes[it].begin_addr, "", "", UNDSEC, BSF_GLOBAL, 0);
1166               seh_reloc (c->abfd, off, BFD_RELOC_RVA, idx);
1167               off += 4;
1168               idx = seh_symbol (c->abfd, c->scopes[it].end_addr, "", "", UNDSEC, BSF_GLOBAL, 0);
1169               seh_reloc (c->abfd, off, BFD_RELOC_RVA, idx);
1170               off += 4;
1171               if (c->scopes[it].handler_addr[0] == '@')
1172                 {
1173                   if (strcasecmp (c->scopes[it].handler_addr, "@1") == 0)
1174                     bfd_put_32 (c->abfd, (bfd_vma) 1, &data[off]);
1175                 }
1176               else
1177                 {
1178                   idx = seh_symbol (c->abfd, c->scopes[it].handler_addr, "", "", UNDSEC, BSF_GLOBAL, 0);
1179                   seh_reloc (c->abfd, off, BFD_RELOC_RVA, idx);
1180                 }
1181               off += 4;
1182               if (c->scopes[it].jump_addr[0] == '@')
1183                 {
1184                   if (strcasecmp (c->scopes[it].jump_addr, "@1") == 0)
1185                     bfd_put_32 (c->abfd, (bfd_vma) 1, &data[off]);
1186                 }
1187               else
1188                 {
1189                   idx = seh_symbol (c->abfd, c->scopes[it].jump_addr, "", "", UNDSEC, BSF_GLOBAL, 0);
1190                   seh_reloc (c->abfd, off, BFD_RELOC_RVA, idx);
1191                 }
1192               off += 4;
1193             }
1194         }
1195     }
1196   *poffs = off;
1197 }
1198
1199 static size_t
1200 seh_getsize_unwind_data (seh_context *c)
1201 {
1202   int need = seh_needed_unwind_info (c);
1203   int i;
1204   char **names = (char **) xmalloc (sizeof (char *) * need);
1205   char **pc_syms = (char **) xmalloc (sizeof (char *) * need);
1206   int *elm_start = (int *) xmalloc (sizeof (int) * (need + 1));
1207   size_t xdata_sz = 0;
1208
1209   seh_fill_pcsyms (c, pc_syms, elm_start);
1210   elm_start[need] = c->elems_count;
1211
1212   xdata_sz += ((12 * (size_t) need));
1213   c->count_syms += 5 * need;
1214   xdata_sz += (seh_getsize_of_unwind_entry (c, elm_start[0], elm_start[1], 1 != need) + 7) & ~7;
1215   for (i = 1; i < need; i++)
1216   {
1217     xdata_sz += (seh_getsize_of_unwind_entry (c, elm_start[i], elm_start[i + 1], 1 != need) + 7) & ~7;
1218   }
1219
1220   /* Create lable names for .xdata unwind info.  */
1221   names[0] = c->xdata_first;
1222   for (i = 1; i < need; i++)
1223     names[i] = seh_make_xlbl_name (c);
1224   c->xdata_names = names;
1225   c->xdata_pcsyms = pc_syms;
1226   c->xdata_elm_start = elm_start;
1227   c->xdata_sz = xdata_sz;
1228   return xdata_sz;
1229 }
1230
1231 static void
1232 seh_create_unwind_data (seh_context *c, unsigned char *data, size_t offs)
1233 {
1234   int need = seh_needed_unwind_info (c);
1235   int i;
1236   char **names = c->xdata_names;
1237   char **pc_syms = c->xdata_pcsyms;
1238   int *elm_start = c->xdata_elm_start;
1239
1240   for (i = 1; i < need; i++)
1241     {
1242       seh_make_function_entry_xdata (c, pc_syms[i], c->end_symbol, names[i], data, &offs, i);
1243     }
1244   /* Generate the function entry. Remark, that just
1245      first is in .pdata section and already emitted.  */
1246   seh_make_unwind_entry (c, c->xdata_first, elm_start[0], elm_start[1], 1 != need, data, &offs, 1);
1247   for (i = 1; i < need; i++)
1248     {
1249       seh_make_unwind_entry (c, names[i], elm_start[i], elm_start[i + 1], (i + 1) != need, data, &offs, i + 1);
1250     }
1251   for (i = 1; i < need; i++)
1252     free (names[i]);
1253   free (names);
1254   free (pc_syms);
1255   free (elm_start);
1256   c->xdata_names = NULL;
1257   c->xdata_pcsyms = NULL;
1258   c->xdata_elm_start = NULL;
1259 }
1260
1261 static void
1262 seh_make_function_entry_xdata (seh_context *c, char *pc_start, char *pc_end, char *pc_xdata, unsigned char *data, size_t *poffs,int no)
1263 {
1264   bfd_vma addr = (bfd_vma) *poffs;
1265   int idx;
1266   char s[100];
1267   if (!data)
1268     return;
1269   sprintf (s,"%x",no);
1270   seh_symbol (c->abfd, ".xdata_fct",c->func_name, s, c->section, BSF_GLOBAL, (int) poffs[0]);
1271   idx = seh_symbol (c->abfd, pc_start,"","", UNDSEC, BSF_GLOBAL,0);
1272   seh_reloc (c->abfd, addr, BFD_RELOC_RVA, idx);
1273   idx = seh_symbol (c->abfd, pc_end,"","", UNDSEC, BSF_GLOBAL,0);
1274   seh_reloc (c->abfd, addr + 4, BFD_RELOC_RVA, idx);
1275   idx = seh_symbol (c->abfd, pc_xdata,"","", UNDSEC, BSF_GLOBAL,0);
1276   seh_reloc (c->abfd, addr + 8, BFD_RELOC_RVA, idx);
1277   poffs[0] += 12;
1278 }
1279
1280 static seh_scope_elem *
1281 seh_x64_makescope_elem (seh_context *c, const char *begin, const char *end,
1282                         const char *handler, const char *jmp)
1283 {
1284   seh_scope_elem *r;
1285   if (!end || !begin)
1286     return NULL;
1287   if (c->scope_count >= c->scope_max)
1288     {
1289       seh_scope_elem *h = (seh_scope_elem *) xmalloc (sizeof (seh_scope_elem) * (c->scope_max + 8));
1290       memset (h, 0, sizeof (seh_scope_elem) * (c->scope_max + 8));
1291       if (c->scopes != NULL)
1292         memcpy (h, c->scopes, sizeof (seh_scope_elem) * c->scope_max);
1293       if (c->scopes != NULL)
1294         free (c->scopes);
1295       c->scopes = h;
1296       c->scope_max += 8;
1297     }
1298   r = &c->scopes[c->scope_count++];
1299   r->begin_addr = xstrdup (begin);
1300   r->end_addr = xstrdup (end);
1301   r->handler_addr = (!handler ? NULL : xstrdup (handler));
1302   r->jump_addr = (!jmp ? NULL : xstrdup (jmp));
1303   return r;
1304 }