1130561Sobrien/* dw2gencfi.c - Support for generating Dwarf2 CFI information.
2218822Sdim   Copyright 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
3130561Sobrien   Contributed by Michal Ludvig <mludvig@suse.cz>
4130561Sobrien
5130561Sobrien   This file is part of GAS, the GNU Assembler.
6130561Sobrien
7130561Sobrien   GAS is free software; you can redistribute it and/or modify
8130561Sobrien   it under the terms of the GNU General Public License as published by
9130561Sobrien   the Free Software Foundation; either version 2, or (at your option)
10130561Sobrien   any later version.
11130561Sobrien
12130561Sobrien   GAS is distributed in the hope that it will be useful,
13130561Sobrien   but WITHOUT ANY WARRANTY; without even the implied warranty of
14130561Sobrien   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15130561Sobrien   GNU General Public License for more details.
16130561Sobrien
17130561Sobrien   You should have received a copy of the GNU General Public License
18130561Sobrien   along with GAS; see the file COPYING.  If not, write to the Free
19218822Sdim   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
20218822Sdim   02110-1301, USA.  */
21130561Sobrien
22130561Sobrien#include "as.h"
23130561Sobrien#include "dw2gencfi.h"
24218822Sdim#include "subsegs.h"
25130561Sobrien
26130561Sobrien
27130561Sobrien/* We re-use DWARF2_LINE_MIN_INSN_LENGTH for the code alignment field
28130561Sobrien   of the CIE.  Default to 1 if not otherwise specified.  */
29218822Sdim#ifndef  DWARF2_LINE_MIN_INSN_LENGTH
30130561Sobrien# define DWARF2_LINE_MIN_INSN_LENGTH 1
31130561Sobrien#endif
32130561Sobrien
33130561Sobrien/* If TARGET_USE_CFIPOP is defined, it is required that the target
34130561Sobrien   provide the following definitions.  Otherwise provide them to
35130561Sobrien   allow compilation to continue.  */
36130561Sobrien#ifndef TARGET_USE_CFIPOP
37218822Sdim# ifndef  DWARF2_DEFAULT_RETURN_COLUMN
38130561Sobrien#  define DWARF2_DEFAULT_RETURN_COLUMN 0
39130561Sobrien# endif
40218822Sdim# ifndef  DWARF2_CIE_DATA_ALIGNMENT
41130561Sobrien#  define DWARF2_CIE_DATA_ALIGNMENT 1
42130561Sobrien# endif
43130561Sobrien#endif
44130561Sobrien
45130561Sobrien#ifndef EH_FRAME_ALIGNMENT
46218822Sdim# define EH_FRAME_ALIGNMENT (bfd_get_arch_size (stdoutput) == 64 ? 3 : 2)
47130561Sobrien#endif
48130561Sobrien
49130561Sobrien#ifndef tc_cfi_frame_initial_instructions
50130561Sobrien# define tc_cfi_frame_initial_instructions() ((void)0)
51130561Sobrien#endif
52130561Sobrien
53130561Sobrien
54130561Sobrienstruct cfi_insn_data
55130561Sobrien{
56130561Sobrien  struct cfi_insn_data *next;
57130561Sobrien  int insn;
58130561Sobrien  union {
59130561Sobrien    struct {
60130561Sobrien      unsigned reg;
61130561Sobrien      offsetT offset;
62130561Sobrien    } ri;
63130561Sobrien
64130561Sobrien    struct {
65130561Sobrien      unsigned reg1;
66130561Sobrien      unsigned reg2;
67130561Sobrien    } rr;
68130561Sobrien
69130561Sobrien    unsigned r;
70130561Sobrien    offsetT i;
71130561Sobrien
72130561Sobrien    struct {
73130561Sobrien      symbolS *lab1;
74130561Sobrien      symbolS *lab2;
75130561Sobrien    } ll;
76130561Sobrien
77130561Sobrien    struct cfi_escape_data {
78130561Sobrien      struct cfi_escape_data *next;
79130561Sobrien      expressionS exp;
80130561Sobrien    } *esc;
81130561Sobrien  } u;
82130561Sobrien};
83130561Sobrien
84130561Sobrienstruct fde_entry
85130561Sobrien{
86130561Sobrien  struct fde_entry *next;
87130561Sobrien  symbolS *start_address;
88130561Sobrien  symbolS *end_address;
89130561Sobrien  struct cfi_insn_data *data;
90130561Sobrien  struct cfi_insn_data **last;
91218822Sdim  unsigned char per_encoding;
92218822Sdim  unsigned char lsda_encoding;
93218822Sdim  expressionS personality;
94218822Sdim  expressionS lsda;
95130561Sobrien  unsigned int return_column;
96218822Sdim  unsigned int signal_frame;
97130561Sobrien};
98130561Sobrien
99130561Sobrienstruct cie_entry
100130561Sobrien{
101130561Sobrien  struct cie_entry *next;
102130561Sobrien  symbolS *start_address;
103130561Sobrien  unsigned int return_column;
104218822Sdim  unsigned int signal_frame;
105218822Sdim  unsigned char per_encoding;
106218822Sdim  unsigned char lsda_encoding;
107218822Sdim  expressionS personality;
108130561Sobrien  struct cfi_insn_data *first, *last;
109130561Sobrien};
110130561Sobrien
111130561Sobrien
112130561Sobrien/* List of FDE entries.  */
113130561Sobrienstatic struct fde_entry *all_fde_data;
114130561Sobrienstatic struct fde_entry **last_fde_data = &all_fde_data;
115130561Sobrien
116130561Sobrien/* List of CIEs so that they could be reused.  */
117130561Sobrienstatic struct cie_entry *cie_root;
118130561Sobrien
119130561Sobrien/* Stack of old CFI data, for save/restore.  */
120130561Sobrienstruct cfa_save_data
121130561Sobrien{
122130561Sobrien  struct cfa_save_data *next;
123130561Sobrien  offsetT cfa_offset;
124130561Sobrien};
125130561Sobrien
126218822Sdim/* Current open FDE entry.  */
127218822Sdimstruct frch_cfi_data
128218822Sdim{
129218822Sdim  struct fde_entry *cur_fde_data;
130218822Sdim  symbolS *last_address;
131218822Sdim  offsetT cur_cfa_offset;
132218822Sdim  struct cfa_save_data *cfa_save_stack;
133218822Sdim};
134130561Sobrien
135130561Sobrien/* Construct a new FDE structure and add it to the end of the fde list.  */
136130561Sobrien
137130561Sobrienstatic struct fde_entry *
138130561Sobrienalloc_fde_entry (void)
139130561Sobrien{
140130561Sobrien  struct fde_entry *fde = xcalloc (1, sizeof (struct fde_entry));
141130561Sobrien
142218822Sdim  frchain_now->frch_cfi_data = xcalloc (1, sizeof (struct frch_cfi_data));
143218822Sdim  frchain_now->frch_cfi_data->cur_fde_data = fde;
144130561Sobrien  *last_fde_data = fde;
145130561Sobrien  last_fde_data = &fde->next;
146130561Sobrien
147130561Sobrien  fde->last = &fde->data;
148130561Sobrien  fde->return_column = DWARF2_DEFAULT_RETURN_COLUMN;
149218822Sdim  fde->per_encoding = DW_EH_PE_omit;
150218822Sdim  fde->lsda_encoding = DW_EH_PE_omit;
151130561Sobrien
152130561Sobrien  return fde;
153130561Sobrien}
154130561Sobrien
155130561Sobrien/* The following functions are available for a backend to construct its
156130561Sobrien   own unwind information, usually from legacy unwind directives.  */
157130561Sobrien
158130561Sobrien/* Construct a new INSN structure and add it to the end of the insn list
159130561Sobrien   for the currently active FDE.  */
160130561Sobrien
161130561Sobrienstatic struct cfi_insn_data *
162130561Sobrienalloc_cfi_insn_data (void)
163130561Sobrien{
164130561Sobrien  struct cfi_insn_data *insn = xcalloc (1, sizeof (struct cfi_insn_data));
165218822Sdim  struct fde_entry *cur_fde_data = frchain_now->frch_cfi_data->cur_fde_data;
166130561Sobrien
167130561Sobrien  *cur_fde_data->last = insn;
168130561Sobrien  cur_fde_data->last = &insn->next;
169130561Sobrien
170130561Sobrien  return insn;
171130561Sobrien}
172130561Sobrien
173130561Sobrien/* Construct a new FDE structure that begins at LABEL.  */
174130561Sobrien
175130561Sobrienvoid
176130561Sobriencfi_new_fde (symbolS *label)
177130561Sobrien{
178130561Sobrien  struct fde_entry *fde = alloc_fde_entry ();
179130561Sobrien  fde->start_address = label;
180218822Sdim  frchain_now->frch_cfi_data->last_address = label;
181130561Sobrien}
182130561Sobrien
183130561Sobrien/* End the currently open FDE.  */
184130561Sobrien
185130561Sobrienvoid
186130561Sobriencfi_end_fde (symbolS *label)
187130561Sobrien{
188218822Sdim  frchain_now->frch_cfi_data->cur_fde_data->end_address = label;
189218822Sdim  free (frchain_now->frch_cfi_data);
190218822Sdim  frchain_now->frch_cfi_data = NULL;
191130561Sobrien}
192130561Sobrien
193130561Sobrien/* Set the return column for the current FDE.  */
194130561Sobrien
195130561Sobrienvoid
196130561Sobriencfi_set_return_column (unsigned regno)
197130561Sobrien{
198218822Sdim  frchain_now->frch_cfi_data->cur_fde_data->return_column = regno;
199130561Sobrien}
200130561Sobrien
201130561Sobrien/* Universal functions to store new instructions.  */
202130561Sobrien
203130561Sobrienstatic void
204130561Sobriencfi_add_CFA_insn(int insn)
205130561Sobrien{
206130561Sobrien  struct cfi_insn_data *insn_ptr = alloc_cfi_insn_data ();
207130561Sobrien
208130561Sobrien  insn_ptr->insn = insn;
209130561Sobrien}
210130561Sobrien
211130561Sobrienstatic void
212130561Sobriencfi_add_CFA_insn_reg (int insn, unsigned regno)
213130561Sobrien{
214130561Sobrien  struct cfi_insn_data *insn_ptr = alloc_cfi_insn_data ();
215130561Sobrien
216130561Sobrien  insn_ptr->insn = insn;
217130561Sobrien  insn_ptr->u.r = regno;
218130561Sobrien}
219130561Sobrien
220130561Sobrienstatic void
221130561Sobriencfi_add_CFA_insn_offset (int insn, offsetT offset)
222130561Sobrien{
223130561Sobrien  struct cfi_insn_data *insn_ptr = alloc_cfi_insn_data ();
224130561Sobrien
225130561Sobrien  insn_ptr->insn = insn;
226130561Sobrien  insn_ptr->u.i = offset;
227130561Sobrien}
228130561Sobrien
229130561Sobrienstatic void
230130561Sobriencfi_add_CFA_insn_reg_reg (int insn, unsigned reg1, unsigned reg2)
231130561Sobrien{
232130561Sobrien  struct cfi_insn_data *insn_ptr = alloc_cfi_insn_data ();
233130561Sobrien
234130561Sobrien  insn_ptr->insn = insn;
235130561Sobrien  insn_ptr->u.rr.reg1 = reg1;
236130561Sobrien  insn_ptr->u.rr.reg2 = reg2;
237130561Sobrien}
238130561Sobrien
239130561Sobrienstatic void
240130561Sobriencfi_add_CFA_insn_reg_offset (int insn, unsigned regno, offsetT offset)
241130561Sobrien{
242130561Sobrien  struct cfi_insn_data *insn_ptr = alloc_cfi_insn_data ();
243130561Sobrien
244130561Sobrien  insn_ptr->insn = insn;
245130561Sobrien  insn_ptr->u.ri.reg = regno;
246130561Sobrien  insn_ptr->u.ri.offset = offset;
247130561Sobrien}
248130561Sobrien
249130561Sobrien/* Add a CFI insn to advance the PC from the last address to LABEL.  */
250130561Sobrien
251130561Sobrienvoid
252130561Sobriencfi_add_advance_loc (symbolS *label)
253130561Sobrien{
254130561Sobrien  struct cfi_insn_data *insn = alloc_cfi_insn_data ();
255130561Sobrien
256130561Sobrien  insn->insn = DW_CFA_advance_loc;
257218822Sdim  insn->u.ll.lab1 = frchain_now->frch_cfi_data->last_address;
258130561Sobrien  insn->u.ll.lab2 = label;
259130561Sobrien
260218822Sdim  frchain_now->frch_cfi_data->last_address = label;
261130561Sobrien}
262130561Sobrien
263130561Sobrien/* Add a DW_CFA_offset record to the CFI data.  */
264130561Sobrien
265130561Sobrienvoid
266130561Sobriencfi_add_CFA_offset (unsigned regno, offsetT offset)
267130561Sobrien{
268130561Sobrien  unsigned int abs_data_align;
269130561Sobrien
270218822Sdim  assert (DWARF2_CIE_DATA_ALIGNMENT != 0);
271130561Sobrien  cfi_add_CFA_insn_reg_offset (DW_CFA_offset, regno, offset);
272130561Sobrien
273130561Sobrien  abs_data_align = (DWARF2_CIE_DATA_ALIGNMENT < 0
274130561Sobrien		    ? -DWARF2_CIE_DATA_ALIGNMENT : DWARF2_CIE_DATA_ALIGNMENT);
275130561Sobrien  if (offset % abs_data_align)
276130561Sobrien    as_bad (_("register save offset not a multiple of %u"), abs_data_align);
277130561Sobrien}
278130561Sobrien
279130561Sobrien/* Add a DW_CFA_def_cfa record to the CFI data.  */
280130561Sobrien
281130561Sobrienvoid
282130561Sobriencfi_add_CFA_def_cfa (unsigned regno, offsetT offset)
283130561Sobrien{
284130561Sobrien  cfi_add_CFA_insn_reg_offset (DW_CFA_def_cfa, regno, offset);
285218822Sdim  frchain_now->frch_cfi_data->cur_cfa_offset = offset;
286130561Sobrien}
287130561Sobrien
288130561Sobrien/* Add a DW_CFA_register record to the CFI data.  */
289130561Sobrien
290130561Sobrienvoid
291130561Sobriencfi_add_CFA_register (unsigned reg1, unsigned reg2)
292130561Sobrien{
293130561Sobrien  cfi_add_CFA_insn_reg_reg (DW_CFA_register, reg1, reg2);
294130561Sobrien}
295130561Sobrien
296130561Sobrien/* Add a DW_CFA_def_cfa_register record to the CFI data.  */
297130561Sobrien
298130561Sobrienvoid
299130561Sobriencfi_add_CFA_def_cfa_register (unsigned regno)
300130561Sobrien{
301130561Sobrien  cfi_add_CFA_insn_reg (DW_CFA_def_cfa_register, regno);
302130561Sobrien}
303130561Sobrien
304130561Sobrien/* Add a DW_CFA_def_cfa_offset record to the CFI data.  */
305130561Sobrien
306130561Sobrienvoid
307130561Sobriencfi_add_CFA_def_cfa_offset (offsetT offset)
308130561Sobrien{
309130561Sobrien  cfi_add_CFA_insn_offset (DW_CFA_def_cfa_offset, offset);
310218822Sdim  frchain_now->frch_cfi_data->cur_cfa_offset = offset;
311130561Sobrien}
312130561Sobrien
313130561Sobrienvoid
314130561Sobriencfi_add_CFA_restore (unsigned regno)
315130561Sobrien{
316130561Sobrien  cfi_add_CFA_insn_reg (DW_CFA_restore, regno);
317130561Sobrien}
318130561Sobrien
319130561Sobrienvoid
320130561Sobriencfi_add_CFA_undefined (unsigned regno)
321130561Sobrien{
322130561Sobrien  cfi_add_CFA_insn_reg (DW_CFA_undefined, regno);
323130561Sobrien}
324130561Sobrien
325130561Sobrienvoid
326130561Sobriencfi_add_CFA_same_value (unsigned regno)
327130561Sobrien{
328130561Sobrien  cfi_add_CFA_insn_reg (DW_CFA_same_value, regno);
329130561Sobrien}
330130561Sobrien
331130561Sobrienvoid
332130561Sobriencfi_add_CFA_remember_state (void)
333130561Sobrien{
334130561Sobrien  struct cfa_save_data *p;
335130561Sobrien
336130561Sobrien  cfi_add_CFA_insn (DW_CFA_remember_state);
337130561Sobrien
338130561Sobrien  p = xmalloc (sizeof (*p));
339218822Sdim  p->cfa_offset = frchain_now->frch_cfi_data->cur_cfa_offset;
340218822Sdim  p->next = frchain_now->frch_cfi_data->cfa_save_stack;
341218822Sdim  frchain_now->frch_cfi_data->cfa_save_stack = p;
342130561Sobrien}
343130561Sobrien
344130561Sobrienvoid
345130561Sobriencfi_add_CFA_restore_state (void)
346130561Sobrien{
347130561Sobrien  struct cfa_save_data *p;
348130561Sobrien
349130561Sobrien  cfi_add_CFA_insn (DW_CFA_restore_state);
350130561Sobrien
351218822Sdim  p = frchain_now->frch_cfi_data->cfa_save_stack;
352130561Sobrien  if (p)
353130561Sobrien    {
354218822Sdim      frchain_now->frch_cfi_data->cur_cfa_offset = p->cfa_offset;
355218822Sdim      frchain_now->frch_cfi_data->cfa_save_stack = p->next;
356130561Sobrien      free (p);
357130561Sobrien    }
358218822Sdim  else
359218822Sdim    as_bad (_("CFI state restore without previous remember"));
360130561Sobrien}
361130561Sobrien
362130561Sobrien
363130561Sobrien/* Parse CFI assembler directives.  */
364130561Sobrien
365130561Sobrienstatic void dot_cfi (int);
366130561Sobrienstatic void dot_cfi_escape (int);
367130561Sobrienstatic void dot_cfi_startproc (int);
368130561Sobrienstatic void dot_cfi_endproc (int);
369218822Sdimstatic void dot_cfi_personality (int);
370218822Sdimstatic void dot_cfi_lsda (int);
371130561Sobrien
372130561Sobrien/* Fake CFI type; outside the byte range of any real CFI insn.  */
373130561Sobrien#define CFI_adjust_cfa_offset	0x100
374130561Sobrien#define CFI_return_column	0x101
375130561Sobrien#define CFI_rel_offset		0x102
376130561Sobrien#define CFI_escape		0x103
377218822Sdim#define CFI_signal_frame	0x104
378130561Sobrien
379130561Sobrienconst pseudo_typeS cfi_pseudo_table[] =
380130561Sobrien  {
381130561Sobrien    { "cfi_startproc", dot_cfi_startproc, 0 },
382130561Sobrien    { "cfi_endproc", dot_cfi_endproc, 0 },
383130561Sobrien    { "cfi_def_cfa", dot_cfi, DW_CFA_def_cfa },
384130561Sobrien    { "cfi_def_cfa_register", dot_cfi, DW_CFA_def_cfa_register },
385130561Sobrien    { "cfi_def_cfa_offset", dot_cfi, DW_CFA_def_cfa_offset },
386130561Sobrien    { "cfi_adjust_cfa_offset", dot_cfi, CFI_adjust_cfa_offset },
387130561Sobrien    { "cfi_offset", dot_cfi, DW_CFA_offset },
388130561Sobrien    { "cfi_rel_offset", dot_cfi, CFI_rel_offset },
389130561Sobrien    { "cfi_register", dot_cfi, DW_CFA_register },
390130561Sobrien    { "cfi_return_column", dot_cfi, CFI_return_column },
391130561Sobrien    { "cfi_restore", dot_cfi, DW_CFA_restore },
392130561Sobrien    { "cfi_undefined", dot_cfi, DW_CFA_undefined },
393130561Sobrien    { "cfi_same_value", dot_cfi, DW_CFA_same_value },
394130561Sobrien    { "cfi_remember_state", dot_cfi, DW_CFA_remember_state },
395130561Sobrien    { "cfi_restore_state", dot_cfi, DW_CFA_restore_state },
396130561Sobrien    { "cfi_window_save", dot_cfi, DW_CFA_GNU_window_save },
397130561Sobrien    { "cfi_escape", dot_cfi_escape, 0 },
398218822Sdim    { "cfi_signal_frame", dot_cfi, CFI_signal_frame },
399218822Sdim    { "cfi_personality", dot_cfi_personality, 0 },
400218822Sdim    { "cfi_lsda", dot_cfi_lsda, 0 },
401130561Sobrien    { NULL, NULL, 0 }
402130561Sobrien  };
403130561Sobrien
404130561Sobrienstatic void
405130561Sobriencfi_parse_separator (void)
406130561Sobrien{
407130561Sobrien  SKIP_WHITESPACE ();
408130561Sobrien  if (*input_line_pointer == ',')
409130561Sobrien    input_line_pointer++;
410130561Sobrien  else
411130561Sobrien    as_bad (_("missing separator"));
412130561Sobrien}
413130561Sobrien
414130561Sobrienstatic unsigned
415130561Sobriencfi_parse_reg (void)
416130561Sobrien{
417130561Sobrien  int regno;
418130561Sobrien  expressionS exp;
419130561Sobrien
420130561Sobrien#ifdef tc_regname_to_dw2regnum
421130561Sobrien  SKIP_WHITESPACE ();
422130561Sobrien  if (is_name_beginner (*input_line_pointer)
423130561Sobrien      || (*input_line_pointer == '%'
424130561Sobrien	  && is_name_beginner (*++input_line_pointer)))
425130561Sobrien    {
426130561Sobrien      char *name, c;
427130561Sobrien
428130561Sobrien      name = input_line_pointer;
429130561Sobrien      c = get_symbol_end ();
430130561Sobrien
431130561Sobrien      if ((regno = tc_regname_to_dw2regnum (name)) < 0)
432130561Sobrien	{
433130561Sobrien	  as_bad (_("bad register expression"));
434130561Sobrien	  regno = 0;
435130561Sobrien	}
436130561Sobrien
437130561Sobrien      *input_line_pointer = c;
438130561Sobrien      return regno;
439130561Sobrien    }
440130561Sobrien#endif
441130561Sobrien
442218822Sdim  expression_and_evaluate (&exp);
443130561Sobrien  switch (exp.X_op)
444130561Sobrien    {
445130561Sobrien    case O_register:
446130561Sobrien    case O_constant:
447130561Sobrien      regno = exp.X_add_number;
448130561Sobrien      break;
449130561Sobrien
450130561Sobrien    default:
451130561Sobrien      as_bad (_("bad register expression"));
452130561Sobrien      regno = 0;
453130561Sobrien      break;
454130561Sobrien    }
455130561Sobrien
456130561Sobrien  return regno;
457130561Sobrien}
458130561Sobrien
459130561Sobrienstatic offsetT
460130561Sobriencfi_parse_const (void)
461130561Sobrien{
462130561Sobrien  return get_absolute_expression ();
463130561Sobrien}
464130561Sobrien
465130561Sobrienstatic void
466130561Sobriendot_cfi (int arg)
467130561Sobrien{
468130561Sobrien  offsetT offset;
469130561Sobrien  unsigned reg1, reg2;
470130561Sobrien
471218822Sdim  if (frchain_now->frch_cfi_data == NULL)
472130561Sobrien    {
473130561Sobrien      as_bad (_("CFI instruction used without previous .cfi_startproc"));
474218822Sdim      ignore_rest_of_line ();
475130561Sobrien      return;
476130561Sobrien    }
477130561Sobrien
478130561Sobrien  /* If the last address was not at the current PC, advance to current.  */
479218822Sdim  if (symbol_get_frag (frchain_now->frch_cfi_data->last_address) != frag_now
480218822Sdim      || S_GET_VALUE (frchain_now->frch_cfi_data->last_address)
481218822Sdim	 != frag_now_fix ())
482130561Sobrien    cfi_add_advance_loc (symbol_temp_new_now ());
483130561Sobrien
484130561Sobrien  switch (arg)
485130561Sobrien    {
486130561Sobrien    case DW_CFA_offset:
487130561Sobrien      reg1 = cfi_parse_reg ();
488130561Sobrien      cfi_parse_separator ();
489130561Sobrien      offset = cfi_parse_const ();
490130561Sobrien      cfi_add_CFA_offset (reg1, offset);
491130561Sobrien      break;
492130561Sobrien
493130561Sobrien    case CFI_rel_offset:
494130561Sobrien      reg1 = cfi_parse_reg ();
495130561Sobrien      cfi_parse_separator ();
496130561Sobrien      offset = cfi_parse_const ();
497218822Sdim      cfi_add_CFA_offset (reg1,
498218822Sdim			  offset - frchain_now->frch_cfi_data->cur_cfa_offset);
499130561Sobrien      break;
500130561Sobrien
501130561Sobrien    case DW_CFA_def_cfa:
502130561Sobrien      reg1 = cfi_parse_reg ();
503130561Sobrien      cfi_parse_separator ();
504130561Sobrien      offset = cfi_parse_const ();
505130561Sobrien      cfi_add_CFA_def_cfa (reg1, offset);
506130561Sobrien      break;
507130561Sobrien
508130561Sobrien    case DW_CFA_register:
509130561Sobrien      reg1 = cfi_parse_reg ();
510130561Sobrien      cfi_parse_separator ();
511130561Sobrien      reg2 = cfi_parse_reg ();
512130561Sobrien      cfi_add_CFA_register (reg1, reg2);
513130561Sobrien      break;
514130561Sobrien
515130561Sobrien    case DW_CFA_def_cfa_register:
516130561Sobrien      reg1 = cfi_parse_reg ();
517130561Sobrien      cfi_add_CFA_def_cfa_register (reg1);
518130561Sobrien      break;
519130561Sobrien
520130561Sobrien    case DW_CFA_def_cfa_offset:
521130561Sobrien      offset = cfi_parse_const ();
522130561Sobrien      cfi_add_CFA_def_cfa_offset (offset);
523130561Sobrien      break;
524130561Sobrien
525130561Sobrien    case CFI_adjust_cfa_offset:
526130561Sobrien      offset = cfi_parse_const ();
527218822Sdim      cfi_add_CFA_def_cfa_offset (frchain_now->frch_cfi_data->cur_cfa_offset
528218822Sdim				  + offset);
529130561Sobrien      break;
530130561Sobrien
531130561Sobrien    case DW_CFA_restore:
532218822Sdim      for (;;)
533218822Sdim	{
534218822Sdim	  reg1 = cfi_parse_reg ();
535218822Sdim	  cfi_add_CFA_restore (reg1);
536218822Sdim	  SKIP_WHITESPACE ();
537218822Sdim	  if (*input_line_pointer != ',')
538218822Sdim	    break;
539218822Sdim	  ++input_line_pointer;
540218822Sdim	}
541130561Sobrien      break;
542130561Sobrien
543130561Sobrien    case DW_CFA_undefined:
544218822Sdim      for (;;)
545218822Sdim	{
546218822Sdim	  reg1 = cfi_parse_reg ();
547218822Sdim	  cfi_add_CFA_undefined (reg1);
548218822Sdim	  SKIP_WHITESPACE ();
549218822Sdim	  if (*input_line_pointer != ',')
550218822Sdim	    break;
551218822Sdim	  ++input_line_pointer;
552218822Sdim	}
553130561Sobrien      break;
554130561Sobrien
555130561Sobrien    case DW_CFA_same_value:
556130561Sobrien      reg1 = cfi_parse_reg ();
557130561Sobrien      cfi_add_CFA_same_value (reg1);
558130561Sobrien      break;
559130561Sobrien
560130561Sobrien    case CFI_return_column:
561130561Sobrien      reg1 = cfi_parse_reg ();
562130561Sobrien      cfi_set_return_column (reg1);
563130561Sobrien      break;
564130561Sobrien
565130561Sobrien    case DW_CFA_remember_state:
566130561Sobrien      cfi_add_CFA_remember_state ();
567130561Sobrien      break;
568130561Sobrien
569130561Sobrien    case DW_CFA_restore_state:
570130561Sobrien      cfi_add_CFA_restore_state ();
571130561Sobrien      break;
572130561Sobrien
573130561Sobrien    case DW_CFA_GNU_window_save:
574130561Sobrien      cfi_add_CFA_insn (DW_CFA_GNU_window_save);
575130561Sobrien      break;
576130561Sobrien
577218822Sdim    case CFI_signal_frame:
578218822Sdim      frchain_now->frch_cfi_data->cur_fde_data->signal_frame = 1;
579218822Sdim      break;
580218822Sdim
581130561Sobrien    default:
582130561Sobrien      abort ();
583130561Sobrien    }
584130561Sobrien
585130561Sobrien  demand_empty_rest_of_line ();
586130561Sobrien}
587130561Sobrien
588130561Sobrienstatic void
589130561Sobriendot_cfi_escape (int ignored ATTRIBUTE_UNUSED)
590130561Sobrien{
591130561Sobrien  struct cfi_escape_data *head, **tail, *e;
592130561Sobrien  struct cfi_insn_data *insn;
593130561Sobrien
594218822Sdim  if (frchain_now->frch_cfi_data == NULL)
595130561Sobrien    {
596130561Sobrien      as_bad (_("CFI instruction used without previous .cfi_startproc"));
597218822Sdim      ignore_rest_of_line ();
598130561Sobrien      return;
599130561Sobrien    }
600130561Sobrien
601130561Sobrien  /* If the last address was not at the current PC, advance to current.  */
602218822Sdim  if (symbol_get_frag (frchain_now->frch_cfi_data->last_address) != frag_now
603218822Sdim      || S_GET_VALUE (frchain_now->frch_cfi_data->last_address)
604218822Sdim	 != frag_now_fix ())
605130561Sobrien    cfi_add_advance_loc (symbol_temp_new_now ());
606130561Sobrien
607130561Sobrien  tail = &head;
608130561Sobrien  do
609130561Sobrien    {
610130561Sobrien      e = xmalloc (sizeof (*e));
611130561Sobrien      do_parse_cons_expression (&e->exp, 1);
612130561Sobrien      *tail = e;
613130561Sobrien      tail = &e->next;
614130561Sobrien    }
615130561Sobrien  while (*input_line_pointer++ == ',');
616130561Sobrien  *tail = NULL;
617130561Sobrien
618130561Sobrien  insn = alloc_cfi_insn_data ();
619130561Sobrien  insn->insn = CFI_escape;
620130561Sobrien  insn->u.esc = head;
621218822Sdim
622218822Sdim  --input_line_pointer;
623218822Sdim  demand_empty_rest_of_line ();
624130561Sobrien}
625130561Sobrien
626130561Sobrienstatic void
627218822Sdimdot_cfi_personality (int ignored ATTRIBUTE_UNUSED)
628218822Sdim{
629218822Sdim  struct fde_entry *fde;
630218822Sdim  offsetT encoding;
631218822Sdim
632218822Sdim  if (frchain_now->frch_cfi_data == NULL)
633218822Sdim    {
634218822Sdim      as_bad (_("CFI instruction used without previous .cfi_startproc"));
635218822Sdim      ignore_rest_of_line ();
636218822Sdim      return;
637218822Sdim    }
638218822Sdim
639218822Sdim  fde = frchain_now->frch_cfi_data->cur_fde_data;
640218822Sdim  encoding = get_absolute_expression ();
641218822Sdim  if (encoding == DW_EH_PE_omit)
642218822Sdim    {
643218822Sdim      demand_empty_rest_of_line ();
644218822Sdim      fde->per_encoding = encoding;
645218822Sdim      return;
646218822Sdim    }
647218822Sdim
648218822Sdim  if ((encoding & 0xff) != encoding
649218822Sdim      || ((encoding & 0x70) != 0
650218822Sdim#if defined DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr
651218822Sdim	  && (encoding & 0x70) != DW_EH_PE_pcrel
652218822Sdim#endif
653218822Sdim	  )
654218822Sdim	 /* leb128 can be handled, but does something actually need it?  */
655218822Sdim      || (encoding & 7) == DW_EH_PE_uleb128
656218822Sdim      || (encoding & 7) > DW_EH_PE_udata8)
657218822Sdim    {
658218822Sdim      as_bad (_("invalid or unsupported encoding in .cfi_personality"));
659218822Sdim      ignore_rest_of_line ();
660218822Sdim      return;
661218822Sdim    }
662218822Sdim
663218822Sdim  if (*input_line_pointer++ != ',')
664218822Sdim    {
665218822Sdim      as_bad (_(".cfi_personality requires encoding and symbol arguments"));
666218822Sdim      ignore_rest_of_line ();
667218822Sdim      return;
668218822Sdim    }
669218822Sdim
670218822Sdim  expression_and_evaluate (&fde->personality);
671218822Sdim  switch (fde->personality.X_op)
672218822Sdim    {
673218822Sdim    case O_symbol:
674218822Sdim      break;
675218822Sdim    case O_constant:
676218822Sdim      if ((encoding & 0x70) == DW_EH_PE_pcrel)
677218822Sdim	encoding = DW_EH_PE_omit;
678218822Sdim      break;
679218822Sdim    default:
680218822Sdim      encoding = DW_EH_PE_omit;
681218822Sdim      break;
682218822Sdim    }
683218822Sdim
684218822Sdim  fde->per_encoding = encoding;
685218822Sdim
686218822Sdim  if (encoding == DW_EH_PE_omit)
687218822Sdim    {
688218822Sdim      as_bad (_("wrong second argument to .cfi_personality"));
689218822Sdim      ignore_rest_of_line ();
690218822Sdim      return;
691218822Sdim    }
692218822Sdim
693218822Sdim  demand_empty_rest_of_line ();
694218822Sdim}
695218822Sdim
696218822Sdimstatic void
697218822Sdimdot_cfi_lsda (int ignored ATTRIBUTE_UNUSED)
698218822Sdim{
699218822Sdim  struct fde_entry *fde;
700218822Sdim  offsetT encoding;
701218822Sdim
702218822Sdim  if (frchain_now->frch_cfi_data == NULL)
703218822Sdim    {
704218822Sdim      as_bad (_("CFI instruction used without previous .cfi_startproc"));
705218822Sdim      ignore_rest_of_line ();
706218822Sdim      return;
707218822Sdim    }
708218822Sdim
709218822Sdim  fde = frchain_now->frch_cfi_data->cur_fde_data;
710218822Sdim  encoding = get_absolute_expression ();
711218822Sdim  if (encoding == DW_EH_PE_omit)
712218822Sdim    {
713218822Sdim      demand_empty_rest_of_line ();
714218822Sdim      fde->lsda_encoding = encoding;
715218822Sdim      return;
716218822Sdim    }
717218822Sdim
718218822Sdim  if ((encoding & 0xff) != encoding
719218822Sdim      || ((encoding & 0x70) != 0
720218822Sdim#if defined DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr
721218822Sdim	  && (encoding & 0x70) != DW_EH_PE_pcrel
722218822Sdim#endif
723218822Sdim	  )
724218822Sdim	 /* leb128 can be handled, but does something actually need it?  */
725218822Sdim      || (encoding & 7) == DW_EH_PE_uleb128
726218822Sdim      || (encoding & 7) > DW_EH_PE_udata8)
727218822Sdim    {
728218822Sdim      as_bad (_("invalid or unsupported encoding in .cfi_lsda"));
729218822Sdim      ignore_rest_of_line ();
730218822Sdim      return;
731218822Sdim    }
732218822Sdim
733218822Sdim  if (*input_line_pointer++ != ',')
734218822Sdim    {
735218822Sdim      as_bad (_(".cfi_lsda requires encoding and symbol arguments"));
736218822Sdim      ignore_rest_of_line ();
737218822Sdim      return;
738218822Sdim    }
739218822Sdim
740218822Sdim  fde->lsda_encoding = encoding;
741218822Sdim
742218822Sdim  expression_and_evaluate (&fde->lsda);
743218822Sdim  switch (fde->lsda.X_op)
744218822Sdim    {
745218822Sdim    case O_symbol:
746218822Sdim      break;
747218822Sdim    case O_constant:
748218822Sdim      if ((encoding & 0x70) == DW_EH_PE_pcrel)
749218822Sdim	encoding = DW_EH_PE_omit;
750218822Sdim      break;
751218822Sdim    default:
752218822Sdim      encoding = DW_EH_PE_omit;
753218822Sdim      break;
754218822Sdim    }
755218822Sdim
756218822Sdim  fde->lsda_encoding = encoding;
757218822Sdim
758218822Sdim  if (encoding == DW_EH_PE_omit)
759218822Sdim    {
760218822Sdim      as_bad (_("wrong second argument to .cfi_lsda"));
761218822Sdim      ignore_rest_of_line ();
762218822Sdim      return;
763218822Sdim    }
764218822Sdim
765218822Sdim  demand_empty_rest_of_line ();
766218822Sdim}
767218822Sdim
768218822Sdimstatic void
769130561Sobriendot_cfi_startproc (int ignored ATTRIBUTE_UNUSED)
770130561Sobrien{
771130561Sobrien  int simple = 0;
772130561Sobrien
773218822Sdim  if (frchain_now->frch_cfi_data != NULL)
774130561Sobrien    {
775130561Sobrien      as_bad (_("previous CFI entry not closed (missing .cfi_endproc)"));
776218822Sdim      ignore_rest_of_line ();
777130561Sobrien      return;
778130561Sobrien    }
779130561Sobrien
780130561Sobrien  cfi_new_fde (symbol_temp_new_now ());
781130561Sobrien
782130561Sobrien  SKIP_WHITESPACE ();
783130561Sobrien  if (is_name_beginner (*input_line_pointer))
784130561Sobrien    {
785130561Sobrien      char *name, c;
786130561Sobrien
787130561Sobrien      name = input_line_pointer;
788130561Sobrien      c = get_symbol_end ();
789130561Sobrien
790130561Sobrien      if (strcmp (name, "simple") == 0)
791130561Sobrien	{
792130561Sobrien	  simple = 1;
793130561Sobrien	  *input_line_pointer = c;
794130561Sobrien	}
795130561Sobrien      else
796130561Sobrien	input_line_pointer = name;
797130561Sobrien    }
798130561Sobrien  demand_empty_rest_of_line ();
799130561Sobrien
800218822Sdim  frchain_now->frch_cfi_data->cur_cfa_offset = 0;
801130561Sobrien  if (!simple)
802130561Sobrien    tc_cfi_frame_initial_instructions ();
803130561Sobrien}
804130561Sobrien
805130561Sobrienstatic void
806130561Sobriendot_cfi_endproc (int ignored ATTRIBUTE_UNUSED)
807130561Sobrien{
808218822Sdim  if (frchain_now->frch_cfi_data == NULL)
809130561Sobrien    {
810130561Sobrien      as_bad (_(".cfi_endproc without corresponding .cfi_startproc"));
811218822Sdim      ignore_rest_of_line ();
812130561Sobrien      return;
813130561Sobrien    }
814130561Sobrien
815130561Sobrien  cfi_end_fde (symbol_temp_new_now ());
816218822Sdim
817218822Sdim  demand_empty_rest_of_line ();
818130561Sobrien}
819130561Sobrien
820130561Sobrien
821130561Sobrien/* Emit a single byte into the current segment.  */
822130561Sobrien
823130561Sobrienstatic inline void
824130561Sobrienout_one (int byte)
825130561Sobrien{
826130561Sobrien  FRAG_APPEND_1_CHAR (byte);
827130561Sobrien}
828130561Sobrien
829130561Sobrien/* Emit a two-byte word into the current segment.  */
830130561Sobrien
831130561Sobrienstatic inline void
832130561Sobrienout_two (int data)
833130561Sobrien{
834130561Sobrien  md_number_to_chars (frag_more (2), data, 2);
835130561Sobrien}
836130561Sobrien
837130561Sobrien/* Emit a four byte word into the current segment.  */
838130561Sobrien
839130561Sobrienstatic inline void
840130561Sobrienout_four (int data)
841130561Sobrien{
842130561Sobrien  md_number_to_chars (frag_more (4), data, 4);
843130561Sobrien}
844130561Sobrien
845130561Sobrien/* Emit an unsigned "little-endian base 128" number.  */
846130561Sobrien
847130561Sobrienstatic void
848130561Sobrienout_uleb128 (addressT value)
849130561Sobrien{
850130561Sobrien  output_leb128 (frag_more (sizeof_leb128 (value, 0)), value, 0);
851130561Sobrien}
852130561Sobrien
853130561Sobrien/* Emit an unsigned "little-endian base 128" number.  */
854130561Sobrien
855130561Sobrienstatic void
856130561Sobrienout_sleb128 (offsetT value)
857130561Sobrien{
858130561Sobrien  output_leb128 (frag_more (sizeof_leb128 (value, 1)), value, 1);
859130561Sobrien}
860130561Sobrien
861130561Sobrienstatic void
862130561Sobrienoutput_cfi_insn (struct cfi_insn_data *insn)
863130561Sobrien{
864130561Sobrien  offsetT offset;
865130561Sobrien  unsigned int regno;
866130561Sobrien
867130561Sobrien  switch (insn->insn)
868130561Sobrien    {
869130561Sobrien    case DW_CFA_advance_loc:
870130561Sobrien      {
871130561Sobrien	symbolS *from = insn->u.ll.lab1;
872130561Sobrien	symbolS *to = insn->u.ll.lab2;
873130561Sobrien
874130561Sobrien	if (symbol_get_frag (to) == symbol_get_frag (from))
875130561Sobrien	  {
876130561Sobrien	    addressT delta = S_GET_VALUE (to) - S_GET_VALUE (from);
877130561Sobrien	    addressT scaled = delta / DWARF2_LINE_MIN_INSN_LENGTH;
878130561Sobrien
879130561Sobrien	    if (scaled <= 0x3F)
880130561Sobrien	      out_one (DW_CFA_advance_loc + scaled);
881130561Sobrien	    else if (delta <= 0xFF)
882130561Sobrien	      {
883218822Sdim		out_one (DW_CFA_advance_loc1);
884218822Sdim		out_one (delta);
885130561Sobrien	      }
886130561Sobrien	    else if (delta <= 0xFFFF)
887130561Sobrien	      {
888218822Sdim		out_one (DW_CFA_advance_loc2);
889218822Sdim		out_two (delta);
890130561Sobrien	      }
891130561Sobrien	    else
892130561Sobrien	      {
893218822Sdim		out_one (DW_CFA_advance_loc4);
894218822Sdim		out_four (delta);
895130561Sobrien	      }
896130561Sobrien	  }
897130561Sobrien	else
898130561Sobrien	  {
899130561Sobrien	    expressionS exp;
900130561Sobrien
901130561Sobrien	    exp.X_op = O_subtract;
902130561Sobrien	    exp.X_add_symbol = to;
903130561Sobrien	    exp.X_op_symbol = from;
904130561Sobrien	    exp.X_add_number = 0;
905130561Sobrien
906130561Sobrien	    /* The code in ehopt.c expects that one byte of the encoding
907130561Sobrien	       is already allocated to the frag.  This comes from the way
908130561Sobrien	       that it scans the .eh_frame section looking first for the
909130561Sobrien	       .byte DW_CFA_advance_loc4.  */
910130561Sobrien	    frag_more (1);
911130561Sobrien
912130561Sobrien	    frag_var (rs_cfa, 4, 0, DWARF2_LINE_MIN_INSN_LENGTH << 3,
913130561Sobrien		      make_expr_symbol (&exp), frag_now_fix () - 1,
914130561Sobrien		      (char *) frag_now);
915130561Sobrien	  }
916130561Sobrien      }
917130561Sobrien      break;
918130561Sobrien
919130561Sobrien    case DW_CFA_def_cfa:
920130561Sobrien      offset = insn->u.ri.offset;
921130561Sobrien      if (offset < 0)
922130561Sobrien	{
923130561Sobrien	  out_one (DW_CFA_def_cfa_sf);
924130561Sobrien	  out_uleb128 (insn->u.ri.reg);
925218822Sdim	  out_sleb128 (offset / DWARF2_CIE_DATA_ALIGNMENT);
926130561Sobrien	}
927130561Sobrien      else
928130561Sobrien	{
929130561Sobrien	  out_one (DW_CFA_def_cfa);
930130561Sobrien	  out_uleb128 (insn->u.ri.reg);
931130561Sobrien	  out_uleb128 (offset);
932130561Sobrien	}
933130561Sobrien      break;
934130561Sobrien
935130561Sobrien    case DW_CFA_def_cfa_register:
936130561Sobrien    case DW_CFA_undefined:
937130561Sobrien    case DW_CFA_same_value:
938130561Sobrien      out_one (insn->insn);
939130561Sobrien      out_uleb128 (insn->u.r);
940130561Sobrien      break;
941130561Sobrien
942130561Sobrien    case DW_CFA_def_cfa_offset:
943130561Sobrien      offset = insn->u.i;
944130561Sobrien      if (offset < 0)
945130561Sobrien	{
946130561Sobrien	  out_one (DW_CFA_def_cfa_offset_sf);
947218822Sdim	  out_sleb128 (offset / DWARF2_CIE_DATA_ALIGNMENT);
948130561Sobrien	}
949130561Sobrien      else
950130561Sobrien	{
951130561Sobrien	  out_one (DW_CFA_def_cfa_offset);
952130561Sobrien	  out_uleb128 (offset);
953130561Sobrien	}
954130561Sobrien      break;
955130561Sobrien
956130561Sobrien    case DW_CFA_restore:
957130561Sobrien      regno = insn->u.r;
958130561Sobrien      if (regno <= 0x3F)
959130561Sobrien	{
960130561Sobrien	  out_one (DW_CFA_restore + regno);
961130561Sobrien	}
962130561Sobrien      else
963130561Sobrien	{
964130561Sobrien	  out_one (DW_CFA_restore_extended);
965130561Sobrien	  out_uleb128 (regno);
966130561Sobrien	}
967130561Sobrien      break;
968130561Sobrien
969130561Sobrien    case DW_CFA_offset:
970130561Sobrien      regno = insn->u.ri.reg;
971130561Sobrien      offset = insn->u.ri.offset / DWARF2_CIE_DATA_ALIGNMENT;
972130561Sobrien      if (offset < 0)
973130561Sobrien	{
974130561Sobrien	  out_one (DW_CFA_offset_extended_sf);
975130561Sobrien	  out_uleb128 (regno);
976130561Sobrien	  out_sleb128 (offset);
977130561Sobrien	}
978130561Sobrien      else if (regno <= 0x3F)
979130561Sobrien	{
980130561Sobrien	  out_one (DW_CFA_offset + regno);
981130561Sobrien	  out_uleb128 (offset);
982130561Sobrien	}
983130561Sobrien      else
984130561Sobrien	{
985130561Sobrien	  out_one (DW_CFA_offset_extended);
986130561Sobrien	  out_uleb128 (regno);
987130561Sobrien	  out_uleb128 (offset);
988130561Sobrien	}
989130561Sobrien      break;
990130561Sobrien
991130561Sobrien    case DW_CFA_register:
992130561Sobrien      out_one (DW_CFA_register);
993130561Sobrien      out_uleb128 (insn->u.rr.reg1);
994130561Sobrien      out_uleb128 (insn->u.rr.reg2);
995130561Sobrien      break;
996130561Sobrien
997130561Sobrien    case DW_CFA_remember_state:
998130561Sobrien    case DW_CFA_restore_state:
999130561Sobrien      out_one (insn->insn);
1000130561Sobrien      break;
1001130561Sobrien
1002130561Sobrien    case DW_CFA_GNU_window_save:
1003130561Sobrien      out_one (DW_CFA_GNU_window_save);
1004130561Sobrien      break;
1005130561Sobrien
1006130561Sobrien    case CFI_escape:
1007130561Sobrien      {
1008130561Sobrien	struct cfi_escape_data *e;
1009130561Sobrien	for (e = insn->u.esc; e ; e = e->next)
1010130561Sobrien	  emit_expr (&e->exp, 1);
1011130561Sobrien	break;
1012130561Sobrien      }
1013130561Sobrien
1014130561Sobrien    default:
1015130561Sobrien      abort ();
1016130561Sobrien    }
1017130561Sobrien}
1018130561Sobrien
1019218822Sdimstatic offsetT
1020218822Sdimencoding_size (unsigned char encoding)
1021218822Sdim{
1022218822Sdim  if (encoding == DW_EH_PE_omit)
1023218822Sdim    return 0;
1024218822Sdim  switch (encoding & 0x7)
1025218822Sdim    {
1026218822Sdim    case 0:
1027218822Sdim      return bfd_get_arch_size (stdoutput) == 64 ? 8 : 4;
1028218822Sdim    case DW_EH_PE_udata2:
1029218822Sdim      return 2;
1030218822Sdim    case DW_EH_PE_udata4:
1031218822Sdim      return 4;
1032218822Sdim    case DW_EH_PE_udata8:
1033218822Sdim      return 8;
1034218822Sdim    default:
1035218822Sdim      abort ();
1036218822Sdim    }
1037218822Sdim}
1038218822Sdim
1039130561Sobrienstatic void
1040130561Sobrienoutput_cie (struct cie_entry *cie)
1041130561Sobrien{
1042130561Sobrien  symbolS *after_size_address, *end_address;
1043130561Sobrien  expressionS exp;
1044130561Sobrien  struct cfi_insn_data *i;
1045218822Sdim  offsetT augmentation_size;
1046130561Sobrien
1047130561Sobrien  cie->start_address = symbol_temp_new_now ();
1048130561Sobrien  after_size_address = symbol_temp_make ();
1049130561Sobrien  end_address = symbol_temp_make ();
1050130561Sobrien
1051130561Sobrien  exp.X_op = O_subtract;
1052130561Sobrien  exp.X_add_symbol = end_address;
1053130561Sobrien  exp.X_op_symbol = after_size_address;
1054130561Sobrien  exp.X_add_number = 0;
1055130561Sobrien
1056218822Sdim  emit_expr (&exp, 4);				/* Length.  */
1057130561Sobrien  symbol_set_value_now (after_size_address);
1058218822Sdim  out_four (0);					/* CIE id.  */
1059218822Sdim  out_one (DW_CIE_VERSION);			/* Version.  */
1060218822Sdim  out_one ('z');				/* Augmentation.  */
1061218822Sdim  if (cie->per_encoding != DW_EH_PE_omit)
1062218822Sdim    out_one ('P');
1063218822Sdim  if (cie->lsda_encoding != DW_EH_PE_omit)
1064218822Sdim    out_one ('L');
1065130561Sobrien  out_one ('R');
1066218822Sdim  if (cie->signal_frame)
1067218822Sdim    out_one ('S');
1068130561Sobrien  out_one (0);
1069218822Sdim  out_uleb128 (DWARF2_LINE_MIN_INSN_LENGTH);	/* Code alignment.  */
1070218822Sdim  out_sleb128 (DWARF2_CIE_DATA_ALIGNMENT);	/* Data alignment.  */
1071218822Sdim  if (DW_CIE_VERSION == 1)			/* Return column.  */
1072218822Sdim    out_one (cie->return_column);
1073218822Sdim  else
1074218822Sdim    out_uleb128 (cie->return_column);
1075218822Sdim  augmentation_size = 1 + (cie->lsda_encoding != DW_EH_PE_omit);
1076218822Sdim  if (cie->per_encoding != DW_EH_PE_omit)
1077218822Sdim    augmentation_size += 1 + encoding_size (cie->per_encoding);
1078218822Sdim  out_uleb128 (augmentation_size);		/* Augmentation size.  */
1079218822Sdim  if (cie->per_encoding != DW_EH_PE_omit)
1080218822Sdim    {
1081218822Sdim      offsetT size = encoding_size (cie->per_encoding);
1082218822Sdim      out_one (cie->per_encoding);
1083218822Sdim      exp = cie->personality;
1084218822Sdim      if ((cie->per_encoding & 0x70) == DW_EH_PE_pcrel)
1085218822Sdim	{
1086218822Sdim#ifdef DIFF_EXPR_OK
1087218822Sdim	  exp.X_op = O_subtract;
1088218822Sdim	  exp.X_op_symbol = symbol_temp_new_now ();
1089218822Sdim	  emit_expr (&exp, size);
1090218822Sdim#elif defined (tc_cfi_emit_pcrel_expr)
1091218822Sdim	  tc_cfi_emit_pcrel_expr (&exp, size);
1092218822Sdim#else
1093218822Sdim	  abort ();
1094218822Sdim#endif
1095218822Sdim	}
1096218822Sdim      else
1097218822Sdim	emit_expr (&exp, size);
1098218822Sdim    }
1099218822Sdim  if (cie->lsda_encoding != DW_EH_PE_omit)
1100218822Sdim    out_one (cie->lsda_encoding);
1101130561Sobrien#if defined DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr
1102130561Sobrien  out_one (DW_EH_PE_pcrel | DW_EH_PE_sdata4);
1103130561Sobrien#else
1104130561Sobrien  out_one (DW_EH_PE_sdata4);
1105130561Sobrien#endif
1106130561Sobrien
1107130561Sobrien  if (cie->first)
1108130561Sobrien    for (i = cie->first; i != cie->last; i = i->next)
1109130561Sobrien      output_cfi_insn (i);
1110130561Sobrien
1111218822Sdim  frag_align (2, DW_CFA_nop, 0);
1112130561Sobrien  symbol_set_value_now (end_address);
1113130561Sobrien}
1114130561Sobrien
1115130561Sobrienstatic void
1116130561Sobrienoutput_fde (struct fde_entry *fde, struct cie_entry *cie,
1117130561Sobrien	    struct cfi_insn_data *first, int align)
1118130561Sobrien{
1119130561Sobrien  symbolS *after_size_address, *end_address;
1120130561Sobrien  expressionS exp;
1121218822Sdim  offsetT augmentation_size;
1122130561Sobrien
1123130561Sobrien  after_size_address = symbol_temp_make ();
1124130561Sobrien  end_address = symbol_temp_make ();
1125130561Sobrien
1126130561Sobrien  exp.X_op = O_subtract;
1127130561Sobrien  exp.X_add_symbol = end_address;
1128130561Sobrien  exp.X_op_symbol = after_size_address;
1129130561Sobrien  exp.X_add_number = 0;
1130218822Sdim  emit_expr (&exp, 4);				/* Length.  */
1131130561Sobrien  symbol_set_value_now (after_size_address);
1132130561Sobrien
1133130561Sobrien  exp.X_add_symbol = after_size_address;
1134130561Sobrien  exp.X_op_symbol = cie->start_address;
1135218822Sdim  emit_expr (&exp, 4);				/* CIE offset.  */
1136130561Sobrien
1137218822Sdim#ifdef DIFF_EXPR_OK
1138130561Sobrien  exp.X_add_symbol = fde->start_address;
1139130561Sobrien  exp.X_op_symbol = symbol_temp_new_now ();
1140218822Sdim  emit_expr (&exp, 4);				/* Code offset.  */
1141130561Sobrien#else
1142130561Sobrien  exp.X_op = O_symbol;
1143130561Sobrien  exp.X_add_symbol = fde->start_address;
1144130561Sobrien  exp.X_op_symbol = NULL;
1145130561Sobrien#ifdef tc_cfi_emit_pcrel_expr
1146218822Sdim  tc_cfi_emit_pcrel_expr (&exp, 4);		/* Code offset.  */
1147130561Sobrien#else
1148218822Sdim  emit_expr (&exp, 4);				/* Code offset.  */
1149130561Sobrien#endif
1150130561Sobrien  exp.X_op = O_subtract;
1151130561Sobrien#endif
1152130561Sobrien
1153130561Sobrien  exp.X_add_symbol = fde->end_address;
1154218822Sdim  exp.X_op_symbol = fde->start_address;		/* Code length.  */
1155130561Sobrien  emit_expr (&exp, 4);
1156130561Sobrien
1157218822Sdim  augmentation_size = encoding_size (fde->lsda_encoding);
1158218822Sdim  out_uleb128 (augmentation_size);		/* Augmentation size.  */
1159130561Sobrien
1160218822Sdim  if (fde->lsda_encoding != DW_EH_PE_omit)
1161218822Sdim    {
1162218822Sdim      exp = fde->lsda;
1163218822Sdim      if ((fde->lsda_encoding & 0x70) == DW_EH_PE_pcrel)
1164218822Sdim	{
1165218822Sdim#ifdef DIFF_EXPR_OK
1166218822Sdim	  exp.X_op = O_subtract;
1167218822Sdim	  exp.X_op_symbol = symbol_temp_new_now ();
1168218822Sdim	  emit_expr (&exp, augmentation_size);
1169218822Sdim#elif defined (tc_cfi_emit_pcrel_expr)
1170218822Sdim	  tc_cfi_emit_pcrel_expr (&exp, augmentation_size);
1171218822Sdim#else
1172218822Sdim	  abort ();
1173218822Sdim#endif
1174218822Sdim	}
1175218822Sdim      else
1176218822Sdim	emit_expr (&exp, augmentation_size);
1177218822Sdim    }
1178218822Sdim
1179130561Sobrien  for (; first; first = first->next)
1180130561Sobrien    output_cfi_insn (first);
1181130561Sobrien
1182218822Sdim  frag_align (align, DW_CFA_nop, 0);
1183130561Sobrien  symbol_set_value_now (end_address);
1184130561Sobrien}
1185130561Sobrien
1186130561Sobrienstatic struct cie_entry *
1187130561Sobrienselect_cie_for_fde (struct fde_entry *fde, struct cfi_insn_data **pfirst)
1188130561Sobrien{
1189130561Sobrien  struct cfi_insn_data *i, *j;
1190130561Sobrien  struct cie_entry *cie;
1191130561Sobrien
1192130561Sobrien  for (cie = cie_root; cie; cie = cie->next)
1193130561Sobrien    {
1194218822Sdim      if (cie->return_column != fde->return_column
1195218822Sdim	  || cie->signal_frame != fde->signal_frame
1196218822Sdim	  || cie->per_encoding != fde->per_encoding
1197218822Sdim	  || cie->lsda_encoding != fde->lsda_encoding)
1198130561Sobrien	continue;
1199218822Sdim      if (cie->per_encoding != DW_EH_PE_omit)
1200218822Sdim	{
1201218822Sdim	  if (cie->personality.X_op != fde->personality.X_op
1202218822Sdim	      || cie->personality.X_add_number
1203218822Sdim		 != fde->personality.X_add_number)
1204218822Sdim	    continue;
1205218822Sdim	  switch (cie->personality.X_op)
1206218822Sdim	    {
1207218822Sdim	    case O_constant:
1208218822Sdim	      if (cie->personality.X_unsigned != fde->personality.X_unsigned)
1209218822Sdim		continue;
1210218822Sdim	      break;
1211218822Sdim	    case O_symbol:
1212218822Sdim	      if (cie->personality.X_add_symbol
1213218822Sdim		  != fde->personality.X_add_symbol)
1214218822Sdim		continue;
1215218822Sdim	      break;
1216218822Sdim	    default:
1217218822Sdim	      abort ();
1218218822Sdim	    }
1219218822Sdim	}
1220130561Sobrien      for (i = cie->first, j = fde->data;
1221130561Sobrien	   i != cie->last && j != NULL;
1222130561Sobrien	   i = i->next, j = j->next)
1223130561Sobrien	{
1224130561Sobrien	  if (i->insn != j->insn)
1225130561Sobrien	    goto fail;
1226130561Sobrien	  switch (i->insn)
1227130561Sobrien	    {
1228130561Sobrien	    case DW_CFA_advance_loc:
1229218822Sdim	    case DW_CFA_remember_state:
1230218822Sdim	      /* We reached the first advance/remember in the FDE,
1231218822Sdim		 but did not reach the end of the CIE list.  */
1232130561Sobrien	      goto fail;
1233130561Sobrien
1234130561Sobrien	    case DW_CFA_offset:
1235130561Sobrien	    case DW_CFA_def_cfa:
1236130561Sobrien	      if (i->u.ri.reg != j->u.ri.reg)
1237130561Sobrien		goto fail;
1238130561Sobrien	      if (i->u.ri.offset != j->u.ri.offset)
1239130561Sobrien		goto fail;
1240130561Sobrien	      break;
1241130561Sobrien
1242130561Sobrien	    case DW_CFA_register:
1243130561Sobrien	      if (i->u.rr.reg1 != j->u.rr.reg1)
1244130561Sobrien		goto fail;
1245130561Sobrien	      if (i->u.rr.reg2 != j->u.rr.reg2)
1246130561Sobrien		goto fail;
1247130561Sobrien	      break;
1248130561Sobrien
1249130561Sobrien	    case DW_CFA_def_cfa_register:
1250130561Sobrien	    case DW_CFA_restore:
1251130561Sobrien	    case DW_CFA_undefined:
1252130561Sobrien	    case DW_CFA_same_value:
1253130561Sobrien	      if (i->u.r != j->u.r)
1254130561Sobrien		goto fail;
1255130561Sobrien	      break;
1256130561Sobrien
1257130561Sobrien	    case DW_CFA_def_cfa_offset:
1258130561Sobrien	      if (i->u.i != j->u.i)
1259130561Sobrien		goto fail;
1260130561Sobrien	      break;
1261130561Sobrien
1262130561Sobrien	    case CFI_escape:
1263130561Sobrien	      /* Don't bother matching these for now.  */
1264130561Sobrien	      goto fail;
1265130561Sobrien
1266130561Sobrien	    default:
1267130561Sobrien	      abort ();
1268130561Sobrien	    }
1269130561Sobrien	}
1270130561Sobrien
1271130561Sobrien      /* Success if we reached the end of the CIE list, and we've either
1272218822Sdim	 run out of FDE entries or we've encountered an advance,
1273218822Sdim	 remember, or escape.  */
1274218822Sdim      if (i == cie->last
1275218822Sdim	  && (!j
1276218822Sdim	      || j->insn == DW_CFA_advance_loc
1277218822Sdim	      || j->insn == DW_CFA_remember_state
1278218822Sdim	      || j->insn == CFI_escape))
1279130561Sobrien	{
1280130561Sobrien	  *pfirst = j;
1281130561Sobrien	  return cie;
1282130561Sobrien	}
1283130561Sobrien
1284130561Sobrien    fail:;
1285130561Sobrien    }
1286130561Sobrien
1287130561Sobrien  cie = xmalloc (sizeof (struct cie_entry));
1288130561Sobrien  cie->next = cie_root;
1289130561Sobrien  cie_root = cie;
1290130561Sobrien  cie->return_column = fde->return_column;
1291218822Sdim  cie->signal_frame = fde->signal_frame;
1292218822Sdim  cie->per_encoding = fde->per_encoding;
1293218822Sdim  cie->lsda_encoding = fde->lsda_encoding;
1294218822Sdim  cie->personality = fde->personality;
1295130561Sobrien  cie->first = fde->data;
1296130561Sobrien
1297130561Sobrien  for (i = cie->first; i ; i = i->next)
1298218822Sdim    if (i->insn == DW_CFA_advance_loc
1299218822Sdim	|| i->insn == DW_CFA_remember_state
1300218822Sdim	|| i->insn == CFI_escape)
1301130561Sobrien      break;
1302130561Sobrien
1303130561Sobrien  cie->last = i;
1304130561Sobrien  *pfirst = i;
1305130561Sobrien
1306130561Sobrien  output_cie (cie);
1307130561Sobrien
1308130561Sobrien  return cie;
1309130561Sobrien}
1310130561Sobrien
1311130561Sobrienvoid
1312130561Sobriencfi_finish (void)
1313130561Sobrien{
1314130561Sobrien  segT cfi_seg;
1315130561Sobrien  struct fde_entry *fde;
1316130561Sobrien  int save_flag_traditional_format;
1317130561Sobrien
1318130561Sobrien  if (all_fde_data == 0)
1319130561Sobrien    return;
1320130561Sobrien
1321130561Sobrien  /* Open .eh_frame section.  */
1322130561Sobrien  cfi_seg = subseg_new (".eh_frame", 0);
1323130561Sobrien  bfd_set_section_flags (stdoutput, cfi_seg,
1324130561Sobrien			 SEC_ALLOC | SEC_LOAD | SEC_DATA | SEC_READONLY);
1325130561Sobrien  subseg_set (cfi_seg, 0);
1326130561Sobrien  record_alignment (cfi_seg, EH_FRAME_ALIGNMENT);
1327130561Sobrien
1328130561Sobrien  /* Make sure check_eh_frame doesn't do anything with our output.  */
1329130561Sobrien  save_flag_traditional_format = flag_traditional_format;
1330130561Sobrien  flag_traditional_format = 1;
1331130561Sobrien
1332130561Sobrien  for (fde = all_fde_data; fde ; fde = fde->next)
1333130561Sobrien    {
1334130561Sobrien      struct cfi_insn_data *first;
1335130561Sobrien      struct cie_entry *cie;
1336130561Sobrien
1337218822Sdim      if (fde->end_address == NULL)
1338218822Sdim	{
1339218822Sdim	  as_bad (_("open CFI at the end of file; missing .cfi_endproc directive"));
1340218822Sdim	  fde->end_address = fde->start_address;
1341218822Sdim	}
1342218822Sdim
1343130561Sobrien      cie = select_cie_for_fde (fde, &first);
1344130561Sobrien      output_fde (fde, cie, first, fde->next == NULL ? EH_FRAME_ALIGNMENT : 2);
1345130561Sobrien    }
1346130561Sobrien
1347130561Sobrien  flag_traditional_format = save_flag_traditional_format;
1348130561Sobrien}
1349