Commit | Line | Data |
---|---|---|
54cfded0 AM |
1 | /* dw2gencfi.c - Support for generating Dwarf2 CFI information. |
2 | Copyright 2003 Free Software Foundation, Inc. | |
3 | Contributed by Michal Ludvig <mludvig@suse.cz> | |
4 | ||
5 | This file is part of GAS, the GNU Assembler. | |
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 2, 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, 59 Temple Place - Suite 330, Boston, MA | |
20 | 02111-1307, USA. */ | |
21 | ||
54cfded0 AM |
22 | #include "as.h" |
23 | #include "dw2gencfi.h" | |
24 | ||
a4447b93 RH |
25 | |
26 | /* We re-use DWARF2_LINE_MIN_INSN_LENGTH for the code alignment field | |
27 | of the CIE. Default to 1 if not otherwise specified. */ | |
28 | #ifndef DWARF2_LINE_MIN_INSN_LENGTH | |
29 | # define DWARF2_LINE_MIN_INSN_LENGTH 1 | |
30 | #endif | |
31 | ||
32 | /* If TARGET_USE_CFIPOP is defined, it is required that the target | |
33 | provide the following definitions. Otherwise provide them to | |
34 | allow compilation to continue. */ | |
35 | #ifndef TARGET_USE_CFIPOP | |
36 | # ifndef DWARF2_DEFAULT_RETURN_COLUMN | |
37 | # define DWARF2_DEFAULT_RETURN_COLUMN 0 | |
38 | # endif | |
39 | # ifndef DWARF2_CIE_DATA_ALIGNMENT | |
40 | # define DWARF2_CIE_DATA_ALIGNMENT 1 | |
41 | # endif | |
42 | #endif | |
43 | ||
44 | #ifndef tc_cfi_frame_initial_instructions | |
45 | # define tc_cfi_frame_initial_instructions() ((void)0) | |
46 | #endif | |
47 | ||
48 | ||
49 | struct cfi_insn_data | |
39b82151 | 50 | { |
a4447b93 RH |
51 | struct cfi_insn_data *next; |
52 | int insn; | |
53 | union { | |
54 | struct { | |
55 | unsigned reg; | |
56 | offsetT offset; | |
57 | } ri; | |
58 | ||
59 | struct { | |
60 | unsigned reg1; | |
61 | unsigned reg2; | |
62 | } rr; | |
63 | ||
64 | unsigned r; | |
65 | offsetT i; | |
66 | ||
67 | struct { | |
68 | symbolS *lab1; | |
69 | symbolS *lab2; | |
70 | } ll; | |
71 | } u; | |
39b82151 ML |
72 | }; |
73 | ||
a4447b93 | 74 | struct fde_entry |
39b82151 | 75 | { |
a4447b93 RH |
76 | struct fde_entry *next; |
77 | symbolS *start_address; | |
78 | symbolS *end_address; | |
79 | struct cfi_insn_data *data; | |
80 | struct cfi_insn_data **last; | |
81 | unsigned int return_column; | |
39b82151 ML |
82 | }; |
83 | ||
a4447b93 | 84 | struct cie_entry |
39b82151 | 85 | { |
a4447b93 RH |
86 | struct cie_entry *next; |
87 | symbolS *start_address; | |
88 | unsigned int return_column; | |
89 | struct cfi_insn_data *first, *last; | |
39b82151 ML |
90 | }; |
91 | ||
a4447b93 RH |
92 | |
93 | /* Current open FDE entry. */ | |
94 | static struct fde_entry *cur_fde_data; | |
95 | static symbolS *last_address; | |
96 | static offsetT cur_cfa_offset; | |
97 | ||
98 | /* List of FDE entries. */ | |
99 | static struct fde_entry *all_fde_data; | |
100 | static struct fde_entry **last_fde_data = &all_fde_data; | |
39b82151 ML |
101 | |
102 | /* List of CIEs so that they could be reused. */ | |
103 | static struct cie_entry *cie_root; | |
104 | ||
a4447b93 RH |
105 | \f |
106 | /* Construct a new FDE structure and add it to the end of the fde list. */ | |
54cfded0 | 107 | |
a4447b93 RH |
108 | static struct fde_entry * |
109 | alloc_fde_entry (void) | |
110 | { | |
111 | struct fde_entry *fde = xcalloc (1, sizeof (struct fde_entry)); | |
54cfded0 | 112 | |
a4447b93 RH |
113 | cur_fde_data = fde; |
114 | *last_fde_data = fde; | |
115 | last_fde_data = &fde->next; | |
54cfded0 | 116 | |
a4447b93 RH |
117 | fde->last = &fde->data; |
118 | fde->return_column = DWARF2_DEFAULT_RETURN_COLUMN; | |
119 | ||
120 | return fde; | |
121 | } | |
122 | ||
123 | /* The following functions are available for a backend to construct its | |
124 | own unwind information, usually from legacy unwind directives. */ | |
125 | ||
126 | /* Construct a new INSN structure and add it to the end of the insn list | |
127 | for the currently active FDE. */ | |
128 | ||
129 | static struct cfi_insn_data * | |
130 | alloc_cfi_insn_data (void) | |
54cfded0 | 131 | { |
a4447b93 RH |
132 | struct cfi_insn_data *insn = xcalloc (1, sizeof (struct cfi_insn_data)); |
133 | ||
134 | *cur_fde_data->last = insn; | |
135 | cur_fde_data->last = &insn->next; | |
54cfded0 | 136 | |
a4447b93 | 137 | return insn; |
54cfded0 AM |
138 | } |
139 | ||
a4447b93 RH |
140 | /* Construct a new FDE structure that begins at LABEL. */ |
141 | ||
142 | void | |
143 | cfi_new_fde (symbolS *label) | |
54cfded0 | 144 | { |
a4447b93 RH |
145 | struct fde_entry *fde = alloc_fde_entry (); |
146 | fde->start_address = label; | |
147 | last_address = label; | |
54cfded0 AM |
148 | } |
149 | ||
a4447b93 RH |
150 | /* End the currently open FDE. */ |
151 | ||
152 | void | |
153 | cfi_end_fde (symbolS *label) | |
54cfded0 | 154 | { |
a4447b93 RH |
155 | cur_fde_data->end_address = label; |
156 | cur_fde_data = NULL; | |
54cfded0 AM |
157 | } |
158 | ||
a4447b93 RH |
159 | /* Set the return column for the current FDE. */ |
160 | ||
161 | void | |
162 | cfi_set_return_column (unsigned regno) | |
54cfded0 | 163 | { |
a4447b93 RH |
164 | cur_fde_data->return_column = regno; |
165 | } | |
54cfded0 | 166 | |
a4447b93 | 167 | /* Add a CFI insn to advance the PC from the last address to LABEL. */ |
54cfded0 | 168 | |
a4447b93 RH |
169 | void |
170 | cfi_add_advance_loc (symbolS *label) | |
171 | { | |
172 | struct cfi_insn_data *insn = alloc_cfi_insn_data (); | |
7c0295b1 | 173 | |
a4447b93 RH |
174 | insn->insn = DW_CFA_advance_loc; |
175 | insn->u.ll.lab1 = last_address; | |
176 | insn->u.ll.lab2 = label; | |
54cfded0 | 177 | |
a4447b93 RH |
178 | last_address = label; |
179 | } | |
54cfded0 | 180 | |
a4447b93 RH |
181 | /* Add a DW_CFA_offset record to the CFI data. */ |
182 | ||
183 | void | |
184 | cfi_add_CFA_offset (unsigned regno, offsetT offset) | |
185 | { | |
186 | struct cfi_insn_data *insn = alloc_cfi_insn_data (); | |
187 | ||
188 | insn->insn = DW_CFA_offset; | |
189 | insn->u.ri.reg = regno; | |
190 | insn->u.ri.offset = offset; | |
191 | } | |
54cfded0 | 192 | |
a4447b93 | 193 | /* Add a DW_CFA_def_cfa record to the CFI data. */ |
54cfded0 | 194 | |
a4447b93 RH |
195 | void |
196 | cfi_add_CFA_def_cfa (unsigned regno, offsetT offset) | |
197 | { | |
198 | struct cfi_insn_data *insn = alloc_cfi_insn_data (); | |
199 | ||
200 | insn->insn = DW_CFA_def_cfa; | |
201 | insn->u.ri.reg = regno; | |
202 | insn->u.ri.offset = offset; | |
54cfded0 | 203 | |
a4447b93 | 204 | cur_cfa_offset = offset; |
54cfded0 AM |
205 | } |
206 | ||
a4447b93 RH |
207 | /* Add a DW_CFA_register record to the CFI data. */ |
208 | ||
209 | void | |
210 | cfi_add_CFA_register (unsigned reg1, unsigned reg2) | |
54cfded0 | 211 | { |
a4447b93 RH |
212 | struct cfi_insn_data *insn = alloc_cfi_insn_data (); |
213 | ||
214 | insn->insn = DW_CFA_register; | |
215 | insn->u.rr.reg1 = reg1; | |
216 | insn->u.rr.reg2 = reg2; | |
54cfded0 AM |
217 | } |
218 | ||
a4447b93 RH |
219 | /* Add a DW_CFA_def_cfa_register record to the CFI data. */ |
220 | ||
221 | void | |
222 | cfi_add_CFA_def_cfa_register (unsigned regno) | |
54cfded0 | 223 | { |
a4447b93 RH |
224 | struct cfi_insn_data *insn = alloc_cfi_insn_data (); |
225 | ||
226 | insn->insn = DW_CFA_def_cfa_register; | |
227 | insn->u.r = regno; | |
54cfded0 AM |
228 | } |
229 | ||
a4447b93 RH |
230 | /* Add a DW_CFA_def_cfa_offset record to the CFI data. */ |
231 | ||
54cfded0 | 232 | void |
a4447b93 | 233 | cfi_add_CFA_def_cfa_offset (offsetT offset) |
54cfded0 | 234 | { |
a4447b93 RH |
235 | struct cfi_insn_data *insn = alloc_cfi_insn_data (); |
236 | ||
237 | insn->insn = DW_CFA_def_cfa_offset; | |
238 | insn->u.i = offset; | |
54cfded0 | 239 | |
a4447b93 RH |
240 | cur_cfa_offset = offset; |
241 | } | |
54cfded0 | 242 | |
a4447b93 RH |
243 | \f |
244 | /* Parse CFI assembler directives. */ | |
54cfded0 | 245 | |
a4447b93 RH |
246 | static void dot_cfi (int); |
247 | static void dot_cfi_startproc (int); | |
248 | static void dot_cfi_endproc (int); | |
54cfded0 | 249 | |
a4447b93 RH |
250 | /* Fake CFI type; outside the byte range of any real CFI insn. */ |
251 | #define CFI_adjust_cfa_offset 0x100 | |
54cfded0 | 252 | |
a4447b93 RH |
253 | const pseudo_typeS cfi_pseudo_table[] = |
254 | { | |
255 | { "cfi_startproc", dot_cfi_startproc, 0 }, | |
256 | { "cfi_endproc", dot_cfi_endproc, 0 }, | |
257 | { "cfi_def_cfa", dot_cfi, DW_CFA_def_cfa }, | |
258 | { "cfi_def_cfa_register", dot_cfi, DW_CFA_def_cfa_register }, | |
259 | { "cfi_def_cfa_offset", dot_cfi, DW_CFA_def_cfa_offset }, | |
260 | { "cfi_adjust_cfa_offset", dot_cfi, CFI_adjust_cfa_offset }, | |
261 | { "cfi_offset", dot_cfi, DW_CFA_offset }, | |
262 | { "cfi_register", dot_cfi, DW_CFA_register }, | |
263 | { NULL, NULL, 0 } | |
264 | }; | |
54cfded0 AM |
265 | |
266 | static void | |
a4447b93 | 267 | cfi_parse_separator (void) |
54cfded0 | 268 | { |
a4447b93 RH |
269 | SKIP_WHITESPACE (); |
270 | if (*input_line_pointer == ',') | |
271 | input_line_pointer++; | |
272 | else | |
273 | as_bad (_("missing separator")); | |
54cfded0 AM |
274 | } |
275 | ||
a4447b93 RH |
276 | static unsigned |
277 | cfi_parse_reg (void) | |
54cfded0 | 278 | { |
a4447b93 RH |
279 | int regno; |
280 | expressionS exp; | |
281 | ||
282 | #ifdef tc_regname_to_dw2regnum | |
283 | SKIP_WHITESPACE (); | |
284 | if (is_name_beginner (*input_line_pointer) | |
285 | || (*input_line_pointer == '%' | |
286 | && is_name_beginner (*++input_line_pointer))) | |
287 | { | |
288 | char *name, c; | |
289 | ||
290 | name = input_line_pointer; | |
291 | c = get_symbol_end (); | |
292 | ||
293 | if ((regno = tc_regname_to_dw2regnum (name)) < 0) | |
294 | { | |
295 | as_bad (_("bad register expression")); | |
296 | regno = 0; | |
297 | } | |
54cfded0 | 298 | |
a4447b93 RH |
299 | *input_line_pointer = c; |
300 | return regno; | |
301 | } | |
302 | #endif | |
303 | ||
304 | expression (&exp); | |
305 | switch (exp.X_op) | |
54cfded0 | 306 | { |
a4447b93 RH |
307 | case O_register: |
308 | case O_constant: | |
309 | regno = exp.X_add_number; | |
310 | break; | |
311 | ||
312 | default: | |
313 | as_bad (_("bad register expression")); | |
314 | regno = 0; | |
315 | break; | |
54cfded0 AM |
316 | } |
317 | ||
a4447b93 RH |
318 | return regno; |
319 | } | |
320 | ||
321 | static offsetT | |
322 | cfi_parse_const (void) | |
323 | { | |
324 | return get_absolute_expression (); | |
54cfded0 AM |
325 | } |
326 | ||
327 | static void | |
a4447b93 | 328 | dot_cfi (int arg) |
54cfded0 | 329 | { |
a4447b93 RH |
330 | offsetT offset; |
331 | unsigned reg1, reg2; | |
54cfded0 | 332 | |
a4447b93 | 333 | if (!cur_fde_data) |
54cfded0 AM |
334 | { |
335 | as_bad (_("CFI instruction used without previous .cfi_startproc")); | |
336 | return; | |
337 | } | |
338 | ||
a4447b93 RH |
339 | /* If the last address was not at the current PC, advance to current. */ |
340 | if (symbol_get_frag (last_address) != frag_now | |
341 | || S_GET_VALUE (last_address) != frag_now_fix ()) | |
342 | cfi_add_advance_loc (symbol_temp_new_now ()); | |
54cfded0 AM |
343 | |
344 | switch (arg) | |
345 | { | |
346 | /* Instructions that take two arguments (register, integer). */ | |
a4447b93 RH |
347 | case DW_CFA_offset: |
348 | case DW_CFA_def_cfa: | |
349 | reg1 = cfi_parse_reg (); | |
350 | cfi_parse_separator (); | |
351 | offset = cfi_parse_const (); | |
352 | ||
353 | if (arg == DW_CFA_def_cfa) | |
354 | cfi_add_CFA_def_cfa (reg1, offset); | |
355 | else | |
356 | cfi_add_CFA_offset (reg1, offset); | |
54cfded0 AM |
357 | break; |
358 | ||
a4447b93 RH |
359 | /* Instructions that take two arguments (register, register). */ |
360 | case DW_CFA_register: | |
361 | reg1 = cfi_parse_reg (); | |
362 | cfi_parse_separator (); | |
363 | reg2 = cfi_parse_reg (); | |
364 | ||
365 | cfi_add_CFA_register (reg1, reg2); | |
39b82151 ML |
366 | break; |
367 | ||
54cfded0 | 368 | /* Instructions that take one register argument. */ |
a4447b93 RH |
369 | case DW_CFA_def_cfa_register: |
370 | reg1 = cfi_parse_reg (); | |
371 | cfi_add_CFA_def_cfa_register (reg1); | |
54cfded0 AM |
372 | break; |
373 | ||
374 | /* Instructions that take one integer argument. */ | |
a4447b93 RH |
375 | case DW_CFA_def_cfa_offset: |
376 | offset = cfi_parse_const (); | |
377 | cfi_add_CFA_def_cfa_offset (offset); | |
54cfded0 AM |
378 | break; |
379 | ||
380 | /* Special handling for pseudo-instruction. */ | |
381 | case CFI_adjust_cfa_offset: | |
a4447b93 RH |
382 | offset = cfi_parse_const (); |
383 | cfi_add_CFA_def_cfa_offset (cur_cfa_offset + offset); | |
54cfded0 AM |
384 | break; |
385 | ||
386 | default: | |
a4447b93 | 387 | abort (); |
54cfded0 | 388 | } |
54cfded0 | 389 | |
a4447b93 | 390 | demand_empty_rest_of_line (); |
54cfded0 AM |
391 | } |
392 | ||
393 | static void | |
a4447b93 | 394 | dot_cfi_startproc (int ignored ATTRIBUTE_UNUSED) |
54cfded0 | 395 | { |
a4447b93 | 396 | int simple = 0; |
39b82151 | 397 | |
a4447b93 | 398 | if (cur_fde_data) |
54cfded0 AM |
399 | { |
400 | as_bad (_("previous CFI entry not closed (missing .cfi_endproc)")); | |
401 | return; | |
402 | } | |
403 | ||
a4447b93 | 404 | cfi_new_fde (symbol_temp_new_now ()); |
39b82151 | 405 | |
a4447b93 RH |
406 | SKIP_WHITESPACE (); |
407 | if (is_name_beginner (*input_line_pointer)) | |
408 | { | |
409 | char *name, c; | |
54cfded0 | 410 | |
a4447b93 RH |
411 | name = input_line_pointer; |
412 | c = get_symbol_end (); | |
54cfded0 | 413 | |
a4447b93 RH |
414 | if (strcmp (name, "simple") == 0) |
415 | { | |
416 | simple = 1; | |
417 | *input_line_pointer = c; | |
418 | } | |
419 | else | |
420 | input_line_pointer = name; | |
421 | } | |
422 | demand_empty_rest_of_line (); | |
423 | ||
424 | if (!simple) | |
39b82151 | 425 | tc_cfi_frame_initial_instructions (); |
54cfded0 AM |
426 | } |
427 | ||
a4447b93 RH |
428 | static void |
429 | dot_cfi_endproc (int ignored ATTRIBUTE_UNUSED) | |
430 | { | |
431 | if (! cur_fde_data) | |
432 | { | |
433 | as_bad (_(".cfi_endproc without corresponding .cfi_startproc")); | |
434 | return; | |
435 | } | |
54cfded0 | 436 | |
a4447b93 RH |
437 | cfi_end_fde (symbol_temp_new_now ()); |
438 | } | |
39b82151 | 439 | |
a4447b93 RH |
440 | \f |
441 | /* Emit a single byte into the current segment. */ | |
54cfded0 | 442 | |
a4447b93 RH |
443 | static inline void |
444 | out_one (int byte) | |
54cfded0 | 445 | { |
a4447b93 RH |
446 | FRAG_APPEND_1_CHAR (byte); |
447 | } | |
54cfded0 | 448 | |
a4447b93 | 449 | /* Emit a two-byte word into the current segment. */ |
54cfded0 | 450 | |
a4447b93 RH |
451 | static inline void |
452 | out_two (int data) | |
453 | { | |
454 | md_number_to_chars (frag_more (2), data, 2); | |
455 | } | |
54cfded0 | 456 | |
a4447b93 | 457 | /* Emit a four byte word into the current segment. */ |
54cfded0 | 458 | |
a4447b93 RH |
459 | static inline void |
460 | out_four (int data) | |
461 | { | |
462 | md_number_to_chars (frag_more (4), data, 4); | |
463 | } | |
464 | ||
465 | /* Emit an unsigned "little-endian base 128" number. */ | |
54cfded0 | 466 | |
a4447b93 RH |
467 | static void |
468 | out_uleb128 (addressT value) | |
469 | { | |
470 | output_leb128 (frag_more (sizeof_leb128 (value, 0)), value, 0); | |
54cfded0 AM |
471 | } |
472 | ||
a4447b93 RH |
473 | /* Emit an unsigned "little-endian base 128" number. */ |
474 | ||
475 | static void | |
476 | out_sleb128 (offsetT value) | |
54cfded0 | 477 | { |
a4447b93 RH |
478 | output_leb128 (frag_more (sizeof_leb128 (value, 1)), value, 1); |
479 | } | |
54cfded0 | 480 | |
a4447b93 RH |
481 | static void |
482 | output_cfi_insn (struct cfi_insn_data *insn) | |
483 | { | |
484 | offsetT offset; | |
485 | unsigned int regno; | |
54cfded0 | 486 | |
a4447b93 | 487 | switch (insn->insn) |
54cfded0 | 488 | { |
a4447b93 RH |
489 | case DW_CFA_advance_loc: |
490 | { | |
491 | symbolS *from = insn->u.ll.lab1; | |
492 | symbolS *to = insn->u.ll.lab2; | |
493 | ||
494 | if (symbol_get_frag (to) == symbol_get_frag (from)) | |
495 | { | |
496 | addressT delta = S_GET_VALUE (to) - S_GET_VALUE (from); | |
497 | addressT scaled = delta / DWARF2_LINE_MIN_INSN_LENGTH; | |
498 | ||
499 | if (scaled <= 0x3F) | |
500 | out_one (DW_CFA_advance_loc + scaled); | |
501 | else if (delta <= 0xFF) | |
502 | { | |
503 | out_one (DW_CFA_advance_loc1); | |
504 | out_one (delta); | |
505 | } | |
506 | else if (delta <= 0xFFFF) | |
507 | { | |
508 | out_one (DW_CFA_advance_loc2); | |
509 | out_two (delta); | |
510 | } | |
511 | else | |
512 | { | |
513 | out_one (DW_CFA_advance_loc4); | |
514 | out_four (delta); | |
515 | } | |
516 | } | |
517 | else | |
518 | { | |
519 | expressionS exp; | |
520 | ||
521 | exp.X_op = O_subtract; | |
522 | exp.X_add_symbol = to; | |
523 | exp.X_op_symbol = from; | |
524 | exp.X_add_number = 0; | |
525 | ||
526 | /* The code in ehopt.c expects that one byte of the encoding | |
527 | is already allocated to the frag. This comes from the way | |
528 | that it scans the .eh_frame section looking first for the | |
529 | .byte DW_CFA_advance_loc4. */ | |
530 | frag_more (1); | |
531 | ||
532 | frag_var (rs_cfa, 4, 0, DWARF2_LINE_MIN_INSN_LENGTH << 3, | |
533 | make_expr_symbol (&exp), frag_now_fix () - 1, | |
534 | (char *) frag_now); | |
535 | } | |
536 | } | |
537 | break; | |
538 | ||
539 | case DW_CFA_def_cfa: | |
540 | offset = insn->u.ri.offset; | |
541 | if (offset < 0) | |
54cfded0 | 542 | { |
a4447b93 RH |
543 | out_one (DW_CFA_def_cfa_sf); |
544 | out_uleb128 (insn->u.ri.reg); | |
545 | out_uleb128 (offset); | |
54cfded0 AM |
546 | } |
547 | else | |
548 | { | |
a4447b93 RH |
549 | out_one (DW_CFA_def_cfa); |
550 | out_uleb128 (insn->u.ri.reg); | |
551 | out_uleb128 (offset); | |
54cfded0 AM |
552 | } |
553 | break; | |
554 | ||
a4447b93 RH |
555 | case DW_CFA_def_cfa_register: |
556 | out_one (DW_CFA_def_cfa_register); | |
557 | out_uleb128 (insn->u.i); | |
54cfded0 AM |
558 | break; |
559 | ||
a4447b93 RH |
560 | case DW_CFA_def_cfa_offset: |
561 | offset = insn->u.i; | |
562 | if (offset < 0) | |
563 | { | |
564 | out_one (DW_CFA_def_cfa_offset_sf); | |
565 | out_sleb128 (offset); | |
566 | } | |
567 | else | |
568 | { | |
569 | out_one (DW_CFA_def_cfa_offset); | |
570 | out_uleb128 (offset); | |
571 | } | |
54cfded0 AM |
572 | break; |
573 | ||
a4447b93 RH |
574 | case DW_CFA_offset: |
575 | regno = insn->u.ri.reg; | |
576 | offset = insn->u.ri.offset / DWARF2_CIE_DATA_ALIGNMENT; | |
577 | if (offset < 0) | |
578 | { | |
1233ae62 | 579 | out_one (DW_CFA_offset_extended_sf); |
a4447b93 RH |
580 | out_uleb128 (regno); |
581 | out_sleb128 (offset); | |
582 | } | |
583 | else if (regno <= 0x3F) | |
584 | { | |
585 | out_one (DW_CFA_offset + regno); | |
586 | out_uleb128 (offset); | |
587 | } | |
54cfded0 AM |
588 | else |
589 | { | |
a4447b93 RH |
590 | out_one (DW_CFA_offset_extended); |
591 | out_uleb128 (regno); | |
592 | out_uleb128 (offset); | |
54cfded0 | 593 | } |
54cfded0 AM |
594 | break; |
595 | ||
a4447b93 RH |
596 | case DW_CFA_register: |
597 | out_one (DW_CFA_register); | |
598 | out_uleb128 (insn->u.rr.reg1); | |
599 | out_uleb128 (insn->u.rr.reg2); | |
39b82151 ML |
600 | break; |
601 | ||
a4447b93 RH |
602 | case DW_CFA_nop: |
603 | out_one (DW_CFA_nop); | |
54cfded0 AM |
604 | break; |
605 | ||
606 | default: | |
a4447b93 | 607 | abort (); |
54cfded0 | 608 | } |
54cfded0 AM |
609 | } |
610 | ||
611 | static void | |
a4447b93 | 612 | output_cie (struct cie_entry *cie) |
54cfded0 | 613 | { |
a4447b93 | 614 | symbolS *after_size_address, *end_address; |
7c0295b1 | 615 | expressionS exp; |
a4447b93 RH |
616 | struct cfi_insn_data *i; |
617 | ||
618 | cie->start_address = symbol_temp_new_now (); | |
619 | after_size_address = symbol_temp_make (); | |
620 | end_address = symbol_temp_make (); | |
621 | ||
622 | exp.X_op = O_subtract; | |
623 | exp.X_add_symbol = end_address; | |
624 | exp.X_op_symbol = after_size_address; | |
625 | exp.X_add_number = 0; | |
626 | ||
627 | emit_expr (&exp, 4); /* Length */ | |
628 | symbol_set_value_now (after_size_address); | |
629 | out_four (0); /* CIE id */ | |
630 | out_one (DW_CIE_VERSION); /* Version */ | |
631 | out_one ('z'); /* Augmentation */ | |
632 | out_one ('R'); | |
633 | out_one (0); | |
634 | out_uleb128 (DWARF2_LINE_MIN_INSN_LENGTH); /* Code alignment */ | |
635 | out_sleb128 (DWARF2_CIE_DATA_ALIGNMENT); /* Data alignment */ | |
636 | out_one (cie->return_column); /* Return column */ | |
637 | out_uleb128 (1); /* Augmentation size */ | |
638 | out_one (DW_EH_PE_pcrel | DW_EH_PE_sdata4); | |
639 | ||
640 | if (cie->first) | |
641 | for (i = cie->first; i != cie->last; i = i->next) | |
642 | output_cfi_insn (i); | |
643 | ||
644 | frag_align (2, 0, 0); | |
645 | symbol_set_value_now (end_address); | |
646 | } | |
54cfded0 | 647 | |
a4447b93 RH |
648 | static void |
649 | output_fde (struct fde_entry *fde, struct cie_entry *cie, | |
650 | struct cfi_insn_data *first) | |
651 | { | |
652 | symbolS *after_size_address, *end_address; | |
653 | expressionS exp; | |
54cfded0 | 654 | |
a4447b93 RH |
655 | after_size_address = symbol_temp_make (); |
656 | end_address = symbol_temp_make (); | |
54cfded0 | 657 | |
a4447b93 RH |
658 | exp.X_op = O_subtract; |
659 | exp.X_add_symbol = end_address; | |
660 | exp.X_op_symbol = after_size_address; | |
661 | exp.X_add_number = 0; | |
662 | emit_expr (&exp, 4); /* Length */ | |
663 | symbol_set_value_now (after_size_address); | |
54cfded0 | 664 | |
a4447b93 RH |
665 | exp.X_add_symbol = after_size_address; |
666 | exp.X_op_symbol = cie->start_address; | |
667 | emit_expr (&exp, 4); /* CIE offset */ | |
668 | ||
669 | exp.X_add_symbol = fde->start_address; | |
670 | exp.X_op_symbol = symbol_temp_new_now (); | |
ed7d5d1a | 671 | emit_expr (&exp, 4); /* Code offset */ |
54cfded0 | 672 | |
a4447b93 RH |
673 | exp.X_add_symbol = fde->end_address; |
674 | exp.X_op_symbol = fde->start_address; /* Code length */ | |
675 | emit_expr (&exp, 4); | |
54cfded0 | 676 | |
a4447b93 | 677 | out_uleb128 (0); /* Augmentation size */ |
39b82151 | 678 | |
a4447b93 RH |
679 | for (; first; first = first->next) |
680 | output_cfi_insn (first); | |
39b82151 | 681 | |
a4447b93 RH |
682 | frag_align (2, 0, 0); |
683 | symbol_set_value_now (end_address); | |
684 | } | |
685 | ||
686 | static struct cie_entry * | |
687 | select_cie_for_fde (struct fde_entry *fde, struct cfi_insn_data **pfirst) | |
688 | { | |
689 | struct cfi_insn_data *i, *j; | |
690 | struct cie_entry *cie; | |
691 | ||
692 | for (cie = cie_root; cie; cie = cie->next) | |
39b82151 | 693 | { |
a4447b93 RH |
694 | if (cie->return_column != fde->return_column) |
695 | continue; | |
696 | for (i = cie->first, j = fde->data; | |
697 | i != cie->last && j != NULL; | |
698 | i = i->next, j = j->next) | |
39b82151 | 699 | { |
a4447b93 RH |
700 | if (i->insn != j->insn) |
701 | goto fail; | |
702 | switch (i->insn) | |
703 | { | |
704 | case DW_CFA_advance_loc: | |
705 | /* We reached the first advance in the FDE, but did not | |
706 | reach the end of the CIE list. */ | |
707 | goto fail; | |
708 | ||
709 | case DW_CFA_offset: | |
710 | case DW_CFA_def_cfa: | |
711 | if (i->u.ri.reg != j->u.ri.reg) | |
712 | goto fail; | |
713 | if (i->u.ri.offset != j->u.ri.offset) | |
714 | goto fail; | |
715 | break; | |
716 | ||
717 | case DW_CFA_register: | |
718 | if (i->u.rr.reg1 != j->u.rr.reg1) | |
719 | goto fail; | |
720 | if (i->u.rr.reg2 != j->u.rr.reg2) | |
721 | goto fail; | |
722 | break; | |
723 | ||
724 | case DW_CFA_def_cfa_register: | |
725 | if (i->u.r != j->u.r) | |
726 | goto fail; | |
727 | break; | |
728 | ||
729 | case DW_CFA_def_cfa_offset: | |
730 | if (i->u.i != j->u.i) | |
731 | goto fail; | |
732 | break; | |
733 | ||
734 | default: | |
735 | abort (); | |
736 | } | |
39b82151 | 737 | } |
a4447b93 RH |
738 | |
739 | /* Success if we reached the end of the CIE list, and we've either | |
740 | run out of FDE entries or we've encountered an advance. */ | |
741 | if (i == cie->last && (!j || j->insn == DW_CFA_advance_loc)) | |
39b82151 | 742 | { |
a4447b93 RH |
743 | *pfirst = j; |
744 | return cie; | |
39b82151 ML |
745 | } |
746 | ||
a4447b93 | 747 | fail:; |
54cfded0 AM |
748 | } |
749 | ||
a4447b93 RH |
750 | cie = xmalloc (sizeof (struct cie_entry)); |
751 | cie->next = cie_root; | |
752 | cie_root = cie; | |
753 | cie->return_column = fde->return_column; | |
754 | cie->first = fde->data; | |
54cfded0 | 755 | |
a4447b93 RH |
756 | for (i = cie->first; i ; i = i->next) |
757 | if (i->insn == DW_CFA_advance_loc) | |
758 | break; | |
54cfded0 | 759 | |
a4447b93 RH |
760 | cie->last = i; |
761 | *pfirst = i; | |
762 | ||
763 | output_cie (cie); | |
54cfded0 | 764 | |
a4447b93 | 765 | return cie; |
54cfded0 AM |
766 | } |
767 | ||
768 | void | |
a4447b93 | 769 | cfi_finish (void) |
54cfded0 | 770 | { |
a4447b93 RH |
771 | segT cfi_seg; |
772 | struct fde_entry *fde; | |
eafbc43f | 773 | int save_flag_traditional_format; |
54cfded0 | 774 | |
a4447b93 | 775 | if (cur_fde_data) |
54cfded0 | 776 | { |
a4447b93 RH |
777 | as_bad (_("open CFI at the end of file; missing .cfi_endproc directive")); |
778 | cur_fde_data->end_address = cur_fde_data->start_address; | |
54cfded0 | 779 | } |
54cfded0 | 780 | |
a4447b93 RH |
781 | if (all_fde_data == 0) |
782 | return; | |
54cfded0 | 783 | |
a4447b93 RH |
784 | /* Open .eh_frame section. */ |
785 | cfi_seg = subseg_new (".eh_frame", 0); | |
786 | #ifdef BFD_ASSEMBLER | |
787 | bfd_set_section_flags (stdoutput, cfi_seg, | |
757bc393 | 788 | SEC_ALLOC | SEC_LOAD | SEC_DATA | SEC_READONLY); |
a4447b93 RH |
789 | #endif |
790 | subseg_set (cfi_seg, 0); | |
791 | record_alignment (cfi_seg, 2); | |
54cfded0 | 792 | |
eafbc43f RH |
793 | /* Make sure check_eh_frame doesn't do anything with our output. */ |
794 | save_flag_traditional_format = flag_traditional_format; | |
795 | flag_traditional_format = 1; | |
796 | ||
a4447b93 RH |
797 | for (fde = all_fde_data; fde ; fde = fde->next) |
798 | { | |
799 | struct cfi_insn_data *first; | |
800 | struct cie_entry *cie; | |
801 | ||
802 | cie = select_cie_for_fde (fde, &first); | |
803 | output_fde (fde, cie, first); | |
804 | } | |
eafbc43f RH |
805 | |
806 | flag_traditional_format = save_flag_traditional_format; | |
54cfded0 | 807 | } |