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