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