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. */ | |
604ab327 | 62 | |
987499b2 KT |
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; | |
688805f3 | 133 | if (!h || h->done) |
987499b2 KT |
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; | |
604ab327 | 179 | |
987499b2 KT |
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; | |
604ab327 | 214 | if (h == NULL || h->done) |
987499b2 KT |
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; | |
604ab327 | 272 | |
987499b2 KT |
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, | |
604ab327 | 279 | ((seh_ctx_cur->elems_max + 8) * sizeof (seh_prologue_element))); |
987499b2 KT |
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] = | |
604ab327 NC |
295 | { "cfa", "rcx", "rdx", "rbx", "rsp", "rbp","rsi","rdi", |
296 | "r8","r9","r10","r11","r12","r13","r14","r15" }; | |
987499b2 | 297 | static const char *int_regs[16] = |
604ab327 NC |
298 | { "rax", "rcx", "rdx", "rbx", "rsp", "rbp","rsi","rdi", |
299 | "r8","r9","r10","r11","r12","r13","r14","r15" }; | |
987499b2 | 300 | static const char *xmm_regs[16] = |
604ab327 NC |
301 | { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", |
302 | "xmm8", "xmm9", "xmm10","xmm11","xmm12","xmm13","xmm14","xmm15" }; | |
987499b2 | 303 | static const char *mm_regs[16] = |
604ab327 NC |
304 | { "xmm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7", |
305 | "xmm8", "mm9", "mm10","mm11","mm12","mm13","mm14","mm15" }; | |
987499b2 KT |
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) | |
604ab327 NC |
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 | ||
987499b2 | 333 | if (*input_line_pointer == 0 || *input_line_pointer == '\n') |
987499b2 | 334 | return 0; |
604ab327 | 335 | |
987499b2 KT |
336 | if (*input_line_pointer == '%') |
337 | ++input_line_pointer; | |
338 | symbol_name = input_line_pointer; | |
339 | name_end = get_symbol_end (); | |
604ab327 | 340 | |
987499b2 | 341 | for (i = 0; i < 16; i++) |
987499b2 KT |
342 | if (! strcasecmp (p[i], symbol_name)) |
343 | break; | |
604ab327 | 344 | |
987499b2 | 345 | if (i == 16) |
987499b2 | 346 | as_warn (_("In %s we found the invalid register name %s.\n"), |
604ab327 NC |
347 | tok, symbol_name); |
348 | ||
987499b2 KT |
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') | |
987499b2 | 369 | { |
987499b2 | 370 | ++input_line_pointer; |
604ab327 | 371 | had_one = 1; |
987499b2 | 372 | base = 8; |
604ab327 NC |
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 | } | |
987499b2 | 391 | } |
987499b2 | 392 | while (*input_line_pointer != 0) |
604ab327 NC |
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 | } | |
987499b2 KT |
407 | *off = v; |
408 | if (had_one == 0) | |
604ab327 NC |
409 | { |
410 | as_warn (_("In %s we expect a number.\n"), | |
411 | tok); | |
412 | } | |
987499b2 KT |
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) | |
604ab327 NC |
605 | { |
606 | as_warn (_(".seh_proc has to be closed by .seh_endprog\n")); | |
607 | obj_coff_seh_endproc (0); | |
608 | } | |
987499b2 KT |
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) | |
604ab327 NC |
644 | { |
645 | as_warn (_(".seh_endprog without prior .seh_proc (ignored)\n")); | |
646 | demand_empty_rest_of_line (); | |
647 | return; | |
648 | } | |
987499b2 KT |
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; | |
604ab327 | 661 | |
987499b2 | 662 | if (seh_ctx_cur == NULL) |
604ab327 NC |
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. */ | |
987499b2 | 669 | switch (what) |
604ab327 NC |
670 | { |
671 | case 0: | |
672 | if (seh_x64_read_reg (".seh_push", 1, ®)) | |
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 | } | |
987499b2 KT |
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) | |
604ab327 NC |
700 | { |
701 | case 0: | |
702 | ok &= seh_x64_read_reg (".seh_savereg", 1, ®); | |
703 | kind = UWOP_SAVE_NONVOL; | |
704 | break; | |
705 | case 1: | |
706 | ok &= seh_x64_read_reg (".seh_savemm", 2, ®); | |
707 | kind = UWOP_SAVE_XMM; | |
708 | break; | |
709 | case 2: | |
710 | ok &= seh_x64_read_reg (".seh_savexmm", 3, ®); | |
711 | kind = UWOP_SAVE_XMM128; | |
712 | break; | |
713 | default: | |
714 | abort (); | |
715 | } | |
987499b2 KT |
716 | ok &= seh_read_offset (".seh_save", &off); |
717 | if (seh_ctx_cur == NULL) | |
604ab327 NC |
718 | { |
719 | as_warn (_(".seh_save used outside of .seh_proc block.\n")); | |
720 | demand_empty_rest_of_line (); | |
721 | return; | |
722 | } | |
987499b2 KT |
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) | |
604ab327 NC |
734 | { |
735 | as_warn (_(".seh_endprologue used outside of .seh_proc block.\n")); | |
736 | demand_empty_rest_of_line (); | |
737 | return; | |
738 | } | |
987499b2 KT |
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; | |
604ab327 | 749 | |
987499b2 | 750 | if (seh_ctx_cur == NULL) |
604ab327 NC |
751 | { |
752 | as_warn (_(".seh_stackalloc used outside of .seh_proc block.\n")); | |
753 | demand_empty_rest_of_line (); | |
754 | return; | |
755 | } | |
987499b2 KT |
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, ®); | |
773 | ok &= seh_read_offset (".seh_setframe", &off); | |
774 | if (seh_ctx_cur == NULL) | |
604ab327 NC |
775 | { |
776 | as_warn (_(".seh_setframe used outside of .seh_proc block.\n")); | |
777 | demand_empty_rest_of_line (); | |
778 | return; | |
779 | } | |
987499b2 | 780 | if (ok) |
604ab327 NC |
781 | { |
782 | seh_ctx_cur->framereg = reg; | |
783 | seh_ctx_cur->frameoff = off; | |
784 | } | |
987499b2 KT |
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; | |
604ab327 | 829 | |
987499b2 KT |
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; | |
604ab327 | 844 | |
987499b2 KT |
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; | |
604ab327 | 862 | |
987499b2 KT |
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, | |
604ab327 NC |
954 | ((SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA | flags) |
955 | & bfd_applicable_section_flags (stdoutput))); | |
987499b2 KT |
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); | |
604ab327 | 977 | |
987499b2 KT |
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; | |
604ab327 | 991 | |
987499b2 KT |
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) | |
604ab327 NC |
998 | { |
999 | seh_make_globl (sym_name); | |
1000 | *addr = colon (sym_name); | |
1001 | } | |
987499b2 KT |
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; | |
604ab327 | 1015 | |
987499b2 KT |
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++) | |
987499b2 | 1021 | { |
604ab327 NC |
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 | } | |
987499b2 | 1030 | } |
987499b2 KT |
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; | |
604ab327 | 1041 | |
987499b2 KT |
1042 | if (c->elems_count == 0) |
1043 | return count; | |
1044 | for (i = 0; i < c->elems_count; i++) | |
987499b2 | 1045 | { |
604ab327 NC |
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 | } | |
987499b2 | 1052 | } |
987499b2 KT |
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); | |
604ab327 | 1060 | |
987499b2 KT |
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 || | |
604ab327 | 1073 | ((c->elems[elm_start].offset / 8) > 0xffff)) |
987499b2 KT |
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); | |
604ab327 | 1093 | |
987499b2 KT |
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]; | |
604ab327 | 1164 | |
987499b2 KT |
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++) | |
987499b2 | 1251 | xdata_sz += (seh_getsize_of_unwind_entry (c, elm_start[i], elm_start[i + 1], 1 != need) + 7) & ~7; |
987499b2 KT |
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++) | |
604ab327 NC |
1274 | seh_make_function_entry_xdata (c, pc_syms[i], c->end_symbol, names[i], data, &offs, i); |
1275 | ||
987499b2 KT |
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++) | |
604ab327 NC |
1280 | seh_make_unwind_entry (c, names[i], elm_start[i], elm_start[i + 1], (i + 1) != need, data, &offs, i + 1); |
1281 | ||
987499b2 KT |
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]; | |
604ab327 | 1298 | |
987499b2 KT |
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; | |
604ab327 | 1317 | |
987499b2 KT |
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 | } |