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