This is the mail archive of the binutils@sources.redhat.com mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] CFI directives for GAS


Hi all,
the attached patch introduces some new pseudo-ops for easier generating of Dwarf2 CFI unwind info. This is especially needed for unwinding over hand-written assembler routines on amd64 target, where no standard prologues are used.


Supported directives are for now:
.cfi_verbose [1|0] - enable/disable verbose mode. 'as' will print out
    the .eh_frame contents during it's generation. Or use --verbose
    switch to 'as' command.

.cfi_startproc - use at the beginning of each function. It initializes
    some internal data structures and emits initial CFI
    instructions.

.cfi_endproc - opens .eh_frame, generates appropriate binary structures
    (CIE, FDE) and sets up relocation records.

.cfi_def_cfa <reg>,<imm> - set a rule for computing CFA to: take content
    of register <reg> and add <imm> to it.

.cfi_def_cfa_register <reg> - change rule for CFA to use <reg>. Offset
    remains the same.

.cfi_def_cfa_offset <imm> - change rule for CFA to use offset <imm>.
    Register remains the same.

.cfi_adjust_cfa_offset <imm> - Like the previous one but <imm> is a
    relative value that will be added to current offset instead of
    an absolute value as in .cfi_def_cfa_offset.

.cfi_offset <reg>,<imm> - Generate rule saying that register <reg> is
    saved at offset <imm> from CFA.

Side note: CFA_advance_loc* macros are used automatically if needed whenever a .cfi_* directive is met. So don't care about them :-)

For now the architectures that have these new directives enabled are i386 and amd64.

Commented example on the usage can be found here:
http://www.logix.cz/~mic/devel/gas-cfi/example.s.html

How do you like this idea? Any comments? Can I apply the patch?

Michal Ludvig
--
* SuSE CR, s.r.o     * mludvig@suse.cz
* (+420) 296.545.373 * http://www.suse.cz
2003-05-15  Michal Ludvig  <mludvig@suse.cz>

	* dw2gencfi.c, dw2gencfi.h: New files.
	* config/tc-i386.c (tc_x86_cfi_init): New function.
	* config/tc-i386.h (TARGET_USE_CFIPOP, tc_cfi_init): New
	defines.
	* as.c (parse_args): Set verbose flag on --verbose.
	(main): Call tc_cfi_init()/cfi_finish().
	* as.h (verbose): New external variable.
	* read.c (pobegin): Insert CFI pops to the list.
	* symbols.c (local_symbol_make): Make symbol external.
	* symbols.h (local_symbol_make): New prototype.
	* Makefile.am, Makefile.in: Add dw2gencfi.[ch] files.

