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