This is the mail archive of the binutils@sourceware.org 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] C6X unwinding table generation


The attached patch teaches gas how to generate C6X unwinding tables.

In principle these are very similar to the ARM EABI unwinding tables. However 
instead of inventing a whole new set of directives, we derive the frame layout 
from the existing dwarf frame information/directives.

This requires a couple of additional hooks in common dwarf2 CFI code.  The new 
tables are enabled via the existing .cfi_sections directive.

There is one major limitation to this code:  It assumes that only the function 
prologue is described by the CFI directives, and that the state at the end of 
the function is that which should be unwound.  Currently I believe this is 
true.  If we start encountering compilers that also annotate function epilogue 
then we'll probably want an additional directive to mark which state 
represents the body of the function.

The ABI defined unwinding opcodes can only describe a fairly limited set of 
frame layouts.  Frames that cannot be represented will generate an error.

Ok?

Paul

2011-04-07  Paul Brook  <paul@codesourcery.com>

	bfd/
	* elf32-tic6x.c (is_tic6x_elf_unwind_section_name,
	elf32_tic6x_fake_sections): New functions.
	(elf_backend_fake_sections): Define.

	gas/
	* config/tc-tic6x.c (streq): Define.
	(unwind): New variable.
	(s_tic6x_cantunwind, s_tic6x_handlerdata, s_tic6x_endp,
	s_tic6x_personalityindex, s_tic6x_personality): New functions.
	(md_pseudo_table): Add "endp", "handlerdata", "personalityindex",
	"personality" and "cantunwind".
	(tic6x_regname_to_dw2regnum, tic6x_frame_initial_instructions,
	start_unwind_section, unwind_frame_regs, pop_rts_offset_little,
	pop_rts_offset_big, unwind_reg_from_dwarf, flush_unwind_word,
	unwind_byte, unwind_2byte, unwind_uleb, tic6x_cfi_startproc,
	output_exidx_entry, tic6x_output_unwinding, tic6x_cfi_endproc,
	* config/tc-tic6x.h (tic6x_segment_info_type): Add
	marked_pr_dependency.
	(TARGET_USE_CFIPOP, tc_regname_to_dw2regnum,
	tc_cfi_frame_initial_instructions, DWARF2_DEFAULT_RETURN_COLUMN,
	DWARF2_CIE_DATA_ALIGNMENT, tc_cfi_startproc, tc_cfi_endproc,
	tc_cfi_section_name): Define.
	* doc/c-tic6x.texi: Document new unwinding directives.
	* dw2gencfi.c (tc_cfi_startproc, tc_cfi_endproc): Add default
	definitions.
	(cfi_insn_data, fde_entry, CFI_adjust_cfa_offset, CFI_return_column,
	CFI_rel_offset, CFI_escape, CFI_signal_frame, CFI_val_encoded_addr):
	Move to dw2gencfi.h.
	(CFI_EMIT_target): Define.
	(dot_cfi_sections): Check tc_cfi_section_name.
	(dot_cfi_startproc): Use tc_cfi_startproc.
	(dot_cfi_endproc): Use tc_cfi_endproc.
	* dw2gencfi.h (cfi_insn_data, fde_entry, CFI_adjust_cfa_offset,
	CFI_return_column, CFI_rel_offset, CFI_escape, CFI_signal_frame,
	CFI_val_encoded_addr):  Move to here from dw2gencfi.c.

	gas/testsuite:
	* gas/tic6x/unwind-1.d: New test.
	* gas/tic6x/unwind-1.s: New test.
	* gas/tic6x/unwind-2.d: New test.
	* gas/tic6x/unwind-2.s: New test.

	include/
	* elf/tic6x.h (ELF_STRING_C6000_unwind,
	ELF_STRING_C6000_unwind_info, ELF_STRING_C6000_unwind_once,
	ELF_STRING_C6000_unwind_info_once): Define.
diff --git a/bfd/elf32-tic6x.c b/bfd/elf32-tic6x.c
index 8160a00..52a3b79 100644
--- a/bfd/elf32-tic6x.c
+++ b/bfd/elf32-tic6x.c
@@ -1863,6 +1863,36 @@ elf32_tic6x_gc_mark_extra_sections (struct bfd_link_info *info,
   return TRUE;
 }
 
+/* Return TRUE if this is an unwinding table index.  */
+
+static bfd_boolean
+is_tic6x_elf_unwind_section_name (const char *name)
+{
+  return (CONST_STRNEQ (name, ELF_STRING_C6000_unwind)
+	  || CONST_STRNEQ (name, ELF_STRING_C6000_unwind_once));
+}
+
+
+/* Set the type and flags for an unwinding index table.  We do this by
+   the section name, which is a hack, but ought to work.  */
+
+static bfd_boolean
+elf32_tic6x_fake_sections (bfd *abfd ATTRIBUTE_UNUSED,
+			   Elf_Internal_Shdr *hdr, asection *sec)
+{
+  const char * name;
+
+  name = bfd_get_section_name (abfd, sec);
+
+  if (is_tic6x_elf_unwind_section_name (name))
+    {
+      hdr->sh_type = SHT_C6000_UNWIND;
+      hdr->sh_flags |= SHF_LINK_ORDER;
+    }
+
+  return TRUE;
+}
+
 /* Update the got entry reference counts for the section being removed.  */
 
 static bfd_boolean
@@ -3969,6 +3999,7 @@ elf32_tic6x_copy_private_data (bfd * ibfd, bfd * obfd)
 #define elf_backend_plt_readonly	1
 #define elf_backend_rela_normal		1
 #define elf_backend_got_header_size     8
+#define elf_backend_fake_sections       elf32_tic6x_fake_sections
 #define elf_backend_gc_sweep_hook	elf32_tic6x_gc_sweep_hook
 #define elf_backend_gc_mark_extra_sections elf32_tic6x_gc_mark_extra_sections
 #define elf_backend_modify_program_headers \
diff --git a/gas/config/tc-tic6x.c b/gas/config/tc-tic6x.c
index cd24dff..a5b4f7c 100644
--- a/gas/config/tc-tic6x.c
+++ b/gas/config/tc-tic6x.c
@@ -23,6 +23,7 @@
 
 #include "as.h"
 #include "dwarf2dbg.h"
+#include "dw2gencfi.h"
 #include "safe-ctype.h"
 #include "subsegs.h"
 #include "opcode/tic6x.h"
@@ -34,6 +35,8 @@
 #define TRUNC(X)	((valueT) (X) & 0xffffffffU)
 #define SEXT(X)		((TRUNC (X) ^ 0x80000000U) - 0x80000000U)
 
+#define streq(a, b)           (strcmp (a, b) == 0)
+
 /* Stuff for .scomm symbols.  */
 static segT sbss_section;
 static asection scom_section;
@@ -162,6 +165,57 @@ static const tic6x_arch_table tic6x_arches[] =
 				      | TIC6X_INSN_C674X) }
   };
 
+/* Caller saved register encodings.  The standard frame layout uses this
+   order, starting from the highest address.  */
+enum
+{
+  UNWIND_A15,
+  UNWIND_B15,
+  UNWIND_B14,
+  UNWIND_B13,
+  UNWIND_B12,
+  UNWIND_B11,
+  UNWIND_B10,
+  UNWIND_B3,
+  UNWIND_A14,
+  UNWIND_A13,
+  UNWIND_A12,
+  UNWIND_A11,
+  UNWIND_A10,
+  NUM_UNWIND_REGS
+};
+
+/* Unwinding information state.  */
+static struct c6x_unwind {
+    int personality_index;
+    symbolS *personality_routine;
+    symbolS *function_start;
+    segT saved_seg;
+    subsegT saved_subseg;
+    /* NULL if table entry is inline.  */
+    symbolS *table_entry;
+    char *frag_start;
+    valueT data;
+    /* 0 before .cfi_startproc
+      -1 between .cfi_startproc and .handlerdata
+      >0 between .handlerdata and .endp */
+    int data_bytes;
+
+    offsetT reg_offset[NUM_UNWIND_REGS];
+    bfd_boolean reg_saved[NUM_UNWIND_REGS];
+    int cfa_reg;
+    int return_reg;
+    unsigned safe_mask;
+    unsigned compact_mask;
+    unsigned reg_saved_mask;
+    offsetT cfa_offset;
+    bfd_boolean pop_rts;
+    /* Only valid for UNWIND_OP_POP_REG */
+    int saved_reg_count;
+} unwind;
+
+static void tic6x_output_unwinding (bfd_boolean need_extab);
+
 /* Update the selected architecture based on ARCH, giving an error if
    ARCH is an invalid value.  Does not call tic6x_update_features; the
    caller must do that if necessary.  */
@@ -325,9 +379,112 @@ tic6x_after_parse_args (void)
   tic6x_update_features ();
 }
 
