Rev 5222 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
5222 | serge | 1 | /* seh pdata/xdata coff object file format |
6324 | serge | 2 | Copyright (C) 2009-2015 Free Software Foundation, Inc. |
5222 | serge | 3 | |
4 | This file is part of GAS. |
||
5 | |||
6 | GAS is free software; you can redistribute it and/or modify |
||
7 | it under the terms of the GNU General Public License as published by |
||
8 | the Free Software Foundation; either version 3, or (at your option) |
||
9 | any later version. |
||
10 | |||
11 | GAS is distributed in the hope that it will be useful, |
||
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
14 | GNU General Public License for more details. |
||
15 | |||
16 | You should have received a copy of the GNU General Public License |
||
17 | along with GAS; see the file COPYING. If not, write to the Free |
||
18 | Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA |
||
19 | 02110-1301, USA. */ |
||
20 | |||
21 | #include "obj-coff-seh.h" |
||
22 | |||
23 | |||
24 | /* Private segment collection list. */ |
||
25 | struct seh_seg_list { |
||
26 | segT seg; |
||
27 | int subseg; |
||
28 | char *seg_name; |
||
29 | }; |
||
30 | |||
31 | /* Local data. */ |
||
32 | static seh_context *seh_ctx_cur = NULL; |
||
33 | |||
34 | static struct hash_control *seh_hash; |
||
35 | |||
36 | static struct seh_seg_list *x_segcur = NULL; |
||
37 | static struct seh_seg_list *p_segcur = NULL; |
||
38 | |||
39 | static void write_function_xdata (seh_context *); |
||
40 | static void write_function_pdata (seh_context *); |
||
41 | |||
42 | |||
43 | /* Build based on segment the derived .pdata/.xdata |
||
44 | segment name containing origin segment's postfix name part. */ |
||
45 | static char * |
||
46 | get_pxdata_name (segT seg, const char *base_name) |
||
47 | { |
||
48 | const char *name,*dollar, *dot; |
||
49 | char *sname; |
||
50 | |||
51 | name = bfd_get_section_name (stdoutput, seg); |
||
52 | |||
53 | dollar = strchr (name, '$'); |
||
54 | dot = strchr (name + 1, '.'); |
||
55 | |||
56 | if (!dollar && !dot) |
||
57 | name = ""; |
||
58 | else if (!dollar) |
||
59 | name = dot; |
||
60 | else if (!dot) |
||
61 | name = dollar; |
||
62 | else if (dot < dollar) |
||
63 | name = dot; |
||
64 | else |
||
65 | name = dollar; |
||
66 | |||
67 | sname = concat (base_name, name, NULL); |
||
68 | |||
69 | return sname; |
||
70 | } |
||
71 | |||
72 | /* Allocate a seh_seg_list structure. */ |
||
73 | static struct seh_seg_list * |
||
74 | alloc_pxdata_item (segT seg, int subseg, char *name) |
||
75 | { |
||
76 | struct seh_seg_list *r; |
||
77 | |||
78 | r = (struct seh_seg_list *) |
||
79 | xmalloc (sizeof (struct seh_seg_list) + strlen (name)); |
||
80 | r->seg = seg; |
||
81 | r->subseg = subseg; |
||
82 | r->seg_name = name; |
||
83 | return r; |
||
84 | } |
||
85 | |||
86 | /* Generate pdata/xdata segment with same linkonce properties |
||
87 | of based segment. */ |
||
88 | static segT |
||
89 | make_pxdata_seg (segT cseg, char *name) |
||
90 | { |
||
91 | segT save_seg = now_seg; |
||
92 | int save_subseg = now_subseg; |
||
93 | segT r; |
||
94 | flagword flags; |
||
95 | |||
96 | r = subseg_new (name, 0); |
||
97 | /* Check if code segment is marked as linked once. */ |
||
98 | flags = bfd_get_section_flags (stdoutput, cseg) |
||
99 | & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD |
||
100 | | SEC_LINK_DUPLICATES_ONE_ONLY | SEC_LINK_DUPLICATES_SAME_SIZE |
||
101 | | SEC_LINK_DUPLICATES_SAME_CONTENTS); |
||
102 | |||
103 | /* Add standard section flags. */ |
||
104 | flags |= SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA; |
||
105 | |||
106 | /* Apply possibly linked once flags to new generated segment, too. */ |
||
107 | if (!bfd_set_section_flags (stdoutput, r, flags)) |
||
108 | as_bad (_("bfd_set_section_flags: %s"), |
||
109 | bfd_errmsg (bfd_get_error ())); |
||
110 | |||
111 | /* Restore to previous segment. */ |
||
112 | subseg_set (save_seg, save_subseg); |
||
113 | return r; |
||
114 | } |
||
115 | |||
116 | static void |
||
117 | seh_hash_insert (const char *name, struct seh_seg_list *item) |
||
118 | { |
||
119 | const char *error_string; |
||
120 | |||
121 | if ((error_string = hash_jam (seh_hash, name, (char *) item))) |
||
122 | as_fatal (_("Inserting \"%s\" into structure table failed: %s"), |
||
123 | name, error_string); |
||
124 | } |
||
125 | |||
126 | static struct seh_seg_list * |
||
127 | seh_hash_find (char *name) |
||
128 | { |
||
129 | return (struct seh_seg_list *) hash_find (seh_hash, name); |
||
130 | } |
||
131 | |||
132 | static struct seh_seg_list * |
||
133 | seh_hash_find_or_make (segT cseg, const char *base_name) |
||
134 | { |
||
135 | struct seh_seg_list *item; |
||
136 | char *name; |
||
137 | |||
138 | /* Initialize seh_hash once. */ |
||
139 | if (!seh_hash) |
||
140 | seh_hash = hash_new (); |
||
141 | |||
142 | name = get_pxdata_name (cseg, base_name); |
||
143 | |||
144 | item = seh_hash_find (name); |
||
145 | if (!item) |
||
146 | { |
||
147 | item = alloc_pxdata_item (make_pxdata_seg (cseg, name), 0, name); |
||
148 | |||
149 | seh_hash_insert (item->seg_name, item); |
||
150 | } |
||
151 | else |
||
152 | free (name); |
||
153 | |||
154 | return item; |
||
155 | } |
||
156 | |||
157 | /* Check if current segment has same name. */ |
||
158 | static int |
||
159 | seh_validate_seg (const char *directive) |
||
160 | { |
||
161 | const char *cseg_name, *nseg_name; |
||
162 | if (seh_ctx_cur->code_seg == now_seg) |
||
163 | return 1; |
||
164 | cseg_name = bfd_get_section_name (stdoutput, seh_ctx_cur->code_seg); |
||
165 | nseg_name = bfd_get_section_name (stdoutput, now_seg); |
||
166 | as_bad (_("%s used in segment '%s' instead of expected '%s'"), |
||
167 | directive, nseg_name, cseg_name); |
||
168 | ignore_rest_of_line (); |
||
169 | return 0; |
||
170 | } |
||
171 | |||
6324 | serge | 172 | /* Switch back to the code section, whatever that may be. */ |
5222 | serge | 173 | static void |
6324 | serge | 174 | obj_coff_seh_code (int ignored ATTRIBUTE_UNUSED) |
175 | { |
||
176 | subseg_set (seh_ctx_cur->code_seg, 0); |
||
177 | } |
||
178 | |||
179 | static void |
||
5222 | serge | 180 | switch_xdata (int subseg, segT code_seg) |
181 | { |
||
182 | x_segcur = seh_hash_find_or_make (code_seg, ".xdata"); |
||
183 | |||
184 | subseg_set (x_segcur->seg, subseg); |
||
185 | } |
||
186 | |||
187 | static void |
||
188 | switch_pdata (segT code_seg) |
||
189 | { |
||
190 | p_segcur = seh_hash_find_or_make (code_seg, ".pdata"); |
||
191 | |||
192 | subseg_set (p_segcur->seg, p_segcur->subseg); |
||
193 | } |
||
194 | |||
195 | /* Parsing routines. */ |
||
196 | |||
197 | /* Return the style of SEH unwind info to generate. */ |
||
198 | |||
199 | static seh_kind |
||
200 | seh_get_target_kind (void) |
||
201 | { |
||
202 | if (!stdoutput) |
||
203 | return seh_kind_unknown; |
||
204 | switch (bfd_get_arch (stdoutput)) |
||
205 | { |
||
206 | case bfd_arch_arm: |
||
207 | case bfd_arch_powerpc: |
||
208 | case bfd_arch_sh: |
||
209 | return seh_kind_arm; |
||
210 | case bfd_arch_i386: |
||
211 | switch (bfd_get_mach (stdoutput)) |
||
212 | { |
||
213 | case bfd_mach_x86_64: |
||
214 | case bfd_mach_x86_64_intel_syntax: |
||
215 | return seh_kind_x64; |
||
216 | default: |
||
217 | break; |
||
218 | } |
||
219 | /* FALL THROUGH. */ |
||
220 | case bfd_arch_mips: |
||
221 | return seh_kind_mips; |
||
222 | case bfd_arch_ia64: |
||
223 | /* Should return seh_kind_x64. But not implemented yet. */ |
||
224 | return seh_kind_unknown; |
||
225 | default: |
||
226 | break; |
||
227 | } |
||
228 | return seh_kind_unknown; |
||
229 | } |
||
230 | |||
231 | /* Verify that we're in the context of a seh_proc. */ |
||
232 | |||
233 | static int |
||
234 | verify_context (const char *directive) |
||
235 | { |
||
236 | if (seh_ctx_cur == NULL) |
||
237 | { |
||
238 | as_bad (_("%s used outside of .seh_proc block"), directive); |
||
239 | ignore_rest_of_line (); |
||
240 | return 0; |
||
241 | } |
||
242 | return 1; |
||
243 | } |
||
244 | |||
245 | /* Similar, except we also verify the appropriate target. */ |
||
246 | |||
247 | static int |
||
248 | verify_context_and_target (const char *directive, seh_kind target) |
||
249 | { |
||
250 | if (seh_get_target_kind () != target) |
||
251 | { |
||
252 | as_warn (_("%s ignored for this target"), directive); |
||
253 | ignore_rest_of_line (); |
||
254 | return 0; |
||
255 | } |
||
256 | return verify_context (directive); |
||
257 | } |
||
258 | |||
259 | /* Skip whitespace and a comma. Error if the comma is not seen. */ |
||
260 | |||
261 | static int |
||
262 | skip_whitespace_and_comma (int required) |
||
263 | { |
||
264 | SKIP_WHITESPACE (); |
||
265 | if (*input_line_pointer == ',') |
||
266 | { |
||
267 | input_line_pointer++; |
||
268 | SKIP_WHITESPACE (); |
||
269 | return 1; |
||
270 | } |
||
271 | else if (required) |
||
272 | { |
||
273 | as_bad (_("missing separator")); |
||
274 | ignore_rest_of_line (); |
||
275 | } |
||
276 | else |
||
277 | demand_empty_rest_of_line (); |
||
278 | return 0; |
||
279 | } |
||
280 | |||
281 | /* Mark current context to use 32-bit instruction (arm). */ |
||
282 | |||
283 | static void |
||
284 | obj_coff_seh_32 (int what) |
||
285 | { |
||
286 | if (!verify_context_and_target ((what ? ".seh_32" : ".seh_no32"), |
||
287 | seh_kind_arm)) |
||
288 | return; |
||
289 | |||
290 | seh_ctx_cur->use_instruction_32 = (what ? 1 : 0); |
||
291 | demand_empty_rest_of_line (); |
||
292 | } |
||
293 | |||
294 | /* Set for current context the handler and optional data (arm). */ |
||
295 | |||
296 | static void |
||
297 | obj_coff_seh_eh (int what ATTRIBUTE_UNUSED) |
||
298 | { |
||
299 | if (!verify_context_and_target (".seh_eh", seh_kind_arm)) |
||
300 | return; |
||
301 | |||
302 | /* Write block to .text if exception handler is set. */ |
||
303 | seh_ctx_cur->handler_written = 1; |
||
304 | emit_expr (&seh_ctx_cur->handler, 4); |
||
305 | emit_expr (&seh_ctx_cur->handler_data, 4); |
||
306 | |||
307 | demand_empty_rest_of_line (); |
||
308 | } |
||
309 | |||
310 | /* Set for current context the default handler (x64). */ |
||
311 | |||
312 | static void |
||
313 | obj_coff_seh_handler (int what ATTRIBUTE_UNUSED) |
||
314 | { |
||
315 | char *symbol_name; |
||
316 | char name_end; |
||
317 | |||
318 | if (!verify_context (".seh_handler")) |
||
319 | return; |
||
320 | |||
321 | if (*input_line_pointer == 0 || *input_line_pointer == '\n') |
||
322 | { |
||
323 | as_bad (_(".seh_handler requires a handler")); |
||
324 | demand_empty_rest_of_line (); |
||
325 | return; |
||
326 | } |
||
327 | |||
328 | SKIP_WHITESPACE (); |
||
329 | |||
330 | if (*input_line_pointer == '@') |
||
331 | { |
||
6324 | serge | 332 | name_end = get_symbol_name (&symbol_name); |
5222 | serge | 333 | |
334 | seh_ctx_cur->handler.X_op = O_constant; |
||
335 | seh_ctx_cur->handler.X_add_number = 0; |
||
336 | |||
337 | if (strcasecmp (symbol_name, "@0") == 0 |
||
338 | || strcasecmp (symbol_name, "@null") == 0) |
||
339 | ; |
||
340 | else if (strcasecmp (symbol_name, "@1") == 0) |
||
341 | seh_ctx_cur->handler.X_add_number = 1; |
||
342 | else |
||
343 | as_bad (_("unknown constant value '%s' for handler"), symbol_name); |
||
344 | |||
6324 | serge | 345 | (void) restore_line_pointer (name_end); |
5222 | serge | 346 | } |
347 | else |
||
348 | expression (&seh_ctx_cur->handler); |
||
349 | |||
350 | seh_ctx_cur->handler_data.X_op = O_constant; |
||
351 | seh_ctx_cur->handler_data.X_add_number = 0; |
||
352 | seh_ctx_cur->handler_flags = 0; |
||
353 | |||
354 | if (!skip_whitespace_and_comma (0)) |
||
355 | return; |
||
356 | |||
357 | if (seh_get_target_kind () == seh_kind_x64) |
||
358 | { |
||
359 | do |
||
360 | { |
||
6324 | serge | 361 | name_end = get_symbol_name (&symbol_name); |
5222 | serge | 362 | |
363 | if (strcasecmp (symbol_name, "@unwind") == 0) |
||
364 | seh_ctx_cur->handler_flags |= UNW_FLAG_UHANDLER; |
||
365 | else if (strcasecmp (symbol_name, "@except") == 0) |
||
366 | seh_ctx_cur->handler_flags |= UNW_FLAG_EHANDLER; |
||
367 | else |
||
368 | as_bad (_(".seh_handler constant '%s' unknown"), symbol_name); |
||
369 | |||
6324 | serge | 370 | (void) restore_line_pointer (name_end); |
5222 | serge | 371 | } |
372 | while (skip_whitespace_and_comma (0)); |
||
373 | } |
||
374 | else |
||
375 | { |
||
376 | expression (&seh_ctx_cur->handler_data); |
||
377 | demand_empty_rest_of_line (); |
||
378 | |||
379 | if (seh_ctx_cur->handler_written) |
||
380 | as_warn (_(".seh_handler after .seh_eh is ignored")); |
||
381 | } |
||
382 | } |
||
383 | |||
384 | /* Switch to subsection for handler data for exception region (x64). */ |
||
385 | |||
386 | static void |
||
387 | obj_coff_seh_handlerdata (int what ATTRIBUTE_UNUSED) |
||
388 | { |
||
389 | if (!verify_context_and_target (".seh_handlerdata", seh_kind_x64)) |
||
390 | return; |
||
391 | demand_empty_rest_of_line (); |
||
392 | |||
393 | switch_xdata (seh_ctx_cur->subsection + 1, seh_ctx_cur->code_seg); |
||
394 | } |
||
395 | |||
396 | /* Mark end of current context. */ |
||
397 | |||
398 | static void |
||
399 | do_seh_endproc (void) |
||
400 | { |
||
401 | seh_ctx_cur->end_addr = symbol_temp_new_now (); |
||
402 | |||
403 | write_function_xdata (seh_ctx_cur); |
||
404 | write_function_pdata (seh_ctx_cur); |
||
405 | seh_ctx_cur = NULL; |
||
406 | } |
||
407 | |||
408 | static void |
||
409 | obj_coff_seh_endproc (int what ATTRIBUTE_UNUSED) |
||
410 | { |
||
411 | demand_empty_rest_of_line (); |
||
412 | if (seh_ctx_cur == NULL) |
||
413 | { |
||
414 | as_bad (_(".seh_endproc used without .seh_proc")); |
||
415 | return; |
||
416 | } |
||
417 | seh_validate_seg (".seh_endproc"); |
||
418 | do_seh_endproc (); |
||
419 | } |
||
420 | |||
421 | /* Mark begin of new context. */ |
||
422 | |||
423 | static void |
||
424 | obj_coff_seh_proc (int what ATTRIBUTE_UNUSED) |
||
425 | { |
||
426 | char *symbol_name; |
||
427 | char name_end; |
||
428 | |||
429 | if (seh_ctx_cur != NULL) |
||
430 | { |
||
431 | as_bad (_("previous SEH entry not closed (missing .seh_endproc)")); |
||
432 | do_seh_endproc (); |
||
433 | } |
||
434 | |||
435 | if (*input_line_pointer == 0 || *input_line_pointer == '\n') |
||
436 | { |
||
437 | as_bad (_(".seh_proc requires function label name")); |
||
438 | demand_empty_rest_of_line (); |
||
439 | return; |
||
440 | } |
||
441 | |||
442 | seh_ctx_cur = XCNEW (seh_context); |
||
443 | |||
444 | seh_ctx_cur->code_seg = now_seg; |
||
445 | |||
446 | if (seh_get_target_kind () == seh_kind_x64) |
||
447 | { |
||
448 | x_segcur = seh_hash_find_or_make (seh_ctx_cur->code_seg, ".xdata"); |
||
449 | seh_ctx_cur->subsection = x_segcur->subseg; |
||
450 | x_segcur->subseg += 2; |
||
451 | } |
||
452 | |||
453 | SKIP_WHITESPACE (); |
||
454 | |||
6324 | serge | 455 | name_end = get_symbol_name (&symbol_name); |
5222 | serge | 456 | seh_ctx_cur->func_name = xstrdup (symbol_name); |
6324 | serge | 457 | (void) restore_line_pointer (name_end); |
5222 | serge | 458 | |
459 | demand_empty_rest_of_line (); |
||
460 | |||
461 | seh_ctx_cur->start_addr = symbol_temp_new_now (); |
||
462 | } |
||
463 | |||
464 | /* Mark end of prologue for current context. */ |
||
465 | |||
466 | static void |
||
467 | obj_coff_seh_endprologue (int what ATTRIBUTE_UNUSED) |
||
468 | { |
||
469 | if (!verify_context (".seh_endprologue") |
||
470 | || !seh_validate_seg (".seh_endprologue")) |
||
471 | return; |
||
472 | demand_empty_rest_of_line (); |
||
473 | |||
474 | if (seh_ctx_cur->endprologue_addr != NULL) |
||
475 | as_warn (_("duplicate .seh_endprologue in .seh_proc block")); |
||
476 | else |
||
477 | seh_ctx_cur->endprologue_addr = symbol_temp_new_now (); |
||
478 | } |
||
479 | |||
480 | /* End-of-file hook. */ |
||
481 | |||
482 | void |
||
483 | obj_coff_seh_do_final (void) |
||
484 | { |
||
485 | if (seh_ctx_cur != NULL) |
||
486 | { |
||
487 | as_bad (_("open SEH entry at end of file (missing .cfi_endproc)")); |
||
488 | do_seh_endproc (); |
||
489 | } |
||
490 | } |
||
491 | |||
492 | /* Enter a prologue element into current context (x64). */ |
||
493 | |||
494 | static void |
||
495 | seh_x64_make_prologue_element (int code, int info, offsetT off) |
||
496 | { |
||
497 | seh_prologue_element *n; |
||
498 | |||
499 | if (seh_ctx_cur == NULL) |
||
500 | return; |
||
501 | if (seh_ctx_cur->elems_count == seh_ctx_cur->elems_max) |
||
502 | { |
||
503 | seh_ctx_cur->elems_max += 8; |
||
504 | seh_ctx_cur->elems = XRESIZEVEC (seh_prologue_element, |
||
505 | seh_ctx_cur->elems, |
||
506 | seh_ctx_cur->elems_max); |
||
507 | } |
||
508 | |||
509 | n = &seh_ctx_cur->elems[seh_ctx_cur->elems_count++]; |
||
510 | n->code = code; |
||
511 | n->info = info; |
||
512 | n->off = off; |
||
513 | n->pc_addr = symbol_temp_new_now (); |
||
514 | } |
||
515 | |||
516 | /* Helper to read a register name from input stream (x64). */ |
||
517 | |||
518 | static int |
||
519 | seh_x64_read_reg (const char *directive, int kind) |
||
520 | { |
||
521 | static const char * const int_regs[16] = |
||
522 | { "rax", "rcx", "rdx", "rbx", "rsp", "rbp","rsi","rdi", |
||
523 | "r8","r9","r10","r11","r12","r13","r14","r15" }; |
||
524 | static const char * const xmm_regs[16] = |
||
525 | { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", |
||
526 | "xmm8", "xmm9", "xmm10","xmm11","xmm12","xmm13","xmm14","xmm15" }; |
||
527 | |||
528 | const char * const *regs = NULL; |
||
529 | char name_end; |
||
530 | char *symbol_name = NULL; |
||
531 | int i; |
||
532 | |||
533 | switch (kind) |
||
534 | { |
||
535 | case 0: |
||
536 | case 1: |
||
537 | regs = int_regs; |
||
538 | break; |
||
539 | case 2: |
||
540 | regs = xmm_regs; |
||
541 | break; |
||
542 | default: |
||
543 | abort (); |
||
544 | } |
||
545 | |||
546 | SKIP_WHITESPACE (); |
||
547 | if (*input_line_pointer == '%') |
||
548 | ++input_line_pointer; |
||
6324 | serge | 549 | name_end = get_symbol_name (& symbol_name); |
5222 | serge | 550 | |
551 | for (i = 0; i < 16; i++) |
||
552 | if (! strcasecmp (regs[i], symbol_name)) |
||
553 | break; |
||
554 | |||
6324 | serge | 555 | (void) restore_line_pointer (name_end); |
5222 | serge | 556 | |
557 | /* Error if register not found, or EAX used as a frame pointer. */ |
||
558 | if (i == 16 || (kind == 0 && i == 0)) |
||
559 | { |
||
560 | as_bad (_("invalid register for %s"), directive); |
||
561 | return -1; |
||
562 | } |
||
563 | |||
564 | return i; |
||
565 | } |
||
566 | |||
567 | /* Add a register push-unwind token to the current context. */ |
||
568 | |||
569 | static void |
||
570 | obj_coff_seh_pushreg (int what ATTRIBUTE_UNUSED) |
||
571 | { |
||
572 | int reg; |
||
573 | |||
574 | if (!verify_context_and_target (".seh_pushreg", seh_kind_x64) |
||
575 | || !seh_validate_seg (".seh_pushreg")) |
||
576 | return; |
||
577 | |||
578 | reg = seh_x64_read_reg (".seh_pushreg", 1); |
||
579 | demand_empty_rest_of_line (); |
||
580 | |||
581 | if (reg < 0) |
||
582 | return; |
||
583 | |||
584 | seh_x64_make_prologue_element (UWOP_PUSH_NONVOL, reg, 0); |
||
585 | } |
||
586 | |||
587 | /* Add a register frame-unwind token to the current context. */ |
||
588 | |||
589 | static void |
||
590 | obj_coff_seh_pushframe (int what ATTRIBUTE_UNUSED) |
||
591 | { |
||
592 | if (!verify_context_and_target (".seh_pushframe", seh_kind_x64) |
||
593 | || !seh_validate_seg (".seh_pushframe")) |
||
594 | return; |
||
595 | demand_empty_rest_of_line (); |
||
596 | |||
597 | seh_x64_make_prologue_element (UWOP_PUSH_MACHFRAME, 0, 0); |
||
598 | } |
||
599 | |||
600 | /* Add a register save-unwind token to current context. */ |
||
601 | |||
602 | static void |
||
603 | obj_coff_seh_save (int what) |
||
604 | { |
||
605 | const char *directive = (what == 1 ? ".seh_savereg" : ".seh_savexmm"); |
||
606 | int code, reg, scale; |
||
607 | offsetT off; |
||
608 | |||
609 | if (!verify_context_and_target (directive, seh_kind_x64) |
||
610 | || !seh_validate_seg (directive)) |
||
611 | return; |
||
612 | |||
613 | reg = seh_x64_read_reg (directive, what); |
||
614 | |||
615 | if (!skip_whitespace_and_comma (1)) |
||
616 | return; |
||
617 | |||
618 | off = get_absolute_expression (); |
||
619 | demand_empty_rest_of_line (); |
||
620 | |||
621 | if (reg < 0) |
||
622 | return; |
||
623 | if (off < 0) |
||
624 | { |
||
625 | as_bad (_("%s offset is negative"), directive); |
||
626 | return; |
||
627 | } |
||
628 | |||
629 | scale = (what == 1 ? 8 : 16); |
||
630 | |||
631 | if ((off & (scale - 1)) == 0 && off <= (offsetT) (0xffff * scale)) |
||
632 | { |
||
633 | code = (what == 1 ? UWOP_SAVE_NONVOL : UWOP_SAVE_XMM128); |
||
634 | off /= scale; |
||
635 | } |
||
636 | else if (off < (offsetT) 0xffffffff) |
||
637 | code = (what == 1 ? UWOP_SAVE_NONVOL_FAR : UWOP_SAVE_XMM128_FAR); |
||
638 | else |
||
639 | { |
||
640 | as_bad (_("%s offset out of range"), directive); |
||
641 | return; |
||
642 | } |
||
643 | |||
644 | seh_x64_make_prologue_element (code, reg, off); |
||
645 | } |
||
646 | |||
647 | /* Add a stack-allocation token to current context. */ |
||
648 | |||
649 | static void |
||
650 | obj_coff_seh_stackalloc (int what ATTRIBUTE_UNUSED) |
||
651 | { |
||
652 | offsetT off; |
||
653 | int code, info; |
||
654 | |||
655 | if (!verify_context_and_target (".seh_stackalloc", seh_kind_x64) |
||
656 | || !seh_validate_seg (".seh_stackalloc")) |
||
657 | return; |
||
658 | |||
659 | off = get_absolute_expression (); |
||
660 | demand_empty_rest_of_line (); |
||
661 | |||
662 | if (off == 0) |
||
663 | return; |
||
664 | if (off < 0) |
||
665 | { |
||
666 | as_bad (_(".seh_stackalloc offset is negative")); |
||
667 | return; |
||
668 | } |
||
669 | |||
670 | if ((off & 7) == 0 && off <= 128) |
||
671 | code = UWOP_ALLOC_SMALL, info = (off - 8) >> 3, off = 0; |
||
672 | else if ((off & 7) == 0 && off <= (offsetT) (0xffff * 8)) |
||
673 | code = UWOP_ALLOC_LARGE, info = 0, off >>= 3; |
||
674 | else if (off <= (offsetT) 0xffffffff) |
||
675 | code = UWOP_ALLOC_LARGE, info = 1; |
||
676 | else |
||
677 | { |
||
678 | as_bad (_(".seh_stackalloc offset out of range")); |
||
679 | return; |
||
680 | } |
||
681 | |||
682 | seh_x64_make_prologue_element (code, info, off); |
||
683 | } |
||
684 | |||
685 | /* Add a frame-pointer token to current context. */ |
||
686 | |||
687 | static void |
||
688 | obj_coff_seh_setframe (int what ATTRIBUTE_UNUSED) |
||
689 | { |
||
690 | offsetT off; |
||
691 | int reg; |
||
692 | |||
693 | if (!verify_context_and_target (".seh_setframe", seh_kind_x64) |
||
694 | || !seh_validate_seg (".seh_setframe")) |
||
695 | return; |
||
696 | |||
697 | reg = seh_x64_read_reg (".seh_setframe", 0); |
||
698 | |||
699 | if (!skip_whitespace_and_comma (1)) |
||
700 | return; |
||
701 | |||
702 | off = get_absolute_expression (); |
||
703 | demand_empty_rest_of_line (); |
||
704 | |||
705 | if (reg < 0) |
||
706 | return; |
||
707 | if (off < 0) |
||
708 | as_bad (_(".seh_setframe offset is negative")); |
||
709 | else if (off > 240) |
||
710 | as_bad (_(".seh_setframe offset out of range")); |
||
711 | else if (off & 15) |
||
712 | as_bad (_(".seh_setframe offset not a multiple of 16")); |
||
713 | else if (seh_ctx_cur->framereg != 0) |
||
714 | as_bad (_("duplicate .seh_setframe in current .seh_proc")); |
||
715 | else |
||
716 | { |
||
717 | seh_ctx_cur->framereg = reg; |
||
718 | seh_ctx_cur->frameoff = off; |
||
719 | seh_x64_make_prologue_element (UWOP_SET_FPREG, 0, 0); |
||
720 | } |
||
721 | } |
||
722 | |||
723 | /* Data writing routines. */ |
||
724 | |||
725 | /* Output raw integers in 1, 2, or 4 bytes. */ |
||
726 | |||
727 | static inline void |
||
728 | out_one (int byte) |
||
729 | { |
||
730 | FRAG_APPEND_1_CHAR (byte); |
||
731 | } |
||
732 | |||
733 | static inline void |
||
734 | out_two (int data) |
||
735 | { |
||
736 | md_number_to_chars (frag_more (2), data, 2); |
||
737 | } |
||
738 | |||
739 | static inline void |
||
740 | out_four (int data) |
||
741 | { |
||
742 | md_number_to_chars (frag_more (4), data, 4); |
||
743 | } |
||
744 | |||
745 | /* Write out prologue data for x64. */ |
||
746 | |||
747 | static void |
||
748 | seh_x64_write_prologue_data (const seh_context *c) |
||
749 | { |
||
750 | int i; |
||
751 | |||
752 | /* We have to store in reverse order. */ |
||
753 | for (i = c->elems_count - 1; i >= 0; --i) |
||
754 | { |
||
755 | const seh_prologue_element *e = c->elems + i; |
||
756 | expressionS exp; |
||
757 | |||
758 | /* First comes byte offset in code. */ |
||
759 | exp.X_op = O_subtract; |
||
760 | exp.X_add_symbol = e->pc_addr; |
||
761 | exp.X_op_symbol = c->start_addr; |
||
762 | exp.X_add_number = 0; |
||
763 | emit_expr (&exp, 1); |
||
764 | |||
765 | /* Second comes code+info packed into a byte. */ |
||
766 | out_one ((e->info << 4) | e->code); |
||
767 | |||
768 | switch (e->code) |
||
769 | { |
||
770 | case UWOP_PUSH_NONVOL: |
||
771 | case UWOP_ALLOC_SMALL: |
||
772 | case UWOP_SET_FPREG: |
||
773 | case UWOP_PUSH_MACHFRAME: |
||
774 | /* These have no extra data. */ |
||
775 | break; |
||
776 | |||
777 | case UWOP_ALLOC_LARGE: |
||
778 | if (e->info) |
||
779 | { |
||
780 | case UWOP_SAVE_NONVOL_FAR: |
||
781 | case UWOP_SAVE_XMM128_FAR: |
||
782 | /* An unscaled 4 byte offset. */ |
||
783 | out_four (e->off); |
||
784 | break; |
||
785 | } |
||
786 | /* FALLTHRU */ |
||
787 | |||
788 | case UWOP_SAVE_NONVOL: |
||
789 | case UWOP_SAVE_XMM128: |
||
790 | /* A scaled 2 byte offset. */ |
||
791 | out_two (e->off); |
||
792 | break; |
||
793 | |||
794 | default: |
||
795 | abort (); |
||
796 | } |
||
797 | } |
||
798 | } |
||
799 | |||
800 | static int |
||
801 | seh_x64_size_prologue_data (const seh_context *c) |
||
802 | { |
||
803 | int i, ret = 0; |
||
804 | |||
805 | for (i = c->elems_count - 1; i >= 0; --i) |
||
806 | switch (c->elems[i].code) |
||
807 | { |
||
808 | case UWOP_PUSH_NONVOL: |
||
809 | case UWOP_ALLOC_SMALL: |
||
810 | case UWOP_SET_FPREG: |
||
811 | case UWOP_PUSH_MACHFRAME: |
||
812 | ret += 1; |
||
813 | break; |
||
814 | |||
815 | case UWOP_SAVE_NONVOL: |
||
816 | case UWOP_SAVE_XMM128: |
||
817 | ret += 2; |
||
818 | break; |
||
819 | |||
820 | case UWOP_SAVE_NONVOL_FAR: |
||
821 | case UWOP_SAVE_XMM128_FAR: |
||
822 | ret += 3; |
||
823 | break; |
||
824 | |||
825 | case UWOP_ALLOC_LARGE: |
||
826 | ret += (c->elems[i].info ? 3 : 2); |
||
827 | break; |
||
828 | |||
829 | default: |
||
830 | abort (); |
||
831 | } |
||
832 | |||
833 | return ret; |
||
834 | } |
||
835 | |||
836 | /* Write out the xdata information for one function (x64). */ |
||
837 | |||
838 | static void |
||
839 | seh_x64_write_function_xdata (seh_context *c) |
||
840 | { |
||
841 | int flags, count_unwind_codes; |
||
842 | expressionS exp; |
||
843 | |||
844 | /* Set 4-byte alignment. */ |
||
845 | frag_align (2, 0, 0); |
||
846 | |||
847 | c->xdata_addr = symbol_temp_new_now (); |
||
848 | flags = c->handler_flags; |
||
849 | count_unwind_codes = seh_x64_size_prologue_data (c); |
||
850 | |||
851 | /* ubyte:3 version, ubyte:5 flags. */ |
||
852 | out_one ((flags << 3) | 1); |
||
853 | |||
854 | /* Size of prologue. */ |
||
855 | if (c->endprologue_addr) |
||
856 | { |
||
857 | exp.X_op = O_subtract; |
||
858 | exp.X_add_symbol = c->endprologue_addr; |
||
859 | exp.X_op_symbol = c->start_addr; |
||
860 | exp.X_add_number = 0; |
||
861 | emit_expr (&exp, 1); |
||
862 | } |
||
863 | else |
||
864 | out_one (0); |
||
865 | |||
866 | /* Number of slots (i.e. shorts) in the unwind codes array. */ |
||
867 | if (count_unwind_codes > 255) |
||
868 | as_fatal (_("too much unwind data in this .seh_proc")); |
||
869 | out_one (count_unwind_codes); |
||
870 | |||
871 | /* ubyte:4 frame-reg, ubyte:4 frame-reg-offset. */ |
||
872 | /* Note that frameoff is already a multiple of 16, and therefore |
||
873 | the offset is already both scaled and shifted into place. */ |
||
874 | out_one (c->frameoff | c->framereg); |
||
875 | |||
876 | seh_x64_write_prologue_data (c); |
||
877 | |||
878 | /* We need to align prologue data. */ |
||
879 | if (count_unwind_codes & 1) |
||
880 | out_two (0); |
||
881 | |||
882 | if (flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER)) |
||
883 | { |
||
884 | /* Force the use of segment-relative relocations instead of absolute |
||
885 | valued expressions. Don't adjust for constants (e.g. NULL). */ |
||
886 | if (c->handler.X_op == O_symbol) |
||
887 | c->handler.X_op = O_symbol_rva; |
||
888 | emit_expr (&c->handler, 4); |
||
889 | } |
||
890 | |||
891 | /* Handler data will be tacked in here by subsections. */ |
||
892 | } |
||
893 | |||
894 | /* Write out xdata for one function. */ |
||
895 | |||
896 | static void |
||
897 | write_function_xdata (seh_context *c) |
||
898 | { |
||
899 | segT save_seg = now_seg; |
||
900 | int save_subseg = now_subseg; |
||
901 | |||
902 | /* MIPS, SH, ARM don't have xdata. */ |
||
903 | if (seh_get_target_kind () != seh_kind_x64) |
||
904 | return; |
||
905 | |||
906 | switch_xdata (c->subsection, c->code_seg); |
||
907 | |||
908 | seh_x64_write_function_xdata (c); |
||
909 | |||
910 | subseg_set (save_seg, save_subseg); |
||
911 | } |
||
912 | |||
913 | /* Write pdata section data for one function (arm). */ |
||
914 | |||
915 | static void |
||
916 | seh_arm_write_function_pdata (seh_context *c) |
||
917 | { |
||
918 | expressionS exp; |
||
919 | unsigned int prol_len = 0, func_len = 0; |
||
920 | unsigned int val; |
||
921 | |||
922 | /* Start address of the function. */ |
||
923 | exp.X_op = O_symbol; |
||
924 | exp.X_add_symbol = c->start_addr; |
||
925 | exp.X_add_number = 0; |
||
926 | emit_expr (&exp, 4); |
||
927 | |||
928 | exp.X_op = O_subtract; |
||
929 | exp.X_add_symbol = c->end_addr; |
||
930 | exp.X_op_symbol = c->start_addr; |
||
931 | exp.X_add_number = 0; |
||
932 | if (resolve_expression (&exp) && exp.X_op == O_constant) |
||
933 | func_len = exp.X_add_number; |
||
934 | else |
||
935 | as_bad (_(".seh_endproc in a different section from .seh_proc")); |
||
936 | |||
937 | if (c->endprologue_addr) |
||
938 | { |
||
939 | exp.X_op = O_subtract; |
||
940 | exp.X_add_symbol = c->endprologue_addr; |
||
941 | exp.X_op_symbol = c->start_addr; |
||
942 | exp.X_add_number = 0; |
||
943 | |||
944 | if (resolve_expression (&exp) && exp.X_op == O_constant) |
||
945 | prol_len = exp.X_add_number; |
||
946 | else |
||
947 | as_bad (_(".seh_endprologue in a different section from .seh_proc")); |
||
948 | } |
||
949 | |||
950 | /* Both function and prologue are in units of instructions. */ |
||
951 | func_len >>= (c->use_instruction_32 ? 2 : 1); |
||
952 | prol_len >>= (c->use_instruction_32 ? 2 : 1); |
||
953 | |||
954 | /* Assemble the second word of the pdata. */ |
||
955 | val = prol_len & 0xff; |
||
956 | val |= (func_len & 0x3fffff) << 8; |
||
957 | if (c->use_instruction_32) |
||
958 | val |= 0x40000000U; |
||
959 | if (c->handler_written) |
||
960 | val |= 0x80000000U; |
||
961 | out_four (val); |
||
962 | } |
||
963 | |||
964 | /* Write out pdata for one function. */ |
||
965 | |||
966 | static void |
||
967 | write_function_pdata (seh_context *c) |
||
968 | { |
||
969 | expressionS exp; |
||
970 | segT save_seg = now_seg; |
||
971 | int save_subseg = now_subseg; |
||
972 | memset (&exp, 0, sizeof (expressionS)); |
||
973 | switch_pdata (c->code_seg); |
||
974 | |||
975 | switch (seh_get_target_kind ()) |
||
976 | { |
||
977 | case seh_kind_x64: |
||
978 | exp.X_op = O_symbol_rva; |
||
979 | exp.X_add_number = 0; |
||
980 | |||
981 | exp.X_add_symbol = c->start_addr; |
||
982 | emit_expr (&exp, 4); |
||
983 | exp.X_op = O_symbol_rva; |
||
984 | exp.X_add_number = 0; |
||
985 | exp.X_add_symbol = c->end_addr; |
||
986 | emit_expr (&exp, 4); |
||
987 | exp.X_op = O_symbol_rva; |
||
988 | exp.X_add_number = 0; |
||
989 | exp.X_add_symbol = c->xdata_addr; |
||
990 | emit_expr (&exp, 4); |
||
991 | break; |
||
992 | |||
993 | case seh_kind_mips: |
||
994 | exp.X_op = O_symbol; |
||
995 | exp.X_add_number = 0; |
||
996 | |||
997 | exp.X_add_symbol = c->start_addr; |
||
998 | emit_expr (&exp, 4); |
||
999 | exp.X_add_symbol = c->end_addr; |
||
1000 | emit_expr (&exp, 4); |
||
1001 | |||
1002 | emit_expr (&c->handler, 4); |
||
1003 | emit_expr (&c->handler_data, 4); |
||
1004 | |||
1005 | exp.X_add_symbol = (c->endprologue_addr |
||
1006 | ? c->endprologue_addr |
||
1007 | : c->start_addr); |
||
1008 | emit_expr (&exp, 4); |
||
1009 | break; |
||
1010 | |||
1011 | case seh_kind_arm: |
||
1012 | seh_arm_write_function_pdata (c); |
||
1013 | break; |
||
1014 | |||
1015 | default: |
||
1016 | abort (); |
||
1017 | } |
||
1018 | |||
1019 | subseg_set (save_seg, save_subseg); |
||
1020 | }><>><>><>>>=>=>=>>>=>>>>>> |