36a5722eeecd832febd15b252542be99b9fe997d
[binutils-gdb.git] / gas / dw2gencfi.c
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
22 #include <errno.h>
23 #include "as.h"
24 #include "dw2gencfi.h"
25
26 /* Current target config. */
27 static struct cfi_config current_config;
28
29 /* This is the main entry point to the CFI machinery. */
30 static void dot_cfi (int arg);
31
32 const pseudo_typeS cfi_pseudo_table[] =
33 {
34 { "cfi_verbose", dot_cfi, CFI_verbose },
35 { "cfi_startproc", dot_cfi, CFI_startproc },
36 { "cfi_endproc", dot_cfi, CFI_endproc },
37 { "cfi_def_cfa", dot_cfi, CFA_def_cfa },
38 { "cfi_def_cfa_register", dot_cfi, CFA_def_cfa_register },
39 { "cfi_def_cfa_offset", dot_cfi, CFA_def_cfa_offset },
40 { "cfi_adjust_cfa_offset", dot_cfi, CFI_adjust_cfa_offset },
41 { "cfi_offset", dot_cfi, CFA_offset },
42 { NULL, NULL, 0 }
43 };
44
45 static const char *
46 cfi_insn_str (enum cfi_insn insn)
47 {
48 switch (insn)
49 {
50 case CFA_nop:
51 return "CFA_nop";
52 case CFA_set_loc:
53 return "CFA_set_loc";
54 case CFA_advance_loc1:
55 return "CFA_advance_loc1";
56 case CFA_advance_loc2:
57 return "CFA_advance_loc2";
58 case CFA_advance_loc4:
59 return "CFA_advance_loc4";
60 case CFA_offset_extended:
61 return "CFA_offset_extended";
62 case CFA_resotre_extended:
63 return "CFA_resotre_extended";
64 case CFA_undefined:
65 return "CFA_undefined";
66 case CFA_same_value:
67 return "CFA_same_value";
68 case CFA_register:
69 return "CFA_register";
70 case CFA_remember_state:
71 return "CFA_remember_state";
72 case CFA_restore_state:
73 return "CFA_restore_state";
74 case CFA_def_cfa:
75 return "CFA_def_cfa";
76 case CFA_def_cfa_register:
77 return "CFA_def_cfa_register";
78 case CFA_def_cfa_offset:
79 return "CFA_def_cfa_offset";
80 case CFA_advance_loc:
81 return "CFA_advance_loc";
82 case CFA_offset:
83 return "CFA_offset";
84 case CFA_restore:
85 return "CFA_restore";
86 default:
87 break;
88 }
89
90 return "CFA_unknown";
91 }
92
93 struct cfi_data
94 {
95 enum cfi_insn insn;
96 long param[2];
97 struct cfi_data *next;
98 };
99
100 struct cfi_info
101 {
102 addressT start_address;
103 addressT end_address;
104 addressT last_address;
105 const char *labelname;
106 struct cfi_data *data;
107 struct cfi_info *next;
108 };
109
110 static struct cfi_info *cfi_info;
111
112 static struct cfi_data *
113 alloc_cfi_data (void)
114 {
115 return (struct cfi_data *) xcalloc (sizeof (struct cfi_info), 1);
116 }
117
118 static struct cfi_info *
119 alloc_cfi_info (void)
120 {
121 return (struct cfi_info *) xcalloc (sizeof (struct cfi_info), 1);
122 }
123
124 /* Parse arguments. */
125 static int
126 cfi_parse_arg (long *param, int resolvereg)
127 {
128 long value;
129 int retval = -1;
130 int nchars;
131
132 assert (param != NULL);
133 SKIP_WHITESPACE ();
134
135 if (sscanf (input_line_pointer, "%li%n", &value, &nchars) >= 1)
136 {
137 input_line_pointer += nchars;
138 retval = 1;
139 }
140 #ifdef tc_regname_to_dw2regnum
141 else if (resolvereg && (is_name_beginner (*input_line_pointer)))
142 {
143 char *name, c, *p;
144
145 name = input_line_pointer;
146 c = get_symbol_end ();
147 p = input_line_pointer;
148
149 if ((value = tc_regname_to_dw2regnum (name)) >= 0)
150 retval = 1;
151
152 *p = c;
153 }
154 #endif
155 else
156 as_bad (resolvereg ?
157 _("can't convert argument to a register number") :
158 _("can't convert argument to an integer"));
159
160 if (retval > 0)
161 *param = value;
162
163 SKIP_WHITESPACE ();
164 if (*input_line_pointer == ',')
165 {
166 input_line_pointer++;
167 SKIP_WHITESPACE ();
168 }
169
170 return retval;
171 }
172
173 static int
174 cfi_parse_reg (long *param)
175 {
176 return cfi_parse_arg (param, 1);
177 }
178
179 static int
180 cfi_parse_const (long *param)
181 {
182 return cfi_parse_arg (param, 0);
183 }
184
185 void
186 cfi_add_insn (enum cfi_insn insn, long param0, long param1)
187 {
188 struct cfi_data *data_ptr;
189
190 if (!cfi_info->data)
191 {
192 cfi_info->data = alloc_cfi_data ();
193 data_ptr = cfi_info->data;
194 }
195 else
196 {
197 data_ptr = cfi_info->data;
198
199 while (data_ptr && data_ptr->next)
200 data_ptr = data_ptr->next;
201
202 data_ptr->next = alloc_cfi_data ();
203
204 data_ptr = data_ptr->next;
205 }
206
207 data_ptr->insn = insn;
208 data_ptr->param[0] = param0;
209 data_ptr->param[1] = param1;
210 }
211
212 static void
213 cfi_advance_loc (void)
214 {
215 addressT curr_address = frag_now_fix ();
216 if (cfi_info->last_address == curr_address)
217 return;
218 cfi_add_insn (CFA_advance_loc,
219 (long) (curr_address - cfi_info->last_address), 0);
220 cfi_info->last_address = curr_address;
221 }
222
223 static long
224 get_current_offset (struct cfi_info *info)
225 {
226 long current_offset = 0;
227 struct cfi_data *data = info->data;
228
229 current_offset = 0;
230 while (data)
231 {
232 if (data->insn == CFA_def_cfa)
233 current_offset = data->param[1];
234 else if (data->insn == CFA_def_cfa_offset)
235 current_offset = data->param[0];
236 data = data->next;
237 }
238
239 return current_offset;
240 }
241
242 static void
243 cfi_make_insn (int arg)
244 {
245 long param[2] = { 0, 0 };
246
247 if (!cfi_info)
248 {
249 as_bad (_("CFI instruction used without previous .cfi_startproc"));
250 return;
251 }
252
253 cfi_advance_loc ();
254
255 switch (arg)
256 {
257 /* Instructions that take two arguments (register, integer). */
258 case CFA_offset:
259 case CFA_def_cfa:
260 if (cfi_parse_reg (&param[0]) < 0)
261 {
262 as_bad (_("first argument to %s is not a register"),
263 cfi_insn_str (arg));
264 return;
265 }
266 if (cfi_parse_const (&param[1]) < 0)
267 {
268 as_bad (_("second argument to %s is not a number"),
269 cfi_insn_str (arg));
270 return;
271 }
272 break;
273
274 /* Instructions that take one register argument. */
275 case CFA_def_cfa_register:
276 if (cfi_parse_reg (&param[0]) < 0)
277 {
278 as_bad (_("argument to %s is not a register"), cfi_insn_str (arg));
279 return;
280 }
281 break;
282
283 /* Instructions that take one integer argument. */
284 case CFA_def_cfa_offset:
285 if (cfi_parse_const (&param[0]) < 0)
286 {
287 as_bad (_("argument to %s is not a number"), cfi_insn_str (arg));
288 return;
289 }
290 break;
291
292 /* Special handling for pseudo-instruction. */
293 case CFI_adjust_cfa_offset:
294 if (cfi_parse_const (&param[0]) < 0)
295 {
296 as_bad (_("argument to %s is not a number"),
297 ".cfi_adjust_cfa_offset");
298 return;
299 }
300 param[0] += get_current_offset (cfi_info);
301 arg = CFA_def_cfa_offset;
302 break;
303
304 default:
305 as_bad (_("unknown CFI instruction %d (%s)"), arg, cfi_insn_str (arg));
306 return;
307 }
308 cfi_add_insn (arg, param[0], param[1]);
309 }
310
311 static symbolS *
312 cfi_get_label (void)
313 {
314 char symname[40], *symbase=".Llbl_cfi";
315 symbolS *symbolP;
316 unsigned int i = 0;
317
318 snprintf (symname, sizeof (symname), "%s_0x%lx",
319 symbase, (long) frag_now_fix ());
320 while ((symbolP = symbol_find (symname)))
321 {
322 if ((S_GET_VALUE (symbolP) == frag_now_fix ())
323 && (S_GET_SEGMENT (symbolP) == now_seg))
324 {
325 return symbolP;
326 }
327 snprintf (symname, sizeof (symname), "%s_0x%lx_%u",
328 symbase, (long) frag_now_fix (), i++);
329 }
330 symbolP = (symbolS *) local_symbol_make (symname, now_seg,
331 (valueT) frag_now_fix (),
332 frag_now);
333 return symbolP;
334 }
335
336 static void
337 dot_cfi_startproc (void)
338 {
339 if (cfi_info)
340 {
341 as_bad (_("previous CFI entry not closed (missing .cfi_endproc)"));
342 return;
343 }
344
345 cfi_info = alloc_cfi_info ();
346
347 cfi_info->start_address = frag_now_fix ();
348 cfi_info->last_address = cfi_info->start_address;
349 cfi_info->labelname = S_GET_NAME (cfi_get_label ());
350
351 #ifdef tc_cfi_frame_initial_instructions
352 tc_cfi_frame_initial_instructions ();
353 #endif
354 }
355
356 #define cfi_is_advance_insn(insn) \
357 ((insn >= CFA_set_loc && insn <= CFA_advance_loc4) \
358 || insn == CFA_advance_loc)
359
360 enum data_types
361 {
362 t_ascii = 0,
363 t_byte = 1,
364 t_half = 2,
365 t_long = 4,
366 t_quad = 8,
367 t_uleb128 = 0x10,
368 t_sleb128 = 0x11
369 };
370
371 /* Output CFI instructions to the file. */
372
373 static int
374 output_data (char **p, unsigned long *size, enum data_types type, long value)
375 {
376 char *ptr = *p;
377 unsigned int ret_size;
378
379 switch (type)
380 {
381 case t_byte:
382 ret_size = 1;
383 break;
384 case t_half:
385 ret_size = 2;
386 break;
387 case t_long:
388 ret_size = 4;
389 break;
390 case t_quad:
391 case t_uleb128:
392 case t_sleb128:
393 ret_size = 8;
394 break;
395 default:
396 as_warn (_("unknown type %d"), type);
397 return 0;
398 }
399
400 if (*size < ret_size)
401 {
402 as_bad (_("output_data buffer is too small"));
403 return 0;
404 }
405
406 switch (type)
407 {
408 case t_byte:
409 *ptr = (char) value;
410 if (verbose)
411 printf ("\t.byte\t0x%x\n", (unsigned char) *ptr);
412 break;
413 case t_half:
414 *(short *) ptr = (short) value & 0xFFFF;
415 if (verbose)
416 printf ("\t.half\t0x%x\n", (unsigned short) *ptr);
417 break;
418 case t_long:
419 *(int *) ptr = (int) value & 0xFFFFFFFF;
420 if (verbose)
421 printf ("\t.long\t0x%x\n", (unsigned int) *ptr);
422 break;
423 case t_quad:
424 *(long long *) ptr = (long long) value & 0xFFFFFFFF;
425 if (verbose)
426 printf ("\t.quad\t0x%x\n", (unsigned int) *ptr);
427 break;
428 case t_uleb128:
429 case t_sleb128:
430 ret_size = output_leb128 (ptr, value, type == t_sleb128);
431 if (verbose)
432 printf ("\t.%s\t0x%lx\n",
433 type == t_sleb128 ? "sleb128" : "uleb128",
434 value);
435 break;
436 default:
437 as_warn ("unknown type %d", type);
438 return 0;
439 }
440
441 *size -= ret_size;
442 *p += ret_size;
443
444 return ret_size;
445 }
446
447 static int
448 cfi_output_insn (struct cfi_data *data, char **buf, unsigned long *buf_size)
449 {
450 char **pbuf = buf, *orig_buf = *buf;
451 unsigned long size;
452
453 if (!data || !buf)
454 as_fatal (_("cfi_output_insn called with NULL pointer"));
455
456 switch (data->insn)
457 {
458 case CFA_advance_loc:
459 if (verbose)
460 printf ("\t# %s(%ld)\n", cfi_insn_str (data->insn),
461 data->param[0]);
462 if (data->param[0] <= 0x3F)
463 {
464 output_data (pbuf, buf_size, t_byte, CFA_advance_loc +
465 (data->param[0] / current_config.code_align));
466 }
467 else if (data->param[0] <= 0xFF)
468 {
469 output_data (pbuf, buf_size, t_byte, CFA_advance_loc1);
470 output_data (pbuf, buf_size, t_byte,
471 data->param[0] / current_config.code_align);
472 }
473 else if (data->param[0] <= 0xFFFF)
474 {
475 output_data (pbuf, buf_size, t_byte, CFA_advance_loc2);
476 output_data (pbuf, buf_size, t_half,
477 data->param[0] / current_config.code_align);
478 }
479 else
480 {
481 output_data (pbuf, buf_size, t_byte, CFA_advance_loc4);
482 output_data (pbuf, buf_size, t_long,
483 data->param[0] / current_config.code_align);
484 }
485 break;
486
487 case CFA_def_cfa:
488 if (verbose)
489 printf ("\t# CFA_def_cfa(%ld,%ld)\n", data->param[0], data->param[1]);
490 output_data (pbuf, buf_size, t_byte, CFA_def_cfa);
491 output_data (pbuf, buf_size, t_uleb128, data->param[0]);
492 output_data (pbuf, buf_size, t_uleb128, data->param[1]);
493 break;
494
495 case CFA_def_cfa_register:
496 case CFA_def_cfa_offset:
497 if (verbose)
498 printf ("\t# %s(%ld)\n", cfi_insn_str (data->insn),
499 data->param[0]);
500 output_data (pbuf, buf_size, t_byte, data->insn);
501 output_data (pbuf, buf_size, t_uleb128, data->param[0]);
502 break;
503
504 case CFA_offset:
505 if (verbose)
506 printf ("\t# %s(%ld,%ld)\n", cfi_insn_str (data->insn),
507 data->param[0], data->param[1]);
508
509 /* Check whether to use CFA_offset or CFA_offset_extended. */
510 if (data->param[0] <= 0x3F)
511 output_data (pbuf, buf_size, t_byte, CFA_offset + data->param[0]);
512 else
513 {
514 output_data (pbuf, buf_size, t_byte, CFA_offset_extended);
515 output_data (pbuf, buf_size, t_uleb128, data->param[0]);
516 }
517 output_data (pbuf, buf_size, t_uleb128,
518 data->param[1] / current_config.data_align);
519 break;
520
521 case CFA_nop:
522 if (verbose)
523 printf ("\t# CFA_nop\n");
524 output_data (pbuf, buf_size, t_byte, CFA_nop);
525 break;
526
527 default:
528 as_warn ("CFA_unknown[%d](%ld,%ld)", data->insn,
529 data->param[0], data->param[1]);
530 }
531 size = *pbuf - orig_buf;
532 *buf = *pbuf;
533 *buf_size -= size;
534 return size;
535 }
536
537 static void
538 dot_cfi_endproc (void)
539 {
540 struct cfi_data *data_ptr;
541 char *cie_buf, *fde_buf, *pbuf, *where;
542 unsigned long buf_size, cie_size, fde_size, last_cie_offset;
543 unsigned long fde_initloc_offset, fde_len_offset;
544 void *saved_seg, *cfi_seg;
545 expressionS exp;
546
547 if (! cfi_info)
548 {
549 as_bad (_(".cfi_endproc without corresponding .cfi_startproc"));
550 return;
551 }
552 cfi_info->end_address = frag_now_fix ();
553
554 /* Open .eh_frame section. */
555 saved_seg = now_seg;
556 cfi_seg = subseg_new (".eh_frame", 0);
557 bfd_set_section_flags (stdoutput, cfi_seg,
558 SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA);
559 subseg_set (cfi_seg, 0);
560
561 /* Build CIE. */
562 cie_buf = xcalloc (1024, 1);
563 /* Skip space for CIE length. */
564 pbuf = cie_buf + 4;
565 buf_size = 1020;
566
567 if (verbose)
568 printf ("# CIE *****\n");
569
570 /* CIE id. */
571 output_data (&pbuf, &buf_size, t_long, 0x0);
572 /* Version. */
573 output_data (&pbuf, &buf_size, t_byte, 1);
574 /* Augmentation. */
575 output_data (&pbuf, &buf_size, t_byte, 0);
576 /* Code alignment. */
577 output_data (&pbuf, &buf_size, t_uleb128, current_config.code_align);
578 /* Data alignment. */
579 output_data (&pbuf, &buf_size, t_sleb128, current_config.data_align);
580 /* Return address column. */
581 output_data (&pbuf, &buf_size, t_byte, current_config.ra_column);
582
583 /* Build CFI instructions. */
584 data_ptr = cfi_info->data;
585 while (data_ptr && !cfi_is_advance_insn (data_ptr->insn))
586 {
587 cfi_output_insn (data_ptr, &pbuf, &buf_size);
588 data_ptr = data_ptr->next;
589 }
590
591 /* Align the whole data to current_config.eh_align. */
592 cie_size = pbuf - cie_buf;
593 cie_size += current_config.eh_align - cie_size % current_config.eh_align;
594
595 /* CIE length. */
596 pbuf = cie_buf;
597 output_data (&pbuf, &buf_size, t_long, cie_size - 4);
598
599 /* OK, we built the CIE. Let's write it to the file... */
600 last_cie_offset = frag_now_fix ();
601 where = (unsigned char *) frag_more (cie_size);
602 memcpy (where, cie_buf, cie_size);
603
604 /* Clean up. */
605 free (cie_buf);
606
607 /* Build the FDE... */
608 fde_buf = xcalloc (1024, 1);
609 pbuf = fde_buf;
610 buf_size = 1024;
611
612 if (verbose)
613 {
614 printf ("# FDE: start=0x%lx, end=0x%lx, delta=%d\n",
615 (long) cfi_info->start_address,
616 (long) cfi_info->end_address,
617 (int) (cfi_info->end_address - cfi_info->start_address));
618 }
619
620 /* FDE length (t_long, 4 bytes) - will be set later. */
621 fde_len_offset = pbuf - fde_buf;
622 pbuf += 4;
623 buf_size -= 4;
624
625 /* CIE pointer - offset from here. */
626 output_data (&pbuf, &buf_size, t_long, cie_size + 4);
627
628 /* FDE initial location - this must be set relocatable! */
629 fde_initloc_offset = pbuf - fde_buf;
630 output_data (&pbuf, &buf_size, current_config.addr_length,
631 cfi_info->start_address);
632
633 /* FDE address range. */
634 output_data (&pbuf, &buf_size, current_config.addr_length,
635 cfi_info->end_address - cfi_info->start_address);
636
637 while (data_ptr)
638 {
639 cfi_output_insn (data_ptr, &pbuf, &buf_size);
640 data_ptr = data_ptr->next;
641 }
642
643 fde_size = pbuf - fde_buf;
644 fde_size += current_config.eh_align - fde_size % current_config.eh_align;
645
646 /* Now we can set FDE length. */
647 pbuf = fde_buf + fde_len_offset;
648 buf_size = 4;
649 output_data (&pbuf, &buf_size, t_long, fde_size - 4);
650
651 /* Adjust initloc offset. */
652 fde_initloc_offset += frag_now_fix ();
653
654 /* Copy FDE to objfile. */
655 where = (unsigned char *) frag_more (fde_size);
656 memcpy (where, fde_buf, fde_size);
657
658 /* Set relocation for initial address. */
659 buf_size = current_config.addr_length;
660 memset (&exp, 0, sizeof (exp));
661 exp.X_op = O_symbol;
662 exp.X_add_symbol = symbol_find (cfi_info->labelname);
663 fix_new_exp (frag_now, fde_initloc_offset,
664 current_config.addr_length,
665 &exp, 0, current_config.reloc_type);
666
667 /* Clean up. */
668 free (fde_buf);
669
670 free (cfi_info);
671 cfi_info = NULL;
672
673 /* Restore previous segment. */
674 subseg_set (saved_seg, 0);
675 }
676
677 void
678 dot_cfi (int arg)
679 {
680 long param;
681
682 switch (arg)
683 {
684 case CFI_startproc:
685 dot_cfi_startproc ();
686 break;
687 case CFI_endproc:
688 dot_cfi_endproc ();
689 break;
690 case CFA_def_cfa:
691 case CFA_def_cfa_register:
692 case CFA_def_cfa_offset:
693 case CFA_offset:
694 case CFI_adjust_cfa_offset:
695 cfi_make_insn (arg);
696 break;
697 case CFI_verbose:
698 if (cfi_parse_const (&param) >= 0)
699 verbose = (int) param;
700 else
701 verbose = 1;
702 break;
703 default:
704 as_bad (_("unknown CFI code 0x%x (%s)"), arg, cfi_insn_str (arg));
705 break;
706 }
707 ignore_rest_of_line ();
708 }
709
710 void
711 cfi_set_config (struct cfi_config *cfg)
712 {
713 assert (cfg != NULL);
714 assert (cfg->addr_length > 0);
715
716 current_config = *cfg;
717 }
718
719 void
720 cfi_finish (void)
721 {
722 if (cfi_info)
723 as_bad (_("open CFI at the end of file; missing .cfi_endproc directive"));
724 }