-/* Parse a .arch directive.  */
+/* Parse a .cantunwind directive.  */
+static void
+s_tic6x_cantunwind (int ignored ATTRIBUTE_UNUSED)
+{
+  /* GCC sometimes spits out superfluous .cantunwind directives, so ignore
+     them.  */
+  if (unwind.data_bytes == 0)
+    return;
+
+  if (unwind.data_bytes != -1)
+    as_bad (_("unexpected .cantunwind directive"));
+
+  demand_empty_rest_of_line ();
+
+  if (unwind.personality_routine || unwind.personality_index != -1)
+    as_bad (_("personality routine specified for cantunwind frame"));
+
+  unwind.personality_index = -2;
+}
+
+/* Parse a .handlerdata directive.  */
+static void
+s_tic6x_handlerdata (int ignored ATTRIBUTE_UNUSED)
+{
+  if (!unwind.saved_seg)
+    {
+      as_bad (_("unexpected .handlerdata directive"));
+      return;
+    }
+
+  if (unwind.table_entry || unwind.personality_index == -2)
+    {
+      as_bad (_("duplicate .handlerdata directive"));
+      return;
+    }
+
+  if (unwind.personality_index == -1 && unwind.personality_routine == NULL)
+    {
+      as_bad (_("personality routine required before .handlerdata directive"));
+      return;
+    }
+
+  tic6x_output_unwinding (TRUE);
+}
+
+/* Parse a .endp directive.  */
+static void
+s_tic6x_endp (int ignored ATTRIBUTE_UNUSED)
+{
+  if (unwind.data_bytes != 0)
+    {
+      /* Output a .exidx entry if we have not already done so.
+	 Then switch back to the text section.  */
+      if (!unwind.table_entry)
+	tic6x_output_unwinding (FALSE);
+
+      subseg_set (unwind.saved_seg, unwind.saved_subseg);
+    }
+
+  unwind.saved_seg = NULL;
+  unwind.table_entry = NULL;
+  unwind.data_bytes = 0;
+}
+
+/* Parse a .personalityindex directive.  */
+static void
+s_tic6x_personalityindex (int ignored ATTRIBUTE_UNUSED)
+{
+  expressionS exp;
+
+  if (unwind.personality_routine || unwind.personality_index != -1)
+    as_bad (_("duplicate .personalityindex directive"));
+
+  expression (&exp);
+
+  if (exp.X_op != O_constant
+      || exp.X_add_number < 0 || exp.X_add_number > 15)
+    {
+      as_bad (_("bad personality routine number"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  unwind.personality_index = exp.X_add_number;
+
+  demand_empty_rest_of_line ();
+}
 
 static void
+s_tic6x_personality (int ignored ATTRIBUTE_UNUSED)
+{
+  char *name, *p, c;
+
+  if (unwind.personality_routine || unwind.personality_index != -1)
+    as_bad (_("duplicate .personality directive"));
+
+  name = input_line_pointer;
+  c = get_symbol_end ();
+  p = input_line_pointer;
+  unwind.personality_routine = symbol_find_or_make (name);
+  *p = c;
+  demand_empty_rest_of_line ();
+}
+
+/* Parse a .arch directive.  */
+static void
 s_tic6x_arch (int ignored ATTRIBUTE_UNUSED)
 {
   char c;
@@ -574,6 +731,11 @@ const pseudo_typeS md_pseudo_table[] =
     { "scomm",	s_tic6x_scomm, 0 },
     { "word", cons, 4 },
     { "ehtype", s_ehtype, 0 },
+    { "endp", s_tic6x_endp, 0 },
+    { "handlerdata", s_tic6x_handlerdata, 0 },
+    { "personalityindex", s_tic6x_personalityindex, 0 },
+    { "personality", s_tic6x_personality, 0 },
+    { "cantunwind", s_tic6x_cantunwind, 0 },
     { 0, 0, 0 }
   };
 
@@ -4267,3 +4437,802 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
 
   return reloc;
 }
+
+/* Convert REGNAME to a DWARF-2 register number.  */
+
+int
+tic6x_regname_to_dw2regnum (char *regname)
+{
+  bfd_boolean reg_ok;
+  tic6x_register reg;
+  char *rq = regname;
+
+  reg_ok = tic6x_parse_register (&rq, &reg);
+
+  if (!reg_ok)
+    return -1;
+
+  switch (reg.side)
+    {
+    case 1: /* A0-A15.  */
+      return reg.num;
+
+    case 2: /* B0-B15.  */
+      return reg.num + 16;
+
+    default:
+      return -1;
+    }
+}
+
+/* Initialize the DWARF-2 unwind information for this procedure.  */
+
+void
+tic6x_frame_initial_instructions (void)
+{
+  /* CFA is initial stack pointer (B15).  */
+  cfi_add_CFA_def_cfa (31, 0);
+}
+
+/* Start an exception table entry.  If idx is nonzero this is an index table
+   entry.  */
+
+static void
+start_unwind_section (const segT text_seg, int idx)
+{
+  const char * text_name;
+  const char * prefix;
+  const char * prefix_once;
+  const char * group_name;
+  size_t prefix_len;
+  size_t text_len;
+  char * sec_name;
+  size_t sec_name_len;
+  int type;
+  int flags;
+  int linkonce;
+
+  if (idx)
+    {
+      prefix = ELF_STRING_C6000_unwind;
+      prefix_once = ELF_STRING_C6000_unwind_once;
+      type = SHT_C6000_UNWIND;
+    }
+  else
+    {
+      prefix = ELF_STRING_C6000_unwind_info;
+      prefix_once = ELF_STRING_C6000_unwind_info_once;
+      type = SHT_PROGBITS;
+    }
+
+  text_name = segment_name (text_seg);
+  if (streq (text_name, ".text"))
+    text_name = "";
+
+  if (strncmp (text_name, ".gnu.linkonce.t.",
+	       strlen (".gnu.linkonce.t.")) == 0)
+    {
+      prefix = prefix_once;
+      text_name += strlen (".gnu.linkonce.t.");
+    }
+
+  prefix_len = strlen (prefix);
+  text_len = strlen (text_name);
+  sec_name_len = prefix_len + text_len;
+  sec_name = (char *) xmalloc (sec_name_len + 1);
+  memcpy (sec_name, prefix, prefix_len);
+  memcpy (sec_name + prefix_len, text_name, text_len);
+  sec_name[prefix_len + text_len] = '\0';
+
+  flags = SHF_ALLOC;
+  linkonce = 0;
+  group_name = 0;
+
+  /* Handle COMDAT group.  */
+  if (prefix != prefix_once && (text_seg->flags & SEC_LINK_ONCE) != 0)
+    {
+      group_name = elf_group_name (text_seg);
+      if (group_name == NULL)
+	{
+	  as_bad (_("Group section `%s' has no group signature"),
+		  segment_name (text_seg));
+	  ignore_rest_of_line ();
+	  return;
+	}
+      flags |= SHF_GROUP;
+      linkonce = 1;
+    }
+
+  obj_elf_change_section (sec_name, type, flags, 0, group_name, linkonce, 0);
+
+  /* Set the section link for index tables.  */
+  if (idx)
+    elf_linked_to_section (now_seg) = text_seg;
+}
+
+
+static const int
+unwind_frame_regs[NUM_UNWIND_REGS] = 
+/* A15 B15 B14 B13 B12 B11 B10  B3 A14 A13 A12 A11 A10.  */
+  { 15, 31, 30, 29, 28, 27, 26, 19, 14, 13, 12, 11, 10 };
+
+/* Register save offsets for __c6xabi_push_rts.  */
+static const int
+pop_rts_offset_little[NUM_UNWIND_REGS] = 
+/* A15 B15 B14 B13 B12 B11 B10  B3 A14 A13 A12 A11 A10.  */
+  { -1,  1,  0, -3, -4, -7, -8,-11, -2, -5, -6, -9,-10};
+
+static const int
+pop_rts_offset_big[NUM_UNWIND_REGS] = 
+/* A15 B15 B14 B13 B12 B11 B10  B3 A14 A13 A12 A11 A10.  */
+  { -2,  1,  0, -4, -3, -8, -7,-12, -1, -6, -5,-10, -9};
+
+/* Map from dwarf register number to unwind frame register number.  */
+static int
+unwind_reg_from_dwarf (int dwarf)
+{
+  int reg;
+
+  for (reg = 0; reg < NUM_UNWIND_REGS; reg++)
+    {
+      if (unwind_frame_regs[reg] == dwarf)
+	return reg;
+    }
+
+  return -1;
+}
+
+/* Unwinding bytecode definitions.  */
+#define UNWIND_OP_ADD_SP  0x00
+#define UNWIND_OP_ADD_SP2 0xd2
+#define UNWIND_OP2_CANTUNWIND 0x8000
+#define UNWIND_OP2_POP 0x8000
+#define UNWIND_OP2_POP_COMPACT 0xa000
+#define UNWIND_OP_POP_REG 0xc0
+#define UNWIND_OP_MV_FP 0xd0
+#define UNWIND_OP_POP_RTS 0xd1
+#define UNWIND_OP_RET 0xe0
+
+/* Maximum stack adjustment for __c6xabi_unwind_cpp_pr3/4 */
+#define MAX_COMPACT_SP_OFFSET (0x7f << 3)
+
+static void
+flush_unwind_word (valueT data)
+{
+  char *ptr;
+
+  /* Create EXTAB entry if it does not exist.  */
+  if (unwind.table_entry == NULL)
+    {
+      start_unwind_section (unwind.saved_seg, 0);
+      frag_align (2, 0, 0);
+      record_alignment (now_seg, 2);
+      unwind.table_entry = expr_build_dot ();
+      ptr = frag_more (4);
+      unwind.frag_start = ptr;
+    }
+  else
+    {
+      /* Append additional word of data.  */
+      ptr = frag_more (4);
+    }
+
+  md_number_to_chars (ptr, data, 4);
+}
+
+/* Add a single byte of unwinding data.  */
+
+static void
+unwind_byte (int byte)
+{
+  unwind.data_bytes++;
+  /* Only flush the first word after we know multiple words are required.  */
+  if (unwind.data_bytes == 5)
+    {
+      if (unwind.personality_index == -1)
+	{
+	  /* At this point we know we are too big for pr0.  */
+	  unwind.personality_index = 1;
+	  flush_unwind_word (0x81000000 | ((unwind.data >> 8) & 0xffff));
+	  unwind.data = ((unwind.data & 0xff) << 8) | byte;
+	  unwind.data_bytes++;
+	}
+      else
+	{
+	  flush_unwind_word (unwind.data);
+	  unwind.data = byte;
+	}
+    }
+  else
+    {
+      unwind.data = (unwind.data << 8) | byte;
+      if ((unwind.data_bytes & 3) == 0 && unwind.data_bytes > 4)
+	{
+	  flush_unwind_word (unwind.data);
+	  unwind.data = 0;
+	}
+    }
+}
+
+/* Add a two-byte unwinding opcode.  */
+static void
+unwind_2byte (int bytes)
+{
+  unwind_byte (bytes >> 8);
+  unwind_byte (bytes & 0xff);
+}
+
+static void
+unwind_uleb (offsetT offset)
+{
+  while (offset > 0x7f)
+    {
+      unwind_byte ((offset & 0x7f) | 0x80);
+      offset >>= 7;
+    }
+  unwind_byte (offset);
+}
+
+void
+tic6x_cfi_startproc (void)
+{
+  unwind.personality_index = -1;
+  unwind.personality_routine = NULL;
+  if (unwind.table_entry)
+    as_bad (_("Missing .handlerdata before .cfi_startproc"));
+
+  unwind.table_entry = NULL;
+  unwind.data_bytes = -1;
+}
+
+static void
+output_exidx_entry (void)
+{
+  char *ptr;
+  long where;
+  unsigned int marked_pr_dependency;
+  segT old_seg;
+  subsegT old_subseg;
+
+  old_seg = now_seg;
+  old_subseg = now_subseg;
+
+  /* Add index table entry.  This is two words.	 */
+  start_unwind_section (unwind.saved_seg, 1);
+  frag_align (2, 0, 0);
+  record_alignment (now_seg, 2);
+
+  ptr = frag_more (8);
+  where = frag_now_fix () - 8;
+
+  /* Self relative offset of the function start.  */
+  fix_new (frag_now, where, 4, unwind.function_start, 0, 1,
+	   BFD_RELOC_C6000_PREL31);
+
+  /* Indicate dependency on ABI-defined personality routines to the
+     linker, if it hasn't been done already.  */
+  marked_pr_dependency
+    = seg_info (now_seg)->tc_segment_info_data.marked_pr_dependency;
+  if (unwind.personality_index >= 0 && unwind.personality_index < 5
+      && !(marked_pr_dependency & (1 << unwind.personality_index)))
+    {
+      static const char *const name[] =
+	{
+	  "__c6xabi_unwind_cpp_pr0",
+	  "__c6xabi_unwind_cpp_pr1",
+	  "__c6xabi_unwind_cpp_pr2",
+	  "__c6xabi_unwind_cpp_pr3",
+	  "__c6xabi_unwind_cpp_pr4"
+	};
+      symbolS *pr = symbol_find_or_make (name[unwind.personality_index]);
+      fix_new (frag_now, where, 0, pr, 0, 1, BFD_RELOC_NONE);
+      seg_info (now_seg)->tc_segment_info_data.marked_pr_dependency
+	|= 1 << unwind.personality_index;
+    }
+
+  if (unwind.table_entry)
+    {
+      /* Self relative offset of the table entry.	 */
+      fix_new (frag_now, where + 4, 4, unwind.table_entry, 0, 1,
+	       BFD_RELOC_C6000_PREL31);
+    }
+  else
+    {
+      /* Inline exception table entry.  */
+      md_number_to_chars (ptr + 4, unwind.data, 4);
+    }
+
+  /* Restore the original section.  */
+  subseg_set (old_seg, old_subseg);
+}
+
+static void
+tic6x_output_unwinding (bfd_boolean need_extab)
+{
+  unsigned safe_mask = unwind.safe_mask;
+  unsigned compact_mask = unwind.compact_mask;
+  unsigned reg_saved_mask = unwind.reg_saved_mask;
+  offsetT cfa_offset = unwind.cfa_offset;
+  long where;
+  int reg;
+
+  if (unwind.personality_index == -2)
+    {
+      /* Function can not be unwound.  */
+      unwind.data = 1u << 31;
+      output_exidx_entry ();
+      return;
+    }
+
+  if (unwind.personality_index == -1 && unwind.personality_routine == NULL)
+    {
+      /* Auto-select a personality routine if none specified.  */
+      if (reg_saved_mask || cfa_offset >= MAX_COMPACT_SP_OFFSET)
+	unwind.personality_index = -1;
+      else if (safe_mask)
+	unwind.personality_index = 3;
+      else
+	unwind.personality_index = 4;
+    }
+
+  /* Calculate unwinding opcodes, and emit to EXTAB if necessary.  */
+  unwind.table_entry = NULL;
+  if (unwind.personality_index == 3 || unwind.personality_index == 4)
+    {
+      if (cfa_offset >= MAX_COMPACT_SP_OFFSET)
+	{
+	  as_bad (_("Stack pointer offset too large for personality routine"));
+	  return;
+	}
+      if (reg_saved_mask
+	  || (unwind.personality_index == 3 && compact_mask != 0)
+	  || (unwind.personality_index == 4 && safe_mask != 0))
+	{
+	  as_bad (_("Stack frame layout does not match personality routine"));
+	  return;
+	}
+
+      unwind.data = (1u << 31) | (unwind.personality_index << 24);
+      if (unwind.cfa_reg == 15)
+	unwind.data |= 0x7f << 17;
+      else
+	unwind.data |= cfa_offset << (17 - 3);
+
+      if (unwind.personality_index == 3)
+	unwind.data |= safe_mask << 4;
+      else
+	unwind.data |= compact_mask << 4;
+      unwind.data |= unwind.return_reg;
+      unwind.data_bytes = 4;
+    }
+  else
+    {
+      if (unwind.personality_routine)
+	{
+	  unwind.data = 0;
+	  unwind.data_bytes = 5;
+	  flush_unwind_word (0);
+	  /* First word is personality routine.  */
+	  where = frag_now_fix () - 4;
+	  fix_new (frag_now, where, 4, unwind.personality_routine, 0, 1,
+		   BFD_RELOC_C6000_PREL31);
+	}
+      else if (unwind.personality_index > 0)
+	{
+	  unwind.data = 0x8000 | (unwind.personality_index << 8);
+	  unwind.data_bytes = 2;
+	}
+      else /* pr0 or undecided */
+	{
+	  unwind.data = 0x80;
+	  unwind.data_bytes = 1;
+	}
+
+      if (unwind.return_reg != UNWIND_B3)
+	{
+	  unwind_byte (UNWIND_OP_RET | unwind.return_reg);
+	}
+
+      if (unwind.cfa_reg == 15)
+	{
+	  unwind_byte (UNWIND_OP_MV_FP);
+	}
+      else if (cfa_offset != 0)
+	{
+	  cfa_offset >>= 3;
+	  if (cfa_offset > 0x80)
+	    {
+	      unwind_byte (UNWIND_OP_ADD_SP2);
+	      unwind_uleb (cfa_offset - 0x81);
+	    }
+	  else if (cfa_offset > 0x40)
+	    {
+	      unwind_byte (UNWIND_OP_ADD_SP | 0x3f);
+	      unwind_byte (UNWIND_OP_ADD_SP | (cfa_offset - 0x40));
+	    }
+	  else
+	    {
+	      unwind_byte (UNWIND_OP_ADD_SP | (cfa_offset - 1));
+	    }
+	}
+
+      if (safe_mask)
+	unwind_2byte (UNWIND_OP2_POP | unwind.safe_mask);
+      else if (unwind.pop_rts)
+	unwind_byte (UNWIND_OP_POP_RTS);
+      else if (compact_mask)
+	unwind_2byte (UNWIND_OP2_POP_COMPACT | unwind.compact_mask);
+      else if (reg_saved_mask)
+	{
+	  offsetT cur_offset;
+	  int val;
+	  int last_val;
+
+	  unwind_byte (UNWIND_OP_POP_REG | unwind.saved_reg_count);
+	  last_val = 0;
+	  for (cur_offset = 0; unwind.saved_reg_count > 0; cur_offset -= 4)
+	    {
+	      val = 0xf;
+	      for (reg = 0; reg < NUM_UNWIND_REGS; reg++)
+		{
+		  if (!unwind.reg_saved[reg])
+		    continue;
+
+		  if (unwind.reg_offset[reg] == cur_offset)
+		    {
+		      unwind.saved_reg_count--;
+		      val = reg;
+		      break;
+		    }
+		}
+	      if ((cur_offset & 4) == 4)
+		unwind_byte ((last_val << 4) | val);
+	      else
+		last_val = val;
+	    }
+	  if ((cur_offset & 4) == 4)
+	    unwind_byte ((last_val << 4) | 0xf);
+	}
+
+      /* Pad with RETURN opcodes.  */
+      while ((unwind.data_bytes & 3) != 0)
+	unwind_byte (UNWIND_OP_RET | UNWIND_B3);
+
+      if (unwind.personality_index == -1 && unwind.personality_routine == NULL)
+	unwind.personality_index = 0;
+    }
+
+  /* Force creation of an EXTAB entry if an LSDA is required.  */
+  if (need_extab && !unwind.table_entry)
+    {
+      if (unwind.data_bytes != 4)
+	abort ();
+
+      flush_unwind_word (unwind.data);
+    }
+  else if (unwind.table_entry && !need_extab)
+    {
+      /* Add an empty descriptor if there is no user-specified data.   */
+      char *ptr = frag_more (4);
+      md_number_to_chars (ptr, 0, 4);
+    }
+
+  /* Fill in length of unwinding bytecode.  */
+  if (unwind.table_entry)
+    {
+      valueT tmp;
+      if (unwind.data_bytes > 0x400)
+	as_bad (_("too many unwinding instructions"));
+
+      if (unwind.personality_index == -1)
+	{
+	  tmp = md_chars_to_number (unwind.frag_start + 4, 4);
+	  tmp |= ((unwind.data_bytes - 8) >> 2) << 24;
+	  md_number_to_chars (unwind.frag_start + 4, tmp, 4);
+	}
+      else if (unwind.personality_index == 1 || unwind.personality_index == 2)
+	{
+	  tmp = md_chars_to_number (unwind.frag_start, 4);
+	  tmp |= ((unwind.data_bytes - 4) >> 2) << 16;
+	  md_number_to_chars (unwind.frag_start, tmp, 4);
+	}
+    }
+  output_exidx_entry ();
+}
+
+/* FIXME: This will get horribly confused if cfi directives are emitted for
+   function epilogue.  */
+void
+tic6x_cfi_endproc (struct fde_entry *fde)
+{
+  struct cfi_insn_data *insn;
+  int reg;
+  unsigned safe_mask = 0;
+  unsigned compact_mask = 0;
+  unsigned reg_saved_mask = 0;
+  offsetT cfa_offset = 0;
+  offsetT save_offset = 0;
+
+  unwind.cfa_reg = 31;
+  unwind.return_reg = UNWIND_B3;
+  unwind.saved_reg_count = 0;
+  unwind.pop_rts = FALSE;
+
+  unwind.saved_seg = now_seg;
+  unwind.saved_subseg = now_subseg;
+
+  for (reg = 0; reg < NUM_UNWIND_REGS; reg++)
+    unwind.reg_saved[reg] = FALSE;
+
+  /* Scan FDE instructions to build up stack frame layout.  */
+  for (insn = fde->data; insn; insn = insn->next)
+    {
+      switch (insn->insn)
+	{
+	case DW_CFA_advance_loc:
+	  break;
+
+	case DW_CFA_def_cfa:
+	  unwind.cfa_reg = insn->u.ri.reg;
+	  cfa_offset = insn->u.ri.offset;
+	  break;
+
+	case DW_CFA_def_cfa_register:
+	  unwind.cfa_reg = insn->u.r;
+	  break;
+
+	case DW_CFA_def_cfa_offset:
+	  cfa_offset = insn->u.i;
+	  break;
+
+	case DW_CFA_undefined:
+	case DW_CFA_same_value:
+	  reg = unwind_reg_from_dwarf (insn->u.r);
+	  if (reg >= 0)
+	    unwind.reg_saved[reg] = FALSE;
+	  break;
+
+	case DW_CFA_offset:
+	  reg = unwind_reg_from_dwarf (insn->u.ri.reg);
+	  if (reg < 0)
+	    {
+	      as_bad (_("Unable to generate unwinding opcode for reg %d"),
+		      insn->u.ri.reg);
+	      return;
+	    }
+	  unwind.reg_saved[reg] = TRUE;
+	  unwind.reg_offset[reg] = insn->u.ri.offset;
+	  if (insn->u.ri.reg == UNWIND_B3)
+	    unwind.return_reg = UNWIND_B3;
+	  break;
+
+	case DW_CFA_register:
+	  if (insn->u.rr.reg1 != 19)
+	    {
+	      as_bad (_("Unable to generate unwinding opcode for reg %d"),
+		      insn->u.rr.reg1);
+	      return;
+	    }
+
+	  reg = unwind_reg_from_dwarf (insn->u.rr.reg2);
+	  if (reg < 0)
+	    {
+	      as_bad (_("Unable to generate unwinding opcode for reg %d"),
+		      insn->u.rr.reg2);
+	      return;
+	    }
+
+	  unwind.return_reg = reg;
+	  unwind.reg_saved[UNWIND_B3] = FALSE;
+	  if (unwind.reg_saved[reg])
+	    {
+	      as_bad (_("Unable to restore return address from "
+			"previously restored reg"));
+	      return;
+	    }
+	  break;
+
+	case DW_CFA_restore:
+	case DW_CFA_remember_state:
+	case DW_CFA_restore_state:
+	case DW_CFA_GNU_window_save:
+	case CFI_escape:
+	case CFI_val_encoded_addr:
+	  as_bad (_("Unhandled CFA insn for unwinding (%d)"), insn->insn);
+	  break;
+
+	default:
+	  abort ();
+	}
+    }
+
+  if (unwind.cfa_reg != 15 && unwind.cfa_reg != 31)
+    {
+      as_bad (_("Unable to generate unwinding opcode for frame pointer reg %d"),
+	      insn->u.ri.reg);
+      return;
+    }
+
+  if (unwind.cfa_reg == 15)
+    {
+      if (cfa_offset != 0)
+	{
+	  as_bad (_("Unable to generate unwinding opcode for "
+		    "frame pointer offset"));
+	  return;
+	}
+    }
+  else
+    {
+      if ((cfa_offset & 7) != 0)
+	{
+	  as_bad (_("Unwound stack pointer not doubleword aligned"));
+	  return;
+	}
+    }
+
+  for (reg = 0; reg < NUM_UNWIND_REGS; reg++)
+    {
+      if (unwind.reg_saved[reg])
+	reg_saved_mask |= 1 << (NUM_UNWIND_REGS - (reg + 1));
+    }
+
+  /* Check for standard "safe debug" frame layout */
+  if (reg_saved_mask)
+    {
+      save_offset = 0;
+      for (reg = 0; reg < NUM_UNWIND_REGS; reg++)
+	{
+	  if (!unwind.reg_saved[reg])
+	    continue;
+
+	  if (target_big_endian
+	      && reg < NUM_UNWIND_REGS - 1
+	      && unwind.reg_saved[reg + 1]
+	      && unwind_frame_regs[reg] == unwind_frame_regs[reg + 1] + 1
+	      && (unwind_frame_regs[reg] & 1) == 1
+	      && (save_offset & 4) == 4)
+	    {
+	      /* Swapped pair */
+	      if (save_offset != unwind.reg_offset[reg + 1]
+		  || save_offset - 4 != unwind.reg_offset[reg])
+		break;
+	      save_offset -= 8;
+	      reg++;
+	    }
+	  else
+	    {
+	      if (save_offset != unwind.reg_offset[reg])
+		break;
+	      save_offset -= 4;
+	    }
+	}
+      if (reg == NUM_UNWIND_REGS)
+	{
+	  safe_mask = reg_saved_mask;
+	  reg_saved_mask = 0;
+	}
+    }
+
+  /* Check for compact frame layout.  */
+  if (reg_saved_mask)
+    {
+      save_offset = 0;
+      for (reg = 0; reg < NUM_UNWIND_REGS; reg++)
+	{
+	  int reg2;
+
+	  if (!unwind.reg_saved[reg])
+	    continue;
+
+	  if (reg < NUM_UNWIND_REGS - 1)
+	    {
+	      reg2 = reg + 1;
+
+	      if (!unwind.reg_saved[reg2]
+		  || unwind_frame_regs[reg] != unwind_frame_regs[reg2] + 1
+		  || (unwind_frame_regs[reg2] & 1) != 0
+		  || save_offset == 0)
+		reg2 = -1;
+	    }
+	  else
+	    reg2 = -1;
+
+	  if (reg2 >= 0)
+	    {
+	      int high_offset;
+	      if (target_big_endian)
+		high_offset = 4; /* lower address = positive stack offset.  */
+	      else
+		high_offset = 0;
+
+	      if (save_offset + 4 - high_offset != unwind.reg_offset[reg]
+		  || save_offset + high_offset != unwind.reg_offset[reg2])
+		{
+		  break;
+		}
+	      reg++;
+	    }
+	  else
+	    {
+	      if (save_offset != unwind.reg_offset[reg])
+		break;
+	    }
+	  save_offset -= 8;
+	}
+
+      if (reg == NUM_UNWIND_REGS)
+	{
+	  compact_mask = reg_saved_mask;
+	  reg_saved_mask = 0;
+	}
+    }
+
+  /* Check for __c6xabi_pop_rts format */
+  if (reg_saved_mask == 0x17ff)
+    {
+      const int *pop_rts_offset = target_big_endian
+				? pop_rts_offset_big : pop_rts_offset_little;
+
+      save_offset = 0;
+      for (reg = 0; reg < NUM_UNWIND_REGS; reg++)
+	{
+	  if (reg == UNWIND_B15)
+	    continue;
+
+	  if (unwind.reg_offset[reg] != pop_rts_offset[reg] * 4)
+	    break;
+	}
+
+      if (reg == NUM_UNWIND_REGS)
+	{
+	  unwind.pop_rts = TRUE;
+	  reg_saved_mask = 0;
+	}
+    }
+  /* If all else fails then describe the frame manually.  */
+  if (reg_saved_mask)
+    {
+      save_offset = 0;
+
+      for (reg = 0; reg < NUM_UNWIND_REGS; reg++)
+	{
+	  if (!unwind.reg_saved[reg])
+	    continue;
+
+	  unwind.saved_reg_count++;
+	  /* Encoding uses 4 bits per word, so size of unwinding opcode data 
+	     limits the save area size.  The exact cap will be figured out
+	     later due to overflow, the 0x800 here is just a quick sanity
+	     check to weed out obviously excessive offsets.  */
+	  if (unwind.reg_offset[reg] > 0 || unwind.reg_offset[reg] < -0x800
+	      || (unwind.reg_offset[reg] & 3) != 0)
+	    {
+	      as_bad (_("stack frame layout too complex for unwinder"));
+	      return;
+	    }
+
+	  if (unwind.reg_offset[reg] < save_offset)
+	    save_offset = unwind.reg_offset[reg] - 4;
+	}
+    }
+
+  /* Align to 8-byte boundary (stack grows towards negative offsets).  */
+  save_offset &= ~7;
+
+  if (unwind.cfa_reg == 31 && !reg_saved_mask)
+    {
+      cfa_offset += save_offset;
+      if (cfa_offset < 0)
+	{
+	  as_bad (_("Unwound frame has negative size"));
+	  return;
+	}
+    }
+
+  unwind.safe_mask = safe_mask;
+  unwind.compact_mask = compact_mask;
+  unwind.reg_saved_mask = reg_saved_mask;
+  unwind.cfa_offset = cfa_offset;
+  unwind.function_start = fde->start_address;
+}
diff --git a/gas/config/tc-tic6x.h b/gas/config/tc-tic6x.h
index a324c02..9c0073d 100644
--- a/gas/config/tc-tic6x.h
+++ b/gas/config/tc-tic6x.h
@@ -77,6 +77,11 @@ typedef struct
      from the SPLOOP instruction (in the range 1 to 14); otherwise
      0.  */
   int sploop_ii;
+
+  /* Bit N indicates that an R_C6000_NONE relocation has been output for
+     __c6xabi_unwind_cpp_prN already if set. This enables dependencies to be
+     emitted only once per section, to save unnecessary bloat.  */
+  unsigned int marked_pr_dependency;
 } tic6x_segment_info_type;
 #define TC_SEGMENT_INFO_TYPE tic6x_segment_info_type
 
@@ -158,3 +163,28 @@ extern void tic6x_init_after_args (void);
 
 #define tc_unrecognized_line(c) tic6x_unrecognized_line (c)
 extern int tic6x_unrecognized_line (int c);
+
+/* We want .cfi_* pseudo-ops for generating unwind info.  */
+#define TARGET_USE_CFIPOP              1
+
+/* CFI hooks.  */
+#define tc_regname_to_dw2regnum            tic6x_regname_to_dw2regnum
+int tic6x_regname_to_dw2regnum (char *regname);
+
+#define tc_cfi_frame_initial_instructions  tic6x_frame_initial_instructions
+void tic6x_frame_initial_instructions (void);
+
+/* The return register is B3.  */
+#define DWARF2_DEFAULT_RETURN_COLUMN  (16 + 3)
+
+/* Registers are generally saved at negative offsets to the CFA.  */
+#define DWARF2_CIE_DATA_ALIGNMENT     (-4)
+
+#define tc_cfi_startproc tic6x_cfi_startproc
+void tic6x_cfi_startproc (void);
+
+#define tc_cfi_endproc tic6x_cfi_endproc
+struct fde_entry;
+void tic6x_cfi_endproc (struct fde_entry *fde);
+
+#define tc_cfi_section_name ".c6xabi.exidx"
diff --git a/gas/doc/c-tic6x.texi b/gas/doc/c-tic6x.texi
index b1e04f9..bc69160 100644
--- a/gas/doc/c-tic6x.texi
+++ b/gas/doc/c-tic6x.texi
@@ -131,6 +131,14 @@ subsequent directive overriding it.
 @item .arch @var{arch}
 This has the same effect as @option{-march=@var{arch}}.
 
+@cindex @code{.cantunwind} directive, TIC6X
+@item .cantunwind
+Prevents unwinding through the current function.  No personality routine
+or exception table data is required or permitted.
+
+If this is not specified then frame unwinding information will be
+constructed from CFI directives. @pxref{CFI directives}.
+
 @cindex @code{.c6xabi_attribute} directive, TIC6X
 @item .c6xabi_attribute @var{tag}, @var{value}
 Set the C6000 EABI build attribute @var{tag} to @var{value}.
@@ -150,11 +158,35 @@ The @var{tag} is either an attribute number or one of
 @item .ehtype @var{symbol}
 Output an exception type table reference to @var{symbol}.
 
+@cindex @code{.endp} directive, TIC6X
+@item .endp
+Marks the end of and exception table or function.  If preceeded by a
+@code{.handlerdata} directive then this also switched back to the previous
+text section.
+
+@cindex @code{.handlerdata} directive, TIC6X
+@item .handlerdata
+Marks the end of the current function, and the start of the exception table
+entry for that function.  Anything between this directive and the
+@code{.endp} directive will be added to the exception table entry.
+
+Must be preceded by a CFI block containing a @code{.cfi_lsda} directive.
+directive.
+
 @cindex @code{.nocmp} directive, TIC6X
 @item .nocmp
 Disallow use of C64x+ compact instructions in the current text
 section.
 
+@cindex @code{.personalityindex} directive, TIC6X
+@item .personalityindex @var{index}
+Sets the personality routine for the current function to the ABI specified 
+compact routine number @var{index}
+
+@cindex @code{.personality} directive, TIC6X
+@item .personality @var{name}
+Sets the personality routine for the current function to @var{name}.
+
 @cindex @code{.scomm} directive, TIC6X
 @item .scomm @var{symbol}, @var{size}, @var{align}
 Like @code{.comm}, creating a common symbol @var{symbol} with size @var{size}
diff --git a/gas/dw2gencfi.c b/gas/dw2gencfi.c
index 7a051e3..7910703 100644
--- a/gas/dw2gencfi.c
+++ b/gas/dw2gencfi.c
@@ -68,6 +68,14 @@
 # define tc_cfi_frame_initial_instructions() ((void)0)
 #endif
 
+#ifndef tc_cfi_startproc
+# define tc_cfi_startproc() ((void)0)
+#endif
+
+#ifndef tc_cfi_endproc
+# define tc_cfi_endproc(fde) ((void)0)
+#endif
+
 #ifndef DWARF2_FORMAT
 # define DWARF2_FORMAT(SEC) dwarf2_format_32bit
 #endif
@@ -99,53 +107,6 @@ struct cfi_escape_data {
   expressionS exp;
 };
 
-struct cfi_insn_data
-{
-  struct cfi_insn_data *next;
-  int insn;
-  union {
-    struct {
-      unsigned reg;
-      offsetT offset;
-    } ri;
-
-    struct {
-      unsigned reg1;
-      unsigned reg2;
-    } rr;
-
-    unsigned r;
-    offsetT i;
-
-    struct {
-      symbolS *lab1;
-      symbolS *lab2;
-    } ll;
-
-    struct cfi_escape_data *esc;
-
-    struct {
-      unsigned reg, encoding;
-      expressionS exp;
-    } ea;
-  } u;
-};
-
-struct fde_entry
-{
-  struct fde_entry *next;
-  symbolS *start_address;
-  symbolS *end_address;
-  struct cfi_insn_data *data;
-  struct cfi_insn_data **last;
-  unsigned char per_encoding;
-  unsigned char lsda_encoding;
-  expressionS personality;
-  expressionS lsda;
-  unsigned int return_column;
-  unsigned int signal_frame;
-};
-
 struct cie_entry
 {
   struct cie_entry *next;
@@ -424,14 +385,6 @@ static void dot_cfi_personality (int);
 static void dot_cfi_lsda (int);
 static void dot_cfi_val_encoded_addr (int);
 
-/* Fake CFI type; outside the byte range of any real CFI insn.  */
-#define CFI_adjust_cfa_offset	0x100
-#define CFI_return_column	0x101
-#define CFI_rel_offset		0x102
-#define CFI_escape		0x103
-#define CFI_signal_frame	0x104
-#define CFI_val_encoded_addr	0x105
-
 const pseudo_typeS cfi_pseudo_table[] =
   {
     { "cfi_sections", dot_cfi_sections, 0 },
@@ -900,6 +853,7 @@ dot_cfi_val_encoded_addr (int ignored ATTRIBUTE_UNUSED)
 /* By default emit .eh_frame only, not .debug_frame.  */
 #define CFI_EMIT_eh_frame	(1 << 0)
 #define CFI_EMIT_debug_frame	(1 << 1)
+#define CFI_EMIT_target		(1 << 2)
 static int cfi_sections = CFI_EMIT_eh_frame;
 
 static void
@@ -920,6 +874,10 @@ dot_cfi_sections (int ignored ATTRIBUTE_UNUSED)
 	  sections |= CFI_EMIT_eh_frame;
 	else if (strcmp (name, ".debug_frame") == 0)
 	  sections |= CFI_EMIT_debug_frame;
+#ifdef tc_cfi_section_name
+	else if (strcmp (name, tc_cfi_section_name) == 0)
+	  sections |= CFI_EMIT_target;
+#endif
 	else
 	  {
 	    *input_line_pointer = c;
@@ -982,11 +940,16 @@ dot_cfi_startproc (int ignored ATTRIBUTE_UNUSED)
   frchain_now->frch_cfi_data->cur_cfa_offset = 0;
   if (!simple)
     tc_cfi_frame_initial_instructions ();
+
+  if ((cfi_sections & CFI_EMIT_target) != 0)
+    tc_cfi_startproc ();
 }
 
 static void
 dot_cfi_endproc (int ignored ATTRIBUTE_UNUSED)
 {
+  struct fde_entry *fde;
+
   if (frchain_now->frch_cfi_data == NULL)
     {
       as_bad (_(".cfi_endproc without corresponding .cfi_startproc"));
@@ -994,9 +957,14 @@ dot_cfi_endproc (int ignored ATTRIBUTE_UNUSED)
       return;
     }
 
+  fde = frchain_now->frch_cfi_data->cur_fde_data;
+
   cfi_end_fde (symbol_temp_new_now ());
 
   demand_empty_rest_of_line ();
+
+  if ((cfi_sections & CFI_EMIT_target) != 0)
+    tc_cfi_endproc (fde);
 }
 
 
diff --git a/gas/dw2gencfi.h b/gas/dw2gencfi.h
index f8f235d..bbc10ff 100644
--- a/gas/dw2gencfi.h
+++ b/gas/dw2gencfi.h
@@ -49,4 +49,60 @@ extern void cfi_add_CFA_same_value (unsigned);
 extern void cfi_add_CFA_remember_state (void);
 extern void cfi_add_CFA_restore_state (void);
 
+/* Structures for md_cfi_end.  */
+struct cfi_insn_data
+{
+  struct cfi_insn_data *next;
+  int insn;
+  union {
+    struct {
+      unsigned reg;
+      offsetT offset;
+    } ri;
+
+    struct {
+      unsigned reg1;
+      unsigned reg2;
+    } rr;
+
+    unsigned r;
+    offsetT i;
+
+    struct {
+      symbolS *lab1;
+      symbolS *lab2;
+    } ll;
+
+    struct cfi_escape_data *esc;
+
+    struct {
+      unsigned reg, encoding;
+      expressionS exp;
+    } ea;
+  } u;
+};
+
+struct fde_entry
+{
+  struct fde_entry *next;
+  symbolS *start_address;
+  symbolS *end_address;
+  struct cfi_insn_data *data;
+  struct cfi_insn_data **last;
+  unsigned char per_encoding;
+  unsigned char lsda_encoding;
+  expressionS personality;
+  expressionS lsda;
+  unsigned int return_column;
+  unsigned int signal_frame;
+};
+
+/* Fake CFI type; outside the byte range of any real CFI insn.  */
+#define CFI_adjust_cfa_offset	0x100
+#define CFI_return_column	0x101
+#define CFI_rel_offset		0x102
+#define CFI_escape		0x103
+#define CFI_signal_frame	0x104
+#define CFI_val_encoded_addr	0x105
+
 #endif /* DW2GENCFI_H */
diff --git a/gas/testsuite/gas/tic6x/unwind-1.d b/gas/testsuite/gas/tic6x/unwind-1.d
new file mode 100644
index 0000000..1b240f9
--- /dev/null
+++ b/gas/testsuite/gas/tic6x/unwind-1.d
@@ -0,0 +1,100 @@
+#readelf: -u
+#name: C6X unwinding directives 1 (little endian)
+#as: -mlittle-endian
+#source: unwind-1.s
+
+Unwind table index '.c6xabi.exidx' .*
+
+0x0: 0x83020227
+  Compact model 3
+  Stack increment 8
+  Registers restored: A11, B3
+  Return register: B3
+
+0x100: 0x808003e7
+  Compact model 0
+  0x80 0x03 pop {A10, A11}
+  0xe7      RETURN
+
+0x200: 0x81008863
+  Compact model 1
+  0x88 0x63 pop {A10, A11, B3, B10, B15}
+
+0x300: 0x83020227
+  Compact model 3
+  Stack increment 8
+  Registers restored: A11, B3
+  Return register: B3
+
+0x400: 0x84000227
+  Compact model 4
+  Stack increment 0
+  Registers restored:  \(compact\) A11, B3
+  Return register: B3
+
+0x500: 0x80a022e7
+  Compact model 0
+  0xa0 0x22 pop compact {A11, B3}
+  0xe7      RETURN
+
+0x600: 0x84000227
+  Compact model 4
+  Stack increment 0
+  Registers restored:  \(compact\) A11, B3
+  Return register: B3
+
+0x700: 0x84000637
+  Compact model 4
+  Stack increment 0
+  Registers restored:  \(compact\) A10, A11, B3, B10
+  Return register: B3
+
+0x800: 0x840002d7
+  Compact model 4
+  Stack increment 0
+  Registers restored:  \(compact\) A10, A12, A13, B3
+  Return register: B3
+
+0x900: 0x84000c07
+  Compact model 4
+  Stack increment 0
+  Registers restored:  \(compact\) B10, B11
+  Return register: B3
+
+0xa00: 0x83ff0027
+  Compact model 3
+  Restore stack from frame pointer
+  Registers restored: A11, A15
+  Return register: B3
+
+0xb00: 0x84ff0027
+  Compact model 4
+  Restore stack from frame pointer
+  Registers restored:  \(compact\) A11, A15
+  Return register: B3
+
+0xc00: 0x8001c1f7
+  Compact model 0
+  0x01      sp = sp \+ 16
+  0xc1 0xf7 pop frame {B3, \[pad\]}
+
+0xd00: @0x.*
+  Compact model 1
+  0x01      sp = sp \+ 16
+  0xc2 0xf7 0xbf pop frame {\[pad\], A11, B3, \[pad\]}
+  0xe7      RETURN
+  0xe7      RETURN
+
+0xe00: @0x.*
+  Compact model 1
+  0x01      sp = sp \+ 16
+  0xc2 0xf7 0xfb pop frame {A11, \[pad\], B3, \[pad\]}
+  0xe7      RETURN
+  0xe7      RETURN
+
+0xf00: @0x.*
+  Compact model 1
+  0x02      sp = sp \+ 24
+  0xc2 0x7f 0xff 0xfb pop frame {A11, \[pad\], \[pad\], \[pad\], \[pad\], B3}
+  0xe7      RETURN
+
diff --git a/gas/testsuite/gas/tic6x/unwind-1.s b/gas/testsuite/gas/tic6x/unwind-1.s
new file mode 100644
index 0000000..3fbc888
--- /dev/null
+++ b/gas/testsuite/gas/tic6x/unwind-1.s
@@ -0,0 +1,242 @@
+.cfi_sections .c6xabi.exidx
+
+# standard layout
+.p2align 8
+f0:
+.cfi_startproc
+stw .d2t2 B3, *B15--(16)
+.cfi_def_cfa_offset 16
+.cfi_offset 19, 0
+stw .d2t1 A11, *+B15(12)
+.cfi_offset 11, -4
+nop 4
+.cfi_endproc
+.endp
+
+# standard layout (pr0)
+.p2align 8
+f1:
+.cfi_startproc
+.cfi_def_cfa_offset 8
+stw .d2t1 A11, *+B15(8)
+.cfi_offset 11, -0
+stw .d2t1 A10, *+B15(4)
+.cfi_offset 10, -4
+nop 4
+.cfi_endproc
+.personalityindex 0
+.endp
+
+# standard layout (pr1)
+.p2align 8
+f2:
+.cfi_startproc
+stw .d2t2 B15, *B15--(24)
+.cfi_def_cfa_offset 24
+.cfi_offset 31, 0
+stw .d2t2 B10, *+B15(20)
+.cfi_offset 26, -4
+stw .d2t2 B3, *+B15(16)
+.cfi_offset 19, -8
+stdw .d2t1 A11:A10, *+B15(8)
+.cfi_offset 11, -12
+.cfi_offset 10, -16
+nop 4
+.cfi_endproc
+.personalityindex 1
+.endp
+
+# standard layout (pr3)
+.p2align 8
+f3:
+.cfi_startproc
+stw .d2t2 B3, *B15--(16)
+.cfi_def_cfa_offset 16
+.cfi_offset 19, 0
+stw .d2t1 A11, *+B15(12)
+.cfi_offset 11, -4
+nop 4
+.cfi_endproc
+.personalityindex 3
+.endp
+
+# compact layout
+.p2align 8
+f4:
+.cfi_startproc
+stw .d2t2 B3, *B15--(8)
+.cfi_offset 19, 0
+.cfi_def_cfa_offset 8
+stw .d2t1 A11, *B15--(8)
+.cfi_offset 11, -8
+.cfi_def_cfa_offset 16
+nop 4
+.cfi_endproc
+.endp
+
+# compact layout (pr0)
+.p2align 8
+f5:
+.cfi_startproc
+stw .d2t2 B3, *B15--(8)
+.cfi_offset 19, 0
+.cfi_def_cfa_offset 8
+stw .d2t1 A11, *B15--(8)
+.cfi_offset 11, -8
+.cfi_def_cfa_offset 16
+nop 4
+.cfi_endproc
+.personalityindex 0
+.endp
+
+# compact layout (pr4)
+.p2align 8
+f6:
+.cfi_startproc
+stw .d2t2 B3, *B15--(8)
+.cfi_offset 19, 0
+.cfi_def_cfa_offset 8
+stw .d2t1 A11, *B15--(8)
+.cfi_offset 11, -8
+.cfi_def_cfa_offset 16
+nop 4
+.cfi_endproc
+.personalityindex 4
+.endp
+
+# compact layout (aligned pair)
+.p2align 8
+f7:
+.cfi_startproc
+stw .d2t2 B10, *B15--(8)
+.cfi_offset 26, 0
+.cfi_def_cfa_offset 8
+stw .d2t2 B3, *B15--(8)
+.cfi_offset 19, -8
+.cfi_def_cfa_offset 8
+stdw .d2t1 A11:A10, *B15--(8)
+.cfi_offset 11, -12
+.cfi_offset 10, -16
+.cfi_def_cfa_offset 24
+nop 4
+.cfi_endproc
+.endp
+
+# compact layout (aligned pair + 1)
+.p2align 8
+f8:
+.cfi_startproc
+stw .d2t2 B3, *B15--(8)
+.cfi_offset 19, 0
+.cfi_def_cfa_offset 8
+stdw .d2t1 A13:A12, *B15--(8)
+.cfi_offset 13, -4
+.cfi_offset 12, -8
+.cfi_def_cfa_offset 16
+stw .d2t1 A10, *B15--(8)
+.cfi_offset 10, -16
+.cfi_def_cfa_offset 24
+nop 4
+.cfi_endproc
+.endp
+
+# compact layout (misaligned pair)
+.p2align 8
+f9:
+.cfi_startproc
+stw .d2t2 B11, *B15--(8)
+.cfi_offset 27, 0
+.cfi_def_cfa_offset 8
+stw .d2t2 B10, *B15--(8)
+.cfi_offset 26, -8
+.cfi_def_cfa_offset 16
+nop 4
+.cfi_endproc
+.endp
+
+# standard frame pointer
+.p2align 8
+fa:
+.cfi_startproc
+stw .d2t1 A15, *B15--(16)
+.cfi_def_cfa_offset 8
+.cfi_offset 15, 0
+mv .s1x B15, A15
+addk .s1 16, A15
+.cfi_def_cfa 15, 0
+stw .d2t1 A11, *+B15(12)
+.cfi_offset 11, -4
+nop 4
+.cfi_endproc
+.endp
+
+# compact frame pointer
+.p2align 8
+fb:
+.cfi_startproc
+stw .d2t1 A15, *B15--(8)
+.cfi_def_cfa_offset 8
+.cfi_offset 15, 0
+mv .s1x B15, A15
+addk .s1 16, A15
+.cfi_def_cfa 15, 0
+stw .d2t1 A11, *B15--(8)
+.cfi_offset 11, -8
+nop 4
+.cfi_endproc
+.endp
+
+# custom layout
+.p2align 8
+fc:
+.cfi_startproc
+sub .s2 B15, 16, B15
+stw .d2t2 B3, *+B15(12)
+.cfi_def_cfa_offset 16
+.cfi_offset 19, -4
+nop 4
+.cfi_endproc
+.endp
+
+# custom layout
+.p2align 8
+fd:
+.cfi_startproc
+sub .s2 B15, 16, B15
+stw .d2t2 B3, *+B15(12)
+.cfi_def_cfa_offset 16
+.cfi_offset 19, -4
+stw .d2t1 A11, *+B15(8)
+.cfi_offset 11, -8
+nop 4
+.cfi_endproc
+.endp
+
+# custom layout
+.p2align 8
+fe:
+.cfi_startproc
+sub .s2 B15, 16, B15
+stw .d2t2 B3, *+B15(12)
+.cfi_def_cfa_offset 16
+.cfi_offset 19, -4
+stw .d2t1 A11, *+B15(4)
+.cfi_offset 11, -12
+nop 4
+.cfi_endproc
+.endp
+
+# custom layout
+.p2align 8
+ff:
+.cfi_startproc
+addk .s2 -24, B15
+stw .d2t2 B3, *+B15(24)
+.cfi_def_cfa_offset 24
+.cfi_offset 19, 0
+stw .d2t1 A11, *+B15(4)
+.cfi_offset 11, -20
+nop 4
+.cfi_endproc
+.endp
+
diff --git a/gas/testsuite/gas/tic6x/unwind-2.d b/gas/testsuite/gas/tic6x/unwind-2.d
new file mode 100644
index 0000000..c022ec4
--- /dev/null
+++ b/gas/testsuite/gas/tic6x/unwind-2.d
@@ -0,0 +1,100 @@
+#readelf: -u
+#name: C6X unwinding directives 2 (big endian)
+#as: -mbig-endian
+#source: unwind-2.s
+
+Unwind table index '.c6xabi.exidx' .*
+
+0x0: 0x83020227
+  Compact model 3
+  Stack increment 8
+  Registers restored: A11, B3
+  Return register: B3
+
+0x100: 0x808003e7
+  Compact model 0
+  0x80 0x03 pop {A10, A11}
+  0xe7      RETURN
+
+0x200: 0x81008863
+  Compact model 1
+  0x88 0x63 pop {A10, A11, B3, B10, B15}
+
+0x300: 0x83020227
+  Compact model 3
+  Stack increment 8
+  Registers restored: A11, B3
+  Return register: B3
+
+0x400: 0x84000227
+  Compact model 4
+  Stack increment 0
+  Registers restored:  \(compact\) A11, B3
+  Return register: B3
+
+0x500: 0x80a022e7
+  Compact model 0
+  0xa0 0x22 pop compact {A11, B3}
+  0xe7      RETURN
+
+0x600: 0x84000227
+  Compact model 4
+  Stack increment 0
+  Registers restored:  \(compact\) A11, B3
+  Return register: B3
+
+0x700: 0x84000637
+  Compact model 4
+  Stack increment 0
+  Registers restored:  \(compact\) A10, A11, B3, B10
+  Return register: B3
+
+0x800: 0x840002d7
+  Compact model 4
+  Stack increment 0
+  Registers restored:  \(compact\) A10, A12, A13, B3
+  Return register: B3
+
+0x900: 0x84000c07
+  Compact model 4
+  Stack increment 0
+  Registers restored:  \(compact\) B10, B11
+  Return register: B3
+
+0xa00: 0x83ff0027
+  Compact model 3
+  Restore stack from frame pointer
+  Registers restored: A11, A15
+  Return register: B3
+
+0xb00: 0x84ff0027
+  Compact model 4
+  Restore stack from frame pointer
+  Registers restored:  \(compact\) A11, A15
+  Return register: B3
+
+0xc00: 0x8001c1f7
+  Compact model 0
+  0x01      sp = sp \+ 16
+  0xc1 0xf7 pop frame {B3, \[pad\]}
+
+0xd00: @0x.*
+  Compact model 1
+  0x01      sp = sp \+ 16
+  0xc2 0xf7 0xbf pop frame {\[pad\], A11, B3, \[pad\]}
+  0xe7      RETURN
+  0xe7      RETURN
+
+0xe00: @0x.*
+  Compact model 1
+  0x01      sp = sp \+ 16
+  0xc2 0xf7 0xfb pop frame {A11, \[pad\], B3, \[pad\]}
+  0xe7      RETURN
+  0xe7      RETURN
+
+0xf00: @0x.*
+  Compact model 1
+  0x02      sp = sp \+ 24
+  0xc2 0x7f 0xff 0xfb pop frame {A11, \[pad\], \[pad\], \[pad\], \[pad\], B3}
+  0xe7      RETURN
+
diff --git a/gas/testsuite/gas/tic6x/unwind-2.s b/gas/testsuite/gas/tic6x/unwind-2.s
new file mode 100644
index 0000000..1ab4d67
--- /dev/null
+++ b/gas/testsuite/gas/tic6x/unwind-2.s
@@ -0,0 +1,242 @@
+.cfi_sections .c6xabi.exidx
+
+# standard layout
+.p2align 8
+f0:
+.cfi_startproc
+stw .d2t2 B3, *B15--(16)
+.cfi_def_cfa_offset 16
+.cfi_offset 19, 0
+stw .d2t1 A11, *+B15(12)
+.cfi_offset 11, -4
+nop 4
+.cfi_endproc
+.endp
+
+# standard layout (pr0)
+.p2align 8
+f1:
+.cfi_startproc
+.cfi_def_cfa_offset 8
+stw .d2t1 A11, *+B15(8)
+.cfi_offset 11, -0
+stw .d2t1 A10, *+B15(4)
+.cfi_offset 10, -4
+nop 4
+.cfi_endproc
+.personalityindex 0
+.endp
+
+# standard layout (pr1)
+.p2align 8
+f2:
+.cfi_startproc
+stw .d2t2 B15, *B15--(24)
+.cfi_def_cfa_offset 24
+.cfi_offset 31, 0
+stw .d2t2 B10, *+B15(20)
+.cfi_offset 26, -4
+stw .d2t2 B3, *+B15(16)
+.cfi_offset 19, -8
+stdw .d2t1 A11:A10, *+B15(8)
+.cfi_offset 11, -16
+.cfi_offset 10, -12
+nop 4
+.cfi_endproc
+.personalityindex 1
+.endp
+
+# standard layout (pr3)
+.p2align 8
+f3:
+.cfi_startproc
+stw .d2t2 B3, *B15--(16)
+.cfi_def_cfa_offset 16
+.cfi_offset 19, 0
+stw .d2t1 A11, *+B15(12)
+.cfi_offset 11, -4
+nop 4
+.cfi_endproc
+.personalityindex 3
+.endp
+
+# compact layout
+.p2align 8
+f4:
+.cfi_startproc
+stw .d2t2 B3, *B15--(8)
+.cfi_offset 19, 0
+.cfi_def_cfa_offset 8
+stw .d2t1 A11, *B15--(8)
+.cfi_offset 11, -8
+.cfi_def_cfa_offset 16
+nop 4
+.cfi_endproc
+.endp
+
+# compact layout (pr0)
+.p2align 8
+f5:
+.cfi_startproc
+stw .d2t2 B3, *B15--(8)
+.cfi_offset 19, 0
+.cfi_def_cfa_offset 8
+stw .d2t1 A11, *B15--(8)
+.cfi_offset 11, -8
+.cfi_def_cfa_offset 16
+nop 4
+.cfi_endproc
+.personalityindex 0
+.endp
+
+# compact layout (pr4)
+.p2align 8
+f6:
+.cfi_startproc
+stw .d2t2 B3, *B15--(8)
+.cfi_offset 19, 0
+.cfi_def_cfa_offset 8
+stw .d2t1 A11, *B15--(8)
+.cfi_offset 11, -8
+.cfi_def_cfa_offset 16
+nop 4
+.cfi_endproc
+.personalityindex 4
+.endp
+
+# compact layout (aligned pair)
+.p2align 8
+f7:
+.cfi_startproc
+stw .d2t2 B10, *B15--(8)
+.cfi_offset 26, 0
+.cfi_def_cfa_offset 8
+stw .d2t2 B3, *B15--(8)
+.cfi_offset 19, -8
+.cfi_def_cfa_offset 8
+stdw .d2t1 A11:A10, *B15--(8)
+.cfi_offset 11, -16
+.cfi_offset 10, -12
+.cfi_def_cfa_offset 24
+nop 4
+.cfi_endproc
+.endp
+
+# compact layout (aligned pair + 1)
+.p2align 8
+f8:
+.cfi_startproc
+stw .d2t2 B3, *B15--(8)
+.cfi_offset 19, 0
+.cfi_def_cfa_offset 8
+stdw .d2t1 A13:A12, *B15--(8)
+.cfi_offset 13, -8
+.cfi_offset 12, -4
+.cfi_def_cfa_offset 16
+stw .d2t1 A10, *B15--(8)
+.cfi_offset 10, -16
+.cfi_def_cfa_offset 24
+nop 4
+.cfi_endproc
+.endp
+
+# compact layout (misaligned pair)
+.p2align 8
+f9:
+.cfi_startproc
+stw .d2t2 B11, *B15--(8)
+.cfi_offset 27, 0
+.cfi_def_cfa_offset 8
+stw .d2t2 B10, *B15--(8)
+.cfi_offset 26, -8
+.cfi_def_cfa_offset 16
+nop 4
+.cfi_endproc
+.endp
+
+# standard frame pointer
+.p2align 8
+fa:
+.cfi_startproc
+stw .d2t1 A15, *B15--(16)
+.cfi_def_cfa_offset 8
+.cfi_offset 15, 0
+mv .s1x B15, A15
+addk .s1 16, A15
+.cfi_def_cfa 15, 0
+stw .d2t1 A11, *+B15(12)
+.cfi_offset 11, -4
+nop 4
+.cfi_endproc
+.endp
+
+# compact frame pointer
+.p2align 8
+fb:
+.cfi_startproc
+stw .d2t1 A15, *B15--(8)
+.cfi_def_cfa_offset 8
+.cfi_offset 15, 0
+mv .s1x B15, A15
+addk .s1 16, A15
+.cfi_def_cfa 15, 0
+stw .d2t1 A11, *B15--(8)
+.cfi_offset 11, -8
+nop 4
+.cfi_endproc
+.endp
+
+# custom layout
+.p2align 8
+fc:
+.cfi_startproc
+sub .s2 B15, 16, B15
+stw .d2t2 B3, *+B15(12)
+.cfi_def_cfa_offset 16
+.cfi_offset 19, -4
+nop 4
+.cfi_endproc
+.endp
+
+# custom layout
+.p2align 8
+fd:
+.cfi_startproc
+sub .s2 B15, 16, B15
+stw .d2t2 B3, *+B15(12)
+.cfi_def_cfa_offset 16
+.cfi_offset 19, -4
+stw .d2t1 A11, *+B15(8)
+.cfi_offset 11, -8
+nop 4
+.cfi_endproc
+.endp
+
+# custom layout
+.p2align 8
+fe:
+.cfi_startproc
+sub .s2 B15, 16, B15
+stw .d2t2 B3, *+B15(12)
+.cfi_def_cfa_offset 16
+.cfi_offset 19, -4
+stw .d2t1 A11, *+B15(4)
+.cfi_offset 11, -12
+nop 4
+.cfi_endproc
+.endp
+
+# custom layout
+.p2align 8
+ff:
+.cfi_startproc
+addk .s2 -24, B15
+stw .d2t2 B3, *+B15(24)
+.cfi_def_cfa_offset 24
+.cfi_offset 19, 0
+stw .d2t1 A11, *+B15(4)
+.cfi_offset 11, -20
+nop 4
+.cfi_endproc
+.endp
+
diff --git a/include/elf/tic6x.h b/include/elf/tic6x.h
index 46f43c8..e686cc3 100644
--- a/include/elf/tic6x.h
+++ b/include/elf/tic6x.h
@@ -158,4 +158,10 @@ enum
     C6XABI_Tag_ISA_C674X = 8
   };
 
+/* Special section names.  */
+#define ELF_STRING_C6000_unwind           ".c6xabi.exidx"
+#define ELF_STRING_C6000_unwind_info      ".c6xabi.extab"
+#define ELF_STRING_C6000_unwind_once      ".gnu.linkonce.c6xabi.exidx."
+#define ELF_STRING_C6000_unwind_info_once ".gnu.linkonce.c6xabi.extab."
+
 #endif /* _ELF_TIC6X_H */
-- 
1.7.4.1


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