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