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