Index: dw2gencfi.c
===================================================================
RCS file: dw2gencfi.c
diff -N dw2gencfi.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ dw2gencfi.c	15 May 2003 12:18:20 -0000
@@ -0,0 +1,784 @@
+/* dw2gencfi.c - Support for generating Dwarf2 CFI information.
+   Copyright 2003 Free Software Foundation, Inc.
+   Contributed by Michal Ludvig <mludvig@suse.cz>
+
+   This file is part of GAS, the GNU Assembler.
+
+   GAS is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   GAS is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GAS; see the file COPYING.  If not, write to the Free
+   Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA.  */
+
+#include <errno.h>
+#include "as.h"
+#include "dw2gencfi.h"
+
+/* Current target config.  */
+static struct cfi_config current_config;
+
+/* This is the main entry point to the CFI machinery.  */
+static void dot_cfi (int arg);
+
+/* Codes of CFI instructions taken from Dwarf2 standard.  */
+enum cfi_insn { 
+  CFA_nop = 0x0,
+  CFA_set_loc = 0x01,
+  CFA_advance_loc1 = 0x02,
+  CFA_advance_loc2 = 0x03,
+  CFA_advance_loc4 = 0x04,
+  CFA_offset_extended = 0x05,
+  CFA_resotre_extended = 0x06,
+  CFA_undefined = 0x07,
+  CFA_same_value = 0x08,
+  CFA_register = 0x09,
+  CFA_remember_state = 0x0a,
+  CFA_restore_state = 0x0b,
+  CFA_def_cfa = 0x0c, 
+  CFA_def_cfa_register = 0x0d, 
+  CFA_def_cfa_offset = 0x0e, 
+  CFA_advance_loc = 0x40,
+  CFA_offset = 0x80,
+  CFA_restore = 0xc0,
+  
+  /* These don't belong to the standard.  */
+  CFI_startproc = 0xff00,
+  CFI_endproc = 0xff01,
+  CFI_adjust_cfa_offset = 0xff10,
+  CFI_verbose = 0xffff
+};
+
+const pseudo_typeS cfi_pseudo_table[] = {
+  {"cfi_verbose", dot_cfi, CFI_verbose},
+  {"cfi_startproc", dot_cfi, CFI_startproc},
+  {"cfi_endproc", dot_cfi, CFI_endproc},
+  {"cfi_def_cfa", dot_cfi, CFA_def_cfa},
+  {"cfi_def_cfa_register", dot_cfi, CFA_def_cfa_register},
+  {"cfi_def_cfa_offset", dot_cfi, CFA_def_cfa_offset},
+  {"cfi_adjust_cfa_offset", dot_cfi, CFI_adjust_cfa_offset},
+  {"cfi_offset", dot_cfi, CFA_offset},
+  {NULL, NULL, 0}
+};
+
+static int
+reg_name_to_dw2_num (char *regname)
+{
+  char *regnames[] = { 
+    "rax", "rbx", "rcx", "rdx",
+    "rdi", "rsi", "rbp", "rsp",
+    "r8", "r9", "r10", "r11",
+    "r12", "r13", "r14", "r15",
+    "rip" };
+  unsigned int regnum;
+
+  for (regnum = 0; regnum < sizeof (regnames); regnum++)
+    if (strcmp (regname, regnames[regnum]) == 0)
+      return regnum;
+
+  as_bad ("Unknown register name '%s'.", regname);
+  return -1;
+}
+
+static const char *
+cfi_insn_str (enum cfi_insn insn)
+{
+  switch (insn)
+  {
+    case CFA_nop:
+      return "CFA_nop";
+    case CFA_set_loc:
+      return "CFA_set_loc";
+    case CFA_advance_loc1:
+      return "CFA_advance_loc1";
+    case CFA_advance_loc2:
+      return "CFA_advance_loc2";
+    case CFA_advance_loc4:
+      return "CFA_advance_loc4";
+    case CFA_offset_extended:
+      return "CFA_offset_extended";
+    case CFA_resotre_extended:
+      return "CFA_resotre_extended";
+    case CFA_undefined:
+      return "CFA_undefined";
+    case CFA_same_value:
+      return "CFA_same_value";
+    case CFA_register:
+      return "CFA_register";
+    case CFA_remember_state:
+      return "CFA_remember_state";
+    case CFA_restore_state:
+      return "CFA_restore_state";
+    case CFA_def_cfa:
+      return "CFA_def_cfa";
+    case CFA_def_cfa_register:
+      return "CFA_def_cfa_register";
+    case CFA_def_cfa_offset:
+      return "CFA_def_cfa_offset";
+    case CFA_advance_loc:
+      return "CFA_advance_loc";
+    case CFA_offset:
+      return "CFA_offset";
+    case CFA_restore:
+      return "CFA_restore";
+    default:
+      break;
+  }
+
+  return "CFA_unknown";
+}
+
+struct cfi_data {
+  enum cfi_insn insn;
+  long param[2];
+  struct cfi_data *next;
+};
+
+struct cfi_info {
+  addressT start_address;
+  addressT end_address;
+  addressT last_address;
+  int closed;
+  const char *labelname;
+  struct cfi_data *data;
+  struct cfi_info *next;
+};
+
+static struct cfi_info *cfi_root, *cfi_last;
+
+static struct cfi_data *alloc_cfi_data(void);
+static struct cfi_data *
+alloc_cfi_data ()
+{
+  struct cfi_data *ret = calloc (sizeof (struct cfi_info), 1);
+  if (!ret)
+    as_fatal ("alloc_cfi_data(): %s", strerror (errno));
+  return ret;
+}
+
+static struct cfi_info *alloc_cfi_info(void);
+static struct cfi_info *
+alloc_cfi_info ()
+{
+  struct cfi_info *ret = calloc (sizeof (struct cfi_info), 1);
+  if (!ret)
+    as_fatal ("alloc_cfi_info(): %s", strerror (errno));
+  return ret;
+}
+
+/* Parse arguments */
+static int
+cfi_parse_arg (long *param, int resolvereg)
+{
+  char *name, c, *p;
+  long value;
+  int retval = -1;
+  
+  assert (param != NULL);
+  SKIP_WHITESPACE ();
+  
+  if (sscanf (input_line_pointer, "%li", &value) == 1)
+  {
+    while ((*input_line_pointer >= '1' && 
+	    *input_line_pointer <= '9') ||
+	   (*input_line_pointer == '0') ||
+	   (*input_line_pointer == '-') ||
+	   (*input_line_pointer == 'x') ||
+	   (*input_line_pointer == 'X'))
+      input_line_pointer++;
+
+    retval = 1;
+  }
+  else if (resolvereg && (is_name_beginner (*input_line_pointer)))
+  {
+    name = input_line_pointer;
+    c = get_symbol_end ();
+    p = input_line_pointer;
+
+    if ((value = reg_name_to_dw2_num (name)) >= 0)
+      retval = 1;
+    
+    *p = c;
+  }
+  else
+    as_bad ("Can't convert argument to %s.",
+	    resolvereg ? "register number" : "integer");
+  
+  if (retval > 0)
+    *param = value;
+
+  SKIP_WHITESPACE ();
+  if (*input_line_pointer == ',')
+  {
+    input_line_pointer++;
+    SKIP_WHITESPACE ();
+  }
+  
+  return retval;
+}
+
+static int
+cfi_parse_reg (long *param)
+{
+  return cfi_parse_arg (param, 1);
+}
+
+static int
+cfi_parse_const (long *param)
+{
+  return cfi_parse_arg (param, 0);
+}
+
+static void
+cfi_add_insn (struct cfi_info *info, enum cfi_insn insn, 
+	      long param0, long param1)
+{
+  struct cfi_data *data_ptr;
+  
+  if (!info->data)
+  {
+    info->data = alloc_cfi_data ();
+    data_ptr = info->data;
+  }
+  else
+  {
+    data_ptr = info->data;
+
+    while (data_ptr && data_ptr->next)
+      data_ptr = data_ptr->next;
+    
+    data_ptr->next = alloc_cfi_data ();
+
+    data_ptr = data_ptr->next;
+  }
+    
+  data_ptr->insn = insn;
+  data_ptr->param[0] = param0;
+  data_ptr->param[1] = param1;
+}
+
+static void
+cfi_advance_loc (void)
+{
+  addressT curr_address = frag_now_fix ();
+  if (cfi_last->last_address == curr_address)
+    return;
+  cfi_add_insn (cfi_last, CFA_advance_loc, 
+		(long)(curr_address - cfi_last->last_address), 0);
+  cfi_last->last_address = curr_address;
+}
+
+static long
+get_current_offset (struct cfi_info *info)
+{
+  long current_offset = 0;
+  struct cfi_data *data = info->data;
+  
+  current_offset = 0;
+  while (data)
+  {
+    if (data->insn == CFA_def_cfa)
+      current_offset = data->param[1];
+    else if (data->insn == CFA_def_cfa_offset)
+      current_offset = data->param[0];
+    data = data->next;
+  }
+
+  return current_offset;
+}
+
+static void
+cfi_make_insn (int arg)
+{
+  long param[2] = { 0, 0 };
+  
+  if (!cfi_last || cfi_last->closed)
+  {
+    as_bad ("CFI instruction used without previous .cfi_startproc!");
+    return;
+  }
+
+  cfi_advance_loc ();
+
+  switch (arg)
+  {
+    /* Instructions that take two arguments (register, integer). */
+    case CFA_offset:
+    case CFA_def_cfa:
+      if (cfi_parse_reg (&param[0]) < 0)
+      {
+	as_bad ("First argument to %s is not a register.",
+		cfi_insn_str (arg));
+	return;
+      }
+      if (cfi_parse_const (&param[1]) < 0)
+      {
+	as_bad ("Second argument to %s is not a number.",
+		cfi_insn_str (arg));
+	return;
+      }
+      break;
+
+    /* Instructions that take one register argument. */
+    case CFA_def_cfa_register:
+      if (cfi_parse_reg (&param[0]) < 0)
+      {
+	as_bad ("Argument to %s is not a register.", cfi_insn_str (arg));
+	return;
+      }
+      break;
+
+    /* Instructions that take one integer argument. */
+    case CFA_def_cfa_offset:
+      if (cfi_parse_const (&param[0]) < 0)
+      {
+	as_bad ("Argument to %s is not a number.", cfi_insn_str (arg));
+	return;
+      }
+      break;
+    
+    /* Special handling for pseudo-instruction. */
+    case CFI_adjust_cfa_offset:
+      if (cfi_parse_const (&param[0]) < 0)
+      {
+	as_bad ("Argument to .cfi_adjust_cfa_offset is not a number.");
+	return;
+      }
+      param[0] += get_current_offset (cfi_last);
+      arg = CFA_def_cfa_offset;
+      break;
+
+    default:
+      as_bad ("Unknown CFI instruction %d (%s).", arg, cfi_insn_str (arg));
+      return;
+  }
+  cfi_add_insn (cfi_last, arg, param[0], param[1]);
+}
+
+static symbolS *
+cfi_get_label (void)
+{
+  char symname[40], *symbase=".Llbl_cfi";
+  symbolS *symbolP;
+  unsigned int i;
+
+  snprintf (symname, sizeof (symname), "%s_0x%lx",
+	    symbase, (long) frag_now_fix ());
+  while ((symbolP = symbol_find (symname)))
+  {
+    if ((S_GET_VALUE (symbolP) == frag_now_fix ())
+	&& (S_GET_SEGMENT (symbolP) == now_seg))
+    {
+      return symbolP;
+    }
+    snprintf (symname, sizeof (symname), "%s_0x%lx_%u",
+	      symbase, (long) frag_now_fix (), i++);
+  }
+  symbolP = (symbolS *) local_symbol_make (symname, now_seg,
+				    (valueT) frag_now_fix (),
+				    frag_now);
+  return symbolP;
+}
+
+static void
+dot_cfi_startproc (void)
+{
+  if (cfi_last && !cfi_last->closed)
+    as_fatal ("Previous CFI entry not closed (missing .cfi_endproc).");
+
+  if (!cfi_root)
+  {
+    cfi_root = alloc_cfi_info ();
+    cfi_last = cfi_root;
+  }
+  else
+  {
+    cfi_last->next = alloc_cfi_info ();
+    cfi_last = cfi_last->next;
+  }
+
+  cfi_last->start_address = frag_now_fix ();
+  cfi_last->last_address = cfi_last->start_address;
+  cfi_last->labelname = S_GET_NAME (cfi_get_label ());
+
+  cfi_add_insn (cfi_last, CFA_def_cfa, reg_name_to_dw2_num ("rsp"), 8);
+  cfi_add_insn (cfi_last, CFA_offset, reg_name_to_dw2_num ("rip"), -8);
+}
+
+#define cfi_is_advance_insn(insn) \
+	((insn >= CFA_set_loc && insn <= CFA_advance_loc4) || \
+	 insn == CFA_advance_loc)
+
+/* Output CFI instructions to the file */
+
+enum data_types { t_ascii = 0, t_byte = 1, t_half = 2,  
+  t_long = 4, t_quad = 8, t_uleb128 = 0x10, t_sleb128 = 0x11 };
+
+static int
+output_data (char **p, unsigned long *size, 
+	     enum data_types type, long value)
+{
+  char *ptr = *p;
+  unsigned int ret_size;
+
+  switch (type)
+  {
+    case t_byte:
+      ret_size = 1;
+      break;
+    case t_half:
+      ret_size = 2;
+      break;
+    case t_long:
+      ret_size = 4;
+      break;
+    case t_quad:
+    case t_uleb128:
+    case t_sleb128:
+      ret_size = 8;
+      break;
+    default:
+      as_warn ("unknown type %d\n", type);
+      return 0;
+  }
+
+  if (*size < ret_size)
+  {
+    as_warn ("Internal error: buffer is too small in output_data()");
+    return 0;
+  }
+  
+  switch (type)
+  {
+    case t_byte:
+      *ptr = (char) value;
+      if (verbose)
+	printf ("\t.byte\t0x%x\n", (unsigned char) *ptr);
+      break;
+    case t_half:
+      *(short *)ptr = (short) value & 0xFFFF;
+      if (verbose)
+	printf ("\t.half\t0x%x\n", (unsigned short) *ptr);
+      break;
+    case t_long:
+      *(int *)ptr = (int) value & 0xFFFFFFFF;
+      if (verbose)
+	printf ("\t.long\t0x%x\n", (unsigned int) *ptr);
+      break;
+    case t_quad:
+      *(long long *)ptr = (long long) value & 0xFFFFFFFF;
+      if (verbose)
+	printf ("\t.quad\t0x%x\n", (unsigned int) *ptr);
+      break;
+    case t_uleb128:
+    case t_sleb128:
+      ret_size = output_leb128 (ptr, value, type == t_sleb128);
+      if (verbose)
+	printf ("\t.%s\t0x%lx\n", type==t_sleb128?"sleb128":"uleb128", value);
+      break;
+    default:
+      as_warn ("unknown type %d\n", type);
+      return 0;
+  }
+
+  *size -= ret_size;
+  *p += ret_size;
+
+  return ret_size;
+}
+
+static int 
+cfi_output_insn (struct cfi_info *info, struct cfi_data *data, 
+	       char **buf, unsigned long *buf_size)
+{
+  char **pbuf = buf, *orig_buf = *buf;
+  unsigned long size;
+
+  if (!info || !data || !buf)
+    as_fatal ("make_dw2_insn() called with NULL pointer...");
+
+  switch (data->insn)
+  {
+    case CFA_advance_loc:
+      if (verbose)
+	printf ("\t# %s(%ld)\n", cfi_insn_str (data->insn),
+		data->param[0]);
+      if (data->param[0] <= 0x3F)
+      {
+	output_data (pbuf, buf_size, t_byte, CFA_advance_loc + 
+		     (data->param[0] / current_config.code_align));
+      }
+      else if (data->param[0] <= 0xFF)
+      {
+	output_data (pbuf, buf_size, t_byte, CFA_advance_loc1);
+	output_data (pbuf, buf_size, t_byte,
+		     data->param[0] / current_config.code_align);
+      }
+      else if (data->param[0] <= 0xFFFF)
+      {
+	output_data (pbuf, buf_size, t_byte, CFA_advance_loc2);
+	output_data (pbuf, buf_size, t_half,
+		     data->param[0] / current_config.code_align);
+      }
+      else
+      {
+	output_data (pbuf, buf_size, t_byte, CFA_advance_loc4);
+	output_data (pbuf, buf_size, t_long,
+		     data->param[0] / current_config.code_align);
+      }
+      break;
+
+    case CFA_def_cfa:
+      if (verbose)
+	printf ("\t# CFA_def_cfa(%ld,%ld)\n", data->param[0], data->param[1]);
+      output_data (pbuf, buf_size, t_byte, CFA_def_cfa);
+      output_data (pbuf, buf_size, t_uleb128, data->param[0]);
+      output_data (pbuf, buf_size, t_uleb128, data->param[1]);
+      break;
+
+    case CFA_def_cfa_register:
+    case CFA_def_cfa_offset:
+      if (verbose)
+	printf ("\t# %s(%ld)\n", cfi_insn_str (data->insn), 
+		data->param[0]);
+      output_data (pbuf, buf_size, t_byte, data->insn);
+      output_data (pbuf, buf_size, t_uleb128, data->param[0]);
+      break;
+
+    case CFA_offset:
+      if (verbose)
+	printf ("\t# %s(%ld,%ld)\n", cfi_insn_str (data->insn),
+		data->param[0], data->param[1]);
+
+      /* Check whether to use CFA_offset or CFA_offset_extended */
+      if (data->param[0] <= 0x3F)
+	output_data (pbuf, buf_size, t_byte, CFA_offset + data->param[0]);
+      else
+      {
+      	output_data (pbuf, buf_size, t_byte, CFA_offset_extended);
+      	output_data (pbuf, buf_size, t_uleb128, data->param[0]);
+      }
+      output_data (pbuf, buf_size, t_uleb128, 
+		   data->param[1] / current_config.data_align);
+      break;
+
+    case CFA_nop:
+      if (verbose)
+	printf ("\t# CFA_nop\n");
+      output_data (pbuf, buf_size, t_byte, CFA_nop);
+      break;
+
+    default:
+      as_warn ("CFA_unknown[%d](%ld,%ld)\n", data->insn,
+	      data->param[0], data->param[1]);
+  }
+  size = *pbuf - orig_buf;
+  *buf = *pbuf;
+  *buf_size -= size;
+  return size;
+}
+
+static void
+dot_cfi_endproc (void)
+{
+  struct cfi_data *data_ptr;
+  char *cie_buf, *fde_buf, *pbuf, *where;
+  unsigned long  buf_size, cie_size, fde_size, last_cie_offset;
+  unsigned long	fde_initloc_offset, fde_len_offset;
+  void *saved_seg, *cfi_seg;
+  
+  if (! cfi_last || cfi_last->closed)
+  {
+    as_fatal (".cfi_endproc w/o corresponding .cfi_startproc");
+    return;
+  }
+  cfi_last->end_address = frag_now_fix ();
+  cfi_last->closed = 1;
+  
+  /* Open .eh_frame section.  */
+  saved_seg = now_seg;
+  cfi_seg = subseg_new (".eh_frame", 0);
+  bfd_set_section_flags (stdoutput, cfi_seg, 
+			 SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA); 
+  subseg_set (cfi_seg, 0);
+  
+  /* Build CIE.  */
+  cie_buf = calloc (1024, 1);
+  pbuf = cie_buf + 4;	/* skip space for CIE length */
+  buf_size = 1020;
+
+  if (verbose) printf ("# CIE *****\n");
+
+  /* CIE id */
+  output_data (&pbuf, &buf_size, t_long, 0x0);
+  /* Version */
+  output_data (&pbuf, &buf_size, t_byte, 1);
+  /* Augmentation */
+  output_data (&pbuf, &buf_size, t_byte, 0);
+  /* Code alignment */
+  output_data (&pbuf, &buf_size, t_uleb128, current_config.code_align);
+  /* Data alignment */
+  output_data (&pbuf, &buf_size, t_sleb128, current_config.data_align);
+  /* Return address column */
+  output_data (&pbuf, &buf_size, t_byte, current_config.ra_column);
+  
+  /* Build CFI instructions.  */
+  data_ptr = cfi_last->data;
+  while (data_ptr && !cfi_is_advance_insn (data_ptr->insn))
+  {
+    cfi_output_insn (cfi_last, data_ptr, &pbuf, &buf_size);
+    data_ptr = data_ptr->next;
+  }
+
+  /* Align the whole data to current_config.eh_align */
+  cie_size = pbuf - cie_buf;
+  cie_size += current_config.eh_align - cie_size % current_config.eh_align;
+
+  /* CIE length */
+  pbuf = cie_buf;
+  output_data (&pbuf, &buf_size, t_long, cie_size - 4);
+  
+  /* OK, we built the CIE. Let's write it to the file...  */
+  last_cie_offset = frag_now_fix();
+  where = (unsigned char *) frag_more (cie_size);
+  memcpy (where, cie_buf, cie_size);
+  
+  /* Clean up */
+  free (cie_buf);
+
+  /* Build the FDE... */
+  fde_buf = calloc (1024, 1);
+  pbuf = fde_buf;
+  buf_size = 1024;
+
+  if (verbose)
+  {
+    printf ("# FDE: start=0x%lx, end=0x%lx, delta=%d\n",
+	    (long) cfi_last->start_address,
+	    (long) cfi_last->end_address,
+	    (int) (cfi_last->end_address - cfi_last->start_address));
+  }
+
+  /* FDE length (t_long, 4 bytes) - will be set later */
+  fde_len_offset = pbuf - fde_buf;
+  pbuf += 4;
+  buf_size -= 4;
+
+  /* CIE pointer - offset from here */
+  output_data (&pbuf, &buf_size, t_long, cie_size + 4);
+
+  /* FDE initial location - this must be set relocatable! */
+  fde_initloc_offset = pbuf - fde_buf;
+  output_data (&pbuf, &buf_size, current_config.addr_length,
+	       cfi_last->start_address);
+  
+  /* FDE address range */
+  output_data (&pbuf, &buf_size, current_config.addr_length, 
+	       cfi_last->end_address - cfi_last->start_address);
+
+  while (data_ptr)
+  {
+    cfi_output_insn (cfi_last, data_ptr, &pbuf, &buf_size);
+    data_ptr = data_ptr->next;
+  }
+
+  fde_size = pbuf - fde_buf;
+  fde_size += current_config.eh_align - fde_size % current_config.eh_align;
+  
+  /* Now we can set FDE length.  */
+  pbuf = fde_buf + fde_len_offset;
+  buf_size = 4;
+  output_data (&pbuf, &buf_size, t_long, fde_size - 4);
+  
+  /* Adjust initloc offset */
+  fde_initloc_offset += frag_now_fix();
+  
+  /* Copy FDE to objfile.  */
+  where = (unsigned char *) frag_more (fde_size);
+  memcpy (where, fde_buf, fde_size);
+  
+  /* Set relocation for initial address.  */
+  buf_size = current_config.addr_length;
+  expressionS exp;
+  memset (&exp, 0, sizeof (exp));
+  exp.X_op = O_symbol;
+  exp.X_add_symbol = symbol_find (cfi_last->labelname);
+  fix_new_exp (frag_now, fde_initloc_offset,
+	       current_config.addr_length, 
+	       &exp, 0, current_config.reloc_type);
+
+  /* Clean up. */
+  free (fde_buf);
+
+  /* Restore previous segment.  */
+  subseg_set (saved_seg, 0);
+}
+
+void
+dot_cfi (arg)
+     int arg;
+{
+  long param;
+  
+  switch (arg)
+  {
+    case CFI_startproc:
+      dot_cfi_startproc ();
+      break;
+    case CFI_endproc:
+      dot_cfi_endproc ();
+      break;
+    case CFA_def_cfa:
+    case CFA_def_cfa_register:
+    case CFA_def_cfa_offset:
+    case CFA_offset:
+    case CFI_adjust_cfa_offset:
+      cfi_make_insn (arg);
+      break;
+    case CFI_verbose:
+      if (cfi_parse_const (&param) >= 0)
+	verbose = (int) param;
+      else
+	verbose = 1;
+      break;
+    default:
+      as_bad ("Unknown CFI code 0x%x (%s)", arg, cfi_insn_str (arg));
+      break;
+  }
+  ignore_rest_of_line ();
+}
+
+void
+cfi_set_config (struct cfi_config *cfg)
+{
+  assert (cfg != NULL);
+  assert (cfg->addr_length > 0);
+
+  current_config = *cfg;
+
+  if (verbose)
+  {
+    printf ("CFI current config:\n");
+    printf ("\taddr_length = %u\n", current_config.addr_length);
+    printf ("\teh_align = %u\n", current_config.eh_align);
+    printf ("\tcode_align = %d\n", current_config.code_align);
+    printf ("\tdata_align = %d\n", current_config.data_align);
+    printf ("\tra_column = %d\n", current_config.ra_column);
+    printf ("\treloc_type = %d\n", current_config.reloc_type);
+  }
+}
+
+void 
+cfi_finish (void)
+{
+  if (cfi_last && ! cfi_last->closed)
+    as_bad ("Open CFI at the ond of file. Missing .cfi_endproc directive");
+}
Index: dw2gencfi.h
===================================================================
RCS file: dw2gencfi.h
diff -N dw2gencfi.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ dw2gencfi.h	15 May 2003 12:18:20 -0000
@@ -0,0 +1,65 @@
+/* dw2gencfi.h - Support for generating Dwarf2 CFI information.
+   Copyright 2003 Free Software Foundation, Inc.
+   Contributed by Michal Ludvig <mludvig@suse.cz>
+
+   This file is part of GAS, the GNU Assembler.
+
+   GAS is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   GAS is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GAS; see the file COPYING.  If not, write to the Free
+   Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA.  */
+
+#ifndef DW2GENCFI_H
+#define DW2GENCFI_H
+
+struct cfi_config {
+  /* Target address length in bytes. (usually 4 or 8).
+     Round it up for archs like S/390 with 31b addresses.  */
+  unsigned int addr_length;
+  
+  /* Alignment of .eh_frame blocks in bytes (usually 1, 4 or 8).  */
+  unsigned int eh_align;
+  
+  /* Code alignment (1 for x86/amd64 machines, 4 or 8 for 
+     RISC machines). Consult Dwarf2 standard for details.  */
+  int code_align;
+
+  /* Data (stack) alignment (-4 on x86, -8 on amd64, something
+     positive on archs where stack grows up).  Consult Dwarf2 
+     standard for details.  */
+  int data_align;
+
+  /* Return address column (0x8 on x86, 0x10 on amd64).  Consult 
+     Dwarf2 standard for details.  */
+  int ra_column;
+
+  /* Relocation type for init_addr FDE record. (BFD_RELOC_64 
+     on amd64).  */
+  int reloc_type;
+};
+
+extern const pseudo_typeS cfi_pseudo_table[];
+
+/* Insert .cfi_* directives to the list of pseudo-ops.  */
+void cfi_pop_insert PARAMS ((void));
+
+/* Change setup of the CFI machinery.  This change won't 
+   affect already generated CIEs/FDEs. 
+   Note that CIE/FDE is generated in .cfi_endproc handler.  */
+void cfi_set_config PARAMS ((struct cfi_config *cfg));
+
+/* This is called at the end of file. It will complain if 
+   the last CFI wasn't properly closed by .cfi_endproc.  */
+void cfi_finish PARAMS ((void));
+
+#endif /* DW2GENCFI_H */
Index: config/tc-i386.c
===================================================================
RCS file: /cvs/src/src/gas/config/tc-i386.c,v
retrieving revision 1.138
diff -u -p -r1.138 tc-i386.c
--- config/tc-i386.c	29 Jan 2003 10:05:52 -0000	1.138
+++ config/tc-i386.c	15 May 2003 12:18:21 -0000
@@ -30,6 +30,7 @@
 #include "safe-ctype.h"
 #include "subsegs.h"
 #include "dwarf2dbg.h"
