Commit | Line | Data |
---|---|---|
284e0531 | 1 | /* seh pdata/xdata coff object file format |
681418c2 | 2 | Copyright 2009, 2010 |
284e0531 KT |
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 | ||
987499b2 KT |
24 | |
25 | /* Local data. */ | |
987499b2 KT |
26 | static seh_context *seh_ctx_cur = NULL; |
27 | ||
681418c2 RH |
28 | static segT xdata_seg; |
29 | static segT pdata_seg; | |
30 | static int xdata_subseg; | |
31 | ||
32 | static void write_function_xdata (seh_context *); | |
33 | static void write_function_pdata (seh_context *); | |
604ab327 | 34 | |
681418c2 | 35 | \f |
987499b2 | 36 | static void |
681418c2 | 37 | switch_xdata (int subseg) |
987499b2 | 38 | { |
681418c2 RH |
39 | if (xdata_seg == NULL) |
40 | { | |
41 | xdata_seg = subseg_new (".xdata", 0); | |
42 | bfd_set_section_flags (stdoutput, xdata_seg, | |
43 | ((SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA) | |
44 | & bfd_applicable_section_flags (stdoutput))); | |
45 | } | |
46 | subseg_set (xdata_seg, subseg); | |
987499b2 KT |
47 | } |
48 | ||
987499b2 | 49 | static void |
681418c2 | 50 | switch_pdata (void) |
987499b2 | 51 | { |
681418c2 RH |
52 | if (pdata_seg == NULL) |
53 | { | |
54 | pdata_seg = subseg_new (".pdata", 0); | |
55 | bfd_set_section_flags (stdoutput, pdata_seg, | |
56 | ((SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA) | |
57 | & bfd_applicable_section_flags (stdoutput))); | |
58 | } | |
59 | else | |
60 | subseg_set (pdata_seg, 0); | |
61 | } | |
62 | \f | |
63 | /* Parsing routines. */ | |
987499b2 | 64 | |
681418c2 RH |
65 | /* Return the style of SEH unwind info to generate. */ |
66 | ||
67 | static seh_kind | |
68 | seh_get_target_kind (void) | |
69 | { | |
70 | if (!stdoutput) | |
71 | return seh_kind_unknown; | |
72 | switch (bfd_get_arch (stdoutput)) | |
987499b2 | 73 | { |
681418c2 RH |
74 | case bfd_arch_arm: |
75 | case bfd_arch_powerpc: | |
76 | case bfd_arch_sh: | |
77 | return seh_kind_arm; | |
78 | case bfd_arch_i386: | |
79 | switch (bfd_get_mach (stdoutput)) | |
987499b2 | 80 | { |
681418c2 RH |
81 | case bfd_mach_x86_64: |
82 | case bfd_mach_x86_64_intel_syntax: | |
83 | return seh_kind_x64; | |
84 | default: | |
85 | break; | |
987499b2 | 86 | } |
681418c2 RH |
87 | /* FALL THROUGH. */ |
88 | case bfd_arch_mips: | |
89 | return seh_kind_mips; | |
90 | case bfd_arch_ia64: | |
91 | /* Should return seh_kind_x64. But not implemented yet. */ | |
92 | return seh_kind_unknown; | |
987499b2 KT |
93 | default: |
94 | break; | |
95 | } | |
681418c2 | 96 | return seh_kind_unknown; |
987499b2 KT |
97 | } |
98 | ||
681418c2 RH |
99 | /* Verify that we're in the context of a seh_proc. */ |
100 | ||
101 | static int | |
102 | verify_context (const char *directive) | |
987499b2 | 103 | { |
681418c2 | 104 | if (seh_ctx_cur == NULL) |
987499b2 | 105 | { |
681418c2 RH |
106 | as_bad (_("%s used outside of .seh_proc block"), directive); |
107 | ignore_rest_of_line (); | |
108 | return 0; | |
987499b2 | 109 | } |
681418c2 RH |
110 | return 1; |
111 | } | |
987499b2 | 112 | |
681418c2 | 113 | /* Similar, except we also verify the appropriate target. */ |
987499b2 | 114 | |
681418c2 RH |
115 | static int |
116 | verify_context_and_target (const char *directive, seh_kind target) | |
117 | { | |
118 | if (seh_get_target_kind () != target) | |
987499b2 | 119 | { |
681418c2 RH |
120 | as_warn (_("%s ignored for this target"), directive); |
121 | ignore_rest_of_line (); | |
122 | return 0; | |
123 | } | |
124 | return verify_context (directive); | |
125 | } | |
126 | ||
127 | /* Skip whitespace and a comma. Error if the comma is not seen. */ | |
128 | ||
129 | static int | |
130 | skip_whitespace_and_comma (int required) | |
131 | { | |
132 | SKIP_WHITESPACE (); | |
133 | if (*input_line_pointer == ',') | |
134 | { | |
135 | input_line_pointer++; | |
136 | SKIP_WHITESPACE (); | |
137 | return 1; | |
987499b2 | 138 | } |
681418c2 RH |
139 | else if (required) |
140 | { | |
141 | as_bad (_("missing separator")); | |
142 | ignore_rest_of_line (); | |
143 | } | |
144 | else | |
145 | demand_empty_rest_of_line (); | |
146 | return 0; | |
987499b2 KT |
147 | } |
148 | ||
681418c2 | 149 | /* Mark current context to use 32-bit instruction (arm). */ |
1e17085d | 150 | |
987499b2 | 151 | static void |
681418c2 | 152 | obj_coff_seh_32 (int what) |
987499b2 | 153 | { |
681418c2 RH |
154 | if (!verify_context_and_target ((what ? ".seh_32" : ".seh_no32"), |
155 | seh_kind_arm)) | |
156 | return; | |
604ab327 | 157 | |
681418c2 RH |
158 | seh_ctx_cur->use_instruction_32 = (what ? 1 : 0); |
159 | demand_empty_rest_of_line (); | |
160 | } | |
161 | ||
162 | /* Set for current context the handler and optional data (arm). */ | |
163 | ||
164 | static void | |
165 | obj_coff_seh_eh (int what ATTRIBUTE_UNUSED) | |
166 | { | |
167 | if (!verify_context_and_target (".seh_eh", seh_kind_arm)) | |
987499b2 | 168 | return; |
681418c2 RH |
169 | |
170 | /* Write block to .text if exception handler is set. */ | |
171 | seh_ctx_cur->handler_written = 1; | |
172 | emit_expr (&seh_ctx_cur->handler, 4); | |
173 | emit_expr (&seh_ctx_cur->handler_data, 4); | |
174 | ||
175 | demand_empty_rest_of_line (); | |
987499b2 KT |
176 | } |
177 | ||
681418c2 RH |
178 | /* Set for current context the default handler (x64). */ |
179 | ||
987499b2 | 180 | static void |
681418c2 | 181 | obj_coff_seh_handler (int what ATTRIBUTE_UNUSED) |
987499b2 | 182 | { |
681418c2 RH |
183 | char *symbol_name; |
184 | char name_end; | |
185 | ||
186 | if (!verify_context (".seh_handler")) | |
987499b2 | 187 | return; |
681418c2 RH |
188 | |
189 | if (*input_line_pointer == 0 || *input_line_pointer == '\n') | |
190 | { | |
191 | as_bad (_(".seh_handler requires a handler")); | |
192 | demand_empty_rest_of_line (); | |
193 | return; | |
194 | } | |
195 | ||
196 | SKIP_WHITESPACE (); | |
197 | ||
198 | if (*input_line_pointer == '@') | |
987499b2 | 199 | { |
681418c2 RH |
200 | symbol_name = input_line_pointer; |
201 | name_end = get_symbol_end (); | |
202 | ||
203 | seh_ctx_cur->handler.X_op = O_constant; | |
204 | seh_ctx_cur->handler.X_add_number = 0; | |
205 | ||
206 | if (strcasecmp (symbol_name, "@0") == 0 | |
207 | || strcasecmp (symbol_name, "@null") == 0) | |
208 | ; | |
209 | else if (strcasecmp (symbol_name, "@1") == 0) | |
210 | seh_ctx_cur->handler.X_add_number = 1; | |
211 | else | |
212 | as_bad (_("unknown constant value '%s' for handler"), symbol_name); | |
213 | ||
214 | *input_line_pointer = name_end; | |
987499b2 | 215 | } |
681418c2 RH |
216 | else |
217 | expression (&seh_ctx_cur->handler); | |
987499b2 | 218 | |
681418c2 RH |
219 | seh_ctx_cur->handler_data.X_op = O_constant; |
220 | seh_ctx_cur->handler_data.X_add_number = 0; | |
221 | seh_ctx_cur->handler_flags = 0; | |
222 | ||
223 | if (!skip_whitespace_and_comma (0)) | |
987499b2 KT |
224 | return; |
225 | ||
681418c2 | 226 | if (seh_get_target_kind () == seh_kind_x64) |
987499b2 | 227 | { |
681418c2 | 228 | do |
987499b2 | 229 | { |
681418c2 RH |
230 | symbol_name = input_line_pointer; |
231 | name_end = get_symbol_end (); | |
232 | ||
233 | if (strcasecmp (symbol_name, "@unwind") == 0) | |
234 | seh_ctx_cur->handler_flags |= UNW_FLAG_UHANDLER; | |
235 | else if (strcasecmp (symbol_name, "@except") == 0) | |
236 | seh_ctx_cur->handler_flags |= UNW_FLAG_EHANDLER; | |
237 | else | |
238 | as_bad (_(".seh_handler constant '%s' unknown"), symbol_name); | |
239 | ||
240 | *input_line_pointer = name_end; | |
987499b2 | 241 | } |
681418c2 RH |
242 | while (skip_whitespace_and_comma (0)); |
243 | } | |
244 | else | |
245 | { | |
246 | expression (&seh_ctx_cur->handler_data); | |
247 | demand_empty_rest_of_line (); | |
248 | ||
249 | if (seh_ctx_cur->handler_written) | |
250 | as_warn (_(".seh_handler after .seh_eh is ignored")); | |
251 | } | |
252 | } | |
253 | ||
254 | /* Switch to subsection for handler data for exception region (x64). */ | |
255 | ||
256 | static void | |
257 | obj_coff_seh_handlerdata (int what ATTRIBUTE_UNUSED) | |
258 | { | |
259 | if (!verify_context_and_target (".seh_handlerdata", seh_kind_x64)) | |
260 | return; | |
261 | demand_empty_rest_of_line (); | |
262 | ||
263 | switch_xdata (seh_ctx_cur->subsection + 1); | |
264 | } | |
265 | ||
266 | /* Mark end of current context. */ | |
267 | ||
268 | static void | |
269 | do_seh_endproc (void) | |
270 | { | |
271 | seh_ctx_cur->end_addr = symbol_temp_new_now (); | |
272 | ||
273 | write_function_xdata (seh_ctx_cur); | |
274 | write_function_pdata (seh_ctx_cur); | |
275 | seh_ctx_cur = NULL; | |
276 | } | |
277 | ||
278 | static void | |
279 | obj_coff_seh_endproc (int what ATTRIBUTE_UNUSED) | |
280 | { | |
281 | demand_empty_rest_of_line (); | |
282 | if (seh_ctx_cur == NULL) | |
283 | { | |
284 | as_bad (_(".seh_endproc used without .seh_proc")); | |
285 | return; | |
286 | } | |
287 | ||
288 | do_seh_endproc (); | |
289 | } | |
290 | ||
291 | /* Mark begin of new context. */ | |
292 | ||
293 | static void | |
294 | obj_coff_seh_proc (int what ATTRIBUTE_UNUSED) | |
295 | { | |
296 | char *symbol_name; | |
297 | char name_end; | |
298 | ||
299 | if (seh_ctx_cur != NULL) | |
300 | { | |
301 | as_bad (_("previous SEH entry not closed (missing .seh_endproc)")); | |
302 | do_seh_endproc (); | |
303 | } | |
304 | ||
305 | if (*input_line_pointer == 0 || *input_line_pointer == '\n') | |
306 | { | |
307 | as_bad (_(".seh_proc requires function label name")); | |
308 | demand_empty_rest_of_line (); | |
309 | return; | |
310 | } | |
311 | ||
312 | seh_ctx_cur = XCNEW (seh_context); | |
313 | ||
314 | if (seh_get_target_kind () == seh_kind_x64) | |
315 | { | |
316 | seh_ctx_cur->subsection = xdata_subseg; | |
317 | xdata_subseg += 2; | |
987499b2 | 318 | } |
681418c2 RH |
319 | |
320 | SKIP_WHITESPACE (); | |
321 | ||
322 | symbol_name = input_line_pointer; | |
323 | name_end = get_symbol_end (); | |
324 | seh_ctx_cur->func_name = xstrdup (symbol_name); | |
325 | *input_line_pointer = name_end; | |
326 | ||
327 | demand_empty_rest_of_line (); | |
328 | ||
329 | seh_ctx_cur->start_addr = symbol_temp_new_now (); | |
987499b2 KT |
330 | } |
331 | ||
681418c2 RH |
332 | /* Mark end of prologue for current context. */ |
333 | ||
334 | static void | |
335 | obj_coff_seh_endprologue (int what ATTRIBUTE_UNUSED) | |
336 | { | |
337 | if (!verify_context (".seh_endprologue")) | |
338 | return; | |
339 | demand_empty_rest_of_line (); | |
340 | ||
341 | if (seh_ctx_cur->endprologue_addr != NULL) | |
342 | as_warn (_("duplicate .seh_endprologue in .seh_proc block")); | |
343 | else | |
344 | seh_ctx_cur->endprologue_addr = symbol_temp_new_now (); | |
345 | } | |
346 | ||
347 | /* End-of-file hook. */ | |
348 | ||
987499b2 KT |
349 | void |
350 | obj_coff_seh_do_final (void) | |
351 | { | |
681418c2 | 352 | if (seh_ctx_cur != NULL) |
987499b2 | 353 | { |
681418c2 RH |
354 | as_bad (_("open SEH entry at end of file (missing .cfi_endproc)")); |
355 | do_seh_endproc (); | |
987499b2 KT |
356 | } |
357 | } | |
358 | ||
681418c2 RH |
359 | /* Enter a prologue element into current context (x64). */ |
360 | ||
987499b2 | 361 | static void |
681418c2 | 362 | seh_x64_make_prologue_element (int code, int info, offsetT off) |
987499b2 KT |
363 | { |
364 | seh_prologue_element *n; | |
604ab327 | 365 | |
987499b2 KT |
366 | if (seh_ctx_cur == NULL) |
367 | return; | |
368 | if (seh_ctx_cur->elems_count == seh_ctx_cur->elems_max) | |
369 | { | |
987499b2 | 370 | seh_ctx_cur->elems_max += 8; |
681418c2 RH |
371 | seh_ctx_cur->elems = XRESIZEVEC (seh_prologue_element, |
372 | seh_ctx_cur->elems, | |
373 | seh_ctx_cur->elems_max); | |
987499b2 | 374 | } |
681418c2 RH |
375 | |
376 | n = &seh_ctx_cur->elems[seh_ctx_cur->elems_count++]; | |
377 | n->code = code; | |
378 | n->info = info; | |
379 | n->off = off; | |
380 | n->pc_addr = symbol_temp_new_now (); | |
987499b2 KT |
381 | } |
382 | ||
681418c2 RH |
383 | /* Helper to read a register name from input stream (x64). */ |
384 | ||
987499b2 | 385 | static int |
681418c2 | 386 | seh_x64_read_reg (const char *directive, int kind) |
987499b2 | 387 | { |
681418c2 | 388 | static const char * const int_regs[16] = |
604ab327 NC |
389 | { "rax", "rcx", "rdx", "rbx", "rsp", "rbp","rsi","rdi", |
390 | "r8","r9","r10","r11","r12","r13","r14","r15" }; | |
681418c2 | 391 | static const char * const xmm_regs[16] = |
604ab327 NC |
392 | { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", |
393 | "xmm8", "xmm9", "xmm10","xmm11","xmm12","xmm13","xmm14","xmm15" }; | |
681418c2 RH |
394 | |
395 | const char * const *regs = NULL; | |
987499b2 KT |
396 | char name_end; |
397 | char *symbol_name = NULL; | |
398 | int i; | |
399 | ||
987499b2 | 400 | switch (kind) |
604ab327 NC |
401 | { |
402 | case 0: | |
604ab327 | 403 | case 1: |
681418c2 | 404 | regs = int_regs; |
604ab327 NC |
405 | break; |
406 | case 2: | |
681418c2 | 407 | regs = xmm_regs; |
604ab327 NC |
408 | break; |
409 | default: | |
410 | abort (); | |
411 | } | |
412 | ||
681418c2 | 413 | SKIP_WHITESPACE (); |
987499b2 KT |
414 | if (*input_line_pointer == '%') |
415 | ++input_line_pointer; | |
416 | symbol_name = input_line_pointer; | |
417 | name_end = get_symbol_end (); | |
604ab327 | 418 | |
987499b2 | 419 | for (i = 0; i < 16; i++) |
681418c2 | 420 | if (! strcasecmp (regs[i], symbol_name)) |
987499b2 | 421 | break; |
604ab327 | 422 | |
987499b2 | 423 | *input_line_pointer = name_end; |
987499b2 | 424 | |
681418c2 RH |
425 | /* Error if register not found, or EAX used as a frame pointer. */ |
426 | if (i == 16 || (kind == 0 && i == 0)) | |
604ab327 | 427 | { |
681418c2 RH |
428 | as_bad (_("invalid register for %s"), directive); |
429 | return -1; | |
604ab327 | 430 | } |
1e17085d | 431 | |
681418c2 | 432 | return i; |
987499b2 KT |
433 | } |
434 | ||
681418c2 RH |
435 | /* Add a register push-unwind token to the current context. */ |
436 | ||
987499b2 | 437 | static void |
681418c2 | 438 | obj_coff_seh_pushreg (int what ATTRIBUTE_UNUSED) |
987499b2 | 439 | { |
681418c2 RH |
440 | int reg; |
441 | ||
442 | if (!verify_context_and_target (".seh_pushreg", seh_kind_x64)) | |
443 | return; | |
444 | ||
445 | reg = seh_x64_read_reg (".seh_pushreg", 1); | |
987499b2 | 446 | demand_empty_rest_of_line (); |
681418c2 RH |
447 | |
448 | if (reg < 0) | |
449 | return; | |
450 | ||
451 | seh_x64_make_prologue_element (UWOP_PUSH_NONVOL, reg, 0); | |
987499b2 KT |
452 | } |
453 | ||
681418c2 RH |
454 | /* Add a register frame-unwind token to the current context. */ |
455 | ||
987499b2 | 456 | static void |
681418c2 | 457 | obj_coff_seh_pushframe (int what ATTRIBUTE_UNUSED) |
987499b2 | 458 | { |
681418c2 RH |
459 | if (!verify_context_and_target (".seh_pushframe", seh_kind_x64)) |
460 | return; | |
987499b2 | 461 | demand_empty_rest_of_line (); |
987499b2 | 462 | |
681418c2 | 463 | seh_x64_make_prologue_element (UWOP_PUSH_MACHFRAME, 0, 0); |
987499b2 KT |
464 | } |
465 | ||
681418c2 RH |
466 | /* Add a register save-unwind token to current context. */ |
467 | ||
987499b2 | 468 | static void |
681418c2 | 469 | obj_coff_seh_save (int what) |
987499b2 | 470 | { |
681418c2 RH |
471 | const char *directive = (what == 1 ? ".seh_savereg" : ".seh_savexmm"); |
472 | int code, reg, scale; | |
473 | offsetT off; | |
987499b2 | 474 | |
681418c2 RH |
475 | if (!verify_context_and_target (directive, seh_kind_x64)) |
476 | return; | |
987499b2 | 477 | |
681418c2 | 478 | reg = seh_x64_read_reg (directive, what); |
987499b2 | 479 | |
681418c2 RH |
480 | if (!skip_whitespace_and_comma (1)) |
481 | return; | |
987499b2 | 482 | |
681418c2 | 483 | off = get_absolute_expression (); |
987499b2 | 484 | demand_empty_rest_of_line (); |
987499b2 | 485 | |
681418c2 RH |
486 | if (reg < 0) |
487 | return; | |
488 | if (off < 0) | |
987499b2 | 489 | { |
681418c2 | 490 | as_bad (_("%s offset is negative"), directive); |
987499b2 KT |
491 | return; |
492 | } | |
493 | ||
681418c2 | 494 | scale = (what == 1 ? 8 : 16); |
987499b2 | 495 | |
681418c2 | 496 | if ((off & (scale - 1)) == 0 && off <= 0xffff * scale) |
987499b2 | 497 | { |
681418c2 RH |
498 | code = (what == 1 ? UWOP_SAVE_NONVOL : UWOP_SAVE_XMM128); |
499 | off /= scale; | |
987499b2 | 500 | } |
681418c2 RH |
501 | else if (off < 0xffffffff) |
502 | code = (what == 1 ? UWOP_SAVE_NONVOL_FAR : UWOP_SAVE_XMM128_FAR); | |
503 | else | |
604ab327 | 504 | { |
681418c2 | 505 | as_bad (_("%s offset out of range"), directive); |
604ab327 NC |
506 | return; |
507 | } | |
681418c2 RH |
508 | |
509 | seh_x64_make_prologue_element (code, reg, off); | |
987499b2 KT |
510 | } |
511 | ||
681418c2 RH |
512 | /* Add a stack-allocation token to current context. */ |
513 | ||
987499b2 | 514 | static void |
681418c2 | 515 | obj_coff_seh_stackalloc (int what ATTRIBUTE_UNUSED) |
987499b2 | 516 | { |
681418c2 RH |
517 | offsetT off; |
518 | int code, info; | |
604ab327 | 519 | |
681418c2 RH |
520 | if (!verify_context_and_target (".seh_stackalloc", seh_kind_x64)) |
521 | return; | |
1e17085d | 522 | |
681418c2 | 523 | off = get_absolute_expression (); |
987499b2 | 524 | demand_empty_rest_of_line (); |
987499b2 | 525 | |
681418c2 RH |
526 | if (off == 0) |
527 | return; | |
528 | if (off < 0) | |
604ab327 | 529 | { |
681418c2 | 530 | as_bad (_(".seh_stackalloc offset is negative")); |
604ab327 NC |
531 | return; |
532 | } | |
987499b2 | 533 | |
681418c2 RH |
534 | if ((off & 7) == 0 && off <= 128) |
535 | code = UWOP_ALLOC_SMALL, info = (off - 8) >> 3, off = 0; | |
536 | else if ((off & 7) == 0 && off <= 0xffff * 8) | |
537 | code = UWOP_ALLOC_LARGE, info = 0, off >>= 3; | |
538 | else if (off <= 0xffffffff) | |
539 | code = UWOP_ALLOC_LARGE, info = 1; | |
987499b2 | 540 | else |
604ab327 | 541 | { |
681418c2 | 542 | as_bad (_(".seh_stackalloc offset out of range")); |
604ab327 NC |
543 | return; |
544 | } | |
681418c2 RH |
545 | |
546 | seh_x64_make_prologue_element (code, info, off); | |
987499b2 KT |
547 | } |
548 | ||
681418c2 RH |
549 | /* Add a frame-pointer token to current context. */ |
550 | ||
987499b2 KT |
551 | static void |
552 | obj_coff_seh_setframe (int what ATTRIBUTE_UNUSED) | |
553 | { | |
681418c2 | 554 | offsetT off; |
987499b2 | 555 | int reg; |
987499b2 | 556 | |
681418c2 RH |
557 | if (!verify_context_and_target (".seh_setframe", seh_kind_x64)) |
558 | return; | |
559 | ||
560 | reg = seh_x64_read_reg (".seh_setframe", 0); | |
561 | ||
562 | if (!skip_whitespace_and_comma (1)) | |
563 | return; | |
564 | ||
565 | off = get_absolute_expression (); | |
566 | demand_empty_rest_of_line (); | |
567 | ||
568 | if (reg < 0) | |
569 | return; | |
570 | if (off < 0) | |
571 | as_bad (_(".seh_setframe offset is negative")); | |
572 | else if (off > 240) | |
573 | as_bad (_(".seh_setframe offset out of range")); | |
574 | else if (off & 15) | |
575 | as_bad (_(".seh_setframe offset not a multiple of 16")); | |
576 | else if (seh_ctx_cur->framereg != 0) | |
577 | as_bad (_("duplicate .seh_setframe in current .seh_proc")); | |
578 | else | |
604ab327 NC |
579 | { |
580 | seh_ctx_cur->framereg = reg; | |
581 | seh_ctx_cur->frameoff = off; | |
681418c2 | 582 | seh_x64_make_prologue_element (UWOP_SET_FPREG, 0, 0); |
604ab327 | 583 | } |
987499b2 | 584 | } |
681418c2 RH |
585 | \f |
586 | /* Data writing routines. */ | |
987499b2 | 587 | |
681418c2 | 588 | /* Output raw integers in 1, 2, or 4 bytes. */ |
987499b2 | 589 | |
681418c2 RH |
590 | static inline void |
591 | out_one (int byte) | |
987499b2 | 592 | { |
681418c2 | 593 | FRAG_APPEND_1_CHAR (byte); |
987499b2 KT |
594 | } |
595 | ||
681418c2 RH |
596 | static inline void |
597 | out_two (int data) | |
987499b2 | 598 | { |
681418c2 | 599 | md_number_to_chars (frag_more (2), data, 2); |
987499b2 KT |
600 | } |
601 | ||
681418c2 RH |
602 | static inline void |
603 | out_four (int data) | |
987499b2 | 604 | { |
681418c2 | 605 | md_number_to_chars (frag_more (4), data, 4); |
987499b2 KT |
606 | } |
607 | ||
681418c2 RH |
608 | /* Write out prologue data for x64. */ |
609 | ||
610 | static void | |
611 | seh_x64_write_prologue_data (const seh_context *c) | |
987499b2 | 612 | { |
681418c2 | 613 | int i; |
604ab327 | 614 | |
681418c2 RH |
615 | /* We have to store in reverse order. */ |
616 | for (i = c->elems_count - 1; i >= 0; --i) | |
617 | { | |
618 | const seh_prologue_element *e = c->elems + i; | |
619 | expressionS exp; | |
987499b2 | 620 | |
681418c2 RH |
621 | /* First comes byte offset in code. */ |
622 | exp.X_op = O_subtract; | |
623 | exp.X_add_symbol = e->pc_addr; | |
624 | exp.X_op_symbol = c->start_addr; | |
625 | exp.X_add_number = 0; | |
626 | emit_expr (&exp, 1); | |
987499b2 | 627 | |
681418c2 RH |
628 | /* Second comes code+info packed into a byte. */ |
629 | out_one ((e->info << 4) | e->code); | |
987499b2 | 630 | |
681418c2 | 631 | switch (e->code) |
987499b2 | 632 | { |
681418c2 RH |
633 | case UWOP_PUSH_NONVOL: |
634 | case UWOP_ALLOC_SMALL: | |
635 | case UWOP_SET_FPREG: | |
636 | case UWOP_PUSH_MACHFRAME: | |
637 | /* These have no extra data. */ | |
987499b2 | 638 | break; |
681418c2 RH |
639 | |
640 | case UWOP_ALLOC_LARGE: | |
641 | if (e->info) | |
642 | { | |
643 | case UWOP_SAVE_NONVOL_FAR: | |
644 | case UWOP_SAVE_XMM128_FAR: | |
645 | /* An unscaled 4 byte offset. */ | |
646 | out_four (e->off); | |
647 | break; | |
648 | } | |
649 | /* FALLTHRU */ | |
650 | ||
651 | case UWOP_SAVE_NONVOL: | |
652 | case UWOP_SAVE_XMM128: | |
653 | /* A scaled 2 byte offset. */ | |
654 | out_two (e->off); | |
655 | break; | |
656 | ||
657 | default: | |
658 | abort (); | |
987499b2 | 659 | } |
987499b2 | 660 | } |
987499b2 KT |
661 | } |
662 | ||
681418c2 RH |
663 | static int |
664 | seh_x64_size_prologue_data (const seh_context *c) | |
665 | { | |
666 | int i, ret = 0; | |
667 | ||
668 | for (i = c->elems_count - 1; i >= 0; --i) | |
669 | switch (c->elems[i].code) | |
670 | { | |
671 | case UWOP_PUSH_NONVOL: | |
672 | case UWOP_ALLOC_SMALL: | |
673 | case UWOP_SET_FPREG: | |
674 | case UWOP_PUSH_MACHFRAME: | |
675 | ret += 1; | |
676 | break; | |
987499b2 | 677 | |
681418c2 RH |
678 | case UWOP_SAVE_NONVOL: |
679 | case UWOP_SAVE_XMM128: | |
680 | ret += 2; | |
681 | break; | |
987499b2 | 682 | |
681418c2 RH |
683 | case UWOP_SAVE_NONVOL_FAR: |
684 | case UWOP_SAVE_XMM128_FAR: | |
685 | ret += 3; | |
686 | break; | |
987499b2 | 687 | |
681418c2 RH |
688 | case UWOP_ALLOC_LARGE: |
689 | ret += (c->elems[i].info ? 3 : 2); | |
690 | break; | |
987499b2 | 691 | |
681418c2 RH |
692 | default: |
693 | abort (); | |
694 | } | |
987499b2 | 695 | |
681418c2 | 696 | return ret; |
987499b2 KT |
697 | } |
698 | ||
681418c2 RH |
699 | /* Write out the xdata information for one function (x64). */ |
700 | ||
701 | static void | |
702 | seh_x64_write_function_xdata (seh_context *c) | |
987499b2 | 703 | { |
681418c2 RH |
704 | int flags, count_unwind_codes; |
705 | expressionS exp; | |
987499b2 | 706 | |
681418c2 RH |
707 | /* Set 4-byte alignment. */ |
708 | frag_align (2, 0, 0); | |
987499b2 | 709 | |
681418c2 RH |
710 | c->xdata_addr = symbol_temp_new_now (); |
711 | flags = c->handler_flags; | |
712 | count_unwind_codes = seh_x64_size_prologue_data (c); | |
987499b2 | 713 | |
681418c2 RH |
714 | /* ubyte:3 version, ubyte:5 flags. */ |
715 | out_one ((flags << 3) | 1); | |
987499b2 | 716 | |
681418c2 RH |
717 | /* Size of prologue. */ |
718 | if (c->endprologue_addr) | |
719 | { | |
720 | exp.X_op = O_subtract; | |
721 | exp.X_add_symbol = c->endprologue_addr; | |
722 | exp.X_op_symbol = c->start_addr; | |
723 | exp.X_add_number = 0; | |
724 | emit_expr (&exp, 1); | |
725 | } | |
726 | else | |
727 | out_one (0); | |
987499b2 | 728 | |
681418c2 RH |
729 | /* Number of slots (i.e. shorts) in the unwind codes array. */ |
730 | if (count_unwind_codes > 255) | |
731 | as_fatal (_("too much unwind data in this .seh_proc")); | |
732 | out_one (count_unwind_codes); | |
987499b2 | 733 | |
681418c2 RH |
734 | /* ubyte:4 frame-reg, ubyte:4 frame-reg-offset. */ |
735 | /* Note that frameoff is already a multiple of 16, and therefore | |
736 | the offset is already both scaled and shifted into place. */ | |
737 | out_one (c->frameoff | c->framereg); | |
604ab327 | 738 | |
681418c2 | 739 | seh_x64_write_prologue_data (c); |
987499b2 | 740 | |
681418c2 RH |
741 | /* We need to align prologue data. */ |
742 | if (count_unwind_codes & 1) | |
743 | out_two (0); | |
744 | ||
745 | if (flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER)) | |
604ab327 | 746 | { |
681418c2 RH |
747 | /* Force the use of segment-relative relocations instead of absolute |
748 | valued expressions. Don't adjust for constants (e.g. NULL). */ | |
749 | if (c->handler.X_op == O_symbol) | |
750 | c->handler.X_op = O_symbol_rva; | |
751 | emit_expr (&c->handler, 4); | |
604ab327 | 752 | } |
681418c2 RH |
753 | |
754 | /* Handler data will be tacked in here by subsections. */ | |
987499b2 KT |
755 | } |
756 | ||
681418c2 | 757 | /* Write out xdata for one function. */ |
987499b2 KT |
758 | |
759 | static void | |
681418c2 | 760 | write_function_xdata (seh_context *c) |
987499b2 | 761 | { |
681418c2 RH |
762 | segT save_seg = now_seg; |
763 | int save_subseg = now_subseg; | |
764 | ||
765 | /* MIPS, SH, ARM don't have xdata. */ | |
766 | if (seh_get_target_kind () != seh_kind_x64) | |
987499b2 | 767 | return; |
987499b2 | 768 | |
681418c2 | 769 | switch_xdata (c->subsection); |
987499b2 | 770 | |
681418c2 | 771 | seh_x64_write_function_xdata (c); |
1e17085d | 772 | |
681418c2 | 773 | subseg_set (save_seg, save_subseg); |
1e17085d KT |
774 | } |
775 | ||
681418c2 | 776 | /* Write pdata section data for one function (arm). */ |
987499b2 | 777 | |
681418c2 RH |
778 | static void |
779 | seh_arm_write_function_pdata (seh_context *c) | |
987499b2 | 780 | { |
681418c2 RH |
781 | expressionS exp; |
782 | unsigned int prol_len = 0, func_len = 0; | |
783 | unsigned int val; | |
604ab327 | 784 | |
681418c2 RH |
785 | /* Start address of the function. */ |
786 | exp.X_op = O_symbol; | |
787 | exp.X_add_symbol = c->start_addr; | |
788 | exp.X_add_number = 0; | |
789 | emit_expr (&exp, 4); | |
790 | ||
791 | exp.X_op = O_subtract; | |
792 | exp.X_add_symbol = c->end_addr; | |
793 | exp.X_op_symbol = c->start_addr; | |
794 | exp.X_add_number = 0; | |
795 | if (resolve_expression (&exp) && exp.X_op == O_constant) | |
796 | func_len = exp.X_add_number; | |
987499b2 | 797 | else |
681418c2 RH |
798 | as_bad (_(".seh_endproc in a different section from .seh_proc")); |
799 | ||
800 | if (c->endprologue_addr) | |
987499b2 | 801 | { |
681418c2 RH |
802 | exp.X_op = O_subtract; |
803 | exp.X_add_symbol = c->endprologue_addr; | |
804 | exp.X_op_symbol = c->start_addr; | |
805 | exp.X_add_number = 0; | |
806 | ||
807 | if (resolve_expression (&exp) && exp.X_op == O_constant) | |
808 | prol_len = exp.X_add_number; | |
809 | else | |
810 | as_bad (_(".seh_endprologue in a different section from .seh_proc")); | |
987499b2 | 811 | } |
681418c2 RH |
812 | |
813 | /* Both function and prologue are in units of instructions. */ | |
814 | func_len >>= (c->use_instruction_32 ? 2 : 1); | |
815 | prol_len >>= (c->use_instruction_32 ? 2 : 1); | |
816 | ||
817 | /* Assemble the second word of the pdata. */ | |
818 | val = prol_len & 0xff; | |
819 | val |= (func_len & 0x3fffff) << 8; | |
820 | if (c->use_instruction_32) | |
821 | val |= 0x40000000U; | |
822 | if (c->handler_written) | |
823 | val |= 0x80000000U; | |
824 | out_four (val); | |
987499b2 KT |
825 | } |
826 | ||
681418c2 RH |
827 | /* Write out pdata for one function. */ |
828 | ||
987499b2 | 829 | static void |
681418c2 | 830 | write_function_pdata (seh_context *c) |
987499b2 | 831 | { |
681418c2 RH |
832 | expressionS exp; |
833 | segT save_seg = now_seg; | |
834 | int save_subseg = now_subseg; | |
604ab327 | 835 | |
681418c2 RH |
836 | switch_pdata (); |
837 | ||
838 | switch (seh_get_target_kind ()) | |
987499b2 | 839 | { |
681418c2 RH |
840 | case seh_kind_x64: |
841 | exp.X_op = O_symbol_rva; | |
842 | exp.X_add_number = 0; | |
843 | ||
844 | exp.X_add_symbol = c->start_addr; | |
845 | emit_expr (&exp, 4); | |
846 | exp.X_add_symbol = c->end_addr; | |
847 | emit_expr (&exp, 4); | |
848 | exp.X_add_symbol = c->xdata_addr; | |
849 | emit_expr (&exp, 4); | |
850 | break; | |
987499b2 | 851 | |
681418c2 RH |
852 | case seh_kind_mips: |
853 | exp.X_op = O_symbol; | |
854 | exp.X_add_number = 0; | |
987499b2 | 855 | |
681418c2 RH |
856 | exp.X_add_symbol = c->start_addr; |
857 | emit_expr (&exp, 4); | |
858 | exp.X_add_symbol = c->end_addr; | |
859 | emit_expr (&exp, 4); | |
987499b2 | 860 | |
681418c2 RH |
861 | emit_expr (&c->handler, 4); |
862 | emit_expr (&c->handler_data, 4); | |
604ab327 | 863 | |
681418c2 RH |
864 | exp.X_add_symbol = (c->endprologue_addr |
865 | ? c->endprologue_addr | |
866 | : c->start_addr); | |
867 | emit_expr (&exp, 4); | |
868 | break; | |
987499b2 | 869 | |
681418c2 RH |
870 | case seh_kind_arm: |
871 | seh_arm_write_function_pdata (c); | |
872 | break; | |
604ab327 | 873 | |
681418c2 RH |
874 | default: |
875 | abort (); | |
987499b2 | 876 | } |
681418c2 RH |
877 | |
878 | subseg_set (save_seg, save_subseg); | |
987499b2 | 879 | } |