* dw2gencfi.c, dw2gencfi.h: New files.
[external/binutils.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   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
170 static int
171 cfi_parse_reg (long *param)
172 {
173   return cfi_parse_arg (param, 1);
174 }
175
176 static int
177 cfi_parse_const (long *param)
178 {
179   return cfi_parse_arg (param, 0);
180 }
181
182 void
183 cfi_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
209 static void
210 cfi_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
220 static long
221 get_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
239 static void
240 cfi_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
308 static symbolS *
309 cfi_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
333 static void
334 dot_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
357 enum 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
370 static int
371 output_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
444 static int
445 cfi_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
534 static void
535 dot_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
674 void
675 dot_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
707 void
708 cfi_set_config (struct cfi_config *cfg)
709 {
710   assert (cfg != NULL);
711   assert (cfg->addr_length > 0);
712
713   current_config = *cfg;
714 }
715
716 void
717 cfi_finish (void)
718 {
719   if (cfi_info)
720     as_bad (_("open CFI at the end of file; missing .cfi_endproc directive"));
721 }