+#include "dw2gencfi.h"
 #include "opcode/i386.h"
 
 #ifndef REGISTER_WARNINGS
@@ -6298,4 +6299,31 @@ intel_putback_token ()
   prev_token.code = T_NIL;
   prev_token.reg = NULL;
   prev_token.str = NULL;
+}
+
+void
+tc_x86_cfi_init (void)
+{
+  struct cfi_config cfi_config;
+  
+  if (CODE_64BIT)
+  {
+    cfi_config.addr_length = 8;
+    cfi_config.eh_align = 8;
+    cfi_config.code_align = 1;
+    cfi_config.data_align = -8;
+    cfi_config.ra_column = 0x10;
+    cfi_config.reloc_type = BFD_RELOC_64;
+  }
+  else
+  {
+    cfi_config.addr_length = 4;
+    cfi_config.eh_align = 4;
+    cfi_config.code_align = 1;
+    cfi_config.data_align = -4;
+    cfi_config.ra_column = 0x08;
+    cfi_config.reloc_type = BFD_RELOC_32;
+  }
+
+  cfi_set_config (&cfi_config);
 }
Index: config/tc-i386.h
===================================================================
RCS file: /cvs/src/src/gas/config/tc-i386.h,v
retrieving revision 1.39
diff -u -p -r1.39 tc-i386.h
--- config/tc-i386.h	23 Jan 2003 12:51:05 -0000	1.39
+++ config/tc-i386.h	15 May 2003 12:18:21 -0000
@@ -545,4 +545,11 @@ void i386_print_statistics PARAMS ((FILE
 extern void sco_id PARAMS ((void));
 #endif
 
+/* We want .cfi_* pseudo-ops for generating unwind info.  */
+#define TARGET_USE_CFIPOP
+#ifdef TARGET_USE_CFIPOP
+#define tc_cfi_init() tc_x86_cfi_init ()
+extern void tc_x86_cfi_init PARAMS ((void));
+#endif
+
 #endif /* TC_I386 */
Index: as.c
===================================================================
RCS file: /cvs/src/src/gas/as.c,v
retrieving revision 1.42
diff -u -p -r1.42 as.c
--- as.c	8 Apr 2003 12:47:07 -0000	1.42
+++ as.c	15 May 2003 12:18:20 -0000
@@ -42,6 +42,7 @@
 #include "sb.h"
 #include "macro.h"
 #include "dwarf2dbg.h"
+#include "dw2gencfi.h"
 
 #ifdef BFD_ASSEMBLER
 #include "bfdver.h"
@@ -98,6 +99,9 @@ int chunksize = 0;
    Then the chunk sizes for gas and bfd will be reduced.  */
 int debug_memory = 0;
 
+/* Enable verbose mode.  */
+int verbose = 0;
+
 /* We build a list of defsyms as we read the options, and then define
    them after we have initialized everything.  */
 
@@ -497,6 +501,7 @@ parse_args (pargc, pargv)
 #endif
 	      case OPTION_VERBOSE:
 		print_version_id ();
+		verbose = 1;
 	      break;
 	    }
 	  /* Fall through.  */
@@ -832,6 +837,10 @@ main (argc, argv)
   bfd_set_error_program_name (myname);
 #endif
 
+#ifdef TARGET_USE_CFIPOP
+  tc_cfi_init ();
+#endif
+
 #ifdef USE_EMULATIONS
   select_emulation_mode (argc, argv);
 #endif
@@ -905,6 +914,10 @@ main (argc, argv)
   /* If we've been collecting dwarf2 .debug_line info, either for
      assembly debugging or on behalf of the compiler, emit it now.  */
   dwarf2_finish ();
+
+#ifdef TARGET_USE_CFIPOP
+  cfi_finish ();
+#endif
 
   if (seen_at_least_1_file ()
       && (flag_always_generate_output || had_errors () == 0))
Index: as.h
===================================================================
RCS file: /cvs/src/src/gas/as.h,v
retrieving revision 1.31
diff -u -p -r1.31 as.h
--- as.h	24 Jan 2003 01:12:30 -0000	1.31
+++ as.h	15 May 2003 12:18:20 -0000
@@ -474,6 +474,9 @@ extern enum debug_info_type debug_type;
 /* Maximum level of macro nesting.  */
 extern int max_macro_nest;
 
+/* Verbosity level.  */
+extern int verbose;
+
 /* Obstack chunk size.  Keep large for efficient space use, make small to
    increase malloc calls for monitoring memory allocation.  */
 extern int chunksize;
Index: read.c
===================================================================
RCS file: /cvs/src/src/gas/read.c,v
retrieving revision 1.61
diff -u -p -r1.61 read.c
--- read.c	13 May 2003 01:51:40 -0000	1.61
+++ read.c	15 May 2003 12:18:20 -0000
@@ -43,6 +43,7 @@ Software Foundation, 59 Temple Place - S
 #include "obstack.h"
 #include "listing.h"
 #include "ecoff.h"
+#include "dw2gencfi.h"
 
 #ifndef TC_START_LABEL
 #define TC_START_LABEL(x,y) (x == ':')
@@ -451,6 +452,10 @@ pop_insert (table)
 #define obj_pop_insert()	pop_insert(obj_pseudo_table)
 #endif
 
+#ifndef cfi_pop_insert
+#define cfi_pop_insert()	pop_insert(cfi_pseudo_table)
+#endif
+
 static void
 pobegin ()
 {
@@ -468,6 +473,12 @@ pobegin ()
   /* Now portable ones.  Skip any that we've seen already.  */
   pop_table_name = "standard";
   pop_insert (potable);
+  
+#ifdef TARGET_USE_CFIPOP
+  pop_table_name = "cfi";
+  pop_override_ok = 1;
+  cfi_pop_insert ();
+#endif
 }
 
 #define HANDLE_CONDITIONAL_ASSEMBLY()					\
Index: symbols.c
===================================================================
RCS file: /cvs/src/src/gas/symbols.c,v
retrieving revision 1.43
diff -u -p -r1.43 symbols.c
--- symbols.c	24 Jan 2003 01:12:30 -0000	1.43
+++ symbols.c	15 May 2003 12:18:20 -0000
@@ -182,8 +182,6 @@ symbol_create (name, segment, valu, frag
 /* Local symbol support.  If we can get away with it, we keep only a
    small amount of information for local symbols.  */
 
-static struct local_symbol *local_symbol_make PARAMS ((const char *, segT,
-						       valueT, fragS *));
 static symbolS *local_symbol_convert PARAMS ((struct local_symbol *));
 
 /* Used for statistics.  */
@@ -205,7 +203,7 @@ static unsigned long local_symbol_conver
 
 /* Create a local symbol and insert it into the local hash table.  */
 
-static struct local_symbol *
+struct local_symbol *
 local_symbol_make (name, section, value, frag)
      const char *name;
      segT section;
Index: symbols.h
===================================================================
RCS file: /cvs/src/src/gas/symbols.h,v
retrieving revision 1.11
diff -u -p -r1.11 symbols.h
--- symbols.h	23 Jan 2003 12:51:04 -0000	1.11
+++ symbols.h	15 May 2003 12:18:20 -0000
@@ -57,6 +57,8 @@ symbolS *symbol_new PARAMS ((const char 
 			     fragS * frag));
 symbolS *symbol_create PARAMS ((const char *name, segT segment, valueT value,
 				fragS * frag));
+struct local_symbol *local_symbol_make PARAMS ((const char *name, segT section, 
+					 valueT value, fragS * frag));
 symbolS *colon PARAMS ((const char *sym_name));
 void local_colon PARAMS ((int n));
 void symbol_begin PARAMS ((void));
Index: Makefile.am
===================================================================
RCS file: /cvs/src/src/gas/Makefile.am,v
retrieving revision 1.74
diff -u -p -r1.74 Makefile.am
--- Makefile.am	24 Apr 2003 12:47:31 -0000	1.74
+++ Makefile.am	15 May 2003 12:18:19 -0000
@@ -176,6 +176,7 @@ GAS_CFILES = \
 	cond.c \
 	depend.c \
 	dwarf2dbg.c \
+	dw2gencfi.c \
 	ecoff.c \
 	ehopt.c \
 	expr.c \
@@ -207,6 +208,7 @@ HFILES = \
 	bit_fix.h \
 	cgen.h \
 	dwarf2dbg.h \
+	dw2gencfi.h \
 	ecoff.h \
 	emul-target.h \
 	emul.h \
@@ -422,6 +424,7 @@ GENERIC_OBJS = \
 	cond.o \
 	depend.o \
 	dwarf2dbg.o \
+	dw2gencfi.o \
 	ehopt.o \
 	expr.o \
 	flonum-konst.o \
Index: Makefile.in
===================================================================
RCS file: /cvs/src/src/gas/Makefile.in,v
retrieving revision 1.84
diff -u -p -r1.84 Makefile.in
--- Makefile.in	24 Apr 2003 12:47:32 -0000	1.84
+++ Makefile.in	15 May 2003 12:18:19 -0000
@@ -291,6 +291,7 @@ GAS_CFILES = \
 	cond.c \
 	depend.c \
 	dwarf2dbg.c \
+	dw2gencfi.c \
 	ecoff.c \
 	ehopt.c \
 	expr.c \
@@ -323,6 +324,7 @@ HFILES = \
 	bit_fix.h \
 	cgen.h \
 	dwarf2dbg.h \
+	dw2gencfi.h \
 	ecoff.h \
 	emul-target.h \
 	emul.h \
@@ -546,6 +548,7 @@ GENERIC_OBJS = \
 	cond.o \
 	depend.o \
 	dwarf2dbg.o \
+	dw2gencfi.o \
 	ehopt.o \
 	expr.o \
 	flonum-konst.o \
@@ -886,19 +889,19 @@ DEPTC_i370_elf = $(INCDIR)/symcat.h $(sr
 DEPTC_i386_aout = $(INCDIR)/symcat.h $(srcdir)/config/obj-aout.h \
   $(srcdir)/config/tc-i386.h $(BFDDIR)/libaout.h $(INCDIR)/bfdlink.h \
   $(INCDIR)/safe-ctype.h subsegs.h $(INCDIR)/obstack.h \
-  dwarf2dbg.h $(INCDIR)/opcode/i386.h
+  dwarf2dbg.h dw2gencfi.h $(INCDIR)/opcode/i386.h
 
 DEPTC_i386_coff = $(INCDIR)/symcat.h $(srcdir)/config/obj-coff.h \
   $(srcdir)/config/tc-i386.h $(INCDIR)/coff/internal.h \
   $(INCDIR)/coff/i386.h $(INCDIR)/coff/external.h $(BFDDIR)/libcoff.h \
   $(INCDIR)/bfdlink.h $(INCDIR)/safe-ctype.h subsegs.h \
-  $(INCDIR)/obstack.h dwarf2dbg.h $(INCDIR)/opcode/i386.h
+  $(INCDIR)/obstack.h dwarf2dbg.h dw2gencfi.h $(INCDIR)/opcode/i386.h
 
 DEPTC_i386_elf = $(INCDIR)/symcat.h $(srcdir)/config/obj-elf.h \
   $(BFDDIR)/elf-bfd.h $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h \
   $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h $(srcdir)/config/tc-i386.h \
   $(INCDIR)/safe-ctype.h subsegs.h $(INCDIR)/obstack.h \
-  dwarf2dbg.h $(INCDIR)/opcode/i386.h
+  dwarf2dbg.h dw2gencfi.h $(INCDIR)/opcode/i386.h
 
 DEPTC_i860_elf = $(INCDIR)/symcat.h $(srcdir)/config/obj-elf.h \
   $(BFDDIR)/elf-bfd.h $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h \
@@ -2468,7 +2471,7 @@ itbl_test_DEPENDENCIES =  itbl-tops.o it
 ../libiberty/libiberty.a
 itbl_test_LDFLAGS = 
 as_new_OBJECTS =  app.$(OBJEXT) as.$(OBJEXT) atof-generic.$(OBJEXT) \
-bignum-copy.$(OBJEXT) cond.$(OBJEXT) depend.$(OBJEXT) \
+bignum-copy.$(OBJEXT) cond.$(OBJEXT) depend.$(OBJEXT) dw2gencfi.$(OBJEXT) \
 dwarf2dbg.$(OBJEXT) ecoff.$(OBJEXT) ehopt.$(OBJEXT) expr.$(OBJEXT) \
 flonum-copy.$(OBJEXT) flonum-konst.$(OBJEXT) flonum-mult.$(OBJEXT) \
 frags.$(OBJEXT) hash.$(OBJEXT) input-file.$(OBJEXT) \
@@ -3315,7 +3318,7 @@ dep-am: DEP
 #MKDEP    DO NOT PUT ANYTHING BETWEEN THIS LINE AND THE MATCHING WARNING BELOW.
 app.o: app.c $(INCDIR)/symcat.h
 as.o: as.c $(INCDIR)/symcat.h subsegs.h $(INCDIR)/obstack.h \
-  output-file.h sb.h macro.h dwarf2dbg.h $(BFDVER_H)
+  output-file.h sb.h macro.h dwarf2dbg.h dw2gencfi.h $(BFDVER_H)
 atof-generic.o: atof-generic.c $(INCDIR)/symcat.h $(INCDIR)/safe-ctype.h
 bignum-copy.o: bignum-copy.c $(INCDIR)/symcat.h
 cond.o: cond.c $(INCDIR)/symcat.h macro.h sb.h $(INCDIR)/obstack.h

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]