public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* [Patch] Gas support for MIPS Compact EH
@ 2013-05-31 18:56 Moore, Catherine
  2013-06-01 11:07 ` Richard Sandiford
  0 siblings, 1 reply; 31+ messages in thread
From: Moore, Catherine @ 2013-05-31 18:56 UTC (permalink / raw)
  To: rdsandiford; +Cc: Moore, Catherine, binutils

[-- Attachment #1: Type: text/plain, Size: 322 bytes --]

Hi Richard,
These are the remaining assembler patches required to support the Compact EH format.  Will you please take a look and let me if these are ready to be committed?
Thanks,
Catherine

MIPS Compact Exception Handling Specification:
https://github.com/MentorEmbedded/cxx-abi/blob/master/MIPSCompactEH.pdf



[-- Attachment #2: gas.cl --]
[-- Type: application/octet-stream, Size: 1308 bytes --]

2013-05-13 Paul Brook <paul@codesourcery.com>

	* doc/as.texinfo: Document .cfi_sections .eh_frame_header and
	.cfi_inline_lsda.
	* dw2gencfi.c (EH_FRAME_LINKONCE): Define.
	(last_fde): New variable.
	(tc_cfi_emit_expr, tc_cfi_special_encoding): Provide defaults.
	(compact_eh): New variable.
	(FRAME_NAME): Use .gnu.extab for compact frames.
	(get_debugseg_name): Suffix all EH sections except for .text.
	(is_now_linkonce_segment): Do not squich regular compact EH
	sections together.
	(make_debug_seg): Use tc_make_debug_seg.
	(cie_entry): Add fde_encoding.
	(cfi_pseudo_table): Add "cfi_inline_lsda".
	(dot_cfi_personality, dot_cfi_lsda): Use tc_cfi_special_encoding.
	(dot_cfi_sections): Accept .eh_frame_entry.
	(dot_cfi_startproc): Set last_fde.  Call tc_cfi_endproc for compact EH.
	(get_cfi_seg): Move higher in file.
	(output_compact_unwind_data, dot_cfi_inline_lsda): New functions.
	(output_cie): Use tc_cfi_special_encoding and
	DWARF2_FDE_RELOC_ENCODING.
	(output_fde): Use tc_cfi_special_encoding.
	(cfi_emit_eh_header, output_eh_header): New functions.
	(cfi_finish): Handle compact EH frame generation.
	* dw2gencfi.h (SUPPORT_COMPACT_EH, MULTIPLE_FRAME_SECTIONS): Define.
	(EH_COMPACT_*): Define.
	(fde_entry): Add eh_header_type, eh_data_size, eh_data, eh_loc.

[-- Attachment #3: gas.patch --]
[-- Type: application/octet-stream, Size: 22979 bytes --]

Index: doc/as.texinfo
===================================================================
RCS file: /cvs/src/src/gas/doc/as.texinfo,v
retrieving revision 1.266
diff -p -u -r1.266 as.texinfo
--- doc/as.texinfo	29 Apr 2013 13:38:58 -0000	1.266
+++ doc/as.texinfo	31 May 2013 17:24:20 -0000
@@ -4479,6 +4479,9 @@ if @var{section_list} is @code{.debug_fr
 To emit both use @code{.eh_frame, .debug_frame}.  The default if this
 directive is not used is @code{.cfi_sections .eh_frame}.
 
+On targets that support compact unwinding tables these can be generated
+by specifying @code{.eh_frame_header} instead of @code{.eh_frame}.
+
 @section @code{.cfi_startproc [simple]}
 @cindex @code{cfi_startproc} directive
 @code{.cfi_startproc} is used at the beginning of each function that
@@ -4514,6 +4517,23 @@ argument is not present, otherwise secon
 or a symbol name.  The default after @code{.cfi_startproc} is @code{.cfi_lsda 0xff},
 no LSDA.
 
+@section @code{.cfi_inline_lsda} [@var{align}]
+@code{.cfi_inline_lsda} marks the start of a LSDA data section and
+switches to the corresponding @code{.gnu.extab} section.
+It must be preceded by a CFI block containing a @code{.cfi_lsda} directive and
+is only valid when generating compact EH frames (i.e.
+with @code{.cfi_sections eh_frame_entry}.
+
+If a compact encoding is being used then the table header and unwinding
+opcodes will be generated at this point, so that they are immediately
+followed by the LSDA data.  The symbol referenced by the @code{.cfi_lsda}
+directive should still be defined in case a fallback FDE based encoding
+is used.
+
+The optional @var{align} argument specifies the alignment required.
+The alignment is specified as a power of two, as with the
+@code{.p2align} directive.
+
 @section @code{.cfi_def_cfa @var{register}, @var{offset}}
 @code{.cfi_def_cfa} defines a rule for computing CFA as: @i{take
 address from @var{register} and add @var{offset} to it}.

Index: dw2gencfi.c
===================================================================
RCS file: /cvs/src/src/gas/dw2gencfi.c,v
retrieving revision 1.58
diff -p -u -r1.58 dw2gencfi.c
--- dw2gencfi.c	10 Jan 2013 19:51:54 -0000	1.58
+++ dw2gencfi.c	31 May 2013 17:24:20 -0000
@@ -76,6 +76,8 @@
 # define tc_cfi_endproc(fde) ((void) (fde))
 #endif
 
+#define EH_FRAME_LINKONCE (SUPPORT_FRAME_LINKONCE || compact_eh)
+
 #ifndef DWARF2_FORMAT
 #define DWARF2_FORMAT(SEC) dwarf2_format_32bit
 #endif
@@ -84,7 +86,7 @@
 #define DWARF2_ADDR_SIZE(bfd) (bfd_arch_bits_per_address (bfd) / 8)
 #endif
 
-#if SUPPORT_FRAME_LINKONCE
+#if MULTIPLE_FRAME_SECTIONS
 #define CUR_SEG(structp) structp->cur_seg
 #define SET_CUR_SEG(structp, seg) structp->cur_seg = seg
 #define HANDLED(structp) structp->handled
@@ -96,6 +98,11 @@
 #define SET_HANDLED(structp, val) (void) (0 && val)
 #endif
 
+#ifndef tc_cfi_special_encoding
+#define tc_cfi_emit_expr(exp, enc) (void) 0
+#define tc_cfi_special_encoding(e) 0
+#endif
+
 /* Private segment collection list.  */
 struct dwcfi_seg_list
 {
@@ -104,7 +111,11 @@ struct dwcfi_seg_list
   char * seg_name;
 };
 
-#define FRAME_NAME ".eh_frame"
+#ifdef SUPPORT_COMPACT_EH
+static bfd_boolean compact_eh;
+#else
+#define compact_eh 0
+#endif
 
 static struct hash_control *dwcfi_hash;
 
@@ -129,7 +140,12 @@ get_debugseg_name (segT seg, const char 
       dot = strchr (name + 1, '.');
 
       if (!dollar && !dot)
-	name = "";
+	{
+	  if (compact_eh && strcmp (name, ".text") != 0)
+	    return concat (base_name, ".", name, NULL);
+
+	  name = "";
+	}
       else if (!dollar)
 	name = dot;
       else if (!dot)
@@ -161,6 +177,9 @@ alloc_debugseg_item (segT seg, int subse
 static segT
 is_now_linkonce_segment (void)
 {
+  if (compact_eh)
+    return now_seg;
+
   if ((bfd_get_section_flags (stdoutput, now_seg)
        & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD
 	  | SEC_LINK_DUPLICATES_ONE_ONLY | SEC_LINK_DUPLICATES_SAME_SIZE
@@ -180,7 +199,11 @@ make_debug_seg (segT cseg, char *name, i
   segT r;
   flagword flags;
 
+#ifdef tc_make_debug_seg
+  r = tc_make_debug_seg (cseg, name);
+#else
   r = subseg_new (name, 0);
+#endif
 
   /* Check if code segment is marked as linked once.  */
   if (!cseg)
@@ -273,12 +296,13 @@ struct cfi_escape_data
 struct cie_entry
 {
   struct cie_entry *next;
-#if SUPPORT_FRAME_LINKONCE
+#if MULTIPLE_FRAME_SECTIONS
   segT cur_seg;
 #endif
   symbolS *start_address;
   unsigned int return_column;
   unsigned int signal_frame;
+  unsigned char fde_encoding;
   unsigned char per_encoding;
   unsigned char lsda_encoding;
   expressionS personality;
@@ -551,6 +575,7 @@ static void dot_cfi_endproc (int);
 static void dot_cfi_personality (int);
 static void dot_cfi_lsda (int);
 static void dot_cfi_val_encoded_addr (int);
+static void dot_cfi_inline_lsda (int);
 
 const pseudo_typeS cfi_pseudo_table[] =
   {
@@ -576,6 +601,7 @@ const pseudo_typeS cfi_pseudo_table[] =
     { "cfi_personality", dot_cfi_personality, 0 },
     { "cfi_lsda", dot_cfi_lsda, 0 },
     { "cfi_val_encoded_addr", dot_cfi_val_encoded_addr, 0 },
+    { "cfi_inline_lsda", dot_cfi_inline_lsda, 0 },
     { NULL, NULL, 0 }
   };
 
@@ -833,14 +859,15 @@ dot_cfi_personality (int ignored ATTRIBU
     }
 
   if ((encoding & 0xff) != encoding
-      || ((encoding & 0x70) != 0
+      || ((((encoding & 0x70) != 0
 #if CFI_DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr
-	  && (encoding & 0x70) != DW_EH_PE_pcrel
+	   && (encoding & 0x70) != DW_EH_PE_pcrel
 #endif
 	  )
 	 /* leb128 can be handled, but does something actually need it?  */
-      || (encoding & 7) == DW_EH_PE_uleb128
-      || (encoding & 7) > DW_EH_PE_udata8)
+	   || (encoding & 7) == DW_EH_PE_uleb128
+	   || (encoding & 7) > DW_EH_PE_udata8)
+	&& !tc_cfi_special_encoding (encoding)))
     {
       as_bad (_("invalid or unsupported encoding in .cfi_personality"));
       ignore_rest_of_line ();
@@ -903,14 +930,15 @@ dot_cfi_lsda (int ignored ATTRIBUTE_UNUS
     }
 
   if ((encoding & 0xff) != encoding
-      || ((encoding & 0x70) != 0
+      || ((((encoding & 0x70) != 0
 #if CFI_DIFF_LSDA_OK || defined tc_cfi_emit_pcrel_expr
-	  && (encoding & 0x70) != DW_EH_PE_pcrel
+	    && (encoding & 0x70) != DW_EH_PE_pcrel
 #endif
-	  )
+	   )
 	 /* leb128 can be handled, but does something actually need it?  */
-      || (encoding & 7) == DW_EH_PE_uleb128
-      || (encoding & 7) > DW_EH_PE_udata8)
+	   || (encoding & 7) == DW_EH_PE_uleb128
+	   || (encoding & 7) > DW_EH_PE_udata8)
+	  && !tc_cfi_special_encoding (encoding)))
     {
       as_bad (_("invalid or unsupported encoding in .cfi_lsda"));
       ignore_rest_of_line ();
@@ -1022,6 +1050,8 @@ dot_cfi_val_encoded_addr (int ignored AT
 #define CFI_EMIT_debug_frame	(1 << 1)
 #define CFI_EMIT_target		(1 << 2)
 static int cfi_sections = CFI_EMIT_eh_frame;
+static int all_cfi_sections = 0;
+static struct fde_entry *last_fde;
 
 static void
 dot_cfi_sections (int ignored ATTRIBUTE_UNUSED)
@@ -1042,6 +1072,13 @@ dot_cfi_sections (int ignored ATTRIBUTE_
 	  sections |= CFI_EMIT_eh_frame;
 	else if (strncmp (name, ".debug_frame", sizeof ".debug_frame") == 0)
 	  sections |= CFI_EMIT_debug_frame;
+#if SUPPORT_COMPACT_EH
+	else if (strncmp (name, ".eh_frame_entry", sizeof ".eh_frame_entry") == 0)
+	  {
+	    compact_eh = TRUE;
+	    sections |= CFI_EMIT_eh_frame | CFI_EMIT_target;
+	  }
+#endif
 #ifdef tc_cfi_section_name
 	else if (strcmp (name, tc_cfi_section_name) == 0)
 	  sections |= CFI_EMIT_target;
@@ -1105,6 +1142,8 @@ dot_cfi_startproc (int ignored ATTRIBUTE
     }
   demand_empty_rest_of_line ();
 
+  all_cfi_sections |= cfi_sections;
+  frchain_now->frch_cfi_data->cur_fde_data->sections = cfi_sections;
   frchain_now->frch_cfi_data->cur_cfa_offset = 0;
   if (!simple)
     tc_cfi_frame_initial_instructions ();
@@ -1116,8 +1155,6 @@ dot_cfi_startproc (int ignored ATTRIBUTE
 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"));
@@ -1125,16 +1162,147 @@ dot_cfi_endproc (int ignored ATTRIBUTE_U
       return;
     }
 
-  fde = frchain_now->frch_cfi_data->cur_fde_data;
+  last_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);
+  if ((cfi_sections & CFI_EMIT_target) != 0
+      || (compact_eh && (cfi_sections & CFI_EMIT_eh_frame) != 0))
+    tc_cfi_endproc (last_fde);
 }
 
+static segT
+get_cfi_seg (segT cseg, const char *base, flagword flags, int align)
+{
+  if (SUPPORT_FRAME_LINKONCE || ((flags & SEC_DEBUGGING) == 0 && compact_eh))
+    {
+      struct dwcfi_seg_list *l;
+
+      l = dwcfi_hash_find_or_make (cseg, base, flags);
+
+      cseg = l->seg;
+      subseg_set (cseg, l->subseg);
+    }
+  else
+    {
+      cseg = subseg_new (base, 0);
+      bfd_set_section_flags (stdoutput, cseg, flags);
+    }
+  record_alignment (cseg, align);
+  return cseg;
+}
+
+#if SUPPORT_COMPACT_EH
+static void
+output_compact_unwind_data (struct fde_entry *fde, int align)
+{
+  int data_size = fde->eh_data_size + 2;
+  int amask;
+  char *p;
+  char *end;
+
+  fde->eh_loc = symbol_temp_new_now ();
+  if (fde->per_encoding != DW_EH_PE_omit)
+    data_size += 4;
+
+  amask = (1 << align) - 1;
+  data_size = (data_size + amask) & ~amask;
+  p = frag_more (data_size);
+  end = p + data_size - 1;
+  if (fde->per_encoding != DW_EH_PE_omit)
+    {
+      *(p++) = 0;
+      md_number_to_chars (p, 0, 4);
+      tc_cfi_fix_eh_ref (p, &fde->personality);
+      p += 4;
+    }
+  else
+    {
+      *(p++) = 1;
+    }
+
+  memcpy (p, fde->eh_data, fde->eh_data_size);
+  p += fde->eh_data_size;
+  while (p != end)
+    *(p++) = 0x5f;
+
+  *(p++) = 0x5c;
+  fde->eh_header_type = EH_COMPACT_OUTLINE_DONE;
+}
+
+static void
+dot_cfi_inline_lsda (int ignored ATTRIBUTE_UNUSED)
+{
+  segT cfi_seg, ccseg;
+  int align;
+  long max_alignment = 28;
+
+  if (!last_fde)
+    {
+      as_bad (_("unexpected .cfi_inline_lsda"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  if (!compact_eh
+      || (last_fde->sections & CFI_EMIT_eh_frame) == 0
+      || (last_fde->eh_header_type != EH_COMPACT_LEGACY
+	  && last_fde->eh_header_type != EH_COMPACT_HAS_LSDA))
+
+    {
+      as_bad (_(".cfi_inline_lsda not valid for this frame"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+#ifdef md_flush_pending_output
+  md_flush_pending_output ();
+#endif
+
+  align = get_absolute_expression ();
+  if (align > max_alignment)
+    {
+      align = max_alignment;
+      as_bad (_("Alignment too large: %d. assumed."), align);
+    }
+  else if (align < 0)
+    {
+      as_warn (_("Alignment negative: 0 assumed."));
+      align = 0;
+    }
+
+  demand_empty_rest_of_line ();
+  ccseg = CUR_SEG (last_fde);
+  /* Open .gnu_extab section.  */
+  cfi_seg = get_cfi_seg (ccseg, ".gnu_extab",
+			 (SEC_ALLOC | SEC_LOAD | SEC_DATA
+			  | DWARF2_EH_FRAME_READ_ONLY),
+			 1);
+#ifdef md_fix_up_eh_frame
+  md_fix_up_eh_frame (cfi_seg);
+#else
+  (void) cfi_seg;
+#endif
+
+  frag_align (align, 0, 0);
+  record_alignment (now_seg, align);
+  if (last_fde->eh_header_type == EH_COMPACT_HAS_LSDA)
+    output_compact_unwind_data (last_fde, align);
+
+  last_fde = NULL;
+
+  return;
+}
+#else /* !SUPPORT_COMPACT_EH */
+static void
+dot_cfi_inline_lsda (int ignored ATTRIBUTE_UNUSED)
+{
+  as_bad (_(".cfi_inline_lsda is not supported for this target"));
+  ignore_rest_of_line ();
+}
+#endif
 \f
 /* Emit a single byte into the current segment.  */
 
@@ -1479,7 +1647,11 @@ output_cie (struct cie_entry *cie, bfd_b
 	  offsetT size = encoding_size (cie->per_encoding);
 	  out_one (cie->per_encoding);
 	  exp = cie->personality;
-	  if ((cie->per_encoding & 0x70) == DW_EH_PE_pcrel)
+	  if (tc_cfi_special_encoding (cie->per_encoding))
+	    {
+	      tc_cfi_emit_expr (&exp, cie->per_encoding);
+	    }
+	  else if ((cie->per_encoding & 0x70) == DW_EH_PE_pcrel)
 	    {
 #if CFI_DIFF_EXPR_OK
 	      exp.X_op = O_subtract;
@@ -1516,6 +1688,11 @@ output_cie (struct cie_entry *cie, bfd_b
 #if CFI_DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr
   enc |= DW_EH_PE_pcrel;
 #endif
+#ifdef DWARF2_FDE_RELOC_ENCODING
+  /* Allow target to override encoding.  */
+  enc = DWARF2_FDE_RELOC_ENCODING (enc);
+#endif
+  cie->fde_encoding = enc;
   if (eh_frame)
     out_one (enc);
 
@@ -1578,21 +1755,31 @@ output_fde (struct fde_entry *fde, struc
 
   if (eh_frame)
     {
-      exp.X_op = O_subtract;
-      exp.X_add_number = 0;
+      if (tc_cfi_special_encoding (cie->fde_encoding))
+	{
+	  exp.X_op = O_symbol;
+	  exp.X_add_symbol = fde->start_address;
+	  tc_cfi_emit_expr (&exp, cie->fde_encoding);
+	}
+      else
+	{
+	  exp.X_op = O_subtract;
+	  exp.X_add_number = 0;
 #if CFI_DIFF_EXPR_OK
-      exp.X_add_symbol = fde->start_address;
-      exp.X_op_symbol = symbol_temp_new_now ();
-      emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code offset.  */
+	  exp.X_add_symbol = fde->start_address;
+	  exp.X_op_symbol = symbol_temp_new_now ();
+	  emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code offset.  */
 #else
-      exp.X_op = O_symbol;
-      exp.X_add_symbol = fde->start_address;
-#ifdef tc_cfi_emit_pcrel_expr
-      tc_cfi_emit_pcrel_expr (&exp, DWARF2_FDE_RELOC_SIZE);	 /* Code offset.  */
+	  exp.X_op = O_symbol;
+	  exp.X_add_symbol = fde->start_address;
+
+#if defined(tc_cfi_emit_pcrel_expr)
+	  tc_cfi_emit_pcrel_expr (&exp, DWARF2_FDE_RELOC_SIZE);	 /* Code offset.  */
 #else
-      emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code offset.  */
+	  emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code offset.  */
 #endif
 #endif
+	}
       addr_size = DWARF2_FDE_RELOC_SIZE;
     }
   else
@@ -1617,7 +1804,11 @@ output_fde (struct fde_entry *fde, struc
   if (fde->lsda_encoding != DW_EH_PE_omit)
     {
       exp = fde->lsda;
-      if ((fde->lsda_encoding & 0x70) == DW_EH_PE_pcrel)
+      if (tc_cfi_special_encoding (cie->lsda_encoding))
+	{
+	  tc_cfi_emit_expr (&exp, cie->lsda_encoding);
+	}
+      else if ((fde->lsda_encoding & 0x70) == DW_EH_PE_pcrel)
 	{
 #if CFI_DIFF_LSDA_OK
 	  exp.X_op = O_subtract;
@@ -1821,26 +2012,66 @@ cfi_change_reg_numbers (struct cfi_insn_
 #define cfi_change_reg_numbers(insn, cseg) do { } while (0)
 #endif
 
-static segT
-get_cfi_seg (segT cseg, const char *base, flagword flags, int align)
+#if SUPPORT_COMPACT_EH
+static void
+cfi_emit_eh_header (symbolS *sym, bfd_vma addend)
 {
-  if (SUPPORT_FRAME_LINKONCE)
+  expressionS exp;
+
+  exp.X_add_number = addend;
+  exp.X_add_symbol = sym;
+  if (tc_cfi_special_encoding (DW_EH_PE_sdata4 | DW_EH_PE_pcrel))
     {
-      struct dwcfi_seg_list *l;
+      tc_cfi_emit_expr (&exp, DW_EH_PE_sdata4 | DW_EH_PE_pcrel);
+    }
+  else
+    {
+#if CFI_DIFF_EXPR_OK
+      exp.X_op = O_subtract;
+      exp.X_op_symbol = symbol_temp_new_now ();
+      emit_expr (&exp, 4);	/* Code offset.  */
+#elif defined (tc_cfi_emit_pcrel_expr)
+      exp.X_op = O_symbol;
+      tc_cfi_emit_pcrel_expr (&exp, 4);	 /* Code offset.  */
+#else
+      abort();
+#endif
+    }
+}
 
-      l = dwcfi_hash_find_or_make (cseg, base, flags);
+static void
+output_eh_header (struct fde_entry *fde)
+{
+  char *p;
+  bfd_vma addend;
 
-      cseg = l->seg;
-      subseg_set (cseg, l->subseg);
+  if (fde->eh_header_type == EH_COMPACT_INLINE)
+    addend = 0;
+  else
+    addend = 1;
+
+  cfi_emit_eh_header (fde->start_address, addend);
+
+  if (fde->eh_header_type == EH_COMPACT_INLINE)
+    {
+      p = frag_more (4);
+      /* Inline entries always use PR1.  */
+      *(p++) = 1;
+      memcpy(p, fde->eh_data, 3);
     }
   else
     {
-      cseg = subseg_new (base, 0);
-      bfd_set_section_flags (stdoutput, cseg, flags);
+      if (fde->eh_header_type == EH_COMPACT_LEGACY)
+	addend = 1;
+      else if (fde->eh_header_type == EH_COMPACT_OUTLINE
+	       || fde->eh_header_type == EH_COMPACT_OUTLINE_DONE)
+	addend = 0;
+      else
+	abort ();
+      cfi_emit_eh_header (fde->eh_loc, addend);
     }
-  record_alignment (cseg, align);
-  return cseg;
 }
+#endif
 
 void
 cfi_finish (void)
@@ -1854,13 +2085,13 @@ cfi_finish (void)
   if (all_fde_data == 0)
     return;
 
-  if ((cfi_sections & CFI_EMIT_eh_frame) != 0)
+  if ((all_cfi_sections & CFI_EMIT_eh_frame) != 0)
     {
       /* Make sure check_eh_frame doesn't do anything with our output.  */
       save_flag_traditional_format = flag_traditional_format;
       flag_traditional_format = 1;
 
-      if (!SUPPORT_FRAME_LINKONCE)
+      if (!EH_FRAME_LINKONCE)
 	{
 	  /* Open .eh_frame section.  */
 	  cfi_seg = get_cfi_seg (NULL, ".eh_frame",
@@ -1888,7 +2119,17 @@ cfi_finish (void)
 
 	  for (fde = all_fde_data; fde ; fde = fde->next)
 	    {
-	      if (SUPPORT_FRAME_LINKONCE)
+	      if ((fde->sections & CFI_EMIT_eh_frame) == 0)
+		continue;
+
+#if SUPPORT_COMPACT_EH
+	      if (fde->eh_header_type == EH_COMPACT_HAS_LSDA)
+		fde->eh_header_type = EH_COMPACT_LEGACY;
+
+	      if (fde->eh_header_type != EH_COMPACT_LEGACY)
+		continue;
+#endif
+	      if (EH_FRAME_LINKONCE)
 		{
 		  if (HANDLED (fde))
 		    continue;
@@ -1922,20 +2163,111 @@ cfi_finish (void)
 		}
 
 	      cie = select_cie_for_fde (fde, TRUE, &first, 2);
+	      fde->eh_loc = symbol_temp_new_now ();
 	      output_fde (fde, cie, TRUE, first,
 			  fde->next == NULL ? EH_FRAME_ALIGNMENT : 2);
 	    }
 	}
-      while (SUPPORT_FRAME_LINKONCE && seek_next_seg == 2);
+      while (EH_FRAME_LINKONCE && seek_next_seg == 2);
 
-      if (SUPPORT_FRAME_LINKONCE)
+      if (EH_FRAME_LINKONCE)
 	for (fde = all_fde_data; fde ; fde = fde->next)
 	  SET_HANDLED (fde, 0);
 
+#if SUPPORT_COMPACT_EH
+      if (compact_eh)
+	{
+	  /* Create remaining out of line table entries.  */
+	  do
+	    {
+	      ccseg = NULL;
+	      seek_next_seg = 0;
+
+	      for (fde = all_fde_data; fde ; fde = fde->next)
+		{
+		  if ((fde->sections & CFI_EMIT_eh_frame) == 0)
+		    continue;
+
+		  if (fde->eh_header_type != EH_COMPACT_OUTLINE)
+		    continue;
+		  if (HANDLED (fde))
+		    continue;
+		  if (seek_next_seg && CUR_SEG (fde) != ccseg)
+		    {
+		      seek_next_seg = 2;
+		      continue;
+		    }
+		  if (!seek_next_seg)
+		    {
+		      ccseg = CUR_SEG (fde);
+		      /* Open .eh_frame section.  */
+		      cfi_seg = get_cfi_seg (ccseg, ".gnu_extab",
+					     (SEC_ALLOC | SEC_LOAD | SEC_DATA
+					      | DWARF2_EH_FRAME_READ_ONLY),
+					     1);
+#ifdef md_fix_up_eh_frame
+		      md_fix_up_eh_frame (cfi_seg);
+#else
+		      (void) cfi_seg;
+#endif
+		      seek_next_seg = 1;
+		    }
+		  SET_HANDLED (fde, 1);
+
+		  frag_align (1, 0, 0);
+		  record_alignment (now_seg, 1);
+		  output_compact_unwind_data (fde, 1);
+		}
+	    }
+	  while (EH_FRAME_LINKONCE && seek_next_seg == 2);
+
+	  for (fde = all_fde_data; fde ; fde = fde->next)
+	    SET_HANDLED (fde, 0);
+
+	  /* Create index table fragments.  */
+	  do
+	    {
+	      ccseg = NULL;
+	      seek_next_seg = 0;
+
+	      for (fde = all_fde_data; fde ; fde = fde->next)
+		{
+		  if ((fde->sections & CFI_EMIT_eh_frame) == 0)
+		    continue;
+
+		  if (HANDLED (fde))
+		    continue;
+		  if (seek_next_seg && CUR_SEG (fde) != ccseg)
+		    {
+		      seek_next_seg = 2;
+		      continue;
+		    }
+		  if (!seek_next_seg)
+		    {
+		      ccseg = CUR_SEG (fde);
+		      /* Open .eh_frame_entry section.  */
+		      cfi_seg = get_cfi_seg (ccseg, ".eh_frame_entry",
+					     (SEC_ALLOC | SEC_LOAD | SEC_DATA
+					      | DWARF2_EH_FRAME_READ_ONLY),
+					     2);
+		      seek_next_seg = 1;
+		    }
+		  SET_HANDLED (fde, 1);
+
+		  output_eh_header (fde);
+		}
+	    }
+	  while (seek_next_seg == 2);
+
+	  for (fde = all_fde_data; fde ; fde = fde->next)
+	    SET_HANDLED (fde, 0);
+	}
+#endif /* SUPPORT_COMPACT_EH */
+
       flag_traditional_format = save_flag_traditional_format;
     }
 
-  if ((cfi_sections & CFI_EMIT_debug_frame) != 0)
+  if ((all_cfi_sections & CFI_EMIT_debug_frame) != 0)
     {
       int alignment = ffs (DWARF2_ADDR_SIZE (stdoutput)) - 1;
 
@@ -1958,6 +2290,9 @@ cfi_finish (void)
 
 	  for (fde = all_fde_data; fde ; fde = fde->next)
 	    {
+	      if ((fde->sections & CFI_EMIT_debug_frame) == 0)
+		continue;
+
 	      if (SUPPORT_FRAME_LINKONCE)
 		{
 		  if (HANDLED (fde))
@@ -2034,6 +2369,7 @@ const pseudo_typeS cfi_pseudo_table[] =
     { "cfi_personality", dot_cfi_dummy, 0 },
     { "cfi_lsda", dot_cfi_dummy, 0 },
     { "cfi_val_encoded_addr", dot_cfi_dummy, 0 },
+    { "cfi_inline_lsda", dot_cfi_dummy, 0 },
     { NULL, NULL, 0 }
   };
 
Index: dw2gencfi.h
===================================================================
RCS file: /cvs/src/src/gas/dw2gencfi.h,v
retrieving revision 1.10
diff -p -u -r1.10 dw2gencfi.h
--- dw2gencfi.h	7 Aug 2011 16:32:20 -0000	1.10
+++ dw2gencfi.h	31 May 2013 17:24:20 -0000
@@ -57,10 +57,18 @@ extern void cfi_add_CFA_restore_state (v
 #define SUPPORT_FRAME_LINKONCE 0
 #endif
 
+#ifdef tc_cfi_fix_eh_ref
+#define SUPPORT_COMPACT_EH 1
+#else
+#define SUPPORT_COMPACT_EH 0
+#endif
+
+#define MULTIPLE_FRAME_SECTIONS (SUPPORT_FRAME_LINKONCE || SUPPORT_COMPACT_EH)
+
 struct cfi_insn_data
 {
   struct cfi_insn_data *next;
-#if SUPPORT_FRAME_LINKONCE
+#if MULTIPLE_FRAME_SECTIONS
   segT cur_seg;
 #endif
   int insn;
@@ -97,10 +105,19 @@ struct cfi_insn_data
   } u;
 };
 
+enum {
+    EH_COMPACT_LEGACY,
+    EH_COMPACT_INLINE,
+    EH_COMPACT_OUTLINE,
+    EH_COMPACT_OUTLINE_DONE,
+    /* Outline if .cfi_inline_lsda used, otherwise legacy FDE.  */
+    EH_COMPACT_HAS_LSDA
+};
+
 struct fde_entry
 {
   struct fde_entry *next;
-#if SUPPORT_FRAME_LINKONCE
+#if MULTIPLE_FRAME_SECTIONS
   segT cur_seg;
 #endif
   symbolS *start_address;
@@ -113,9 +130,16 @@ struct fde_entry
   expressionS lsda;
   unsigned int return_column;
   unsigned int signal_frame;
-#if SUPPORT_FRAME_LINKONCE
+#if MULTIPLE_FRAME_SECTIONS
   int handled;
 #endif
+  int eh_header_type;
+  /* Compact unwinding opcodes, not including the PR byte or LSDA.  */
+  int eh_data_size;
+  bfd_byte *eh_data;
+  /* For out of line tables and FDEs.  */
+  symbolS *eh_loc;
+  int sections;
 };
 
 /* The list of all FDEs that have been collected.  */

[-- Attachment #4: gas-mips.cl --]
[-- Type: application/octet-stream, Size: 394 bytes --]

	* config/tc-mips.c (md_apply_fix): Handle BFD_RELOC_NONE.
	(mips_make_debug_seg): New function.
	(mips_unwind_reg_from_dwarf, mips_cfi_endproc, mips_cfi_fix_eh_ref,
	mips_cfi_emit_expr): New functions.
	* config/tc-mips.h (DWARF2_FDE_RELOC_SIZE): Use compact_eh.
	(DWARF2_FDE_RELOC_ENCODING): Define.
	(tc_make_debug_seg, tc_cfi_endproc, tc_cfi_emit_expr,
	tc_cfi_fix_eh_ref): Define.

[-- Attachment #5: gas-mips.patch --]
[-- Type: application/octet-stream, Size: 8841 bytes --]

Index: config/tc-mips.c
===================================================================
RCS file: /cvs/src/src/gas/config/tc-mips.c,v
retrieving revision 1.536
diff -p -u -r1.536 tc-mips.c
--- config/tc-mips.c	31 May 2013 17:04:52 -0000	1.536
+++ config/tc-mips.c	31 May 2013 17:24:20 -0000
@@ -15712,7 +15712,8 @@ md_apply_fix (fixS *fixP, valueT *valP, 
 	      || fixP->fx_r_type == BFD_RELOC_MICROMIPS_SUB
 	      || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
 	      || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY
-	      || fixP->fx_r_type == BFD_RELOC_MIPS_TLS_DTPREL64);
+	      || fixP->fx_r_type == BFD_RELOC_MIPS_TLS_DTPREL64
+	      || fixP->fx_r_type == BFD_RELOC_NONE);
 
   buf = fixP->fx_frag->fr_literal + fixP->fx_where;
 
@@ -15951,6 +15952,7 @@ md_apply_fix (fixS *fixP, valueT *valP, 
         S_SET_WEAK (fixP->fx_addsy);
       break;
 
+    case BFD_RELOC_NONE:
     case BFD_RELOC_VTABLE_ENTRY:
       fixP->fx_done = 0;
       break;
@@ -19793,3 +19795,289 @@ tc_mips_regname_to_dw2regnum (char *regn
 
   return regnum;
 }
+
+#if defined (OBJ_ELF)
+segT
+mips_make_debug_seg (segT cseg, char *name)
+{
+  const char *group_name = NULL;
+  int linkonce;
+  int flags = 0;
+
+  if (cseg && (cseg->flags & SEC_LINK_ONCE) != 0)
+    {
+      group_name = elf_group_name (cseg);
+      if (group_name == NULL)
+	{
+	  as_bad (_("Group section `%s' has no group signature"),
+		  segment_name (cseg));
+	}
+      flags |= SHF_GROUP;
+      linkonce = 1;
+    }
+  obj_elf_change_section (name, SHT_PROGBITS, flags, 0, group_name, linkonce, 0);
+  return now_seg;
+}
+
+/* TODO: Can also save float regs.  */
+#define MIPS_NUM_UNWIND_REGS 32
+
+static int
+mips_unwind_reg_from_dwarf (int reg)
+{
+  if (reg > MIPS_NUM_UNWIND_REGS)
+    return -1;
+
+  return reg;
+}
+
+/* Attempt to generate compact frame unwind encodings.  */
+
+void
+mips_cfi_endproc (struct fde_entry *fde ATTRIBUTE_UNUSED)
+{
+  struct cfi_insn_data *insn;
+  int reg;
+  offsetT cfa_offset = 0;
+  offsetT next_offset = 0;
+  bfd_boolean reg_offset[MIPS_NUM_UNWIND_REGS];
+  int cfa_reg = 29;
+  bfd_byte opbuff[40];
+  int num_ops;
+  int i;
+  bfd_byte op;
+  offsetT stack_align = HAVE_NEWABI ? 16 : 8;
+  offsetT gpr_size = HAVE_64BIT_GPRS ? 8 : 4;
+
+  for (reg = 0; reg < MIPS_NUM_UNWIND_REGS; reg++)
+    reg_offset[reg] = 0;
+
+  /* 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:
+	  cfa_reg = insn->u.ri.reg;
+	  cfa_offset = insn->u.ri.offset;
+	  break;
+
+	case DW_CFA_def_cfa_register:
+	  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 = mips_unwind_reg_from_dwarf (insn->u.r);
+	  if (reg >= 0)
+	    reg_offset[reg] = 0;
+	  break;
+
+	case DW_CFA_offset:
+	  reg = mips_unwind_reg_from_dwarf (insn->u.ri.reg);
+	  if (reg < 0)
+	    return;
+	  if (insn->u.ri.offset >= 0)
+	    return;
+	  reg_offset[reg] = insn->u.ri.offset;
+	  break;
+
+	default:
+	  abort ();
+	}
+    }
+
+  /* Frame pointer must be callee caved.  */
+  if (cfa_reg < 16
+      || (cfa_reg > 23 && cfa_reg < 29)
+      || cfa_reg > 30)
+    return;
+
+  /* Can only increment stack pointer.  */
+  if (cfa_offset < 0)
+    return;
+
+  /* Stack must be aligned.  */
+  if ((cfa_offset & (stack_align - 1)) != 0)
+    return;
+
+  /* Registers must be contiguous.  There's only limited benefit in
+     supprting more complex layouts, so For now we also require they
+     be in order.  */
+  next_offset = -gpr_size;
+  for (i = MIPS_NUM_UNWIND_REGS - 1; i >= 0; i--)
+    {
+      if (reg_offset[i] == 0)
+	continue;
+
+      if (reg_offset[i] != next_offset)
+	return;
+
+      next_offset -= gpr_size;
+    }
+
+  num_ops = 0;
+  if (cfa_reg != 29)
+    opbuff[num_ops++] = 0x50 + cfa_reg - 16;
+
+  cfa_offset /= stack_align;
+  if (cfa_offset && cfa_offset <= 129)
+    {
+      while (cfa_offset > 64)
+	{
+	  opbuff[num_ops++] = 0x3f;
+	  cfa_offset -= 64;
+	}
+      opbuff[num_ops++] = cfa_offset - 1;
+    }
+  else if (cfa_offset > 0)
+    {
+      opbuff[num_ops++] = 0x58;
+      cfa_offset -= 129;
+      while (cfa_offset > 0x7f)
+	{
+	  opbuff[num_ops++] = (cfa_offset & 0x7f) | 0x80;
+	  cfa_offset >>= 7;
+	}
+      opbuff[num_ops++] = cfa_offset;
+    }
+
+  op = 0;
+  /* r16[-r23], [r30], r31 */
+  if (reg_offset[31])
+    {
+      if (reg_offset[30])
+	op = 0x48;
+      else
+	op = 0x40;
+
+      for (i = 29; i > 23; i--)
+	{
+	  if (reg_offset[i])
+	    op = 0;
+	}
+
+      while (i > 16 && reg_offset[i] == 0)
+	i--;
+
+      reg = i;
+      for (i = 16; i <= reg; i++)
+	if (reg_offset[i] == 0)
+	  op = 0;
+
+      if (op)
+	op |= (reg - 16);
+    }
+
+  if (!op)
+    {
+      /* mips16 save multiple.  */
+      op = 0x6c;
+      if (reg_offset[16] == 0 || reg_offset[17] == 0
+	  || reg_offset[21] == 0 || reg_offset[22] == 0
+	  || reg_offset[23] == 0 || reg_offset[31] == 0)
+	op = 0;
+
+      for (i = 24; i < 31; i++)
+	if (reg_offset[i] != 0)
+	  op = 0;
+
+      if (op && reg_offset[18] == 0)
+	op++;
+      if (reg_offset[19] == 0)
+	{
+	  if (op == 0x6d)
+	    op++;
+	  else
+	    op = 0;
+	}
+      if (reg_offset[20] == 0)
+	{
+	  if (op == 0x6e)
+	    op++;
+	  else
+	    op = 0;
+	}
+    }
+
+  if (op)
+    {
+      opbuff[num_ops++] = op;
+      reg = 15;
+    }
+  else
+    {
+      reg = 31;
+    }
+  /* Arbitrary blocks of registers.  */
+  i = 0;
+  while (reg >= 0)
+    {
+      if (reg_offset[reg] == 0)
+	{
+	  if (i)
+	    {
+	      opbuff[num_ops++] = 0x59;
+	      opbuff[num_ops++] = ((reg + 1) << 3) | (i - 1);
+	      i = 0;
+	    }
+	}
+      else
+	i++;
+      reg--;
+    }
+
+  if (i)
+    {
+      opbuff[num_ops++] = 0x59;
+      opbuff[num_ops++] = i - 1;
+    }
+
+  if (fde->lsda_encoding != DW_EH_PE_omit)
+    fde->eh_header_type = EH_COMPACT_HAS_LSDA;
+  else if (num_ops <= 3 && fde->per_encoding == DW_EH_PE_omit)
+    fde->eh_header_type = EH_COMPACT_INLINE;
+  else
+    fde->eh_header_type = EH_COMPACT_OUTLINE;
+
+  if (fde->eh_header_type == EH_COMPACT_INLINE)
+    {
+      while (num_ops < 3)
+	opbuff[num_ops++] = 0x5c;
+    }
+
+  fde->eh_data_size = num_ops;
+  fde->eh_data = (bfd_byte *) xmalloc (num_ops);
+  memcpy (fde->eh_data, opbuff, num_ops);
+}
+
+void
+mips_cfi_fix_eh_ref (char *where, expressionS *exp)
+{
+  fix_new (frag_now, where - frag_now->fr_literal, 4, exp->X_add_symbol,
+	   exp->X_add_number, FALSE, BFD_RELOC_MIPS_EH);
+}
+
+void
+mips_cfi_emit_expr (expressionS *exp, int encoding)
+{
+  char *p;
+
+  p = frag_more(4);
+  md_number_to_chars (p, 0, 4);
+  if ((encoding & 0x70) == DW_EH_PE_datarel)
+    mips_cfi_fix_eh_ref (p, exp);
+  else
+    {
+      fix_new (frag_now, p - frag_now->fr_literal, 4, exp->X_add_symbol,
+	       exp->X_add_number, TRUE, BFD_RELOC_32_PCREL);
+    }
+}
+#endif
Index: config/tc-mips.h
===================================================================
RCS file: /cvs/src/src/gas/config/tc-mips.h,v
retrieving revision 1.59
diff -p -u -r1.59 tc-mips.h
--- config/tc-mips.h	23 Sep 2012 11:14:25 -0000	1.59
+++ config/tc-mips.h	31 May 2013 17:24:20 -0000
@@ -177,7 +177,9 @@ extern enum dwarf2_format mips_dwarf2_fo
 
 extern int mips_dwarf2_addr_size (void);
 #define DWARF2_ADDR_SIZE(bfd) mips_dwarf2_addr_size ()
-#define DWARF2_FDE_RELOC_SIZE mips_dwarf2_addr_size ()
+#define DWARF2_FDE_RELOC_SIZE (compact_eh ? 4 : mips_dwarf2_addr_size ())
+#define DWARF2_FDE_RELOC_ENCODING(enc) \
+  (enc | (compact_eh ? DW_EH_PE_pcrel : 0))
 
 #define TARGET_USE_CFIPOP 1
 
@@ -190,4 +192,24 @@ extern int tc_mips_regname_to_dw2regnum 
 #define DWARF2_DEFAULT_RETURN_COLUMN 31
 #define DWARF2_CIE_DATA_ALIGNMENT (-4)
 
+#if defined (OBJ_ELF)
+
+#define tc_make_debug_seg mips_make_debug_seg
+segT mips_make_debug_seg (segT cseg, char *name);
+
+#define tc_cfi_endproc mips_cfi_endproc
+struct fde_entry;
+void mips_cfi_endproc (struct fde_entry *fde);
+
+#define tc_cfi_emit_expr mips_cfi_emit_expr
+void mips_cfi_emit_expr (expressionS *exp, int encoding);
+#define tc_cfi_special_encoding(e) \
+  ((e) == (DW_EH_PE_sdata4 | DW_EH_PE_datarel | DW_EH_PE_indirect) \
+   || (e) == (DW_EH_PE_sdata4 | DW_EH_PE_pcrel))
+
+#define tc_cfi_fix_eh_ref mips_cfi_fix_eh_ref
+void mips_cfi_fix_eh_ref (char *where, expressionS *exp);
+
+#endif /* OBJ_ELF */
+
 #endif /* TC_MIPS */

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Patch] Gas support for MIPS Compact EH
  2013-05-31 18:56 [Patch] Gas support for MIPS Compact EH Moore, Catherine
@ 2013-06-01 11:07 ` Richard Sandiford
  2014-02-04 21:56   ` Moore, Catherine
  0 siblings, 1 reply; 31+ messages in thread
From: Richard Sandiford @ 2013-06-01 11:07 UTC (permalink / raw)
  To: Moore, Catherine; +Cc: binutils

"Moore, Catherine" <Catherine_Moore@mentor.com> writes:
> Index: doc/as.texinfo
> ===================================================================
> RCS file: /cvs/src/src/gas/doc/as.texinfo,v
> retrieving revision 1.266
> diff -p -u -r1.266 as.texinfo
> --- doc/as.texinfo	29 Apr 2013 13:38:58 -0000	1.266
> +++ doc/as.texinfo	31 May 2013 17:24:20 -0000
> @@ -4479,6 +4479,9 @@ if @var{section_list} is @code{.debug_fr
>  To emit both use @code{.eh_frame, .debug_frame}.  The default if this
>  directive is not used is @code{.cfi_sections .eh_frame}.
>  
> +On targets that support compact unwinding tables these can be generated
> +by specifying @code{.eh_frame_header} instead of @code{.eh_frame}.

Should this be .eh_frame_entry instead of .eh_frame_header?

> @@ -4514,6 +4517,23 @@ argument is not present, otherwise secon
>  or a symbol name.  The default after @code{.cfi_startproc} is @code{.cfi_lsda 0xff},
>  no LSDA.
>  
> +@section @code{.cfi_inline_lsda} [@var{align}]
> +@code{.cfi_inline_lsda} marks the start of a LSDA data section and
> +switches to the corresponding @code{.gnu.extab} section.
> +It must be preceded by a CFI block containing a @code{.cfi_lsda} directive and
> +is only valid when generating compact EH frames (i.e.
> +with @code{.cfi_sections eh_frame_entry}.
> +
> +If a compact encoding is being used then the table header and unwinding
> +opcodes will be generated at this point, so that they are immediately
> +followed by the LSDA data.  The symbol referenced by the @code{.cfi_lsda}
> +directive should still be defined in case a fallback FDE based encoding
> +is used.
> +
> +The optional @var{align} argument specifies the alignment required.
> +The alignment is specified as a power of two, as with the
> +@code{.p2align} directive.

Hmm, switching sections and emitting data feels very different in style
from the other .cfi directives, which just annotate code without changing
the flow of assembly.  I'd like to know other people's thoughts on this.

Also, how do you terminate the LSDA?  The documentation doesn't say
(but should :-)) and I couldn't see this directive in the spec either.

"If a compact encoding is being used" seems redundant, since it comes
just after the bit saying "only valid when generating compact EH frames".

There need to be tests for all of this.  TBH, without tests, and without
an explanation of what the code is doing, I found this patch pretty hard
to review.  E.g.:

> @@ -129,7 +140,12 @@ get_debugseg_name (segT seg, const char 
>        dot = strchr (name + 1, '.');
>  
>        if (!dollar && !dot)
> -	name = "";
> +	{
> +	  if (compact_eh && strcmp (name, ".text") != 0)
> +	    return concat (base_name, ".", name, NULL);
> +
> +	  name = "";
> +	}

why is this change needed?  I.e., for a text section called something
like .foobar, why does compact_eh need to put things in a section name
ending in "..foobar", rather than in the main EH section?  I assume the
double dots are deliberate, e.g. to avoid confusion with ".text.foobar"?

> @@ -161,6 +177,9 @@ alloc_debugseg_item (segT seg, int subse
>  static segT
>  is_now_linkonce_segment (void)
>  {
> +  if (compact_eh)
> +    return now_seg;
> +
>    if ((bfd_get_section_flags (stdoutput, now_seg)
>         & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD
>  	  | SEC_LINK_DUPLICATES_ONE_ONLY | SEC_LINK_DUPLICATES_SAME_SIZE

Why is this change needed?

> @@ -180,7 +199,11 @@ make_debug_seg (segT cseg, char *name, i
>    segT r;
>    flagword flags;
>  
> +#ifdef tc_make_debug_seg
> +  r = tc_make_debug_seg (cseg, name);
> +#else
>    r = subseg_new (name, 0);
> +#endif

Why is this change needed?  And what does the new hook do?  It should
be documented in internals.texi.

Do we really want to change the behaviour for traditional DWARF EH too?

> @@ -833,14 +859,15 @@ dot_cfi_personality (int ignored ATTRIBU
>      }
>  
>    if ((encoding & 0xff) != encoding
> -      || ((encoding & 0x70) != 0
> +      || ((((encoding & 0x70) != 0
>  #if CFI_DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr
> -	  && (encoding & 0x70) != DW_EH_PE_pcrel
> +	   && (encoding & 0x70) != DW_EH_PE_pcrel
>  #endif
>  	  )
>  	 /* leb128 can be handled, but does something actually need it?  */
> -      || (encoding & 7) == DW_EH_PE_uleb128
> -      || (encoding & 7) > DW_EH_PE_udata8)
> +	   || (encoding & 7) == DW_EH_PE_uleb128
> +	   || (encoding & 7) > DW_EH_PE_udata8)
> +	&& !tc_cfi_special_encoding (encoding)))
>      {
>        as_bad (_("invalid or unsupported encoding in .cfi_personality"));
>        ignore_rest_of_line ();

What does a "special" encoding mean?  Again, this hook should be documented
in internals.texi.  And do we really want to change the set of encodings
that are allowed for DWARF, even on MIPS systems that predate compat EH?

> @@ -1042,6 +1072,13 @@ dot_cfi_sections (int ignored ATTRIBUTE_
>  	  sections |= CFI_EMIT_eh_frame;
>  	else if (strncmp (name, ".debug_frame", sizeof ".debug_frame") == 0)
>  	  sections |= CFI_EMIT_debug_frame;
> +#if SUPPORT_COMPACT_EH
> +	else if (strncmp (name, ".eh_frame_entry", sizeof ".eh_frame_entry") == 0)
> +	  {
> +	    compact_eh = TRUE;
> +	    sections |= CFI_EMIT_eh_frame | CFI_EMIT_target;
> +	  }
> +#endif
>  #ifdef tc_cfi_section_name
>  	else if (strcmp (name, tc_cfi_section_name) == 0)
>  	  sections |= CFI_EMIT_target;

I think there needs to be more error checking here.  Do we support
mixtures of DWARF and new-format EH info in the same .s file?
If not, we should check for it, including making sure that later
.cfi_sections don't contradict earlier ones.  (At the moment,
the last one wins, except for the CFI_EMIT_target handling.)
If we do support mixtures, shouldn't something clear compat_eh,
since compat_eh is checked in parsing contexts?

Why is the CFI_EMIT_target needed?

> @@ -1125,16 +1162,147 @@ dot_cfi_endproc (int ignored ATTRIBUTE_U
>        return;
>      }
>  
> -  fde = frchain_now->frch_cfi_data->cur_fde_data;
> +  last_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);
> +  if ((cfi_sections & CFI_EMIT_target) != 0
> +      || (compact_eh && (cfi_sections & CFI_EMIT_eh_frame) != 0))
> +    tc_cfi_endproc (last_fde);
>  }

I think it'd be better to use a separate hook, especially if the idea
is that other targets could use compact EH info.  Targets might then
need a "legacy" tc_cfi_endproc as well as a "new" hook.

> +#if SUPPORT_COMPACT_EH
> +static void
> +output_compact_unwind_data (struct fde_entry *fde, int align)
> +{
> +  int data_size = fde->eh_data_size + 2;
> +  int amask;
> +  char *p;
> +  char *end;
> +
> +  fde->eh_loc = symbol_temp_new_now ();
> +  if (fde->per_encoding != DW_EH_PE_omit)
> +    data_size += 4;

This 4 shouldn't be hardcoded.  Hopefully an unconditional:

  data_size += encoding_size (fde->per_encoding);

would work; if not, please extend encoding_size.  We then have:

> +  if (fde->per_encoding != DW_EH_PE_omit)
> +    {
> +      *(p++) = 0;
> +      md_number_to_chars (p, 0, 4);
> +      tc_cfi_fix_eh_ref (p, &fde->personality);
> +      p += 4;

where tc_cfi_fix_eh_ref emits an R_MIPS_EH relocation regardless of
which personality encoding is used.  AIUI, the encoding must be one
of the "special" ones allowed by tc_cfi_special_encoding, so we should
check for that.

The tc_cfi_fix_eh_ref and tc_cfi_emit_expr hooks don't seem very consistent;
the former relies on the caller to clear the bytes, whereas the latter
is supposed to do it itself.

Neither of the hooks really seem to be doing anything target-specific
except for the all-important job of picking a reloc.  I think it would
be cleaner to have a hook that returns the reloc instead.
In the case of tc_cfi_emit_expr, this could be done by making
tc_cfi_special_encoding return the reloc for an encoding,
or BFD_RELOC_NONE for unsupported encodings.

Also, this is more of a design point, but I find the handling of the
personality encoding and R_MIPS_EH handling a bit confusing.  To quote:

---------------------------------------------------------------------
11 Relocations

A new static relocation, R_MIPS_EH, is defined. The semantics of this
relocation depend on whether static or dynamic linking is provided.

A GNU extension using relocation number 249 shall be used. The
relocation address need not be naturally aligned.

11.1 Static code

For static code generation, the calculation is the same as an
R_MIPS_REL32 relocation.

At runtime, the following expression provides the relocated value, if
'ptr' points to the relocation location.

• (ptrdiff_t)ptr + *(int32_t __attribute__((packed))*)ptr

[...]

11.2 PIC code

For PIC code generation, a 32- or 64-bit GOT-table entry must be
allocated to refer to the (dynamically resolved) target address. Once
the GOT entry has been allocated, the static calculation is as for an
R_MIPS_GPREL32 relocation (except that the symbol is externally
visible). The GOT- slot has an associated R_MIPS_{32,64} dynamic
relocation emitted – and that will of course be at a naturally aligned
location

At runtime, the following expression provides the relocated value, if
'ptr' points to the relocation location, and 'gp' is the global pointer
value:

• *(ptrdiff_t *)((ptrdiff_t)gp + *(int32_t __attribute__((packed)) *)ptr)

[...]
---------------------------------------------------------------------

So as I understand the first sentence, it's actually the linker that
decides whether R_MIPS_EH relocations act as direct PC-relative
references (11.1) or as indirect datarel references (11.2).
It's therefore the linker that decides what DWARF encoding
R_MIPS_EH fields use.  The linker then records that choice in the
.eh_frame_hdr.  Is that right?

If so, the assembler seems to be associating R_MIPS_EH with specific DWARF
encodings, even though the interpretation of R_MIPS_EH isn't known at
that stage.  I.e. the code reads:

#define tc_cfi_special_encoding(e) \
  ((e) == (DW_EH_PE_sdata4 | DW_EH_PE_datarel | DW_EH_PE_indirect) \
   || (e) == (DW_EH_PE_sdata4 | DW_EH_PE_pcrel))

void
mips_cfi_emit_expr (expressionS *exp, int encoding)
{
  char *p;

  p = frag_more(4);
  md_number_to_chars (p, 0, 4);
  if ((encoding & 0x70) == DW_EH_PE_datarel)
    mips_cfi_fix_eh_ref (p, exp);
  else
    {
      fix_new (frag_now, p - frag_now->fr_literal, 4, exp->X_add_symbol,
	       exp->X_add_number, TRUE, BFD_RELOC_32_PCREL);
    }
}

where mips_cfi_fix_eh_ref emits an R_MIPS_EH.  So the code appears to
be using R_MIPS_EH for the DWARF encoding:

    DW_EH_PE_sdata4 | DW_EH_PE_datarel | DW_EH_PE_indirect

even though there's no guarantee that the final value will be either
datarel or indirect.

Or, to put it another way, why are we making the choice between
R_MIPS_PC32 and R_MIPS_EH at assembly time in mips_cfi_emit_expr,
but not in mips_cfi_fix_eh_ref, even though the patch appears to
allow the same two encodings in both casees?

> +  memcpy (p, fde->eh_data, fde->eh_data_size);
> +  p += fde->eh_data_size;
> +  while (p != end)
> +    *(p++) = 0x5f;
> +
> +  *(p++) = 0x5c;

Please use something more mnemonic than 0x5f and 0x5c.  Are these
the "Spare" and "PC = VR[31]; Finish" encodings from page 16?
If so, they seem target-specific, so should probably use tc_* #defines.

> +static void
> +dot_cfi_inline_lsda (int ignored ATTRIBUTE_UNUSED)
> +{
> +  segT cfi_seg, ccseg;
> +  int align;
> +  long max_alignment = 28;
> +
> +  if (!last_fde)
> +    {
> +      as_bad (_("unexpected .cfi_inline_lsda"));
> +      ignore_rest_of_line ();
> +      return;
> +    }
> +
> +  if (!compact_eh
> +      || (last_fde->sections & CFI_EMIT_eh_frame) == 0
> +      || (last_fde->eh_header_type != EH_COMPACT_LEGACY
> +	  && last_fde->eh_header_type != EH_COMPACT_HAS_LSDA))
> +
> +    {
> +      as_bad (_(".cfi_inline_lsda not valid for this frame"));
> +      ignore_rest_of_line ();
> +      return;
> +    }

Excess blank line.  The first two lines of the if are clear enough,
but what is the eh_header_type condition doing?  It looks like one of the
cases it catches is where two LSDAs are specified for the same frame.
If so, I think a better error message should be given in that case.

> +  demand_empty_rest_of_line ();
> +  ccseg = CUR_SEG (last_fde);
> +  /* Open .gnu_extab section.  */
> +  cfi_seg = get_cfi_seg (ccseg, ".gnu_extab",
> +			 (SEC_ALLOC | SEC_LOAD | SEC_DATA
> +			  | DWARF2_EH_FRAME_READ_ONLY),
> +			 1);
> +#ifdef md_fix_up_eh_frame
> +  md_fix_up_eh_frame (cfi_seg);
> +#else
> +  (void) cfi_seg;
> +#endif
> +
> +  frag_align (align, 0, 0);
> +  record_alignment (now_seg, align);
> +  if (last_fde->eh_header_type == EH_COMPACT_HAS_LSDA)
> +    output_compact_unwind_data (last_fde, align);

Please could you explain the EH_COMPACT_LEGACY handling here?

> @@ -1479,7 +1647,11 @@ output_cie (struct cie_entry *cie, bfd_b
>  	  offsetT size = encoding_size (cie->per_encoding);
>  	  out_one (cie->per_encoding);
>  	  exp = cie->personality;
> -	  if ((cie->per_encoding & 0x70) == DW_EH_PE_pcrel)
> +	  if (tc_cfi_special_encoding (cie->per_encoding))
> +	    {
> +	      tc_cfi_emit_expr (&exp, cie->per_encoding);
> +	    }

Excess braces.

> @@ -1617,7 +1804,11 @@ output_fde (struct fde_entry *fde, struc
>    if (fde->lsda_encoding != DW_EH_PE_omit)
>      {
>        exp = fde->lsda;
> -      if ((fde->lsda_encoding & 0x70) == DW_EH_PE_pcrel)
> +      if (tc_cfi_special_encoding (cie->lsda_encoding))
> +	{
> +	  tc_cfi_emit_expr (&exp, cie->lsda_encoding);
> +	}

Same here.

> +#if SUPPORT_COMPACT_EH
> +static void
> +cfi_emit_eh_header (symbolS *sym, bfd_vma addend)
>  {
> -  if (SUPPORT_FRAME_LINKONCE)
> +  expressionS exp;
> +
> +  exp.X_add_number = addend;
> +  exp.X_add_symbol = sym;
> +  if (tc_cfi_special_encoding (DW_EH_PE_sdata4 | DW_EH_PE_pcrel))
>      {
> -      struct dwcfi_seg_list *l;
> +      tc_cfi_emit_expr (&exp, DW_EH_PE_sdata4 | DW_EH_PE_pcrel);
> +    }

Here too.

> +		      /* Open .eh_frame section.  */
> +		      cfi_seg = get_cfi_seg (ccseg, ".gnu_extab",
> +					     (SEC_ALLOC | SEC_LOAD | SEC_DATA
> +					      | DWARF2_EH_FRAME_READ_ONLY),
> +					     1);

Pasto, this isn't .eh_frame.

> @@ -15712,7 +15712,8 @@ md_apply_fix (fixS *fixP, valueT *valP, 
>  	      || fixP->fx_r_type == BFD_RELOC_MICROMIPS_SUB
>  	      || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
>  	      || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY
> -	      || fixP->fx_r_type == BFD_RELOC_MIPS_TLS_DTPREL64);
> +	      || fixP->fx_r_type == BFD_RELOC_MIPS_TLS_DTPREL64
> +	      || fixP->fx_r_type == BFD_RELOC_NONE);
>  
>    buf = fixP->fx_frag->fr_literal + fixP->fx_where;
>  
> @@ -15951,6 +15952,7 @@ md_apply_fix (fixS *fixP, valueT *valP, 
>          S_SET_WEAK (fixP->fx_addsy);
>        break;
>  
> +    case BFD_RELOC_NONE:
>      case BFD_RELOC_VTABLE_ENTRY:
>        fixP->fx_done = 0;
>        break;

Why are these two hunks needed?

> @@ -19793,3 +19795,289 @@ tc_mips_regname_to_dw2regnum (char *regn
>  
>    return regnum;
>  }
> +
> +#if defined (OBJ_ELF)
> +segT
> +mips_make_debug_seg (segT cseg, char *name)
> +{
> +  const char *group_name = NULL;
> +  int linkonce;
> +  int flags = 0;
> +
> +  if (cseg && (cseg->flags & SEC_LINK_ONCE) != 0)
> +    {
> +      group_name = elf_group_name (cseg);
> +      if (group_name == NULL)
> +	{
> +	  as_bad (_("Group section `%s' has no group signature"),
> +		  segment_name (cseg));
> +	}
> +      flags |= SHF_GROUP;
> +      linkonce = 1;
> +    }
> +  obj_elf_change_section (name, SHT_PROGBITS, flags, 0, group_name, linkonce, 0);
> +  return now_seg;
> +}
> +

This function doesn't seem to contain any MIPS-specific code.
If a hook really is needed, then I think this implementation belongs
in obj-elf.c rather than tc-mips.c.

> +/* Attempt to generate compact frame unwind encodings.  */
> +
> +void
> +mips_cfi_endproc (struct fde_entry *fde ATTRIBUTE_UNUSED)
> +{
> +  struct cfi_insn_data *insn;
> +  int reg;
> +  offsetT cfa_offset = 0;
> +  offsetT next_offset = 0;
> +  bfd_boolean reg_offset[MIPS_NUM_UNWIND_REGS];
> +  int cfa_reg = 29;
> +  bfd_byte opbuff[40];

Why 40?  As a maths exam might say, please show your working :-)

> +  /* Frame pointer must be callee caved.  */
> +  if (cfa_reg < 16
> +      || (cfa_reg > 23 && cfa_reg < 29)
> +      || cfa_reg > 30)
> +    return;
> +
> +  /* Can only increment stack pointer.  */
> +  if (cfa_offset < 0)
> +    return;
> +
> +  /* Stack must be aligned.  */
> +  if ((cfa_offset & (stack_align - 1)) != 0)
> +    return;
> +
> +  /* Registers must be contiguous.  There's only limited benefit in
> +     supprting more complex layouts, so For now we also require they
> +     be in order.  */
> +  next_offset = -gpr_size;
> +  for (i = MIPS_NUM_UNWIND_REGS - 1; i >= 0; i--)
> +    {
> +      if (reg_offset[i] == 0)
> +	continue;
> +
> +      if (reg_offset[i] != next_offset)
> +	return;
> +
> +      next_offset -= gpr_size;
> +    }

What happens after these early returns?  Are they user errors
(in which case we should report them), or do we fall back to something
else (in which case a boolean return value would probably make sense)?

> +  if (fde->lsda_encoding != DW_EH_PE_omit)
> +    fde->eh_header_type = EH_COMPACT_HAS_LSDA;
> +  else if (num_ops <= 3 && fde->per_encoding == DW_EH_PE_omit)
> +    fde->eh_header_type = EH_COMPACT_INLINE;
> +  else
> +    fde->eh_header_type = EH_COMPACT_OUTLINE;

Why does this part need to be in MIPS-specific code?

Thanks,
Richard

^ permalink raw reply	[flat|nested] 31+ messages in thread

* RE: [Patch] Gas support for MIPS Compact EH
  2013-06-01 11:07 ` Richard Sandiford
@ 2014-02-04 21:56   ` Moore, Catherine
  2014-02-05 22:38     ` Maciej W. Rozycki
  2014-02-08 16:34     ` Richard Sandiford
  0 siblings, 2 replies; 31+ messages in thread
From: Moore, Catherine @ 2014-02-04 21:56 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: binutils, Moore, Catherine

[-- Attachment #1: Type: text/plain, Size: 19580 bytes --]

Hi Richard,

I finally got some time to work on fixing up (with some help from Bernd) the binutils submission for compact EH.   Hopefully,  the review comments have been addressed.
The current patch has been expanded to include all of binutils. 

As a reminder, the spec is available here:
https://github.com/MentorEmbedded/cxx-abi/blob/master/MIPSCompactEH.pdf

Specific comments related to your review are embedded.

Thanks,
Catherine

> -----Original Message-----
> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> Sent: Saturday, June 01, 2013 7:08 AM
> To: Moore, Catherine
> Cc: binutils@sourceware.org
> Subject: Re: [Patch] Gas support for MIPS Compact EH
> 
> "Moore, Catherine" <Catherine_Moore@mentor.com> writes:
> > Index: doc/as.texinfo
> >
> ==========================================================
> =========
> > RCS file: /cvs/src/src/gas/doc/as.texinfo,v retrieving revision 1.266
> > diff -p -u -r1.266 as.texinfo
> > --- doc/as.texinfo	29 Apr 2013 13:38:58 -0000	1.266
> > +++ doc/as.texinfo	31 May 2013 17:24:20 -0000
> > @@ -4479,6 +4479,9 @@ if @var{section_list} is @code{.debug_fr  To
> > emit both use @code{.eh_frame, .debug_frame}.  The default if this
> > directive is not used is @code{.cfi_sections .eh_frame}.
> >
> > +On targets that support compact unwinding tables these can be
> > +generated by specifying @code{.eh_frame_header} instead of
> @code{.eh_frame}.
> 
> Should this be .eh_frame_entry instead of .eh_frame_header?

Yes, now fixed.

> 
> > @@ -4514,6 +4517,23 @@ argument is not present, otherwise secon  or a
> > symbol name.  The default after @code{.cfi_startproc} is
> > @code{.cfi_lsda 0xff},  no LSDA.
> >
> > +@section @code{.cfi_inline_lsda} [@var{align}]
> > +@code{.cfi_inline_lsda} marks the start of a LSDA data section and
> > +switches to the corresponding @code{.gnu.extab} section.
> > +It must be preceded by a CFI block containing a @code{.cfi_lsda}
> > +directive and is only valid when generating compact EH frames (i.e.
> > +with @code{.cfi_sections eh_frame_entry}.
> > +
> > +If a compact encoding is being used then the table header and
> > +unwinding opcodes will be generated at this point, so that they are
> > +immediately followed by the LSDA data.  The symbol referenced by the
> > +@code{.cfi_lsda} directive should still be defined in case a fallback
> > +FDE based encoding is used.
> > +
> > +The optional @var{align} argument specifies the alignment required.
> > +The alignment is specified as a power of two, as with the
> > +@code{.p2align} directive.
> 
> Hmm, switching sections and emitting data feels very different in style from
> the other .cfi directives, which just annotate code without changing the flow
> of assembly.  I'd like to know other people's thoughts on this.
> 
> Also, how do you terminate the LSDA?  The documentation doesn't say (but
> should :-)) and I couldn't see this directive in the spec either.

The LSDA is terminated by a section switch.
> 
> "If a compact encoding is being used" seems redundant, since it comes just
> after the bit saying "only valid when generating compact EH frames".
> 
> There need to be tests for all of this. 

Tests are now included in the patch.

>TBH, without tests, and without an
> explanation of what the code is doing, I found this patch pretty hard to
> review.  E.g.:
> 
> > @@ -129,7 +140,12 @@ get_debugseg_name (segT seg, const char
> >        dot = strchr (name + 1, '.');
> >
> >        if (!dollar && !dot)
> > -	name = "";
> > +	{
> > +	  if (compact_eh && strcmp (name, ".text") != 0)
> > +	    return concat (base_name, ".", name, NULL);
> > +
> > +	  name = "";
> > +	}
> 
> why is this change needed?  I.e., for a text section called something like
> .foobar, why does compact_eh need to put things in a section name ending
> in "..foobar", rather than in the main EH section?  I assume the double dots
> are deliberate, e.g. to avoid confusion with ".text.foobar"?

I'm not sure why Paul put this is and the next hunk.  There were test failures without.  I can revisit this
For the next iteration if necessary.

> 
> > @@ -161,6 +177,9 @@ alloc_debugseg_item (segT seg, int subse  static
> > segT  is_now_linkonce_segment (void)  {
> > +  if (compact_eh)
> > +    return now_seg;
> > +
> >    if ((bfd_get_section_flags (stdoutput, now_seg)
> >         & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD
> >  	  | SEC_LINK_DUPLICATES_ONE_ONLY |
> SEC_LINK_DUPLICATES_SAME_SIZE
> 
> Why is this change needed?
> 
> > @@ -180,7 +199,11 @@ make_debug_seg (segT cseg, char *name, i
> >    segT r;
> >    flagword flags;
> >
> > +#ifdef tc_make_debug_seg
> > +  r = tc_make_debug_seg (cseg, name); #else
> >    r = subseg_new (name, 0);
> > +#endif
> 
> Why is this change needed?  And what does the new hook do?  It should be
> documented in internals.texi.
> 
> Do we really want to change the behaviour for traditional DWARF EH too?
> 

It looks like this was intended to make .group sections so that text, extab and eh_frame entries are only deleted together.  This is now removed and this new patch add some linker smarts to handle things.

> > @@ -833,14 +859,15 @@ dot_cfi_personality (int ignored ATTRIBU
> >      }
> >
> >    if ((encoding & 0xff) != encoding
> > -      || ((encoding & 0x70) != 0
> > +      || ((((encoding & 0x70) != 0
> >  #if CFI_DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr
> > -	  && (encoding & 0x70) != DW_EH_PE_pcrel
> > +	   && (encoding & 0x70) != DW_EH_PE_pcrel
> >  #endif
> >  	  )
> >  	 /* leb128 can be handled, but does something actually need it?  */
> > -      || (encoding & 7) == DW_EH_PE_uleb128
> > -      || (encoding & 7) > DW_EH_PE_udata8)
> > +	   || (encoding & 7) == DW_EH_PE_uleb128
> > +	   || (encoding & 7) > DW_EH_PE_udata8)
> > +	&& !tc_cfi_special_encoding (encoding)))
> >      {
> >        as_bad (_("invalid or unsupported encoding in .cfi_personality"));
> >        ignore_rest_of_line ();
> 
> What does a "special" encoding mean?  Again, this hook should be
> documented in internals.texi.  And do we really want to change the set of
> encodings that are allowed for DWARF, even on MIPS systems that predate
> compat EH?
> 
Special means that we have code in the backend to emit a reloc for it.  In the revised patch, it goes along with
Tc_cfi_reloc_for_encoding.  It also looks like internals.texi fails to document many of the tc_macros and it doesn't appear to be built into a .info fiie.

> > @@ -1042,6 +1072,13 @@ dot_cfi_sections (int ignored ATTRIBUTE_
> >  	  sections |= CFI_EMIT_eh_frame;
> >  	else if (strncmp (name, ".debug_frame", sizeof ".debug_frame") ==
> 0)
> >  	  sections |= CFI_EMIT_debug_frame;
> > +#if SUPPORT_COMPACT_EH
> > +	else if (strncmp (name, ".eh_frame_entry", sizeof
> ".eh_frame_entry") == 0)
> > +	  {
> > +	    compact_eh = TRUE;
> > +	    sections |= CFI_EMIT_eh_frame | CFI_EMIT_target;
> > +	  }
> > +#endif
> >  #ifdef tc_cfi_section_name
> >  	else if (strcmp (name, tc_cfi_section_name) == 0)
> >  	  sections |= CFI_EMIT_target;
> 
> I think there needs to be more error checking here.  Do we support mixtures
> of DWARF and new-format EH info in the same .s file?
No, we don't support mixing the two formats in the same .s file.
Error checking has now been added.

> If not, we should check for it, including making sure that later .cfi_sections
> don't contradict earlier ones.  (At the moment, the last one wins, except for
> the CFI_EMIT_target handling.) If we do support mixtures, shouldn't
> something clear compat_eh, since compat_eh is checked in parsing
> contexts?
> 
> Why is the CFI_EMIT_target needed?
Sttrictly speaking it isn't, but having a different value was useful in the error checking.

> > @@ -1125,16 +1162,147 @@ dot_cfi_endproc (int ignored ATTRIBUTE_U
> >        return;
> >      }
> >
> > -  fde = frchain_now->frch_cfi_data->cur_fde_data;
> > +  last_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);
> > +  if ((cfi_sections & CFI_EMIT_target) != 0
> > +      || (compact_eh && (cfi_sections & CFI_EMIT_eh_frame) != 0))
> > +    tc_cfi_endproc (last_fde);
> >  }
> 
> I think it'd be better to use a separate hook, especially if the idea is that other
> targets could use compact EH info.  Targets might then need a "legacy"
> tc_cfi_endproc as well as a "new" hook.
> 
Now changed to use .cfi_fde_data.

> > +#if SUPPORT_COMPACT_EH
> > +static void
> > +output_compact_unwind_data (struct fde_entry *fde, int align) {
> > +  int data_size = fde->eh_data_size + 2;
> > +  int amask;
> > +  char *p;
> > +  char *end;
> > +
> > +  fde->eh_loc = symbol_temp_new_now ();  if (fde->per_encoding !=
> > + DW_EH_PE_omit)
> > +    data_size += 4;
> 
> This 4 shouldn't be hardcoded.  Hopefully an unconditional:

Now fixed.

> 
>   data_size += encoding_size (fde->per_encoding);
> 
> would work; if not, please extend encoding_size.  We then have:
> 
> > +  if (fde->per_encoding != DW_EH_PE_omit)
> > +    {
> > +      *(p++) = 0;
> > +      md_number_to_chars (p, 0, 4);
> > +      tc_cfi_fix_eh_ref (p, &fde->personality);
> > +      p += 4;
> 
> where tc_cfi_fix_eh_ref emits an R_MIPS_EH relocation regardless of which
> personality encoding is used.  AIUI, the encoding must be one of the
> "special" ones allowed by tc_cfi_special_encoding, so we should check for
> that.
> 
> The tc_cfi_fix_eh_ref and tc_cfi_emit_expr hooks don't seem very
> consistent; the former relies on the caller to clear the bytes, whereas the
> latter is supposed to do it itself.

All fixed, now using a hook to return a reloc and eliminated the use of R_MIPS_EH from the assembler.
> 
> Neither of the hooks really seem to be doing anything target-specific except
> for the all-important job of picking a reloc.  I think it would be cleaner to have
> a hook that returns the reloc instead.
> In the case of tc_cfi_emit_expr, this could be done by making
> tc_cfi_special_encoding return the reloc for an encoding, or
> BFD_RELOC_NONE for unsupported encodings.
> 
> Also, this is more of a design point, but I find the handling of the personality
> encoding and R_MIPS_EH handling a bit confusing.  To quote:
> 
> ---------------------------------------------------------------------
> 11 Relocations
> 
> A new static relocation, R_MIPS_EH, is defined. The semantics of this
> relocation depend on whether static or dynamic linking is provided.
> 
> A GNU extension using relocation number 249 shall be used. The relocation
> address need not be naturally aligned.
> 
> 11.1 Static code
> 
> For static code generation, the calculation is the same as an
> R_MIPS_REL32 relocation.
> 
> At runtime, the following expression provides the relocated value, if 'ptr'
> points to the relocation location.
> 
> • (ptrdiff_t)ptr + *(int32_t __attribute__((packed))*)ptr
> 
> [...]
> 
> 11.2 PIC code
> 
> For PIC code generation, a 32- or 64-bit GOT-table entry must be allocated to
> refer to the (dynamically resolved) target address. Once the GOT entry has
> been allocated, the static calculation is as for an
> R_MIPS_GPREL32 relocation (except that the symbol is externally visible).
> The GOT- slot has an associated R_MIPS_{32,64} dynamic relocation emitted
> – and that will of course be at a naturally aligned location
> 
> At runtime, the following expression provides the relocated value, if 'ptr'
> points to the relocation location, and 'gp' is the global pointer
> value:
> 
> • *(ptrdiff_t *)((ptrdiff_t)gp + *(int32_t __attribute__((packed)) *)ptr)
> 
> [...]
> ---------------------------------------------------------------------
> 
> So as I understand the first sentence, it's actually the linker that decides
> whether R_MIPS_EH relocations act as direct PC-relative references (11.1) or
> as indirect datarel references (11.2).
> It's therefore the linker that decides what DWARF encoding R_MIPS_EH
> fields use.  The linker then records that choice in the .eh_frame_hdr.  Is that
> right?
> 
> If so, the assembler seems to be associating R_MIPS_EH with specific DWARF
> encodings, even though the interpretation of R_MIPS_EH isn't known at that
> stage.  I.e. the code reads:
> 
> #define tc_cfi_special_encoding(e) \
>   ((e) == (DW_EH_PE_sdata4 | DW_EH_PE_datarel | DW_EH_PE_indirect) \
>    || (e) == (DW_EH_PE_sdata4 | DW_EH_PE_pcrel))
> 
> void
> mips_cfi_emit_expr (expressionS *exp, int encoding) {
>   char *p;
> 
>   p = frag_more(4);
>   md_number_to_chars (p, 0, 4);
>   if ((encoding & 0x70) == DW_EH_PE_datarel)
>     mips_cfi_fix_eh_ref (p, exp);
>   else
>     {
>       fix_new (frag_now, p - frag_now->fr_literal, 4, exp->X_add_symbol,
> 	       exp->X_add_number, TRUE, BFD_RELOC_32_PCREL);
>     }
> }
> 
> where mips_cfi_fix_eh_ref emits an R_MIPS_EH.  So the code appears to be
> using R_MIPS_EH for the DWARF encoding:
> 
>     DW_EH_PE_sdata4 | DW_EH_PE_datarel | DW_EH_PE_indirect
> 
> even though there's no guarantee that the final value will be either datarel or
> indirect.
> 
> Or, to put it another way, why are we making the choice between
> R_MIPS_PC32 and R_MIPS_EH at assembly time in mips_cfi_emit_expr, but
> not in mips_cfi_fix_eh_ref, even though the patch appears to allow the
> same two encodings in both casees?
> 
> > +  memcpy (p, fde->eh_data, fde->eh_data_size);  p +=
> > + fde->eh_data_size;  while (p != end)
> > +    *(p++) = 0x5f;
> > +
> > +  *(p++) = 0x5c;
> 
> Please use something more mnemonic than 0x5f and 0x5c.  Are these the
> "Spare" and "PC = VR[31]; Finish" encodings from page 16?
> If so, they seem target-specific, so should probably use tc_* #defines.

Now fixed.
> 
> > +static void
> > +dot_cfi_inline_lsda (int ignored ATTRIBUTE_UNUSED) {
> > +  segT cfi_seg, ccseg;
> > +  int align;
> > +  long max_alignment = 28;
> > +
> > +  if (!last_fde)
> > +    {
> > +      as_bad (_("unexpected .cfi_inline_lsda"));
> > +      ignore_rest_of_line ();
> > +      return;
> > +    }
> > +
> > +  if (!compact_eh
> > +      || (last_fde->sections & CFI_EMIT_eh_frame) == 0
> > +      || (last_fde->eh_header_type != EH_COMPACT_LEGACY
> > +	  && last_fde->eh_header_type != EH_COMPACT_HAS_LSDA))
> > +
> > +    {
> > +      as_bad (_(".cfi_inline_lsda not valid for this frame"));
> > +      ignore_rest_of_line ();
> > +      return;
> > +    }
> 
> Excess blank line.  The first two lines of the if are clear enough, but what is
> the eh_header_type condition doing?  It looks like one of the cases it catches
> is where two LSDAs are specified for the same frame.
> If so, I think a better error message should be given in that case.

All fixed including a test case for the error message.
> 
> > +  demand_empty_rest_of_line ();
> > +  ccseg = CUR_SEG (last_fde);
> > +  /* Open .gnu_extab section.  */
> > +  cfi_seg = get_cfi_seg (ccseg, ".gnu_extab",
> > +			 (SEC_ALLOC | SEC_LOAD | SEC_DATA
> > +			  | DWARF2_EH_FRAME_READ_ONLY),
> > +			 1);
> > +#ifdef md_fix_up_eh_frame
> > +  md_fix_up_eh_frame (cfi_seg);
> > +#else
> > +  (void) cfi_seg;
> > +#endif
> > +
> > +  frag_align (align, 0, 0);
> > +  record_alignment (now_seg, align);
> > +  if (last_fde->eh_header_type == EH_COMPACT_HAS_LSDA)
> > +    output_compact_unwind_data (last_fde, align);
> 
> Please could you explain the EH_COMPACT_LEGACY handling here? 

Would you please clarify this question?  I don't see the reference to EH_COMPACT_LEGACY.
> 
> > @@ -1479,7 +1647,11 @@ output_cie (struct cie_entry *cie, bfd_b
> >  	  offsetT size = encoding_size (cie->per_encoding);
> >  	  out_one (cie->per_encoding);
> >  	  exp = cie->personality;
> > -	  if ((cie->per_encoding & 0x70) == DW_EH_PE_pcrel)
> > +	  if (tc_cfi_special_encoding (cie->per_encoding))
> > +	    {
> > +	      tc_cfi_emit_expr (&exp, cie->per_encoding);
> > +	    }
> 
> Excess braces.

Should be all fixed now.
> 
> > @@ -1617,7 +1804,11 @@ output_fde (struct fde_entry *fde, struc
> >    if (fde->lsda_encoding != DW_EH_PE_omit)
> >      {
> >        exp = fde->lsda;
> > -      if ((fde->lsda_encoding & 0x70) == DW_EH_PE_pcrel)
> > +      if (tc_cfi_special_encoding (cie->lsda_encoding))
> > +	{
> > +	  tc_cfi_emit_expr (&exp, cie->lsda_encoding);
> > +	}
> 
> Same here.
> 
> > +#if SUPPORT_COMPACT_EH
> > +static void
> > +cfi_emit_eh_header (symbolS *sym, bfd_vma addend)
> >  {
> > -  if (SUPPORT_FRAME_LINKONCE)
> > +  expressionS exp;
> > +
> > +  exp.X_add_number = addend;
> > +  exp.X_add_symbol = sym;
> > +  if (tc_cfi_special_encoding (DW_EH_PE_sdata4 | DW_EH_PE_pcrel))
> >      {
> > -      struct dwcfi_seg_list *l;
> > +      tc_cfi_emit_expr (&exp, DW_EH_PE_sdata4 | DW_EH_PE_pcrel);
> > +    }
> 
> Here too.
> 
> > +		      /* Open .eh_frame section.  */
> > +		      cfi_seg = get_cfi_seg (ccseg, ".gnu_extab",
> > +					     (SEC_ALLOC | SEC_LOAD |
> SEC_DATA
> > +					      |
> DWARF2_EH_FRAME_READ_ONLY),
> > +					     1);
> 
> Pasto, this isn't .eh_frame.

Fixed.
> 
> > @@ -15712,7 +15712,8 @@ md_apply_fix (fixS *fixP, valueT *valP,
> >  	      || fixP->fx_r_type == BFD_RELOC_MICROMIPS_SUB
> >  	      || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
> >  	      || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY
> > -	      || fixP->fx_r_type == BFD_RELOC_MIPS_TLS_DTPREL64);
> > +	      || fixP->fx_r_type == BFD_RELOC_MIPS_TLS_DTPREL64
> > +	      || fixP->fx_r_type == BFD_RELOC_NONE);
> >
> >    buf = fixP->fx_frag->fr_literal + fixP->fx_where;
> >
> > @@ -15951,6 +15952,7 @@ md_apply_fix (fixS *fixP, valueT *valP,
> >          S_SET_WEAK (fixP->fx_addsy);
> >        break;
> >
> > +    case BFD_RELOC_NONE:
> >      case BFD_RELOC_VTABLE_ENTRY:
> >        fixP->fx_done = 0;
> >        break;
> 
> Why are these two hunks needed?

Now reverted.
> 
> > @@ -19793,3 +19795,289 @@ tc_mips_regname_to_dw2regnum (char
> *regn
> >
> >    return regnum;
> >  }
> > +
> > +#if defined (OBJ_ELF)
> > +segT
> > +mips_make_debug_seg (segT cseg, char *name) {
> > +  const char *group_name = NULL;
> > +  int linkonce;
> > +  int flags = 0;
> > +
> > +  if (cseg && (cseg->flags & SEC_LINK_ONCE) != 0)
> > +    {
> > +      group_name = elf_group_name (cseg);
> > +      if (group_name == NULL)
> > +	{
> > +	  as_bad (_("Group section `%s' has no group signature"),
> > +		  segment_name (cseg));
> > +	}
> > +      flags |= SHF_GROUP;
> > +      linkonce = 1;
> > +    }
> > +  obj_elf_change_section (name, SHT_PROGBITS, flags, 0, group_name,
> > +linkonce, 0);
> > +  return now_seg;
> > +}
> > +
> 
> This function doesn't seem to contain any MIPS-specific code.
> If a hook really is needed, then I think this implementation belongs in obj-
> elf.c rather than tc-mips.c.

Now removed in favor of linker smarts.
> 
> > +/* Attempt to generate compact frame unwind encodings.  */
> > +
> > +void
> > +mips_cfi_endproc (struct fde_entry *fde ATTRIBUTE_UNUSED) {

All remaining comments are against this function which was removed.  The compact opcodes are
Now generated by gcc using a .cfi_fde_data directive.



[-- Attachment #2: compact-eh.cl --]
[-- Type: application/octet-stream, Size: 5872 bytes --]

2014-02-03  Catherine Moore  <clm@codesourcery.com>
	    Paul Brook  <paul@codesourcery.com>
	    Bernd Schmidt <bernds@codesourcery.com>

	bfd/
	* bfd-in2.h: Regenerate.
	* elf-bfd.h (eh_frame_hdr_info) Add allocated_entries
	and entries.
	(elf_backend_data): Add compact_eh_encoding.
	(bfd_elf_section_data): Add eh_frame_entry.
	(elf_section_eh_frame_entry): Define.
	(bfd_elf_parse_eh_frame_entry): Declare.
	(bfd_elf_end_eh_frame_parsing): Return type is now bfd_boolean.
	(bfd_elf_write_section_eh_frame_entry): Declare.
	(_bfd_elf_eh_frame_entry_present): Declare.
	* elf-eh-frame.c (bfd_elf_remember_eh_frame_entry): New function.
	(bfd_elf_parse_eh_frame_entry): New function.
	(cmp_eh_frame_hdr): New function.
	(add_eh_frame_hdr_terminator): New function.
	(_bfd_elf_eh_frame_entry_present): New function.
	(_bfd_elf_end_eh_frame_parsingi): Modify for Compact EH support.
	(_bfd_elf_discard_section_eh_frame_hdr): Likewise.
	(_bfd_elf_maybe_strip_eh_frame_hdr): Likewise.
	(_bfd_elf_write_section_eh_frame_entry): New function.
	(_bfd_elf_fixup_eh_frame_hdr): New function.
	(_bfd_elf_write_section_eh_frame_hdr): Modify for Compact EH support.
	* elflink.c (elf_link_input_bfd): Support SEC_INFO_TYPE_EH_FRAME_ENTRY.
	(elf_section_ignore_discarded_relocs): Likewise.
	(bfd_elf_final_link): Call _bfd_elf_fixup_eh_frame_hdr.
	(_bfd_elf_gc_mark): Support Compact EH.
	(bfd_elf_gc_sections): Likewise.
	(bfd_elf_discard_info): Likewise.
	* elfxx-mips.c: Include dwarf2.h.
	(mips_elf_link_hash_table): Add eh_reloc_type.
	(mips_elf_real_reloc): New function.
	(mips_elf_calculate_relocation): Invoke mips_elf_real_reloc.
	Handle R_MIPS_EH.
	(_bfd_mips_elf_check_relocs): Likewise.
	(mips_elf_create_dynamic_relocation): Invoke mips_elf_real_reloc.
	(mips_elf_adjust_addend): Likewise.
	(_bfd_mips_elf_relocate_section): Likewise.
	(_bfd_mips_elf_insn32): Renamed to...
	(_bfd_mips_elf_linker_flags): ... this.
	(_bfd_mips_elf_compact_eh_encoding): New function.
	* elfxx-mips.h (_bfd_mips_elf_insn32): Delete declaration.
	(_bfd_mips_elf_linker_flags): New declaration.
	(_bfd_mips_elf_compact_eh_encoding): New declaration.
	(elf_backend_compact_eh_encoding): Define.
	* elfxx-target.h (elf_backend_compact_eh_encoding): Provide
	default definition.
	(elf_backend_data): Add elf_backend_compact_eh_encoding.
	* section.c (SEC_INFO_TYPE_EH_FRAME_ENTRY): Define.

	gas/
	* config/tc-mips.c (s_force_gpword): New function.
	(mips_pseudo_table): New entry "forcegpword".
	(md_apply_fix): Handle BFD_RELOC_NONE.
	(s_ehword): Use BFD_RELOC_32_PCREL.
	(mips_cfi_reloc_for_encoding): New function.
	* config/tc-mips.h (DWARF2_FDE_RELOC_SIZE): Redefine.
	(DWARF2_FDE_RELOC_ENCODING): Define.
	* doc/as.texinfo: New documentation for Compact EH extensions.
	* dw2gencfi.c (EH_FRAME_LINKONCE): Define.
	(tc_cfi_special_encoding): Define.
	(FRAME_NAME): Define.
	(compact_eh): Declare or define.
	(out_one, out_two, out_four): New functions.
	(out_uleb128, out_sleb128): New functions.
	(encoding_size): New function.
	(emit_expr_encoded): New function.
	(get_debugseg_name): Support Compact EH.
	(alloc_debugseg_item): Likewise.
	(is_now_linkonce_segment): Likewise.
	(make_debug_seg): Likewise.
	(cie_entry): Add fde_encoding.
	(dot_cfi_fde_data, dot_cfi_personality, dot_cfi_inline): New functions.
	(cfi_pseudo_table): "cfi_fde_data", "cfi_personality_id",
	"cfi_inline_lsda", "cfi_epilogue_begin": New entries.
	(dot_cfi): Support Compact EH.
	(dot_cfi_personality): Likewise.
	(dot_cfi_lsda): Likewise.
	(dot_cfi_sections): Likewise.
	(dot_cfi_val_encoded_addr): Likewise.
	(dot_cfi_startproc): Likewise.
	(dot_cfi_endproc): Likewise.
	(get_cfi_seg): Likewise.
	(output_compact_unwind_data): New function.
 	(output_cfi_insn): Support Compact EH.
	(output_cie): Likewise.
	(output_fde): Likewise.
	(select_cie_for_fde): Likewise.
	(cfi_emit_eh_header): Likewise.
	(output_eh_header): Likewise.
	(cfi_finish): Likewise.
	* dw2gencfi.h (SUPPORT_COMPACT_EH): Define.
	(MULTIPLE_FRAME_SECTIONS): Define.
	(EH_COMPACT_LEGACY, EH_COMPACT_INLINE,
	EH_COMPACT_OUTLINE, EH_COMPACT_OUTLINE_DONE,
	EH_COMPACT_HAS_LSDA): New.
	(fde_entry): Add personality_id, eh_header_type,
	eh_data_size, eh_data, el_loc, sections.
	(CFI_EPILOGUE_END): Define. 

	gas/testsuite/
	* gas/mips/compact-eh-1.s: New test.
	* gas/mips/compact-eh-el-1.s: New test.
	* gas/mips/compact-eh-1.d: New test output.
	* gas/mips/compact-eh-2.s: New test.
	* gas/mips/compact-eh-el-2.s: New test.
	* gas/mips/compact-eh-2.d: New test output.
	* gas/mips/compact-eh-3.s: New test.
	* gas/mips/compact-eh-el-3.s: New test.
	* gas/mips/compact-eh-3.d: New test output.
	* gas/mips/compact-eh-4.s: New test.
	* gas/mips/compact-eh-el-4.s: New test.
	* gas/mips/compact-eh-4.d: New test output.
	* gas/mips/compact-eh-5.s: New test.
	* gas/mips/compact-eh-el-5.s: New test.
	* gas/mips/compact-eh-5.d: New test output.
	* gas/mips/compact-eh-6.s: New test.
	* gas/mips/compact-eh-el-6.s: New test.
	* gas/mips/compact-eh-6.d: New test output.
	* gas/mips/compact-eh-7.s: New test.
	* gas/mips/compact-eh-el-7.s: New test.
	* gas/mips/compact-eh-7.d: New test output.
	* gas/mips/compact-eh-err1.s: New test.
	* gas/mips/compact-eh-err1.l: New test output.
	* gas/mips/compact-eh-err2.s: New test.
	* gas/mips/compact-eh-err2.l: New test output.
	* gas/mips/ehword.d: Update output.
	* gas/mips/mips.exp: Run new tests.

	include/
	* bfdlink.h (bfd_link_info): Update size of eh_frame_hdr.

	ld/
	* emultempl/elf32.em (gld${EMULATION_NAME}_after_open): 
	Add Compact EH support.
	* emultempl/mipself.em (mips_create_output_section_statements):
	Add Compact EH support.
	(pcrel-eh-reloc): New option.
	(OPTION_PCREL_EH_RELOC): New.
	* ld.texinfo: Document --pcrel-eh-reloc.

[-- Attachment #3: compact-eh.patch --]
[-- Type: application/octet-stream, Size: 108912 bytes --]

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 33792f4..238aae4 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -1438,6 +1438,7 @@ typedef struct bfd_section
 #define SEC_INFO_TYPE_EH_FRAME  3
 #define SEC_INFO_TYPE_JUST_SYMS 4
 #define SEC_INFO_TYPE_TARGET    5
+#define SEC_INFO_TYPE_EH_FRAME_ENTRY 6
 
   /* Nonzero if this section uses RELA relocations, rather than REL.  */
   unsigned int use_rela_p:1;
diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index 0aab5fa..b7d7df0 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -381,8 +381,10 @@ struct eh_frame_hdr_info
 {
   struct htab *cies;
   asection *hdr_sec;
-  unsigned int fde_count, array_count;
+  unsigned int fde_count, array_count, allocated_entries;
   struct eh_frame_array_ent *array;
+  /* eh_frame_entry fragments.  */
+  asection **entries;
   /* TRUE if we should try to merge CIEs between input sections.  */
   bfd_boolean merge_cies;
   /* TRUE if all .eh_frames have been parsd.  */
@@ -1275,6 +1277,9 @@ struct elf_backend_data
      or give an error and return FALSE.  */
   bfd_boolean (*obj_attrs_handle_unknown) (bfd *, int);
 
+  /* Encoding used for compact EH tables.  */
+  int (*compact_eh_encoding) (struct bfd_link_info *);
+
   /* This is non-zero if static TLS segments require a special alignment.  */
   unsigned static_tls_alignment;
 
@@ -1420,6 +1425,9 @@ struct bfd_elf_section_data
      field acts as a chain pointer.  */
   struct eh_cie_fde *fde_list;
 
+  /* Link from a text section to its .eh_frame_entry section.  */
+  asection *eh_frame_entry;
+
   /* A pointer used for various section optimizations.  */
   void *sec_info;
 };
@@ -1433,6 +1441,7 @@ struct bfd_elf_section_data
 #define elf_next_in_group(sec)	(elf_section_data(sec)->next_in_group)
 #define elf_fde_list(sec)	(elf_section_data(sec)->fde_list)
 #define elf_sec_group(sec)	(elf_section_data(sec)->sec_group)
+#define elf_section_eh_frame_entry(sec)	(elf_section_data(sec)->eh_frame_entry)
 
 #define xvec_get_elf_backend_data(xvec) \
   ((const struct elf_backend_data *) (xvec)->backend_data)
@@ -1964,9 +1973,12 @@ extern void _bfd_elf_strtab_finalize
 
 extern void _bfd_elf_begin_eh_frame_parsing
   (struct bfd_link_info *info);
+extern void _bfd_elf_parse_eh_frame_entry
+  (bfd *, struct bfd_link_info *, asection *, struct elf_reloc_cookie *,
+   bfd_boolean);
 extern void _bfd_elf_parse_eh_frame
   (bfd *, struct bfd_link_info *, asection *, struct elf_reloc_cookie *);
-extern void _bfd_elf_end_eh_frame_parsing
+extern bfd_boolean _bfd_elf_end_eh_frame_parsing
   (struct bfd_link_info *info);
 
 extern bfd_boolean _bfd_elf_discard_section_eh_frame
@@ -1978,10 +1990,15 @@ extern bfd_vma _bfd_elf_eh_frame_section_offset
   (bfd *, struct bfd_link_info *, asection *, bfd_vma);
 extern bfd_boolean _bfd_elf_write_section_eh_frame
   (bfd *, struct bfd_link_info *, asection *, bfd_byte *);
+extern bfd_boolean _bfd_elf_write_section_eh_frame_entry
+  (bfd *, asection *, bfd_byte *);
+extern void _bfd_elf_fixup_eh_frame_hdr (struct bfd_link_info *);
 extern bfd_boolean _bfd_elf_write_section_eh_frame_hdr
   (bfd *, struct bfd_link_info *);
 extern bfd_boolean _bfd_elf_eh_frame_present
   (struct bfd_link_info *);
+extern bfd_boolean _bfd_elf_eh_frame_entry_present
+  (struct bfd_link_info *);
 extern bfd_boolean _bfd_elf_maybe_strip_eh_frame_hdr
   (struct bfd_link_info *);
 
diff --git a/bfd/elf-eh-frame.c b/bfd/elf-eh-frame.c
index 4b6e8ea..4c94c10 100644
--- a/bfd/elf-eh-frame.c
+++ b/bfd/elf-eh-frame.c
@@ -462,6 +462,119 @@ _bfd_elf_begin_eh_frame_parsing (struct bfd_link_info *info)
   hdr_info->merge_cies = !info->relocatable;
 }
 
+/* Add a .eh_frame_entry section.  */
+
+static void
+bfd_elf_remember_eh_frame_entry (struct eh_frame_hdr_info *hdr_info,
+				 asection *sec)
+{
+  if (hdr_info->array_count == hdr_info->allocated_entries)
+    {
+      if (hdr_info->allocated_entries == 0)
+	{
+	  hdr_info->allocated_entries = 2;
+	  hdr_info->entries = bfd_malloc (hdr_info->allocated_entries
+					  * sizeof(hdr_info->entries[0]));
+	}
+      else
+	{
+	  hdr_info->allocated_entries *= 2;
+	  hdr_info->entries = bfd_realloc (hdr_info->entries,
+	    hdr_info->allocated_entries * sizeof(hdr_info->entries[0]));
+	}
+
+      BFD_ASSERT (hdr_info->entries);
+    }
+
+  hdr_info->entries[hdr_info->array_count++] = sec;
+}
+
+/* Parse a .eh_frame_entry section.  Figure out which text section it
+   references.  */
+
+void
+_bfd_elf_parse_eh_frame_entry (bfd *abfd, struct bfd_link_info *info,
+			       asection *sec, struct elf_reloc_cookie *cookie,
+			       bfd_boolean remember)
+{
+  struct elf_link_hash_table *htab;
+  struct eh_frame_hdr_info *hdr_info;
+  unsigned long r_symndx;
+  struct elf_link_hash_entry *h;
+  asection *text_sec;
+
+  htab = elf_hash_table (info);
+  hdr_info = &htab->eh_info;
+
+  if (sec->sec_info_type == SEC_INFO_TYPE_EH_FRAME_ENTRY)
+    {
+      if (!(sec->flags & SEC_EXCLUDE) && remember)
+	bfd_elf_remember_eh_frame_entry (hdr_info, sec);
+      return;
+    }
+
+  if (sec->size == 0
+      || sec->sec_info_type != SEC_INFO_TYPE_NONE)
+    {
+      return;
+    }
+
+  if (sec->output_section && bfd_is_abs_section (sec->output_section))
+    {
+      /* At least one of the sections is being discarded from the
+	 link, so we should just ignore them.  */
+      return;
+    }
+
+  if (cookie->rel == cookie->relend)
+    goto fail;
+
+  /* The first relocation is the function start.  */
+  r_symndx = cookie->rel->r_info >> cookie->r_sym_shift;
+  if (r_symndx == STN_UNDEF)
+    goto fail;
+
+  if (r_symndx >= cookie->locsymcount
+      || ELF_ST_BIND (cookie->locsyms[r_symndx].st_info) != STB_LOCAL)
+    {
+      h = cookie->sym_hashes[r_symndx - cookie->extsymoff];
+      while (h->root.type == bfd_link_hash_indirect
+             || h->root.type == bfd_link_hash_warning)
+        h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+      if (h->root.type != bfd_link_hash_defined
+          && h->root.type != bfd_link_hash_defweak)
+	goto fail;
+
+      text_sec = h->root.u.def.section;
+    }
+  else
+    {
+      Elf_Internal_Sym *isym;
+
+      /* Need to: get the symbol; get the section.  */
+      isym = &cookie->locsyms[r_symndx];
+      text_sec = bfd_section_from_elf_index (abfd, isym->st_shndx);
+    }
+
+  if (text_sec == NULL)
+    goto fail;
+
+  elf_section_eh_frame_entry (text_sec) = sec;
+  if (text_sec->output_section
+      && bfd_is_abs_section (text_sec->output_section))
+    sec->flags |= SEC_EXCLUDE;
+  else if (remember)
+    bfd_elf_remember_eh_frame_entry (hdr_info, sec);
+
+  sec->sec_info_type = SEC_INFO_TYPE_EH_FRAME_ENTRY;
+  elf_section_data (sec)->sec_info = text_sec;
+  return;
+
+fail:
+  (*_bfd_error_handler) (_("%B: failed to precoess .eh_frame_entry"), sec->owner);
+}
+
 /* Try to parse .eh_frame section SEC, which belongs to ABFD.  Store the
    information in the section's sec_info field on success.  COOKIE
    describes the relocations in SEC.  */
@@ -931,15 +1044,87 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info,
 #undef REQUIRE
 }
 
-/* Finish a pass over all .eh_frame sections.  */
+/* Order eh_frame_hdr entries by the VMA of their text section.  */
 
-void
+static int
+cmp_eh_frame_hdr (const void *a, const void *b)
+{
+  bfd_vma text_a;
+  bfd_vma text_b;
+  asection *sec;
+
+  sec = *(asection *const *)a;
+  sec = (asection *) elf_section_data (sec)->sec_info;
+  text_a = sec->output_section->vma + sec->output_offset;
+  sec = *(asection *const *)b;
+  sec = (asection *) elf_section_data (sec)->sec_info;
+  text_b = sec->output_section->vma + sec->output_offset;
+
+  if (text_a < text_b)
+    return -1;
+  return text_a > text_b;
+
+}
+
+/* Add space for a CANTUNWIND terminator to SEC is the text sections
+   referenced by it and NEXT are not contiguous, or NEXT is NULL.  */
+
+static void
+add_eh_frame_hdr_terminator (asection *sec,
+			     asection *next)
+{
+  bfd_vma end;
+  bfd_vma next_start;
+  asection *text_sec;
+
+  if (next)
+    {
+      /* See if there is a gap (presuably a text section without unwind info)
+	 between these two entries.  */
+      text_sec = (asection *) elf_section_data (sec)->sec_info;
+      end = text_sec->output_section->vma + text_sec->output_offset
+	    + text_sec->size;
+      text_sec = (asection *) elf_section_data (next)->sec_info;
+      next_start = text_sec->output_section->vma + text_sec->output_offset;
+      /* Allow for alignment padding.  */
+      end = BFD_ALIGN (end, 1 << bfd_get_section_alignment (text_sec->owner, text_sec));
+      if (end == next_start)
+	return;
+    }
+
+  /* Add space for a CANTUNWIND terminator.  */
+  if (!sec->rawsize)
+    sec->rawsize = sec->size;
+
+  bfd_set_section_size (sec->owner, sec, sec->size + 8);
+}
+
+/* Finish a pass over all .eh_frame and eh_frame_entry sections.  */
+
+bfd_boolean
 _bfd_elf_end_eh_frame_parsing (struct bfd_link_info *info)
 {
   struct eh_frame_hdr_info *hdr_info;
+  unsigned int i;
 
   hdr_info = &elf_hash_table (info)->eh_info;
   hdr_info->parsed_eh_frames = TRUE;
+
+  if (hdr_info->array_count == 0 || info->eh_frame_hdr < 2)
+    return FALSE;
+
+  qsort (hdr_info->entries, hdr_info->array_count,
+	 sizeof (asection *), cmp_eh_frame_hdr);
+
+  for (i = 0; i < hdr_info->array_count - 1; i++)
+    {
+      add_eh_frame_hdr_terminator (hdr_info->entries[i],
+				   hdr_info->entries[i + 1]);
+    }
+
+  /* Add a CANTUNWIND terminator after the last entry.  */
+  add_eh_frame_hdr_terminator (hdr_info->entries[i], NULL);
+  return TRUE;
 }
 
 /* Mark all relocations against CIE or FDE ENT, which occurs in
@@ -1243,9 +1428,18 @@ _bfd_elf_discard_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
   if (sec == NULL)
     return FALSE;
 
-  sec->size = EH_FRAME_HDR_SIZE;
-  if (hdr_info->table)
-    sec->size += 4 + hdr_info->fde_count * 8;
+  if (info->eh_frame_hdr == 2)
+    {
+      /* For compact frames we only add the header.  The actual table comes
+         from the .eh_frame_entry sections.  */
+      sec->size = 8;
+    }
+  else
+    {
+      sec->size = EH_FRAME_HDR_SIZE;
+      if (hdr_info->table)
+	sec->size += 4 + hdr_info->fde_count * 8;
+    }
 
   elf_eh_frame_hdr (abfd) = sec;
   return TRUE;
@@ -1271,6 +1465,45 @@ _bfd_elf_eh_frame_present (struct bfd_link_info *info)
   return FALSE;
 }
 
+/* Return true if there is at least one .eh_frame_entry section in
+   input files.  */
+bfd_boolean
+_bfd_elf_eh_frame_entry_present (struct bfd_link_info *info)
+{
+  asection *o;
+  bfd *abfd;
+
+  for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link_next)
+    {
+      for (o = abfd->sections; o; o = o->next)
+	{
+	  const char *name = bfd_get_section_name (abfd, o);
+
+	  if (strcmp (name, ".eh_frame_entry")
+	      && !bfd_is_abs_section (o->output_section))
+	    {
+	      if (strcmp (o->output_section->name, ".eh_frame_hdr"))
+		return TRUE;
+	      else
+		{
+		  (*_bfd_error_handler)
+			(_("error: an '.eh_frame_entry'"
+			   " input section that is not mapped to the"
+			   " '.eh_frame_hdr' output section was seen"));
+		  (*_bfd_error_handler)
+			(_("note: try adding '*(.eh_frame_entry"
+			   " .eh_frame_entry.*)' to the '.eh_frame_hdr'"
+			   " output section description in the linker"
+			   " script"));
+		  bfd_set_error (bfd_error_invalid_operation);
+		  return FALSE;
+		}
+	    }
+	}
+    }
+  return FALSE;
+}
+
 /* This function is called from size_dynamic_sections.
    It needs to decide whether .eh_frame_hdr should be output or not,
    because when the dynamic symbol table has been sized it is too late
@@ -1281,6 +1514,8 @@ _bfd_elf_maybe_strip_eh_frame_hdr (struct bfd_link_info *info)
 {
   struct elf_link_hash_table *htab;
   struct eh_frame_hdr_info *hdr_info;
+  struct bfd_link_hash_entry *bh = NULL;
+  struct elf_link_hash_entry *h;
 
   htab = elf_hash_table (info);
   hdr_info = &htab->eh_info;
@@ -1289,13 +1524,27 @@ _bfd_elf_maybe_strip_eh_frame_hdr (struct bfd_link_info *info)
 
   if (bfd_is_abs_section (hdr_info->hdr_sec->output_section)
       || !info->eh_frame_hdr
-      || !_bfd_elf_eh_frame_present (info))
+      || (info->eh_frame_hdr == 1 && !_bfd_elf_eh_frame_present (info))
+      || (info->eh_frame_hdr == 2 && !_bfd_elf_eh_frame_entry_present (info)))
     {
       hdr_info->hdr_sec->flags |= SEC_EXCLUDE;
       hdr_info->hdr_sec = NULL;
       return TRUE;
     }
 
+  /* Add a hidden symbol so that systems without access to PHDRs can
+     find the table.  */
+  if (! (_bfd_generic_link_add_one_symbol
+	 (info, info->output_bfd, "__GNU_EH_FRAME_HDR", BSF_LOCAL,
+	  hdr_info->hdr_sec, 0, NULL, FALSE, FALSE, &bh)))
+    return FALSE;
+
+  h = (struct elf_link_hash_entry *) bh;
+  h->def_regular = 1;
+  h->other = STV_HIDDEN;
+  get_elf_backend_data
+    (info->output_bfd)->elf_backend_hide_symbol (info, h, TRUE);
+
   hdr_info->table = TRUE;
   return TRUE;
 }
@@ -1387,6 +1636,71 @@ _bfd_elf_eh_frame_section_offset (bfd *output_bfd ATTRIBUTE_UNUSED,
 	  + extra_augmentation_data_bytes (sec_info->entry + mid));
 }
 
+/* Write out .eh_frame_entry section.  Add CANTUNWIND terminator if needed.
+   Also check that the contents look sane.  */
+
+bfd_boolean
+_bfd_elf_write_section_eh_frame_entry (bfd *abfd,
+				       asection *sec,
+				       bfd_byte *contents)
+{
+  bfd_byte cantunwind[8];
+  bfd_vma addr;
+  bfd_vma last_addr;
+  asection *text_sec;
+  bfd_vma offset;
+
+  if (!sec->rawsize)
+    sec->rawsize = sec->size;
+
+  BFD_ASSERT (sec->sec_info_type == SEC_INFO_TYPE_EH_FRAME_ENTRY);
+
+  if (sec->flags & SEC_EXCLUDE)
+    return TRUE;
+
+  if (!bfd_set_section_contents (abfd, sec->output_section, contents,
+				 sec->output_offset, sec->rawsize))
+      return FALSE;
+
+  last_addr = bfd_get_signed_32 (abfd, contents);
+  /* Check that all the entries are in order.  */
+  for (offset = 8; offset < sec->rawsize; offset += 8)
+    {
+      addr = bfd_get_signed_32 (abfd, contents + offset) + offset;
+      if (addr <= last_addr)
+	{
+	  (*_bfd_error_handler) (_("%B: %s not in order"), sec->owner, sec->name);
+	  return FALSE;
+	}
+
+      last_addr = addr;
+    }
+
+  text_sec = (asection *) elf_section_data (sec)->sec_info;
+  addr = text_sec->output_section->vma + text_sec->output_offset
+	 + text_sec->size;
+  addr &= ~1;
+  addr -= (sec->output_section->vma + sec->output_offset + sec->rawsize);
+  BFD_ASSERT ((addr & 1) == 0);
+  if (last_addr >= addr + sec->rawsize)
+    {
+      (*_bfd_error_handler) (_("%B: %s points past end of text section"),
+			     sec->owner, sec->name);
+      return FALSE;
+    }
+
+  if (sec->size == sec->rawsize)
+    return TRUE;
+
+  BFD_ASSERT (sec->size == sec->rawsize + 8);
+  BFD_ASSERT ((addr & 1) == 0);
+
+  bfd_put_32 (abfd, addr, cantunwind);
+  bfd_put_32 (abfd, 0x015d5d01, cantunwind + 4);
+  return bfd_set_section_contents (abfd, sec->output_section, cantunwind,
+				   sec->output_offset + sec->rawsize, 8);
+}
+
 /* Write out .eh_frame section.  This is called with the relocated
    contents.  */
 
@@ -1746,6 +2060,44 @@ vma_compare (const void *a, const void *b)
   return 0;
 }
 
+/* Reorder .eh_frame_entry sections to match the associated text sections.  */
+
+void
+_bfd_elf_fixup_eh_frame_hdr (struct bfd_link_info *info)
+{
+  asection *sec = NULL;
+  struct eh_frame_hdr_info *hdr_info;
+  unsigned int i;
+  bfd_vma offset;
+  struct bfd_link_order *p;
+
+  hdr_info = &elf_hash_table (info)->eh_info;
+
+  if (hdr_info->hdr_sec == NULL || info->eh_frame_hdr < 2
+      || hdr_info->array_count == 0)
+    return;
+
+  /* Change section output offsets to be in text section order.  */
+  offset = 8;
+  for (i = 0; i < hdr_info->array_count; i++)
+    {
+      sec = hdr_info->entries[i];
+      sec->output_offset = offset;
+      offset += sec->size;
+    }
+
+
+  /* Fix the link_order to match.  */
+  for (p = sec->output_section->map_head.link_order; p != NULL; p = p->next)
+    {
+      if (p->type != bfd_indirect_link_order)
+	abort();
+
+      p->offset = p->u.indirect.section->output_offset;
+      i--;
+    }
+}
+
 /* Write out .eh_frame_hdr section.  This must be called after
    _bfd_elf_write_section_eh_frame has been called on all input
    .eh_frame sections.
@@ -1766,7 +2118,13 @@ vma_compare (const void *a, const void *b)
    fde_count x [encoded] initial_loc, fde
 				(array of encoded pairs containing
 				 FDE initial_location field and FDE address,
-				 sorted by increasing initial_loc).  */
+				 sorted by increasing initial_loc).
+
+   For compact frames we use the following format:
+   ubyte version		(2)
+   ubyte eh_ref_enc		(DW_EH_PE_* encoding of typinfo references)
+   uint32_t count		(Number of entries in table)
+   [array from .eh_frame_entry sections]  */
 
 bfd_boolean
 _bfd_elf_write_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
@@ -1787,6 +2145,36 @@ _bfd_elf_write_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
       bfd_size_type size;
       bfd_vma encoded_eh_frame;
 
+      if (info->eh_frame_hdr == 2)
+	{
+	  const struct elf_backend_data *bed;
+	  bfd_vma count;
+
+	  contents = bfd_malloc (8);
+	  if (contents == NULL)
+	    return FALSE;
+
+	  if (sec->size != 8)
+	    abort ();
+
+	  memset (contents, 0, 8);
+	  contents[0] = 2;
+	  bed = get_elf_backend_data (abfd);
+	  if (bed->compact_eh_encoding)
+	    contents[1] = (*bed->compact_eh_encoding) (info);
+	  else
+	    contents[0] = 0xff;
+
+	  count = (sec->output_section->size -9 ) / 8;
+	  bfd_put_32 (abfd, count, contents + 4);
+	  retval = bfd_set_section_contents (abfd, sec->output_section,
+					     contents,
+					     (file_ptr) sec->output_offset,
+					     sec->size);
+	  free (contents);
+	  return retval;
+	}
+
       size = EH_FRAME_HDR_SIZE;
       if (hdr_info->array && hdr_info->array_count == hdr_info->fde_count)
 	size += 4 + hdr_info->fde_count * 8;
diff --git a/bfd/elflink.c b/bfd/elflink.c
index 88967c8..310e0c4 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -9142,6 +9142,7 @@ elf_section_ignore_discarded_relocs (asection *sec)
     {
     case SEC_INFO_TYPE_STABS:
     case SEC_INFO_TYPE_EH_FRAME:
+    case SEC_INFO_TYPE_EH_FRAME_ENTRY:
       return TRUE;
     default:
       break;
@@ -10039,6 +10040,10 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
 	      return FALSE;
 	  }
 	  break;
+	case SEC_INFO_TYPE_EH_FRAME_ENTRY:
+	    if (! _bfd_elf_write_section_eh_frame_entry (output_bfd, o, contents))
+	      return FALSE;
+	  break;
 	default:
 	  {
 	    /* FIXME: octets_per_byte.  */
@@ -10901,6 +10906,8 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
 	return FALSE;
     }
 
+  _bfd_elf_fixup_eh_frame_hdr (info);
+
   /* Since ELF permits relocations to be against local symbols, we
      must have the local symbols available when we do the relocations.
      Since we would rather only read the local symbols once, and we
@@ -11807,6 +11814,13 @@ _bfd_elf_gc_mark (struct bfd_link_info *info,
 	}
     }
 
+  eh_frame = elf_section_eh_frame_entry (sec);
+  if (eh_frame && !eh_frame->gc_mark)
+    {
+    if (!_bfd_elf_gc_mark (info, eh_frame, gc_mark_hook))
+      return FALSE;
+    }
+
   return ret;
 }
 
@@ -12190,22 +12204,42 @@ bfd_elf_gc_sections (bfd *abfd, struct bfd_link_info *info)
   bed->gc_keep (info);
 
   /* Try to parse each bfd's .eh_frame section.  Point elf_eh_frame_section
-     at the .eh_frame section if we can mark the FDEs individually.  */
+     at the .eh_frame section if we can mark the FDEs individually.
+     Establish links from text sections to their corresponding
+     .eh_frame_entry sections.  */
   _bfd_elf_begin_eh_frame_parsing (info);
   for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
     {
       asection *sec;
       struct elf_reloc_cookie cookie;
 
-      sec = bfd_get_section_by_name (sub, ".eh_frame");
-      while (sec && init_reloc_cookie_for_section (&cookie, info, sec))
+      if (!init_reloc_cookie (&cookie, info, sub))
+	return FALSE;
+
+      if (info->eh_frame_hdr < 2)
 	{
-	  _bfd_elf_parse_eh_frame (sub, info, sec, &cookie);
-	  if (elf_section_data (sec)->sec_info
-	      && (sec->flags & SEC_LINKER_CREATED) == 0)
-	    elf_eh_frame_section (sub) = sec;
-	  fini_reloc_cookie_for_section (&cookie, sec);
-	  sec = bfd_get_next_section_by_name (sec);
+	  sec = bfd_get_section_by_name (sub, ".eh_frame");
+	  if (sec && init_reloc_cookie_rels (&cookie, info, sub, sec))
+	    {
+	      _bfd_elf_parse_eh_frame (sub, info, sec, &cookie);
+	      if (elf_section_data (sec)->sec_info
+		  && (sec->flags & SEC_LINKER_CREATED) == 0)
+		elf_eh_frame_section (sub) = sec;
+	      fini_reloc_cookie_for_section (&cookie, sec);
+	    }
+	}
+      else
+	{
+	  for (sec = sub->sections; sec; sec = sec->next)
+	    {
+	      if (CONST_STRNEQ (bfd_section_name (sub, sec), ".eh_frame_entry")
+		  && init_reloc_cookie_rels (&cookie, info, sub, sec))
+		{
+		  _bfd_elf_parse_eh_frame_entry (sub, info, sec, &cookie,
+						 FALSE);
+		  fini_reloc_cookie_for_section (&cookie, sec);
+		}
+	    }
 	}
     }
   _bfd_elf_end_eh_frame_parsing (info);
@@ -12664,6 +12698,7 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
 {
   struct elf_reloc_cookie cookie;
   asection *stab, *eh;
+  struct eh_frame_hdr_info *hdr_info;
   const struct elf_backend_data *bed;
   bfd *abfd;
   bfd_boolean ret = FALSE;
@@ -12672,6 +12707,7 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
       || !is_elf_hash_table (info->hash))
     return FALSE;
 
+  hdr_info = &elf_hash_table (info)->eh_info;
   _bfd_elf_begin_eh_frame_parsing (info);
   for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link_next)
     {
@@ -12681,7 +12717,7 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
       bed = get_elf_backend_data (abfd);
 
       eh = NULL;
-      if (!info->relocatable)
+      if ((info->eh_frame_hdr < 2) && !info->relocatable)
 	{
 	  eh = bfd_get_section_by_name (abfd, ".eh_frame");
 	  while (eh != NULL
@@ -12699,7 +12735,8 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
 
       if (stab == NULL
 	  && eh == NULL
-	  && bed->elf_backend_discard_info == NULL)
+	  && bed->elf_backend_discard_info == NULL
+	  && info->eh_frame_hdr != 2)
 	continue;
 
       if (!init_reloc_cookie (&cookie, info, abfd))
@@ -12729,16 +12766,34 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
 	  eh = bfd_get_next_section_by_name (eh);
 	}
 
+      /* All the .eh_frame_entry sections may have been garbage collected
+	 thus causing the .eh_frame_hdr section to be discarded.  Therefore
+	 check that the .eh_frame_hdr section still exists before walking
+	 the sections.  */
+      if (info->eh_frame_hdr == 2 && hdr_info->hdr_sec)
+	{
+	  for (eh = abfd->sections; eh; eh = eh->next)
+	    {
+	      if (eh->output_section == hdr_info->hdr_sec->output_section
+		  && init_reloc_cookie_rels (&cookie, info, abfd, eh))
+		{
+		  _bfd_elf_parse_eh_frame_entry (abfd, info, eh, &cookie,
+						 TRUE);
+		  fini_reloc_cookie_rels (&cookie, eh);
+		}
+	    }
+	}
+
       if (bed->elf_backend_discard_info != NULL
 	  && (*bed->elf_backend_discard_info) (abfd, &cookie, info))
 	ret = TRUE;
 
       fini_reloc_cookie (&cookie, abfd);
     }
-  _bfd_elf_end_eh_frame_parsing (info);
+  if (_bfd_elf_end_eh_frame_parsing (info))
+    ret = TRUE;
 
-  if (info->eh_frame_hdr
-      && !info->relocatable
+  if (!info->relocatable
       && _bfd_elf_discard_section_eh_frame_hdr (output_bfd, info))
     ret = TRUE;
 
diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
index f51845b..1dd32dc8 100644
--- a/bfd/elfxx-mips.c
+++ b/bfd/elfxx-mips.c
@@ -36,6 +36,7 @@
 #include "elfxx-mips.h"
 #include "elf/mips.h"
 #include "elf-vxworks.h"
+#include "dwarf2.h"
 
 /* Get the ECOFF swapping routines.  */
 #include "coff/sym.h"
@@ -508,6 +509,9 @@ struct mips_elf_link_hash_table
      returns null.  */
   asection *(*add_stub_section) (const char *, asection *, asection *);
 
+  /* How to interpret R_MIPS_EH.  */
+  int eh_reloc_type;
+
   /* Small local sym cache.  */
   struct sym_cache sym_cache;
 
@@ -5113,6 +5117,17 @@ mips_elf_relocation_needs_la25_stub (bfd *input_bfd, int r_type,
       return FALSE;
     }
 }
+
+/* On some targets R_MIPS_EH is interpreted as R_MIPS_PC32.  */
+static int
+mips_elf_real_reloc (struct bfd_link_info *info, int r_type)
+{
+  struct mips_elf_link_hash_table *htab = mips_elf_hash_table (info);
+  if (r_type == R_MIPS_EH)
+    return htab->eh_reloc_type;
+
+  return r_type;
+}
 \f
 /* Calculate the value produced by the RELOCATION (which comes from
    the INPUT_BFD).  The ADDEND is the addend to use for this
@@ -5188,6 +5203,7 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
   /* Parse the relocation.  */
   r_symndx = ELF_R_SYM (input_bfd, relocation->r_info);
   r_type = ELF_R_TYPE (input_bfd, relocation->r_info);
+  r_type = mips_elf_real_reloc (info, r_type);
   p = (input_section->output_section->vma
        + input_section->output_offset
        + relocation->r_offset);
@@ -5541,6 +5557,7 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
     case R_MICROMIPS_TLS_GD:
     case R_MICROMIPS_TLS_GOTTPREL:
     case R_MICROMIPS_TLS_LDM:
+    case R_MIPS_EH:
       /* Find the index into the GOT where this value is located.  */
       if (tls_ldm_reloc_p (r_type))
 	{
@@ -5875,6 +5892,11 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
       overflowed_p = mips_elf_overflow_p (value, 16);
       break;
 
+    case R_MIPS_EH:
+      value = g;
+      overflowed_p = mips_elf_overflow_p (value, 32);
+      break;
+
     case R_MIPS_GPREL32:
       value = (addend + symbol + gp0 - gp);
       if (!save_addend)
@@ -6041,6 +6063,7 @@ mips_elf_perform_relocation (struct bfd_link_info *info,
   bfd_byte *location;
   int r_type = ELF_R_TYPE (input_bfd, relocation->r_info);
 
+  r_type = mips_elf_real_reloc (info, r_type);
   /* Figure out where the relocation is occurring.  */
   location = contents + relocation->r_offset;
 
@@ -6166,6 +6189,7 @@ mips_elf_create_dynamic_relocation (bfd *output_bfd,
   BFD_ASSERT (htab != NULL);
 
   r_type = ELF_R_TYPE (output_bfd, rel->r_info);
+  r_type = mips_elf_real_reloc (info, r_type);
   dynobj = elf_hash_table (info)->dynobj;
   sreloc = mips_elf_rel_dyn_section (info, FALSE);
   BFD_ASSERT (sreloc != NULL);
@@ -7981,6 +8005,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
       r_symndx = ELF_R_SYM (abfd, rel->r_info);
       r_type = ELF_R_TYPE (abfd, rel->r_info);
+      r_type = mips_elf_real_reloc (info, r_type);
 
       if (r_symndx < extsymoff)
 	h = NULL;
@@ -8055,6 +8080,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	case R_MICROMIPS_TLS_GOTTPREL:
 	case R_MICROMIPS_TLS_GD:
 	case R_MICROMIPS_TLS_LDM:
+	case R_MIPS_EH:
 	  if (dynobj == NULL)
 	    elf_hash_table (info)->dynobj = dynobj = abfd;
 	  if (!mips_elf_create_got_section (dynobj, info))
@@ -8168,7 +8194,8 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
       else if (call_lo16_reloc_p (r_type)
 	       || got_lo16_reloc_p (r_type)
 	       || got_disp_reloc_p (r_type)
-	       || (got16_reloc_p (r_type) && htab->is_vxworks))
+	       || (got16_reloc_p (r_type) && htab->is_vxworks)
+	       || r_type == R_MIPS_EH)
 	{
 	  /* We may need a local GOT entry for this relocation.  We
 	     don't count R_MIPS_GOT_PAGE because we can estimate the
@@ -8276,6 +8303,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
 	case R_MIPS_GOT_DISP:
 	case R_MICROMIPS_GOT_DISP:
+	case R_MIPS_EH:
 	  if (h && !mips_elf_record_global_got_symbol (h, abfd, info,
 						       FALSE, r_type))
 	    return FALSE;
@@ -9685,8 +9713,10 @@ mips_elf_adjust_addend (bfd *output_bfd, struct bfd_link_info *info,
   if (mips_elf_local_relocation_p (input_bfd, rel, local_sections))
     {
       r_type = ELF_R_TYPE (output_bfd, rel->r_info);
+      r_type = mips_elf_real_reloc (info, r_type);
       if (gprel16_reloc_p (r_type)
 	  || r_type == R_MIPS_GPREL32
+	  || r_type == R_MIPS_EH
 	  || literal_reloc_p (r_type))
 	{
 	  rel->r_addend += _bfd_get_gp_value (input_bfd);
@@ -9792,6 +9822,7 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
       struct elf_link_hash_entry *h;
       bfd_boolean rel_reloc;
 
+      r_type = mips_elf_real_reloc (info, r_type);
       rel_reloc = (NEWABI_P (input_bfd)
 		   && mips_elf_rel_relocation_p (input_bfd, input_section,
 						 relocs, rel));
@@ -13562,12 +13593,14 @@ _bfd_mips_elf_use_plts_and_copy_relocs (struct bfd_link_info *info)
 }
 
 /* A function that the linker calls to select between all or only
-   32-bit microMIPS instructions.  */
+   32-bit microMIPS instructions, and the meaning of R_MIPS_EH.  */
 
 void
-_bfd_mips_elf_insn32 (struct bfd_link_info *info, bfd_boolean on)
+_bfd_mips_elf_linker_flags (struct bfd_link_info *info, bfd_boolean insn32,
+			    int eh_reloc)
 {
-  mips_elf_hash_table (info)->insn32 = on;
+  mips_elf_hash_table (info)->insn32 = insn32;
+  mips_elf_hash_table (info)->eh_reloc_type = eh_reloc;
 }
 \f
 /* We need to use a special link routine to handle the .reginfo and
@@ -15352,3 +15385,17 @@ _bfd_mips_post_process_headers (bfd *abfd, struct bfd_link_info *link_info)
 
   _bfd_elf_post_process_headers (abfd, link_info);
 }
+
+int
+_bfd_mips_elf_compact_eh_encoding (struct bfd_link_info *link_info)
+{
+  switch (mips_elf_hash_table (link_info) ->eh_reloc_type)
+    {
+    case R_MIPS_EH:
+      return DW_EH_PE_datarel | DW_EH_PE_indirect | DW_EH_PE_sdata4;
+    case R_MIPS_PC32:
+      return DW_EH_PE_pcrel | DW_EH_PE_sdata4;
+    default:
+      abort();
+    }
+}
diff --git a/bfd/elfxx-mips.h b/bfd/elfxx-mips.h
index f27dc15..4060142 100644
--- a/bfd/elfxx-mips.h
+++ b/bfd/elfxx-mips.h
@@ -147,8 +147,8 @@ extern bfd_boolean _bfd_mips_elf_ignore_undef_symbol
   (struct elf_link_hash_entry *);
 extern void _bfd_mips_elf_use_plts_and_copy_relocs
   (struct bfd_link_info *);
-extern void _bfd_mips_elf_insn32
-  (struct bfd_link_info *, bfd_boolean);
+extern void _bfd_mips_elf_linker_flags
+  (struct bfd_link_info *, bfd_boolean, int);
 extern bfd_boolean _bfd_mips_elf_init_stubs
   (struct bfd_link_info *,
    asection *(*) (const char *, asection *, asection *));
@@ -163,6 +163,8 @@ extern const struct bfd_elf_special_section _bfd_mips_elf_special_sections [];
 
 extern bfd_boolean _bfd_mips_elf_common_definition (Elf_Internal_Sym *);
 
+extern int _bfd_mips_elf_compact_eh_encoding (struct bfd_link_info *);
+
 static inline bfd_boolean
 gprel16_reloc_p (unsigned int r_type)
 {
@@ -186,3 +188,4 @@ literal_reloc_p (int r_type)
 #define elf_backend_merge_symbol_attribute  _bfd_mips_elf_merge_symbol_attribute
 #define elf_backend_ignore_undef_symbol _bfd_mips_elf_ignore_undef_symbol
 #define elf_backend_post_process_headers _bfd_mips_post_process_headers
+#define elf_backend_compact_eh_encoding _bfd_mips_elf_compact_eh_encoding
diff --git a/bfd/elfxx-target.h b/bfd/elfxx-target.h
index 2e7cbca..8533a69 100644
--- a/bfd/elfxx-target.h
+++ b/bfd/elfxx-target.h
@@ -664,6 +664,10 @@
 #define elf_backend_maybe_function_sym _bfd_elf_maybe_function_sym
 #endif
 
+#ifndef elf_backend_compact_eh_encoding
+#define elf_backend_compact_eh_encoding NULL
+#endif
+
 #ifndef elf_match_priority
 #define elf_match_priority \
   (ELF_ARCH == bfd_arch_unknown ? 2 : ELF_OSABI == ELFOSABI_NONE ? 1 : 0)
@@ -774,6 +778,7 @@ static struct elf_backend_data elfNN_bed =
   elf_backend_obj_attrs_section_type,
   elf_backend_obj_attrs_order,
   elf_backend_obj_attrs_handle_unknown,
+  elf_backend_compact_eh_encoding,
   elf_backend_static_tls_alignment,
   elf_backend_stack_align,
   elf_backend_collect,
diff --git a/bfd/section.c b/bfd/section.c
index d21df1c..f79f8c0 100644
--- a/bfd/section.c
+++ b/bfd/section.c
@@ -389,6 +389,7 @@ CODE_FRAGMENT
 .#define SEC_INFO_TYPE_EH_FRAME  3
 .#define SEC_INFO_TYPE_JUST_SYMS 4
 .#define SEC_INFO_TYPE_TARGET    5
+.#define SEC_INFO_TYPE_EH_FRAME_ENTRY 6
 .
 .  {* Nonzero if this section uses RELA relocations, rather than REL.  *}
 .  unsigned int use_rela_p:1;
diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c
index 34f1bf0..90688f1 100644
--- a/gas/config/tc-mips.c
+++ b/gas/config/tc-mips.c
@@ -1265,6 +1265,7 @@ static void s_dtpreldword (int);
 static void s_tprelword (int);
 static void s_tpreldword (int);
 static void s_gpvalue (int);
+static void s_force_gpword (int);
 static void s_gpword (int);
 static void s_gpdword (int);
 static void s_ehword (int);
@@ -1648,6 +1649,7 @@ static const pseudo_typeS mips_pseudo_table[] =
   {"tprelword", s_tprelword, 0},
   {"tpreldword", s_tpreldword, 0},
   {"gpvalue", s_gpvalue, 0},
+  {"forcegpword", s_force_gpword, 0},
   {"gpword", s_gpword, 0},
   {"gpdword", s_gpdword, 0},
   {"ehword", s_ehword, 0},
@@ -15574,20 +15576,13 @@ s_gpvalue (int ignore ATTRIBUTE_UNUSED)
    code.  It generates a 32 bit GP relative reloc.  */
 
 static void
-s_gpword (int ignore ATTRIBUTE_UNUSED)
+s_force_gpword (int ignore ATTRIBUTE_UNUSED)
 {
   segment_info_type *si;
   struct insn_label_list *l;
   expressionS ex;
   char *p;
 
-  /* When not generating PIC code, this is treated as .word.  */
-  if (mips_pic != SVR4_PIC)
-    {
-      s_cons (2);
-      return;
-    }
-
   si = seg_info (now_seg);
   l = si->label_list;
   mips_emit_delays ();
@@ -15653,6 +15648,18 @@ s_gpdword (int ignore ATTRIBUTE_UNUSED)
   demand_empty_rest_of_line ();
 }
 
+static void
+s_gpword (int ignore ATTRIBUTE_UNUSED)
+{
+  /* When not generating PIC code, this is treated as .word.  */
+  if (mips_pic != SVR4_PIC)
+    {
+      s_cons (2);
+      return;
+    }
+  s_force_gpword (ignore);
+}
+
 /* Handle the .ehword pseudo-op.  This is used when generating unwinding
    tables.  It generates a R_MIPS_EH reloc.  */
 
@@ -15676,7 +15683,7 @@ s_ehword (int ignore ATTRIBUTE_UNUSED)
   p = frag_more (4);
   md_number_to_chars (p, 0, 4);
   fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &ex, FALSE,
-	       BFD_RELOC_MIPS_EH);
+	       BFD_RELOC_32_PCREL);
 
   demand_empty_rest_of_line ();
 }
@@ -18249,3 +18256,13 @@ tc_mips_regname_to_dw2regnum (char *regname)
 
   return regnum;
 }
+
+#if defined (OBJ_ELF)
+bfd_reloc_code_real_type
+mips_cfi_reloc_for_encoding (int encoding)
+{
+  if ((encoding & 0x70) == DW_EH_PE_datarel)
+    return BFD_RELOC_GPREL32;
+  return BFD_RELOC_32_PCREL;
+}
+#endif
diff --git a/gas/config/tc-mips.h b/gas/config/tc-mips.h
index c7eaa04..a300062 100644
--- a/gas/config/tc-mips.h
+++ b/gas/config/tc-mips.h
@@ -177,7 +177,9 @@ extern enum dwarf2_format mips_dwarf2_format (asection *);
 
 extern int mips_dwarf2_addr_size (void);
 #define DWARF2_ADDR_SIZE(bfd) mips_dwarf2_addr_size ()
-#define DWARF2_FDE_RELOC_SIZE mips_dwarf2_addr_size ()
+#define DWARF2_FDE_RELOC_SIZE (compact_eh ? 4 : mips_dwarf2_addr_size ())
+#define DWARF2_FDE_RELOC_ENCODING(enc) \
+  (enc | (compact_eh ? DW_EH_PE_pcrel : 0))
 
 #define TARGET_USE_CFIPOP 1
 
@@ -190,4 +192,18 @@ extern int tc_mips_regname_to_dw2regnum (char *regname);
 #define DWARF2_DEFAULT_RETURN_COLUMN 31
 #define DWARF2_CIE_DATA_ALIGNMENT (-4)
 
+#if defined (OBJ_ELF)
+
+#define tc_cfi_reloc_for_encoding mips_cfi_reloc_for_encoding
+extern bfd_reloc_code_real_type mips_cfi_reloc_for_encoding (int encoding);
+
+#define tc_cfi_special_encoding(e)				   \
+  ((e) == (DW_EH_PE_sdata4 | DW_EH_PE_datarel | DW_EH_PE_indirect) \
+   || (e) == (DW_EH_PE_sdata4 | DW_EH_PE_pcrel))
+
+#define tc_compact_eh_opcode_stop 0x5c
+#define tc_compact_eh_opcode_pad 0x5f
+
+#endif /* OBJ_ELF */
+
 #endif /* TC_MIPS */
diff --git a/gas/doc/as.texinfo b/gas/doc/as.texinfo
index 739b5b9..9f452a0 100644
--- a/gas/doc/as.texinfo
+++ b/gas/doc/as.texinfo
@@ -4531,6 +4531,9 @@ if @var{section_list} is @code{.debug_frame}, @code{.debug_frame} is emitted.
 To emit both use @code{.eh_frame, .debug_frame}.  The default if this
 directive is not used is @code{.cfi_sections .eh_frame}.
 
+On targets that support compact unwinding tables these can be generated
+by specifying @code{.eh_frame_entry} instead of @code{.eh_frame}.
+
 @section @code{.cfi_startproc [simple]}
 @cindex @code{cfi_startproc} directive
 @code{.cfi_startproc} is used at the beginning of each function that
@@ -4548,6 +4551,7 @@ unwind entry previously opened by
 @code{.cfi_startproc}, and emits it to @code{.eh_frame}.
 
 @section @code{.cfi_personality @var{encoding} [, @var{exp}]}
+@cindex @code{cfi_personality} directive
 @code{.cfi_personality} defines personality routine and its encoding.
 @var{encoding} must be a constant determining how the personality
 should be encoded.  If it is 255 (@code{DW_EH_PE_omit}), second
@@ -4558,6 +4562,22 @@ can be loaded from, not the personality routine itself.
 The default after @code{.cfi_startproc} is @code{.cfi_personality 0xff},
 no personality routine.
 
+@section @code{.cfi_personality_id @var{id}}
+@cindex @code{cfi_personality_id} directive
+@code{cfi_personality_id} defines a personality routine by its index as
+defined in a compact unwinding format.
+Only valid when generating compact EH frames (i.e.
+with @code{.cfi_sections eh_frame_entry}.
+
+@section @code{.cfi_fde_data [@var{opcode1} [, @dots{}]]}
+@cindex @code{cfi_fde_data} directive
+@code{cfi_fde_data} is used to describe the compact unwind opcodes to be
+used for the current function.  These are emitted inline in the
+@code{.eh_frame_entry} section if small enough and there is no LSDA, or
+in the @code{.gnu.extab} section otherwise.
+Only valid when generating compact EH frames (i.e.
+with @code{.cfi_sections eh_frame_entry}.
+
 @section @code{.cfi_lsda @var{encoding} [, @var{exp}]}
 @code{.cfi_lsda} defines LSDA and its encoding.
 @var{encoding} must be a constant determining how the LSDA
@@ -4566,6 +4586,22 @@ argument is not present, otherwise second argument should be a constant
 or a symbol name.  The default after @code{.cfi_startproc} is @code{.cfi_lsda 0xff},
 no LSDA.
 
+@section @code{.cfi_inline_lsda} [@var{align}]
+@code{.cfi_inline_lsda} marks the start of a LSDA data section and
+switches to the corresponding @code{.gnu.extab} section.
+Must be preceded by a CFI block containing a @code{.cfi_lsda} directive.
+Only valid when generating compact EH frames (i.e.
+with @code{.cfi_sections eh_frame_entry}.
+
+The table header and unwinding opcodes will be generated at this point,
+so that they are immediately followed by the LSDA data.  The symbol
+referenced by the @code{.cfi_lsda} directive should still be defined
+in case a fallback FDE based encoding is used.
+
+The optional @var{align} argument specifies the alignment required.
+The alinment is specified as a power of two, as with the
+@code{.p2align} directive.
+
 @section @code{.cfi_def_cfa @var{register}, @var{offset}}
 @code{.cfi_def_cfa} defines a rule for computing CFA as: @i{take
 address from @var{register} and add @var{offset} to it}.
@@ -4627,7 +4663,7 @@ Mark current function as signal trampoline.
 @section @code{.cfi_window_save}
 SPARC register window has been saved.
 
-@section @code{.cfi_escape} @var{expression}[, @dots{}]
+@section @code{.cfi_escape @var{expression}[, @dots{}]}
 Allows the user to add arbitrary bytes to the unwind info.  One
 might use this to add OS-specific CFI opcodes, or generic CFI
 opcodes that GAS does not yet support.
@@ -4643,6 +4679,20 @@ mark a code segment that has only one return address which is reached
 by a direct branch and no copy of the return address exists in memory
 or another register.
 
+@section @code{.cfi_epilogue_begin}
+A pseudo-operation which marks the beginning of the epilogue in a 
+given function.  This is currently used by the compact unwind-table
+implementation (for exception handling), which assumes a single register
+state for unwinding throughout the body of a function.  The function is
+scanned, and CFI directives are rewritten in a compact form.  However,
+recent versions of GCC emit unwind information for function epilogues,
+which should not be represented in this compact form: hence, any CFI
+directives which occur in a function after @code{.cfi_epilogue_end}
+are not processed.
+
+This operation only affects the internals of the assembler, and is not
+represented in the binary output.
+
 @node Comm
 @section @code{.comm @var{symbol} , @var{length} }
 
diff --git a/gas/dw2gencfi.c b/gas/dw2gencfi.c
index faa8384..cbfadc5 100644
--- a/gas/dw2gencfi.c
+++ b/gas/dw2gencfi.c
@@ -76,6 +76,8 @@
 # define tc_cfi_endproc(fde) ((void) (fde))
 #endif
 
+#define EH_FRAME_LINKONCE (SUPPORT_FRAME_LINKONCE || compact_eh)
+
 #ifndef DWARF2_FORMAT
 #define DWARF2_FORMAT(SEC) dwarf2_format_32bit
 #endif
@@ -84,7 +86,7 @@
 #define DWARF2_ADDR_SIZE(bfd) (bfd_arch_bits_per_address (bfd) / 8)
 #endif
 
-#if SUPPORT_FRAME_LINKONCE
+#if MULTIPLE_FRAME_SECTIONS
 #define CUR_SEG(structp) structp->cur_seg
 #define SET_CUR_SEG(structp, seg) structp->cur_seg = seg
 #define HANDLED(structp) structp->handled
@@ -96,6 +98,11 @@
 #define SET_HANDLED(structp, val) (void) (0 && val)
 #endif
 
+#ifndef tc_cfi_special_encoding
+#define tc_cfi_special_encoding(e) 0
+#define tc_cfi_reloc_for_encoding(e) BFD_RELOC_NONE
+#endif
+
 /* Private segment collection list.  */
 struct dwcfi_seg_list
 {
@@ -104,10 +111,116 @@ struct dwcfi_seg_list
   char * seg_name;
 };
 
-#define FRAME_NAME ".eh_frame"
+#ifdef SUPPORT_COMPACT_EH
+static bfd_boolean compact_eh;
+#else
+#define compact_eh 0
+#endif
 
 static struct hash_control *dwcfi_hash;
+\f
+/* Emit a single byte into the current segment.  */
+
+static inline void
+out_one (int byte)
+{
+  FRAG_APPEND_1_CHAR (byte);
+}
 
+/* Emit a two-byte word into the current segment.  */
+
+static inline void
+out_two (int data)
+{
+  md_number_to_chars (frag_more (2), data, 2);
+}
+
+/* Emit a four byte word into the current segment.  */
+
+static inline void
+out_four (int data)
+{
+  md_number_to_chars (frag_more (4), data, 4);
+}
+
+/* Emit an unsigned "little-endian base 128" number.  */
+
+static void
+out_uleb128 (addressT value)
+{
+  output_leb128 (frag_more (sizeof_leb128 (value, 0)), value, 0);
+}
+
+/* Emit an unsigned "little-endian base 128" number.  */
+
+static void
+out_sleb128 (offsetT value)
+{
+  output_leb128 (frag_more (sizeof_leb128 (value, 1)), value, 1);
+}
+
+/* Return the value of the EH encoding size.  */
+
+static offsetT
+encoding_size (unsigned char encoding)
+{
+  if (encoding == DW_EH_PE_omit)
+    return 0;
+  switch (encoding & 0x7)
+    {
+    case 0:
+      return bfd_get_arch_size (stdoutput) == 64 ? 8 : 4;
+    case DW_EH_PE_udata2:
+      return 2;
+    case DW_EH_PE_udata4:
+      return 4;
+    case DW_EH_PE_udata8:
+      return 8;
+    default:
+      abort ();
+    }
+}
+
+/* Emit expression EXP in ENCODING.  If EMIT_ENCODING is true, first
+   emit a byte containing ENCODING.  */
+
+static void
+emit_expr_encoded (expressionS *exp, int encoding, bfd_boolean emit_encoding)
+{
+  offsetT size = encoding_size (encoding);
+
+  if (encoding == DW_EH_PE_omit)
+    return;
+
+  if (emit_encoding)
+    out_one (encoding);
+  if (tc_cfi_special_encoding (encoding))
+    {
+      bfd_reloc_code_real_type code
+	= tc_cfi_reloc_for_encoding (encoding);
+      reloc_howto_type *howto = bfd_reloc_type_lookup (stdoutput, code);
+      char *p = frag_more(size);
+      md_number_to_chars (p, 0, size);
+      fix_new (frag_now, p - frag_now->fr_literal, size, exp->X_add_symbol,
+	       exp->X_add_number, howto->pc_relative, code);
+    }
+  else if ((encoding & 0x70) == DW_EH_PE_pcrel)
+    {
+#if CFI_DIFF_EXPR_OK
+      expressionS tmp = *exp;
+      tmp.X_op = O_subtract;
+      tmp.X_op_symbol = symbol_temp_new_now ();
+      emit_expr (&tmp, size);
+#elif defined (tc_cfi_emit_pcrel_expr)
+      tc_cfi_emit_pcrel_expr (exp, size);
+#else
+      abort ();
+#endif
+    }
+  else
+    emit_expr (exp, size);
+}
+\f
 /* Build based on segment the derived .debug_...
    segment name containing origin segment's postfix name part.  */
 
@@ -129,7 +242,11 @@ get_debugseg_name (segT seg, const char *base_name)
       dot = strchr (name + 1, '.');
 
       if (!dollar && !dot)
-	name = "";
+	{
+	  if (compact_eh && strcmp (name, ".text") != 0)
+	    return concat (base_name, ".", name, NULL);
+	  name = "";
+	}
       else if (!dollar)
 	name = dot;
       else if (!dot)
@@ -161,6 +278,9 @@ alloc_debugseg_item (segT seg, int subseg, char *name)
 static segT
 is_now_linkonce_segment (void)
 {
+  if (compact_eh)
+    return now_seg;
+
   if ((bfd_get_section_flags (stdoutput, now_seg)
        & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD
 	  | SEC_LINK_DUPLICATES_ONE_ONLY | SEC_LINK_DUPLICATES_SAME_SIZE
@@ -273,12 +393,13 @@ struct cfi_escape_data
 struct cie_entry
 {
   struct cie_entry *next;
-#if SUPPORT_FRAME_LINKONCE
+#if MULTIPLE_FRAME_SECTIONS
   segT cur_seg;
 #endif
   symbolS *start_address;
   unsigned int return_column;
   unsigned int signal_frame;
+  unsigned char fde_encoding;
   unsigned char per_encoding;
   unsigned char lsda_encoding;
   expressionS personality;
@@ -548,15 +669,19 @@ static void dot_cfi_escape (int);
 static void dot_cfi_sections (int);
 static void dot_cfi_startproc (int);
 static void dot_cfi_endproc (int);
+static void dot_cfi_fde_data (int);
 static void dot_cfi_personality (int);
+static void dot_cfi_personality_id (int);
 static void dot_cfi_lsda (int);
 static void dot_cfi_val_encoded_addr (int);
+static void dot_cfi_inline_lsda (int);
 
 const pseudo_typeS cfi_pseudo_table[] =
   {
     { "cfi_sections", dot_cfi_sections, 0 },
     { "cfi_startproc", dot_cfi_startproc, 0 },
     { "cfi_endproc", dot_cfi_endproc, 0 },
+    { "cfi_fde_data", dot_cfi_fde_data, 0 },
     { "cfi_def_cfa", dot_cfi, DW_CFA_def_cfa },
     { "cfi_def_cfa_register", dot_cfi, DW_CFA_def_cfa_register },
     { "cfi_def_cfa_offset", dot_cfi, DW_CFA_def_cfa_offset },
@@ -574,8 +699,11 @@ const pseudo_typeS cfi_pseudo_table[] =
     { "cfi_escape", dot_cfi_escape, 0 },
     { "cfi_signal_frame", dot_cfi, CFI_signal_frame },
     { "cfi_personality", dot_cfi_personality, 0 },
+    { "cfi_personality_id", dot_cfi_personality_id, 0 },
     { "cfi_lsda", dot_cfi_lsda, 0 },
     { "cfi_val_encoded_addr", dot_cfi_val_encoded_addr, 0 },
+    { "cfi_inline_lsda", dot_cfi_inline_lsda, 0 },
+    { "cfi_epilogue_begin", dot_cfi, CFI_epilogue_begin},
     { NULL, NULL, 0 }
   };
 
@@ -765,6 +893,10 @@ dot_cfi (int arg)
       frchain_now->frch_cfi_data->cur_fde_data->signal_frame = 1;
       break;
 
+    case CFI_epilogue_begin:
+      cfi_add_CFA_insn (CFI_epilogue_begin);
+      break;
+
     default:
       abort ();
     }
@@ -833,14 +965,15 @@ dot_cfi_personality (int ignored ATTRIBUTE_UNUSED)
     }
 
   if ((encoding & 0xff) != encoding
-      || ((encoding & 0x70) != 0
+      || ((((encoding & 0x70) != 0
 #if CFI_DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr
-	  && (encoding & 0x70) != DW_EH_PE_pcrel
+	   && (encoding & 0x70) != DW_EH_PE_pcrel
 #endif
 	  )
 	 /* leb128 can be handled, but does something actually need it?  */
-      || (encoding & 7) == DW_EH_PE_uleb128
-      || (encoding & 7) > DW_EH_PE_udata8)
+	   || (encoding & 7) == DW_EH_PE_uleb128
+	   || (encoding & 7) > DW_EH_PE_udata8)
+	&& !tc_cfi_special_encoding (encoding)))
     {
       as_bad (_("invalid or unsupported encoding in .cfi_personality"));
       ignore_rest_of_line ();
@@ -903,14 +1036,15 @@ dot_cfi_lsda (int ignored ATTRIBUTE_UNUSED)
     }
 
   if ((encoding & 0xff) != encoding
-      || ((encoding & 0x70) != 0
+      || ((((encoding & 0x70) != 0
 #if CFI_DIFF_LSDA_OK || defined tc_cfi_emit_pcrel_expr
-	  && (encoding & 0x70) != DW_EH_PE_pcrel
+	    && (encoding & 0x70) != DW_EH_PE_pcrel
 #endif
-	  )
+	   )
 	 /* leb128 can be handled, but does something actually need it?  */
-      || (encoding & 7) == DW_EH_PE_uleb128
-      || (encoding & 7) > DW_EH_PE_udata8)
+	   || (encoding & 7) == DW_EH_PE_uleb128
+	   || (encoding & 7) > DW_EH_PE_udata8)
+	  && !tc_cfi_special_encoding (encoding)))
     {
       as_bad (_("invalid or unsupported encoding in .cfi_lsda"));
       ignore_rest_of_line ();
@@ -1021,7 +1155,10 @@ dot_cfi_val_encoded_addr (int ignored ATTRIBUTE_UNUSED)
 #define CFI_EMIT_eh_frame	(1 << 0)
 #define CFI_EMIT_debug_frame	(1 << 1)
 #define CFI_EMIT_target		(1 << 2)
+static bfd_boolean cfi_sections_set = FALSE;
 static int cfi_sections = CFI_EMIT_eh_frame;
+static int all_cfi_sections = 0;
+static struct fde_entry *last_fde;
 
 static void
 dot_cfi_sections (int ignored ATTRIBUTE_UNUSED)
@@ -1042,6 +1179,13 @@ dot_cfi_sections (int ignored ATTRIBUTE_UNUSED)
 	  sections |= CFI_EMIT_eh_frame;
 	else if (strncmp (name, ".debug_frame", sizeof ".debug_frame") == 0)
 	  sections |= CFI_EMIT_debug_frame;
+#if SUPPORT_COMPACT_EH
+	else if (strncmp (name, ".eh_frame_entry", sizeof ".eh_frame_entry") == 0)
+	  {
+	    compact_eh = TRUE;
+	    sections |= CFI_EMIT_eh_frame | CFI_EMIT_target;
+	  }
+#endif
 #ifdef tc_cfi_section_name
 	else if (strcmp (name, tc_cfi_section_name) == 0)
 	  sections |= CFI_EMIT_target;
@@ -1070,6 +1214,9 @@ dot_cfi_sections (int ignored ATTRIBUTE_UNUSED)
       }
 
   demand_empty_rest_of_line ();
+  if (cfi_sections_set && cfi_sections != sections)
+    as_bad (_("inconsistent uses of .cfi_sections"));
+  cfi_sections_set = TRUE;
   cfi_sections = sections;
 }
 
@@ -1105,6 +1252,8 @@ dot_cfi_startproc (int ignored ATTRIBUTE_UNUSED)
     }
   demand_empty_rest_of_line ();
 
+  all_cfi_sections |= cfi_sections;
+  frchain_now->frch_cfi_data->cur_fde_data->sections = cfi_sections;
   frchain_now->frch_cfi_data->cur_cfa_offset = 0;
   if (!simple)
     tc_cfi_frame_initial_instructions ();
@@ -1116,8 +1265,6 @@ dot_cfi_startproc (int ignored ATTRIBUTE_UNUSED)
 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"));
@@ -1125,58 +1272,254 @@ dot_cfi_endproc (int ignored ATTRIBUTE_UNUSED)
       return;
     }
 
-  fde = frchain_now->frch_cfi_data->cur_fde_data;
+  last_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);
+    tc_cfi_endproc (last_fde);
 }
 
-\f
-/* Emit a single byte into the current segment.  */
-
-static inline void
-out_one (int byte)
+static segT
+get_cfi_seg (segT cseg, const char *base, flagword flags, int align)
 {
-  FRAG_APPEND_1_CHAR (byte);
+  if (SUPPORT_FRAME_LINKONCE || ((flags & SEC_DEBUGGING) == 0 && compact_eh))
+    {
+      struct dwcfi_seg_list *l;
+
+      l = dwcfi_hash_find_or_make (cseg, base, flags);
+
+      cseg = l->seg;
+      subseg_set (cseg, l->subseg);
+    }
+  else
+    {
+      cseg = subseg_new (base, 0);
+      bfd_set_section_flags (stdoutput, cseg, flags);
+    }
+  record_alignment (cseg, align);
+  return cseg;
 }
 
-/* Emit a two-byte word into the current segment.  */
+#if SUPPORT_COMPACT_EH
 
-static inline void
-out_two (int data)
+/* Set the personality id in the FDE structure.  */
+
+static void
+dot_cfi_personality_id (int ignored ATTRIBUTE_UNUSED)
 {
-  md_number_to_chars (frag_more (2), data, 2);
+  struct fde_entry *fde;
+
+  if (frchain_now->frch_cfi_data == NULL)
+    {
+      as_bad (_("CFI instruction used without previous .cfi_startproc"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  fde = frchain_now->frch_cfi_data->cur_fde_data;
+  fde->personality_id = cfi_parse_const ();
+  demand_empty_rest_of_line ();
+
+  if (fde->personality_id == 0 || fde->personality_id > 3)
+    {
+      as_bad (_("wrong argument to .cfi_personality_id"));
+      return;
+    }
 }
 
-/* Emit a four byte word into the current segment.  */
+static void
+dot_cfi_fde_data (int ignored ATTRIBUTE_UNUSED)
+{
+  struct cfi_escape_data *head, **tail, *e;
+  int num_ops = 0;
 
-static inline void
-out_four (int data)
+  if (frchain_now->frch_cfi_data == NULL)
+    {
+      as_bad (_(".cfi_fde_data without corresponding .cfi_startproc"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  last_fde = frchain_now->frch_cfi_data->cur_fde_data;
+
+  if (!compact_eh || (cfi_sections & CFI_EMIT_eh_frame) == 0)
+    as_bad (_(".cfi_fde_data not valid for this frame"));
+
+  tail = &head;
+  if (!is_it_end_of_statement ())
+    {
+      num_ops = 0;
+      do
+	{
+	  e = (struct cfi_escape_data *) xmalloc (sizeof (*e));
+	  do_parse_cons_expression (&e->exp, 1);
+	  *tail = e;
+	  tail = &e->next;
+	  num_ops++;
+	}
+      while (*input_line_pointer++ == ',');
+      --input_line_pointer;
+    }
+  *tail = NULL;
+
+  if (last_fde->lsda_encoding != DW_EH_PE_omit)
+    last_fde->eh_header_type = EH_COMPACT_HAS_LSDA;
+  else if (num_ops <= 3 && last_fde->per_encoding == DW_EH_PE_omit)
+    last_fde->eh_header_type = EH_COMPACT_INLINE;
+  else
+    last_fde->eh_header_type = EH_COMPACT_OUTLINE;
+
+  if (last_fde->eh_header_type == EH_COMPACT_INLINE)
+    num_ops = 3;
+
+  last_fde->eh_data_size = num_ops;
+  last_fde->eh_data = (bfd_byte *) xmalloc (num_ops);
+  num_ops = 0;
+  while (head)
+    {
+      e = head;
+      head = e->next;
+      last_fde->eh_data[num_ops++] = e->exp.X_add_number;
+      free (e);
+    }
+  if (last_fde->eh_header_type == EH_COMPACT_INLINE)
+    while (num_ops < 3)
+      last_fde->eh_data[num_ops++] = tc_compact_eh_opcode_stop;
+
+  demand_empty_rest_of_line ();
+}
+
+static void
+output_compact_unwind_data (struct fde_entry *fde, int align)
 {
-  md_number_to_chars (frag_more (4), data, 4);
+  int data_size = fde->eh_data_size + 2;
+  int align_padding;
+  int amask;
+  char *p;
+
+  fde->eh_loc = symbol_temp_new_now ();
+
+  p = frag_more (1);
+  if (fde->personality_id != 0)
+    *p = fde->personality_id;
+  else if (fde->per_encoding != DW_EH_PE_omit)
+    {
+      *p = 0;
+      emit_expr_encoded (&fde->personality, fde->per_encoding, FALSE);
+      data_size += encoding_size (fde->per_encoding);
+    }
+  else
+    *p = 1;
+
+  amask = (1 << align) - 1;
+  align_padding = ((data_size + amask) & ~amask) - data_size;
+
+  p = frag_more (fde->eh_data_size + 1 + align_padding);
+  memcpy (p, fde->eh_data, fde->eh_data_size);
+  p += fde->eh_data_size;
+
+  while (align_padding-- > 0)
+    *(p++) = tc_compact_eh_opcode_pad;
+
+  *(p++) = tc_compact_eh_opcode_stop;
+  fde->eh_header_type = EH_COMPACT_OUTLINE_DONE;
 }
 
-/* Emit an unsigned "little-endian base 128" number.  */
+static void
+dot_cfi_inline_lsda (int ignored ATTRIBUTE_UNUSED)
+{
+  segT cfi_seg, ccseg;
+  int align;
+  long max_alignment = 28;
+
+  if (!last_fde)
+    {
+      as_bad (_("unexpected .cfi_inline_lsda"));
+      ignore_rest_of_line ();
+      return;
+    }
 
+  if (!compact_eh
+      || (last_fde->sections & CFI_EMIT_eh_frame) == 0)
+    {
+      as_bad (_(".cfi_inline_lsda not valid for this frame"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  if (last_fde->eh_header_type != EH_COMPACT_LEGACY
+      && last_fde->eh_header_type != EH_COMPACT_HAS_LSDA)
+    {
+      as_bad (_(".cfi_inline_lsda seen for frame without .cfi_lsda"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+#ifdef md_flush_pending_output
+  md_flush_pending_output ();
+#endif
+
+  align = get_absolute_expression ();
+  if (align > max_alignment)
+    {
+      align = max_alignment;
+      as_bad (_("Alignment too large: %d. assumed."), align);
+    }
+  else if (align < 0)
+    {
+      as_warn (_("Alignment negative: 0 assumed."));
+      align = 0;
+    }
+
+  demand_empty_rest_of_line ();
+  ccseg = CUR_SEG (last_fde);
+  /* Open .gnu_extab section.  */
+  cfi_seg = get_cfi_seg (ccseg, ".gnu_extab",
+			 (SEC_ALLOC | SEC_LOAD | SEC_DATA
+			  | DWARF2_EH_FRAME_READ_ONLY),
+			 1);
+#ifdef md_fix_up_eh_frame
+  md_fix_up_eh_frame (cfi_seg);
+#else
+  (void) cfi_seg;
+#endif
+
+  frag_align (align, 0, 0);
+  record_alignment (now_seg, align);
+  if (last_fde->eh_header_type == EH_COMPACT_HAS_LSDA)
+    output_compact_unwind_data (last_fde, align);
+
+  last_fde = NULL;
+
+  return;
+}
+#else /* !SUPPORT_COMPACT_EH */
 static void
-out_uleb128 (addressT value)
+dot_cfi_inline_lsda (int ignored ATTRIBUTE_UNUSED)
 {
-  output_leb128 (frag_more (sizeof_leb128 (value, 0)), value, 0);
+  as_bad (_(".cfi_inline_lsda is not supported for this target"));
+  ignore_rest_of_line ();
 }
 
-/* Emit an unsigned "little-endian base 128" number.  */
-
 static void
-out_sleb128 (offsetT value)
+dot_cfi_fde_data (int ignored ATTRIBUTE_UNUSED)
 {
-  output_leb128 (frag_more (sizeof_leb128 (value, 1)), value, 1);
+  as_bad (_(".cfi_fde_data is not supported for this target"));
+  ignore_rest_of_line ();
 }
 
 static void
+dot_cfi_personality_id (int ignored ATTRIBUTE_UNUSED)
+{
+  as_bad (_(".cfi_personality_id is not supported for this target"));
+  ignore_rest_of_line ();
+}
+#endif
+\f
+static void
 output_cfi_insn (struct cfi_insn_data *insn)
 {
   offsetT offset;
@@ -1332,7 +1675,7 @@ output_cfi_insn (struct cfi_insn_data *insn)
     case CFI_val_encoded_addr:
       {
         unsigned encoding = insn->u.ea.encoding;
-        offsetT encoding_size;
+        offsetT enc_size;
 
 	if (encoding == DW_EH_PE_omit)
 	  break;
@@ -1342,16 +1685,16 @@ output_cfi_insn (struct cfi_insn_data *insn)
         switch (encoding & 0x7)
 	  {
 	  case DW_EH_PE_absptr:
-	    encoding_size = DWARF2_ADDR_SIZE (stdoutput);
+	    enc_size = DWARF2_ADDR_SIZE (stdoutput);
 	    break;
 	  case DW_EH_PE_udata2:
-	    encoding_size = 2;
+	    enc_size = 2;
 	    break;
 	  case DW_EH_PE_udata4:
-	    encoding_size = 4;
+	    enc_size = 4;
 	    break;
 	  case DW_EH_PE_udata8:
-	    encoding_size = 8;
+	    enc_size = 8;
 	    break;
 	  default:
 	    abort ();
@@ -1361,12 +1704,12 @@ output_cfi_insn (struct cfi_insn_data *insn)
 	   then use the smaller DW_OP_addr encoding.  */
 	if (insn->u.ea.encoding == DW_EH_PE_absptr)
 	  {
-	    out_uleb128 (1 + encoding_size);
+	    out_uleb128 (1 + enc_size);
 	    out_one (DW_OP_addr);
 	  }
 	else
 	  {
-	    out_uleb128 (1 + 1 + encoding_size);
+	    out_uleb128 (1 + 1 + enc_size);
 	    out_one (DW_OP_GNU_encoded_addr);
 	    out_one (encoding);
 
@@ -1376,37 +1719,21 @@ output_cfi_insn (struct cfi_insn_data *insn)
 		insn->u.ea.exp.X_op = O_subtract;
 		insn->u.ea.exp.X_op_symbol = symbol_temp_new_now ();
 #elif defined (tc_cfi_emit_pcrel_expr)
-		tc_cfi_emit_pcrel_expr (&insn->u.ea.exp, encoding_size);
+		tc_cfi_emit_pcrel_expr (&insn->u.ea.exp, enc_size);
 		break;
 #else
 		abort ();
 #endif
 	      }
 	  }
-	emit_expr (&insn->u.ea.exp, encoding_size);
+	emit_expr (&insn->u.ea.exp, enc_size);
       }
       break;
 
-    default:
-      abort ();
-    }
-}
+    case CFI_epilogue_begin:
+      /* Nothing to emit.  */
+      break;
 
-static offsetT
-encoding_size (unsigned char encoding)
-{
-  if (encoding == DW_EH_PE_omit)
-    return 0;
-  switch (encoding & 0x7)
-    {
-    case 0:
-      return bfd_get_arch_size (stdoutput) == 64 ? 8 : 4;
-    case DW_EH_PE_udata2:
-      return 2;
-    case DW_EH_PE_udata4:
-      return 4;
-    case DW_EH_PE_udata8:
-      return 8;
     default:
       abort ();
     }
@@ -1474,26 +1801,7 @@ output_cie (struct cie_entry *cie, bfd_boolean eh_frame, int align)
 	augmentation_size += 1 + encoding_size (cie->per_encoding);
       out_uleb128 (augmentation_size);		/* Augmentation size.  */
 
-      if (cie->per_encoding != DW_EH_PE_omit)
-	{
-	  offsetT size = encoding_size (cie->per_encoding);
-	  out_one (cie->per_encoding);
-	  exp = cie->personality;
-	  if ((cie->per_encoding & 0x70) == DW_EH_PE_pcrel)
-	    {
-#if CFI_DIFF_EXPR_OK
-	      exp.X_op = O_subtract;
-	      exp.X_op_symbol = symbol_temp_new_now ();
-	      emit_expr (&exp, size);
-#elif defined (tc_cfi_emit_pcrel_expr)
-	      tc_cfi_emit_pcrel_expr (&exp, size);
-#else
-	      abort ();
-#endif
-	    }
-	  else
-	    emit_expr (&exp, size);
-	}
+      emit_expr_encoded (&cie->personality, cie->per_encoding, TRUE);
 
       if (cie->lsda_encoding != DW_EH_PE_omit)
 	out_one (cie->lsda_encoding);
@@ -1516,6 +1824,11 @@ output_cie (struct cie_entry *cie, bfd_boolean eh_frame, int align)
 #if CFI_DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr
   enc |= DW_EH_PE_pcrel;
 #endif
+#ifdef DWARF2_FDE_RELOC_ENCODING
+  /* Allow target to override encoding.  */
+  enc = DWARF2_FDE_RELOC_ENCODING (enc);
+#endif
+  cie->fde_encoding = enc;
   if (eh_frame)
     out_one (enc);
 
@@ -1576,29 +1889,43 @@ output_fde (struct fde_entry *fde, struct cie_entry *cie,
       TC_DWARF2_EMIT_OFFSET (cie->start_address, offset_size);
     }
 
+  exp.X_op = O_symbol;
+  exp.X_add_symbol = fde->start_address;
   if (eh_frame)
     {
-      exp.X_op = O_subtract;
-      exp.X_add_number = 0;
+      if (tc_cfi_special_encoding (cie->fde_encoding))
+	{
+	  bfd_reloc_code_real_type code
+	    = tc_cfi_reloc_for_encoding (cie->fde_encoding);
+	  reloc_howto_type *howto = bfd_reloc_type_lookup (stdoutput, code);
+	  char *p = frag_more(4);
+	  md_number_to_chars (p, 0, 4);
+	  fix_new (frag_now, p - frag_now->fr_literal, 4, exp.X_add_symbol,
+		   exp.X_add_number, howto->pc_relative, code);
+	}
+      else
+	{
+	  exp.X_op = O_subtract;
+	  exp.X_add_number = 0;
 #if CFI_DIFF_EXPR_OK
-      exp.X_add_symbol = fde->start_address;
-      exp.X_op_symbol = symbol_temp_new_now ();
-      emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code offset.  */
+	  exp.X_add_symbol = fde->start_address;
+	  exp.X_op_symbol = symbol_temp_new_now ();
+	  emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code offset.  */
 #else
-      exp.X_op = O_symbol;
-      exp.X_add_symbol = fde->start_address;
-#ifdef tc_cfi_emit_pcrel_expr
-      tc_cfi_emit_pcrel_expr (&exp, DWARF2_FDE_RELOC_SIZE);	 /* Code offset.  */
+	  exp.X_op = O_symbol;
+	  exp.X_add_symbol = fde->start_address;
+
+#if defined(tc_cfi_emit_pcrel_expr)
+	  tc_cfi_emit_pcrel_expr (&exp, DWARF2_FDE_RELOC_SIZE);	 /* Code offset.  */
 #else
-      emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code offset.  */
+	  emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code offset.  */
 #endif
 #endif
+	}
       addr_size = DWARF2_FDE_RELOC_SIZE;
     }
   else
     {
-      exp.X_op = O_symbol;
-      exp.X_add_symbol = fde->start_address;
       exp.X_add_number = 0;
       addr_size = DWARF2_ADDR_SIZE (stdoutput);
       emit_expr (&exp, addr_size);
@@ -1614,24 +1941,7 @@ output_fde (struct fde_entry *fde, struct cie_entry *cie,
   if (eh_frame)
     out_uleb128 (augmentation_size);		/* Augmentation size.  */
 
-  if (fde->lsda_encoding != DW_EH_PE_omit)
-    {
-      exp = fde->lsda;
-      if ((fde->lsda_encoding & 0x70) == DW_EH_PE_pcrel)
-	{
-#if CFI_DIFF_LSDA_OK
-	  exp.X_op = O_subtract;
-	  exp.X_op_symbol = symbol_temp_new_now ();
-	  emit_expr (&exp, augmentation_size);
-#elif defined (tc_cfi_emit_pcrel_expr)
-	  tc_cfi_emit_pcrel_expr (&exp, augmentation_size);
-#else
-	  abort ();
-#endif
-	}
-      else
-	emit_expr (&exp, augmentation_size);
-    }
+  emit_expr_encoded (&fde->lsda, cie->lsda_encoding, FALSE);
 
   for (; first; first = first->next)
     if (CUR_SEG (first) == CUR_SEG (fde))
@@ -1722,6 +2032,7 @@ select_cie_for_fde (struct fde_entry *fde, bfd_boolean eh_frame,
 
 	    case CFI_escape:
 	    case CFI_val_encoded_addr:
+	    case CFI_epilogue_begin:
 	      /* Don't bother matching these for now.  */
 	      goto fail;
 
@@ -1821,26 +2132,50 @@ cfi_change_reg_numbers (struct cfi_insn_data *insn, segT ccseg)
 #define cfi_change_reg_numbers(insn, cseg) do { } while (0)
 #endif
 
-static segT
-get_cfi_seg (segT cseg, const char *base, flagword flags, int align)
+#if SUPPORT_COMPACT_EH
+static void
+cfi_emit_eh_header (symbolS *sym, bfd_vma addend)
 {
-  if (SUPPORT_FRAME_LINKONCE)
-    {
-      struct dwcfi_seg_list *l;
+  expressionS exp;
 
-      l = dwcfi_hash_find_or_make (cseg, base, flags);
+  exp.X_add_number = addend;
+  exp.X_add_symbol = sym;
+  emit_expr_encoded (&exp, DW_EH_PE_sdata4 | DW_EH_PE_pcrel, FALSE);
+}
 
-      cseg = l->seg;
-      subseg_set (cseg, l->subseg);
+static void
+output_eh_header (struct fde_entry *fde)
+{
+  char *p;
+  bfd_vma addend;
+
+  if (fde->eh_header_type == EH_COMPACT_INLINE)
+    addend = 0;
+  else
+    addend = 1;
+
+  cfi_emit_eh_header (fde->start_address, addend);
+
+  if (fde->eh_header_type == EH_COMPACT_INLINE)
+    {
+      p = frag_more (4);
+      /* Inline entries always use PR1.  */
+      *(p++) = 1;
+      memcpy(p, fde->eh_data, 3);
     }
   else
     {
-      cseg = subseg_new (base, 0);
-      bfd_set_section_flags (stdoutput, cseg, flags);
+      if (fde->eh_header_type == EH_COMPACT_LEGACY)
+	addend = 1;
+      else if (fde->eh_header_type == EH_COMPACT_OUTLINE
+	       || fde->eh_header_type == EH_COMPACT_OUTLINE_DONE)
+	addend = 0;
+      else
+	abort ();
+      cfi_emit_eh_header (fde->eh_loc, addend);
     }
-  record_alignment (cseg, align);
-  return cseg;
 }
+#endif
 
 void
 cfi_finish (void)
@@ -1854,13 +2189,13 @@ cfi_finish (void)
   if (all_fde_data == 0)
     return;
 
-  if ((cfi_sections & CFI_EMIT_eh_frame) != 0)
+  if ((all_cfi_sections & CFI_EMIT_eh_frame) != 0)
     {
       /* Make sure check_eh_frame doesn't do anything with our output.  */
       save_flag_traditional_format = flag_traditional_format;
       flag_traditional_format = 1;
 
-      if (!SUPPORT_FRAME_LINKONCE)
+      if (!EH_FRAME_LINKONCE)
 	{
 	  /* Open .eh_frame section.  */
 	  cfi_seg = get_cfi_seg (NULL, ".eh_frame",
@@ -1888,7 +2223,17 @@ cfi_finish (void)
 
 	  for (fde = all_fde_data; fde ; fde = fde->next)
 	    {
-	      if (SUPPORT_FRAME_LINKONCE)
+	      if ((fde->sections & CFI_EMIT_eh_frame) == 0)
+		continue;
+
+#if SUPPORT_COMPACT_EH
+	      if (fde->eh_header_type == EH_COMPACT_HAS_LSDA)
+		fde->eh_header_type = EH_COMPACT_LEGACY;
+
+	      if (fde->eh_header_type != EH_COMPACT_LEGACY)
+		continue;
+#endif
+	      if (EH_FRAME_LINKONCE)
 		{
 		  if (HANDLED (fde))
 		    continue;
@@ -1922,16 +2267,107 @@ cfi_finish (void)
 		}
 
 	      cie = select_cie_for_fde (fde, TRUE, &first, 2);
+	      fde->eh_loc = symbol_temp_new_now ();
 	      output_fde (fde, cie, TRUE, first,
 			  fde->next == NULL ? EH_FRAME_ALIGNMENT : 2);
 	    }
 	}
-      while (SUPPORT_FRAME_LINKONCE && seek_next_seg == 2);
+      while (EH_FRAME_LINKONCE && seek_next_seg == 2);
 
-      if (SUPPORT_FRAME_LINKONCE)
+      if (EH_FRAME_LINKONCE)
 	for (fde = all_fde_data; fde ; fde = fde->next)
 	  SET_HANDLED (fde, 0);
 
+#if SUPPORT_COMPACT_EH
+      if (compact_eh)
+	{
+	  /* Create remaining out of line table entries.  */
+	  do
+	    {
+	      ccseg = NULL;
+	      seek_next_seg = 0;
+
+	      for (fde = all_fde_data; fde ; fde = fde->next)
+		{
+		  if ((fde->sections & CFI_EMIT_eh_frame) == 0)
+		    continue;
+
+		  if (fde->eh_header_type != EH_COMPACT_OUTLINE)
+		    continue;
+		  if (HANDLED (fde))
+		    continue;
+		  if (seek_next_seg && CUR_SEG (fde) != ccseg)
+		    {
+		      seek_next_seg = 2;
+		      continue;
+		    }
+		  if (!seek_next_seg)
+		    {
+		      ccseg = CUR_SEG (fde);
+		      /* Open .gnu_extab section.  */
+		      cfi_seg = get_cfi_seg (ccseg, ".gnu_extab",
+					     (SEC_ALLOC | SEC_LOAD | SEC_DATA
+					      | DWARF2_EH_FRAME_READ_ONLY),
+					     1);
+#ifdef md_fix_up_eh_frame
+		      md_fix_up_eh_frame (cfi_seg);
+#else
+		      (void) cfi_seg;
+#endif
+		      seek_next_seg = 1;
+		    }
+		  SET_HANDLED (fde, 1);
+
+		  frag_align (1, 0, 0);
+		  record_alignment (now_seg, 1);
+		  output_compact_unwind_data (fde, 1);
+		}
+	    }
+	  while (EH_FRAME_LINKONCE && seek_next_seg == 2);
+
+	  for (fde = all_fde_data; fde ; fde = fde->next)
+	    SET_HANDLED (fde, 0);
+
+	  /* Create index table fragments.  */
+	  do
+	    {
+	      ccseg = NULL;
+	      seek_next_seg = 0;
+
+	      for (fde = all_fde_data; fde ; fde = fde->next)
+		{
+		  if ((fde->sections & CFI_EMIT_eh_frame) == 0)
+		    continue;
+
+		  if (HANDLED (fde))
+		    continue;
+		  if (seek_next_seg && CUR_SEG (fde) != ccseg)
+		    {
+		      seek_next_seg = 2;
+		      continue;
+		    }
+		  if (!seek_next_seg)
+		    {
+		      ccseg = CUR_SEG (fde);
+		      /* Open .eh_frame_entry section.  */
+		      cfi_seg = get_cfi_seg (ccseg, ".eh_frame_entry",
+					     (SEC_ALLOC | SEC_LOAD | SEC_DATA
+					      | DWARF2_EH_FRAME_READ_ONLY),
+					     2);
+		      seek_next_seg = 1;
+		    }
+		  SET_HANDLED (fde, 1);
+
+		  output_eh_header (fde);
+		}
+	    }
+	  while (seek_next_seg == 2);
+
+	  for (fde = all_fde_data; fde ; fde = fde->next)
+	    SET_HANDLED (fde, 0);
+	}
+#endif /* SUPPORT_COMPACT_EH */
+
       flag_traditional_format = save_flag_traditional_format;
     }
 
@@ -1958,6 +2394,9 @@ cfi_finish (void)
 
 	  for (fde = all_fde_data; fde ; fde = fde->next)
 	    {
+	      if ((fde->sections & CFI_EMIT_debug_frame) == 0)
+		continue;
+
 	      if (SUPPORT_FRAME_LINKONCE)
 		{
 		  if (HANDLED (fde))
@@ -2015,6 +2454,7 @@ const pseudo_typeS cfi_pseudo_table[] =
     { "cfi_sections", dot_cfi_dummy, 0 },
     { "cfi_startproc", dot_cfi_dummy, 0 },
     { "cfi_endproc", dot_cfi_dummy, 0 },
+    { "cfi_fde_data", dot_cfi_dummy, 0 },
     { "cfi_def_cfa", dot_cfi_dummy, 0 },
     { "cfi_def_cfa_register", dot_cfi_dummy, 0 },
     { "cfi_def_cfa_offset", dot_cfi_dummy, 0 },
@@ -2032,8 +2472,11 @@ const pseudo_typeS cfi_pseudo_table[] =
     { "cfi_escape", dot_cfi_dummy, 0 },
     { "cfi_signal_frame", dot_cfi_dummy, 0 },
     { "cfi_personality", dot_cfi_dummy, 0 },
+    { "cfi_personality_id", dot_cfi_dummy, 0 },
     { "cfi_lsda", dot_cfi_dummy, 0 },
     { "cfi_val_encoded_addr", dot_cfi_dummy, 0 },
+    { "cfi_inline_lsda", dot_cfi_dummy, 0 },
+    { "cfi_prologue_end", dot_cfi_dummy, 0 },
     { NULL, NULL, 0 }
   };
 
diff --git a/gas/dw2gencfi.h b/gas/dw2gencfi.h
index 7f83496..9a7e538 100644
--- a/gas/dw2gencfi.h
+++ b/gas/dw2gencfi.h
@@ -57,10 +57,18 @@ extern void cfi_add_CFA_restore_state (void);
 #define SUPPORT_FRAME_LINKONCE 0
 #endif
 
+#ifdef tc_cfi_special_encoding
+#define SUPPORT_COMPACT_EH 1
+#else
+#define SUPPORT_COMPACT_EH 0
+#endif
+
+#define MULTIPLE_FRAME_SECTIONS (SUPPORT_FRAME_LINKONCE || SUPPORT_COMPACT_EH)
+
 struct cfi_insn_data
 {
   struct cfi_insn_data *next;
-#if SUPPORT_FRAME_LINKONCE
+#if MULTIPLE_FRAME_SECTIONS
   segT cur_seg;
 #endif
   int insn;
@@ -97,10 +105,19 @@ struct cfi_insn_data
   } u;
 };
 
+enum {
+    EH_COMPACT_LEGACY,
+    EH_COMPACT_INLINE,
+    EH_COMPACT_OUTLINE,
+    EH_COMPACT_OUTLINE_DONE,
+    /* Outline if .cfi_inline_lsda used, otherwise legacy FDE.  */
+    EH_COMPACT_HAS_LSDA
+};
+
 struct fde_entry
 {
   struct fde_entry *next;
-#if SUPPORT_FRAME_LINKONCE
+#if MULTIPLE_FRAME_SECTIONS
   segT cur_seg;
 #endif
   symbolS *start_address;
@@ -109,13 +126,21 @@ struct fde_entry
   struct cfi_insn_data **last;
   unsigned char per_encoding;
   unsigned char lsda_encoding;
+  int personality_id;
   expressionS personality;
   expressionS lsda;
   unsigned int return_column;
   unsigned int signal_frame;
-#if SUPPORT_FRAME_LINKONCE
+#if MULTIPLE_FRAME_SECTIONS
   int handled;
 #endif
+  int eh_header_type;
+  /* Compact unwinding opcodes, not including the PR byte or LSDA.  */
+  int eh_data_size;
+  bfd_byte *eh_data;
+  /* For out of line tables and FDEs.  */
+  symbolS *eh_loc;
+  int sections;
 };
 
 /* The list of all FDEs that have been collected.  */
@@ -128,5 +153,6 @@ extern struct fde_entry *all_fde_data;
 #define CFI_escape		0x103
 #define CFI_signal_frame	0x104
 #define CFI_val_encoded_addr	0x105
+#define CFI_epilogue_begin	0x106
 
 #endif /* DW2GENCFI_H */
diff --git a/gas/testsuite/gas/mips/compact-eh-1.d b/gas/testsuite/gas/mips/compact-eh-1.d
new file mode 100644
index 0000000..7c5248b
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-1.d
@@ -0,0 +1,28 @@
+#objdump: -sr
+#name: Compact EH #1 with personality ID and FDE data
+#as: -EB
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+
+
+Contents of section .text:
+ 0000 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .gnu.attributes:
+ 0000 41000000 0f676e75 00010000 00070401  .*
+Contents of section .eh_frame_entry:
+ 0000 00000000 0104405c                    .*
diff --git a/gas/testsuite/gas/mips/compact-eh-1.s b/gas/testsuite/gas/mips/compact-eh-1.s
new file mode 100644
index 0000000..9c4f8d2
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-1.s
@@ -0,0 +1,19 @@
+	.gnu_attribute 4, 1
+	.abicalls
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+$LFB0 = .
+	.cfi_startproc
+	.cfi_personality_id 0x2
+	.set	nomips16
+	.set	nomicromips
+	.ent	_Z3fooi
+	.type	_Z3fooi, @function
+_Z3fooi:
+	nop
+	.end	_Z3fooi
+	.size	_Z3fooi, .-_Z3fooi
+	.cfi_fde_data 0x4,0x40
+	.cfi_endproc
diff --git a/gas/testsuite/gas/mips/compact-eh-2.d b/gas/testsuite/gas/mips/compact-eh-2.d
new file mode 100644
index 0000000..96b9ba4
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-2.d
@@ -0,0 +1,45 @@
+#objdump: -sr
+#name: Compact EH #2 with personality routine and FDE data
+#as: -EB
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.data.DW.ref.__gnu_compact_pr2\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         __gnu_compact_pr2
+
+
+RELOCATION RECORDS FOR \[.gnu_extab\]:
+OFFSET   TYPE              VALUE 
+00000001 R_MIPS_GPREL32    DW.ref.__gnu_compact_pr2
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .group:
+ 0000 00000001 00000008                    .*
+Contents of section .text:
+ 0000 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .data.DW.ref.__gnu_compact_pr2:
+ 0000 00000000                             .*
+Contents of section .gnu.attributes:
+ 0000 41000000 0f676e75 00010000 00070401  .*
+Contents of section .gnu_extab:
+ 0000 00000000 0004405c                    .*
+Contents of section .eh_frame_entry:
+ 0000 00000001 00000000                    .*
diff --git a/gas/testsuite/gas/mips/compact-eh-2.s b/gas/testsuite/gas/mips/compact-eh-2.s
new file mode 100644
index 0000000..f5b2a71
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-2.s
@@ -0,0 +1,28 @@
+	.gnu_attribute 4, 1
+	.abicalls
+        .hidden DW.ref.__gnu_compact_pr2
+        .weak   DW.ref.__gnu_compact_pr2
+        .section        .data.DW.ref.__gnu_compact_pr2,"awG",@progbits,DW.ref.__gnu_compact_pr2,comdat
+        .align  2
+        .type   DW.ref.__gnu_compact_pr2, @object
+        .size   DW.ref.__gnu_compact_pr2, 4
+DW.ref.__gnu_compact_pr2:
+        .word   __gnu_compact_pr2
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+$LFB0 = .
+	.cfi_startproc
+	.cfi_personality 0xbb, DW.ref.__gnu_compact_pr2
+	.set	nomips16
+	.set	nomicromips
+	.ent	_Z3fooi
+	.type	_Z3fooi, @function
+_Z3fooi:
+	nop
+	.end	_Z3fooi
+	.size	_Z3fooi, .-_Z3fooi
+	.cfi_fde_data 0x4,0x40
+	.cfi_endproc
+	.globl	__gnu_compact_pr2
diff --git a/gas/testsuite/gas/mips/compact-eh-3.d b/gas/testsuite/gas/mips/compact-eh-3.d
new file mode 100644
index 0000000..25bf2d1
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-3.d
@@ -0,0 +1,31 @@
+#objdump: -sr
+#name: Compact EH #3 with personality id and large FDE data
+#as: -EB
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .text:
+ 0000 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .gnu.attributes:
+ 0000 41000000 0f676e75 00010000 00070401  .*
+Contents of section .gnu_extab:
+ 0000 02044003 055c                        .*
+Contents of section .eh_frame_entry:
+ 0000 00000001 00000000                    .*
diff --git a/gas/testsuite/gas/mips/compact-eh-3.s b/gas/testsuite/gas/mips/compact-eh-3.s
new file mode 100644
index 0000000..fd9def1
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-3.s
@@ -0,0 +1,19 @@
+	.gnu_attribute 4, 1
+	.abicalls
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+$LFB0 = .
+	.cfi_startproc
+	.cfi_personality_id 0x2
+	.set	nomips16
+	.set	nomicromips
+	.ent	_Z3fooi
+	.type	_Z3fooi, @function
+_Z3fooi:
+	nop
+	.end	_Z3fooi
+	.size	_Z3fooi, .-_Z3fooi
+	.cfi_fde_data 0x4,0x40,0x3,0x5
+	.cfi_endproc
diff --git a/gas/testsuite/gas/mips/compact-eh-4.d b/gas/testsuite/gas/mips/compact-eh-4.d
new file mode 100644
index 0000000..c2e9cb5
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-4.d
@@ -0,0 +1,32 @@
+#objdump: -sr
+#name: Compact EH #4 with personality id, FDE data and LSDA
+#as: -EB
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .text:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .gnu_extab:
+ 0000 0204405c 020a0104 7f050404 0005047f  .*
+Contents of section .gnu.attributes:
+ 0000 41000000 0f676e75 00010000 00070401  .*
+Contents of section .eh_frame_entry:
+ 0000 00000001 00000000                    .*
diff --git a/gas/testsuite/gas/mips/compact-eh-4.s b/gas/testsuite/gas/mips/compact-eh-4.s
new file mode 100644
index 0000000..d0be1c7
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-4.s
@@ -0,0 +1,47 @@
+	.gnu_attribute 4, 1
+	.abicalls
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+$LFB0 = .
+	.cfi_startproc
+	.cfi_personality_id 0x2
+	.cfi_lsda 0x1b,$LLSDA0
+	.set	nomips16
+	.set	nomicromips
+	.ent	_Z3fooi
+	.type	_Z3fooi, @function
+_Z3fooi:
+$LEHB0 = .
+	nop
+$LEHE0 = .
+	nop
+$LEHB1 = .
+	nop
+$LEHE1 = .
+	nop
+$LEHB2 = .
+$L3:
+	nop
+$LEHE2 = .
+	.end	_Z3fooi
+	.size	_Z3fooi, .-_Z3fooi
+	.cfi_fde_data 0x4,0x40
+	.cfi_endproc
+	.cfi_inline_lsda 2
+$LLSDA0:
+	.byte	0x2
+	.uleb128 $LLSDACSE0-$LLSDACSB0
+$LLSDACSB0:
+	.uleb128 ($LEHB0-$LFB0)|1
+	.uleb128 ($LEHE0-$LEHB0)
+	.sleb128 -1
+	.uleb128 ($LEHB1-$LEHE0)|1
+	.uleb128 ($LEHE1-$LEHB1)
+	.sleb128 ($L3-($LEHE1))
+	.sleb128 (0<<2)|0
+	.uleb128 ($LEHB2-$LEHE1)|1
+	.uleb128 ($LEHE2-$LEHB2)
+	.sleb128 -1
+$LLSDACSE0:
diff --git a/gas/testsuite/gas/mips/compact-eh-5.d b/gas/testsuite/gas/mips/compact-eh-5.d
new file mode 100644
index 0000000..0aa9e0d
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-5.d
@@ -0,0 +1,47 @@
+#objdump: -sr
+#name: Compact EH #5 with personality routine, FDE data and LSDA
+#as: -EB
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.data.DW.ref.__gnu_compact_pr2\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         __gnu_compact_pr2
+
+
+RELOCATION RECORDS FOR \[.gnu_extab\]:
+OFFSET   TYPE              VALUE 
+00000001 R_MIPS_GPREL32    DW.ref.__gnu_compact_pr2
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .group:
+ 0000 00000001 00000008                    .*
+Contents of section .text:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .data.DW.ref.__gnu_compact_pr2:
+ 0000 00000000                             .*
+Contents of section .gnu_extab:
+ 0000 00000000 0004405c 020a0104 7f050404  .*
+ 0010 0005047f                             .*
+Contents of section .gnu.attributes:
+ 0000 41000000 0f676e75 00010000 00070401  .*
+Contents of section .eh_frame_entry:
+ 0000 00000001 00000000                    .*
diff --git a/gas/testsuite/gas/mips/compact-eh-5.s b/gas/testsuite/gas/mips/compact-eh-5.s
new file mode 100644
index 0000000..6bdf551
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-5.s
@@ -0,0 +1,56 @@
+	.gnu_attribute 4, 1
+	.abicalls
+        .hidden DW.ref.__gnu_compact_pr2
+        .weak   DW.ref.__gnu_compact_pr2
+        .section        .data.DW.ref.__gnu_compact_pr2,"awG",@progbits,DW.ref.__gnu_compact_pr2,comdat
+        .align  2
+        .type   DW.ref.__gnu_compact_pr2, @object
+        .size   DW.ref.__gnu_compact_pr2, 4
+DW.ref.__gnu_compact_pr2:
+        .word   __gnu_compact_pr2
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+$LFB0 = .
+	.cfi_startproc
+	.cfi_personality 0xbb, DW.ref.__gnu_compact_pr2
+	.cfi_lsda 0x1b,$LLSDA0
+	.set	nomips16
+	.set	nomicromips
+	.ent	_Z3fooi
+	.type	_Z3fooi, @function
+_Z3fooi:
+$LEHB0 = .
+	nop
+$LEHE0 = .
+	nop
+$LEHB1 = .
+	nop
+$LEHE1 = .
+	nop
+$LEHB2 = .
+$L3:
+	nop
+$LEHE2 = .
+	.end	_Z3fooi
+	.size	_Z3fooi, .-_Z3fooi
+	.cfi_fde_data 0x4,0x40
+	.cfi_endproc
+	.globl	__gnu_compact_pr2
+	.cfi_inline_lsda 2
+$LLSDA0:
+	.byte	0x2
+	.uleb128 $LLSDACSE0-$LLSDACSB0
+$LLSDACSB0:
+	.uleb128 ($LEHB0-$LFB0)|1
+	.uleb128 ($LEHE0-$LEHB0)
+	.sleb128 -1
+	.uleb128 ($LEHB1-$LEHE0)|1
+	.uleb128 ($LEHE1-$LEHB1)
+	.sleb128 ($L3-($LEHE1))
+	.sleb128 (0<<2)|0
+	.uleb128 ($LEHB2-$LEHE1)|1
+	.uleb128 ($LEHE2-$LEHB2)
+	.sleb128 -1
+$LLSDACSE0:
diff --git a/gas/testsuite/gas/mips/compact-eh-6.d b/gas/testsuite/gas/mips/compact-eh-6.d
new file mode 100644
index 0000000..3852ab9
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-6.d
@@ -0,0 +1,33 @@
+#objdump: -sr
+#name: Compact EH #6 with personality id, LSDA and large FDE data
+#as: -EB
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .text:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .gnu_extab:
+ 0000 02044003 055f5f5c 020a0104 7f050404  .*
+ 0010 0005047f                             .*
+Contents of section .gnu.attributes:
+ 0000 41000000 0f676e75 00010000 00070401  .*
+Contents of section .eh_frame_entry:
+ 0000 00000001 00000000                    .*
diff --git a/gas/testsuite/gas/mips/compact-eh-6.s b/gas/testsuite/gas/mips/compact-eh-6.s
new file mode 100644
index 0000000..2e49054
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-6.s
@@ -0,0 +1,47 @@
+	.gnu_attribute 4, 1
+	.abicalls
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+$LFB0 = .
+	.cfi_startproc
+	.cfi_personality_id 0x2
+	.cfi_lsda 0x1b,$LLSDA0
+	.set	nomips16
+	.set	nomicromips
+	.ent	_Z3fooi
+	.type	_Z3fooi, @function
+_Z3fooi:
+$LEHB0 = .
+	nop
+$LEHE0 = .
+	nop
+$LEHB1 = .
+	nop
+$LEHE1 = .
+	nop
+$LEHB2 = .
+$L3:
+	nop
+$LEHE2 = .
+	.end	_Z3fooi
+	.size	_Z3fooi, .-_Z3fooi
+	.cfi_fde_data 0x4,0x40,0x3,0x5
+	.cfi_endproc
+	.cfi_inline_lsda 2
+$LLSDA0:
+	.byte	0x2
+	.uleb128 $LLSDACSE0-$LLSDACSB0
+$LLSDACSB0:
+	.uleb128 ($LEHB0-$LFB0)|1
+	.uleb128 ($LEHE0-$LEHB0)
+	.sleb128 -1
+	.uleb128 ($LEHB1-$LEHE0)|1
+	.uleb128 ($LEHE1-$LEHB1)
+	.sleb128 ($L3-($LEHE1))
+	.sleb128 (0<<2)|0
+	.uleb128 ($LEHB2-$LEHE1)|1
+	.uleb128 ($LEHE2-$LEHB2)
+	.sleb128 -1
+$LLSDACSE0:
diff --git a/gas/testsuite/gas/mips/compact-eh-7.d b/gas/testsuite/gas/mips/compact-eh-7.d
new file mode 100644
index 0000000..c0c9c61
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-7.d
@@ -0,0 +1,38 @@
+#objdump: -sr
+#name: Compact EH #7 with personality id and fallback FDE
+#as: -EB
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame\]:
+OFFSET   TYPE              VALUE 
+0000001c R_MIPS_PC32       .text.*
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .eh_frame.*
+
+
+Contents of section .text:
+ 0000 00000000 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .gnu.attributes:
+ 0000 41000000 0f676e75 00010000 00070401  .*
+Contents of section .eh_frame:
+ 0000 00000010 00000000 017a5200 017c1f01  .*
+ 0010 1b0d1d00 00000014 00000018 00000000  .*
+ 0020 00000008 00441308 440e0000           .*
+Contents of section .eh_frame_entry:
+ 0000 00000001 00000015                    .*
diff --git a/gas/testsuite/gas/mips/compact-eh-7.s b/gas/testsuite/gas/mips/compact-eh-7.s
new file mode 100644
index 0000000..e7554d2
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-7.s
@@ -0,0 +1,22 @@
+	.gnu_attribute 4, 1
+	.abicalls
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+$LFB0 = .
+	.cfi_startproc
+	.cfi_personality_id 0x2
+	.set	nomips16
+	.set	nomicromips
+	.ent	_Z3fooi
+	.type	_Z3fooi, @function
+_Z3fooi:
+	nop
+	.cfi_def_cfa_offset -32
+	nop
+	.cfi_def_cfa_offset 0
+	
+	.end	_Z3fooi
+	.size	_Z3fooi, .-_Z3fooi
+	.cfi_endproc
diff --git a/gas/testsuite/gas/mips/compact-eh-el-1.d b/gas/testsuite/gas/mips/compact-eh-el-1.d
new file mode 100644
index 0000000..a6ebed9
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-el-1.d
@@ -0,0 +1,29 @@
+#objdump: -sr
+#name: Compact EH EL #1 with personality ID and FDE data
+#source: compact-eh-1.s
+#as: -EL
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+
+
+Contents of section .text:
+ 0000 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .gnu.attributes:
+ 0000 410f0000 00676e75 00010700 00000401  .*
+Contents of section .eh_frame_entry:
+ 0000 00000000 0104405c                    .*
diff --git a/gas/testsuite/gas/mips/compact-eh-el-2.d b/gas/testsuite/gas/mips/compact-eh-el-2.d
new file mode 100644
index 0000000..075df62
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-el-2.d
@@ -0,0 +1,46 @@
+#objdump: -sr
+#name: Compact EH EL #2 with personality routine and FDE data
+#source: compact-eh-2.s
+#as: -EL
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.data.DW.ref.__gnu_compact_pr2\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         __gnu_compact_pr2
+
+
+RELOCATION RECORDS FOR \[.gnu_extab\]:
+OFFSET   TYPE              VALUE 
+00000001 R_MIPS_GPREL32    DW.ref.__gnu_compact_pr2
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .group:
+ 0000 01000000 08000000                    .*
+Contents of section .text:
+ 0000 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .data.DW.ref.__gnu_compact_pr2:
+ 0000 00000000                             .*
+Contents of section .gnu.attributes:
+ 0000 410f0000 00676e75 00010700 00000401  .*
+Contents of section .gnu_extab:
+ 0000 00000000 0004405c                    .*
+Contents of section .eh_frame_entry:
+ 0000 01000000 00000000                    .*
diff --git a/gas/testsuite/gas/mips/compact-eh-el-3.d b/gas/testsuite/gas/mips/compact-eh-el-3.d
new file mode 100644
index 0000000..6a29fd0
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-el-3.d
@@ -0,0 +1,32 @@
+#objdump: -sr
+#name: Compact EH EL #3 with personality id and large FDE data
+#source: compact-eh-3.s
+#as: -EL
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .text:
+ 0000 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .gnu.attributes:
+ 0000 410f0000 00676e75 00010700 00000401  .*
+Contents of section .gnu_extab:
+ 0000 02044003 055c                        .*
+Contents of section .eh_frame_entry:
+ 0000 01000000 00000000                    .*
diff --git a/gas/testsuite/gas/mips/compact-eh-el-4.d b/gas/testsuite/gas/mips/compact-eh-el-4.d
new file mode 100644
index 0000000..2368e66
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-el-4.d
@@ -0,0 +1,33 @@
+#objdump: -sr
+#name: Compact EH EL #4 with personality id, FDE data and LSDA
+#source: compact-eh-4.s
+#as: -EL
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .text:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .gnu_extab:
+ 0000 0204405c 020a0104 7f050404 0005047f  .*
+Contents of section .gnu.attributes:
+ 0000 410f0000 00676e75 00010700 00000401  .*
+Contents of section .eh_frame_entry:
+ 0000 01000000 00000000                    .*
diff --git a/gas/testsuite/gas/mips/compact-eh-el-5.d b/gas/testsuite/gas/mips/compact-eh-el-5.d
new file mode 100644
index 0000000..2813fa0
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-el-5.d
@@ -0,0 +1,48 @@
+#objdump: -sr
+#name: Compact EH EL #5 with personality routine, FDE data and LSDA
+#source: compact-eh-5.s
+#as: -EL
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.data.DW.ref.__gnu_compact_pr2\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         __gnu_compact_pr2
+
+
+RELOCATION RECORDS FOR \[.gnu_extab\]:
+OFFSET   TYPE              VALUE 
+00000001 R_MIPS_GPREL32    DW.ref.__gnu_compact_pr2
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .group:
+ 0000 01000000 08000000                    .*
+Contents of section .text:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .data.DW.ref.__gnu_compact_pr2:
+ 0000 00000000                             .*
+Contents of section .gnu_extab:
+ 0000 00000000 0004405c 020a0104 7f050404  .*
+ 0010 0005047f                             .*
+Contents of section .gnu.attributes:
+ 0000 410f0000 00676e75 00010700 00000401  .*
+Contents of section .eh_frame_entry:
+ 0000 01000000 00000000                    .*
diff --git a/gas/testsuite/gas/mips/compact-eh-el-6.d b/gas/testsuite/gas/mips/compact-eh-el-6.d
new file mode 100644
index 0000000..7c6de83
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-el-6.d
@@ -0,0 +1,34 @@
+#objdump: -sr
+#name: Compact EH EL #6 with personality id, LSDA and large FDE data
+#source: compact-eh-6.s
+#as: -EL
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .text:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .gnu_extab:
+ 0000 02044003 055f5f5c 020a0104 7f050404  .*
+ 0010 0005047f                             .*
+Contents of section .gnu.attributes:
+ 0000 410f0000 00676e75 00010700 00000401  .*
+Contents of section .eh_frame_entry:
+ 0000 01000000 00000000                    .*
diff --git a/gas/testsuite/gas/mips/compact-eh-el-7.d b/gas/testsuite/gas/mips/compact-eh-el-7.d
new file mode 100644
index 0000000..af6b6c9
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-el-7.d
@@ -0,0 +1,39 @@
+#objdump: -sr
+#name: Compact EH EL #7 with personality id and fallback FDE
+#source: compact-eh-7.s
+#as: -EL
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame\]:
+OFFSET   TYPE              VALUE 
+0000001c R_MIPS_PC32       .text.*
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .eh_frame.*
+
+
+Contents of section .text:
+ 0000 00000000 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .gnu.attributes:
+ 0000 410f0000 00676e75 00010700 00000401  .*
+Contents of section .eh_frame:
+ 0000 10000000 00000000 017a5200 017c1f01  .*
+ 0010 1b0d1d00 14000000 18000000 00000000  .*
+ 0020 08000000 00441308 440e0000           .*
+Contents of section .eh_frame_entry:
+ 0000 01000000 15000000                    .*
diff --git a/gas/testsuite/gas/mips/compact-eh-err1.l b/gas/testsuite/gas/mips/compact-eh-err1.l
new file mode 100644
index 0000000..3ee03de
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-err1.l
@@ -0,0 +1,2 @@
+.*: Assembler messages:
+.*:20: Error: .cfi_inline_lsda seen for frame without .cfi_lsda
diff --git a/gas/testsuite/gas/mips/compact-eh-err1.s b/gas/testsuite/gas/mips/compact-eh-err1.s
new file mode 100644
index 0000000..967313f
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-err1.s
@@ -0,0 +1,20 @@
+	.gnu_attribute 4, 1
+	.abicalls
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+$LFB0 = .
+	.cfi_startproc
+	.cfi_personality_id 0x2
+	.set	nomips16
+	.set	nomicromips
+	.ent	_Z3fooi
+	.type	_Z3fooi, @function
+_Z3fooi:
+	nop
+	.end	_Z3fooi
+	.size	_Z3fooi, .-_Z3fooi
+	.cfi_fde_data 0x4,0x40
+	.cfi_endproc
+	.cfi_inline_lsda 1
diff --git a/gas/testsuite/gas/mips/compact-eh-err2.l b/gas/testsuite/gas/mips/compact-eh-err2.l
new file mode 100644
index 0000000..c52976a
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-err2.l
@@ -0,0 +1,2 @@
+.*: Assembler messages:
+.*:7: Error: inconsistent uses of .cfi_sections
diff --git a/gas/testsuite/gas/mips/compact-eh-err2.s b/gas/testsuite/gas/mips/compact-eh-err2.s
new file mode 100644
index 0000000..acf83d1
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-err2.s
@@ -0,0 +1,7 @@
+	.gnu_attribute 4, 1
+	.abicalls
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+	.cfi_sections .eh_frame
diff --git a/gas/testsuite/gas/mips/ehword.d b/gas/testsuite/gas/mips/ehword.d
index 4cbef12..38db904 100644
--- a/gas/testsuite/gas/mips/ehword.d
+++ b/gas/testsuite/gas/mips/ehword.d
@@ -6,4 +6,4 @@
 
 RELOCATION RECORDS FOR \[\.text\]:
 OFFSET   TYPE              VALUE 
-00000000 R_MIPS_EH         _ZTI5myExc
+00000000 R_MIPS_PC32       _ZTI5myExc
diff --git a/gas/testsuite/gas/mips/mips.exp b/gas/testsuite/gas/mips/mips.exp
index ece3a13..b0ba636 100644
--- a/gas/testsuite/gas/mips/mips.exp
+++ b/gas/testsuite/gas/mips/mips.exp
@@ -518,6 +518,23 @@ if { [istarget mips*-*-vxworks*] } {
 	    "MIPS branch swapping ($count)"
     }
 
+    run_dump_test "compact-eh-1"
+    run_dump_test "compact-eh-el-1"
+    run_dump_test "compact-eh-2"
+    run_dump_test "compact-eh-el-2"
+    run_dump_test "compact-eh-3"
+    run_dump_test "compact-eh-el-3"
+    run_dump_test "compact-eh-4"
+    run_dump_test "compact-eh-el-4"
+    run_dump_test "compact-eh-5"
+    run_dump_test "compact-eh-el-5"
+    run_dump_test "compact-eh-6"
+    run_dump_test "compact-eh-el-6"
+    run_dump_test "compact-eh-7"
+    run_dump_test "compact-eh-el-7"
+    run_list_test "compact-eh-err1"
+    run_list_test "compact-eh-err2"
+
     run_dump_test "div"
 
     if { !$addr32 } {
diff --git a/include/bfdlink.h b/include/bfdlink.h
index 1ac0738..1b13dd8 100644
--- a/include/bfdlink.h
+++ b/include/bfdlink.h
@@ -330,9 +330,9 @@ struct bfd_link_info
   /* TRUE if PT_GNU_RELRO segment should be created.  */
   unsigned int relro: 1;
 
-  /* TRUE if .eh_frame_hdr section and PT_GNU_EH_FRAME ELF segment
-     should be created.  */
-  unsigned int eh_frame_hdr: 1;
+  /* Nonzero if .eh_frame_hdr section and PT_GNU_EH_FRAME ELF segment
+     should be created.  1 for DWARF2 tables, 2 for compact tables.  */
+  unsigned int eh_frame_hdr: 2;
 
   /* TRUE if we should warn when adding a DT_TEXTREL to a shared object.  */
   unsigned int warn_shared_textrel: 1;
diff --git a/ld/emultempl/elf32.em b/ld/emultempl/elf32.em
index fda0e68..ad79c9f 100644
--- a/ld/emultempl/elf32.em
+++ b/ld/emultempl/elf32.em
@@ -1116,29 +1116,60 @@ gld${EMULATION_NAME}_after_open (void)
   if (link_info.relocatable)
     return;
 
-  if (link_info.eh_frame_hdr
-      && !link_info.traditional_format)
+  if (!link_info.traditional_format)
     {
-      bfd *abfd, *elfbfd = NULL;
+      bfd *abfd, *elfbfd = NULL, *seenbfd = NULL;
       bfd_boolean warn_eh_frame = FALSE;
       asection *s;
+      int seen_type = 0;
 
       for (abfd = link_info.input_bfds; abfd; abfd = abfd->link_next)
 	{
-	  if (bfd_get_flavour (abfd) == bfd_target_elf_flavour)
-	    elfbfd = abfd;
-	  if (!warn_eh_frame)
+	  int type = 0;
+	  for (s = abfd->sections; s && type < 2 ; s = s->next)
 	    {
-	      s = bfd_get_section_by_name (abfd, ".eh_frame");
-	      while (s != NULL
-		     && (s->size <= 8
-			 || bfd_is_abs_section (s->output_section)))
-		s = bfd_get_next_section_by_name (s);
-	      warn_eh_frame = s != NULL;
+	      const char *name = bfd_get_section_name (abfd, s);
+
+	      if (bfd_is_abs_section (s->output_section))
+		continue;
+
+	      if (CONST_STRNEQ (name, ".eh_frame_entry"))
+		type = 2;
+	      else if (strcmp (name, ".eh_frame") == 0
+		       && s->size > 8)
+		type = 1;
+	    }
+
+	  if (type != 0)
+	    {
+	      if (!elfbfd && (type == 2 || link_info.eh_frame_hdr))
+		{
+		  if (bfd_get_flavour (abfd) == bfd_target_elf_flavour)
+		    elfbfd = abfd;
+
+		  warn_eh_frame = TRUE;
+		}
+
+	      if (seen_type == 0)
+                {
+		  seen_type = type;
+                  seenbfd = abfd;
+                }
+	      else if (seen_type != type)
+		{
+		  einfo (_("%P%F: compact frame descriptions from %B"
+                           " are incompatible with DWARF2 .eh_frame"
+                           " from %B\n"), seenbfd,
+			 type == 1 ? abfd : elfbfd);
+		  break;
+		}
+
 	    }
-	  if (elfbfd && warn_eh_frame)
-	    break;
 	}
+
+      if (seen_type == 2)
+	link_info.eh_frame_hdr = 2;
+
       if (elfbfd)
 	{
 	  const struct elf_backend_data *bed;
diff --git a/ld/emultempl/mipself.em b/ld/emultempl/mipself.em
index 02043b7..252af0f 100644
--- a/ld/emultempl/mipself.em
+++ b/ld/emultempl/mipself.em
@@ -34,6 +34,7 @@ static lang_input_statement_type *stub_file;
 static bfd *stub_bfd;
 
 static bfd_boolean insn32;
+static int eh_reloc_type = R_MIPS_EH;
 
 static void
 mips_after_parse (void)
@@ -202,7 +203,7 @@ mips_create_output_section_statements (void)
 
   htab = elf_hash_table (&link_info);
   if (is_elf_hash_table (htab) && is_mips_elf (link_info.output_bfd))
-    _bfd_mips_elf_insn32 (&link_info, insn32);
+    _bfd_mips_elf_linker_flags (&link_info, insn32, eh_reloc_type);
 
   if (is_mips_elf (link_info.output_bfd))
     _bfd_mips_elf_init_stubs (&link_info, mips_add_stub_section);
@@ -252,11 +253,13 @@ EOF
 PARSE_AND_LIST_PROLOGUE='
 #define OPTION_INSN32			301
 #define OPTION_NO_INSN32		(OPTION_INSN32 + 1)
+#define OPTION_PCREL_EH_RELOC		303
 '
 
 PARSE_AND_LIST_LONGOPTS='
   { "insn32", no_argument, NULL, OPTION_INSN32 },
   { "no-insn32", no_argument, NULL, OPTION_NO_INSN32 },
+  { "pcrel-eh-reloc", no_argument, NULL, OPTION_PCREL_EH_RELOC },
 '
 
 PARSE_AND_LIST_OPTIONS='
@@ -276,6 +279,10 @@ PARSE_AND_LIST_ARGS_CASES='
     case OPTION_NO_INSN32:
       insn32 = FALSE;
       break;
+
+    case OPTION_PCREL_EH_RELOC:
+      eh_reloc_type = R_MIPS_PC32;
+      break;
 '
 
 LDEMUL_AFTER_PARSE=mips_after_parse
diff --git a/ld/ld.texinfo b/ld/ld.texinfo
index bfc2643..042093c 100644
--- a/ld/ld.texinfo
+++ b/ld/ld.texinfo
@@ -2775,6 +2775,11 @@ or in relaxation.  If @samp{--insn32} is used, then the linker only uses
 used, all instruction encodings are used, including 16-bit ones where
 possible.
 
+@kindex --pcrel-eh-reloc
+@item --pcrel-eh-reloc
+Interpret R_MIPS_EH as a pc-relative address.  The default is a
+GP-relative indirect address.
+
 @end table
 
 @c man end
diff --git a/ld/scripttempl/elf.sc b/ld/scripttempl/elf.sc
index e8126cb..151e874 100644
--- a/ld/scripttempl/elf.sc
+++ b/ld/scripttempl/elf.sc
@@ -522,10 +522,11 @@ cat <<EOF
   ${CREATE_SHLIB-${SDATA2}}
   ${CREATE_SHLIB-${SBSS2}}
   ${OTHER_READONLY_SECTIONS}
-  .eh_frame_hdr : { *(.eh_frame_hdr) }
-  .eh_frame     ${RELOCATING-0} : ONLY_IF_RO { KEEP (*(.eh_frame)) }
+  .eh_frame_hdr : { *(.eh_frame_hdr) ${RELOCATING+*(.eh_frame_entry .eh_frame_entry.*)} }
+  .eh_frame     ${RELOCATING-0} : ONLY_IF_RO { KEEP (*(.eh_frame)) ${RELOCATING+*(.eh_frame.*)} }
   .gcc_except_table ${RELOCATING-0} : ONLY_IF_RO { *(.gcc_except_table
   .gcc_except_table.*) }
+  .gnu_extab ${RELOCATING-0} : ONLY_IF_RO { *(.gnu_extab*) }
   /* These sections are generated by the Sun/Oracle C++ compiler.  */
   .exception_ranges ${RELOCATING-0} : ONLY_IF_RO { *(.exception_ranges
   .exception_ranges*) }
@@ -538,7 +539,8 @@ cat <<EOF
   ${CREATE_PIE+${RELOCATING+. = ${SHLIB_DATA_ADDR-${DATA_SEGMENT_ALIGN}};}}
 
   /* Exception handling  */
-  .eh_frame     ${RELOCATING-0} : ONLY_IF_RW { KEEP (*(.eh_frame)) }
+  .eh_frame     ${RELOCATING-0} : ONLY_IF_RW { KEEP (*(.eh_frame)) ${RELOCATING+*(.eh_frame.*)} }
+  .gnu_extab    ${RELOCATING-0} : ONLY_IF_RW { *(.gnu_extab) }
   .gcc_except_table ${RELOCATING-0} : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
   .exception_ranges ${RELOCATING-0} : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) }
 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* RE: [Patch] Gas support for MIPS Compact EH
  2014-02-04 21:56   ` Moore, Catherine
@ 2014-02-05 22:38     ` Maciej W. Rozycki
  2014-02-08 16:34     ` Richard Sandiford
  1 sibling, 0 replies; 31+ messages in thread
From: Maciej W. Rozycki @ 2014-02-05 22:38 UTC (permalink / raw)
  To: Moore, Catherine; +Cc: Richard Sandiford, binutils

On Tue, 4 Feb 2014, Moore, Catherine wrote:

> I finally got some time to work on fixing up (with some help from Bernd) 
> the binutils submission for compact EH.  Hopefully, the review comments 
> have been addressed.
> 
> The current patch has been expanded to include all of binutils. 
> 
> As a reminder, the spec is available here:
> https://github.com/MentorEmbedded/cxx-abi/blob/master/MIPSCompactEH.pdf

 Just a couple of minor nits I found skimming over the change.  These of 
course by no means invalidate your excellent work!

> diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c
> index 34f1bf0..90688f1 100644
> --- a/gas/config/tc-mips.c
> +++ b/gas/config/tc-mips.c
> @@ -18249,3 +18256,13 @@ tc_mips_regname_to_dw2regnum (char *regname)
>  
>    return regnum;
>  }
> +
> +#if defined (OBJ_ELF)
> +bfd_reloc_code_real_type
> +mips_cfi_reloc_for_encoding (int encoding)
> +{
> +  if ((encoding & 0x70) == DW_EH_PE_datarel)
> +    return BFD_RELOC_GPREL32;
> +  return BFD_RELOC_32_PCREL;
> +}
> +#endif

 No need to check for OBJ_ELF anymore, we don't support non-ELF targets 
these days (cf. commit f3ded42a5d397a8dd1548e68a647c690088d3fbd).

> diff --git a/gas/config/tc-mips.h b/gas/config/tc-mips.h
> index c7eaa04..a300062 100644
> --- a/gas/config/tc-mips.h
> +++ b/gas/config/tc-mips.h
> @@ -190,4 +192,18 @@ extern int tc_mips_regname_to_dw2regnum (char *regname);
>  #define DWARF2_DEFAULT_RETURN_COLUMN 31
>  #define DWARF2_CIE_DATA_ALIGNMENT (-4)
>  
> +#if defined (OBJ_ELF)
> +
> +#define tc_cfi_reloc_for_encoding mips_cfi_reloc_for_encoding
> +extern bfd_reloc_code_real_type mips_cfi_reloc_for_encoding (int encoding);
> +
> +#define tc_cfi_special_encoding(e)				   \
> +  ((e) == (DW_EH_PE_sdata4 | DW_EH_PE_datarel | DW_EH_PE_indirect) \
> +   || (e) == (DW_EH_PE_sdata4 | DW_EH_PE_pcrel))
> +
> +#define tc_compact_eh_opcode_stop 0x5c
> +#define tc_compact_eh_opcode_pad 0x5f
> +
> +#endif /* OBJ_ELF */
> +
>  #endif /* TC_MIPS */

 Likewise.

> diff --git a/ld/emultempl/mipself.em b/ld/emultempl/mipself.em
> index 02043b7..252af0f 100644
> --- a/ld/emultempl/mipself.em
> +++ b/ld/emultempl/mipself.em
> @@ -252,11 +253,13 @@ EOF
>  PARSE_AND_LIST_PROLOGUE='
>  #define OPTION_INSN32			301
>  #define OPTION_NO_INSN32		(OPTION_INSN32 + 1)
> +#define OPTION_PCREL_EH_RELOC		303
>  '

 (OPTION_NO_INSN32 + 1) please instead -- this allows easy option 
insertion and removal without the need to renumber options or leaving 
holes behind.

  Maciej

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Patch] Gas support for MIPS Compact EH
  2014-02-04 21:56   ` Moore, Catherine
  2014-02-05 22:38     ` Maciej W. Rozycki
@ 2014-02-08 16:34     ` Richard Sandiford
  2014-02-08 18:48       ` Bernd Schmidt
                         ` (3 more replies)
  1 sibling, 4 replies; 31+ messages in thread
From: Richard Sandiford @ 2014-02-08 16:34 UTC (permalink / raw)
  To: Moore, Catherine; +Cc: binutils

Thanks for the updates.

"Moore, Catherine" <Catherine_Moore@mentor.com> writes:
>> > @@ -4514,6 +4517,23 @@ argument is not present, otherwise secon  or a
>> > symbol name.  The default after @code{.cfi_startproc} is
>> > @code{.cfi_lsda 0xff},  no LSDA.
>> >
>> > +@section @code{.cfi_inline_lsda} [@var{align}]
>> > +@code{.cfi_inline_lsda} marks the start of a LSDA data section and
>> > +switches to the corresponding @code{.gnu.extab} section.
>> > +It must be preceded by a CFI block containing a @code{.cfi_lsda}
>> > +directive and is only valid when generating compact EH frames (i.e.
>> > +with @code{.cfi_sections eh_frame_entry}.
>> > +
>> > +If a compact encoding is being used then the table header and
>> > +unwinding opcodes will be generated at this point, so that they are
>> > +immediately followed by the LSDA data.  The symbol referenced by the
>> > +@code{.cfi_lsda} directive should still be defined in case a fallback
>> > +FDE based encoding is used.
>> > +
>> > +The optional @var{align} argument specifies the alignment required.
>> > +The alignment is specified as a power of two, as with the
>> > +@code{.p2align} directive.
>> 
>> Hmm, switching sections and emitting data feels very different in style from
>> the other .cfi directives, which just annotate code without changing the flow
>> of assembly.  I'd like to know other people's thoughts on this.
>> 
>> Also, how do you terminate the LSDA?  The documentation doesn't say (but
>> should :-)) and I couldn't see this directive in the spec either.
>
> The LSDA is terminated by a section switch.

OK.  Like I say, please mention this in the documentation.

>> "If a compact encoding is being used" seems redundant, since it comes just
>> after the bit saying "only valid when generating compact EH frames".
>> 
>> There need to be tests for all of this. 
>
> Tests are now included in the patch.
>
>> TBH, without tests, and without an
>> explanation of what the code is doing, I found this patch pretty hard to
>> review.  E.g.:
>> 
>> > @@ -129,7 +140,12 @@ get_debugseg_name (segT seg, const char
>> >        dot = strchr (name + 1, '.');
>> >
>> >        if (!dollar && !dot)
>> > -	name = "";
>> > +	{
>> > +	  if (compact_eh && strcmp (name, ".text") != 0)
>> > +	    return concat (base_name, ".", name, NULL);
>> > +
>> > +	  name = "";
>> > +	}
>> 
>> why is this change needed?  I.e., for a text section called something like
>> .foobar, why does compact_eh need to put things in a section name ending
>> in "..foobar", rather than in the main EH section?  I assume the double dots
>> are deliberate, e.g. to avoid confusion with ".text.foobar"?
>
> I'm not sure why Paul put this is and the next hunk.  There were test
> failures without.  I can revisit this
> For the next iteration if necessary.

Yeah, if you could that'd be great.  The code can't really go in if there's
no-one around who understands what it does.

I assume it's just to ensure that each text section has its own
.eh_frame_entry, but in that case I think we should check based
on the base_name rather than compact_eh.  Or do we need the same
treatment for .gnu_extab.  If so, why?

A comment is needed at the very least.

>> > @@ -161,6 +177,9 @@ alloc_debugseg_item (segT seg, int subse  static
>> > segT  is_now_linkonce_segment (void)  {
>> > +  if (compact_eh)
>> > +    return now_seg;
>> > +
>> >    if ((bfd_get_section_flags (stdoutput, now_seg)
>> >         & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD
>> >  	  | SEC_LINK_DUPLICATES_ONE_ONLY |
>> SEC_LINK_DUPLICATES_SAME_SIZE
>> 
>> Why is this change needed?
>> 
>> > @@ -180,7 +199,11 @@ make_debug_seg (segT cseg, char *name, i
>> >    segT r;
>> >    flagword flags;
>> >
>> > +#ifdef tc_make_debug_seg
>> > +  r = tc_make_debug_seg (cseg, name); #else
>> >    r = subseg_new (name, 0);
>> > +#endif
>> 
>> Why is this change needed?  And what does the new hook do?  It should be
>> documented in internals.texi.
>> 
>> Do we really want to change the behaviour for traditional DWARF EH too?
>> 
>
> It looks like this was intended to make .group sections so that text,
> extab and eh_frame entries are only deleted together.  This is now
> removed and this new patch add some linker smarts to handle things.

Thanks.

>> > @@ -833,14 +859,15 @@ dot_cfi_personality (int ignored ATTRIBU
>> >      }
>> >
>> >    if ((encoding & 0xff) != encoding
>> > -      || ((encoding & 0x70) != 0
>> > +      || ((((encoding & 0x70) != 0
>> >  #if CFI_DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr
>> > -	  && (encoding & 0x70) != DW_EH_PE_pcrel
>> > +	   && (encoding & 0x70) != DW_EH_PE_pcrel
>> >  #endif
>> >  	  )
>> >  	 /* leb128 can be handled, but does something actually need it?  */
>> > -      || (encoding & 7) == DW_EH_PE_uleb128
>> > -      || (encoding & 7) > DW_EH_PE_udata8)
>> > +	   || (encoding & 7) == DW_EH_PE_uleb128
>> > +	   || (encoding & 7) > DW_EH_PE_udata8)
>> > +	&& !tc_cfi_special_encoding (encoding)))
>> >      {
>> >        as_bad (_("invalid or unsupported encoding in .cfi_personality"));
>> >        ignore_rest_of_line ();
>> 
>> What does a "special" encoding mean?  Again, this hook should be
>> documented in internals.texi.  And do we really want to change the set of
>> encodings that are allowed for DWARF, even on MIPS systems that predate
>> compat EH?
>> 
> Special means that we have code in the backend to emit a reloc for it.
> In the revised patch, it goes along with
> Tc_cfi_reloc_for_encoding.  It also looks like internals.texi fails to
> document many of the tc_macros and it doesn't appear to be built into a
> .info fiie.

That's no reason not to document new hooks though.  I know I've used
internals.texi to read more about a hook in the past.  If you don't want
to put it there though then please at least put it in a comment instead.

I found this a really hard and time-consuming patch to review.
That could just be because I'm stupid, but I think you're underestimating
how difficult this is to follow for someone coming to it new.

>> Why is the CFI_EMIT_target needed?
> Sttrictly speaking it isn't, but having a different value was useful in
> the error checking.

Specifically, useful in what way?  Please could you add a comment
explaining why.

>> 
>>   data_size += encoding_size (fde->per_encoding);
>> 
>> would work; if not, please extend encoding_size.  We then have:
>> 
>> > +  if (fde->per_encoding != DW_EH_PE_omit)
>> > +    {
>> > +      *(p++) = 0;
>> > +      md_number_to_chars (p, 0, 4);
>> > +      tc_cfi_fix_eh_ref (p, &fde->personality);
>> > +      p += 4;
>> 
>> where tc_cfi_fix_eh_ref emits an R_MIPS_EH relocation regardless of which
>> personality encoding is used.  AIUI, the encoding must be one of the
>> "special" ones allowed by tc_cfi_special_encoding, so we should check for
>> that.
>> 
>> The tc_cfi_fix_eh_ref and tc_cfi_emit_expr hooks don't seem very
>> consistent; the former relies on the caller to clear the bytes, whereas the
>> latter is supposed to do it itself.
>
> All fixed, now using a hook to return a reloc and eliminated the use of
> R_MIPS_EH from the assembler.

Hmm, but how does it work under the new scheme?  It looks like gas now
always emits the .eh_frame_entry sections using R_MIPS_PC32, is that right?
But the linker chooses the .eh_frame_hdr encoding based on --pcrel-eh-reloc,
which also controls how R_MIPS_EH is handled.  So if the:

  DW_EH_PE_sdata4 | DW_EH_PE_datarel | DW_EH_PE_indirect

encoding is chosen for the .eh_frame_entry sections at link time, what
converts the input sections to use that encoding instead of the original
R_MIPS_PC32?  I'd assumed R_MIPS_EH was defined the way it was to avoid
that kind of thing.

I wasn't expecting you to drop the R_MIPS_EH stuff altogether.
I was just confused by the way that the old assembler patch
seemed to be associating R_MIPS_EH with a specific encoding
(DW_EH_PE_sdata4 | DW_EH_PE_datarel | DW_EH_PE_indirect).  So:

>> Neither of the hooks really seem to be doing anything target-specific except
>> for the all-important job of picking a reloc.  I think it would be
>> cleaner to have
>> a hook that returns the reloc instead.
>> In the case of tc_cfi_emit_expr, this could be done by making
>> tc_cfi_special_encoding return the reloc for an encoding, or
>> BFD_RELOC_NONE for unsupported encodings.
>> 
>> Also, this is more of a design point, but I find the handling of the
>> personality
>> encoding and R_MIPS_EH handling a bit confusing.  To quote:
>> 
>> ---------------------------------------------------------------------
>> 11 Relocations
>> 
>> A new static relocation, R_MIPS_EH, is defined. The semantics of this
>> relocation depend on whether static or dynamic linking is provided.
>> 
>> A GNU extension using relocation number 249 shall be used. The relocation
>> address need not be naturally aligned.
>> 
>> 11.1 Static code
>> 
>> For static code generation, the calculation is the same as an
>> R_MIPS_REL32 relocation.
>> 
>> At runtime, the following expression provides the relocated value, if 'ptr'
>> points to the relocation location.
>> 
>> • (ptrdiff_t)ptr + *(int32_t __attribute__((packed))*)ptr
>> 
>> [...]
>> 
>> 11.2 PIC code
>> 
>> For PIC code generation, a 32- or 64-bit GOT-table entry must be allocated to
>> refer to the (dynamically resolved) target address. Once the GOT entry has
>> been allocated, the static calculation is as for an
>> R_MIPS_GPREL32 relocation (except that the symbol is externally visible).
>> The GOT- slot has an associated R_MIPS_{32,64} dynamic relocation emitted
>> – and that will of course be at a naturally aligned location
>> 
>> At runtime, the following expression provides the relocated value, if 'ptr'
>> points to the relocation location, and 'gp' is the global pointer
>> value:
>> 
>> • *(ptrdiff_t *)((ptrdiff_t)gp + *(int32_t __attribute__((packed)) *)ptr)
>> 
>> [...]
>> ---------------------------------------------------------------------
>> 
>> So as I understand the first sentence, it's actually the linker that decides
>> whether R_MIPS_EH relocations act as direct PC-relative references (11.1) or
>> as indirect datarel references (11.2).
>> It's therefore the linker that decides what DWARF encoding R_MIPS_EH
>> fields use.  The linker then records that choice in the .eh_frame_hdr.
>> Is that
>> right?
>> 
>> If so, the assembler seems to be associating R_MIPS_EH with specific DWARF
>> encodings, even though the interpretation of R_MIPS_EH isn't known at that
>> stage.  I.e. the code reads:
>> 
>> #define tc_cfi_special_encoding(e) \
>>   ((e) == (DW_EH_PE_sdata4 | DW_EH_PE_datarel | DW_EH_PE_indirect) \
>>    || (e) == (DW_EH_PE_sdata4 | DW_EH_PE_pcrel))
>> 
>> void
>> mips_cfi_emit_expr (expressionS *exp, int encoding) {
>>   char *p;
>> 
>>   p = frag_more(4);
>>   md_number_to_chars (p, 0, 4);
>>   if ((encoding & 0x70) == DW_EH_PE_datarel)
>>     mips_cfi_fix_eh_ref (p, exp);
>>   else
>>     {
>>       fix_new (frag_now, p - frag_now->fr_literal, 4, exp->X_add_symbol,
>> 	       exp->X_add_number, TRUE, BFD_RELOC_32_PCREL);
>>     }
>> }
>> 
>> where mips_cfi_fix_eh_ref emits an R_MIPS_EH.  So the code appears to be
>> using R_MIPS_EH for the DWARF encoding:
>> 
>>     DW_EH_PE_sdata4 | DW_EH_PE_datarel | DW_EH_PE_indirect
>> 
>> even though there's no guarantee that the final value will be either datarel or
>> indirect.
>> 
>> Or, to put it another way, why are we making the choice between
>> R_MIPS_PC32 and R_MIPS_EH at assembly time in mips_cfi_emit_expr, but
>> not in mips_cfi_fix_eh_ref, even though the patch appears to allow the
>> same two encodings in both casees?

I thought R_MIPS_EH would be used for the .eh_frame_entry entries only,
since in that case the actual encoding of the address isn't known
until link time.  I thought mips_cfi_emit_expr, which deals with specific
assembly-time encoding types, ought to use specific relocations for those
relocation types instead.  I.e. mips_cfi_emit_expr would always use
R_MIPS_PC32 for:

  DW_EH_PE_sdata4 | DW_EH_PE_pcrel

and always use a new relocation type for:

  DW_EH_PE_sdata4 | DW_EH_PE_datarel | DW_EH_PE_indirect

Note that the spec is wrong to say that R_MIPS_GPREL32 is equivalent
to the above, since R_MIPS_GPREL32 doesn't include any indirection.
I think we need to define a new reloc such as R_MIPS_GOT_DISP32.

But more fundamentally, I don't really understand why we want the
GOT-based encoding for .eh_frame_hdr by default.  Is that intended for
systems that allow the text and data segments to be moved relative to
each other, like VxWorks?  It seems like a big hammer on bare-metal
and GNU/Linux, where the PC-relative form seems better.  And on VxWorks
it seems a bad idea to eat up valuable GOT space, since VxWorks doesn't
have any form of multigot support.

>> > +  demand_empty_rest_of_line ();
>> > +  ccseg = CUR_SEG (last_fde);
>> > +  /* Open .gnu_extab section.  */
>> > +  cfi_seg = get_cfi_seg (ccseg, ".gnu_extab",
>> > +			 (SEC_ALLOC | SEC_LOAD | SEC_DATA
>> > +			  | DWARF2_EH_FRAME_READ_ONLY),
>> > +			 1);
>> > +#ifdef md_fix_up_eh_frame
>> > +  md_fix_up_eh_frame (cfi_seg);
>> > +#else
>> > +  (void) cfi_seg;
>> > +#endif
>> > +
>> > +  frag_align (align, 0, 0);
>> > +  record_alignment (now_seg, align);
>> > +  if (last_fde->eh_header_type == EH_COMPACT_HAS_LSDA)
>> > +    output_compact_unwind_data (last_fde, align);
>> 
>> Please could you explain the EH_COMPACT_LEGACY handling here? 
>
> Would you please clarify this question?  I don't see the reference to
> EH_COMPACT_LEGACY.

That was the problem :-)  Further up there's:

  if (last_fde->eh_header_type != EH_COMPACT_LEGACY
      && last_fde->eh_header_type != EH_COMPACT_HAS_LSDA)
    {
      as_bad (_(".cfi_inline_lsda seen for frame without .cfi_lsda"));
      ignore_rest_of_line ();
      return;
    }

So what does this code mean/do in the:

  last_fde->eh_header_type == EH_COMPACT_LEGACY

case?

> diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
> index 0aab5fa..b7d7df0 100644
> --- a/bfd/elf-bfd.h
> +++ b/bfd/elf-bfd.h
> @@ -381,8 +381,10 @@ struct eh_frame_hdr_info
>  {
>    struct htab *cies;
>    asection *hdr_sec;
> -  unsigned int fde_count, array_count;
> +  unsigned int fde_count, array_count, allocated_entries;
>    struct eh_frame_array_ent *array;
> +  /* eh_frame_entry fragments.  */
> +  asection **entries;
>    /* TRUE if we should try to merge CIEs between input sections.  */
>    bfd_boolean merge_cies;
>    /* TRUE if all .eh_frames have been parsd.  */

I'm not sure there's enough commonality between the DWARF and compact
versions to share this structure.  Either we should have separate
structures or use a union to separate out the format-specific bits.

> @@ -1433,6 +1441,7 @@ struct bfd_elf_section_data
>  #define elf_next_in_group(sec)	(elf_section_data(sec)->next_in_group)
>  #define elf_fde_list(sec)	(elf_section_data(sec)->fde_list)
>  #define elf_sec_group(sec)	(elf_section_data(sec)->sec_group)
> +#define elf_section_eh_frame_entry(sec)	(elf_section_data(sec)->eh_frame_entry)

Long line.

> +/* Add a .eh_frame_entry section.  */
> +
> +static void
> +bfd_elf_remember_eh_frame_entry (struct eh_frame_hdr_info *hdr_info,
> +				 asection *sec)
> +{
> +  if (hdr_info->array_count == hdr_info->allocated_entries)
> +    {
> +      if (hdr_info->allocated_entries == 0)
> +	{
> +	  hdr_info->allocated_entries = 2;
> +	  hdr_info->entries = bfd_malloc (hdr_info->allocated_entries
> +					  * sizeof(hdr_info->entries[0]));
> +	}
> +      else
> +	{
> +	  hdr_info->allocated_entries *= 2;
> +	  hdr_info->entries = bfd_realloc (hdr_info->entries,
> +	    hdr_info->allocated_entries * sizeof(hdr_info->entries[0]));

Space before "sizeof" (both times).

> +	}
> +
> +      BFD_ASSERT (hdr_info->entries);
> +    }
> +
> +  hdr_info->entries[hdr_info->array_count++] = sec;
> +}
> +
> +/* Parse a .eh_frame_entry section.  Figure out which text section it
> +   references.  */
> +
> +void
> +_bfd_elf_parse_eh_frame_entry (bfd *abfd, struct bfd_link_info *info,
> +			       asection *sec, struct elf_reloc_cookie *cookie,
> +			       bfd_boolean remember)

This does more than the comment says and the name implies; the REMEMBER
stuff isn't mentioned.

The patch tries to do the parsing during bfd_elf_discard_info, but since
the parsing wants to be able to fail with an error, I think we need to do
it in an earlier pass.  We can then return a bfd_boolean success code
and propagate error returns up, which the current patch doesn't do.
Ideally we'd put the pass somewhere before GC, so that both the GC and
bfd_elf_discard_info stages can assume parsed .eh_frame_entry sections.

Having bfd_elf_discard_info add info (as per REMEMBER == TRUE) seems
a bit counterintuitive.  I think the earlier pass should record
all .eh_frame_entry sections and then the code currently in
_bfd_elf_end_eh_frame_parsing (but see below) should remove unwanted
entries from the eh_frame_hdr_info array.

> +  if (r_symndx >= cookie->locsymcount
> +      || ELF_ST_BIND (cookie->locsyms[r_symndx].st_info) != STB_LOCAL)
> +    {
> +      h = cookie->sym_hashes[r_symndx - cookie->extsymoff];
> +      while (h->root.type == bfd_link_hash_indirect
> +             || h->root.type == bfd_link_hash_warning)
> +        h = (struct elf_link_hash_entry *) h->root.u.i.link;
> +
> +      if (h->root.type != bfd_link_hash_defined
> +          && h->root.type != bfd_link_hash_defweak)
> +	goto fail;
> +
> +      text_sec = h->root.u.def.section;
> +    }
> +  else
> +    {
> +      Elf_Internal_Sym *isym;
> +
> +      /* Need to: get the symbol; get the section.  */
> +      isym = &cookie->locsyms[r_symndx];
> +      text_sec = bfd_section_from_elf_index (abfd, isym->st_shndx);
> +    }

Looks like this was lifted from elflink.c.  Please separate it out into
a subroutine that both sites can use.  E.g.:

asection *
_bfd_elf_section_for_symbol (struct elf_reloc_cookie *cookie,
			     unsigned long r_symndx)
{
...
}

returning null if not known.

> +fail:
> +  (*_bfd_error_handler) (_("%B: failed to precoess .eh_frame_entry"), sec->owner);

Long line.

> +/* Add space for a CANTUNWIND terminator to SEC is the text sections
> +   referenced by it and NEXT are not contiguous, or NEXT is NULL.  */

s/is the text/if the text/.

> +
> +static void
> +add_eh_frame_hdr_terminator (asection *sec,
> +			     asection *next)

No need for a line break in the arguments.

> +{
> +  bfd_vma end;
> +  bfd_vma next_start;
> +  asection *text_sec;
> +
> +  if (next)
> +    {
> +      /* See if there is a gap (presuably a text section without unwind info)
> +	 between these two entries.  */
> +      text_sec = (asection *) elf_section_data (sec)->sec_info;
> +      end = text_sec->output_section->vma + text_sec->output_offset
> +	    + text_sec->size;
> +      text_sec = (asection *) elf_section_data (next)->sec_info;
> +      next_start = text_sec->output_section->vma + text_sec->output_offset;
> +      /* Allow for alignment padding.  */
> +      end = BFD_ALIGN (end, 1 << bfd_get_section_alignment (text_sec->owner, text_sec));
> +      if (end == next_start)
> +	return;
> +    }
> +
> +  /* Add space for a CANTUNWIND terminator.  */
> +  if (!sec->rawsize)
> +    sec->rawsize = sec->size;
> +
> +  bfd_set_section_size (sec->owner, sec, sec->size + 8);
> +}

Is the idea to detect when there's "real" text (as opposed to padding)
between the two text sections?  If so, I don't think aligning the end
of SEC's text section according to NEXT's is correct.  If NEXT's text
section has a particularly high alignment then you could have
"end == next_start" even if there are other text sections inbetween.

Maybe in the first cut we should just be conservative and compare
the start and end directly, even if that leads to unnecessary CANTUNWINDs
for padding.

> +/* Finish a pass over all .eh_frame and eh_frame_entry sections.  */
> +
> +bfd_boolean
>  _bfd_elf_end_eh_frame_parsing (struct bfd_link_info *info)
>  {
>    struct eh_frame_hdr_info *hdr_info;
> +  unsigned int i;
>  
>    hdr_info = &elf_hash_table (info)->eh_info;
>    hdr_info->parsed_eh_frames = TRUE;
> +
> +  if (hdr_info->array_count == 0 || info->eh_frame_hdr < 2)
> +    return FALSE;
> +
> +  qsort (hdr_info->entries, hdr_info->array_count,
> +	 sizeof (asection *), cmp_eh_frame_hdr);
> +
> +  for (i = 0; i < hdr_info->array_count - 1; i++)
> +    {
> +      add_eh_frame_hdr_terminator (hdr_info->entries[i],
> +				   hdr_info->entries[i + 1]);
> +    }
> +
> +  /* Add a CANTUNWIND terminator after the last entry.  */
> +  add_eh_frame_hdr_terminator (hdr_info->entries[i], NULL);
> +  return TRUE;

This routine is called from both bfd_elf_gc_sections and
bfd_elf_discard_info but I think you only want it for bfd_elf_discard_info.
So perhaps this should be a separate function.

I wonder whether we could instead insert CANTUNWINDs earlier (say in the
non-dynamic part of size_dynamic_sections) based on the link order.
I.e. rather than comparing the start and end addresses of two sections,
we could just walk the sections in the order that they're going to be
linked.

It sounds like doing it that way would be more direct and more efficient.
Returning TRUE here forces the linker to map sections twice.

> @@ -1271,6 +1465,45 @@ _bfd_elf_eh_frame_present (struct bfd_link_info *info)
>    return FALSE;
>  }
>  
> +/* Return true if there is at least one .eh_frame_entry section in
> +   input files.  */
> +bfd_boolean
> +_bfd_elf_eh_frame_entry_present (struct bfd_link_info *info)
> +{
> +  asection *o;
> +  bfd *abfd;
> +
> +  for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link_next)
> +    {
> +      for (o = abfd->sections; o; o = o->next)
> +	{
> +	  const char *name = bfd_get_section_name (abfd, o);
> +
> +	  if (strcmp (name, ".eh_frame_entry")
> +	      && !bfd_is_abs_section (o->output_section))
> +	    {
> +	      if (strcmp (o->output_section->name, ".eh_frame_hdr"))
> +		return TRUE;
> +	      else
> +		{
> +		  (*_bfd_error_handler)
> +			(_("error: an '.eh_frame_entry'"
> +			   " input section that is not mapped to the"
> +			   " '.eh_frame_hdr' output section was seen"));
> +		  (*_bfd_error_handler)
> +			(_("note: try adding '*(.eh_frame_entry"
> +			   " .eh_frame_entry.*)' to the '.eh_frame_hdr'"
> +			   " output section description in the linker"
> +			   " script"));
> +		  bfd_set_error (bfd_error_invalid_operation);
> +		  return FALSE;
> +		}
> +	    }
> +	}
> +    }
> +  return FALSE;

The caller can't tell "FALSE because an error was reported" from
"FALSE because there was no .eh_frame_entry".  We should separate
out the two cases and propagate error returns up.

> @@ -1387,6 +1636,71 @@ _bfd_elf_eh_frame_section_offset (bfd *output_bfd ATTRIBUTE_UNUSED,
>  	  + extra_augmentation_data_bytes (sec_info->entry + mid));
>  }
>  
> +/* Write out .eh_frame_entry section.  Add CANTUNWIND terminator if needed.
> +   Also check that the contents look sane.  */
> +
> +bfd_boolean
> +_bfd_elf_write_section_eh_frame_entry (bfd *abfd,
> +				       asection *sec,
> +				       bfd_byte *contents)

Formatting: first two arguments fit on a line.

> +  text_sec = (asection *) elf_section_data (sec)->sec_info;
> +  addr = text_sec->output_section->vma + text_sec->output_offset
> +	 + text_sec->size;
> +  addr &= ~1;
> +  addr -= (sec->output_section->vma + sec->output_offset + sec->rawsize);
> +  BFD_ASSERT ((addr & 1) == 0);

It looks like this could trigger for odd-sized input text sections.
I think it should be an error instead of an assert.

> +  if (last_addr >= addr + sec->rawsize)
> +    {
> +      (*_bfd_error_handler) (_("%B: %s points past end of text section"),
> +			     sec->owner, sec->name);
> +      return FALSE;
> +    }
> +
> +  if (sec->size == sec->rawsize)
> +    return TRUE;
> +
> +  BFD_ASSERT (sec->size == sec->rawsize + 8);
> +  BFD_ASSERT ((addr & 1) == 0);
> +  bfd_put_32 (abfd, addr, cantunwind);
> +  bfd_put_32 (abfd, 0x015d5d01, cantunwind + 4);
> +  return bfd_set_section_contents (abfd, sec->output_section, cantunwind,
> +				   sec->output_offset + sec->rawsize, 8);

ARM and c6x seem to use 0 rather than 0x5d as the "no unwind" opcode,
is that right?  If so, I think this should be a hook.

The decision about whether to insert the CANTUNWIND is made during
bfd_elf_discard_info, but addresses can change after that “thanks”
to relaxation.  So this could in principle end up emitting a CANTUNWIND
for the same address as the following text section.

> @@ -1746,6 +2060,44 @@ vma_compare (const void *a, const void *b)
>    return 0;
>  }
>  
> +/* Reorder .eh_frame_entry sections to match the associated text sections.  */

I think a bit more commentary would help here.  E.g.:

/* Reorder .eh_frame_entry sections to match the associated text sections.
   This routine is called during the final linking step, just before writing
   the contents.  At this stage sections in the eh_frame_hdr_info are already
   sorted in order of increasing text section address and so we simply need
   to make the .eh_frame_entrys themselves follow that same order.  Note that
   it is invalid for a linker script to try to force a particular order of
   .eh_frame_entry sections.  */

However, even so:

> +
> +void
> +_bfd_elf_fixup_eh_frame_hdr (struct bfd_link_info *info)
> +{
> +  asection *sec = NULL;
> +  struct eh_frame_hdr_info *hdr_info;
> +  unsigned int i;
> +  bfd_vma offset;
> +  struct bfd_link_order *p;
> +
> +  hdr_info = &elf_hash_table (info)->eh_info;
> +
> +  if (hdr_info->hdr_sec == NULL || info->eh_frame_hdr < 2
> +      || hdr_info->array_count == 0)
> +    return;
> +
> +  /* Change section output offsets to be in text section order.  */
> +  offset = 8;
> +  for (i = 0; i < hdr_info->array_count; i++)
> +    {
> +      sec = hdr_info->entries[i];
> +      sec->output_offset = offset;
> +      offset += sec->size;
> +    }

...this assumes without checking that all .eh_frame_entry sections have
the same output section, and that there's nothing in that output section
besides these sections.  We ought to check for that.
(_bfd_elf_eh_frame_entry_present does some sanity checking, but AFAICT
only for one input section.)

We could either do the checking earlier or do it here, e.g. something like:

  /* Change section output offsets to be in text section order.  */
  offset = 8;
  osec = hdr_info->entries[0]->output_section;
  for (i = 0; i < hdr_info->array_count; i++)
    {
      sec = hdr_info->entries[i];
      if (sec->output_section != osec)
        ...error...
      sec->output_offset = offset;
      offset += sec->size;
    }

and:

> +  /* Fix the link_order to match.  */
> +  for (p = sec->output_section->map_head.link_order; p != NULL; p = p->next)
> +    {
> +      if (p->type != bfd_indirect_link_order)
> +	abort();
> +
> +      p->offset = p->u.indirect.section->output_offset;
> +      i--;
> +    }

if (i != 0)
  ...error...

(with bfd_boolean return of course).

> @@ -1787,6 +2145,36 @@ _bfd_elf_write_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
>        bfd_size_type size;
>        bfd_vma encoded_eh_frame;
>  
> +      if (info->eh_frame_hdr == 2)
> +	{
> +	  const struct elf_backend_data *bed;
> +	  bfd_vma count;
> +
> +	  contents = bfd_malloc (8);
> +	  if (contents == NULL)
> +	    return FALSE;
> +
> +	  if (sec->size != 8)
> +	    abort ();
> +
> +	  memset (contents, 0, 8);
> +	  contents[0] = 2;
> +	  bed = get_elf_backend_data (abfd);
> +	  if (bed->compact_eh_encoding)
> +	    contents[1] = (*bed->compact_eh_encoding) (info);
> +	  else
> +	    contents[0] = 0xff;

Can the 0xff case actually happen for MIPS?  If not, I think we should
assert or report an error instead.  It seems wrong to generate a header
but silently mark it invalid.

> +
> +	  count = (sec->output_section->size -9 ) / 8;

Why -9?  Deserves a comment at least.  The space should be before the 9.

> +	  bfd_put_32 (abfd, count, contents + 4);
> +	  retval = bfd_set_section_contents (abfd, sec->output_section,
> +					     contents,
> +					     (file_ptr) sec->output_offset,
> +					     sec->size);
> +	  free (contents);

malloc/free seems a bit excessive for 8 bytes.  We might as well just use
a stack array instead.

> @@ -10039,6 +10040,10 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
>  	      return FALSE;
>  	  }
>  	  break;
> +	case SEC_INFO_TYPE_EH_FRAME_ENTRY:
> +	    if (! _bfd_elf_write_section_eh_frame_entry (output_bfd, o, contents))
> +	      return FALSE;
> +	  break;

Excess indentation of the "if".

> @@ -11807,6 +11814,13 @@ _bfd_elf_gc_mark (struct bfd_link_info *info,
>  	}
>      }
>  
> +  eh_frame = elf_section_eh_frame_entry (sec);
> +  if (eh_frame && !eh_frame->gc_mark)
> +    {
> +    if (!_bfd_elf_gc_mark (info, eh_frame, gc_mark_hook))
> +      return FALSE;
> +    }

In this context we should be using "ret":

  eh_frame = elf_section_eh_frame_entry (sec);
  if (ret && eh_frame && !eh_frame->gc_mark)
    ret = _bfd_elf_gc_mark (info, eh_frame, gc_mark_hook);

> @@ -12190,22 +12204,42 @@ bfd_elf_gc_sections (bfd *abfd, struct bfd_link_info *info)
>    bed->gc_keep (info);
>  
>    /* Try to parse each bfd's .eh_frame section.  Point elf_eh_frame_section
> -     at the .eh_frame section if we can mark the FDEs individually.  */
> +     at the .eh_frame section if we can mark the FDEs individually.
> +     Establish links from text sections to their corresponding
> +     .eh_frame_entry sections.  */
>    _bfd_elf_begin_eh_frame_parsing (info);
>    for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
>      {
>        asection *sec;
>        struct elf_reloc_cookie cookie;
>  
> -      sec = bfd_get_section_by_name (sub, ".eh_frame");
> -      while (sec && init_reloc_cookie_for_section (&cookie, info, sec))
> +      if (!init_reloc_cookie (&cookie, info, sub))
> +	return FALSE;
> +
> +      if (info->eh_frame_hdr < 2)
>  	{
> -	  _bfd_elf_parse_eh_frame (sub, info, sec, &cookie);
> -	  if (elf_section_data (sec)->sec_info
> -	      && (sec->flags & SEC_LINKER_CREATED) == 0)
> -	    elf_eh_frame_section (sub) = sec;
> -	  fini_reloc_cookie_for_section (&cookie, sec);
> -	  sec = bfd_get_next_section_by_name (sec);
> +	  sec = bfd_get_section_by_name (sub, ".eh_frame");
> +	  if (sec && init_reloc_cookie_rels (&cookie, info, sub, sec))
> +	    {
> +	      _bfd_elf_parse_eh_frame (sub, info, sec, &cookie);
> +	      if (elf_section_data (sec)->sec_info
> +		  && (sec->flags & SEC_LINKER_CREATED) == 0)
> +		elf_eh_frame_section (sub) = sec;
> +	      fini_reloc_cookie_for_section (&cookie, sec);
> +	    }
> +	}
> +      else
> +	{
> +	  for (sec = sub->sections; sec; sec = sec->next)
> +	    {
> +	      if (CONST_STRNEQ (bfd_section_name (sub, sec), ".eh_frame_entry")
> +		  && init_reloc_cookie_rels (&cookie, info, sub, sec))
> +		{
> +		  _bfd_elf_parse_eh_frame_entry (sub, info, sec, &cookie,
> +						 FALSE);
> +		  fini_reloc_cookie_for_section (&cookie, sec);
> +		}
> +	    }

This changes the info->eh_frame_hdr != 2 case to only handle the first
.eh_frame section.  ("while" -> "if").

Also, the init/fini_reloc_cookie* calls don't match up.  I assume the
idea is to avoid excessive allocation and freeing of the locsyms,
but in that case you should use fini_reloc_cookie_rels instead of
fini_reloc_cookie_for_section and call fini_reloc_cookie at the end.
At the moment I think this leaks memory if there are no EH sections.

I don't know either way whether splitting the init_reloc_cookie_for_section
call up is a win or not for .eh_frame.  It will be a win if there are
multiple EH sections but a loss if there are none, since we then
initialise and free the bfd-level information unnecessarily.  So I think
it might make sense to keep the .eh_frame code as it is now and restrict
the *_rels to the new code.

> @@ -12681,7 +12717,7 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
>        bed = get_elf_backend_data (abfd);
>  
>        eh = NULL;
> -      if (!info->relocatable)
> +      if ((info->eh_frame_hdr < 2) && !info->relocatable)

Formatting: no brackets around "info->eh_frame_hdr < 2".

> @@ -5113,6 +5117,17 @@ mips_elf_relocation_needs_la25_stub (bfd *input_bfd, int r_type,
>        return FALSE;
>      }
>  }
> +
> +/* On some targets R_MIPS_EH is interpreted as R_MIPS_PC32.  */
> +static int
> +mips_elf_real_reloc (struct bfd_link_info *info, int r_type)
> +{
> +  struct mips_elf_link_hash_table *htab = mips_elf_hash_table (info);
> +  if (r_type == R_MIPS_EH)
> +    return htab->eh_reloc_type;
> +
> +  return r_type;
> +}

This seems a bit error-prone, since if a mips_elf_real_reloc call
("what am I really looking at?") gets missed at some point it will
still work in the common case of R_MIPS_EH being unchanged.  If we
do add the R_MIPS_GOT_DISP32 suggested above then that problem would
go away, since R_MIPS_EH would always be replaced by something else.

> @@ -1648,6 +1649,7 @@ static const pseudo_typeS mips_pseudo_table[] =
>    {"tprelword", s_tprelword, 0},
>    {"tpreldword", s_tpreldword, 0},
>    {"gpvalue", s_gpvalue, 0},
> +  {"forcegpword", s_force_gpword, 0},
>    {"gpword", s_gpword, 0},
>    {"gpdword", s_gpdword, 0},
>    {"ehword", s_ehword, 0},

Would prefer s_forcegpword, for consistency.

> @@ -18249,3 +18256,13 @@ tc_mips_regname_to_dw2regnum (char *regname)
>  
>    return regnum;
>  }
> +
> +#if defined (OBJ_ELF)
> +bfd_reloc_code_real_type
> +mips_cfi_reloc_for_encoding (int encoding)
> +{
> +  if ((encoding & 0x70) == DW_EH_PE_datarel)
> +    return BFD_RELOC_GPREL32;
> +  return BFD_RELOC_32_PCREL;
> +}
> +#endif

Given the answer to what "special" means above, I think we should combine
tc_cfi_special_encoding and tc_cfi_reloc_for_encoding into a single hook
that returns BFD_RELOC_NONE for an encoding that isn't special.

As mentioned above, I don't think R_MIPS_GPREL32 is right for the
datarel-indirect case.

> diff --git a/gas/config/tc-mips.h b/gas/config/tc-mips.h
> index c7eaa04..a300062 100644
> --- a/gas/config/tc-mips.h
> +++ b/gas/config/tc-mips.h
> @@ -177,7 +177,9 @@ extern enum dwarf2_format mips_dwarf2_format (asection *);
>  
>  extern int mips_dwarf2_addr_size (void);
>  #define DWARF2_ADDR_SIZE(bfd) mips_dwarf2_addr_size ()
> -#define DWARF2_FDE_RELOC_SIZE mips_dwarf2_addr_size ()
> +#define DWARF2_FDE_RELOC_SIZE (compact_eh ? 4 : mips_dwarf2_addr_size ())
> +#define DWARF2_FDE_RELOC_ENCODING(enc) \
> +  (enc | (compact_eh ? DW_EH_PE_pcrel : 0))

What case is this handling?  Please explain this a bit more.

> @@ -4566,6 +4586,22 @@ argument is not present, otherwise second argument should be a constant
>  or a symbol name.  The default after @code{.cfi_startproc} is @code{.cfi_lsda 0xff},
>  no LSDA.
>  
> +@section @code{.cfi_inline_lsda} [@var{align}]
> +@code{.cfi_inline_lsda} marks the start of a LSDA data section and
> +switches to the corresponding @code{.gnu.extab} section.
> +Must be preceded by a CFI block containing a @code{.cfi_lsda} directive.
> +Only valid when generating compact EH frames (i.e.
> +with @code{.cfi_sections eh_frame_entry}.

Missing ")".

> @@ -4643,6 +4679,20 @@ mark a code segment that has only one return address which is reached
>  by a direct branch and no copy of the return address exists in memory
>  or another register.
>  
> +@section @code{.cfi_epilogue_begin}
> +A pseudo-operation which marks the beginning of the epilogue in a 
> +given function.  This is currently used by the compact unwind-table
> +implementation (for exception handling), which assumes a single register
> +state for unwinding throughout the body of a function.  The function is
> +scanned, and CFI directives are rewritten in a compact form.  However,
> +recent versions of GCC emit unwind information for function epilogues,
> +which should not be represented in this compact form: hence, any CFI
> +directives which occur in a function after @code{.cfi_epilogue_end}
> +are not processed.
> +
> +This operation only affects the internals of the assembler, and is not
> +represented in the binary output.

Don't really follow this, sorry.  Can you give an example, or better yet,
a testcase?

> @@ -161,6 +278,9 @@ alloc_debugseg_item (segT seg, int subseg, char *name)
>  static segT
>  is_now_linkonce_segment (void)
>  {
> +  if (compact_eh)
> +    return now_seg;
> +

Please add a comment explaining why this is correct.

> -/* Emit a single byte into the current segment.  */
> -
> -static inline void
> -out_one (int byte)
> +static segT
> +get_cfi_seg (segT cseg, const char *base, flagword flags, int align)
>  {
> -  FRAG_APPEND_1_CHAR (byte);
> +  if (SUPPORT_FRAME_LINKONCE || ((flags & SEC_DEBUGGING) == 0 && compact_eh))
> +    {

Please add a comment here too to explain the SEC_DEBUGGING test.

> +/* Set the personality id in the FDE structure.  */
> +
> +static void
> +dot_cfi_personality_id (int ignored ATTRIBUTE_UNUSED)
>  {
> -  md_number_to_chars (frag_more (2), data, 2);
> +  struct fde_entry *fde;
> +
> +  if (frchain_now->frch_cfi_data == NULL)
> +    {
> +      as_bad (_("CFI instruction used without previous .cfi_startproc"));
> +      ignore_rest_of_line ();
> +      return;
> +    }
> +
> +  fde = frchain_now->frch_cfi_data->cur_fde_data;
> +  fde->personality_id = cfi_parse_const ();
> +  demand_empty_rest_of_line ();
> +
> +  if (fde->personality_id == 0 || fde->personality_id > 3)
> +    {
> +      as_bad (_("wrong argument to .cfi_personality_id"));
> +      return;
> +    }

No need for { ... return; }

> +static void
> +output_compact_unwind_data (struct fde_entry *fde, int align)

Probably worth a comment here, since it wasn't obvious to me the
"align" is the alignment of the end of the data rather than the start.

>  {
> -  md_number_to_chars (frag_more (4), data, 4);
> +  int data_size = fde->eh_data_size + 2;

Add a comment for the "2" (the personality encoding and the stop byte, right?)

> +  int align_padding;
> +  int amask;
> +  char *p;
> +
> +  fde->eh_loc = symbol_temp_new_now ();
> +
> +  p = frag_more (1);
> +  if (fde->personality_id != 0)
> +    *p = fde->personality_id;
> +  else if (fde->per_encoding != DW_EH_PE_omit)
> +    {
> +      *p = 0;
> +      emit_expr_encoded (&fde->personality, fde->per_encoding, FALSE);
> +      data_size += encoding_size (fde->per_encoding);
> +    }
> +  else
> +    *p = 1;
> +
> +  amask = (1 << align) - 1;
> +  align_padding = ((data_size + amask) & ~amask) - data_size;

Simpler as:

  align_padding = -data_size & amask.

> +  align = get_absolute_expression ();
> +  if (align > max_alignment)
> +    {
> +      align = max_alignment;
> +      as_bad (_("Alignment too large: %d. assumed."), align);

No "." after %d.

> +  /* Open .gnu_extab section.  */
> +  cfi_seg = get_cfi_seg (ccseg, ".gnu_extab",
> +			 (SEC_ALLOC | SEC_LOAD | SEC_DATA
> +			  | DWARF2_EH_FRAME_READ_ONLY),
> +			 1);
> +#ifdef md_fix_up_eh_frame
> +  md_fix_up_eh_frame (cfi_seg);
> +#else
> +  (void) cfi_seg;
> +#endif

md_fix_up_eh_frame doesn't seem appropriate for .gnu_extab.

> @@ -1576,29 +1889,43 @@ output_fde (struct fde_entry *fde, struct cie_entry *cie,
>        TC_DWARF2_EMIT_OFFSET (cie->start_address, offset_size);
>      }
>  
> +  exp.X_op = O_symbol;
> +  exp.X_add_symbol = fde->start_address;
>    if (eh_frame)
>      {
> -      exp.X_op = O_subtract;
> -      exp.X_add_number = 0;
> +      if (tc_cfi_special_encoding (cie->fde_encoding))
> +	{
> +	  bfd_reloc_code_real_type code
> +	    = tc_cfi_reloc_for_encoding (cie->fde_encoding);
> +	  reloc_howto_type *howto = bfd_reloc_type_lookup (stdoutput, code);
> +	  char *p = frag_more(4);

Space before "(".

> +	  md_number_to_chars (p, 0, 4);
> +	  fix_new (frag_now, p - frag_now->fr_literal, 4, exp.X_add_symbol,
> +		   exp.X_add_number, howto->pc_relative, code);

What's the significance of exp.X_add_number here?  If it was supposed
to be initialised to 0 above then I think it would be easier to leave
the "exp" stuff alone and just use fde->start_address and 0 directly
in this fix_new call.  Certainly...

>    else
>      {
> -      exp.X_op = O_symbol;
> -      exp.X_add_symbol = fde->start_address;
>        exp.X_add_number = 0;
>        addr_size = DWARF2_ADDR_SIZE (stdoutput);
>        emit_expr (&exp, addr_size);

...separating the add_symbol and add_number feels odd.

> @@ -1614,24 +1941,7 @@ output_fde (struct fde_entry *fde, struct cie_entry *cie,
>    if (eh_frame)
>      out_uleb128 (augmentation_size);		/* Augmentation size.  */
>  
> -  if (fde->lsda_encoding != DW_EH_PE_omit)
> -    {
> -      exp = fde->lsda;
> -      if ((fde->lsda_encoding & 0x70) == DW_EH_PE_pcrel)
> -	{
> -#if CFI_DIFF_LSDA_OK
> -	  exp.X_op = O_subtract;
> -	  exp.X_op_symbol = symbol_temp_new_now ();
> -	  emit_expr (&exp, augmentation_size);
> -#elif defined (tc_cfi_emit_pcrel_expr)
> -	  tc_cfi_emit_pcrel_expr (&exp, augmentation_size);
> -#else
> -	  abort ();
> -#endif
> -	}
> -      else
> -	emit_expr (&exp, augmentation_size);
> -    }
> +  emit_expr_encoded (&fde->lsda, cie->lsda_encoding, FALSE);

This emit_expr_encoded stuff was a nice clean-up, thanks.

> +  if (fde->eh_header_type == EH_COMPACT_INLINE)
> +    {
> +      p = frag_more (4);
> +      /* Inline entries always use PR1.  */
> +      *(p++) = 1;
> +      memcpy(p, fde->eh_data, 3);

Space before "(".

> @@ -1888,7 +2223,17 @@ cfi_finish (void)
>  
>  	  for (fde = all_fde_data; fde ; fde = fde->next)
>  	    {
> -	      if (SUPPORT_FRAME_LINKONCE)
> +	      if ((fde->sections & CFI_EMIT_eh_frame) == 0)
> +		continue;
> +
> +#if SUPPORT_COMPACT_EH
> +	      if (fde->eh_header_type == EH_COMPACT_HAS_LSDA)
> +		fde->eh_header_type = EH_COMPACT_LEGACY;
> +
> +	      if (fde->eh_header_type != EH_COMPACT_LEGACY)
> +		continue;
> +#endif

Please add a comment explaining this.

> +#if SUPPORT_COMPACT_EH
> +      if (compact_eh)
> +	{
> +	  /* Create remaining out of line table entries.  */
> +	  do
> +	    {
> +	      ccseg = NULL;
> +	      seek_next_seg = 0;
> +
> +	      for (fde = all_fde_data; fde ; fde = fde->next)
> +		{
> +		  if ((fde->sections & CFI_EMIT_eh_frame) == 0)
> +		    continue;
> +
> +		  if (fde->eh_header_type != EH_COMPACT_OUTLINE)
> +		    continue;
> +		  if (HANDLED (fde))
> +		    continue;
> +		  if (seek_next_seg && CUR_SEG (fde) != ccseg)
> +		    {
> +		      seek_next_seg = 2;
> +		      continue;
> +		    }
> +		  if (!seek_next_seg)
> +		    {
> +		      ccseg = CUR_SEG (fde);
> +		      /* Open .gnu_extab section.  */
> +		      cfi_seg = get_cfi_seg (ccseg, ".gnu_extab",
> +					     (SEC_ALLOC | SEC_LOAD | SEC_DATA
> +					      | DWARF2_EH_FRAME_READ_ONLY),
> +					     1);
> +#ifdef md_fix_up_eh_frame
> +		      md_fix_up_eh_frame (cfi_seg);
> +#else
> +		      (void) cfi_seg;
> +#endif

As above, I don't think the md_fix_up_eh_frame is appropriate.

> +		      seek_next_seg = 1;
> +		    }
> +		  SET_HANDLED (fde, 1);
> +
> +		  frag_align (1, 0, 0);
> +		  record_alignment (now_seg, 1);
> +		  output_compact_unwind_data (fde, 1);
> +		}
> +	    }
> +	  while (EH_FRAME_LINKONCE && seek_next_seg == 2);

The quadraticness seems a bit unfortunate, but I realise you're just
following the existing structure.

> @@ -1958,6 +2394,9 @@ cfi_finish (void)
>  
>  	  for (fde = all_fde_data; fde ; fde = fde->next)
>  	    {
> +	      if ((fde->sections & CFI_EMIT_debug_frame) == 0)
> +		continue;
> +
>  	      if (SUPPORT_FRAME_LINKONCE)
>  		{
>  		  if (HANDLED (fde))

Please add a comment explaining why this is needed/correct.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Patch] Gas support for MIPS Compact EH
  2014-02-08 16:34     ` Richard Sandiford
@ 2014-02-08 18:48       ` Bernd Schmidt
  2014-02-09 11:11         ` Richard Sandiford
  2014-03-06 17:44       ` Moore, Catherine
                         ` (2 subsequent siblings)
  3 siblings, 1 reply; 31+ messages in thread
From: Bernd Schmidt @ 2014-02-08 18:48 UTC (permalink / raw)
  To: Moore, Catherine, binutils, rdsandiford

On 02/08/2014 05:34 PM, Richard Sandiford wrote:
>>> The tc_cfi_fix_eh_ref and tc_cfi_emit_expr hooks don't seem very
>>> consistent; the former relies on the caller to clear the bytes, whereas the
>>> latter is supposed to do it itself.
>>
>> All fixed, now using a hook to return a reloc and eliminated the use of
>> R_MIPS_EH from the assembler.
>
> Hmm, but how does it work under the new scheme?  It looks like gas now
> always emits the .eh_frame_entry sections using R_MIPS_PC32, is that right?
> But the linker chooses the .eh_frame_hdr encoding based on --pcrel-eh-reloc,
> which also controls how R_MIPS_EH is handled.  So if the:
>
>    DW_EH_PE_sdata4 | DW_EH_PE_datarel | DW_EH_PE_indirect
>
> encoding is chosen for the .eh_frame_entry sections at link time, what
> converts the input sections to use that encoding instead of the original
> R_MIPS_PC32?  I'd assumed R_MIPS_EH was defined the way it was to avoid
> that kind of thing.

What's changed is that the linker is no longer really involved in these 
decisions - the code you see in the linker-specific parts of the patch 
are there merely to deal with R_MIPS_EH relocs in object files generated 
by previous toolchains. We've always kind of already decided at compile 
time which encoding to use and passed the -pcrel-eh-reloc option to the 
linker to ensure it made the choice we wanted. What's new in this 
version of the patches is based on the realization that gcc can produce 
datarel|indirect encoding without linker help (using the new forcegpword 
op). On Linux targets you'll see this generated by the compiler, on 
bare-metal you'll get R_MIPS_PC32. The R_MIPS_EH reloc is longer 
produced. So it's all a lot more straightforward, directly producing an 
encoding appropriate for the target at compile time.

> I thought R_MIPS_EH would be used for the .eh_frame_entry entries only,
> since in that case the actual encoding of the address isn't known
> until link time.

I think previous versions of the code were just slightly confused - the 
idea was that datarel|indirect required things to be put into the got, 
which has to be done by the linker. It turns out that this isn't 
necessary, so there is no longer a need to use R_MIPS_EH.

Does this clarify things?


Bernd

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Patch] Gas support for MIPS Compact EH
  2014-02-08 18:48       ` Bernd Schmidt
@ 2014-02-09 11:11         ` Richard Sandiford
  2014-02-19 23:13           ` Moore, Catherine
  0 siblings, 1 reply; 31+ messages in thread
From: Richard Sandiford @ 2014-02-09 11:11 UTC (permalink / raw)
  To: Bernd Schmidt; +Cc: Moore, Catherine, binutils

Bernd Schmidt <bernds@codesourcery.com> writes:
> On 02/08/2014 05:34 PM, Richard Sandiford wrote:
>>>> The tc_cfi_fix_eh_ref and tc_cfi_emit_expr hooks don't seem very
>>>> consistent; the former relies on the caller to clear the bytes, whereas the
>>>> latter is supposed to do it itself.
>>>
>>> All fixed, now using a hook to return a reloc and eliminated the use of
>>> R_MIPS_EH from the assembler.
>>
>> Hmm, but how does it work under the new scheme?  It looks like gas now
>> always emits the .eh_frame_entry sections using R_MIPS_PC32, is that right?
>> But the linker chooses the .eh_frame_hdr encoding based on --pcrel-eh-reloc,
>> which also controls how R_MIPS_EH is handled.  So if the:
>>
>>    DW_EH_PE_sdata4 | DW_EH_PE_datarel | DW_EH_PE_indirect
>>
>> encoding is chosen for the .eh_frame_entry sections at link time, what
>> converts the input sections to use that encoding instead of the original
>> R_MIPS_PC32?  I'd assumed R_MIPS_EH was defined the way it was to avoid
>> that kind of thing.
>
> What's changed is that the linker is no longer really involved in these 
> decisions - the code you see in the linker-specific parts of the patch 
> are there merely to deal with R_MIPS_EH relocs in object files generated 
> by previous toolchains. We've always kind of already decided at compile 
> time which encoding to use and passed the -pcrel-eh-reloc option to the 
> linker to ensure it made the choice we wanted. What's new in this 
> version of the patches is based on the realization that gcc can produce 
> datarel|indirect encoding without linker help (using the new forcegpword 
> op).

Ah, OK, so it's now up to the assembler writer to do the indirection
by hand?  I hadn't realised that.  (To be fair, the patch added the
.forcegpword directive but didn't have any examples or tests to show how
it was used, or any documentation explaining it.  I deliberately didn't
complain about the latter though because most ops are undocumented.)

Using GP-relative is probably a bad idea for MIPS because the GP base
can vary in the case of multigot.  When resolving the relocations in the
individual input sections, the GP used will be for that input bfd's GOT,
which isn't necessarily going to be the primary GOT.  So if we're doing
the indirection by hand, why not use pcrel|indirect instead?  I suppose
that amounts to using .ehword (under the new PC-relative definition)
rather than .forcegpword.  I think it'd be better to drop .forcegpword
if possible.

FWIW, pcrel|indirect is what the linker tries to use for .eh_frame on MIPS,
if the original input object used absolute indirect.

> On Linux targets you'll see this generated by the compiler, on 
> bare-metal you'll get R_MIPS_PC32. The R_MIPS_EH reloc is longer 
> produced. So it's all a lot more straightforward, directly producing an 
> encoding appropriate for the target at compile time.
>
>> I thought R_MIPS_EH would be used for the .eh_frame_entry entries only,
>> since in that case the actual encoding of the address isn't known
>> until link time.
>
> I think previous versions of the code were just slightly confused - the 
> idea was that datarel|indirect required things to be put into the got, 
> which has to be done by the linker. It turns out that this isn't 
> necessary, so there is no longer a need to use R_MIPS_EH.
>
> Does this clarify things?

I don't think it really answers my first question.  It's still the
linker that decides what encoding goes in the .eh_frame_hdr.
Then all the following .eh_frame_entry sections must use that
encoding for the text addresses.

The input objects use relocations to mark those .eh_frame_entry text
addresses.  In the original scheme those relocations were R_MIPS_EH,
giving the linker control of both the .eh_frame_hdr encoding and the
.eh_frame_entry fields that that encoding controls.  That part seemed
consistent and safe.  (The unsafe part was that other parts of the
assembler seemed to assume that R_MIPS_EH always meant datarel-indirect.)

In the new scheme the assembler picks an encoding-specific relocation
for those .eh_frame_entry text addresses (always PC-relative in the posted
patch), but the linker is still the one that chooses the .eh_frame_hdr
encoding.  And in the posted patch the encoding used in .eh_frame_hdr is
decided by the --pcrel-eh-reloc option.  The default linker behaviour is
to use datarel-indirect, which is the opposite of what the assembler now
uses.  So if I do:

    as foo.s -o foo.o
    ld foo.o -o foo

it looked like foo.o would use a PC-relative encoding for any
.eh_frame_entry sections, but foo's .eh_frame_hdr would say that
they are datarel-indirect rather than PC-relative.

In other words, I think using datarel-indirect for the .eh_frame_hdr is
only safe if all input objects are using the old scheme.  So that seems
like a bad default.  And I'm not sure --pcrel-eh-reloc is safe for old
objects because of the assumption in some cases that R_MIPS_EH would
generate datarel-indirect.

Maybe for the FSF version we should just drop the R_MIPS_EH stuff
altogether, since at this point it sounds like it would be safer
to reject the old objects than to try to guess what's happening.
The .eh_frame_hdr could then be hard-coded to use a PC-relative
encoding, like the assembler is now.  It seems a bit of a shame
in a way though -- and like I say, using PC-relative wouldn't
apply well to targets like VxWorks where the gap between the text
and data segments isn't fixed at link time.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 31+ messages in thread

* RE: [Patch] Gas support for MIPS Compact EH
  2014-02-09 11:11         ` Richard Sandiford
@ 2014-02-19 23:13           ` Moore, Catherine
  2014-02-20 11:36             ` Richard Sandiford
  0 siblings, 1 reply; 31+ messages in thread
From: Moore, Catherine @ 2014-02-19 23:13 UTC (permalink / raw)
  To: Richard Sandiford, Schmidt, Bernd; +Cc: binutils



> -----Original Message-----
> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> Sent: Sunday, February 09, 2014 6:11 AM
> To: Schmidt, Bernd
> Cc: Moore, Catherine; binutils@sourceware.org
> Subject: Re: [Patch] Gas support for MIPS Compact EH
> 
> Bernd Schmidt <bernds@codesourcery.com> writes:
> > On 02/08/2014 05:34 PM, Richard Sandiford wrote:
> >>>> The tc_cfi_fix_eh_ref and tc_cfi_emit_expr hooks don't seem very
> >>>> consistent; the former relies on the caller to clear the bytes,
> >>>> whereas the latter is supposed to do it itself.
> >>>
> >>> All fixed, now using a hook to return a reloc and eliminated the use
> >>> of R_MIPS_EH from the assembler.
> >>
> >> Hmm, but how does it work under the new scheme?  It looks like gas
> >> now always emits the .eh_frame_entry sections using R_MIPS_PC32, is
> that right?
> >> But the linker chooses the .eh_frame_hdr encoding based on
> >> --pcrel-eh-reloc, which also controls how R_MIPS_EH is handled.  So if
> the:
> >>
> >>    DW_EH_PE_sdata4 | DW_EH_PE_datarel | DW_EH_PE_indirect
> >>
> >> encoding is chosen for the .eh_frame_entry sections at link time,
> >> what converts the input sections to use that encoding instead of the
> >> original R_MIPS_PC32?  I'd assumed R_MIPS_EH was defined the way it
> >> was to avoid that kind of thing.
> >
> > What's changed is that the linker is no longer really involved in
> > these decisions - the code you see in the linker-specific parts of the
> > patch are there merely to deal with R_MIPS_EH relocs in object files
> > generated by previous toolchains. We've always kind of already decided
> > at compile time which encoding to use and passed the -pcrel-eh-reloc
> > option to the linker to ensure it made the choice we wanted. What's
> > new in this version of the patches is based on the realization that
> > gcc can produce
> > datarel|indirect encoding without linker help (using the new
> > datarel|forcegpword
> > op).
> 
> Ah, OK, so it's now up to the assembler writer to do the indirection by hand?
> I hadn't realised that.  (To be fair, the patch added the .forcegpword directive
> but didn't have any examples or tests to show how it was used, or any
> documentation explaining it.  I deliberately didn't complain about the latter
> though because most ops are undocumented.)
> 
> Using GP-relative is probably a bad idea for MIPS because the GP base can
> vary in the case of multigot.  When resolving the relocations in the individual
> input sections, the GP used will be for that input bfd's GOT, which isn't
> necessarily going to be the primary GOT.  So if we're doing the indirection by
> hand, why not use pcrel|indirect instead?  I suppose that amounts to using
> .ehword (under the new PC-relative definition) rather than .forcegpword.  I
> think it'd be better to drop .forcegpword if possible.
> 
> FWIW, pcrel|indirect is what the linker tries to use for .eh_frame on MIPS, if
> the original input object used absolute indirect.
> 
> > On Linux targets you'll see this generated by the compiler, on
> > bare-metal you'll get R_MIPS_PC32. The R_MIPS_EH reloc is longer
> > produced. So it's all a lot more straightforward, directly producing
> > an encoding appropriate for the target at compile time.
> >
> >> I thought R_MIPS_EH would be used for the .eh_frame_entry entries
> >> only, since in that case the actual encoding of the address isn't
> >> known until link time.
> >
> > I think previous versions of the code were just slightly confused -
> > the idea was that datarel|indirect required things to be put into the
> > got, which has to be done by the linker. It turns out that this isn't
> > necessary, so there is no longer a need to use R_MIPS_EH.
> >
> > Does this clarify things?
> 
> I don't think it really answers my first question.  It's still the linker that decides
> what encoding goes in the .eh_frame_hdr.
> Then all the following .eh_frame_entry sections must use that encoding for
> the text addresses.
> 
> The input objects use relocations to mark those .eh_frame_entry text
> addresses.  In the original scheme those relocations were R_MIPS_EH, giving
> the linker control of both the .eh_frame_hdr encoding and the
> .eh_frame_entry fields that that encoding controls.  That part seemed
> consistent and safe.  (The unsafe part was that other parts of the assembler
> seemed to assume that R_MIPS_EH always meant datarel-indirect.)
> 
> In the new scheme the assembler picks an encoding-specific relocation for
> those .eh_frame_entry text addresses (always PC-relative in the posted
> patch), but the linker is still the one that chooses the .eh_frame_hdr
> encoding.  And in the posted patch the encoding used in .eh_frame_hdr is
> decided by the --pcrel-eh-reloc option.  The default linker behaviour is to use
> datarel-indirect, which is the opposite of what the assembler now uses.  So if
> I do:
> 
>     as foo.s -o foo.o
>     ld foo.o -o foo
> 
> it looked like foo.o would use a PC-relative encoding for any
> .eh_frame_entry sections, but foo's .eh_frame_hdr would say that they are
> datarel-indirect rather than PC-relative.
> 
> In other words, I think using datarel-indirect for the .eh_frame_hdr is only
> safe if all input objects are using the old scheme.  So that seems like a bad
> default.  And I'm not sure --pcrel-eh-reloc is safe for old objects because of
> the assumption in some cases that R_MIPS_EH would generate datarel-
> indirect.
> 
> Maybe for the FSF version we should just drop the R_MIPS_EH stuff
> altogether, since at this point it sounds like it would be safer to reject the old
> objects than to try to guess what's happening.
> The .eh_frame_hdr could then be hard-coded to use a PC-relative encoding,
> like the assembler is now.  It seems a bit of a shame in a way though -- and
> like I say, using PC-relative wouldn't apply well to targets like VxWorks where
> the gap between the text and data segments isn't fixed at link time.
> 

Dropping the R_MIPS_EH stuff is not a desirable option for us.
If we were to go back to the original implementation, using R_MIPS_EH for entries in the .eh_frame_entry sections, then your objection to the implementation was the inconsistent treatment in the assembler?

You said:
RS>  Or, to put it another way, why are we making the choice between
RS> R_MIPS_PC32 and R_MIPS_EH at assembly time in mips_cfi_emit_expr, but not in mips_cfi_fix_eh_ref, even though the patch appears to allow the same two encodings in both casees?

I'd like to take this approach in the next patch:
1. Keep the R_MIPS_EH relocation
2. Let the linker choose the appropriate encoding
3. Clean up the assembler inconsistencies

This would also mean dropping the .forcegpword directive as you suggested.  

How does that sound?
Catherine




^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Patch] Gas support for MIPS Compact EH
  2014-02-19 23:13           ` Moore, Catherine
@ 2014-02-20 11:36             ` Richard Sandiford
  2014-02-20 21:24               ` Moore, Catherine
  2014-02-24 20:48               ` Moore, Catherine
  0 siblings, 2 replies; 31+ messages in thread
From: Richard Sandiford @ 2014-02-20 11:36 UTC (permalink / raw)
  To: Moore, Catherine; +Cc: Schmidt, Bernd, binutils

"Moore, Catherine" <Catherine_Moore@mentor.com> writes:
>> -----Original Message-----
>> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
>> Sent: Sunday, February 09, 2014 6:11 AM
>> To: Schmidt, Bernd
>> Cc: Moore, Catherine; binutils@sourceware.org
>> Subject: Re: [Patch] Gas support for MIPS Compact EH
>> 
>> Bernd Schmidt <bernds@codesourcery.com> writes:
>> > On 02/08/2014 05:34 PM, Richard Sandiford wrote:
>> >>>> The tc_cfi_fix_eh_ref and tc_cfi_emit_expr hooks don't seem very
>> >>>> consistent; the former relies on the caller to clear the bytes,
>> >>>> whereas the latter is supposed to do it itself.
>> >>>
>> >>> All fixed, now using a hook to return a reloc and eliminated the use
>> >>> of R_MIPS_EH from the assembler.
>> >>
>> >> Hmm, but how does it work under the new scheme?  It looks like gas
>> >> now always emits the .eh_frame_entry sections using R_MIPS_PC32, is
>> that right?
>> >> But the linker chooses the .eh_frame_hdr encoding based on
>> >> --pcrel-eh-reloc, which also controls how R_MIPS_EH is handled.  So if
>> the:
>> >>
>> >>    DW_EH_PE_sdata4 | DW_EH_PE_datarel | DW_EH_PE_indirect
>> >>
>> >> encoding is chosen for the .eh_frame_entry sections at link time,
>> >> what converts the input sections to use that encoding instead of the
>> >> original R_MIPS_PC32?  I'd assumed R_MIPS_EH was defined the way it
>> >> was to avoid that kind of thing.
>> >
>> > What's changed is that the linker is no longer really involved in
>> > these decisions - the code you see in the linker-specific parts of the
>> > patch are there merely to deal with R_MIPS_EH relocs in object files
>> > generated by previous toolchains. We've always kind of already decided
>> > at compile time which encoding to use and passed the -pcrel-eh-reloc
>> > option to the linker to ensure it made the choice we wanted. What's
>> > new in this version of the patches is based on the realization that
>> > gcc can produce
>> > datarel|indirect encoding without linker help (using the new
>> > datarel|forcegpword
>> > op).
>> 
>> Ah, OK, so it's now up to the assembler writer to do the indirection by hand?
>> I hadn't realised that.  (To be fair, the patch added the .forcegpword
>> directive
>> but didn't have any examples or tests to show how it was used, or any
>> documentation explaining it.  I deliberately didn't complain about the latter
>> though because most ops are undocumented.)
>> 
>> Using GP-relative is probably a bad idea for MIPS because the GP base can
>> vary in the case of multigot.  When resolving the relocations in the
>> individual
>> input sections, the GP used will be for that input bfd's GOT, which isn't
>> necessarily going to be the primary GOT.  So if we're doing the indirection by
>> hand, why not use pcrel|indirect instead?  I suppose that amounts to using
>> .ehword (under the new PC-relative definition) rather than .forcegpword.  I
>> think it'd be better to drop .forcegpword if possible.
>> 
>> FWIW, pcrel|indirect is what the linker tries to use for .eh_frame on MIPS, if
>> the original input object used absolute indirect.
>> 
>> > On Linux targets you'll see this generated by the compiler, on
>> > bare-metal you'll get R_MIPS_PC32. The R_MIPS_EH reloc is longer
>> > produced. So it's all a lot more straightforward, directly producing
>> > an encoding appropriate for the target at compile time.
>> >
>> >> I thought R_MIPS_EH would be used for the .eh_frame_entry entries
>> >> only, since in that case the actual encoding of the address isn't
>> >> known until link time.
>> >
>> > I think previous versions of the code were just slightly confused -
>> > the idea was that datarel|indirect required things to be put into the
>> > got, which has to be done by the linker. It turns out that this isn't
>> > necessary, so there is no longer a need to use R_MIPS_EH.
>> >
>> > Does this clarify things?
>> 
>> I don't think it really answers my first question.  It's still the
>> linker that decides
>> what encoding goes in the .eh_frame_hdr.
>> Then all the following .eh_frame_entry sections must use that encoding for
>> the text addresses.
>> 
>> The input objects use relocations to mark those .eh_frame_entry text
>> addresses.  In the original scheme those relocations were R_MIPS_EH, giving
>> the linker control of both the .eh_frame_hdr encoding and the
>> .eh_frame_entry fields that that encoding controls.  That part seemed
>> consistent and safe.  (The unsafe part was that other parts of the assembler
>> seemed to assume that R_MIPS_EH always meant datarel-indirect.)
>> 
>> In the new scheme the assembler picks an encoding-specific relocation for
>> those .eh_frame_entry text addresses (always PC-relative in the posted
>> patch), but the linker is still the one that chooses the .eh_frame_hdr
>> encoding.  And in the posted patch the encoding used in .eh_frame_hdr is
>> decided by the --pcrel-eh-reloc option.  The default linker behaviour
>> is to use
>> datarel-indirect, which is the opposite of what the assembler now uses.  So if
>> I do:
>> 
>>     as foo.s -o foo.o
>>     ld foo.o -o foo
>> 
>> it looked like foo.o would use a PC-relative encoding for any
>> .eh_frame_entry sections, but foo's .eh_frame_hdr would say that they are
>> datarel-indirect rather than PC-relative.
>> 
>> In other words, I think using datarel-indirect for the .eh_frame_hdr is only
>> safe if all input objects are using the old scheme.  So that seems like a bad
>> default.  And I'm not sure --pcrel-eh-reloc is safe for old objects because of
>> the assumption in some cases that R_MIPS_EH would generate datarel-
>> indirect.
>> 
>> Maybe for the FSF version we should just drop the R_MIPS_EH stuff
>> altogether, since at this point it sounds like it would be safer to
>> reject the old
>> objects than to try to guess what's happening.
>> The .eh_frame_hdr could then be hard-coded to use a PC-relative encoding,
>> like the assembler is now.  It seems a bit of a shame in a way though -- and
>> like I say, using PC-relative wouldn't apply well to targets like
>> VxWorks where
>> the gap between the text and data segments isn't fixed at link time.
>> 
>
> Dropping the R_MIPS_EH stuff is not a desirable option for us.
> If we were to go back to the original implementation, using R_MIPS_EH
> for entries in the .eh_frame_entry sections, then your objection to the
> implementation was the inconsistent treatment in the assembler?

Yes, or more specifically: the assembler was sometimes associating
R_MIPS_EH with a specific encoding type, even though we don't know
at assembly time what encoding is associated with R_MIPS_EH.

> You said:
> RS>  Or, to put it another way, why are we making the choice between
> RS> R_MIPS_PC32 and R_MIPS_EH at assembly time in mips_cfi_emit_expr,
> RS> but not in mips_cfi_fix_eh_ref, even though the patch appears to
> RS> allow the same two encodings in both casees?
>
> I'd like to take this approach in the next patch:
> 1. Keep the R_MIPS_EH relocation
> 2. Let the linker choose the appropriate encoding
> 3. Clean up the assembler inconsistencies

That's OK with me, but just to clarify: (3) IMO means that R_MIPS_EH
is never associated with a specific encoding in the assembler.  I.e.
R_MIPS_EH is purely for an as-yet unknown encoding that is chosen by
the linker rather than the assembler or the assembly author.  And AIUI
the only place that happens is in .eh_frame_entry.

Perhaps one way of doing that would to have a generic
BFD_RELOC_EH_FRAME_HDR_32 that R_MIPS_EH maps to.  Then when emitting the
.eh_frame_entry addresses, the assembler unconditionally uses that BFD_RELOC_
rather than a target hook.

What do you plan to do for .ehword?  Since the assembler generates
the .eh_frame_entry itself, and since R_MIPS_EH should only be used
there (since that's the only place where the linker controls the encoding),
I don't think there are any valid uses of an R_MIPS_EH-producing .ehword.

> This would also mean dropping the .forcegpword directive as you suggested.  

My problem with .forcegpword was more that the GP base isn't a DSO-wide
value, it's only local to the input object.  So IMO dropping it (which is fine)
is separate from whether to keep R_MIPS_EH.  I assume if we drop it then
the only supported LSDA encoding will be pcrel and pcrel|indirect,
at least for now?

Thanks,
Richard

^ permalink raw reply	[flat|nested] 31+ messages in thread

* RE: [Patch] Gas support for MIPS Compact EH
  2014-02-20 11:36             ` Richard Sandiford
@ 2014-02-20 21:24               ` Moore, Catherine
  2014-02-20 22:39                 ` Richard Sandiford
  2014-02-24 20:48               ` Moore, Catherine
  1 sibling, 1 reply; 31+ messages in thread
From: Moore, Catherine @ 2014-02-20 21:24 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: Schmidt, Bernd, binutils



> -----Original Message-----
> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> Sent: Thursday, February 20, 2014 6:37 AM
> To: Moore, Catherine
> Cc: Schmidt, Bernd; binutils@sourceware.org
> Subject: Re: [Patch] Gas support for MIPS Compact EH
> 
> "Moore, Catherine" <Catherine_Moore@mentor.com> writes:
> >> -----Original Message-----
> >> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> >> Sent: Sunday, February 09, 2014 6:11 AM
> >> To: Schmidt, Bernd
> >> Cc: Moore, Catherine; binutils@sourceware.org
> >> Subject: Re: [Patch] Gas support for MIPS Compact EH
> >>
> >> Bernd Schmidt <bernds@codesourcery.com> writes:
> >> > On 02/08/2014 05:34 PM, Richard Sandiford wrote:
> >> >>>> The tc_cfi_fix_eh_ref and tc_cfi_emit_expr hooks don't seem very
> >> >>>> consistent; the former relies on the caller to clear the bytes,
> >> >>>> whereas the latter is supposed to do it itself.
> >> >>>
> >> >>> All fixed, now using a hook to return a reloc and eliminated the
> >> >>> use of R_MIPS_EH from the assembler.
> >> >>
> >> >> Hmm, but how does it work under the new scheme?  It looks like gas
> >> >> now always emits the .eh_frame_entry sections using R_MIPS_PC32,
> >> >> is
> >> that right?
> >> >> But the linker chooses the .eh_frame_hdr encoding based on
> >> >> --pcrel-eh-reloc, which also controls how R_MIPS_EH is handled.
> >> >> So if
> >> the:
> >> >>
> >> >>    DW_EH_PE_sdata4 | DW_EH_PE_datarel | DW_EH_PE_indirect
> >> >>
> >> >> encoding is chosen for the .eh_frame_entry sections at link time,
> >> >> what converts the input sections to use that encoding instead of
> >> >> the original R_MIPS_PC32?  I'd assumed R_MIPS_EH was defined the
> >> >> way it was to avoid that kind of thing.
> >> >
> >> > What's changed is that the linker is no longer really involved in
> >> > these decisions - the code you see in the linker-specific parts of
> >> > the patch are there merely to deal with R_MIPS_EH relocs in object
> >> > files generated by previous toolchains. We've always kind of
> >> > already decided at compile time which encoding to use and passed
> >> > the -pcrel-eh-reloc option to the linker to ensure it made the
> >> > choice we wanted. What's new in this version of the patches is
> >> > based on the realization that gcc can produce
> >> > datarel|indirect encoding without linker help (using the new
> >> > datarel|forcegpword
> >> > op).
> >>
> >> Ah, OK, so it's now up to the assembler writer to do the indirection by
> hand?
> >> I hadn't realised that.  (To be fair, the patch added the
> >> .forcegpword directive but didn't have any examples or tests to show
> >> how it was used, or any documentation explaining it.  I deliberately
> >> didn't complain about the latter though because most ops are
> >> undocumented.)
> >>
> >> Using GP-relative is probably a bad idea for MIPS because the GP base
> >> can vary in the case of multigot.  When resolving the relocations in
> >> the individual input sections, the GP used will be for that input
> >> bfd's GOT, which isn't necessarily going to be the primary GOT.  So
> >> if we're doing the indirection by hand, why not use pcrel|indirect
> >> instead?  I suppose that amounts to using .ehword (under the new
> >> PC-relative definition) rather than .forcegpword.  I think it'd be
> >> better to drop .forcegpword if possible.
> >>
> >> FWIW, pcrel|indirect is what the linker tries to use for .eh_frame on
> >> MIPS, if the original input object used absolute indirect.
> >>
> >> > On Linux targets you'll see this generated by the compiler, on
> >> > bare-metal you'll get R_MIPS_PC32. The R_MIPS_EH reloc is longer
> >> > produced. So it's all a lot more straightforward, directly
> >> > producing an encoding appropriate for the target at compile time.
> >> >
> >> >> I thought R_MIPS_EH would be used for the .eh_frame_entry entries
> >> >> only, since in that case the actual encoding of the address isn't
> >> >> known until link time.
> >> >
> >> > I think previous versions of the code were just slightly confused -
> >> > the idea was that datarel|indirect required things to be put into
> >> > the got, which has to be done by the linker. It turns out that this
> >> > isn't necessary, so there is no longer a need to use R_MIPS_EH.
> >> >
> >> > Does this clarify things?
> >>
> >> I don't think it really answers my first question.  It's still the
> >> linker that decides what encoding goes in the .eh_frame_hdr.
> >> Then all the following .eh_frame_entry sections must use that
> >> encoding for the text addresses.
> >>
> >> The input objects use relocations to mark those .eh_frame_entry text
> >> addresses.  In the original scheme those relocations were R_MIPS_EH,
> >> giving the linker control of both the .eh_frame_hdr encoding and the
> >> .eh_frame_entry fields that that encoding controls.  That part seemed
> >> consistent and safe.  (The unsafe part was that other parts of the
> >> assembler seemed to assume that R_MIPS_EH always meant
> >> datarel-indirect.)
> >>
> >> In the new scheme the assembler picks an encoding-specific relocation
> >> for those .eh_frame_entry text addresses (always PC-relative in the
> >> posted patch), but the linker is still the one that chooses the
> >> .eh_frame_hdr encoding.  And in the posted patch the encoding used in
> >> .eh_frame_hdr is decided by the --pcrel-eh-reloc option.  The default
> >> linker behaviour is to use datarel-indirect, which is the opposite of
> >> what the assembler now uses.  So if I do:
> >>
> >>     as foo.s -o foo.o
> >>     ld foo.o -o foo
> >>
> >> it looked like foo.o would use a PC-relative encoding for any
> >> .eh_frame_entry sections, but foo's .eh_frame_hdr would say that they
> >> are datarel-indirect rather than PC-relative.
> >>
> >> In other words, I think using datarel-indirect for the .eh_frame_hdr
> >> is only safe if all input objects are using the old scheme.  So that
> >> seems like a bad default.  And I'm not sure --pcrel-eh-reloc is safe
> >> for old objects because of the assumption in some cases that
> >> R_MIPS_EH would generate datarel- indirect.
> >>
> >> Maybe for the FSF version we should just drop the R_MIPS_EH stuff
> >> altogether, since at this point it sounds like it would be safer to
> >> reject the old objects than to try to guess what's happening.
> >> The .eh_frame_hdr could then be hard-coded to use a PC-relative
> >> encoding, like the assembler is now.  It seems a bit of a shame in a
> >> way though -- and like I say, using PC-relative wouldn't apply well
> >> to targets like VxWorks where the gap between the text and data
> >> segments isn't fixed at link time.
> >>
> >
> > Dropping the R_MIPS_EH stuff is not a desirable option for us.
> > If we were to go back to the original implementation, using R_MIPS_EH
> > for entries in the .eh_frame_entry sections, then your objection to
> > the implementation was the inconsistent treatment in the assembler?
> 
> Yes, or more specifically: the assembler was sometimes associating
> R_MIPS_EH with a specific encoding type, even though we don't know at
> assembly time what encoding is associated with R_MIPS_EH.
> 
> > You said:
> > RS>  Or, to put it another way, why are we making the choice between
> > RS> R_MIPS_PC32 and R_MIPS_EH at assembly time in
> mips_cfi_emit_expr,
> > RS> but not in mips_cfi_fix_eh_ref, even though the patch appears to
> > RS> allow the same two encodings in both casees?
> >
> > I'd like to take this approach in the next patch:
> > 1. Keep the R_MIPS_EH relocation
> > 2. Let the linker choose the appropriate encoding 3. Clean up the
> > assembler inconsistencies
> 
> That's OK with me, but just to clarify: (3) IMO means that R_MIPS_EH is
> never associated with a specific encoding in the assembler.  I.e.
> R_MIPS_EH is purely for an as-yet unknown encoding that is chosen by the
> linker rather than the assembler or the assembly author.  And AIUI the only
> place that happens is in .eh_frame_entry.
> 
> Perhaps one way of doing that would to have a generic
> BFD_RELOC_EH_FRAME_HDR_32 that R_MIPS_EH maps to.  Then when
> emitting the .eh_frame_entry addresses, the assembler unconditionally uses
> that BFD_RELOC_ rather than a target hook.
> 
> What do you plan to do for .ehword?  Since the assembler generates the
> .eh_frame_entry itself, and since R_MIPS_EH should only be used there
> (since that's the only place where the linker controls the encoding), I don't
> think there are any valid uses of an R_MIPS_EH-producing .ehword.
> 
> > This would also mean dropping the .forcegpword directive as you
> suggested.
> 
> My problem with .forcegpword was more that the GP base isn't a DSO-wide
> value, it's only local to the input object.  So IMO dropping it (which is fine) is
> separate from whether to keep R_MIPS_EH.  I assume if we drop it then the
> only supported LSDA encoding will be pcrel and pcrel|indirect, at least for
> now?
> 
That's not right.  We need datarel|indirect for the MIPS Linux toolchain.
I don't follow why dropping the .forcegpword directive implies that datarel|indirect can't be generated.
Thanks,
Catherine

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Patch] Gas support for MIPS Compact EH
  2014-02-20 21:24               ` Moore, Catherine
@ 2014-02-20 22:39                 ` Richard Sandiford
  2014-02-24 20:54                   ` Moore, Catherine
  0 siblings, 1 reply; 31+ messages in thread
From: Richard Sandiford @ 2014-02-20 22:39 UTC (permalink / raw)
  To: Moore, Catherine; +Cc: Schmidt, Bernd, binutils

"Moore, Catherine" <Catherine_Moore@mentor.com> writes:
>> -----Original Message-----
>> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
>> Sent: Thursday, February 20, 2014 6:37 AM
>> To: Moore, Catherine
>> Cc: Schmidt, Bernd; binutils@sourceware.org
>> Subject: Re: [Patch] Gas support for MIPS Compact EH
>> 
>> "Moore, Catherine" <Catherine_Moore@mentor.com> writes:
>> >> -----Original Message-----
>> >> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
>> >> Sent: Sunday, February 09, 2014 6:11 AM
>> >> To: Schmidt, Bernd
>> >> Cc: Moore, Catherine; binutils@sourceware.org
>> >> Subject: Re: [Patch] Gas support for MIPS Compact EH
>> >>
>> >> Bernd Schmidt <bernds@codesourcery.com> writes:
>> >> > On 02/08/2014 05:34 PM, Richard Sandiford wrote:
>> >> >>>> The tc_cfi_fix_eh_ref and tc_cfi_emit_expr hooks don't seem very
>> >> >>>> consistent; the former relies on the caller to clear the bytes,
>> >> >>>> whereas the latter is supposed to do it itself.
>> >> >>>
>> >> >>> All fixed, now using a hook to return a reloc and eliminated the
>> >> >>> use of R_MIPS_EH from the assembler.
>> >> >>
>> >> >> Hmm, but how does it work under the new scheme?  It looks like gas
>> >> >> now always emits the .eh_frame_entry sections using R_MIPS_PC32,
>> >> >> is
>> >> that right?
>> >> >> But the linker chooses the .eh_frame_hdr encoding based on
>> >> >> --pcrel-eh-reloc, which also controls how R_MIPS_EH is handled.
>> >> >> So if
>> >> the:
>> >> >>
>> >> >>    DW_EH_PE_sdata4 | DW_EH_PE_datarel | DW_EH_PE_indirect
>> >> >>
>> >> >> encoding is chosen for the .eh_frame_entry sections at link time,
>> >> >> what converts the input sections to use that encoding instead of
>> >> >> the original R_MIPS_PC32?  I'd assumed R_MIPS_EH was defined the
>> >> >> way it was to avoid that kind of thing.
>> >> >
>> >> > What's changed is that the linker is no longer really involved in
>> >> > these decisions - the code you see in the linker-specific parts of
>> >> > the patch are there merely to deal with R_MIPS_EH relocs in object
>> >> > files generated by previous toolchains. We've always kind of
>> >> > already decided at compile time which encoding to use and passed
>> >> > the -pcrel-eh-reloc option to the linker to ensure it made the
>> >> > choice we wanted. What's new in this version of the patches is
>> >> > based on the realization that gcc can produce
>> >> > datarel|indirect encoding without linker help (using the new
>> >> > datarel|forcegpword
>> >> > op).
>> >>
>> >> Ah, OK, so it's now up to the assembler writer to do the indirection by
>> hand?
>> >> I hadn't realised that.  (To be fair, the patch added the
>> >> .forcegpword directive but didn't have any examples or tests to show
>> >> how it was used, or any documentation explaining it.  I deliberately
>> >> didn't complain about the latter though because most ops are
>> >> undocumented.)
>> >>
>> >> Using GP-relative is probably a bad idea for MIPS because the GP base
>> >> can vary in the case of multigot.  When resolving the relocations in
>> >> the individual input sections, the GP used will be for that input
>> >> bfd's GOT, which isn't necessarily going to be the primary GOT.  So
>> >> if we're doing the indirection by hand, why not use pcrel|indirect
>> >> instead?  I suppose that amounts to using .ehword (under the new
>> >> PC-relative definition) rather than .forcegpword.  I think it'd be
>> >> better to drop .forcegpword if possible.
>> >>
>> >> FWIW, pcrel|indirect is what the linker tries to use for .eh_frame on
>> >> MIPS, if the original input object used absolute indirect.
>> >>
>> >> > On Linux targets you'll see this generated by the compiler, on
>> >> > bare-metal you'll get R_MIPS_PC32. The R_MIPS_EH reloc is longer
>> >> > produced. So it's all a lot more straightforward, directly
>> >> > producing an encoding appropriate for the target at compile time.
>> >> >
>> >> >> I thought R_MIPS_EH would be used for the .eh_frame_entry entries
>> >> >> only, since in that case the actual encoding of the address isn't
>> >> >> known until link time.
>> >> >
>> >> > I think previous versions of the code were just slightly confused -
>> >> > the idea was that datarel|indirect required things to be put into
>> >> > the got, which has to be done by the linker. It turns out that this
>> >> > isn't necessary, so there is no longer a need to use R_MIPS_EH.
>> >> >
>> >> > Does this clarify things?
>> >>
>> >> I don't think it really answers my first question.  It's still the
>> >> linker that decides what encoding goes in the .eh_frame_hdr.
>> >> Then all the following .eh_frame_entry sections must use that
>> >> encoding for the text addresses.
>> >>
>> >> The input objects use relocations to mark those .eh_frame_entry text
>> >> addresses.  In the original scheme those relocations were R_MIPS_EH,
>> >> giving the linker control of both the .eh_frame_hdr encoding and the
>> >> .eh_frame_entry fields that that encoding controls.  That part seemed
>> >> consistent and safe.  (The unsafe part was that other parts of the
>> >> assembler seemed to assume that R_MIPS_EH always meant
>> >> datarel-indirect.)
>> >>
>> >> In the new scheme the assembler picks an encoding-specific relocation
>> >> for those .eh_frame_entry text addresses (always PC-relative in the
>> >> posted patch), but the linker is still the one that chooses the
>> >> .eh_frame_hdr encoding.  And in the posted patch the encoding used in
>> >> .eh_frame_hdr is decided by the --pcrel-eh-reloc option.  The default
>> >> linker behaviour is to use datarel-indirect, which is the opposite of
>> >> what the assembler now uses.  So if I do:
>> >>
>> >>     as foo.s -o foo.o
>> >>     ld foo.o -o foo
>> >>
>> >> it looked like foo.o would use a PC-relative encoding for any
>> >> .eh_frame_entry sections, but foo's .eh_frame_hdr would say that they
>> >> are datarel-indirect rather than PC-relative.
>> >>
>> >> In other words, I think using datarel-indirect for the .eh_frame_hdr
>> >> is only safe if all input objects are using the old scheme.  So that
>> >> seems like a bad default.  And I'm not sure --pcrel-eh-reloc is safe
>> >> for old objects because of the assumption in some cases that
>> >> R_MIPS_EH would generate datarel- indirect.
>> >>
>> >> Maybe for the FSF version we should just drop the R_MIPS_EH stuff
>> >> altogether, since at this point it sounds like it would be safer to
>> >> reject the old objects than to try to guess what's happening.
>> >> The .eh_frame_hdr could then be hard-coded to use a PC-relative
>> >> encoding, like the assembler is now.  It seems a bit of a shame in a
>> >> way though -- and like I say, using PC-relative wouldn't apply well
>> >> to targets like VxWorks where the gap between the text and data
>> >> segments isn't fixed at link time.
>> >>
>> >
>> > Dropping the R_MIPS_EH stuff is not a desirable option for us.
>> > If we were to go back to the original implementation, using R_MIPS_EH
>> > for entries in the .eh_frame_entry sections, then your objection to
>> > the implementation was the inconsistent treatment in the assembler?
>> 
>> Yes, or more specifically: the assembler was sometimes associating
>> R_MIPS_EH with a specific encoding type, even though we don't know at
>> assembly time what encoding is associated with R_MIPS_EH.
>> 
>> > You said:
>> > RS>  Or, to put it another way, why are we making the choice between
>> > RS> R_MIPS_PC32 and R_MIPS_EH at assembly time in
>> mips_cfi_emit_expr,
>> > RS> but not in mips_cfi_fix_eh_ref, even though the patch appears to
>> > RS> allow the same two encodings in both casees?
>> >
>> > I'd like to take this approach in the next patch:
>> > 1. Keep the R_MIPS_EH relocation
>> > 2. Let the linker choose the appropriate encoding 3. Clean up the
>> > assembler inconsistencies
>> 
>> That's OK with me, but just to clarify: (3) IMO means that R_MIPS_EH is
>> never associated with a specific encoding in the assembler.  I.e.
>> R_MIPS_EH is purely for an as-yet unknown encoding that is chosen by the
>> linker rather than the assembler or the assembly author.  And AIUI the only
>> place that happens is in .eh_frame_entry.
>> 
>> Perhaps one way of doing that would to have a generic
>> BFD_RELOC_EH_FRAME_HDR_32 that R_MIPS_EH maps to.  Then when
>> emitting the .eh_frame_entry addresses, the assembler unconditionally uses
>> that BFD_RELOC_ rather than a target hook.
>> 
>> What do you plan to do for .ehword?  Since the assembler generates the
>> .eh_frame_entry itself, and since R_MIPS_EH should only be used there
>> (since that's the only place where the linker controls the encoding), I don't
>> think there are any valid uses of an R_MIPS_EH-producing .ehword.
>> 
>> > This would also mean dropping the .forcegpword directive as you
>> suggested.
>> 
>> My problem with .forcegpword was more that the GP base isn't a DSO-wide
>> value, it's only local to the input object.  So IMO dropping it (which
>> is fine) is
>> separate from whether to keep R_MIPS_EH.  I assume if we drop it then the
>> only supported LSDA encoding will be pcrel and pcrel|indirect, at least for
>> now?
>> 
> That's not right.  We need datarel|indirect for the MIPS Linux toolchain.

Why though?  For GNU/Linux the text and data segments have a fixed
offset relative to one another, so I don't see any advantage of
datarel|indirect over pcrel|indirect.  (The current EH scheme gets by
with just pcrel and pcrel|indirect FWIW.)

I.e. both the text segment and the data segment can use pcrel to refer
to any other part of the same executable or DSO.  Then you use |indirect
in cases where the address doesn't bind locally and so might end up
pointing outside the executable or DSO.

datarel is more useful for targets where the text and data segments
can be moved relative to one another and you want to make a relative
reference to data from the text segment.

Like I say, datarel (i.e. gprel) has the significant downside that
on multigot targets like GNU/Linux a single executable or DSO can
have several GP bases, one for each GOT.  I.e. the GP value used
for GP-relative relocations isn't always the same as the value of
the _gp or _GLOBAL_OFFSET_TABLE_ symbols.  That applies both to
the GP base used for R_MIPS_EH in elfxx-mips.c in the original
patch and the GP base used for R_MIPS_GPREL32 (as harnessed by
the revised patch).

> I don't follow why dropping the .forcegpword directive implies that
> datarel|indirect can't be generated.

How else would you generate datarel|indirect LSDA though?  I.e. what
directive and relocation would you use?  The point is that you can't use
R_MIPS_EH because the LSDA encoding is decided by assembly time while
the R_MIPS_EH behaviour is only decided by the linker.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 31+ messages in thread

* RE: [Patch] Gas support for MIPS Compact EH
  2014-02-20 11:36             ` Richard Sandiford
  2014-02-20 21:24               ` Moore, Catherine
@ 2014-02-24 20:48               ` Moore, Catherine
  2014-02-25  8:29                 ` Richard Sandiford
  1 sibling, 1 reply; 31+ messages in thread
From: Moore, Catherine @ 2014-02-24 20:48 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: Schmidt, Bernd, binutils



> -----Original Message-----
> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> Sent: Thursday, February 20, 2014 6:37 AM
> To: Moore, Catherine
> Cc: Schmidt, Bernd; binutils@sourceware.org
> Subject: Re: [Patch] Gas support for MIPS Compact EH
> 
> >
> > I'd like to take this approach in the next patch:
> > 1. Keep the R_MIPS_EH relocation
> > 2. Let the linker choose the appropriate encoding 3. Clean up the
> > assembler inconsistencies
> 
> That's OK with me, but just to clarify: (3) IMO means that R_MIPS_EH is
> never associated with a specific encoding in the assembler.  I.e.
> R_MIPS_EH is purely for an as-yet unknown encoding that is chosen by the
> linker rather than the assembler or the assembly author.  And AIUI the only
> place that happens is in .eh_frame_entry.
> 
> Perhaps one way of doing that would to have a generic
> BFD_RELOC_EH_FRAME_HDR_32 that R_MIPS_EH maps to.  Then when
> emitting the .eh_frame_entry addresses, the assembler unconditionally uses
> that BFD_RELOC_ rather than a target hook.
> 
> What do you plan to do for .ehword?  Since the assembler generates the
> .eh_frame_entry itself, and since R_MIPS_EH should only be used there
> (since that's the only place where the linker controls the encoding), I don't
> think there are any valid uses of an R_MIPS_EH-producing .ehword.
> 

The current version of the patch generates a BFD_RELOC_32_PCREL when it sees the .ehword directive.
That should be okay going forward, agreed?

Catherine

^ permalink raw reply	[flat|nested] 31+ messages in thread

* RE: [Patch] Gas support for MIPS Compact EH
  2014-02-20 22:39                 ` Richard Sandiford
@ 2014-02-24 20:54                   ` Moore, Catherine
  0 siblings, 0 replies; 31+ messages in thread
From: Moore, Catherine @ 2014-02-24 20:54 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: Schmidt, Bernd, binutils



> -----Original Message-----
> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> Sent: Thursday, February 20, 2014 5:39 PM
> To: Moore, Catherine
> Cc: Schmidt, Bernd; binutils@sourceware.org
> Subject: Re: [Patch] Gas support for MIPS Compact EH
> 
> "Moore, Catherine" <Catherine_Moore@mentor.com> writes:
> >> -----Original Message-----
> >> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> >> Sent: Thursday, February 20, 2014 6:37 AM
> >> To: Moore, Catherine
> >> Cc: Schmidt, Bernd; binutils@sourceware.org
> >> Subject: Re: [Patch] Gas support for MIPS Compact EH
> >>
> >> "Moore, Catherine" <Catherine_Moore@mentor.com> writes:
> >> >> -----Original Message-----
> >> >> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> >> >> Sent: Sunday, February 09, 2014 6:11 AM
> >> >> To: Schmidt, Bernd
> >> >> Cc: Moore, Catherine; binutils@sourceware.org
> >> >> Subject: Re: [Patch] Gas support for MIPS Compact EH
> >> >>
> >> >> Bernd Schmidt <bernds@codesourcery.com> writes:
> >> >> > On 02/08/2014 05:34 PM, Richard Sandiford wrote:
> >> >> >>>> The tc_cfi_fix_eh_ref and tc_cfi_emit_expr hooks don't seem
> >> >> >>>> very consistent; the former relies on the caller to clear the
> >> >> >>>> bytes, whereas the latter is supposed to do it itself.
> >> >> >>>
> >> >> >>> All fixed, now using a hook to return a reloc and eliminated
> >> >> >>> the use of R_MIPS_EH from the assembler.
> >> >> >>
> >> >> >> Hmm, but how does it work under the new scheme?  It looks like
> >> >> >> gas now always emits the .eh_frame_entry sections using
> >> >> >> R_MIPS_PC32, is
> >> >> that right?
> >> >> >> But the linker chooses the .eh_frame_hdr encoding based on
> >> >> >> --pcrel-eh-reloc, which also controls how R_MIPS_EH is handled.
> >> >> >> So if
> >> >> the:
> >> >> >>
> >> >> >>    DW_EH_PE_sdata4 | DW_EH_PE_datarel | DW_EH_PE_indirect
> >> >> >>
> >> >> >> encoding is chosen for the .eh_frame_entry sections at link
> >> >> >> time, what converts the input sections to use that encoding
> >> >> >> instead of the original R_MIPS_PC32?  I'd assumed R_MIPS_EH was
> >> >> >> defined the way it was to avoid that kind of thing.
> >> >> >
> >> >> > What's changed is that the linker is no longer really involved
> >> >> > in these decisions - the code you see in the linker-specific
> >> >> > parts of the patch are there merely to deal with R_MIPS_EH
> >> >> > relocs in object files generated by previous toolchains. We've
> >> >> > always kind of already decided at compile time which encoding to
> >> >> > use and passed the -pcrel-eh-reloc option to the linker to
> >> >> > ensure it made the choice we wanted. What's new in this version
> >> >> > of the patches is based on the realization that gcc can produce
> >> >> > datarel|indirect encoding without linker help (using the new
> >> >> > datarel|forcegpword
> >> >> > op).
> >> >>
> >> >> Ah, OK, so it's now up to the assembler writer to do the
> >> >> indirection by
> >> hand?
> >> >> I hadn't realised that.  (To be fair, the patch added the
> >> >> .forcegpword directive but didn't have any examples or tests to
> >> >> show how it was used, or any documentation explaining it.  I
> >> >> deliberately didn't complain about the latter though because most
> >> >> ops are
> >> >> undocumented.)
> >> >>
> >> >> Using GP-relative is probably a bad idea for MIPS because the GP
> >> >> base can vary in the case of multigot.  When resolving the
> >> >> relocations in the individual input sections, the GP used will be
> >> >> for that input bfd's GOT, which isn't necessarily going to be the
> >> >> primary GOT.  So if we're doing the indirection by hand, why not
> >> >> use pcrel|indirect instead?  I suppose that amounts to using
> >> >> .ehword (under the new PC-relative definition) rather than
> >> >> .forcegpword.  I think it'd be better to drop .forcegpword if possible.
> >> >>
> >> >> FWIW, pcrel|indirect is what the linker tries to use for .eh_frame
> >> >> on MIPS, if the original input object used absolute indirect.
> >> >>
> >> >> > On Linux targets you'll see this generated by the compiler, on
> >> >> > bare-metal you'll get R_MIPS_PC32. The R_MIPS_EH reloc is longer
> >> >> > produced. So it's all a lot more straightforward, directly
> >> >> > producing an encoding appropriate for the target at compile time.
> >> >> >
> >> >> >> I thought R_MIPS_EH would be used for the .eh_frame_entry
> >> >> >> entries only, since in that case the actual encoding of the
> >> >> >> address isn't known until link time.
> >> >> >
> >> >> > I think previous versions of the code were just slightly
> >> >> > confused - the idea was that datarel|indirect required things to
> >> >> > be put into the got, which has to be done by the linker. It
> >> >> > turns out that this isn't necessary, so there is no longer a need to use
> R_MIPS_EH.
> >> >> >
> >> >> > Does this clarify things?
> >> >>
> >> >> I don't think it really answers my first question.  It's still the
> >> >> linker that decides what encoding goes in the .eh_frame_hdr.
> >> >> Then all the following .eh_frame_entry sections must use that
> >> >> encoding for the text addresses.
> >> >>
> >> >> The input objects use relocations to mark those .eh_frame_entry
> >> >> text addresses.  In the original scheme those relocations were
> >> >> R_MIPS_EH, giving the linker control of both the .eh_frame_hdr
> >> >> encoding and the .eh_frame_entry fields that that encoding
> >> >> controls.  That part seemed consistent and safe.  (The unsafe part
> >> >> was that other parts of the assembler seemed to assume that
> >> >> R_MIPS_EH always meant
> >> >> datarel-indirect.)
> >> >>
> >> >> In the new scheme the assembler picks an encoding-specific
> >> >> relocation for those .eh_frame_entry text addresses (always
> >> >> PC-relative in the posted patch), but the linker is still the one
> >> >> that chooses the .eh_frame_hdr encoding.  And in the posted patch
> >> >> the encoding used in .eh_frame_hdr is decided by the
> >> >> --pcrel-eh-reloc option.  The default linker behaviour is to use
> >> >> datarel-indirect, which is the opposite of what the assembler now
> uses.  So if I do:
> >> >>
> >> >>     as foo.s -o foo.o
> >> >>     ld foo.o -o foo
> >> >>
> >> >> it looked like foo.o would use a PC-relative encoding for any
> >> >> .eh_frame_entry sections, but foo's .eh_frame_hdr would say that
> >> >> they are datarel-indirect rather than PC-relative.
> >> >>
> >> >> In other words, I think using datarel-indirect for the
> >> >> .eh_frame_hdr is only safe if all input objects are using the old
> >> >> scheme.  So that seems like a bad default.  And I'm not sure
> >> >> --pcrel-eh-reloc is safe for old objects because of the assumption
> >> >> in some cases that R_MIPS_EH would generate datarel- indirect.
> >> >>
> >> >> Maybe for the FSF version we should just drop the R_MIPS_EH stuff
> >> >> altogether, since at this point it sounds like it would be safer
> >> >> to reject the old objects than to try to guess what's happening.
> >> >> The .eh_frame_hdr could then be hard-coded to use a PC-relative
> >> >> encoding, like the assembler is now.  It seems a bit of a shame in
> >> >> a way though -- and like I say, using PC-relative wouldn't apply
> >> >> well to targets like VxWorks where the gap between the text and
> >> >> data segments isn't fixed at link time.
> >> >>
> >> >
> >> > Dropping the R_MIPS_EH stuff is not a desirable option for us.
> >> > If we were to go back to the original implementation, using
> >> > R_MIPS_EH for entries in the .eh_frame_entry sections, then your
> >> > objection to the implementation was the inconsistent treatment in the
> assembler?
> >>
> >> Yes, or more specifically: the assembler was sometimes associating
> >> R_MIPS_EH with a specific encoding type, even though we don't know at
> >> assembly time what encoding is associated with R_MIPS_EH.
> >>
> >> > You said:
> >> > RS>  Or, to put it another way, why are we making the choice
> >> > RS> between
> >> > RS> R_MIPS_PC32 and R_MIPS_EH at assembly time in
> >> mips_cfi_emit_expr,
> >> > RS> but not in mips_cfi_fix_eh_ref, even though the patch appears
> >> > RS> to allow the same two encodings in both casees?
> >> >
> >> > I'd like to take this approach in the next patch:
> >> > 1. Keep the R_MIPS_EH relocation
> >> > 2. Let the linker choose the appropriate encoding 3. Clean up the
> >> > assembler inconsistencies
> >>
> >> That's OK with me, but just to clarify: (3) IMO means that R_MIPS_EH
> >> is never associated with a specific encoding in the assembler.  I.e.
> >> R_MIPS_EH is purely for an as-yet unknown encoding that is chosen by
> >> the linker rather than the assembler or the assembly author.  And
> >> AIUI the only place that happens is in .eh_frame_entry.
> >>
> >> Perhaps one way of doing that would to have a generic
> >> BFD_RELOC_EH_FRAME_HDR_32 that R_MIPS_EH maps to.  Then when
> emitting
> >> the .eh_frame_entry addresses, the assembler unconditionally uses
> >> that BFD_RELOC_ rather than a target hook.
> >>
> >> What do you plan to do for .ehword?  Since the assembler generates
> >> the .eh_frame_entry itself, and since R_MIPS_EH should only be used
> >> there (since that's the only place where the linker controls the
> >> encoding), I don't think there are any valid uses of an R_MIPS_EH-
> producing .ehword.
> >>
> >> > This would also mean dropping the .forcegpword directive as you
> >> suggested.
> >>
> >> My problem with .forcegpword was more that the GP base isn't a
> >> DSO-wide value, it's only local to the input object.  So IMO dropping
> >> it (which is fine) is separate from whether to keep R_MIPS_EH.  I
> >> assume if we drop it then the only supported LSDA encoding will be
> >> pcrel and pcrel|indirect, at least for now?
> >>
> > That's not right.  We need datarel|indirect for the MIPS Linux toolchain.
> 
> Why though?  For GNU/Linux the text and data segments have a fixed offset
> relative to one another, so I don't see any advantage of
> datarel|indirect over pcrel|indirect.  (The current EH scheme gets by
> with just pcrel and pcrel|indirect FWIW.)

I think we can do this with pcrel and pcrel|indirect. 
The design spec called for datarel, but I've now confirmed that it's not necessary for the implementation.

> 
> I.e. both the text segment and the data segment can use pcrel to refer to
> any other part of the same executable or DSO.  Then you use |indirect in
> cases where the address doesn't bind locally and so might end up pointing
> outside the executable or DSO.
> 
> datarel is more useful for targets where the text and data segments can be
> moved relative to one another and you want to make a relative reference to
> data from the text segment.
> 
> Like I say, datarel (i.e. gprel) has the significant downside that on multigot
> targets like GNU/Linux a single executable or DSO can have several GP bases,
> one for each GOT.  I.e. the GP value used for GP-relative relocations isn't
> always the same as the value of the _gp or _GLOBAL_OFFSET_TABLE_
> symbols.  That applies both to the GP base used for R_MIPS_EH in elfxx-
> mips.c in the original patch and the GP base used for R_MIPS_GPREL32 (as
> harnessed by the revised patch).
> 
> > I don't follow why dropping the .forcegpword directive implies that
> > datarel|indirect can't be generated.
> 
> How else would you generate datarel|indirect LSDA though?  I.e. what
> directive and relocation would you use?  The point is that you can't use
> R_MIPS_EH because the LSDA encoding is decided by assembly time while
> the R_MIPS_EH behaviour is only decided by the linker.
> 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Patch] Gas support for MIPS Compact EH
  2014-02-24 20:48               ` Moore, Catherine
@ 2014-02-25  8:29                 ` Richard Sandiford
  2014-03-17 13:22                   ` Moore, Catherine
  0 siblings, 1 reply; 31+ messages in thread
From: Richard Sandiford @ 2014-02-25  8:29 UTC (permalink / raw)
  To: Moore, Catherine; +Cc: Schmidt, Bernd, binutils

"Moore, Catherine" <Catherine_Moore@mentor.com> writes:
>> -----Original Message-----
>> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
>> Sent: Thursday, February 20, 2014 6:37 AM
>> To: Moore, Catherine
>> Cc: Schmidt, Bernd; binutils@sourceware.org
>> Subject: Re: [Patch] Gas support for MIPS Compact EH
>> 
>> >
>> > I'd like to take this approach in the next patch:
>> > 1. Keep the R_MIPS_EH relocation
>> > 2. Let the linker choose the appropriate encoding 3. Clean up the
>> > assembler inconsistencies
>> 
>> That's OK with me, but just to clarify: (3) IMO means that R_MIPS_EH is
>> never associated with a specific encoding in the assembler.  I.e.
>> R_MIPS_EH is purely for an as-yet unknown encoding that is chosen by the
>> linker rather than the assembler or the assembly author.  And AIUI the only
>> place that happens is in .eh_frame_entry.
>> 
>> Perhaps one way of doing that would to have a generic
>> BFD_RELOC_EH_FRAME_HDR_32 that R_MIPS_EH maps to.  Then when
>> emitting the .eh_frame_entry addresses, the assembler unconditionally uses
>> that BFD_RELOC_ rather than a target hook.
>> 
>> What do you plan to do for .ehword?  Since the assembler generates the
>> .eh_frame_entry itself, and since R_MIPS_EH should only be used there
>> (since that's the only place where the linker controls the encoding), I don't
>> think there are any valid uses of an R_MIPS_EH-producing .ehword.
>> 
>
> The current version of the patch generates a BFD_RELOC_32_PCREL when it
> sees the .ehword directive.
> That should be okay going forward, agreed?

Well, PC-relative can be expressed as:

	.word	foo-.

I think instead we should make that work and drop .ehword, to avoid confusion
with the old behaviour.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 31+ messages in thread

* RE: [Patch] Gas support for MIPS Compact EH
  2014-02-08 16:34     ` Richard Sandiford
  2014-02-08 18:48       ` Bernd Schmidt
@ 2014-03-06 17:44       ` Moore, Catherine
  2014-03-06 22:18         ` Richard Sandiford
  2014-03-25 13:50       ` Moore, Catherine
  2014-11-17 16:10       ` Moore, Catherine
  3 siblings, 1 reply; 31+ messages in thread
From: Moore, Catherine @ 2014-03-06 17:44 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: binutils

Hi Richard,

> -----Original Message-----
> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> 
> > "Moore, Catherine" <Catherine_Moore@mentor.com> writes:
> > @@ -1433,6 +1441,7 @@ struct bfd_elf_section_data
> 
> > +/* Add a .eh_frame_entry section.  */
> > +
> > +static void
> > +bfd_elf_remember_eh_frame_entry (struct eh_frame_hdr_info
> *hdr_info,
> > +				 asection *sec)
> > +{
> > +  if (hdr_info->array_count == hdr_info->allocated_entries)
> > +    {
> > +      if (hdr_info->allocated_entries == 0)
> > +	{
> > +	  hdr_info->allocated_entries = 2;
> > +	  hdr_info->entries = bfd_malloc (hdr_info->allocated_entries
> > +					  * sizeof(hdr_info->entries[0]));
> > +	}
> > +      else
> > +	{
> > +	  hdr_info->allocated_entries *= 2;
> > +	  hdr_info->entries = bfd_realloc (hdr_info->entries,
> > +	    hdr_info->allocated_entries * sizeof(hdr_info->entries[0]));
> 
> Space before "sizeof" (both times).
> 
> > +	}
> > +
> > +      BFD_ASSERT (hdr_info->entries);
> > +    }
> > +
> > +  hdr_info->entries[hdr_info->array_count++] = sec; }
> > +
> > +/* Parse a .eh_frame_entry section.  Figure out which text section it
> > +   references.  */
> > +
> > +void
> > +_bfd_elf_parse_eh_frame_entry (bfd *abfd, struct bfd_link_info *info,
> > +			       asection *sec, struct elf_reloc_cookie *cookie,
> > +			       bfd_boolean remember)
> 
> This does more than the comment says and the name implies; the
> REMEMBER stuff isn't mentioned.
> 
> The patch tries to do the parsing during bfd_elf_discard_info, but since the
> parsing wants to be able to fail with an error, I think we need to do it in an
> earlier pass.  We can then return a bfd_boolean success code and propagate
> error returns up, which the current patch doesn't do.
> Ideally we'd put the pass somewhere before GC, so that both the GC and
> bfd_elf_discard_info stages can assume parsed .eh_frame_entry sections.
> 
It looks like the legacy dwarf code parse .eh_frame sections during both GC and bfd_elf_discard_info.
There is some shared code between the legacy and compact parsing and it feels awkward to keep legacy eh frame parsing as is and move compact eh frame parsing to an earlier pass.
I could post a patch that moves legacy parsing to an earlier pass to address this.  Do you think that's reasonable?
The other option is to remove the compact eh_frame_entry parsing only (as you suggested).  WDYT?
Thanks,
Catherine

> Having bfd_elf_discard_info add info (as per REMEMBER == TRUE) seems a
> bit counterintuitive.  I think the earlier pass should record all
> .eh_frame_entry sections and then the code currently in
> _bfd_elf_end_eh_frame_parsing (but see below) should remove unwanted
> entries from the eh_frame_hdr_info array.
> 
> > +  if (r_symndx >= cookie->locsymcount
> > +      || ELF_ST_BIND (cookie->locsyms[r_symndx].st_info) != STB_LOCAL)
> > +    {
> > +      h = cookie->sym_hashes[r_symndx - cookie->extsymoff];
> > +      while (h->root.type == bfd_link_hash_indirect
> > +             || h->root.type == bfd_link_hash_warning)
> > +        h = (struct elf_link_hash_entry *) h->root.u.i.link;
> > +
> > +      if (h->root.type != bfd_link_hash_defined
> > +          && h->root.type != bfd_link_hash_defweak)
> > +	goto fail;
> > +
> > +      text_sec = h->root.u.def.section;
> > +    }
> > +  else
> > +    {
> > +      Elf_Internal_Sym *isym;
> > +
> > +      /* Need to: get the symbol; get the section.  */
> > +      isym = &cookie->locsyms[r_symndx];
> > +      text_sec = bfd_section_from_elf_index (abfd, isym->st_shndx);
> > +    }
> 
> Looks like this was lifted from elflink.c.  Please separate it out into a
> subroutine that both sites can use.  E.g.:
> 
> asection *
> _bfd_elf_section_for_symbol (struct elf_reloc_cookie *cookie,
> 			     unsigned long r_symndx)
> {
> ...
> }
> 
> returning null if not known.
> 
> > +fail:
> > +  (*_bfd_error_handler) (_("%B: failed to precoess .eh_frame_entry"),
> > +sec->owner);
> 


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Patch] Gas support for MIPS Compact EH
  2014-03-06 17:44       ` Moore, Catherine
@ 2014-03-06 22:18         ` Richard Sandiford
  0 siblings, 0 replies; 31+ messages in thread
From: Richard Sandiford @ 2014-03-06 22:18 UTC (permalink / raw)
  To: Moore, Catherine; +Cc: binutils

"Moore, Catherine" <Catherine_Moore@mentor.com> writes:
> Hi Richard,
>
>> -----Original Message-----
>> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
>> 
>> > "Moore, Catherine" <Catherine_Moore@mentor.com> writes:
>> > @@ -1433,6 +1441,7 @@ struct bfd_elf_section_data
>> 
>> > +/* Add a .eh_frame_entry section.  */
>> > +
>> > +static void
>> > +bfd_elf_remember_eh_frame_entry (struct eh_frame_hdr_info
>> *hdr_info,
>> > +				 asection *sec)
>> > +{
>> > +  if (hdr_info->array_count == hdr_info->allocated_entries)
>> > +    {
>> > +      if (hdr_info->allocated_entries == 0)
>> > +	{
>> > +	  hdr_info->allocated_entries = 2;
>> > +	  hdr_info->entries = bfd_malloc (hdr_info->allocated_entries
>> > +					  * sizeof(hdr_info->entries[0]));
>> > +	}
>> > +      else
>> > +	{
>> > +	  hdr_info->allocated_entries *= 2;
>> > +	  hdr_info->entries = bfd_realloc (hdr_info->entries,
>> > +	    hdr_info->allocated_entries * sizeof(hdr_info->entries[0]));
>> 
>> Space before "sizeof" (both times).
>> 
>> > +	}
>> > +
>> > +      BFD_ASSERT (hdr_info->entries);
>> > +    }
>> > +
>> > +  hdr_info->entries[hdr_info->array_count++] = sec; }
>> > +
>> > +/* Parse a .eh_frame_entry section.  Figure out which text section it
>> > +   references.  */
>> > +
>> > +void
>> > +_bfd_elf_parse_eh_frame_entry (bfd *abfd, struct bfd_link_info *info,
>> > +			       asection *sec, struct elf_reloc_cookie *cookie,
>> > +			       bfd_boolean remember)
>> 
>> This does more than the comment says and the name implies; the
>> REMEMBER stuff isn't mentioned.
>> 
>> The patch tries to do the parsing during bfd_elf_discard_info, but since the
>> parsing wants to be able to fail with an error, I think we need to do it in an
>> earlier pass.  We can then return a bfd_boolean success code and propagate
>> error returns up, which the current patch doesn't do.
>> Ideally we'd put the pass somewhere before GC, so that both the GC and
>> bfd_elf_discard_info stages can assume parsed .eh_frame_entry sections.
>> 
> It looks like the legacy dwarf code parse .eh_frame sections during both
> GC and bfd_elf_discard_info.

Right.  But that parsing is allowed to fail, in which case we just
concatenate the input .eh_frames together as normal.  The parsing for
the compact scheme can't fail since the linker needs to understand
the incoming .eh_frame_entry sections in order to sort them correctly.

> There is some shared code between the legacy and compact parsing and it
> feels awkward to keep legacy eh frame parsing as is and move compact eh
> frame parsing to an earlier pass.

I think it's OK.  The schemes really are significantly different
in terms of what the linker has to do.

> I could post a patch that moves legacy parsing to an earlier pass to
> address this.  Do you think that's reasonable?
> The other option is to remove the compact eh_frame_entry parsing only
> (as you suggested).  WDYT?

Yeah, I think we should leave the current EH scheme as it is now and
only parse .eh_frame_entry sections in advance, so that the data structures
are already available at GC and bfd_elf_discard_info time.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 31+ messages in thread

* RE: [Patch] Gas support for MIPS Compact EH
  2014-02-25  8:29                 ` Richard Sandiford
@ 2014-03-17 13:22                   ` Moore, Catherine
  2014-03-17 13:52                     ` Richard Sandiford
  0 siblings, 1 reply; 31+ messages in thread
From: Moore, Catherine @ 2014-03-17 13:22 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: Schmidt, Bernd, binutils



> -----Original Message-----
> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> Sent: Tuesday, February 25, 2014 3:29 AM
> To: Moore, Catherine
> Cc: Schmidt, Bernd; binutils@sourceware.org
> Subject: Re: [Patch] Gas support for MIPS Compact EH
> 
> "Moore, Catherine" <Catherine_Moore@mentor.com> writes:
> >> -----Original Message-----
> >> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> >> Sent: Thursday, February 20, 2014 6:37 AM
> >> To: Moore, Catherine
> >> Cc: Schmidt, Bernd; binutils@sourceware.org
> >> Subject: Re: [Patch] Gas support for MIPS Compact EH
> >>
> >> >
> >> > I'd like to take this approach in the next patch:
> >> > 1. Keep the R_MIPS_EH relocation
> >> > 2. Let the linker choose the appropriate encoding 3. Clean up the
> >> > assembler inconsistencies
> >>
> >> That's OK with me, but just to clarify: (3) IMO means that R_MIPS_EH
> >> is never associated with a specific encoding in the assembler.  I.e.
> >> R_MIPS_EH is purely for an as-yet unknown encoding that is chosen by
> >> the linker rather than the assembler or the assembly author.  And
> >> AIUI the only place that happens is in .eh_frame_entry.
> >>
> >> Perhaps one way of doing that would to have a generic
> >> BFD_RELOC_EH_FRAME_HDR_32 that R_MIPS_EH maps to.  Then when
> emitting
> >> the .eh_frame_entry addresses, the assembler unconditionally uses
> >> that BFD_RELOC_ rather than a target hook.
> >>
> >> What do you plan to do for .ehword?  Since the assembler generates
> >> the .eh_frame_entry itself, and since R_MIPS_EH should only be used
> >> there (since that's the only place where the linker controls the
> >> encoding), I don't think there are any valid uses of an R_MIPS_EH-
> producing .ehword.
> >>
> >
> > The current version of the patch generates a BFD_RELOC_32_PCREL when
> > it sees the .ehword directive.
> > That should be okay going forward, agreed?
> 
> Well, PC-relative can be expressed as:
> 
> 	.word	foo-.
> 
> I think instead we should make that work and drop .ehword, to avoid
> confusion with the old behaviour.
> 
I've hit a snag with the removal of the .ehword directive.  You suggested PC-relative, but does the MIPS assembler handle that?

The current implementation has  this:

.ehword	_ZTIi

where _ZTIi is defined in libstdc++.a(fundamental_type_info.o).

Changing the assembler to:

.word	_ZTIi-.

results in:
eh8.s:212: Error: can't resolve `_ZTIi' {*UND* section} - `L0' {.gnu_extab section}

and the comments in mips.h above the definition for ASM_PREFERRED_EH_DATA_FORMAT indicate that this is not supported.

Am I doing something wrong or do we need to rethink this?
Thanks,
Catherine

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Patch] Gas support for MIPS Compact EH
  2014-03-17 13:22                   ` Moore, Catherine
@ 2014-03-17 13:52                     ` Richard Sandiford
  2014-03-19 21:12                       ` Moore, Catherine
  0 siblings, 1 reply; 31+ messages in thread
From: Richard Sandiford @ 2014-03-17 13:52 UTC (permalink / raw)
  To: Moore, Catherine; +Cc: Schmidt, Bernd, binutils

"Moore, Catherine" <Catherine_Moore@mentor.com> writes:
>> -----Original Message-----
>> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
>> Sent: Tuesday, February 25, 2014 3:29 AM
>> To: Moore, Catherine
>> Cc: Schmidt, Bernd; binutils@sourceware.org
>> Subject: Re: [Patch] Gas support for MIPS Compact EH
>> 
>> "Moore, Catherine" <Catherine_Moore@mentor.com> writes:
>> >> -----Original Message-----
>> >> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
>> >> Sent: Thursday, February 20, 2014 6:37 AM
>> >> To: Moore, Catherine
>> >> Cc: Schmidt, Bernd; binutils@sourceware.org
>> >> Subject: Re: [Patch] Gas support for MIPS Compact EH
>> >>
>> >> >
>> >> > I'd like to take this approach in the next patch:
>> >> > 1. Keep the R_MIPS_EH relocation
>> >> > 2. Let the linker choose the appropriate encoding 3. Clean up the
>> >> > assembler inconsistencies
>> >>
>> >> That's OK with me, but just to clarify: (3) IMO means that R_MIPS_EH
>> >> is never associated with a specific encoding in the assembler.  I.e.
>> >> R_MIPS_EH is purely for an as-yet unknown encoding that is chosen by
>> >> the linker rather than the assembler or the assembly author.  And
>> >> AIUI the only place that happens is in .eh_frame_entry.
>> >>
>> >> Perhaps one way of doing that would to have a generic
>> >> BFD_RELOC_EH_FRAME_HDR_32 that R_MIPS_EH maps to.  Then when
>> emitting
>> >> the .eh_frame_entry addresses, the assembler unconditionally uses
>> >> that BFD_RELOC_ rather than a target hook.
>> >>
>> >> What do you plan to do for .ehword?  Since the assembler generates
>> >> the .eh_frame_entry itself, and since R_MIPS_EH should only be used
>> >> there (since that's the only place where the linker controls the
>> >> encoding), I don't think there are any valid uses of an R_MIPS_EH-
>> producing .ehword.
>> >>
>> >
>> > The current version of the patch generates a BFD_RELOC_32_PCREL when
>> > it sees the .ehword directive.
>> > That should be okay going forward, agreed?
>> 
>> Well, PC-relative can be expressed as:
>> 
>> 	.word	foo-.
>> 
>> I think instead we should make that work and drop .ehword, to avoid
>> confusion with the old behaviour.
>> 
> I've hit a snag with the removal of the .ehword directive.  You
> suggested PC-relative, but does the MIPS assembler handle that?
>
> The current implementation has  this:
>
> .ehword	_ZTIi
>
> where _ZTIi is defined in libstdc++.a(fundamental_type_info.o).
>
> Changing the assembler to:
>
> .word	_ZTIi-.
>
> results in:
> eh8.s:212: Error: can't resolve `_ZTIi' {*UND* section} - `L0'
> {.gnu_extab section}

Right.  By "make that work" I meant getting it to assemble :-)

The point is that until you guys added R_MIPS_PC32 we had no 32-bit
PC-relative relocation, so ".word X-." wasn't supported.  We should
support it now that we do have a 32-bit PC-relative relocation we can use.

Try assembling the same thing on x86_64 and you'll see it produces
an R_X86_64_PC32.  We should do the same with R_MIPS_PC32.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 31+ messages in thread

* RE: [Patch] Gas support for MIPS Compact EH
  2014-03-17 13:52                     ` Richard Sandiford
@ 2014-03-19 21:12                       ` Moore, Catherine
  2014-03-19 23:45                         ` Richard Sandiford
  0 siblings, 1 reply; 31+ messages in thread
From: Moore, Catherine @ 2014-03-19 21:12 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: Schmidt, Bernd, binutils



> -----Original Message-----
> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> Sent: Monday, March 17, 2014 9:52 AM
> To: Moore, Catherine
> Cc: Schmidt, Bernd; binutils@sourceware.org
> Subject: Re: [Patch] Gas support for MIPS Compact EH
> 
> "Moore, Catherine" <Catherine_Moore@mentor.com> writes:
> >> -----Original Message-----
> >> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> >> Sent: Tuesday, February 25, 2014 3:29 AM
> >> To: Moore, Catherine
> >> Cc: Schmidt, Bernd; binutils@sourceware.org
> >> Subject: Re: [Patch] Gas support for MIPS Compact EH
> >>
> >> "Moore, Catherine" <Catherine_Moore@mentor.com> writes:
> >> >> -----Original Message-----
> >> >> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> >> >> Sent: Thursday, February 20, 2014 6:37 AM
> >> >> To: Moore, Catherine
> >> >> Cc: Schmidt, Bernd; binutils@sourceware.org
> >> >> Subject: Re: [Patch] Gas support for MIPS Compact EH
> >> >>
> >> >> >
> >> >> > I'd like to take this approach in the next patch:
> >> >> > 1. Keep the R_MIPS_EH relocation 2. Let the linker choose the
> >> >> > appropriate encoding 3. Clean up the assembler inconsistencies
> >> >>
> >> >> That's OK with me, but just to clarify: (3) IMO means that
> >> >> R_MIPS_EH is never associated with a specific encoding in the
> assembler.  I.e.
> >> >> R_MIPS_EH is purely for an as-yet unknown encoding that is chosen
> >> >> by the linker rather than the assembler or the assembly author.
> >> >> And AIUI the only place that happens is in .eh_frame_entry.
> >> >>
> >> >> Perhaps one way of doing that would to have a generic
> >> >> BFD_RELOC_EH_FRAME_HDR_32 that R_MIPS_EH maps to.  Then
> when
> >> emitting
> >> >> the .eh_frame_entry addresses, the assembler unconditionally uses
> >> >> that BFD_RELOC_ rather than a target hook.
> >> >>
> >> >> What do you plan to do for .ehword?  Since the assembler generates
> >> >> the .eh_frame_entry itself, and since R_MIPS_EH should only be
> >> >> used there (since that's the only place where the linker controls
> >> >> the encoding), I don't think there are any valid uses of an
> >> >> R_MIPS_EH-
> >> producing .ehword.
> >> >>
> >> >
> >> > The current version of the patch generates a BFD_RELOC_32_PCREL
> >> > when it sees the .ehword directive.
> >> > That should be okay going forward, agreed?
> >>
> >> Well, PC-relative can be expressed as:
> >>
> >> 	.word	foo-.
> >>
> >> I think instead we should make that work and drop .ehword, to avoid
> >> confusion with the old behaviour.
> >>
> > I've hit a snag with the removal of the .ehword directive.  You
> > suggested PC-relative, but does the MIPS assembler handle that?
> >
> > The current implementation has  this:
> >
> > .ehword	_ZTIi
> >
> > where _ZTIi is defined in libstdc++.a(fundamental_type_info.o).
> >
> > Changing the assembler to:
> >
> > .word	_ZTIi-.
> >
> > results in:
> > eh8.s:212: Error: can't resolve `_ZTIi' {*UND* section} - `L0'
> > {.gnu_extab section}
> 
> Right.  By "make that work" I meant getting it to assemble :-)
> 
> The point is that until you guys added R_MIPS_PC32 we had no 32-bit PC-
> relative relocation, so ".word X-." wasn't supported.  We should support it
> now that we do have a 32-bit PC-relative relocation we can use.
> 
> Try assembling the same thing on x86_64 and you'll see it produces an
> R_X86_64_PC32.  We should do the same with R_MIPS_PC32.

I've been working on getting ".word X-." to work in the assembler.  It looks like I can produce the relocation, but I'm seeing failures for  the lui-1.s and lui-2.s tests.
I've added these two definitions to tc-mips.h:

+#define DIFF_EXPR_OK
+#define md_register_arithmetic 0

The first thing is that I'm not sure if the setting for md_register_arithmetic is correct.  The MIPS port currently uses the default of 1 (that's got to be wrong).  
There is interaction between DIFF_EXPR_OK, md_register_arithmetic and the FORCE_RELOC macros that is confusing.

This definition of TC_FORCE_RELOCATION_SUB_LOCAL (write.c) needs to have md_register_arithmetic defined to zero, when DIFF_EXPR_OK is set.

#ifndef TC_FORCE_RELOCATION_SUB_LOCAL
#ifdef DIFF_EXPR_OK
#define TC_FORCE_RELOCATION_SUB_LOCAL(FIX, SEG) \
  (!md_register_arithmetic && (SEG) == reg_section)
#else
#define TC_FORCE_RELOCATION_SUB_LOCAL(FIX, SEG) 1
#endif
#endif

Needs to have md_register_arithmetic set to zero if DIFF_EXPR_OK is set.

Assuming that defining md_register_arithmetic to zero is the correct thing to do, then the error message in md_pcrel_from looks wrong:

long
md_pcrel_from (fixS *fixP)
{
  valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
  switch (fixP->fx_r_type)
    {
    case BFD_RELOC_MICROMIPS_7_PCREL_S1:
    case BFD_RELOC_MICROMIPS_10_PCREL_S1:
      /* Return the address of the delay slot.  */
      return addr + 2;

    case BFD_RELOC_MICROMIPS_16_PCREL_S1:
    case BFD_RELOC_MICROMIPS_JMP:
    case BFD_RELOC_16_PCREL_S2:
    case BFD_RELOC_MIPS_JMP:
      /* Return the address of the delay slot.  */
      return addr + 4;

    case BFD_RELOC_32_PCREL:
      return addr;

    default:
      /* We have no relocation type for PC relative MIPS16 instructions.  */
      if (fixP->fx_addsy && S_GET_SEGMENT (fixP->fx_addsy) != now_seg)
        as_bad_where (fixP->fx_file, fixP->fx_line,
                      _("PC relative MIPS16 instruction references"
                        " a different section"));
      return addr;
    }
}


fixup_segment now treats many relocs as "possibly pc-relative" and waits for md_apply_fix to make the call.
Should the default be to return addr for any reloc that's encountered and try to process errors later?  I don't have any state on how a MIPS16 reloc ends up here.

Then there is an assert in md_apply_fix that assumes a pc relative reloc is one of   a handful of relocs.  The fx_pcrel field needs to be reset for relocations that aren't pc-relative.  Is md_apply_fix the correct place to do that?

Then the test cases in question.   This is lui-1.s:

        .text
foo:
        lui     $2, -1
        lui     $2, 65536
        lui     $2, 0x10000000000000000
        lui     $2, $3
        lui     $2, ($3)
        lui     $2, 0+$3  <<--- this statement
        lui     $2, (($3))

Old error message:
lui-1.s:10: Error: register value used as expression `lui $2,0+$3'

New error message:
None, for the entire test case, but a new file with just the offending line causes a assertion failure in bfd_elf32_swap_symbol_out.  (That's wrong, I need to track it down).

Then lui-2.s:

        .text
foo:
        lui     $2, bar - foo
        lui     $2, baz - bar
        lui     $2, foo - baz
        lui     $2, bar / baz

Should any of these be supported now?

Old error messages:
lui-2.s:10: Error: invalid operands (*UND* and *UND* sections) for `/'
lui-2.s:7: Error: can't resolve `bar' {*UND* section} - `foo' {.text section}
lui-2.s:8: Error: can't resolve `baz' {*UND* section} - `bar' {*UND* section}
lui-2.s:9: Error: can't resolve `.text' {.text section} - `baz' {*UND* section}

New error messages:
lui-2.s:10: Error: invalid operands (*UND* and *UND* sections) for `/'
lui-2.s:8: Error: can't resolve `baz' {*UND* section} - `bar' {*UND* section}
lui-2.s:9: Error: can't resolve `.text' {.text section} - `baz' {*UND* section}

Notice that lui $2 bar-foo seems to be accepted.  Should it be?

Thanks for any help. 
Catherine

The WIP patch:

diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c
index 74c7a10..24d2a32 100644
--- a/gas/config/tc-mips.c
+++ b/gas/config/tc-mips.c
@@ -14057,15 +14057,13 @@ md_pcrel_from (fixS *fixP)
       /* Return the address of the delay slot.  */
       return addr + 4;

-    case BFD_RELOC_32_PCREL:
-      return addr;
-
     default:
-      /* We have no relocation type for PC relative MIPS16 instructions.  */
+#if 0
       if (fixP->fx_addsy && S_GET_SEGMENT (fixP->fx_addsy) != now_seg)
        as_bad_where (fixP->fx_file, fixP->fx_line,
-                     _("PC relative MIPS16 instruction references"
+                     _("PC relative instruction references"
                        " a different section"));
+#endif
       return addr;
     }
 }
@@ -14280,11 +14278,16 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)

   buf = fixP->fx_frag->fr_literal + fixP->fx_where;

-  gas_assert (!fixP->fx_pcrel || fixP->fx_r_type == BFD_RELOC_16_PCREL_S2
-             || fixP->fx_r_type == BFD_RELOC_MICROMIPS_7_PCREL_S1
-             || fixP->fx_r_type == BFD_RELOC_MICROMIPS_10_PCREL_S1
-             || fixP->fx_r_type == BFD_RELOC_MICROMIPS_16_PCREL_S1
-             || fixP->fx_r_type == BFD_RELOC_32_PCREL);
+  if (fixP->fx_pcrel && fixP->fx_r_type == BFD_RELOC_32)
+    fixP->fx_r_type = BFD_RELOC_32_PCREL;
+
+  if (fixP->fx_pcrel
+      && fixP->fx_r_type != BFD_RELOC_16_PCREL_S2
+      && fixP->fx_r_type != BFD_RELOC_MICROMIPS_7_PCREL_S1
+      && fixP->fx_r_type != BFD_RELOC_MICROMIPS_10_PCREL_S1
+      && fixP->fx_r_type != BFD_RELOC_MICROMIPS_16_PCREL_S1
+      && fixP->fx_r_type != BFD_RELOC_32_PCREL)
+    fixP->fx_pcrel = 0;

   /* Don't treat parts of a composite relocation as done.  There are two
      reasons for this:
diff --git a/gas/config/tc-mips.h b/gas/config/tc-mips.h
index 97627df..de3010e 100644
--- a/gas/config/tc-mips.h
+++ b/gas/config/tc-mips.h
@@ -136,6 +136,9 @@ extern int mips_fix_adjustable (struct fix *);
 #define EXTERN_FORCE_RELOC                     \
   (OUTPUT_FLAVOR == bfd_target_elf_flavour)

+#define DIFF_EXPR_OK
+#define md_register_arithmetic 0
+






^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Patch] Gas support for MIPS Compact EH
  2014-03-19 21:12                       ` Moore, Catherine
@ 2014-03-19 23:45                         ` Richard Sandiford
  2014-03-20 14:29                           ` Moore, Catherine
  0 siblings, 1 reply; 31+ messages in thread
From: Richard Sandiford @ 2014-03-19 23:45 UTC (permalink / raw)
  To: Moore, Catherine; +Cc: Schmidt, Bernd, binutils

"Moore, Catherine" <Catherine_Moore@mentor.com> writes:
> I've been working on getting ".word X-." to work in the assembler.  It looks like I can produce the relocation, but I'm seeing failures for  the lui-1.s and lui-2.s tests.
> I've added these two definitions to tc-mips.h:
>
> +#define DIFF_EXPR_OK
> +#define md_register_arithmetic 0
>
> The first thing is that I'm not sure if the setting for md_register_arithmetic is correct.  The MIPS port currently uses the default of 1 (that's got to be wrong).  
> There is interaction between DIFF_EXPR_OK, md_register_arithmetic and the FORCE_RELOC macros that is confusing.
>
> This definition of TC_FORCE_RELOCATION_SUB_LOCAL (write.c) needs to have md_register_arithmetic defined to zero, when DIFF_EXPR_OK is set.
>
> #ifndef TC_FORCE_RELOCATION_SUB_LOCAL
> #ifdef DIFF_EXPR_OK
> #define TC_FORCE_RELOCATION_SUB_LOCAL(FIX, SEG) \
>   (!md_register_arithmetic && (SEG) == reg_section)
> #else
> #define TC_FORCE_RELOCATION_SUB_LOCAL(FIX, SEG) 1
> #endif
> #endif
>
> Needs to have md_register_arithmetic set to zero if DIFF_EXPR_OK is set.

I think we should leave md_register_arithmetic as it is.  The
"(SEG) == reg_section" means that the special handling only applies
to "register minus something" expressions.

> Assuming that defining md_register_arithmetic to zero is the correct thing to do, then the error message in md_pcrel_from looks wrong:
>
> long
> md_pcrel_from (fixS *fixP)
> {
>   valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
>   switch (fixP->fx_r_type)
>     {
>     case BFD_RELOC_MICROMIPS_7_PCREL_S1:
>     case BFD_RELOC_MICROMIPS_10_PCREL_S1:
>       /* Return the address of the delay slot.  */
>       return addr + 2;
>
>     case BFD_RELOC_MICROMIPS_16_PCREL_S1:
>     case BFD_RELOC_MICROMIPS_JMP:
>     case BFD_RELOC_16_PCREL_S2:
>     case BFD_RELOC_MIPS_JMP:
>       /* Return the address of the delay slot.  */
>       return addr + 4;
>
>     case BFD_RELOC_32_PCREL:
>       return addr;
>
>     default:
>       /* We have no relocation type for PC relative MIPS16 instructions.  */
>       if (fixP->fx_addsy && S_GET_SEGMENT (fixP->fx_addsy) != now_seg)
>         as_bad_where (fixP->fx_file, fixP->fx_line,
>                       _("PC relative MIPS16 instruction references"
>                         " a different section"));
>       return addr;
>     }
> }
>
>
> fixup_segment now treats many relocs as "possibly pc-relative" and waits for md_apply_fix to make the call.
> Should the default be to return addr for any reloc that's encountered and try to process errors later?  I don't have any state on how a MIPS16 reloc ends up here.

The change comes from:

    https://www.sourceware.org/ml/binutils/2006-11/msg00029.html

but I'm not sure which case it was handling.

I agree it'd make sense to check this elsewhere.  I think the best place
would be...

> Then there is an assert in md_apply_fix that assumes a pc relative reloc is one of   a handful of relocs.  The fx_pcrel field needs to be reset for relocations that aren't pc-relative.  Is md_apply_fix the correct place to do that?

...instead of this assert.

> Then the test cases in question.   This is lui-1.s:
>
>         .text
> foo:
>         lui     $2, -1
>         lui     $2, 65536
>         lui     $2, 0x10000000000000000
>         lui     $2, $3
>         lui     $2, ($3)
>         lui     $2, 0+$3  <<--- this statement
>         lui     $2, (($3))
>
> Old error message:
> lui-1.s:10: Error: register value used as expression `lui $2,0+$3'
>
> New error message:
> None, for the entire test case, but a new file with just the offending line causes a assertion failure in bfd_elf32_swap_symbol_out.  (That's wrong, I need to track it down).

Yeah, this is a consequence of changing md_register_arithmetic.
The old behaviour was right.

> Then lui-2.s:
>
>         .text
> foo:
>         lui     $2, bar - foo
>         lui     $2, baz - bar
>         lui     $2, foo - baz
>         lui     $2, bar / baz
>
> Should any of these be supported now?
>
> Old error messages:
> lui-2.s:10: Error: invalid operands (*UND* and *UND* sections) for `/'
> lui-2.s:7: Error: can't resolve `bar' {*UND* section} - `foo' {.text section}
> lui-2.s:8: Error: can't resolve `baz' {*UND* section} - `bar' {*UND* section}
> lui-2.s:9: Error: can't resolve `.text' {.text section} - `baz' {*UND* section}
>
> New error messages:
> lui-2.s:10: Error: invalid operands (*UND* and *UND* sections) for `/'
> lui-2.s:8: Error: can't resolve `baz' {*UND* section} - `bar' {*UND* section}
> lui-2.s:9: Error: can't resolve `.text' {.text section} - `baz' {*UND* section}
>
> Notice that lui $2 bar-foo seems to be accepted.  Should it be?

No, the old behaviour was right here too, since we don't have a PC-relative
form of R_MIPS_LO16.  The new error you mentioned should catch it.

How about the patch below?  Tested on various MIPSy targets against
the binutils testsuite, but nothing beyond that so far.

Thanks,
Richard


gas/
	* config/tc-mips.h (DIFF_EXPR_OK, CFI_DIFF_EXPR_OK): Define.
	* config/tc-mips.c (md_pcrel_from): Remove error message.
	(md_apply_fix): Convert PC-relative BFD_RELOC_32s to
	BFD_RELOC_32_PCREL.  Report a specific error message for unhandled
	PC-relative expressions.  Handle BFD_RELOC_8.

gas/testsuite/
	* gas/all/gas.exp: Remove XFAIL of forward.d for MIPS.
	* gas/mips/pcrel-1.s, gas/mips/pcrel-1.d, gas/mips/pcrel-2.s,
	gas/mips/pcrel-2.d, gas/mips/pcrel-3.s, gas/mips/pcrel-3.l,
	gas/mips/pcrel-4.s, gas/mips/pcrel-4-32.d, gas/mips/pcrel-4-n32.d,
	gas/mips/pcrel-4-64.d: New tests.
	* gas/mips/mips.exp: Run them.
	* gas/mips/lui-2.l: Tweak error message for line 7.

ld/testsuite/
	* ld-elf/merge.d: Remove MIPS XFAIL.

Index: gas/config/tc-mips.h
===================================================================
--- gas/config/tc-mips.h	2014-03-19 22:44:18.584854477 +0000
+++ gas/config/tc-mips.h	2014-03-19 23:43:50.695861764 +0000
@@ -189,4 +189,9 @@ extern int tc_mips_regname_to_dw2regnum
 #define DWARF2_DEFAULT_RETURN_COLUMN 31
 #define DWARF2_CIE_DATA_ALIGNMENT (-4)
 
+#define DIFF_EXPR_OK
+/* We define DIFF_EXPR_OK because of R_MIPS_PC32, but we have no
+   64-bit form for n64 CFIs.  */
+#define CFI_DIFF_EXPR_OK 0
+
 #endif /* TC_MIPS */
Index: gas/config/tc-mips.c
===================================================================
--- gas/config/tc-mips.c	2014-03-19 22:44:18.584854477 +0000
+++ gas/config/tc-mips.c	2014-03-19 23:12:27.340621113 +0000
@@ -14057,15 +14057,7 @@ md_pcrel_from (fixS *fixP)
       /* Return the address of the delay slot.  */
       return addr + 4;
 
-    case BFD_RELOC_32_PCREL:
-      return addr;
-
     default:
-      /* We have no relocation type for PC relative MIPS16 instructions.  */
-      if (fixP->fx_addsy && S_GET_SEGMENT (fixP->fx_addsy) != now_seg)
-	as_bad_where (fixP->fx_file, fixP->fx_line,
-		      _("PC relative MIPS16 instruction references"
-			" a different section"));
       return addr;
     }
 }
@@ -14262,13 +14254,38 @@ md_apply_fix (fixS *fixP, valueT *valP,
   unsigned long insn;
   reloc_howto_type *howto;
 
-  /* We ignore generic BFD relocations we don't know about.  */
-  howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
-  if (! howto)
-    return;
+  if (fixP->fx_pcrel)
+    switch (fixP->fx_r_type)
+      {
+      case BFD_RELOC_16_PCREL_S2:
+      case BFD_RELOC_MICROMIPS_7_PCREL_S1:
+      case BFD_RELOC_MICROMIPS_10_PCREL_S1:
+      case BFD_RELOC_MICROMIPS_16_PCREL_S1:
+      case BFD_RELOC_32_PCREL:
+	break;
+
+      case BFD_RELOC_32:
+	fixP->fx_r_type = BFD_RELOC_32_PCREL;
+	break;
+
+      default:
+	as_bad_where (fixP->fx_file, fixP->fx_line,
+		      _("PC-relative reference to a different section"));
+	break;
+      }
+
+  /* Handle BFD_RELOC_8, since it's easy.  Punt on other bfd relocations
+     that have no MIPS ELF equivalent.  */
+  if (fixP->fx_r_type != BFD_RELOC_8)
+    {
+      howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
+      if (!howto)
+	return;
+    }
 
   gas_assert (fixP->fx_size == 2
 	      || fixP->fx_size == 4
+	      || fixP->fx_r_type == BFD_RELOC_8
 	      || fixP->fx_r_type == BFD_RELOC_16
 	      || fixP->fx_r_type == BFD_RELOC_64
 	      || fixP->fx_r_type == BFD_RELOC_CTOR
@@ -14280,12 +14297,6 @@ md_apply_fix (fixS *fixP, valueT *valP,
 
   buf = fixP->fx_frag->fr_literal + fixP->fx_where;
 
-  gas_assert (!fixP->fx_pcrel || fixP->fx_r_type == BFD_RELOC_16_PCREL_S2
-	      || fixP->fx_r_type == BFD_RELOC_MICROMIPS_7_PCREL_S1
-	      || fixP->fx_r_type == BFD_RELOC_MICROMIPS_10_PCREL_S1
-	      || fixP->fx_r_type == BFD_RELOC_MICROMIPS_16_PCREL_S1
-	      || fixP->fx_r_type == BFD_RELOC_32_PCREL);
-
   /* Don't treat parts of a composite relocation as done.  There are two
      reasons for this:
 
@@ -14435,6 +14446,7 @@ md_apply_fix (fixS *fixP, valueT *valP,
     case BFD_RELOC_32:
     case BFD_RELOC_32_PCREL:
     case BFD_RELOC_16:
+    case BFD_RELOC_8:
       /* If we are deleting this reloc entry, we must fill in the
 	 value now.  This can happen if we have a .word which is not
 	 resolved when it appears but is later defined.  */
Index: gas/testsuite/gas/all/gas.exp
===================================================================
--- gas/testsuite/gas/all/gas.exp	2014-03-19 21:26:29.344209431 +0000
+++ gas/testsuite/gas/all/gas.exp	2014-03-19 23:28:58.492008472 +0000
@@ -99,7 +99,7 @@ case $target_triplet in {
     default {
 	# Some targets don't manage to resolve BFD_RELOC_8 for constants.
 	setup_xfail "alpha*-*-*" "*c30*-*-*" "*c4x*-*-*" \
-	    "d\[13\]0v*-*-*" "i860-*-*" "mips*-*-*" \
+	    "d\[13\]0v*-*-*" "i860-*-*" \
 	    "nds32*-*-*" "pdp11-*-*" "xtensa*-*-*"
 	run_dump_test forward
     }
Index: gas/testsuite/gas/mips/pcrel-1.s
===================================================================
--- /dev/null	2014-03-15 14:12:32.004596490 +0000
+++ gas/testsuite/gas/mips/pcrel-1.s	2014-03-19 23:12:27.341621122 +0000
@@ -0,0 +1,13 @@
+	.text
+	.ent	func
+func:
+	lui	$4,%hi(foo-.)
+	addiu	$4,%lo(foo-.)
+	.end	func
+
+	.space	0x8008
+
+	.ent	foo
+foo:
+	nop
+	.end	foo
Index: gas/testsuite/gas/mips/pcrel-1.d
===================================================================
--- /dev/null	2014-03-15 14:12:32.004596490 +0000
+++ gas/testsuite/gas/mips/pcrel-1.d	2014-03-19 23:12:27.341621122 +0000
@@ -0,0 +1,14 @@
+#objdump: -dr
+#name: Locally-resolvable PC-relative code references
+
+.*:     file format .*
+
+Disassembly of section .text:
+
+00000000 <func>:
+       0:	3c040001 	lui	a0,0x1
+       4:	2484800c 	addiu	a0,a0,-32756
+	...
+
+00008010 <foo>:
+#pass
Index: gas/testsuite/gas/mips/pcrel-2.s
===================================================================
--- /dev/null	2014-03-15 14:12:32.004596490 +0000
+++ gas/testsuite/gas/mips/pcrel-2.s	2014-03-19 23:12:27.342621131 +0000
@@ -0,0 +1,7 @@
+	.data
+	.byte	0xff
+	.byte	frob-.
+	.half	frob-.
+	.word	frob-.
+	.quad	frob-.
+frob:
Index: gas/testsuite/gas/mips/pcrel-2.d
===================================================================
--- /dev/null	2014-03-15 14:12:32.004596490 +0000
+++ gas/testsuite/gas/mips/pcrel-2.d	2014-03-19 23:12:27.342621131 +0000
@@ -0,0 +1,8 @@
+#objdump: -s
+#name: Locally-resolvable PC-relative data references
+#as: -EB
+
+#...
+Contents of section \.data:
+ 0000 ff0f000e 0000000c 00000000 00000008  .*
+#pass
Index: gas/testsuite/gas/mips/pcrel-3.s
===================================================================
--- /dev/null	2014-03-15 14:12:32.004596490 +0000
+++ gas/testsuite/gas/mips/pcrel-3.s	2014-03-19 23:12:27.342621131 +0000
@@ -0,0 +1,11 @@
+	.text
+	.ent	func
+func:
+	lui	$4,%hi(foo-.)
+	addiu	$4,%lo(foo-.)
+	lw	$4,%got(foo-.)($gp)
+	.end	func
+
+	.byte	foo-.
+	.half	foo-.
+	.quad	foo-.
Index: gas/testsuite/gas/mips/pcrel-3.l
===================================================================
--- /dev/null	2014-03-15 14:12:32.004596490 +0000
+++ gas/testsuite/gas/mips/pcrel-3.l	2014-03-19 23:12:27.342621131 +0000
@@ -0,0 +1,7 @@
+.*: Assembler messages:
+.*:4: Error: PC-relative reference to a different section
+.*:5: Error: PC-relative reference to a different section
+.*:6: Error: PC-relative reference to a different section
+.*:9: Error: PC-relative reference to a different section
+.*:10: Error: PC-relative reference to a different section
+.*:11: Error: PC-relative reference to a different section
Index: gas/testsuite/gas/mips/pcrel-4.s
===================================================================
--- /dev/null	2014-03-15 14:12:32.004596490 +0000
+++ gas/testsuite/gas/mips/pcrel-4.s	2014-03-19 23:12:27.343621139 +0000
@@ -0,0 +1,6 @@
+	.data
+	.word	foo-.
+	.word	foo-(.-4)
+	.word	foo+8-.
+	.word	foo-.-16
+
Index: gas/testsuite/gas/mips/pcrel-4-32.d
===================================================================
--- /dev/null	2014-03-15 14:12:32.004596490 +0000
+++ gas/testsuite/gas/mips/pcrel-4-32.d	2014-03-19 23:12:27.342621131 +0000
@@ -0,0 +1,18 @@
+#objdump: -sr
+#name: Valid cross-section PC-relative references (o32)
+#as: -32 -EB
+#source: pcrel-4.s
+
+.*:     file format .*
+
+RELOCATION RECORDS FOR \[\.data\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       foo
+00000004 R_MIPS_PC32       foo
+00000008 R_MIPS_PC32       foo
+0000000c R_MIPS_PC32       foo
+
+#...
+Contents of section \.data:
+ 0000 00000000 00000004 00000008 fffffff0  ................
+#pass
Index: gas/testsuite/gas/mips/pcrel-4-n32.d
===================================================================
--- /dev/null	2014-03-15 14:12:32.004596490 +0000
+++ gas/testsuite/gas/mips/pcrel-4-n32.d	2014-03-19 23:26:47.946909763 +0000
@@ -0,0 +1,13 @@
+#objdump: -r
+#name: Valid cross-section PC-relative references (n32)
+#as: -n32 -mips3
+#source: pcrel-4.s
+
+.*:     file format .*
+
+RELOCATION RECORDS FOR \[\.data\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       foo
+00000004 R_MIPS_PC32       foo\+0x00000004
+00000008 R_MIPS_PC32       foo\+0x00000008
+0000000c R_MIPS_PC32       foo-0x00000010
Index: gas/testsuite/gas/mips/pcrel-4-64.d
===================================================================
--- /dev/null	2014-03-15 14:12:32.004596490 +0000
+++ gas/testsuite/gas/mips/pcrel-4-64.d	2014-03-19 23:26:58.591999264 +0000
@@ -0,0 +1,21 @@
+#objdump: -r
+#name: Valid cross-section PC-relative references (n64)
+#as: -64 -mips3
+#source: pcrel-4.s
+
+.*:     file format .*
+
+RELOCATION RECORDS FOR \[\.data\]:
+OFFSET           TYPE              VALUE 
+0+000 R_MIPS_PC32       foo
+0+000 R_MIPS_NONE       \*ABS\*
+0+000 R_MIPS_NONE       \*ABS\*
+0+004 R_MIPS_PC32       foo\+0x0+004
+0+004 R_MIPS_NONE       \*ABS\*\+0x0+004
+0+004 R_MIPS_NONE       \*ABS\*\+0x0+004
+0+008 R_MIPS_PC32       foo\+0x0+008
+0+008 R_MIPS_NONE       \*ABS\*\+0x0+008
+0+008 R_MIPS_NONE       \*ABS\*\+0x0+008
+0+00c R_MIPS_PC32       foo-0x0+010
+0+00c R_MIPS_NONE       \*ABS\*-0x0+010
+0+00c R_MIPS_NONE       \*ABS\*-0x0+010
Index: gas/testsuite/gas/mips/mips.exp
===================================================================
--- gas/testsuite/gas/mips/mips.exp	2014-03-19 22:44:18.584854477 +0000
+++ gas/testsuite/gas/mips/mips.exp	2014-03-19 23:12:27.341621122 +0000
@@ -1169,4 +1169,13 @@ if { [istarget mips*-*-vxworks*] } {
     run_dump_test_arches "msa64"	[mips_arch_list_matching mips64r2]
     run_dump_test_arches "msa-relax"	[mips_arch_list_matching mips32r2]
     run_dump_test_arches "msa-branch"	[mips_arch_list_matching mips32r2]
+
+    run_dump_test "pcrel-1"
+    run_dump_test "pcrel-2"
+    run_list_test "pcrel-3" "" "Invalid cross-section PC-relative references"
+    run_dump_test "pcrel-4-32"
+    if $has_newabi {
+	run_dump_test "pcrel-4-n32"
+	run_dump_test "pcrel-4-64"
+    }
 }
Index: gas/testsuite/gas/mips/lui-2.l
===================================================================
--- gas/testsuite/gas/mips/lui-2.l	2014-03-19 22:44:18.584854477 +0000
+++ gas/testsuite/gas/mips/lui-2.l	2014-03-19 23:12:27.343621139 +0000
@@ -1,5 +1,5 @@
 .*\.s: Assembler messages:
 .*\.s:10: Error: invalid operands \(\*UND\* and \*UND\* sections\) for `/'
-.*\.s:7: Error: can't resolve `bar' {\*UND\* section} - `foo' {\.text section}
+.*\.s:7: Error: PC-relative reference to a different section
 .*\.s:8: Error: can't resolve `baz' {\*UND\* section} - `bar' {\*UND\* section}
 .*\.s:9: Error: can't resolve `\.text' {\.text section} - `baz' {\*UND\* section}
Index: ld/testsuite/ld-elf/merge.d
===================================================================
--- ld/testsuite/ld-elf/merge.d	2013-11-13 21:08:01.200582091 +0000
+++ ld/testsuite/ld-elf/merge.d	2014-03-19 23:30:33.635810673 +0000
@@ -4,7 +4,7 @@
 #xfail: "arc-*-*" "avr-*-*" "bfin-*-*" "cr16-*-*" "cris*-*-*" "crx-*-*" "d10v-*-*" "d30v-*-*"
 #xfail: "dlx-*-*" "fr30-*-*" "frv-*-*" "hppa*64*-*-*" "h8300-*-*" "score-*-*"
 #xfail: "i370-*-*" "i860-*-*" "i960-*-*" "ip2k-*-*" "iq2000-*-*" "lm32-*-*"
-#xfail: "mcore-*-*" "mn102*-*-*" "mips*-*-*" "ms1-*-*" "mep-*-*"
+#xfail: "mcore-*-*" "mn102*-*-*" "ms1-*-*" "mep-*-*"
 #xfail: "or32-*-*" "pj-*-*" "sparc*-*-*" "tic6x-*-*" "vax-*-*" "xstormy16-*-*"
 #xfail: "xtensa*-*-*" "metag-*-*"
 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* RE: [Patch] Gas support for MIPS Compact EH
  2014-03-19 23:45                         ` Richard Sandiford
@ 2014-03-20 14:29                           ` Moore, Catherine
  2014-03-20 21:19                             ` Richard Sandiford
  0 siblings, 1 reply; 31+ messages in thread
From: Moore, Catherine @ 2014-03-20 14:29 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: Schmidt, Bernd, binutils



> -----Original Message-----
> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> Sent: Wednesday, March 19, 2014 7:46 PM
> To: Moore, Catherine
> Cc: Schmidt, Bernd; binutils@sourceware.org
> Subject: Re: [Patch] Gas support for MIPS Compact EH
> 
> "Moore, Catherine" <Catherine_Moore@mentor.com> writes:
> > I've been working on getting ".word X-." to work in the assembler.  It looks
> like I can produce the relocation, but I'm seeing failures for  the lui-1.s and lui-
> 2.s tests.
> > I've added these two definitions to tc-mips.h:
> >
> > +#define DIFF_EXPR_OK
> > +#define md_register_arithmetic 0
> >
> > The first thing is that I'm not sure if the setting for md_register_arithmetic
> is correct.  The MIPS port currently uses the default of 1 (that's got to be
> wrong).
> > There is interaction between DIFF_EXPR_OK, md_register_arithmetic and
> the FORCE_RELOC macros that is confusing.
> >
> > This definition of TC_FORCE_RELOCATION_SUB_LOCAL (write.c) needs to
> have md_register_arithmetic defined to zero, when DIFF_EXPR_OK is set.
> >
> > #ifndef TC_FORCE_RELOCATION_SUB_LOCAL
> > #ifdef DIFF_EXPR_OK
> > #define TC_FORCE_RELOCATION_SUB_LOCAL(FIX, SEG) \
> >   (!md_register_arithmetic && (SEG) == reg_section) #else #define
> > TC_FORCE_RELOCATION_SUB_LOCAL(FIX, SEG) 1 #endif #endif
> >
> > Needs to have md_register_arithmetic set to zero if DIFF_EXPR_OK is set.
> 
> I think we should leave md_register_arithmetic as it is.  The
> "(SEG) == reg_section" means that the special handling only applies to
> "register minus something" expressions.
> 
> > Assuming that defining md_register_arithmetic to zero is the correct thing
> to do, then the error message in md_pcrel_from looks wrong:
> >
> > long
> > md_pcrel_from (fixS *fixP)
> > {
> >   valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
> >   switch (fixP->fx_r_type)
> >     {
> >     case BFD_RELOC_MICROMIPS_7_PCREL_S1:
> >     case BFD_RELOC_MICROMIPS_10_PCREL_S1:
> >       /* Return the address of the delay slot.  */
> >       return addr + 2;
> >
> >     case BFD_RELOC_MICROMIPS_16_PCREL_S1:
> >     case BFD_RELOC_MICROMIPS_JMP:
> >     case BFD_RELOC_16_PCREL_S2:
> >     case BFD_RELOC_MIPS_JMP:
> >       /* Return the address of the delay slot.  */
> >       return addr + 4;
> >
> >     case BFD_RELOC_32_PCREL:
> >       return addr;
> >
> >     default:
> >       /* We have no relocation type for PC relative MIPS16 instructions.  */
> >       if (fixP->fx_addsy && S_GET_SEGMENT (fixP->fx_addsy) != now_seg)
> >         as_bad_where (fixP->fx_file, fixP->fx_line,
> >                       _("PC relative MIPS16 instruction references"
> >                         " a different section"));
> >       return addr;
> >     }
> > }
> >
> >
> > fixup_segment now treats many relocs as "possibly pc-relative" and waits
> for md_apply_fix to make the call.
> > Should the default be to return addr for any reloc that's encountered and
> try to process errors later?  I don't have any state on how a MIPS16 reloc
> ends up here.
> 
> The change comes from:
> 
>     https://www.sourceware.org/ml/binutils/2006-11/msg00029.html
> 
> but I'm not sure which case it was handling.
> 
> I agree it'd make sense to check this elsewhere.  I think the best place would
> be...
> 
> > Then there is an assert in md_apply_fix that assumes a pc relative reloc is
> one of   a handful of relocs.  The fx_pcrel field needs to be reset for
> relocations that aren't pc-relative.  Is md_apply_fix the correct place to do
> that?
> 
> ...instead of this assert.
> 
> > Then the test cases in question.   This is lui-1.s:
> >
> >         .text
> > foo:
> >         lui     $2, -1
> >         lui     $2, 65536
> >         lui     $2, 0x10000000000000000
> >         lui     $2, $3
> >         lui     $2, ($3)
> >         lui     $2, 0+$3  <<--- this statement
> >         lui     $2, (($3))
> >
> > Old error message:
> > lui-1.s:10: Error: register value used as expression `lui $2,0+$3'
> >
> > New error message:
> > None, for the entire test case, but a new file with just the offending line
> causes a assertion failure in bfd_elf32_swap_symbol_out.  (That's wrong, I
> need to track it down).
> 
> Yeah, this is a consequence of changing md_register_arithmetic.
> The old behaviour was right.
> 
> > Then lui-2.s:
> >
> >         .text
> > foo:
> >         lui     $2, bar - foo
> >         lui     $2, baz - bar
> >         lui     $2, foo - baz
> >         lui     $2, bar / baz
> >
> > Should any of these be supported now?
> >
> > Old error messages:
> > lui-2.s:10: Error: invalid operands (*UND* and *UND* sections) for `/'
> > lui-2.s:7: Error: can't resolve `bar' {*UND* section} - `foo' {.text
> > section}
> > lui-2.s:8: Error: can't resolve `baz' {*UND* section} - `bar' {*UND*
> > section}
> > lui-2.s:9: Error: can't resolve `.text' {.text section} - `baz' {*UND*
> > section}
> >
> > New error messages:
> > lui-2.s:10: Error: invalid operands (*UND* and *UND* sections) for `/'
> > lui-2.s:8: Error: can't resolve `baz' {*UND* section} - `bar' {*UND*
> > section}
> > lui-2.s:9: Error: can't resolve `.text' {.text section} - `baz' {*UND*
> > section}
> >
> > Notice that lui $2 bar-foo seems to be accepted.  Should it be?
> 
> No, the old behaviour was right here too, since we don't have a PC-relative
> form of R_MIPS_LO16.  The new error you mentioned should catch it.
> 
> How about the patch below?  Tested on various MIPSy targets against the
> binutils testsuite, but nothing beyond that so far.
> 

Yes, this patch looks good.  I lightly tested it and it appears to DTRT.
Thanks for helping to sort this out.
Catherine


> 
> gas/
> 	* config/tc-mips.h (DIFF_EXPR_OK, CFI_DIFF_EXPR_OK): Define.
> 	* config/tc-mips.c (md_pcrel_from): Remove error message.
> 	(md_apply_fix): Convert PC-relative BFD_RELOC_32s to
> 	BFD_RELOC_32_PCREL.  Report a specific error message for
> unhandled
> 	PC-relative expressions.  Handle BFD_RELOC_8.
> 
> gas/testsuite/
> 	* gas/all/gas.exp: Remove XFAIL of forward.d for MIPS.
> 	* gas/mips/pcrel-1.s, gas/mips/pcrel-1.d, gas/mips/pcrel-2.s,
> 	gas/mips/pcrel-2.d, gas/mips/pcrel-3.s, gas/mips/pcrel-3.l,
> 	gas/mips/pcrel-4.s, gas/mips/pcrel-4-32.d, gas/mips/pcrel-4-n32.d,
> 	gas/mips/pcrel-4-64.d: New tests.
> 	* gas/mips/mips.exp: Run them.
> 	* gas/mips/lui-2.l: Tweak error message for line 7.
> 
> ld/testsuite/
> 	* ld-elf/merge.d: Remove MIPS XFAIL.
> 
> Index: gas/config/tc-mips.h
> ==========================================================
> =========
> --- gas/config/tc-mips.h	2014-03-19 22:44:18.584854477 +0000
> +++ gas/config/tc-mips.h	2014-03-19 23:43:50.695861764 +0000
> @@ -189,4 +189,9 @@ extern int tc_mips_regname_to_dw2regnum
> #define DWARF2_DEFAULT_RETURN_COLUMN 31  #define
> DWARF2_CIE_DATA_ALIGNMENT (-4)
> 
> +#define DIFF_EXPR_OK
> +/* We define DIFF_EXPR_OK because of R_MIPS_PC32, but we have no
> +   64-bit form for n64 CFIs.  */
> +#define CFI_DIFF_EXPR_OK 0
> +
>  #endif /* TC_MIPS */
> Index: gas/config/tc-mips.c
> ==========================================================
> =========
> --- gas/config/tc-mips.c	2014-03-19 22:44:18.584854477 +0000
> +++ gas/config/tc-mips.c	2014-03-19 23:12:27.340621113 +0000
> @@ -14057,15 +14057,7 @@ md_pcrel_from (fixS *fixP)
>        /* Return the address of the delay slot.  */
>        return addr + 4;
> 
> -    case BFD_RELOC_32_PCREL:
> -      return addr;
> -
>      default:
> -      /* We have no relocation type for PC relative MIPS16 instructions.  */
> -      if (fixP->fx_addsy && S_GET_SEGMENT (fixP->fx_addsy) != now_seg)
> -	as_bad_where (fixP->fx_file, fixP->fx_line,
> -		      _("PC relative MIPS16 instruction references"
> -			" a different section"));
>        return addr;
>      }
>  }
> @@ -14262,13 +14254,38 @@ md_apply_fix (fixS *fixP, valueT *valP,
>    unsigned long insn;
>    reloc_howto_type *howto;
> 
> -  /* We ignore generic BFD relocations we don't know about.  */
> -  howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
> -  if (! howto)
> -    return;
> +  if (fixP->fx_pcrel)
> +    switch (fixP->fx_r_type)
> +      {
> +      case BFD_RELOC_16_PCREL_S2:
> +      case BFD_RELOC_MICROMIPS_7_PCREL_S1:
> +      case BFD_RELOC_MICROMIPS_10_PCREL_S1:
> +      case BFD_RELOC_MICROMIPS_16_PCREL_S1:
> +      case BFD_RELOC_32_PCREL:
> +	break;
> +
> +      case BFD_RELOC_32:
> +	fixP->fx_r_type = BFD_RELOC_32_PCREL;
> +	break;
> +
> +      default:
> +	as_bad_where (fixP->fx_file, fixP->fx_line,
> +		      _("PC-relative reference to a different section"));
> +	break;
> +      }
> +
> +  /* Handle BFD_RELOC_8, since it's easy.  Punt on other bfd relocations
> +     that have no MIPS ELF equivalent.  */
> +  if (fixP->fx_r_type != BFD_RELOC_8)
> +    {
> +      howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
> +      if (!howto)
> +	return;
> +    }
> 
>    gas_assert (fixP->fx_size == 2
>  	      || fixP->fx_size == 4
> +	      || fixP->fx_r_type == BFD_RELOC_8
>  	      || fixP->fx_r_type == BFD_RELOC_16
>  	      || fixP->fx_r_type == BFD_RELOC_64
>  	      || fixP->fx_r_type == BFD_RELOC_CTOR @@ -14280,12 +14297,6
> @@ md_apply_fix (fixS *fixP, valueT *valP,
> 
>    buf = fixP->fx_frag->fr_literal + fixP->fx_where;
> 
> -  gas_assert (!fixP->fx_pcrel || fixP->fx_r_type ==
> BFD_RELOC_16_PCREL_S2
> -	      || fixP->fx_r_type == BFD_RELOC_MICROMIPS_7_PCREL_S1
> -	      || fixP->fx_r_type == BFD_RELOC_MICROMIPS_10_PCREL_S1
> -	      || fixP->fx_r_type == BFD_RELOC_MICROMIPS_16_PCREL_S1
> -	      || fixP->fx_r_type == BFD_RELOC_32_PCREL);
> -
>    /* Don't treat parts of a composite relocation as done.  There are two
>       reasons for this:
> 
> @@ -14435,6 +14446,7 @@ md_apply_fix (fixS *fixP, valueT *valP,
>      case BFD_RELOC_32:
>      case BFD_RELOC_32_PCREL:
>      case BFD_RELOC_16:
> +    case BFD_RELOC_8:
>        /* If we are deleting this reloc entry, we must fill in the
>  	 value now.  This can happen if we have a .word which is not
>  	 resolved when it appears but is later defined.  */
> Index: gas/testsuite/gas/all/gas.exp
> ==========================================================
> =========
> --- gas/testsuite/gas/all/gas.exp	2014-03-19 21:26:29.344209431 +0000
> +++ gas/testsuite/gas/all/gas.exp	2014-03-19 23:28:58.492008472 +0000
> @@ -99,7 +99,7 @@ case $target_triplet in {
>      default {
>  	# Some targets don't manage to resolve BFD_RELOC_8 for constants.
>  	setup_xfail "alpha*-*-*" "*c30*-*-*" "*c4x*-*-*" \
> -	    "d\[13\]0v*-*-*" "i860-*-*" "mips*-*-*" \
> +	    "d\[13\]0v*-*-*" "i860-*-*" \
>  	    "nds32*-*-*" "pdp11-*-*" "xtensa*-*-*"
>  	run_dump_test forward
>      }
> Index: gas/testsuite/gas/mips/pcrel-1.s
> ==========================================================
> =========
> --- /dev/null	2014-03-15 14:12:32.004596490 +0000
> +++ gas/testsuite/gas/mips/pcrel-1.s	2014-03-19 23:12:27.341621122 +0000
> @@ -0,0 +1,13 @@
> +	.text
> +	.ent	func
> +func:
> +	lui	$4,%hi(foo-.)
> +	addiu	$4,%lo(foo-.)
> +	.end	func
> +
> +	.space	0x8008
> +
> +	.ent	foo
> +foo:
> +	nop
> +	.end	foo
> Index: gas/testsuite/gas/mips/pcrel-1.d
> ==========================================================
> =========
> --- /dev/null	2014-03-15 14:12:32.004596490 +0000
> +++ gas/testsuite/gas/mips/pcrel-1.d	2014-03-19 23:12:27.341621122 +0000
> @@ -0,0 +1,14 @@
> +#objdump: -dr
> +#name: Locally-resolvable PC-relative code references
> +
> +.*:     file format .*
> +
> +Disassembly of section .text:
> +
> +00000000 <func>:
> +       0:	3c040001 	lui	a0,0x1
> +       4:	2484800c 	addiu	a0,a0,-32756
> +	...
> +
> +00008010 <foo>:
> +#pass
> Index: gas/testsuite/gas/mips/pcrel-2.s
> ==========================================================
> =========
> --- /dev/null	2014-03-15 14:12:32.004596490 +0000
> +++ gas/testsuite/gas/mips/pcrel-2.s	2014-03-19 23:12:27.342621131 +0000
> @@ -0,0 +1,7 @@
> +	.data
> +	.byte	0xff
> +	.byte	frob-.
> +	.half	frob-.
> +	.word	frob-.
> +	.quad	frob-.
> +frob:
> Index: gas/testsuite/gas/mips/pcrel-2.d
> ==========================================================
> =========
> --- /dev/null	2014-03-15 14:12:32.004596490 +0000
> +++ gas/testsuite/gas/mips/pcrel-2.d	2014-03-19 23:12:27.342621131 +0000
> @@ -0,0 +1,8 @@
> +#objdump: -s
> +#name: Locally-resolvable PC-relative data references
> +#as: -EB
> +
> +#...
> +Contents of section \.data:
> + 0000 ff0f000e 0000000c 00000000 00000008  .* #pass
> Index: gas/testsuite/gas/mips/pcrel-3.s
> ==========================================================
> =========
> --- /dev/null	2014-03-15 14:12:32.004596490 +0000
> +++ gas/testsuite/gas/mips/pcrel-3.s	2014-03-19 23:12:27.342621131 +0000
> @@ -0,0 +1,11 @@
> +	.text
> +	.ent	func
> +func:
> +	lui	$4,%hi(foo-.)
> +	addiu	$4,%lo(foo-.)
> +	lw	$4,%got(foo-.)($gp)
> +	.end	func
> +
> +	.byte	foo-.
> +	.half	foo-.
> +	.quad	foo-.
> Index: gas/testsuite/gas/mips/pcrel-3.l
> ==========================================================
> =========
> --- /dev/null	2014-03-15 14:12:32.004596490 +0000
> +++ gas/testsuite/gas/mips/pcrel-3.l	2014-03-19 23:12:27.342621131 +0000
> @@ -0,0 +1,7 @@
> +.*: Assembler messages:
> +.*:4: Error: PC-relative reference to a different section
> +.*:5: Error: PC-relative reference to a different section
> +.*:6: Error: PC-relative reference to a different section
> +.*:9: Error: PC-relative reference to a different section
> +.*:10: Error: PC-relative reference to a different section
> +.*:11: Error: PC-relative reference to a different section
> Index: gas/testsuite/gas/mips/pcrel-4.s
> ==========================================================
> =========
> --- /dev/null	2014-03-15 14:12:32.004596490 +0000
> +++ gas/testsuite/gas/mips/pcrel-4.s	2014-03-19 23:12:27.343621139 +0000
> @@ -0,0 +1,6 @@
> +	.data
> +	.word	foo-.
> +	.word	foo-(.-4)
> +	.word	foo+8-.
> +	.word	foo-.-16
> +
> Index: gas/testsuite/gas/mips/pcrel-4-32.d
> ==========================================================
> =========
> --- /dev/null	2014-03-15 14:12:32.004596490 +0000
> +++ gas/testsuite/gas/mips/pcrel-4-32.d	2014-03-19
> 23:12:27.342621131 +0000
> @@ -0,0 +1,18 @@
> +#objdump: -sr
> +#name: Valid cross-section PC-relative references (o32)
> +#as: -32 -EB
> +#source: pcrel-4.s
> +
> +.*:     file format .*
> +
> +RELOCATION RECORDS FOR \[\.data\]:
> +OFFSET   TYPE              VALUE
> +00000000 R_MIPS_PC32       foo
> +00000004 R_MIPS_PC32       foo
> +00000008 R_MIPS_PC32       foo
> +0000000c R_MIPS_PC32       foo
> +
> +#...
> +Contents of section \.data:
> + 0000 00000000 00000004 00000008 fffffff0  ................
> +#pass
> Index: gas/testsuite/gas/mips/pcrel-4-n32.d
> ==========================================================
> =========
> --- /dev/null	2014-03-15 14:12:32.004596490 +0000
> +++ gas/testsuite/gas/mips/pcrel-4-n32.d	2014-03-19
> 23:26:47.946909763 +0000
> @@ -0,0 +1,13 @@
> +#objdump: -r
> +#name: Valid cross-section PC-relative references (n32)
> +#as: -n32 -mips3
> +#source: pcrel-4.s
> +
> +.*:     file format .*
> +
> +RELOCATION RECORDS FOR \[\.data\]:
> +OFFSET   TYPE              VALUE
> +00000000 R_MIPS_PC32       foo
> +00000004 R_MIPS_PC32       foo\+0x00000004
> +00000008 R_MIPS_PC32       foo\+0x00000008
> +0000000c R_MIPS_PC32       foo-0x00000010
> Index: gas/testsuite/gas/mips/pcrel-4-64.d
> ==========================================================
> =========
> --- /dev/null	2014-03-15 14:12:32.004596490 +0000
> +++ gas/testsuite/gas/mips/pcrel-4-64.d	2014-03-19
> 23:26:58.591999264 +0000
> @@ -0,0 +1,21 @@
> +#objdump: -r
> +#name: Valid cross-section PC-relative references (n64)
> +#as: -64 -mips3
> +#source: pcrel-4.s
> +
> +.*:     file format .*
> +
> +RELOCATION RECORDS FOR \[\.data\]:
> +OFFSET           TYPE              VALUE
> +0+000 R_MIPS_PC32       foo
> +0+000 R_MIPS_NONE       \*ABS\*
> +0+000 R_MIPS_NONE       \*ABS\*
> +0+004 R_MIPS_PC32       foo\+0x0+004
> +0+004 R_MIPS_NONE       \*ABS\*\+0x0+004
> +0+004 R_MIPS_NONE       \*ABS\*\+0x0+004
> +0+008 R_MIPS_PC32       foo\+0x0+008
> +0+008 R_MIPS_NONE       \*ABS\*\+0x0+008
> +0+008 R_MIPS_NONE       \*ABS\*\+0x0+008
> +0+00c R_MIPS_PC32       foo-0x0+010
> +0+00c R_MIPS_NONE       \*ABS\*-0x0+010
> +0+00c R_MIPS_NONE       \*ABS\*-0x0+010
> Index: gas/testsuite/gas/mips/mips.exp
> ==========================================================
> =========
> --- gas/testsuite/gas/mips/mips.exp	2014-03-19 22:44:18.584854477 +0000
> +++ gas/testsuite/gas/mips/mips.exp	2014-03-19 23:12:27.341621122 +0000
> @@ -1169,4 +1169,13 @@ if { [istarget mips*-*-vxworks*] } {
>      run_dump_test_arches "msa64"	[mips_arch_list_matching mips64r2]
>      run_dump_test_arches "msa-relax"	[mips_arch_list_matching mips32r2]
>      run_dump_test_arches "msa-branch"	[mips_arch_list_matching
> mips32r2]
> +
> +    run_dump_test "pcrel-1"
> +    run_dump_test "pcrel-2"
> +    run_list_test "pcrel-3" "" "Invalid cross-section PC-relative references"
> +    run_dump_test "pcrel-4-32"
> +    if $has_newabi {
> +	run_dump_test "pcrel-4-n32"
> +	run_dump_test "pcrel-4-64"
> +    }
>  }
> Index: gas/testsuite/gas/mips/lui-2.l
> ==========================================================
> =========
> --- gas/testsuite/gas/mips/lui-2.l	2014-03-19 22:44:18.584854477 +0000
> +++ gas/testsuite/gas/mips/lui-2.l	2014-03-19 23:12:27.343621139 +0000
> @@ -1,5 +1,5 @@
>  .*\.s: Assembler messages:
>  .*\.s:10: Error: invalid operands \(\*UND\* and \*UND\* sections\) for `/'
> -.*\.s:7: Error: can't resolve `bar' {\*UND\* section} - `foo' {\.text section}
> +.*\.s:7: Error: PC-relative reference to a different section
>  .*\.s:8: Error: can't resolve `baz' {\*UND\* section} - `bar' {\*UND\* section}
>  .*\.s:9: Error: can't resolve `\.text' {\.text section} - `baz' {\*UND\* section}
> Index: ld/testsuite/ld-elf/merge.d
> ==========================================================
> =========
> --- ld/testsuite/ld-elf/merge.d	2013-11-13 21:08:01.200582091 +0000
> +++ ld/testsuite/ld-elf/merge.d	2014-03-19 23:30:33.635810673 +0000
> @@ -4,7 +4,7 @@
>  #xfail: "arc-*-*" "avr-*-*" "bfin-*-*" "cr16-*-*" "cris*-*-*" "crx-*-*" "d10v-
> *-*" "d30v-*-*"
>  #xfail: "dlx-*-*" "fr30-*-*" "frv-*-*" "hppa*64*-*-*" "h8300-*-*" "score-*-
> *"
>  #xfail: "i370-*-*" "i860-*-*" "i960-*-*" "ip2k-*-*" "iq2000-*-*" "lm32-*-*"
> -#xfail: "mcore-*-*" "mn102*-*-*" "mips*-*-*" "ms1-*-*" "mep-*-*"
> +#xfail: "mcore-*-*" "mn102*-*-*" "ms1-*-*" "mep-*-*"
>  #xfail: "or32-*-*" "pj-*-*" "sparc*-*-*" "tic6x-*-*" "vax-*-*" "xstormy16-*-
> *"
>  #xfail: "xtensa*-*-*" "metag-*-*"
> 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Patch] Gas support for MIPS Compact EH
  2014-03-20 14:29                           ` Moore, Catherine
@ 2014-03-20 21:19                             ` Richard Sandiford
  0 siblings, 0 replies; 31+ messages in thread
From: Richard Sandiford @ 2014-03-20 21:19 UTC (permalink / raw)
  To: Moore, Catherine; +Cc: Schmidt, Bernd, binutils

"Moore, Catherine" <Catherine_Moore@mentor.com> writes:
>> How about the patch below?  Tested on various MIPSy targets against the
>> binutils testsuite, but nothing beyond that so far.
>> 
>
> Yes, this patch looks good.  I lightly tested it and it appears to DTRT.

Thanks.  It also tested OK with GCC on mips64-linux-gnu, so I went ahead
and applied it.

Richard

^ permalink raw reply	[flat|nested] 31+ messages in thread

* RE: [Patch] Gas support for MIPS Compact EH
  2014-02-08 16:34     ` Richard Sandiford
  2014-02-08 18:48       ` Bernd Schmidt
  2014-03-06 17:44       ` Moore, Catherine
@ 2014-03-25 13:50       ` Moore, Catherine
  2014-03-25 14:06         ` Richard Sandiford
  2014-11-17 16:10       ` Moore, Catherine
  3 siblings, 1 reply; 31+ messages in thread
From: Moore, Catherine @ 2014-03-25 13:50 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: binutils



> -----Original Message-----
> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> Subject: Re: [Patch] Gas support for MIPS Compact EH
> 
> "Moore, Catherine" <Catherine_Moore@mentor.com> writes:
> 
> > +/* Finish a pass over all .eh_frame and eh_frame_entry sections.  */
> > +
> > +bfd_boolean
> >  _bfd_elf_end_eh_frame_parsing (struct bfd_link_info *info)  {
> >    struct eh_frame_hdr_info *hdr_info;
> > +  unsigned int i;
> >
> >    hdr_info = &elf_hash_table (info)->eh_info;
> >    hdr_info->parsed_eh_frames = TRUE;
> > +
> > +  if (hdr_info->array_count == 0 || info->eh_frame_hdr < 2)
> > +    return FALSE;
> > +
> > +  qsort (hdr_info->entries, hdr_info->array_count,
> > +	 sizeof (asection *), cmp_eh_frame_hdr);
> > +
> > +  for (i = 0; i < hdr_info->array_count - 1; i++)
> > +    {
> > +      add_eh_frame_hdr_terminator (hdr_info->entries[i],
> > +				   hdr_info->entries[i + 1]);
> > +    }
> > +
> > +  /* Add a CANTUNWIND terminator after the last entry.  */
> > + add_eh_frame_hdr_terminator (hdr_info->entries[i], NULL);  return
> > + TRUE;
> 
> This routine is called from both bfd_elf_gc_sections and
> bfd_elf_discard_info but I think you only want it for bfd_elf_discard_info.
> So perhaps this should be a separate function.
> 

Quick question here -- perhaps what should be a separate function?  Sorry, didn't quite understand that comment.
Thanks,
Catherine


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Patch] Gas support for MIPS Compact EH
  2014-03-25 13:50       ` Moore, Catherine
@ 2014-03-25 14:06         ` Richard Sandiford
  0 siblings, 0 replies; 31+ messages in thread
From: Richard Sandiford @ 2014-03-25 14:06 UTC (permalink / raw)
  To: Moore, Catherine; +Cc: binutils

"Moore, Catherine" <Catherine_Moore@mentor.com> writes:
>> -----Original Message-----
>> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
>> Subject: Re: [Patch] Gas support for MIPS Compact EH
>> 
>> "Moore, Catherine" <Catherine_Moore@mentor.com> writes:
>> 
>> > +/* Finish a pass over all .eh_frame and eh_frame_entry sections.  */
>> > +
>> > +bfd_boolean
>> >  _bfd_elf_end_eh_frame_parsing (struct bfd_link_info *info)  {
>> >    struct eh_frame_hdr_info *hdr_info;
>> > +  unsigned int i;
>> >
>> >    hdr_info = &elf_hash_table (info)->eh_info;
>> >    hdr_info->parsed_eh_frames = TRUE;
>> > +
>> > +  if (hdr_info->array_count == 0 || info->eh_frame_hdr < 2)
>> > +    return FALSE;
>> > +
>> > +  qsort (hdr_info->entries, hdr_info->array_count,
>> > +	 sizeof (asection *), cmp_eh_frame_hdr);
>> > +
>> > +  for (i = 0; i < hdr_info->array_count - 1; i++)
>> > +    {
>> > +      add_eh_frame_hdr_terminator (hdr_info->entries[i],
>> > +				   hdr_info->entries[i + 1]);
>> > +    }
>> > +
>> > +  /* Add a CANTUNWIND terminator after the last entry.  */
>> > + add_eh_frame_hdr_terminator (hdr_info->entries[i], NULL);  return
>> > + TRUE;
>> 
>> This routine is called from both bfd_elf_gc_sections and
>> bfd_elf_discard_info but I think you only want it for bfd_elf_discard_info.
>> So perhaps this should be a separate function.
>> 
>
> Quick question here -- perhaps what should be a separate function?
> Sorry, didn't quite understand that comment.

I just thought that the code you were adding here (to add terminators)
should go in a separate function that only gets called once, rather than
being part of _bfd_elf_end_eh_frame_parsing.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 31+ messages in thread

* RE: [Patch] Gas support for MIPS Compact EH
  2014-02-08 16:34     ` Richard Sandiford
                         ` (2 preceding siblings ...)
  2014-03-25 13:50       ` Moore, Catherine
@ 2014-11-17 16:10       ` Moore, Catherine
  2014-11-22 14:42         ` Richard Sandiford
  3 siblings, 1 reply; 31+ messages in thread
From: Moore, Catherine @ 2014-11-17 16:10 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: binutils

[-- Attachment #1: Type: text/plain, Size: 48256 bytes --]

Hi Richard,
Please find the updated Compact EH  patch for binutils. 
This will likely still be a hard patch to review, but I tried to address the many comments that you made earlier.
The main difference is the exception handling relocation type for the Linux toolchain.  It is no longer gprel based. 
The ELF and Linux tools both use pcrel32 and the EH-specific relocation type has been removed. 
I ran into a few issues with the recent changes to eh_frame handling for DWARF, I hopefully covered those with the Compact EH implementation. 
Please let me know what you think.  I hope we are close to converging on an implementation.
Thanks,
Catherine


> -----Original Message-----
> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> Sent: Saturday, February 08, 2014 11:34 AM
> To: Moore, Catherine
> Cc: binutils@sourceware.org
> Subject: Re: [Patch] Gas support for MIPS Compact EH
> 
> Thanks for the updates.
> 
> "Moore, Catherine" <Catherine_Moore@mentor.com> writes:
> >> > @@ -4514,6 +4517,23 @@ argument is not present, otherwise secon  or
> >> > a symbol name.  The default after @code{.cfi_startproc} is
> >> > @code{.cfi_lsda 0xff},  no LSDA.
> >> >
> >> > +@section @code{.cfi_inline_lsda} [@var{align}]
> >> > +@code{.cfi_inline_lsda} marks the start of a LSDA data section and
> >> > +switches to the corresponding @code{.gnu.extab} section.
> >> > +It must be preceded by a CFI block containing a @code{.cfi_lsda}
> >> > +directive and is only valid when generating compact EH frames (i.e.
> >> > +with @code{.cfi_sections eh_frame_entry}.
> >> > +
> >> > +If a compact encoding is being used then the table header and
> >> > +unwinding opcodes will be generated at this point, so that they
> >> > +are immediately followed by the LSDA data.  The symbol referenced
> >> > +by the @code{.cfi_lsda} directive should still be defined in case
> >> > +a fallback FDE based encoding is used.
> >> > +
> >> > +The optional @var{align} argument specifies the alignment required.
> >> > +The alignment is specified as a power of two, as with the
> >> > +@code{.p2align} directive.
> >>
> >> Hmm, switching sections and emitting data feels very different in
> >> style from the other .cfi directives, which just annotate code
> >> without changing the flow of assembly.  I'd like to know other people's
> thoughts on this.
> >>
> >> Also, how do you terminate the LSDA?  The documentation doesn't say
> >> (but should :-)) and I couldn't see this directive in the spec either.
> >
> > The LSDA is terminated by a section switch.
> 
> OK.  Like I say, please mention this in the documentation.
> 
> >> "If a compact encoding is being used" seems redundant, since it comes
> >> just after the bit saying "only valid when generating compact EH frames".
> >>
> >> There need to be tests for all of this.
> >
> > Tests are now included in the patch.
> >
> >> TBH, without tests, and without an
> >> explanation of what the code is doing, I found this patch pretty hard
> >> to review.  E.g.:
> >>
> >> > @@ -129,7 +140,12 @@ get_debugseg_name (segT seg, const char
> >> >        dot = strchr (name + 1, '.');
> >> >
> >> >        if (!dollar && !dot)
> >> > -	name = "";
> >> > +	{
> >> > +	  if (compact_eh && strcmp (name, ".text") != 0)
> >> > +	    return concat (base_name, ".", name, NULL);
> >> > +
> >> > +	  name = "";
> >> > +	}
> >>
> >> why is this change needed?  I.e., for a text section called something
> >> like .foobar, why does compact_eh need to put things in a section
> >> name ending in "..foobar", rather than in the main EH section?  I
> >> assume the double dots are deliberate, e.g. to avoid confusion with
> ".text.foobar"?
> >
> > I'm not sure why Paul put this is and the next hunk.  There were test
> > failures without.  I can revisit this For the next iteration if
> > necessary.
> 
> Yeah, if you could that'd be great.  The code can't really go in if there's no-
> one around who understands what it does.
> 
> I assume it's just to ensure that each text section has its own
> .eh_frame_entry, but in that case I think we should check based on the
> base_name rather than compact_eh.  Or do we need the same treatment
> for .gnu_extab.  If so, why?
> 
> A comment is needed at the very least.
> 
> >> > @@ -161,6 +177,9 @@ alloc_debugseg_item (segT seg, int subse
> >> > static segT  is_now_linkonce_segment (void)  {
> >> > +  if (compact_eh)
> >> > +    return now_seg;
> >> > +
> >> >    if ((bfd_get_section_flags (stdoutput, now_seg)
> >> >         & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD
> >> >  	  | SEC_LINK_DUPLICATES_ONE_ONLY |
> >> SEC_LINK_DUPLICATES_SAME_SIZE
> >>
> >> Why is this change needed?
> >>
> >> > @@ -180,7 +199,11 @@ make_debug_seg (segT cseg, char *name, i
> >> >    segT r;
> >> >    flagword flags;
> >> >
> >> > +#ifdef tc_make_debug_seg
> >> > +  r = tc_make_debug_seg (cseg, name); #else
> >> >    r = subseg_new (name, 0);
> >> > +#endif
> >>
> >> Why is this change needed?  And what does the new hook do?  It should
> >> be documented in internals.texi.
> >>
> >> Do we really want to change the behaviour for traditional DWARF EH too?
> >>
> >
> > It looks like this was intended to make .group sections so that text,
> > extab and eh_frame entries are only deleted together.  This is now
> > removed and this new patch add some linker smarts to handle things.
> 
> Thanks.
> 
> >> > @@ -833,14 +859,15 @@ dot_cfi_personality (int ignored ATTRIBU
> >> >      }
> >> >
> >> >    if ((encoding & 0xff) != encoding
> >> > -      || ((encoding & 0x70) != 0
> >> > +      || ((((encoding & 0x70) != 0
> >> >  #if CFI_DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr
> >> > -	  && (encoding & 0x70) != DW_EH_PE_pcrel
> >> > +	   && (encoding & 0x70) != DW_EH_PE_pcrel
> >> >  #endif
> >> >  	  )
> >> >  	 /* leb128 can be handled, but does something actually need it?  */
> >> > -      || (encoding & 7) == DW_EH_PE_uleb128
> >> > -      || (encoding & 7) > DW_EH_PE_udata8)
> >> > +	   || (encoding & 7) == DW_EH_PE_uleb128
> >> > +	   || (encoding & 7) > DW_EH_PE_udata8)
> >> > +	&& !tc_cfi_special_encoding (encoding)))
> >> >      {
> >> >        as_bad (_("invalid or unsupported encoding in .cfi_personality"));
> >> >        ignore_rest_of_line ();
> >>
> >> What does a "special" encoding mean?  Again, this hook should be
> >> documented in internals.texi.  And do we really want to change the
> >> set of encodings that are allowed for DWARF, even on MIPS systems
> >> that predate compat EH?
> >>
> > Special means that we have code in the backend to emit a reloc for it.
> > In the revised patch, it goes along with Tc_cfi_reloc_for_encoding.
> > It also looks like internals.texi fails to document many of the
> > tc_macros and it doesn't appear to be built into a .info fiie.
> 
> That's no reason not to document new hooks though.  I know I've used
> internals.texi to read more about a hook in the past.  If you don't want to put
> it there though then please at least put it in a comment instead.
> 
> I found this a really hard and time-consuming patch to review.
> That could just be because I'm stupid, but I think you're underestimating how
> difficult this is to follow for someone coming to it new.
> 
> >> Why is the CFI_EMIT_target needed?
> > Sttrictly speaking it isn't, but having a different value was useful
> > in the error checking.
> 
> Specifically, useful in what way?  Please could you add a comment explaining
> why.
> 
> >>
> >>   data_size += encoding_size (fde->per_encoding);
> >>
> >> would work; if not, please extend encoding_size.  We then have:
> >>
> >> > +  if (fde->per_encoding != DW_EH_PE_omit)
> >> > +    {
> >> > +      *(p++) = 0;
> >> > +      md_number_to_chars (p, 0, 4);
> >> > +      tc_cfi_fix_eh_ref (p, &fde->personality);
> >> > +      p += 4;
> >>
> >> where tc_cfi_fix_eh_ref emits an R_MIPS_EH relocation regardless of
> >> which personality encoding is used.  AIUI, the encoding must be one
> >> of the "special" ones allowed by tc_cfi_special_encoding, so we
> >> should check for that.
> >>
> >> The tc_cfi_fix_eh_ref and tc_cfi_emit_expr hooks don't seem very
> >> consistent; the former relies on the caller to clear the bytes,
> >> whereas the latter is supposed to do it itself.
> >
> > All fixed, now using a hook to return a reloc and eliminated the use
> > of R_MIPS_EH from the assembler.
> 
> Hmm, but how does it work under the new scheme?  It looks like gas now
> always emits the .eh_frame_entry sections using R_MIPS_PC32, is that right?
> But the linker chooses the .eh_frame_hdr encoding based on --pcrel-eh-
> reloc, which also controls how R_MIPS_EH is handled.  So if the:
> 
>   DW_EH_PE_sdata4 | DW_EH_PE_datarel | DW_EH_PE_indirect
> 
> encoding is chosen for the .eh_frame_entry sections at link time, what
> converts the input sections to use that encoding instead of the original
> R_MIPS_PC32?  I'd assumed R_MIPS_EH was defined the way it was to avoid
> that kind of thing.
> 
> I wasn't expecting you to drop the R_MIPS_EH stuff altogether.
> I was just confused by the way that the old assembler patch seemed to be
> associating R_MIPS_EH with a specific encoding
> (DW_EH_PE_sdata4 | DW_EH_PE_datarel | DW_EH_PE_indirect).  So:
> 
> >> Neither of the hooks really seem to be doing anything target-specific
> >> except for the all-important job of picking a reloc.  I think it
> >> would be cleaner to have a hook that returns the reloc instead.
> >> In the case of tc_cfi_emit_expr, this could be done by making
> >> tc_cfi_special_encoding return the reloc for an encoding, or
> >> BFD_RELOC_NONE for unsupported encodings.
> >>
> >> Also, this is more of a design point, but I find the handling of the
> >> personality encoding and R_MIPS_EH handling a bit confusing.  To
> >> quote:
> >>
> >> ---------------------------------------------------------------------
> >> 11 Relocations
> >>
> >> A new static relocation, R_MIPS_EH, is defined. The semantics of this
> >> relocation depend on whether static or dynamic linking is provided.
> >>
> >> A GNU extension using relocation number 249 shall be used. The
> >> relocation address need not be naturally aligned.
> >>
> >> 11.1 Static code
> >>
> >> For static code generation, the calculation is the same as an
> >> R_MIPS_REL32 relocation.
> >>
> >> At runtime, the following expression provides the relocated value, if 'ptr'
> >> points to the relocation location.
> >>
> >> • (ptrdiff_t)ptr + *(int32_t __attribute__((packed))*)ptr
> >>
> >> [...]
> >>
> >> 11.2 PIC code
> >>
> >> For PIC code generation, a 32- or 64-bit GOT-table entry must be
> >> allocated to refer to the (dynamically resolved) target address. Once
> >> the GOT entry has been allocated, the static calculation is as for an
> >> R_MIPS_GPREL32 relocation (except that the symbol is externally visible).
> >> The GOT- slot has an associated R_MIPS_{32,64} dynamic relocation
> >> emitted – and that will of course be at a naturally aligned location
> >>
> >> At runtime, the following expression provides the relocated value, if 'ptr'
> >> points to the relocation location, and 'gp' is the global pointer
> >> value:
> >>
> >> • *(ptrdiff_t *)((ptrdiff_t)gp + *(int32_t __attribute__((packed))
> >> *)ptr)
> >>
> >> [...]
> >> ---------------------------------------------------------------------
> >>
> >> So as I understand the first sentence, it's actually the linker that
> >> decides whether R_MIPS_EH relocations act as direct PC-relative
> >> references (11.1) or as indirect datarel references (11.2).
> >> It's therefore the linker that decides what DWARF encoding R_MIPS_EH
> >> fields use.  The linker then records that choice in the .eh_frame_hdr.
> >> Is that
> >> right?
> >>
> >> If so, the assembler seems to be associating R_MIPS_EH with specific
> >> DWARF encodings, even though the interpretation of R_MIPS_EH isn't
> >> known at that stage.  I.e. the code reads:
> >>
> >> #define tc_cfi_special_encoding(e) \
> >>   ((e) == (DW_EH_PE_sdata4 | DW_EH_PE_datarel | DW_EH_PE_indirect)
> \
> >>    || (e) == (DW_EH_PE_sdata4 | DW_EH_PE_pcrel))
> >>
> >> void
> >> mips_cfi_emit_expr (expressionS *exp, int encoding) {
> >>   char *p;
> >>
> >>   p = frag_more(4);
> >>   md_number_to_chars (p, 0, 4);
> >>   if ((encoding & 0x70) == DW_EH_PE_datarel)
> >>     mips_cfi_fix_eh_ref (p, exp);
> >>   else
> >>     {
> >>       fix_new (frag_now, p - frag_now->fr_literal, 4, exp->X_add_symbol,
> >> 	       exp->X_add_number, TRUE, BFD_RELOC_32_PCREL);
> >>     }
> >> }
> >>
> >> where mips_cfi_fix_eh_ref emits an R_MIPS_EH.  So the code appears to
> >> be using R_MIPS_EH for the DWARF encoding:
> >>
> >>     DW_EH_PE_sdata4 | DW_EH_PE_datarel | DW_EH_PE_indirect
> >>
> >> even though there's no guarantee that the final value will be either
> >> datarel or indirect.
> >>
> >> Or, to put it another way, why are we making the choice between
> >> R_MIPS_PC32 and R_MIPS_EH at assembly time in mips_cfi_emit_expr,
> but
> >> not in mips_cfi_fix_eh_ref, even though the patch appears to allow
> >> the same two encodings in both casees?
> 
> I thought R_MIPS_EH would be used for the .eh_frame_entry entries only,
> since in that case the actual encoding of the address isn't known until link
> time.  I thought mips_cfi_emit_expr, which deals with specific assembly-time
> encoding types, ought to use specific relocations for those relocation types
> instead.  I.e. mips_cfi_emit_expr would always use
> R_MIPS_PC32 for:
> 
>   DW_EH_PE_sdata4 | DW_EH_PE_pcrel
> 
> and always use a new relocation type for:
> 
>   DW_EH_PE_sdata4 | DW_EH_PE_datarel | DW_EH_PE_indirect
> 
> Note that the spec is wrong to say that R_MIPS_GPREL32 is equivalent to the
> above, since R_MIPS_GPREL32 doesn't include any indirection.
> I think we need to define a new reloc such as R_MIPS_GOT_DISP32.
> 
> But more fundamentally, I don't really understand why we want the GOT-
> based encoding for .eh_frame_hdr by default.  Is that intended for systems
> that allow the text and data segments to be moved relative to each other,
> like VxWorks?  It seems like a big hammer on bare-metal and GNU/Linux,
> where the PC-relative form seems better.  And on VxWorks it seems a bad
> idea to eat up valuable GOT space, since VxWorks doesn't have any form of
> multigot support.
> 
> >> > +  demand_empty_rest_of_line ();
> >> > +  ccseg = CUR_SEG (last_fde);
> >> > +  /* Open .gnu_extab section.  */
> >> > +  cfi_seg = get_cfi_seg (ccseg, ".gnu_extab",
> >> > +			 (SEC_ALLOC | SEC_LOAD | SEC_DATA
> >> > +			  | DWARF2_EH_FRAME_READ_ONLY),
> >> > +			 1);
> >> > +#ifdef md_fix_up_eh_frame
> >> > +  md_fix_up_eh_frame (cfi_seg);
> >> > +#else
> >> > +  (void) cfi_seg;
> >> > +#endif
> >> > +
> >> > +  frag_align (align, 0, 0);
> >> > +  record_alignment (now_seg, align);  if (last_fde->eh_header_type
> >> > + == EH_COMPACT_HAS_LSDA)
> >> > +    output_compact_unwind_data (last_fde, align);
> >>
> >> Please could you explain the EH_COMPACT_LEGACY handling here?
> >
> > Would you please clarify this question?  I don't see the reference to
> > EH_COMPACT_LEGACY.
> 
> That was the problem :-)  Further up there's:
> 
>   if (last_fde->eh_header_type != EH_COMPACT_LEGACY
>       && last_fde->eh_header_type != EH_COMPACT_HAS_LSDA)
>     {
>       as_bad (_(".cfi_inline_lsda seen for frame without .cfi_lsda"));
>       ignore_rest_of_line ();
>       return;
>     }
> 
> So what does this code mean/do in the:
> 
>   last_fde->eh_header_type == EH_COMPACT_LEGACY
> 
> case?
> 
> > diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index 0aab5fa..b7d7df0
> > 100644
> > --- a/bfd/elf-bfd.h
> > +++ b/bfd/elf-bfd.h
> > @@ -381,8 +381,10 @@ struct eh_frame_hdr_info  {
> >    struct htab *cies;
> >    asection *hdr_sec;
> > -  unsigned int fde_count, array_count;
> > +  unsigned int fde_count, array_count, allocated_entries;
> >    struct eh_frame_array_ent *array;
> > +  /* eh_frame_entry fragments.  */
> > +  asection **entries;
> >    /* TRUE if we should try to merge CIEs between input sections.  */
> >    bfd_boolean merge_cies;
> >    /* TRUE if all .eh_frames have been parsd.  */
> 
> I'm not sure there's enough commonality between the DWARF and compact
> versions to share this structure.  Either we should have separate structures
> or use a union to separate out the format-specific bits.
> 
> > @@ -1433,6 +1441,7 @@ struct bfd_elf_section_data
> >  #define elf_next_in_group(sec)	(elf_section_data(sec)-
> >next_in_group)
> >  #define elf_fde_list(sec)	(elf_section_data(sec)->fde_list)
> >  #define elf_sec_group(sec)	(elf_section_data(sec)->sec_group)
> > +#define elf_section_eh_frame_entry(sec)	(elf_section_data(sec)-
> >eh_frame_entry)
> 
> Long line.
> 
> > +/* Add a .eh_frame_entry section.  */
> > +
> > +static void
> > +bfd_elf_remember_eh_frame_entry (struct eh_frame_hdr_info
> *hdr_info,
> > +				 asection *sec)
> > +{
> > +  if (hdr_info->array_count == hdr_info->allocated_entries)
> > +    {
> > +      if (hdr_info->allocated_entries == 0)
> > +	{
> > +	  hdr_info->allocated_entries = 2;
> > +	  hdr_info->entries = bfd_malloc (hdr_info->allocated_entries
> > +					  * sizeof(hdr_info->entries[0]));
> > +	}
> > +      else
> > +	{
> > +	  hdr_info->allocated_entries *= 2;
> > +	  hdr_info->entries = bfd_realloc (hdr_info->entries,
> > +	    hdr_info->allocated_entries * sizeof(hdr_info->entries[0]));
> 
> Space before "sizeof" (both times).
> 
> > +	}
> > +
> > +      BFD_ASSERT (hdr_info->entries);
> > +    }
> > +
> > +  hdr_info->entries[hdr_info->array_count++] = sec; }
> > +
> > +/* Parse a .eh_frame_entry section.  Figure out which text section it
> > +   references.  */
> > +
> > +void
> > +_bfd_elf_parse_eh_frame_entry (bfd *abfd, struct bfd_link_info *info,
> > +			       asection *sec, struct elf_reloc_cookie *cookie,
> > +			       bfd_boolean remember)
> 
> This does more than the comment says and the name implies; the
> REMEMBER stuff isn't mentioned.
> 
> The patch tries to do the parsing during bfd_elf_discard_info, but since the
> parsing wants to be able to fail with an error, I think we need to do it in an
> earlier pass.  We can then return a bfd_boolean success code and propagate
> error returns up, which the current patch doesn't do.
> Ideally we'd put the pass somewhere before GC, so that both the GC and
> bfd_elf_discard_info stages can assume parsed .eh_frame_entry sections.
> 
> Having bfd_elf_discard_info add info (as per REMEMBER == TRUE) seems a
> bit counterintuitive.  I think the earlier pass should record all
> .eh_frame_entry sections and then the code currently in
> _bfd_elf_end_eh_frame_parsing (but see below) should remove unwanted
> entries from the eh_frame_hdr_info array.
> 
> > +  if (r_symndx >= cookie->locsymcount
> > +      || ELF_ST_BIND (cookie->locsyms[r_symndx].st_info) != STB_LOCAL)
> > +    {
> > +      h = cookie->sym_hashes[r_symndx - cookie->extsymoff];
> > +      while (h->root.type == bfd_link_hash_indirect
> > +             || h->root.type == bfd_link_hash_warning)
> > +        h = (struct elf_link_hash_entry *) h->root.u.i.link;
> > +
> > +      if (h->root.type != bfd_link_hash_defined
> > +          && h->root.type != bfd_link_hash_defweak)
> > +	goto fail;
> > +
> > +      text_sec = h->root.u.def.section;
> > +    }
> > +  else
> > +    {
> > +      Elf_Internal_Sym *isym;
> > +
> > +      /* Need to: get the symbol; get the section.  */
> > +      isym = &cookie->locsyms[r_symndx];
> > +      text_sec = bfd_section_from_elf_index (abfd, isym->st_shndx);
> > +    }
> 
> Looks like this was lifted from elflink.c.  Please separate it out into a
> subroutine that both sites can use.  E.g.:
> 
> asection *
> _bfd_elf_section_for_symbol (struct elf_reloc_cookie *cookie,
> 			     unsigned long r_symndx)
> {
> ...
> }
> 
> returning null if not known.
> 
> > +fail:
> > +  (*_bfd_error_handler) (_("%B: failed to precoess .eh_frame_entry"),
> > +sec->owner);
> 
> Long line.
> 
> > +/* Add space for a CANTUNWIND terminator to SEC is the text sections
> > +   referenced by it and NEXT are not contiguous, or NEXT is NULL.  */
> 
> s/is the text/if the text/.
> 
> > +
> > +static void
> > +add_eh_frame_hdr_terminator (asection *sec,
> > +			     asection *next)
> 
> No need for a line break in the arguments.
> 
> > +{
> > +  bfd_vma end;
> > +  bfd_vma next_start;
> > +  asection *text_sec;
> > +
> > +  if (next)
> > +    {
> > +      /* See if there is a gap (presuably a text section without unwind info)
> > +	 between these two entries.  */
> > +      text_sec = (asection *) elf_section_data (sec)->sec_info;
> > +      end = text_sec->output_section->vma + text_sec->output_offset
> > +	    + text_sec->size;
> > +      text_sec = (asection *) elf_section_data (next)->sec_info;
> > +      next_start = text_sec->output_section->vma + text_sec-
> >output_offset;
> > +      /* Allow for alignment padding.  */
> > +      end = BFD_ALIGN (end, 1 << bfd_get_section_alignment (text_sec-
> >owner, text_sec));
> > +      if (end == next_start)
> > +	return;
> > +    }
> > +
> > +  /* Add space for a CANTUNWIND terminator.  */  if (!sec->rawsize)
> > +    sec->rawsize = sec->size;
> > +
> > +  bfd_set_section_size (sec->owner, sec, sec->size + 8); }
> 
> Is the idea to detect when there's "real" text (as opposed to padding)
> between the two text sections?  If so, I don't think aligning the end of SEC's
> text section according to NEXT's is correct.  If NEXT's text section has a
> particularly high alignment then you could have "end == next_start" even if
> there are other text sections inbetween.
> 
> Maybe in the first cut we should just be conservative and compare the start
> and end directly, even if that leads to unnecessary CANTUNWINDs for
> padding.
> 
> > +/* Finish a pass over all .eh_frame and eh_frame_entry sections.  */
> > +
> > +bfd_boolean
> >  _bfd_elf_end_eh_frame_parsing (struct bfd_link_info *info)  {
> >    struct eh_frame_hdr_info *hdr_info;
> > +  unsigned int i;
> >
> >    hdr_info = &elf_hash_table (info)->eh_info;
> >    hdr_info->parsed_eh_frames = TRUE;
> > +
> > +  if (hdr_info->array_count == 0 || info->eh_frame_hdr < 2)
> > +    return FALSE;
> > +
> > +  qsort (hdr_info->entries, hdr_info->array_count,
> > +	 sizeof (asection *), cmp_eh_frame_hdr);
> > +
> > +  for (i = 0; i < hdr_info->array_count - 1; i++)
> > +    {
> > +      add_eh_frame_hdr_terminator (hdr_info->entries[i],
> > +				   hdr_info->entries[i + 1]);
> > +    }
> > +
> > +  /* Add a CANTUNWIND terminator after the last entry.  */
> > + add_eh_frame_hdr_terminator (hdr_info->entries[i], NULL);  return
> > + TRUE;
> 
> This routine is called from both bfd_elf_gc_sections and
> bfd_elf_discard_info but I think you only want it for bfd_elf_discard_info.
> So perhaps this should be a separate function.
> 
> I wonder whether we could instead insert CANTUNWINDs earlier (say in the
> non-dynamic part of size_dynamic_sections) based on the link order.
> I.e. rather than comparing the start and end addresses of two sections, we
> could just walk the sections in the order that they're going to be linked.
> 
> It sounds like doing it that way would be more direct and more efficient.
> Returning TRUE here forces the linker to map sections twice.
> 
> > @@ -1271,6 +1465,45 @@ _bfd_elf_eh_frame_present (struct
> bfd_link_info *info)
> >    return FALSE;
> >  }
> >
> > +/* Return true if there is at least one .eh_frame_entry section in
> > +   input files.  */
> > +bfd_boolean
> > +_bfd_elf_eh_frame_entry_present (struct bfd_link_info *info) {
> > +  asection *o;
> > +  bfd *abfd;
> > +
> > +  for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link_next)
> > +    {
> > +      for (o = abfd->sections; o; o = o->next)
> > +	{
> > +	  const char *name = bfd_get_section_name (abfd, o);
> > +
> > +	  if (strcmp (name, ".eh_frame_entry")
> > +	      && !bfd_is_abs_section (o->output_section))
> > +	    {
> > +	      if (strcmp (o->output_section->name, ".eh_frame_hdr"))
> > +		return TRUE;
> > +	      else
> > +		{
> > +		  (*_bfd_error_handler)
> > +			(_("error: an '.eh_frame_entry'"
> > +			   " input section that is not mapped to the"
> > +			   " '.eh_frame_hdr' output section was seen"));
> > +		  (*_bfd_error_handler)
> > +			(_("note: try adding '*(.eh_frame_entry"
> > +			   " .eh_frame_entry.*)' to the '.eh_frame_hdr'"
> > +			   " output section description in the linker"
> > +			   " script"));
> > +		  bfd_set_error (bfd_error_invalid_operation);
> > +		  return FALSE;
> > +		}
> > +	    }
> > +	}
> > +    }
> > +  return FALSE;
> 
> The caller can't tell "FALSE because an error was reported" from "FALSE
> because there was no .eh_frame_entry".  We should separate out the two
> cases and propagate error returns up.
> 
> > @@ -1387,6 +1636,71 @@ _bfd_elf_eh_frame_section_offset (bfd
> *output_bfd ATTRIBUTE_UNUSED,
> >  	  + extra_augmentation_data_bytes (sec_info->entry + mid));  }
> >
> > +/* Write out .eh_frame_entry section.  Add CANTUNWIND terminator if
> needed.
> > +   Also check that the contents look sane.  */
> > +
> > +bfd_boolean
> > +_bfd_elf_write_section_eh_frame_entry (bfd *abfd,
> > +				       asection *sec,
> > +				       bfd_byte *contents)
> 
> Formatting: first two arguments fit on a line.
> 
> > +  text_sec = (asection *) elf_section_data (sec)->sec_info;
> > +  addr = text_sec->output_section->vma + text_sec->output_offset
> > +	 + text_sec->size;
> > +  addr &= ~1;
> > +  addr -= (sec->output_section->vma + sec->output_offset +
> > +sec->rawsize);
> > +  BFD_ASSERT ((addr & 1) == 0);
> 
> It looks like this could trigger for odd-sized input text sections.
> I think it should be an error instead of an assert.
> 
> > +  if (last_addr >= addr + sec->rawsize)
> > +    {
> > +      (*_bfd_error_handler) (_("%B: %s points past end of text section"),
> > +			     sec->owner, sec->name);
> > +      return FALSE;
> > +    }
> > +
> > +  if (sec->size == sec->rawsize)
> > +    return TRUE;
> > +
> > +  BFD_ASSERT (sec->size == sec->rawsize + 8);
> > +  BFD_ASSERT ((addr & 1) == 0);
> > +  bfd_put_32 (abfd, addr, cantunwind);
> > +  bfd_put_32 (abfd, 0x015d5d01, cantunwind + 4);
> > +  return bfd_set_section_contents (abfd, sec->output_section,
> cantunwind,
> > +				   sec->output_offset + sec->rawsize, 8);
> 
> ARM and c6x seem to use 0 rather than 0x5d as the "no unwind" opcode, is
> that right?  If so, I think this should be a hook.
> 
> The decision about whether to insert the CANTUNWIND is made during
> bfd_elf_discard_info, but addresses can change after that “thanks”
> to relaxation.  So this could in principle end up emitting a CANTUNWIND for
> the same address as the following text section.
> 
> > @@ -1746,6 +2060,44 @@ vma_compare (const void *a, const void *b)
> >    return 0;
> >  }
> >
> > +/* Reorder .eh_frame_entry sections to match the associated text
> > +sections.  */
> 
> I think a bit more commentary would help here.  E.g.:
> 
> /* Reorder .eh_frame_entry sections to match the associated text sections.
>    This routine is called during the final linking step, just before writing
>    the contents.  At this stage sections in the eh_frame_hdr_info are already
>    sorted in order of increasing text section address and so we simply need
>    to make the .eh_frame_entrys themselves follow that same order.  Note
> that
>    it is invalid for a linker script to try to force a particular order of
>    .eh_frame_entry sections.  */
> 
> However, even so:
> 
> > +
> > +void
> > +_bfd_elf_fixup_eh_frame_hdr (struct bfd_link_info *info) {
> > +  asection *sec = NULL;
> > +  struct eh_frame_hdr_info *hdr_info;
> > +  unsigned int i;
> > +  bfd_vma offset;
> > +  struct bfd_link_order *p;
> > +
> > +  hdr_info = &elf_hash_table (info)->eh_info;
> > +
> > +  if (hdr_info->hdr_sec == NULL || info->eh_frame_hdr < 2
> > +      || hdr_info->array_count == 0)
> > +    return;
> > +
> > +  /* Change section output offsets to be in text section order.  */
> > + offset = 8;  for (i = 0; i < hdr_info->array_count; i++)
> > +    {
> > +      sec = hdr_info->entries[i];
> > +      sec->output_offset = offset;
> > +      offset += sec->size;
> > +    }
> 
> ...this assumes without checking that all .eh_frame_entry sections have the
> same output section, and that there's nothing in that output section besides
> these sections.  We ought to check for that.
> (_bfd_elf_eh_frame_entry_present does some sanity checking, but AFAICT
> only for one input section.)
> 
> We could either do the checking earlier or do it here, e.g. something like:
> 
>   /* Change section output offsets to be in text section order.  */
>   offset = 8;
>   osec = hdr_info->entries[0]->output_section;
>   for (i = 0; i < hdr_info->array_count; i++)
>     {
>       sec = hdr_info->entries[i];
>       if (sec->output_section != osec)
>         ...error...
>       sec->output_offset = offset;
>       offset += sec->size;
>     }
> 
> and:
> 
> > +  /* Fix the link_order to match.  */
> > +  for (p = sec->output_section->map_head.link_order; p != NULL; p = p-
> >next)
> > +    {
> > +      if (p->type != bfd_indirect_link_order)
> > +	abort();
> > +
> > +      p->offset = p->u.indirect.section->output_offset;
> > +      i--;
> > +    }
> 
> if (i != 0)
>   ...error...
> 
> (with bfd_boolean return of course).
> 
> > @@ -1787,6 +2145,36 @@ _bfd_elf_write_section_eh_frame_hdr (bfd
> *abfd, struct bfd_link_info *info)
> >        bfd_size_type size;
> >        bfd_vma encoded_eh_frame;
> >
> > +      if (info->eh_frame_hdr == 2)
> > +	{
> > +	  const struct elf_backend_data *bed;
> > +	  bfd_vma count;
> > +
> > +	  contents = bfd_malloc (8);
> > +	  if (contents == NULL)
> > +	    return FALSE;
> > +
> > +	  if (sec->size != 8)
> > +	    abort ();
> > +
> > +	  memset (contents, 0, 8);
> > +	  contents[0] = 2;
> > +	  bed = get_elf_backend_data (abfd);
> > +	  if (bed->compact_eh_encoding)
> > +	    contents[1] = (*bed->compact_eh_encoding) (info);
> > +	  else
> > +	    contents[0] = 0xff;
> 
> Can the 0xff case actually happen for MIPS?  If not, I think we should assert
> or report an error instead.  It seems wrong to generate a header but silently
> mark it invalid.
> 
> > +
> > +	  count = (sec->output_section->size -9 ) / 8;
> 
> Why -9?  Deserves a comment at least.  The space should be before the 9.
> 
> > +	  bfd_put_32 (abfd, count, contents + 4);
> > +	  retval = bfd_set_section_contents (abfd, sec->output_section,
> > +					     contents,
> > +					     (file_ptr) sec->output_offset,
> > +					     sec->size);
> > +	  free (contents);
> 
> malloc/free seems a bit excessive for 8 bytes.  We might as well just use a
> stack array instead.
> 
> > @@ -10039,6 +10040,10 @@ elf_link_input_bfd (struct elf_final_link_info
> *flinfo, bfd *input_bfd)
> >  	      return FALSE;
> >  	  }
> >  	  break;
> > +	case SEC_INFO_TYPE_EH_FRAME_ENTRY:
> > +	    if (! _bfd_elf_write_section_eh_frame_entry (output_bfd, o,
> contents))
> > +	      return FALSE;
> > +	  break;
> 
> Excess indentation of the "if".
> 
> > @@ -11807,6 +11814,13 @@ _bfd_elf_gc_mark (struct bfd_link_info *info,
> >  	}
> >      }
> >
> > +  eh_frame = elf_section_eh_frame_entry (sec);  if (eh_frame &&
> > + !eh_frame->gc_mark)
> > +    {
> > +    if (!_bfd_elf_gc_mark (info, eh_frame, gc_mark_hook))
> > +      return FALSE;
> > +    }
> 
> In this context we should be using "ret":
> 
>   eh_frame = elf_section_eh_frame_entry (sec);
>   if (ret && eh_frame && !eh_frame->gc_mark)
>     ret = _bfd_elf_gc_mark (info, eh_frame, gc_mark_hook);
> 
> > @@ -12190,22 +12204,42 @@ bfd_elf_gc_sections (bfd *abfd, struct
> bfd_link_info *info)
> >    bed->gc_keep (info);
> >
> >    /* Try to parse each bfd's .eh_frame section.  Point
> elf_eh_frame_section
> > -     at the .eh_frame section if we can mark the FDEs individually.  */
> > +     at the .eh_frame section if we can mark the FDEs individually.
> > +     Establish links from text sections to their corresponding
> > +     .eh_frame_entry sections.  */
> >    _bfd_elf_begin_eh_frame_parsing (info);
> >    for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
> >      {
> >        asection *sec;
> >        struct elf_reloc_cookie cookie;
> >
> > -      sec = bfd_get_section_by_name (sub, ".eh_frame");
> > -      while (sec && init_reloc_cookie_for_section (&cookie, info, sec))
> > +      if (!init_reloc_cookie (&cookie, info, sub))
> > +	return FALSE;
> > +
> > +      if (info->eh_frame_hdr < 2)
> >  	{
> > -	  _bfd_elf_parse_eh_frame (sub, info, sec, &cookie);
> > -	  if (elf_section_data (sec)->sec_info
> > -	      && (sec->flags & SEC_LINKER_CREATED) == 0)
> > -	    elf_eh_frame_section (sub) = sec;
> > -	  fini_reloc_cookie_for_section (&cookie, sec);
> > -	  sec = bfd_get_next_section_by_name (sec);
> > +	  sec = bfd_get_section_by_name (sub, ".eh_frame");
> > +	  if (sec && init_reloc_cookie_rels (&cookie, info, sub, sec))
> > +	    {
> > +	      _bfd_elf_parse_eh_frame (sub, info, sec, &cookie);
> > +	      if (elf_section_data (sec)->sec_info
> > +		  && (sec->flags & SEC_LINKER_CREATED) == 0)
> > +		elf_eh_frame_section (sub) = sec;
> > +	      fini_reloc_cookie_for_section (&cookie, sec);
> > +	    }
> > +	}
> > +      else
> > +	{
> > +	  for (sec = sub->sections; sec; sec = sec->next)
> > +	    {
> > +	      if (CONST_STRNEQ (bfd_section_name (sub, sec),
> ".eh_frame_entry")
> > +		  && init_reloc_cookie_rels (&cookie, info, sub, sec))
> > +		{
> > +		  _bfd_elf_parse_eh_frame_entry (sub, info, sec, &cookie,
> > +						 FALSE);
> > +		  fini_reloc_cookie_for_section (&cookie, sec);
> > +		}
> > +	    }
> 
> This changes the info->eh_frame_hdr != 2 case to only handle the first
> .eh_frame section.  ("while" -> "if").
> 
> Also, the init/fini_reloc_cookie* calls don't match up.  I assume the idea is to
> avoid excessive allocation and freeing of the locsyms, but in that case you
> should use fini_reloc_cookie_rels instead of fini_reloc_cookie_for_section
> and call fini_reloc_cookie at the end.
> At the moment I think this leaks memory if there are no EH sections.
> 
> I don't know either way whether splitting the init_reloc_cookie_for_section
> call up is a win or not for .eh_frame.  It will be a win if there are multiple EH
> sections but a loss if there are none, since we then initialise and free the bfd-
> level information unnecessarily.  So I think it might make sense to keep the
> .eh_frame code as it is now and restrict the *_rels to the new code.
> 
> > @@ -12681,7 +12717,7 @@ bfd_elf_discard_info (bfd *output_bfd, struct
> bfd_link_info *info)
> >        bed = get_elf_backend_data (abfd);
> >
> >        eh = NULL;
> > -      if (!info->relocatable)
> > +      if ((info->eh_frame_hdr < 2) && !info->relocatable)
> 
> Formatting: no brackets around "info->eh_frame_hdr < 2".
> 
> > @@ -5113,6 +5117,17 @@ mips_elf_relocation_needs_la25_stub (bfd
> *input_bfd, int r_type,
> >        return FALSE;
> >      }
> >  }
> > +
> > +/* On some targets R_MIPS_EH is interpreted as R_MIPS_PC32.  */
> > +static int mips_elf_real_reloc (struct bfd_link_info *info, int
> > +r_type) {
> > +  struct mips_elf_link_hash_table *htab = mips_elf_hash_table (info);
> > +  if (r_type == R_MIPS_EH)
> > +    return htab->eh_reloc_type;
> > +
> > +  return r_type;
> > +}
> 
> This seems a bit error-prone, since if a mips_elf_real_reloc call ("what am I
> really looking at?") gets missed at some point it will still work in the common
> case of R_MIPS_EH being unchanged.  If we do add the
> R_MIPS_GOT_DISP32 suggested above then that problem would go away,
> since R_MIPS_EH would always be replaced by something else.
> 
> > @@ -1648,6 +1649,7 @@ static const pseudo_typeS mips_pseudo_table[]
> =
> >    {"tprelword", s_tprelword, 0},
> >    {"tpreldword", s_tpreldword, 0},
> >    {"gpvalue", s_gpvalue, 0},
> > +  {"forcegpword", s_force_gpword, 0},
> >    {"gpword", s_gpword, 0},
> >    {"gpdword", s_gpdword, 0},
> >    {"ehword", s_ehword, 0},
> 
> Would prefer s_forcegpword, for consistency.
> 
> > @@ -18249,3 +18256,13 @@ tc_mips_regname_to_dw2regnum (char
> *regname)
> >
> >    return regnum;
> >  }
> > +
> > +#if defined (OBJ_ELF)
> > +bfd_reloc_code_real_type
> > +mips_cfi_reloc_for_encoding (int encoding) {
> > +  if ((encoding & 0x70) == DW_EH_PE_datarel)
> > +    return BFD_RELOC_GPREL32;
> > +  return BFD_RELOC_32_PCREL;
> > +}
> > +#endif
> 
> Given the answer to what "special" means above, I think we should combine
> tc_cfi_special_encoding and tc_cfi_reloc_for_encoding into a single hook
> that returns BFD_RELOC_NONE for an encoding that isn't special.
> 
> As mentioned above, I don't think R_MIPS_GPREL32 is right for the datarel-
> indirect case.
> 
> > diff --git a/gas/config/tc-mips.h b/gas/config/tc-mips.h index
> > c7eaa04..a300062 100644
> > --- a/gas/config/tc-mips.h
> > +++ b/gas/config/tc-mips.h
> > @@ -177,7 +177,9 @@ extern enum dwarf2_format mips_dwarf2_format
> > (asection *);
> >
> >  extern int mips_dwarf2_addr_size (void);  #define
> > DWARF2_ADDR_SIZE(bfd) mips_dwarf2_addr_size () -#define
> > DWARF2_FDE_RELOC_SIZE mips_dwarf2_addr_size ()
> > +#define DWARF2_FDE_RELOC_SIZE (compact_eh ? 4 :
> mips_dwarf2_addr_size
> > +()) #define DWARF2_FDE_RELOC_ENCODING(enc) \
> > +  (enc | (compact_eh ? DW_EH_PE_pcrel : 0))
> 
> What case is this handling?  Please explain this a bit more.
> 
> > @@ -4566,6 +4586,22 @@ argument is not present, otherwise second
> > argument should be a constant  or a symbol name.  The default after
> > @code{.cfi_startproc} is @code{.cfi_lsda 0xff},  no LSDA.
> >
> > +@section @code{.cfi_inline_lsda} [@var{align}]
> > +@code{.cfi_inline_lsda} marks the start of a LSDA data section and
> > +switches to the corresponding @code{.gnu.extab} section.
> > +Must be preceded by a CFI block containing a @code{.cfi_lsda} directive.
> > +Only valid when generating compact EH frames (i.e.
> > +with @code{.cfi_sections eh_frame_entry}.
> 
> Missing ")".
> 
> > @@ -4643,6 +4679,20 @@ mark a code segment that has only one return
> > address which is reached  by a direct branch and no copy of the return
> > address exists in memory  or another register.
> >
> > +@section @code{.cfi_epilogue_begin}
> > +A pseudo-operation which marks the beginning of the epilogue in a
> > +given function.  This is currently used by the compact unwind-table
> > +implementation (for exception handling), which assumes a single
> > +register state for unwinding throughout the body of a function.  The
> > +function is scanned, and CFI directives are rewritten in a compact
> > +form.  However, recent versions of GCC emit unwind information for
> > +function epilogues, which should not be represented in this compact
> > +form: hence, any CFI directives which occur in a function after
> > +@code{.cfi_epilogue_end} are not processed.
> > +
> > +This operation only affects the internals of the assembler, and is
> > +not represented in the binary output.
> 
> Don't really follow this, sorry.  Can you give an example, or better yet, a
> testcase?
> 
> > @@ -161,6 +278,9 @@ alloc_debugseg_item (segT seg, int subseg, char
> > *name)  static segT  is_now_linkonce_segment (void)  {
> > +  if (compact_eh)
> > +    return now_seg;
> > +
> 
> Please add a comment explaining why this is correct.
> 
> > -/* Emit a single byte into the current segment.  */
> > -
> > -static inline void
> > -out_one (int byte)
> > +static segT
> > +get_cfi_seg (segT cseg, const char *base, flagword flags, int align)
> >  {
> > -  FRAG_APPEND_1_CHAR (byte);
> > +  if (SUPPORT_FRAME_LINKONCE || ((flags & SEC_DEBUGGING) == 0 &&
> compact_eh))
> > +    {
> 
> Please add a comment here too to explain the SEC_DEBUGGING test.
> 
> > +/* Set the personality id in the FDE structure.  */
> > +
> > +static void
> > +dot_cfi_personality_id (int ignored ATTRIBUTE_UNUSED)
> >  {
> > -  md_number_to_chars (frag_more (2), data, 2);
> > +  struct fde_entry *fde;
> > +
> > +  if (frchain_now->frch_cfi_data == NULL)
> > +    {
> > +      as_bad (_("CFI instruction used without previous .cfi_startproc"));
> > +      ignore_rest_of_line ();
> > +      return;
> > +    }
> > +
> > +  fde = frchain_now->frch_cfi_data->cur_fde_data;
> > +  fde->personality_id = cfi_parse_const ();
> > + demand_empty_rest_of_line ();
> > +
> > +  if (fde->personality_id == 0 || fde->personality_id > 3)
> > +    {
> > +      as_bad (_("wrong argument to .cfi_personality_id"));
> > +      return;
> > +    }
> 
> No need for { ... return; }
> 
> > +static void
> > +output_compact_unwind_data (struct fde_entry *fde, int align)
> 
> Probably worth a comment here, since it wasn't obvious to me the "align" is
> the alignment of the end of the data rather than the start.
> 
> >  {
> > -  md_number_to_chars (frag_more (4), data, 4);
> > +  int data_size = fde->eh_data_size + 2;
> 
> Add a comment for the "2" (the personality encoding and the stop byte,
> right?)
> 
> > +  int align_padding;
> > +  int amask;
> > +  char *p;
> > +
> > +  fde->eh_loc = symbol_temp_new_now ();
> > +
> > +  p = frag_more (1);
> > +  if (fde->personality_id != 0)
> > +    *p = fde->personality_id;
> > +  else if (fde->per_encoding != DW_EH_PE_omit)
> > +    {
> > +      *p = 0;
> > +      emit_expr_encoded (&fde->personality, fde->per_encoding, FALSE);
> > +      data_size += encoding_size (fde->per_encoding);
> > +    }
> > +  else
> > +    *p = 1;
> > +
> > +  amask = (1 << align) - 1;
> > +  align_padding = ((data_size + amask) & ~amask) - data_size;
> 
> Simpler as:
> 
>   align_padding = -data_size & amask.
> 
> > +  align = get_absolute_expression ();  if (align > max_alignment)
> > +    {
> > +      align = max_alignment;
> > +      as_bad (_("Alignment too large: %d. assumed."), align);
> 
> No "." after %d.
> 
> > +  /* Open .gnu_extab section.  */
> > +  cfi_seg = get_cfi_seg (ccseg, ".gnu_extab",
> > +			 (SEC_ALLOC | SEC_LOAD | SEC_DATA
> > +			  | DWARF2_EH_FRAME_READ_ONLY),
> > +			 1);
> > +#ifdef md_fix_up_eh_frame
> > +  md_fix_up_eh_frame (cfi_seg);
> > +#else
> > +  (void) cfi_seg;
> > +#endif
> 
> md_fix_up_eh_frame doesn't seem appropriate for .gnu_extab.
> 
> > @@ -1576,29 +1889,43 @@ output_fde (struct fde_entry *fde, struct
> cie_entry *cie,
> >        TC_DWARF2_EMIT_OFFSET (cie->start_address, offset_size);
> >      }
> >
> > +  exp.X_op = O_symbol;
> > +  exp.X_add_symbol = fde->start_address;
> >    if (eh_frame)
> >      {
> > -      exp.X_op = O_subtract;
> > -      exp.X_add_number = 0;
> > +      if (tc_cfi_special_encoding (cie->fde_encoding))
> > +	{
> > +	  bfd_reloc_code_real_type code
> > +	    = tc_cfi_reloc_for_encoding (cie->fde_encoding);
> > +	  reloc_howto_type *howto = bfd_reloc_type_lookup (stdoutput,
> code);
> > +	  char *p = frag_more(4);
> 
> Space before "(".
> 
> > +	  md_number_to_chars (p, 0, 4);
> > +	  fix_new (frag_now, p - frag_now->fr_literal, 4, exp.X_add_symbol,
> > +		   exp.X_add_number, howto->pc_relative, code);
> 
> What's the significance of exp.X_add_number here?  If it was supposed to
> be initialised to 0 above then I think it would be easier to leave the "exp"
> stuff alone and just use fde->start_address and 0 directly in this fix_new call.
> Certainly...
> 
> >    else
> >      {
> > -      exp.X_op = O_symbol;
> > -      exp.X_add_symbol = fde->start_address;
> >        exp.X_add_number = 0;
> >        addr_size = DWARF2_ADDR_SIZE (stdoutput);
> >        emit_expr (&exp, addr_size);
> 
> ...separating the add_symbol and add_number feels odd.
> 
> > @@ -1614,24 +1941,7 @@ output_fde (struct fde_entry *fde, struct
> cie_entry *cie,
> >    if (eh_frame)
> >      out_uleb128 (augmentation_size);		/* Augmentation size.  */
> >
> > -  if (fde->lsda_encoding != DW_EH_PE_omit)
> > -    {
> > -      exp = fde->lsda;
> > -      if ((fde->lsda_encoding & 0x70) == DW_EH_PE_pcrel)
> > -	{
> > -#if CFI_DIFF_LSDA_OK
> > -	  exp.X_op = O_subtract;
> > -	  exp.X_op_symbol = symbol_temp_new_now ();
> > -	  emit_expr (&exp, augmentation_size);
> > -#elif defined (tc_cfi_emit_pcrel_expr)
> > -	  tc_cfi_emit_pcrel_expr (&exp, augmentation_size);
> > -#else
> > -	  abort ();
> > -#endif
> > -	}
> > -      else
> > -	emit_expr (&exp, augmentation_size);
> > -    }
> > +  emit_expr_encoded (&fde->lsda, cie->lsda_encoding, FALSE);
> 
> This emit_expr_encoded stuff was a nice clean-up, thanks.
> 
> > +  if (fde->eh_header_type == EH_COMPACT_INLINE)
> > +    {
> > +      p = frag_more (4);
> > +      /* Inline entries always use PR1.  */
> > +      *(p++) = 1;
> > +      memcpy(p, fde->eh_data, 3);
> 
> Space before "(".
> 
> > @@ -1888,7 +2223,17 @@ cfi_finish (void)
> >
> >  	  for (fde = all_fde_data; fde ; fde = fde->next)
> >  	    {
> > -	      if (SUPPORT_FRAME_LINKONCE)
> > +	      if ((fde->sections & CFI_EMIT_eh_frame) == 0)
> > +		continue;
> > +
> > +#if SUPPORT_COMPACT_EH
> > +	      if (fde->eh_header_type == EH_COMPACT_HAS_LSDA)
> > +		fde->eh_header_type = EH_COMPACT_LEGACY;
> > +
> > +	      if (fde->eh_header_type != EH_COMPACT_LEGACY)
> > +		continue;
> > +#endif
> 
> Please add a comment explaining this.
> 
> > +#if SUPPORT_COMPACT_EH
> > +      if (compact_eh)
> > +	{
> > +	  /* Create remaining out of line table entries.  */
> > +	  do
> > +	    {
> > +	      ccseg = NULL;
> > +	      seek_next_seg = 0;
> > +
> > +	      for (fde = all_fde_data; fde ; fde = fde->next)
> > +		{
> > +		  if ((fde->sections & CFI_EMIT_eh_frame) == 0)
> > +		    continue;
> > +
> > +		  if (fde->eh_header_type != EH_COMPACT_OUTLINE)
> > +		    continue;
> > +		  if (HANDLED (fde))
> > +		    continue;
> > +		  if (seek_next_seg && CUR_SEG (fde) != ccseg)
> > +		    {
> > +		      seek_next_seg = 2;
> > +		      continue;
> > +		    }
> > +		  if (!seek_next_seg)
> > +		    {
> > +		      ccseg = CUR_SEG (fde);
> > +		      /* Open .gnu_extab section.  */
> > +		      cfi_seg = get_cfi_seg (ccseg, ".gnu_extab",
> > +					     (SEC_ALLOC | SEC_LOAD |
> SEC_DATA
> > +					      |
> DWARF2_EH_FRAME_READ_ONLY),
> > +					     1);
> > +#ifdef md_fix_up_eh_frame
> > +		      md_fix_up_eh_frame (cfi_seg); #else
> > +		      (void) cfi_seg;
> > +#endif
> 
> As above, I don't think the md_fix_up_eh_frame is appropriate.
> 
> > +		      seek_next_seg = 1;
> > +		    }
> > +		  SET_HANDLED (fde, 1);
> > +
> > +		  frag_align (1, 0, 0);
> > +		  record_alignment (now_seg, 1);
> > +		  output_compact_unwind_data (fde, 1);
> > +		}
> > +	    }
> > +	  while (EH_FRAME_LINKONCE && seek_next_seg == 2);
> 
> The quadraticness seems a bit unfortunate, but I realise you're just following
> the existing structure.
> 
> > @@ -1958,6 +2394,9 @@ cfi_finish (void)
> >
> >  	  for (fde = all_fde_data; fde ; fde = fde->next)
> >  	    {
> > +	      if ((fde->sections & CFI_EMIT_debug_frame) == 0)
> > +		continue;
> > +
> >  	      if (SUPPORT_FRAME_LINKONCE)
> >  		{
> >  		  if (HANDLED (fde))
> 
> Please add a comment explaining why this is needed/correct.
> 
> Thanks,
> Richard

[-- Attachment #2: compact-eh-2014-11-17.cl --]
[-- Type: application/octet-stream, Size: 7576 bytes --]

2014-11-17  Catherine Moore  <clm@codesourcery.com>
	    Bernd Schmidt <bernds@codesourcery.com>
	    Paul Brook <paul@codesourcery.com>

	bfd/
	* aout-adobe.c (aout_32_bfd_parse_eh_frame_entries): Define.
	* aout-target.h (MY_bfd_parse_eh_frame_entries): Define.
	* aout-tic30.c: Likewise.
	* bfd-in2.h: Regenerate.
	* bfd.c (bfd_parse_eh_frame_entries): Define.
	* binary.c (binary_bfd_parse_eh_frame_entries): Define.
	* bout.c (b_out_bfd_parse_eh_frame_entries): Define.
	* coff-alpha.c (_bfd_ecoff_bfd_parse_eh_frame_entries): Define.
	* coff-mips.c (_bfd_ecoff_bfd_parse_eh_frame_entries): Define.
	* coff-rs6000.c (_bfd_xcoff_bfd_parse_eh_frame_entries): Define.
	* coff64-rs6000.c (rs6000_xcoff64_vec): Add bfd_generic_parse_eh_frame_entries.
	(rs6000_xcoff64_aix_vec): Likewise.
	* coffcode.h (define coff_bfd_parse_eh_frame_entries): Define.
	* elf-bfd.h (DWARF2_EH_HDR, COMPACT_EH_HDR): Define.
	(dwarf_eh_frame_hdr_info): Declare.
	(compact_eh_frame_hdr_info): Declare.
	(eh_frame_hdr_info): Add union.
	(elf_backend_data): Add compact_eh_encoding.
	(bfd_elf_section_data): Add eh_frame_entry.
	(elf_section_eh_frame_entry): Define.
	(_bfd_elf_end_eh_frame_parsing): Declare.
	(bfd_elf_parse_eh_frame_entries): Declare.
	(_bfd_elf_parse_eh_frame_entry): Declare.
	(_bfd_elf_write_section_eh_frame_entry): Declare.
	(_bfd_elf_eh_frame_entry_present): Declare.
	(_bfd_elf_fixup_eh_frame_hdr): Declare.
	(_bfd_elf_section_for_symbol): Declare.
	* elf-eh-frame.c (bfd_elf_discard_eh_frame_entry): New function.
	(bfd_elf_record_eh_frame_entry): New function.
	(_bfd_elf_parse_eh_frame_entry): New function.
	(cmp_eh_frame_hdr): New function.
	(add_eh_frame_hdr_terminator): New function.
	(_bfd_elf_end_eh_frame_parsing): New function.
	(_bfd_elf_eh_frame_entry_present): New function.
	(_bfd_elf_maybe_strip_eh_frame_hdr): Add Compact EH support.
	(_bfd_elf_write_section_eh_frame_entry): New function.
	(_bfd_elf_write_section_eh_frame): Add Compact EH support.
	(_bfd_elf_fixup_eh_frame_hdr): New function.
	(write_compact_eh_frame_hdr): New function.
	(write_dwarf_eh_frame_hdr): New function.
	(_bfd_elf_write_section_eh_frame_hdr): Add Compact EH support.
	* elflink.c (_bfd_section_for_symbol): New function.
	(elf_section_ignore_discarded_relocs): Add Compact EH support.
	(bfd_elf_final_link): Likewise.
	(_bfd_elf_gc_mark): Likewise.
	(bfd_elf_parse_eh_frame_entries): New function.
	(bfd_elf_gc_sections): Add Compact EH support.
	(bfd_elf_discard_info): Likewise.
	* elfxx-mips.c (_bfd_mips_elf_compact_eh_encoding): New function.
	* elfxx-mips.h (_bfd_mips_elf_compact_eh_encoding): Declare.
	(elf_backend_compact_eh_encoding): Define.
	* elfxx-target.h (bfd_elfNN_bfd_parse_eh_frame_entries): Define.
	(elf_backend_compact_eh_encoding): Define.
	(elfNN_bed): Add elf_backend_compact_eh_encoding. 
	* i386msdos.c (msdos_bfd_parse_eh_frame_entries): Define.
	* i386os9k.c (os9k_bfd_parse_eh_frame_entries): Define.
	* ieee.c (ieee_bfd_parse_eh_frame_entries): Define.
	* ihex.c (ihex_bfd_parse_eh_frame_entries): Define.
	* libbfd-in.h: Regenerate.
	* libbfd.h (_bfd_nolink_bfd_parse_eh_frame_entries): Define.
	(bfd_boolean bfd_generic_parse_eh_frame_entries): Declare.
	* mach-o-target.c (bfd_mach_o_bfd_parse_eh_frame_entries): Define.
	* mmo.c (mmo_bfd_parse_eh_frame_entries): Define.
	* nlm.c (nlm_bfd_parse_eh_frame_entries): Define.
	* oasys.c (oasys_bfd_parse_eh_frame_entries): Define.
	* pef.c (bfd_pef_bfd_parse_eh_frame_entries): Define.
	* plugin.c (bfd_plugin_bfd_parse_eh_frame_entries): Define.
	* ppcboot.c (ppcboot_bfd_parse_eh_frame_entries): Define.
	* reloc.c (bfd_generic_parse_eh_frame_entries): New.
	* section.c (SEC_INFO_TYPE_EH_FRAME_ENTRY): Define.
	* som.c (som_bfd_parse_eh_frame_entries): Define.
	* srec.c (srec_bfd_parse_eh_frame_entries): Define.
	* targets.c (BFD_JUMP_TABLE): Add NAME##_bfd_parse_eh_frame_entries.
	* tekhex.c (tekhex_bfd_parse_eh_frame_entries): Define.
	* verilog.c (verilog_bfd_parse_eh_frame_entries): Define.
	* versadoc.c (versados_bfd_parse_eh_frame_entries): Define.
	* vms-alpha.c (alpha_vms_bfd_parse_eh_frame_entries): Define.
	* xsym.c (bfd_sym_bfd_parse_eh_frame_entries): Define.

	gas/
	* config/tc-mips.c (s_ehword): Remove.
	(mips_pseudo_table): Remove "ehword" entry.
	(mips_cfi_reloc_for_encoding): New.
	* config/tc-mips.h (DWARF2_FDE_RELOC_SIZE): Redefine.
	(DWARF2_FDE_RELOC_ENCODING): Define.
	(tc_cfi_reloc_for_encoding): Define.
	(mips_cfi_reloc_for_encoding): Declare.
	* tc_compact_eh_opcode_stop: Define.
	* tc_compact_eh_opcode_pad: Define.
	* doc/as.texinfo: New documentation for Compact EH extensions.
	* doc/internals.texi (tc_cfi_reloc_for_encoding): Document.
	* dw2gencfi.c (EH_FRAME_LINKONCE) Define.
	(tc_cfi_reloc_for_encoding): Provide default.
	(compact_eh): Declare.
	(eh_encoding_size): New.
	(emit_expr_encoded): New.
	(get_debugseg_name): Add Compact EH support.
	(is_now_linkonce_segment): Likewise.
	(cie_entry): Add field fde_encoding.
	(dot_cfi_fde_data): New.
	(dot_cfi_personality_id): New.
	(dot_cfi_inline_lsda): New.
	(cfi_pseudo_table): Add entries for "cfi_fde_data",
	"cfi_personality_id", and "cfi_inline_lsda". 
	(dot_cfi_personality): Add Compact EH support.
	(dot_cfi_lsda): Likewise.
	(CFI_EMIT_eh_frame_compact): Define.
	(cfi_sections_set, all_cfi_sections, last_fde): Declare.
	(dot_cfi_sections): Add Compact EH support.
	(dot_cfi_startproc): Likewise.
	(dot_cfi_endproc): Likewise.
	(get_cfi_seg): Likewise.
	(output_compact_unwind_data): New function.
	(output_cie, output_fde): Add Compact EH support.
	(cfi_emit_eh_header, output_eh_header): New function.
	(cfi_finish): Add Compact EH support.
	* dw2gencfi.h (SUPPORT_COMPACT_EH): Define.
	(EH_COMPACT_LEGACY, EH_COMPACT_INLINE, EH_COMPACT_OUTLINE,
	EH_COMPACT_OUTLINE_DONE, EH_COMPACT_HAS_LSDA): New enum.
	(fde_entry): Add Compact EH support.

	gas/testsuite/
	* gas/mips/compact-eh-1.s: New test.
	* gas/mips/compact-eh-2.s: New test.
	* gas/mips/compact-eh-3.s: New test.
	* gas/mips/compact-eh-4.s: New test.
	* gas/mips/compact-eh-5.s: New test.
	* gas/mips/compact-eh-6.s: New test.
	* gas/mips/compact-eh-7.s: New test.
	* gas/mips/compact-eh-eb-1.d: New dump file.
	* gas/mips/compact-eh-eb-2.d: New dump file.
	* gas/mips/compact-eh-eb-3.d: New dump file.
	* gas/mips/compact-eh-eb-4.d: New dump file.
	* gas/mips/compact-eh-eb-5.d: New dump file.
	* gas/mips/compact-eh-eb-6.d: New dump file.
	* gas/mips/compact-eh-eb-7.d: New dump file.
	* gas/mips/compact-eh-el-1.d: New dump file.
	* gas/mips/compact-eh-el-2.d: New dump file.
	* gas/mips/compact-eh-el-3.d: New dump file.
	* gas/mips/compact-eh-el-4.d: New dump file.
	* gas/mips/compact-eh-el-5.d: New dump file.
	* gas/mips/compact-eh-el-6.d: New dump file.
	* gas/mips/compact-eh-el-7.d: New dump file.
	* gas/mips/compact-eh-err1.s: New test.
	* gas/mips/compact-eh-err1.l: New listing file.
	* gas/mips/compact-eh-err2.s: New test.
	* gas/mips/compact-eh-err2.l: New listing file.
	* gas/mips/ehword.d: Delete.
	* gas/mips/ehword.s: Delete.
	* gas/mips/mips.exp: Run new tests. No longer run ehword test.

	include/
	* bfdlink.h (bfd_link_info): Rename eh_frame_hdr to eh_frame_hdr_type.

	ld/
	* emultempl/elf32.em (gld${EMULATION_NAME}_after_open): Add Compact EH support.
	* ldlang.c (lang_parse_eh_frame_entries): New.
	(lang_process): Call lang_parse_eh_frame_entries.
	* scripttempl/elf.sc: Process .eh_frame_entry and .gnu_extab sections.

[-- Attachment #3: compact-eh-2014-11-17.patch --]
[-- Type: application/octet-stream, Size: 131531 bytes --]

diff --git a/bfd/aout-adobe.c b/bfd/aout-adobe.c
index cfdcc64..72150e6 100644
--- a/bfd/aout-adobe.c
+++ b/bfd/aout-adobe.c
@@ -458,6 +458,7 @@ aout_adobe_sizeof_headers (bfd *ignore_abfd ATTRIBUTE_UNUSED,
 #define aout_32_get_section_contents_in_window      _bfd_generic_get_section_contents_in_window
 #define aout_32_bfd_relax_section                   bfd_generic_relax_section
 #define aout_32_bfd_gc_sections                     bfd_generic_gc_sections
+#define aout_32_bfd_parse_eh_frame_entries	    bfd_generic_parse_eh_frame_entries
 #define aout_32_bfd_lookup_section_flags	    bfd_generic_lookup_section_flags
 #define aout_32_bfd_merge_sections	            bfd_generic_merge_sections
 #define aout_32_bfd_is_group_section	            bfd_generic_is_group_section
diff --git a/bfd/aout-target.h b/bfd/aout-target.h
index 3bca8b5..3b66341 100644
--- a/bfd/aout-target.h
+++ b/bfd/aout-target.h
@@ -487,6 +487,9 @@ MY_bfd_final_link (bfd *abfd, struct bfd_link_info *info)
 #ifndef MY_bfd_gc_sections
 #define MY_bfd_gc_sections bfd_generic_gc_sections
 #endif
+#ifndef MY_bfd_parse_eh_frame_entries
+#define MY_bfd_parse_eh_frame_entries bfd_generic_parse_eh_frame_entries
+#endif
 #ifndef MY_bfd_lookup_section_flags
 #define MY_bfd_lookup_section_flags bfd_generic_lookup_section_flags
 #endif
diff --git a/bfd/aout-tic30.c b/bfd/aout-tic30.c
index e74464d..c697d0b 100644
--- a/bfd/aout-tic30.c
+++ b/bfd/aout-tic30.c
@@ -945,6 +945,9 @@ tic30_aout_set_arch_mach (bfd *abfd,
 #ifndef MY_bfd_gc_sections
 #define MY_bfd_gc_sections bfd_generic_gc_sections
 #endif
+#ifndef MY_bfd_parse_eh_frame_entries
+#define MY_bfd_parse_eh_frame_entries bfd_generic_parse_eh_frame_entries
+#endif
 #ifndef MY_bfd_lookup_section_flags
 #define MY_bfd_lookup_section_flags bfd_generic_lookup_section_flags
 #endif
diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 433b171..216f297 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -1446,6 +1446,7 @@ typedef struct bfd_section
 #define SEC_INFO_TYPE_EH_FRAME  3
 #define SEC_INFO_TYPE_JUST_SYMS 4
 #define SEC_INFO_TYPE_TARGET    5
+#define SEC_INFO_TYPE_EH_FRAME_ENTRY 6
 
   /* Nonzero if this section uses RELA relocations, rather than REL.  */
   unsigned int use_rela_p:1;
@@ -6691,6 +6692,9 @@ bfd_boolean bfd_set_private_flags (bfd *abfd, flagword flags);
 #define bfd_gc_sections(abfd, link_info) \
        BFD_SEND (abfd, _bfd_gc_sections, (abfd, link_info))
 
+#define bfd_parse_eh_frame_entries(abfd, link_info) \
+       BFD_SEND (abfd, _bfd_parse_eh_frame_entries, (abfd, link_info))
+
 #define bfd_lookup_section_flags(link_info, flag_info, section) \
        BFD_SEND (abfd, _bfd_lookup_section_flags, (link_info, flag_info, section))
 
@@ -7100,6 +7104,7 @@ typedef struct bfd_target
   NAME##_bfd_final_link, \
   NAME##_bfd_link_split_section, \
   NAME##_bfd_gc_sections, \
+  NAME##_bfd_parse_eh_frame_entries, \
   NAME##_bfd_lookup_section_flags, \
   NAME##_bfd_merge_sections, \
   NAME##_bfd_is_group_section, \
@@ -7143,6 +7148,9 @@ typedef struct bfd_target
   /* Remove sections that are not referenced from the output.  */
   bfd_boolean (*_bfd_gc_sections) (bfd *, struct bfd_link_info *);
 
+  /* Parse .eh_frame_entry sections. */
+  bfd_boolean (*_bfd_parse_eh_frame_entries) (bfd *, struct bfd_link_info *);
+
   /* Sets the bitmask of allowed and disallowed section flags.  */
   bfd_boolean (*_bfd_lookup_section_flags) (struct bfd_link_info *,
                                             struct flag_info *,
diff --git a/bfd/bfd.c b/bfd/bfd.c
index 2d9397b..9762f7f 100644
--- a/bfd/bfd.c
+++ b/bfd/bfd.c
@@ -1483,6 +1483,9 @@ DESCRIPTION
 .#define bfd_gc_sections(abfd, link_info) \
 .	BFD_SEND (abfd, _bfd_gc_sections, (abfd, link_info))
 .
+.#define bfd_parse_eh_frame_entries(abfd, link_info) \
+.	BFD_SEND (abfd, _bfd_parse_eh_frame_entries, (abfd, link_info))
+.
 .#define bfd_lookup_section_flags(link_info, flag_info, section) \
 .	BFD_SEND (abfd, _bfd_lookup_section_flags, (link_info, flag_info, section))
 .
diff --git a/bfd/binary.c b/bfd/binary.c
index d35e859..196bd17 100644
--- a/bfd/binary.c
+++ b/bfd/binary.c
@@ -299,6 +299,7 @@ binary_sizeof_headers (bfd *abfd ATTRIBUTE_UNUSED,
 #define binary_bfd_get_relocated_section_contents  bfd_generic_get_relocated_section_contents
 #define binary_bfd_relax_section                   bfd_generic_relax_section
 #define binary_bfd_gc_sections                     bfd_generic_gc_sections
+#define binary_bfd_parse_eh_frame_entries	   bfd_generic_parse_eh_frame_entries
 #define binary_bfd_lookup_section_flags            bfd_generic_lookup_section_flags
 #define binary_bfd_merge_sections                  bfd_generic_merge_sections
 #define binary_bfd_is_group_section                bfd_generic_is_group_section
diff --git a/bfd/bout.c b/bfd/bout.c
index a27df58..9e7cb3f 100644
--- a/bfd/bout.c
+++ b/bfd/bout.c
@@ -1385,6 +1385,7 @@ b_out_bfd_get_relocated_section_contents (bfd *output_bfd,
 #define b_out_bfd_final_link                   _bfd_generic_final_link
 #define b_out_bfd_link_split_section           _bfd_generic_link_split_section
 #define b_out_bfd_gc_sections                  bfd_generic_gc_sections
+#define b_out_bfd_parse_eh_frame_entries       bfd_generic_parse_eh_frame_entries
 #define b_out_bfd_lookup_section_flags         bfd_generic_lookup_section_flags
 #define b_out_bfd_merge_sections               bfd_generic_merge_sections
 #define b_out_bfd_is_group_section             bfd_generic_is_group_section
diff --git a/bfd/coff-alpha.c b/bfd/coff-alpha.c
index 0e78088..45e76b9 100644
--- a/bfd/coff-alpha.c
+++ b/bfd/coff-alpha.c
@@ -2339,6 +2339,7 @@ static const struct ecoff_backend_data alpha_ecoff_backend_data =
 /* Relaxing sections is generic.  */
 #define _bfd_ecoff_bfd_relax_section bfd_generic_relax_section
 #define _bfd_ecoff_bfd_gc_sections bfd_generic_gc_sections
+#define _bfd_ecoff_bfd_parse_eh_frame_entries bfd_generic_parse_eh_frame_entries
 #define _bfd_ecoff_bfd_merge_sections bfd_generic_merge_sections
 #define _bfd_ecoff_bfd_is_group_section bfd_generic_is_group_section
 #define _bfd_ecoff_bfd_discard_group bfd_generic_discard_group
diff --git a/bfd/coff-mips.c b/bfd/coff-mips.c
index 298671c..a045b20 100644
--- a/bfd/coff-mips.c
+++ b/bfd/coff-mips.c
@@ -1351,6 +1351,9 @@ static const struct ecoff_backend_data mips_ecoff_backend_data =
 /* GC of sections is not done.  */
 #define _bfd_ecoff_bfd_gc_sections bfd_generic_gc_sections
 
+/* Compact EH is not supported.  */
+#define _bfd_ecoff_bfd_parse_eh_frame_entries bfd_generic_parse_eh_frame_entries
+
 /* Input section flags is not implemented.  */
 #define _bfd_ecoff_bfd_lookup_section_flags bfd_generic_lookup_section_flags
 
diff --git a/bfd/coff-rs6000.c b/bfd/coff-rs6000.c
index fea5f3b..fc4e59a 100644
--- a/bfd/coff-rs6000.c
+++ b/bfd/coff-rs6000.c
@@ -4035,6 +4035,7 @@ const struct xcoff_dwsect_name xcoff_dwsect_names[] = {
   _bfd_generic_copy_link_hash_symbol_type
 #define _bfd_xcoff_bfd_link_split_section _bfd_generic_link_split_section
 #define _bfd_xcoff_bfd_gc_sections bfd_generic_gc_sections
+#define _bfd_xcoff_bfd_parse_eh_frame_entries bfd_generic_parse_eh_frame_entries
 #define _bfd_xcoff_bfd_lookup_section_flags bfd_generic_lookup_section_flags
 #define _bfd_xcoff_bfd_merge_sections bfd_generic_merge_sections
 #define _bfd_xcoff_bfd_is_group_section bfd_generic_is_group_section
diff --git a/bfd/coff64-rs6000.c b/bfd/coff64-rs6000.c
index 5985d81..c7f3c5b 100644
--- a/bfd/coff64-rs6000.c
+++ b/bfd/coff64-rs6000.c
@@ -2743,6 +2743,7 @@ const bfd_target rs6000_xcoff64_vec =
     _bfd_xcoff_bfd_final_link,
     _bfd_generic_link_split_section,
     bfd_generic_gc_sections,
+    bfd_generic_parse_eh_frame_entries,
     bfd_generic_lookup_section_flags,
     bfd_generic_merge_sections,
     bfd_generic_is_group_section,
@@ -3000,6 +3001,7 @@ const bfd_target rs6000_xcoff64_aix_vec =
     _bfd_xcoff_bfd_final_link,
     _bfd_generic_link_split_section,
     bfd_generic_gc_sections,
+    bfd_generic_parse_eh_frame_entries,
     bfd_generic_lookup_section_flags,
     bfd_generic_merge_sections,
     bfd_generic_is_group_section,
diff --git a/bfd/coffcode.h b/bfd/coffcode.h
index 1ca28b8..3185404 100644
--- a/bfd/coffcode.h
+++ b/bfd/coffcode.h
@@ -5933,6 +5933,10 @@ static bfd_coff_backend_data bigobj_swap_table =
 #define coff_bfd_gc_sections		    bfd_generic_gc_sections
 #endif
 
+#ifndef coff_bfd_parse_eh_frame_entries
+#define coff_bfd_parse_eh_frame_entries	    bfd_generic_parse_eh_frame_entries
+#endif
+
 #ifndef coff_bfd_lookup_section_flags
 #define coff_bfd_lookup_section_flags	    bfd_generic_lookup_section_flags
 #endif
diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index 0c82278..7de13d1 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -380,16 +380,37 @@ struct eh_frame_array_ent
 
 struct htab;
 
+#define DWARF2_EH_HDR 1
+#define COMPACT_EH_HDR 2
+
+struct dwarf_eh_frame_hdr_info
+{
+  struct eh_frame_array_ent *array;
+};
+
+struct compact_eh_frame_hdr_info
+{
+  unsigned int allocated_entries;
+  /* eh_frame_entry_fragments.  */
+  asection **entries;
+};
+
 struct eh_frame_hdr_info
 {
   struct htab *cies;
   asection *hdr_sec;
   unsigned int fde_count, array_count;
-  struct eh_frame_array_ent *array;
   /* TRUE if .eh_frame_hdr should contain the sorted search table.
      We build it if we successfully read all .eh_frame input sections
      and recognize them.  */
   bfd_boolean table;
+  bfd_boolean frame_hdr_is_compact;
+  union
+    {
+      struct dwarf_eh_frame_hdr_info dwarf;
+      struct compact_eh_frame_hdr_info compact;
+    }
+  u;
 };
 
 /* Enum used to identify target specific extensions to the elf_obj_tdata
@@ -1275,6 +1296,9 @@ struct elf_backend_data
      or give an error and return FALSE.  */
   bfd_boolean (*obj_attrs_handle_unknown) (bfd *, int);
 
+  /* Encoding used for compact EH tables.  */
+  int (*compact_eh_encoding) (struct bfd_link_info *);
+
   /* This is non-zero if static TLS segments require a special alignment.  */
   unsigned static_tls_alignment;
 
@@ -1425,6 +1449,9 @@ struct bfd_elf_section_data
      field acts as a chain pointer.  */
   struct eh_cie_fde *fde_list;
 
+  /* Link from a text section to its .eh_frame_entry section.  */
+  asection *eh_frame_entry;
+
   /* A pointer used for various section optimizations.  */
   void *sec_info;
 };
@@ -1438,6 +1465,7 @@ struct bfd_elf_section_data
 #define elf_next_in_group(sec)	(elf_section_data(sec)->next_in_group)
 #define elf_fde_list(sec)	(elf_section_data(sec)->fde_list)
 #define elf_sec_group(sec)	(elf_section_data(sec)->sec_group)
+#define elf_section_eh_frame_entry(sec)	(elf_section_data(sec)->eh_frame_entry)
 
 #define xvec_get_elf_backend_data(xvec) \
   ((const struct elf_backend_data *) (xvec)->backend_data)
@@ -1961,9 +1989,17 @@ extern void _bfd_elf_strtab_finalize
 
 extern void _bfd_elf_parse_eh_frame
   (bfd *, struct bfd_link_info *, asection *, struct elf_reloc_cookie *);
+extern bfd_boolean _bfd_elf_end_eh_frame_parsing
+  (struct bfd_link_info *info);
+extern bfd_boolean bfd_elf_parse_eh_frame_entries
+  (bfd *, struct bfd_link_info *);
+extern void _bfd_elf_parse_eh_frame_entry
+  (struct bfd_link_info *, asection *, struct elf_reloc_cookie *);
 extern bfd_boolean _bfd_elf_discard_section_eh_frame
   (bfd *, struct bfd_link_info *, asection *,
    bfd_boolean (*) (bfd_vma, void *), struct elf_reloc_cookie *);
+extern bfd_boolean _bfd_elf_write_section_eh_frame_entry
+  (bfd *, asection *, bfd_byte *);
 extern bfd_boolean _bfd_elf_discard_section_eh_frame_hdr
   (bfd *, struct bfd_link_info *);
 extern bfd_vma _bfd_elf_eh_frame_section_offset
@@ -1974,6 +2010,10 @@ extern bfd_boolean _bfd_elf_write_section_eh_frame_hdr
   (bfd *, struct bfd_link_info *);
 extern bfd_boolean _bfd_elf_eh_frame_present
   (struct bfd_link_info *);
+extern bfd_boolean _bfd_elf_eh_frame_entry_present
+  (struct bfd_link_info *);
+extern bfd_boolean _bfd_elf_fixup_eh_frame_hdr
+  (struct bfd_link_info *);
 extern bfd_boolean _bfd_elf_maybe_strip_eh_frame_hdr
   (struct bfd_link_info *);
 
@@ -1997,6 +2037,8 @@ extern bfd_boolean _bfd_elf_create_dynamic_sections
   (bfd *, struct bfd_link_info *);
 extern bfd_boolean _bfd_elf_create_got_section
   (bfd *, struct bfd_link_info *);
+extern asection *_bfd_elf_section_for_symbol
+  (struct elf_reloc_cookie *, unsigned long, bfd_boolean);
 extern struct elf_link_hash_entry *_bfd_elf_define_linkage_sym
   (bfd *, struct bfd_link_info *, asection *, const char *);
 extern void _bfd_elf_init_1_index_section
diff --git a/bfd/elf-eh-frame.c b/bfd/elf-eh-frame.c
index e481f34..c6a07a7 100644
--- a/bfd/elf-eh-frame.c
+++ b/bfd/elf-eh-frame.c
@@ -452,6 +452,114 @@ make_pc_relative (unsigned char encoding, unsigned int ptr_size)
   return encoding | DW_EH_PE_pcrel;
 }
 
+/*  Examine each .eh_frame_entry section and discard those
+    those that are marked SEC_EXCLUDE.  */
+
+static void
+bfd_elf_discard_eh_frame_entry (struct eh_frame_hdr_info *hdr_info)
+{
+  unsigned int i;
+  for (i = 0; i < hdr_info->array_count; i++)
+    {
+      if (hdr_info->u.compact.entries[i]->flags & SEC_EXCLUDE)
+	{
+	  unsigned int j;
+	  for (j = i + 1; j < hdr_info->array_count; j++)
+	    hdr_info->u.compact.entries[j-1] = hdr_info->u.compact.entries[j];
+
+	  hdr_info->array_count--;
+	  hdr_info->u.compact.entries[hdr_info->array_count] = NULL;
+	  i--;
+        }
+    }
+}
+/* Add a .eh_frame_entry section.  */
+
+static void
+bfd_elf_record_eh_frame_entry (struct eh_frame_hdr_info *hdr_info,
+				 asection *sec)
+{
+  if (hdr_info->array_count == hdr_info->u.compact.allocated_entries)
+    {
+      if (hdr_info->u.compact.allocated_entries == 0)
+	{
+	  hdr_info->frame_hdr_is_compact = TRUE;
+	  hdr_info->u.compact.allocated_entries = 2;
+	  hdr_info->u.compact.entries =
+	    bfd_malloc (hdr_info->u.compact.allocated_entries
+			* sizeof (hdr_info->u.compact.entries[0]));
+	}
+      else
+	{
+	  hdr_info->u.compact.allocated_entries *= 2;
+	  hdr_info->u.compact.entries =
+	    bfd_realloc (hdr_info->u.compact.entries,
+			 hdr_info->u.compact.allocated_entries
+			   * sizeof (hdr_info->u.compact.entries[0]));
+	}
+
+      BFD_ASSERT (hdr_info->u.compact.entries);
+    }
+
+  hdr_info->u.compact.entries[hdr_info->array_count++] = sec;
+}
+
+
+/* Parse a .eh_frame_entry section.  Figure out which text section it
+   references.  */
+
+void
+_bfd_elf_parse_eh_frame_entry (struct bfd_link_info *info,
+			       asection *sec, struct elf_reloc_cookie *cookie)
+{
+  struct elf_link_hash_table *htab;
+  struct eh_frame_hdr_info *hdr_info;
+  unsigned long r_symndx;
+  asection *text_sec;
+
+  htab = elf_hash_table (info);
+  hdr_info = &htab->eh_info;
+
+  if (sec->size == 0
+      || sec->sec_info_type != SEC_INFO_TYPE_NONE)
+    {
+      return;
+    }
+
+  if (sec->output_section && bfd_is_abs_section (sec->output_section))
+    {
+      /* At least one of the sections is being discarded from the
+	 link, so we should just ignore them.  */
+      return;
+    }
+
+  if (cookie->rel == cookie->relend)
+    goto fail;
+
+  /* The first relocation is the function start.  */
+  r_symndx = cookie->rel->r_info >> cookie->r_sym_shift;
+  if (r_symndx == STN_UNDEF)
+    goto fail;
+
+  text_sec = _bfd_elf_section_for_symbol (cookie, r_symndx, FALSE);
+
+  if (text_sec == NULL)
+    goto fail;
+
+  elf_section_eh_frame_entry (text_sec) = sec;
+  if (text_sec->output_section
+      && bfd_is_abs_section (text_sec->output_section))
+    sec->flags |= SEC_EXCLUDE;
+
+  sec->sec_info_type = SEC_INFO_TYPE_EH_FRAME_ENTRY;
+  elf_section_data (sec)->sec_info = text_sec;
+  bfd_elf_record_eh_frame_entry (hdr_info, sec);
+  return;
+
+fail:
+  (*_bfd_error_handler) (_("%B: failed to process .eh_frame_entry"), sec->owner);
+}
+
 /* Try to parse .eh_frame section SEC, which belongs to ABFD.  Store the
    information in the section's sec_info field on success.  COOKIE
    describes the relocations in SEC.  */
@@ -936,6 +1044,89 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info,
 #undef REQUIRE
 }
 
+/* Order eh_frame_hdr entries by the VMA of their text section.  */
+
+static int
+cmp_eh_frame_hdr (const void *a, const void *b)
+{
+  bfd_vma text_a;
+  bfd_vma text_b;
+  asection *sec;
+
+  sec = *(asection *const *)a;
+  sec = (asection *) elf_section_data (sec)->sec_info;
+  text_a = sec->output_section->vma + sec->output_offset;
+  sec = *(asection *const *)b;
+  sec = (asection *) elf_section_data (sec)->sec_info;
+  text_b = sec->output_section->vma + sec->output_offset;
+
+  if (text_a < text_b)
+    return -1;
+  return text_a > text_b;
+
+}
+
+/* Add space for a CANTUNWIND terminator to SEC if the text sections
+   referenced by it and NEXT are not contiguous, or NEXT is NULL.  */
+
+static void
+add_eh_frame_hdr_terminator (asection *sec,
+			     asection *next)
+{
+  bfd_vma end;
+  bfd_vma next_start;
+  asection *text_sec;
+
+  if (next)
+    {
+      /* See if there is a gap (presumably a text section without unwind info)
+	 between these two entries.  */
+      text_sec = (asection *) elf_section_data (sec)->sec_info;
+      end = text_sec->output_section->vma + text_sec->output_offset
+	    + text_sec->size;
+      text_sec = (asection *) elf_section_data (next)->sec_info;
+      next_start = text_sec->output_section->vma + text_sec->output_offset;
+      if (end == next_start)
+	return;
+    }
+
+  /* Add space for a CANTUNWIND terminator.  */
+  if (!sec->rawsize)
+    sec->rawsize = sec->size;
+
+  bfd_set_section_size (sec->owner, sec, sec->size + 8);
+}
+
+/* Finish a pass over all .eh_frame_entry sections.  */
+
+bfd_boolean
+_bfd_elf_end_eh_frame_parsing (struct bfd_link_info *info)
+{
+  struct eh_frame_hdr_info *hdr_info;
+  unsigned int i;
+
+  hdr_info = &elf_hash_table (info)->eh_info;
+
+  if (info->eh_frame_hdr_type != COMPACT_EH_HDR
+      || hdr_info->array_count == 0)
+    return FALSE;
+
+  bfd_elf_discard_eh_frame_entry (hdr_info);
+
+  qsort (hdr_info->u.compact.entries, hdr_info->array_count,
+	 sizeof (asection *), cmp_eh_frame_hdr);
+
+  for (i = 0; i < hdr_info->array_count - 1; i++)
+    {
+      add_eh_frame_hdr_terminator (hdr_info->u.compact.entries[i],
+				   hdr_info->u.compact.entries[i + 1]);
+    }
+
+  /* Add a CANTUNWIND terminator after the last entry.  */
+  add_eh_frame_hdr_terminator (hdr_info->u.compact.entries[i], NULL);
+  return TRUE;
+}
+
 /* Mark all relocations against CIE or FDE ENT, which occurs in
    .eh_frame section SEC.  COOKIE describes the relocations in SEC;
    its "rel" field can be changed freely.  */
@@ -1251,6 +1442,7 @@ _bfd_elf_discard_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
 /* Return true if there is at least one non-empty .eh_frame section in
    input files.  Can only be called after ld has mapped input to
    output sections, and before sections are stripped.  */
+
 bfd_boolean
 _bfd_elf_eh_frame_present (struct bfd_link_info *info)
 {
@@ -1268,6 +1460,29 @@ _bfd_elf_eh_frame_present (struct bfd_link_info *info)
   return FALSE;
 }
 
+/* Return true if there is at least one .eh_frame_entry section in
+   input files.  */
+
+bfd_boolean
+_bfd_elf_eh_frame_entry_present (struct bfd_link_info *info)
+{
+  asection *o;
+  bfd *abfd;
+
+  for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link.next)
+    {
+      for (o = abfd->sections; o; o = o->next)
+	{
+	  const char *name = bfd_get_section_name (abfd, o);
+
+	  if (strcmp (name, ".eh_frame_entry")
+	      && !bfd_is_abs_section (o->output_section))
+	    return TRUE;
+	}
+    }
+  return FALSE;
+}
+
 /* This function is called from size_dynamic_sections.
    It needs to decide whether .eh_frame_hdr should be output or not,
    because when the dynamic symbol table has been sized it is too late
@@ -1285,8 +1500,11 @@ _bfd_elf_maybe_strip_eh_frame_hdr (struct bfd_link_info *info)
     return TRUE;
 
   if (bfd_is_abs_section (hdr_info->hdr_sec->output_section)
-      || !info->eh_frame_hdr
-      || !_bfd_elf_eh_frame_present (info))
+      || info->eh_frame_hdr_type == 0
+      || (info->eh_frame_hdr_type == DWARF2_EH_HDR
+	  && !_bfd_elf_eh_frame_present (info))
+      || (info->eh_frame_hdr_type == COMPACT_EH_HDR
+	  && !_bfd_elf_eh_frame_entry_present (info)))
     {
       hdr_info->hdr_sec->flags |= SEC_EXCLUDE;
       hdr_info->hdr_sec = NULL;
@@ -1384,6 +1602,78 @@ _bfd_elf_eh_frame_section_offset (bfd *output_bfd ATTRIBUTE_UNUSED,
 	  + extra_augmentation_data_bytes (sec_info->entry + mid));
 }
 
+/* Write out .eh_frame_entry section.  Add CANTUNWIND terminator if needed.
+   Also check that the contents look sane.  */
+
+bfd_boolean
+_bfd_elf_write_section_eh_frame_entry (bfd *abfd,
+				       asection *sec,
+				       bfd_byte *contents)
+{
+  bfd_byte cantunwind[8];
+  bfd_vma addr;
+  bfd_vma last_addr;
+  asection *text_sec;
+  bfd_vma offset;
+
+  if (!sec->rawsize)
+    sec->rawsize = sec->size;
+
+  BFD_ASSERT (sec->sec_info_type == SEC_INFO_TYPE_EH_FRAME_ENTRY);
+
+  if (sec->flags & SEC_EXCLUDE)
+    return TRUE;
+
+  if (!bfd_set_section_contents (abfd, sec->output_section, contents,
+				 sec->output_offset, sec->rawsize))
+      return FALSE;
+
+  last_addr = bfd_get_signed_32 (abfd, contents);
+  /* Check that all the entries are in order.  */
+  for (offset = 8; offset < sec->rawsize; offset += 8)
+    {
+      addr = bfd_get_signed_32 (abfd, contents + offset) + offset;
+      if (addr <= last_addr)
+	{
+	  (*_bfd_error_handler) (_("%B: %s not in order"), sec->owner, sec->name);
+	  return FALSE;
+	}
+
+      last_addr = addr;
+    }
+
+  text_sec = (asection *) elf_section_data (sec)->sec_info;
+  addr = text_sec->output_section->vma + text_sec->output_offset
+	 + text_sec->size;
+  addr &= ~1;
+  addr -= (sec->output_section->vma + sec->output_offset + sec->rawsize);
+  if (addr & 1)
+    {
+      (*_bfd_error_handler) (_("%B: %s invalid input section size"),
+			     sec->owner, sec->name);
+      return FALSE;
+    }
+  if (last_addr >= addr + sec->rawsize)
+    {
+      (*_bfd_error_handler) (_("%B: %s points past end of text section"),
+			     sec->owner, sec->name);
+      return FALSE;
+    }
+
+  if (sec->size == sec->rawsize)
+    return TRUE;
+
+  BFD_ASSERT (sec->size == sec->rawsize + 8);
+  BFD_ASSERT ((addr & 1) == 0);
+
+  bfd_put_32 (abfd, addr, cantunwind);
+  /* 0x015d5d01 is an endian-neutral code to indicate that
+     a function that cannot be unwound.  */
+  bfd_put_32 (abfd, 0x015d5d01, cantunwind + 4);
+  return bfd_set_section_contents (abfd, sec->output_section, cantunwind,
+				   sec->output_offset + sec->rawsize, 8);
+}
+
 /* Write out .eh_frame section.  This is called with the relocated
    contents.  */
 
@@ -1412,10 +1702,13 @@ _bfd_elf_write_section_eh_frame (bfd *abfd,
   htab = elf_hash_table (info);
   hdr_info = &htab->eh_info;
 
-  if (hdr_info->table && hdr_info->array == NULL)
-    hdr_info->array = (struct eh_frame_array_ent *)
-        bfd_malloc (hdr_info->fde_count * sizeof(*hdr_info->array));
-  if (hdr_info->array == NULL)
+  if (hdr_info->table && hdr_info->u.dwarf.array == NULL)
+    {
+      hdr_info->frame_hdr_is_compact = FALSE;
+      hdr_info->u.dwarf.array = (struct eh_frame_array_ent *)
+        bfd_malloc (hdr_info->fde_count * sizeof (*hdr_info->u.dwarf.array));
+    }
+  if (hdr_info->u.dwarf.array == NULL)
     hdr_info = NULL;
 
   /* The new offsets can be bigger or smaller than the original offsets.
@@ -1650,10 +1943,11 @@ _bfd_elf_write_section_eh_frame (bfd *abfd,
 		 dwarf_vma is 64-bit.  */
 	      if (sizeof (address) > 4 && ptr_size == 4)
 		address &= 0xffffffff;
-	      hdr_info->array[hdr_info->array_count].initial_loc = address;
-	      hdr_info->array[hdr_info->array_count].range
+	      hdr_info->u.dwarf.array[hdr_info->array_count].initial_loc
+		= address;
+	      hdr_info->u.dwarf.array[hdr_info->array_count].range
 		= read_value (abfd, buf + width, width, FALSE);
-	      hdr_info->array[hdr_info->array_count++].fde
+	      hdr_info->u.dwarf.array[hdr_info->array_count++].fde
 		= (sec->output_section->vma
 		   + sec->output_offset
 		   + ent->new_offset);
@@ -1751,10 +2045,112 @@ vma_compare (const void *a, const void *b)
   return 0;
 }
 
-/* Write out .eh_frame_hdr section.  This must be called after
-   _bfd_elf_write_section_eh_frame has been called on all input
-   .eh_frame sections.
-   .eh_frame_hdr format:
+
+/* Reorder .eh_frame_entry sections to match the associated text sections.
+   This routine is called during the final linking step, just before writing
+   the contents.  At this stage, sections in the eh_frame_hdr_info are already
+   sorted in order of increasing text section address and so we simply need
+   to make the .eh_frame_entrys follow that same order.  Note that it is
+   invalid for a linker script to try to force a particular order of
+   .eh_frame_entry sections.  */
+
+bfd_boolean
+_bfd_elf_fixup_eh_frame_hdr (struct bfd_link_info *info)
+{
+  asection *sec = NULL;
+  asection *osec;
+  struct eh_frame_hdr_info *hdr_info;
+  unsigned int i;
+  bfd_vma offset;
+  struct bfd_link_order *p;
+
+  hdr_info = &elf_hash_table (info)->eh_info;
+
+  if (hdr_info->hdr_sec == NULL
+      || info->eh_frame_hdr_type != COMPACT_EH_HDR
+      || hdr_info->array_count == 0)
+    return TRUE;
+
+  /* Change section output offsets to be in text section order.  */
+  offset = 8;
+  osec = hdr_info->u.compact.entries[0]->output_section;
+  for (i = 0; i < hdr_info->array_count; i++)
+    {
+      sec = hdr_info->u.compact.entries[i];
+      if (sec->output_section != osec)
+	{
+	  (*_bfd_error_handler)
+	    (_("Invalid output section for .eh_frame_entry: %s"),
+	     sec->output_section->name);
+	  return FALSE;
+	}
+      sec->output_offset = offset;
+      offset += sec->size;
+    }
+
+
+  /* Fix the link_order to match.  */
+  for (p = sec->output_section->map_head.link_order; p != NULL; p = p->next)
+    {
+      if (p->type != bfd_indirect_link_order)
+	abort();
+
+      p->offset = p->u.indirect.section->output_offset;
+      if (p->next != NULL)
+        i--;
+    }
+
+  if (i != 0)
+    {
+      (*_bfd_error_handler)
+	(_("Invalid contents in %s section"), osec->name);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+/* The .eh_frame_hdr format for Compact EH frames:
+   ubyte version		(2)
+   ubyte eh_ref_enc		(DW_EH_PE_* encoding of typinfo references)
+   uint32_t count		(Number of entries in table)
+   [array from .eh_frame_entry sections]  */
+
+static bfd_boolean
+write_compact_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
+{
+  struct elf_link_hash_table *htab;
+  struct eh_frame_hdr_info *hdr_info;
+  asection *sec;
+  const struct elf_backend_data *bed;
+  bfd_vma count;
+  bfd_byte contents[8];
+  unsigned int i;
+
+  htab = elf_hash_table (info);
+  hdr_info = &htab->eh_info;
+  sec = hdr_info->hdr_sec;
+
+  if (sec->size != 8)
+    abort();
+
+  for (i = 0; i < sizeof (contents); i++)
+    contents[i] = 0;
+
+  contents[0] = COMPACT_EH_HDR;
+  bed = get_elf_backend_data (abfd);
+
+  BFD_ASSERT (bed->compact_eh_encoding);
+  contents[1] = (*bed->compact_eh_encoding) (info);
+
+  count = (sec->output_section->size - 8) / 8;
+  bfd_put_32 (abfd, count, contents + 4);
+  return bfd_set_section_contents (abfd, sec->output_section, contents,
+				   (file_ptr) sec->output_offset, sec->size);
+}
+
+/* The .eh_frame_hdr format for DWARF frames:
+
    ubyte version		(currently 1)
    ubyte eh_frame_ptr_enc  	(DW_EH_PE_* encoding of pointer to start of
 				 .eh_frame section)
@@ -1773,8 +2169,8 @@ vma_compare (const void *a, const void *b)
 				 FDE initial_location field and FDE address,
 				 sorted by increasing initial_loc).  */
 
-bfd_boolean
-_bfd_elf_write_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
+static bfd_boolean
+write_dwarf_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
 {
   struct elf_link_hash_table *htab;
   struct eh_frame_hdr_info *hdr_info;
@@ -1784,100 +2180,130 @@ _bfd_elf_write_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
   htab = elf_hash_table (info);
   hdr_info = &htab->eh_info;
   sec = hdr_info->hdr_sec;
+  bfd_byte *contents;
+  asection *eh_frame_sec;
+  bfd_size_type size;
+  bfd_vma encoded_eh_frame;
+
+  size = EH_FRAME_HDR_SIZE;
+  if (hdr_info->u.dwarf.array
+      && hdr_info->array_count == hdr_info->fde_count)
+    size += 4 + hdr_info->fde_count * 8;
+  contents = (bfd_byte *) bfd_malloc (size);
+  if (contents == NULL)
+    return FALSE;
 
-  if (info->eh_frame_hdr && sec != NULL)
+  eh_frame_sec = bfd_get_section_by_name (abfd, ".eh_frame");
+  if (eh_frame_sec == NULL)
     {
-      bfd_byte *contents;
-      asection *eh_frame_sec;
-      bfd_size_type size;
-      bfd_vma encoded_eh_frame;
-
-      size = EH_FRAME_HDR_SIZE;
-      if (hdr_info->array && hdr_info->array_count == hdr_info->fde_count)
-	size += 4 + hdr_info->fde_count * 8;
-      contents = (bfd_byte *) bfd_malloc (size);
-      if (contents == NULL)
-	return FALSE;
+      free (contents);
+      return FALSE;
+    }
 
-      eh_frame_sec = bfd_get_section_by_name (abfd, ".eh_frame");
-      if (eh_frame_sec == NULL)
-	{
-	  free (contents);
-	  return FALSE;
-	}
+  memset (contents, 0, EH_FRAME_HDR_SIZE);
+  /* Version.  */
+  contents[0] = 1;
+  /* .eh_frame offset.  */
+  contents[1] = get_elf_backend_data (abfd)->elf_backend_encode_eh_address
+    (abfd, info, eh_frame_sec, 0, sec, 4, &encoded_eh_frame);
 
-      memset (contents, 0, EH_FRAME_HDR_SIZE);
-      /* Version.  */
-      contents[0] = 1;
-      /* .eh_frame offset.  */
-      contents[1] = get_elf_backend_data (abfd)->elf_backend_encode_eh_address
-	(abfd, info, eh_frame_sec, 0, sec, 4, &encoded_eh_frame);
+  if (hdr_info->u.dwarf.array
+      && hdr_info->array_count == hdr_info->fde_count)
+    {
+      /* FDE count encoding.  */
+      contents[2] = DW_EH_PE_udata4;
+      /* Search table encoding.  */
+      contents[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4;
+    }
+  else
+    {
+      contents[2] = DW_EH_PE_omit;
+      contents[3] = DW_EH_PE_omit;
+    }
+  bfd_put_32 (abfd, encoded_eh_frame, contents + 4);
 
-      if (hdr_info->array && hdr_info->array_count == hdr_info->fde_count)
+  if (contents[2] != DW_EH_PE_omit)
+    {
+      unsigned int i;
+      bfd_boolean overlap, overflow;
+
+      bfd_put_32 (abfd, hdr_info->fde_count, contents + EH_FRAME_HDR_SIZE);
+      qsort (hdr_info->u.dwarf.array, hdr_info->fde_count,
+	     sizeof (*hdr_info->u.dwarf.array), vma_compare);
+      overlap = FALSE;
+      overflow = FALSE;
+      for (i = 0; i < hdr_info->fde_count; i++)
 	{
-	  /* FDE count encoding.  */
-	  contents[2] = DW_EH_PE_udata4;
-	  /* Search table encoding.  */
-	  contents[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4;
+	  bfd_vma val;
+
+	  val = hdr_info->u.dwarf.array[i].initial_loc
+	    - sec->output_section->vma;
+	  val = ((val & 0xffffffff) ^ 0x80000000) - 0x80000000;
+	  if (elf_elfheader (abfd)->e_ident[EI_CLASS] == ELFCLASS64
+	      && (hdr_info->u.dwarf.array[i].initial_loc
+		  != sec->output_section->vma + val))
+	    overflow = TRUE;
+	  bfd_put_32 (abfd, val, contents + EH_FRAME_HDR_SIZE + i * 8 + 4);
+	  val = hdr_info->u.dwarf.array[i].fde - sec->output_section->vma;
+	  val = ((val & 0xffffffff) ^ 0x80000000) - 0x80000000;
+	  if (elf_elfheader (abfd)->e_ident[EI_CLASS] == ELFCLASS64
+	      && (hdr_info->u.dwarf.array[i].fde
+		  != sec->output_section->vma + val))
+	    overflow = TRUE;
+	  bfd_put_32 (abfd, val, contents + EH_FRAME_HDR_SIZE + i * 8 + 8);
+	  if (i != 0
+	      && (hdr_info->u.dwarf.array[i].initial_loc
+		  < (hdr_info->u.dwarf.array[i - 1].initial_loc
+		     + hdr_info->u.dwarf.array[i - 1].range)))
+	    overlap = TRUE;
 	}
-      else
+      if (overflow)
+	(*info->callbacks->einfo) (_("%P: .eh_frame_hdr entry overflow.\n"));
+      if (overlap)
+	(*info->callbacks->einfo)
+	  (_("%P: .eh_frame_hdr refers to overlapping FDEs.\n"));
+      if (overflow || overlap)
 	{
-	  contents[2] = DW_EH_PE_omit;
-	  contents[3] = DW_EH_PE_omit;
+	  bfd_set_error (bfd_error_bad_value);
+	  retval = FALSE;
 	}
-      bfd_put_32 (abfd, encoded_eh_frame, contents + 4);
+    }
 
-      if (contents[2] != DW_EH_PE_omit)
-	{
-	  unsigned int i;
+  /* FIXME: octets_per_byte.  */
+  if (!bfd_set_section_contents (abfd, sec->output_section, contents,
+				 (file_ptr) sec->output_offset,
+				 sec->size))
+    retval = FALSE;
+  free (contents);
+
+  if (hdr_info->u.dwarf.array != NULL)
+    free (hdr_info->u.dwarf.array);
+  return retval;
+}
 
-	  bfd_put_32 (abfd, hdr_info->fde_count, contents + EH_FRAME_HDR_SIZE);
-	  qsort (hdr_info->array, hdr_info->fde_count,
-		 sizeof (*hdr_info->array), vma_compare);
-	  for (i = 0; i < hdr_info->fde_count; i++)
-	    {
-	      bfd_vma val;
+/* Write out .eh_frame_hdr section.  This must be called after
+   _bfd_elf_write_section_eh_frame has been called on all input
+   .eh_frame sections.  */
 
-	      val = hdr_info->array[i].initial_loc - sec->output_section->vma;
-	      val = ((val & 0xffffffff) ^ 0x80000000) - 0x80000000;
-	      if (elf_elfheader (abfd)->e_ident[EI_CLASS] == ELFCLASS64
-		  && (hdr_info->array[i].initial_loc
-		      != sec->output_section->vma + val))
-		(*info->callbacks->einfo)
-		  (_("%X%P: .eh_frame_hdr table[%u] PC overflow.\n"), i);
-	      bfd_put_32 (abfd, val, contents + EH_FRAME_HDR_SIZE + i * 8 + 4);
-
-	      val = hdr_info->array[i].fde - sec->output_section->vma;
-	      val = ((val & 0xffffffff) ^ 0x80000000) - 0x80000000;
-	      if (elf_elfheader (abfd)->e_ident[EI_CLASS] == ELFCLASS64
-		  && (hdr_info->array[i].fde
-		      != sec->output_section->vma + val))
-		(*info->callbacks->einfo)
-		  (_("%X%P: .eh_frame_hdr table[%u] FDE overflow.\n"), i);
-	      bfd_put_32 (abfd, val, contents + EH_FRAME_HDR_SIZE + i * 8 + 8);
+bfd_boolean
+_bfd_elf_write_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
+{
+  struct elf_link_hash_table *htab;
+  struct eh_frame_hdr_info *hdr_info;
+  asection *sec;
 
-	      if (i != 0
-		  && (hdr_info->array[i].initial_loc
-		      < (hdr_info->array[i - 1].initial_loc
-			 + hdr_info->array[i - 1].range)))
-		(*info->callbacks->einfo)
-		  (_("%X%P: .eh_frame_hdr table[%u] FDE at %V overlaps "
-		     "table[%u] FDE at %V.\n"),
-		   i - 1, hdr_info->array[i - 1].fde,
-		   i, hdr_info->array[i].fde);
-	    }
-	}
+  htab = elf_hash_table (info);
+  hdr_info = &htab->eh_info;
+  sec = hdr_info->hdr_sec;
 
-      /* FIXME: octets_per_byte.  */
-      if (!bfd_set_section_contents (abfd, sec->output_section, contents,
-				     (file_ptr) sec->output_offset,
-				     sec->size))
-	retval = FALSE;
-      free (contents);
-    }
-  if (hdr_info->array != NULL)
-    free (hdr_info->array);
-  return retval;
+  if (info->eh_frame_hdr_type == 0
+      || sec == NULL)
+    return TRUE;
+
+  if (info->eh_frame_hdr_type == COMPACT_EH_HDR)
+    return write_compact_eh_frame_hdr (abfd, info);
+  else
+    return write_dwarf_eh_frame_hdr (abfd, info);
 }
 
 /* Return the width of FDE addresses.  This is the default implementation.  */
diff --git a/bfd/elflink.c b/bfd/elflink.c
index c8068c0..974d0ea 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -53,6 +53,49 @@ struct elf_find_verdep_info
 static bfd_boolean _bfd_elf_fix_symbol_flags
   (struct elf_link_hash_entry *, struct elf_info_failed *);
 
+/*  Return the section referenced by a relocation's symbol.  */
+
+asection *
+_bfd_elf_section_for_symbol (struct elf_reloc_cookie *cookie,
+			     unsigned long r_symndx,
+			     bfd_boolean discard)
+{
+  if (r_symndx >= cookie->locsymcount
+      || ELF_ST_BIND (cookie->locsyms[r_symndx].st_info) != STB_LOCAL)
+    {
+      struct elf_link_hash_entry *h;
+
+      h = cookie->sym_hashes[r_symndx - cookie->extsymoff];
+
+      while (h->root.type == bfd_link_hash_indirect
+	     || h->root.type == bfd_link_hash_warning)
+	h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+      if ((h->root.type == bfd_link_hash_defined
+	   || h->root.type == bfd_link_hash_defweak)
+	   && discarded_section (h->root.u.def.section))
+        return h->root.u.def.section;
+      else
+	return NULL;
+    }
+  else
+    {
+      /* It's not a relocation against a global symbol,
+	 but it could be a relocation against a local
+	 symbol for a discarded section.  */
+      asection *isec;
+      Elf_Internal_Sym *isym;
+
+      /* Need to: get the symbol; get the section.  */
+      isym = &cookie->locsyms[r_symndx];
+      isec = bfd_section_from_elf_index (cookie->abfd, isym->st_shndx);
+      if (isec != NULL
+	  && discard ? discarded_section (isec) : 1)
+	return isec;
+     }
+  return NULL;
+}
+
 /* Define a symbol in a dynamic linkage section.  */
 
 struct elf_link_hash_entry *
@@ -9106,6 +9149,7 @@ elf_section_ignore_discarded_relocs (asection *sec)
     {
     case SEC_INFO_TYPE_STABS:
     case SEC_INFO_TYPE_EH_FRAME:
+    case SEC_INFO_TYPE_EH_FRAME_ENTRY:
       return TRUE;
     default:
       break;
@@ -10872,6 +10916,9 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
 	return FALSE;
     }
 
+  if (!_bfd_elf_fixup_eh_frame_hdr (info))
+    return FALSE;
+
   /* Since ELF permits relocations to be against local symbols, we
      must have the local symbols available when we do the relocations.
      Since we would rather only read the local symbols once, and we
@@ -11778,6 +11825,12 @@ _bfd_elf_gc_mark (struct bfd_link_info *info,
 	}
     }
 
+  if (eh_frame && !eh_frame->gc_mark)
+    {
+      if (!_bfd_elf_gc_mark (info, eh_frame, gc_mark_hook))
+	ret = FALSE;
+    }
+
   return ret;
 }
 
@@ -12148,6 +12201,38 @@ _bfd_elf_gc_keep (struct bfd_link_info *info)
     }
 }
 
+/* Examine each input and look for .eh_frame_entry sections to parse.  */
+
+bfd_boolean
+bfd_elf_parse_eh_frame_entries (bfd *abfd ATTRIBUTE_UNUSED,
+				struct bfd_link_info *info)
+{
+  bfd *ibfd = info->input_bfds;
+
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+    {
+      asection *sec;
+      struct elf_reloc_cookie cookie;
+
+      if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour)
+	continue;
+
+      if (!init_reloc_cookie (&cookie, info, ibfd))
+	return FALSE;
+
+      for (sec = ibfd->sections; sec; sec = sec->next)
+	{
+	  if (CONST_STRNEQ (bfd_section_name (ibfd, sec), ".eh_frame_entry")
+	      && init_reloc_cookie_rels (&cookie, info, ibfd, sec))
+	    {
+	      _bfd_elf_parse_eh_frame_entry (info, sec, &cookie);
+	      fini_reloc_cookie_rels (&cookie, sec);
+	    }
+	}
+    }
+  return TRUE;
+}
+
 /* Do mark and sweep of unused sections.  */
 
 bfd_boolean
@@ -12171,7 +12256,9 @@ bfd_elf_gc_sections (bfd *abfd, struct bfd_link_info *info)
 
   /* Try to parse each bfd's .eh_frame section.  Point elf_eh_frame_section
      at the .eh_frame section if we can mark the FDEs individually.  */
-  for (sub = info->input_bfds; sub != NULL; sub = sub->link.next)
+  for (sub = info->input_bfds;
+       info->eh_frame_hdr_type != COMPACT_EH_HDR && sub != NULL;
+       sub = sub->link.next)
     {
       asection *sec;
       struct elf_reloc_cookie cookie;
@@ -12674,7 +12761,11 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
 	}
     }
 
-  o = bfd_get_section_by_name (output_bfd, ".eh_frame");
+  if (info->eh_frame_hdr_type == COMPACT_EH_HDR)
+    o = NULL;
+  else
+    o = bfd_get_section_by_name (output_bfd, ".eh_frame");
+
   if (o != NULL)
     {
       asection *i;
@@ -12722,7 +12813,7 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
 	}
     }
 
-  if (info->eh_frame_hdr
+  if (info->eh_frame_hdr_type
       && !info->relocatable
       && _bfd_elf_discard_section_eh_frame_hdr (output_bfd, info))
     changed = 1;
diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
index a88d173..dca5e12 100644
--- a/bfd/elfxx-mips.c
+++ b/bfd/elfxx-mips.c
@@ -36,6 +36,7 @@
 #include "elfxx-mips.h"
 #include "elf/mips.h"
 #include "elf-vxworks.h"
+#include "dwarf2.h"
 
 /* Get the ECOFF swapping routines.  */
 #include "coff/sym.h"
@@ -16090,3 +16091,9 @@ _bfd_mips_post_process_headers (bfd *abfd, struct bfd_link_info *link_info)
       || mips_elf_tdata (abfd)->abiflags.fp_abi == Val_GNU_MIPS_ABI_FP_64A)
     i_ehdrp->e_ident[EI_ABIVERSION] = 3;
 }
+
+int
+_bfd_mips_elf_compact_eh_encoding (struct bfd_link_info *link_info ATTRIBUTE_UNUSED)
+{
+  return DW_EH_PE_pcrel | DW_EH_PE_sdata4;
+}
diff --git a/bfd/elfxx-mips.h b/bfd/elfxx-mips.h
index 8f5c53e..8877a9d 100644
--- a/bfd/elfxx-mips.h
+++ b/bfd/elfxx-mips.h
@@ -166,6 +166,8 @@ extern const struct bfd_elf_special_section _bfd_mips_elf_special_sections [];
 
 extern bfd_boolean _bfd_mips_elf_common_definition (Elf_Internal_Sym *);
 
+extern int _bfd_mips_elf_compact_eh_encoding (struct bfd_link_info *);
+
 static inline bfd_boolean
 gprel16_reloc_p (unsigned int r_type)
 {
@@ -189,3 +191,4 @@ literal_reloc_p (int r_type)
 #define elf_backend_merge_symbol_attribute  _bfd_mips_elf_merge_symbol_attribute
 #define elf_backend_ignore_undef_symbol _bfd_mips_elf_ignore_undef_symbol
 #define elf_backend_post_process_headers _bfd_mips_post_process_headers
+#define elf_backend_compact_eh_encoding _bfd_mips_elf_compact_eh_encoding
diff --git a/bfd/elfxx-target.h b/bfd/elfxx-target.h
index 692fb46..9ce26fc 100644
--- a/bfd/elfxx-target.h
+++ b/bfd/elfxx-target.h
@@ -158,6 +158,9 @@
 #ifndef bfd_elfNN_bfd_gc_sections
 #define bfd_elfNN_bfd_gc_sections bfd_elf_gc_sections
 #endif
+#ifndef bfd_elfNN_bfd_parse_eh_frame_entries
+#define bfd_elfNN_bfd_parse_eh_frame_entries bfd_elf_parse_eh_frame_entries
+#endif
 
 #ifndef bfd_elfNN_bfd_merge_sections
 #define bfd_elfNN_bfd_merge_sections \
@@ -658,6 +661,10 @@
 #define elf_backend_is_function_type _bfd_elf_is_function_type
 #endif
 
+#ifndef elf_backend_compact_eh_encoding
+#define elf_backend_compact_eh_encoding NULL
+#endif
+
 #ifndef elf_backend_maybe_function_sym
 #define elf_backend_maybe_function_sym _bfd_elf_maybe_function_sym
 #endif
@@ -772,6 +779,7 @@ static struct elf_backend_data elfNN_bed =
   elf_backend_obj_attrs_section_type,
   elf_backend_obj_attrs_order,
   elf_backend_obj_attrs_handle_unknown,
+  elf_backend_compact_eh_encoding,
   elf_backend_static_tls_alignment,
   elf_backend_stack_align,
   elf_backend_collect,
diff --git a/bfd/i386msdos.c b/bfd/i386msdos.c
index f02659f..8ede781 100644
--- a/bfd/i386msdos.c
+++ b/bfd/i386msdos.c
@@ -142,6 +142,7 @@ msdos_set_section_contents (bfd *abfd,
   bfd_generic_get_relocated_section_contents
 #define msdos_bfd_relax_section bfd_generic_relax_section
 #define msdos_bfd_gc_sections bfd_generic_gc_sections
+#define msdos_bfd_parse_eh_frame_entries bfd_generic_parse_eh_frame_entries
 #define msdos_bfd_lookup_section_flags bfd_generic_lookup_section_flags
 #define msdos_bfd_merge_sections bfd_generic_merge_sections
 #define msdos_bfd_is_group_section bfd_generic_is_group_section
diff --git a/bfd/i386os9k.c b/bfd/i386os9k.c
index d8d5408..3dba1b7 100644
--- a/bfd/i386os9k.c
+++ b/bfd/i386os9k.c
@@ -167,6 +167,7 @@ os9k_sizeof_headers (bfd *abfd ATTRIBUTE_UNUSED,
   bfd_generic_get_relocated_section_contents
 #define os9k_bfd_relax_section bfd_generic_relax_section
 #define os9k_bfd_gc_sections bfd_generic_gc_sections
+#define os9k_bfd_parse_eh_frame_entries	bfd_generic_parse_eh_frame_entries
 #define os9k_bfd_lookup_section_flags bfd_generic_lookup_section_flags
 #define os9k_bfd_merge_sections bfd_generic_merge_sections
 #define os9k_bfd_is_group_section bfd_generic_is_group_section
diff --git a/bfd/ieee.c b/bfd/ieee.c
index 256e8f6..1639ed1 100644
--- a/bfd/ieee.c
+++ b/bfd/ieee.c
@@ -3754,6 +3754,7 @@ ieee_sizeof_headers (bfd *abfd ATTRIBUTE_UNUSED,
   bfd_generic_get_relocated_section_contents
 #define ieee_bfd_relax_section bfd_generic_relax_section
 #define ieee_bfd_gc_sections bfd_generic_gc_sections
+#define ieee_bfd_parse_eh_frame_entries	bfd_generic_parse_eh_frame_entries
 #define ieee_bfd_lookup_section_flags bfd_generic_lookup_section_flags
 #define ieee_bfd_merge_sections bfd_generic_merge_sections
 #define ieee_bfd_is_group_section bfd_generic_is_group_section
diff --git a/bfd/ihex.c b/bfd/ihex.c
index 9b3b813..59e8944 100644
--- a/bfd/ihex.c
+++ b/bfd/ihex.c
@@ -930,6 +930,7 @@ ihex_sizeof_headers (bfd *abfd ATTRIBUTE_UNUSED,
 #define ihex_bfd_get_relocated_section_contents   bfd_generic_get_relocated_section_contents
 #define ihex_bfd_relax_section                    bfd_generic_relax_section
 #define ihex_bfd_gc_sections                      bfd_generic_gc_sections
+#define ihex_bfd_parse_eh_frame_entries		  bfd_generic_parse_eh_frame_entries
 #define ihex_bfd_lookup_section_flags             bfd_generic_lookup_section_flags
 #define ihex_bfd_merge_sections                   bfd_generic_merge_sections
 #define ihex_bfd_is_group_section                 bfd_generic_is_group_section
diff --git a/bfd/libbfd-in.h b/bfd/libbfd-in.h
index 50a46ac..0da65af 100644
--- a/bfd/libbfd-in.h
+++ b/bfd/libbfd-in.h
@@ -462,6 +462,9 @@ extern bfd_boolean _bfd_generic_set_section_contents
 #define _bfd_nolink_bfd_gc_sections \
   ((bfd_boolean (*) (bfd *, struct bfd_link_info *)) \
    bfd_false)
+#define _bfd_nolink_bfd_parse_eh_frame_entries \
+  ((bfd_boolean (*) (bfd *, struct bfd_link_info *)) \
+   bfd_false)
 #define _bfd_nolink_bfd_lookup_section_flags \
   ((bfd_boolean (*) (struct bfd_link_info *, struct flag_info *, asection *)) \
    bfd_0)
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index 6c48f82..db83f67 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -467,6 +467,9 @@ extern bfd_boolean _bfd_generic_set_section_contents
 #define _bfd_nolink_bfd_gc_sections \
   ((bfd_boolean (*) (bfd *, struct bfd_link_info *)) \
    bfd_false)
+#define _bfd_nolink_bfd_parse_eh_frame_entries \
+  ((bfd_boolean (*) (bfd *, struct bfd_link_info *)) \
+   bfd_false)
 #define _bfd_nolink_bfd_lookup_section_flags \
   ((bfd_boolean (*) (struct bfd_link_info *, struct flag_info *, asection *)) \
    bfd_0)
@@ -2969,6 +2972,9 @@ bfd_boolean bfd_generic_relax_section
 bfd_boolean bfd_generic_gc_sections
    (bfd *, struct bfd_link_info *);
 
+bfd_boolean bfd_generic_parse_eh_frame_entries
+   (bfd *, struct bfd_link_info *);
+
 bfd_boolean bfd_generic_lookup_section_flags
    (struct bfd_link_info *, struct flag_info *, asection *);
 
diff --git a/bfd/mach-o-target.c b/bfd/mach-o-target.c
index a070e67..2f2fb77 100644
--- a/bfd/mach-o-target.c
+++ b/bfd/mach-o-target.c
@@ -47,6 +47,7 @@
 #define bfd_mach_o_bfd_set_private_flags              bfd_mach_o_bfd_set_private_flags
 #define bfd_mach_o_get_section_contents               _bfd_generic_get_section_contents
 #define bfd_mach_o_bfd_gc_sections                    bfd_generic_gc_sections
+#define bfd_mach_o_bfd_parse_eh_frame_entries	      bfd_generic_parse_eh_frame_entries
 #define bfd_mach_o_bfd_lookup_section_flags           bfd_generic_lookup_section_flags
 #define bfd_mach_o_bfd_merge_sections                 bfd_generic_merge_sections
 #define bfd_mach_o_bfd_is_group_section               bfd_generic_is_group_section
diff --git a/bfd/mmo.c b/bfd/mmo.c
index 2c74c76..42bf68a 100644
--- a/bfd/mmo.c
+++ b/bfd/mmo.c
@@ -3222,6 +3222,7 @@ mmo_write_object_contents (bfd *abfd)
 #define mmo_bfd_get_relocated_section_contents \
   bfd_generic_get_relocated_section_contents
 #define mmo_bfd_gc_sections bfd_generic_gc_sections
+#define mmo_bfd_parse_eh_frame_entries bfd_generic_parse_eh_frame_entries
 #define mmo_bfd_lookup_section_flags bfd_generic_lookup_section_flags
 #define mmo_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
 #define mmo_bfd_link_add_symbols _bfd_generic_link_add_symbols
diff --git a/bfd/nlm-target.h b/bfd/nlm-target.h
index 2343cff..17e6a41 100644
--- a/bfd/nlm-target.h
+++ b/bfd/nlm-target.h
@@ -45,6 +45,7 @@
 #define nlm_bfd_get_relocated_section_contents  bfd_generic_get_relocated_section_contents
 #define nlm_bfd_relax_section                   bfd_generic_relax_section
 #define nlm_bfd_gc_sections                     bfd_generic_gc_sections
+#define nlm_bfd_parse_eh_frame_entries		bfd_generic_parse_eh_frame_entries
 #define nlm_bfd_lookup_section_flags		bfd_generic_lookup_section_flags
 #define nlm_bfd_merge_sections                  bfd_generic_merge_sections
 #define nlm_bfd_is_group_section                bfd_generic_is_group_section
diff --git a/bfd/oasys.c b/bfd/oasys.c
index 9ff9b9e..e8a26fe 100644
--- a/bfd/oasys.c
+++ b/bfd/oasys.c
@@ -1180,6 +1180,7 @@ oasys_sizeof_headers (bfd *abfd ATTRIBUTE_UNUSED,
 #define oasys_bfd_get_relocated_section_contents   bfd_generic_get_relocated_section_contents
 #define oasys_bfd_relax_section                    bfd_generic_relax_section
 #define oasys_bfd_gc_sections                      bfd_generic_gc_sections
+#define oasys_bfd_parse_eh_frame_entries	   bfd_generic_parse_eh_frame_entries
 #define oasys_bfd_lookup_section_flags             bfd_generic_lookup_section_flags
 #define oasys_bfd_merge_sections                   bfd_generic_merge_sections
 #define oasys_bfd_is_group_section                 bfd_generic_is_group_section
diff --git a/bfd/pef.c b/bfd/pef.c
index 4c29417..71fbd50 100644
--- a/bfd/pef.c
+++ b/bfd/pef.c
@@ -52,6 +52,7 @@
 #define bfd_pef_bfd_get_relocated_section_contents  bfd_generic_get_relocated_section_contents
 #define bfd_pef_bfd_relax_section                   bfd_generic_relax_section
 #define bfd_pef_bfd_gc_sections                     bfd_generic_gc_sections
+#define bfd_pef_bfd_parse_eh_frame_entries	    bfd_generic_parse_eh_frame_entries
 #define bfd_pef_bfd_lookup_section_flags            bfd_generic_lookup_section_flags
 #define bfd_pef_bfd_merge_sections                  bfd_generic_merge_sections
 #define bfd_pef_bfd_is_group_section		    bfd_generic_is_group_section
diff --git a/bfd/plugin.c b/bfd/plugin.c
index a068861..5821455 100644
--- a/bfd/plugin.c
+++ b/bfd/plugin.c
@@ -97,6 +97,7 @@ dlerror (void)
 #define bfd_plugin_bfd_link_just_syms                 _bfd_generic_link_just_syms
 #define bfd_plugin_bfd_final_link                     _bfd_generic_final_link
 #define bfd_plugin_bfd_link_split_section             _bfd_generic_link_split_section
+#define bfd_plugin_bfd_parse_eh_frame_entries         bfd_generic_parse_eh_frame_entries
 #define bfd_plugin_bfd_gc_sections                    bfd_generic_gc_sections
 #define bfd_plugin_bfd_lookup_section_flags           bfd_generic_lookup_section_flags
 #define bfd_plugin_bfd_merge_sections                 bfd_generic_merge_sections
diff --git a/bfd/ppcboot.c b/bfd/ppcboot.c
index 9ee8ab5..b5d127e 100644
--- a/bfd/ppcboot.c
+++ b/bfd/ppcboot.c
@@ -447,6 +447,7 @@ ppcboot_bfd_print_private_bfd_data (bfd *abfd, void * farg)
   bfd_generic_get_relocated_section_contents
 #define ppcboot_bfd_relax_section bfd_generic_relax_section
 #define ppcboot_bfd_gc_sections bfd_generic_gc_sections
+#define ppcboot_bfd_parse_eh_frame_entries bfd_generic_parse_eh_frame_entries
 #define ppcboot_bfd_lookup_section_flags bfd_generic_lookup_section_flags
 #define ppcboot_bfd_merge_sections bfd_generic_merge_sections
 #define ppcboot_bfd_is_group_section bfd_generic_is_group_section
diff --git a/bfd/reloc.c b/bfd/reloc.c
index dc47173..80ebeea 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -7477,6 +7477,26 @@ bfd_generic_gc_sections (bfd *abfd ATTRIBUTE_UNUSED,
 
 /*
 INTERNAL_FUNCTION
+	bfd_generic_parse_eh_frame_entries
+
+SYNOPSIS
+	bfd_boolean bfd_generic_parse_eh_frame_entries
+	  (bfd *, struct bfd_link_info *);
+
+DESCRIPTION
+	Provides default handling for backends that do not
+	support compact EH.
+*/
+
+bfd_boolean
+bfd_generic_parse_eh_frame_entries (bfd *abfd ATTRIBUTE_UNUSED,
+				    struct bfd_link_info *info ATTRIBUTE_UNUSED)
+{
+  return TRUE;
+}
+
+/*
+INTERNAL_FUNCTION
 	bfd_generic_lookup_section_flags
 
 SYNOPSIS
diff --git a/bfd/section.c b/bfd/section.c
index b27539a..48c85d2 100644
--- a/bfd/section.c
+++ b/bfd/section.c
@@ -386,6 +386,7 @@ CODE_FRAGMENT
 .#define SEC_INFO_TYPE_EH_FRAME  3
 .#define SEC_INFO_TYPE_JUST_SYMS 4
 .#define SEC_INFO_TYPE_TARGET    5
+.#define SEC_INFO_TYPE_EH_FRAME_ENTRY 6
 .
 .  {* Nonzero if this section uses RELA relocations, rather than REL.  *}
 .  unsigned int use_rela_p:1;
diff --git a/bfd/som.c b/bfd/som.c
index 513e4fa..d30f66a 100644
--- a/bfd/som.c
+++ b/bfd/som.c
@@ -6743,6 +6743,7 @@ som_bfd_link_split_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec)
   _bfd_generic_copy_link_hash_symbol_type
 #define som_bfd_final_link                      _bfd_generic_final_link
 #define som_bfd_gc_sections		        bfd_generic_gc_sections
+#define som_bfd_parse_eh_frame_entries		bfd_generic_parse_eh_frame_entries
 #define som_bfd_lookup_section_flags            bfd_generic_lookup_section_flags
 #define som_bfd_merge_sections		        bfd_generic_merge_sections
 #define som_bfd_is_group_section	        bfd_generic_is_group_section
diff --git a/bfd/srec.c b/bfd/srec.c
index 5f9a546..b68e600 100644
--- a/bfd/srec.c
+++ b/bfd/srec.c
@@ -1265,6 +1265,7 @@ srec_print_symbol (bfd *abfd,
 #define srec_bfd_get_relocated_section_contents   bfd_generic_get_relocated_section_contents
 #define srec_bfd_relax_section                    bfd_generic_relax_section
 #define srec_bfd_gc_sections                      bfd_generic_gc_sections
+#define srec_bfd_parse_eh_frame_entries		  bfd_generic_parse_eh_frame_entries
 #define srec_bfd_lookup_section_flags             bfd_generic_lookup_section_flags
 #define srec_bfd_merge_sections                   bfd_generic_merge_sections
 #define srec_bfd_is_group_section                 bfd_generic_is_group_section
diff --git a/bfd/targets.c b/bfd/targets.c
index 8323e92..1285aaa 100644
--- a/bfd/targets.c
+++ b/bfd/targets.c
@@ -448,6 +448,7 @@ BFD_JUMP_TABLE macros.
 .  NAME##_bfd_final_link, \
 .  NAME##_bfd_link_split_section, \
 .  NAME##_bfd_gc_sections, \
+.  NAME##_bfd_parse_eh_frame_entries, \
 .  NAME##_bfd_lookup_section_flags, \
 .  NAME##_bfd_merge_sections, \
 .  NAME##_bfd_is_group_section, \
@@ -491,6 +492,9 @@ BFD_JUMP_TABLE macros.
 .  {* Remove sections that are not referenced from the output.  *}
 .  bfd_boolean (*_bfd_gc_sections) (bfd *, struct bfd_link_info *);
 .
+.  {* Parse .eh_frame_entry sections. *}
+.  bfd_boolean (*_bfd_parse_eh_frame_entries) (bfd *, struct bfd_link_info *);
+.
 .  {* Sets the bitmask of allowed and disallowed section flags.  *}
 .  bfd_boolean (*_bfd_lookup_section_flags) (struct bfd_link_info *,
 .					     struct flag_info *,
diff --git a/bfd/tekhex.c b/bfd/tekhex.c
index 85f5593..42e819b 100644
--- a/bfd/tekhex.c
+++ b/bfd/tekhex.c
@@ -946,6 +946,7 @@ tekhex_print_symbol (bfd *abfd,
 #define tekhex_bfd_get_relocated_section_contents   bfd_generic_get_relocated_section_contents
 #define tekhex_bfd_relax_section                    bfd_generic_relax_section
 #define tekhex_bfd_gc_sections                      bfd_generic_gc_sections
+#define tekhex_bfd_parse_eh_frame_entries	    bfd_generic_parse_eh_frame_entries
 #define tekhex_bfd_lookup_section_flags		    bfd_generic_lookup_section_flags
 #define tekhex_bfd_merge_sections                   bfd_generic_merge_sections
 #define tekhex_bfd_is_group_section                 bfd_generic_is_group_section
diff --git a/bfd/verilog.c b/bfd/verilog.c
index c1c5458..54e2a74 100644
--- a/bfd/verilog.c
+++ b/bfd/verilog.c
@@ -306,6 +306,7 @@ verilog_mkobject (bfd *abfd)
 #define verilog_bfd_get_relocated_section_contents   bfd_generic_get_relocated_section_contents
 #define verilog_bfd_relax_section                    bfd_generic_relax_section
 #define verilog_bfd_gc_sections                      bfd_generic_gc_sections
+#define verilog_bfd_parse_eh_frame_entries	     bfd_generic_parse_eh_frame_entries
 #define verilog_bfd_merge_sections                   bfd_generic_merge_sections
 #define verilog_bfd_is_group_section                 bfd_generic_is_group_section
 #define verilog_bfd_discard_group                    bfd_generic_discard_group
diff --git a/bfd/versados.c b/bfd/versados.c
index 42bf36c..36e6bed 100644
--- a/bfd/versados.c
+++ b/bfd/versados.c
@@ -806,6 +806,7 @@ versados_canonicalize_reloc (bfd *abfd,
 #define versados_bfd_get_relocated_section_contents   bfd_generic_get_relocated_section_contents
 #define versados_bfd_relax_section                    bfd_generic_relax_section
 #define versados_bfd_gc_sections                      bfd_generic_gc_sections
+#define versados_bfd_parse_eh_frame_entries	      bfd_generic_parse_eh_frame_entries
 #define versados_bfd_lookup_section_flags             bfd_generic_lookup_section_flags
 #define versados_bfd_merge_sections                   bfd_generic_merge_sections
 #define versados_bfd_is_group_section                 bfd_generic_is_group_section
diff --git a/bfd/vms-alpha.c b/bfd/vms-alpha.c
index 5ff5bfd..28166fd 100644
--- a/bfd/vms-alpha.c
+++ b/bfd/vms-alpha.c
@@ -9209,6 +9209,7 @@ bfd_vms_get_data (bfd *abfd)
 
 #define alpha_vms_bfd_relax_section bfd_generic_relax_section
 #define alpha_vms_bfd_gc_sections bfd_generic_gc_sections
+#define alpha_vms_bfd_parse_eh_frame_entries bfd_generic_parse_eh_frame_entries
 #define alpha_vms_bfd_lookup_section_flags bfd_generic_lookup_section_flags
 #define alpha_vms_bfd_merge_sections bfd_generic_merge_sections
 #define alpha_vms_bfd_is_group_section bfd_generic_is_group_section
diff --git a/bfd/xsym.c b/bfd/xsym.c
index 4e2a8ab..a9417ec 100644
--- a/bfd/xsym.c
+++ b/bfd/xsym.c
@@ -44,6 +44,7 @@
 #define bfd_sym_bfd_get_relocated_section_contents  bfd_generic_get_relocated_section_contents
 #define bfd_sym_bfd_relax_section                   bfd_generic_relax_section
 #define bfd_sym_bfd_gc_sections                     bfd_generic_gc_sections
+#define bfd_sym_bfd_parse_eh_frame_entries	    bfd_generic_parse_eh_frame_entries
 #define bfd_sym_bfd_lookup_section_flags            bfd_generic_lookup_section_flags
 #define bfd_sym_bfd_merge_sections                  bfd_generic_merge_sections
 #define bfd_sym_bfd_is_group_section                bfd_generic_is_group_section
diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c
index c9266db..452b1ec 100644
--- a/gas/config/tc-mips.c
+++ b/gas/config/tc-mips.c
@@ -1333,7 +1333,6 @@ static void s_tpreldword (int);
 static void s_gpvalue (int);
 static void s_gpword (int);
 static void s_gpdword (int);
-static void s_ehword (int);
 static void s_cpadd (int);
 static void s_insn (int);
 static void s_nan (int);
@@ -1757,7 +1756,6 @@ static const pseudo_typeS mips_pseudo_table[] =
   {"gpvalue", s_gpvalue, 0},
   {"gpword", s_gpword, 0},
   {"gpdword", s_gpdword, 0},
-  {"ehword", s_ehword, 0},
   {"cpadd", s_cpadd, 0},
   {"insn", s_insn, 0},
   {"nan", s_nan, 0},
@@ -16275,34 +16273,6 @@ s_gpdword (int ignore ATTRIBUTE_UNUSED)
   demand_empty_rest_of_line ();
 }
 
-/* Handle the .ehword pseudo-op.  This is used when generating unwinding
-   tables.  It generates a R_MIPS_EH reloc.  */
-
-static void
-s_ehword (int ignore ATTRIBUTE_UNUSED)
-{
-  expressionS ex;
-  char *p;
-
-  mips_emit_delays ();
-
-  expression (&ex);
-  mips_clear_insn_labels ();
-
-  if (ex.X_op != O_symbol || ex.X_add_number != 0)
-    {
-      as_bad (_("unsupported use of .ehword"));
-      ignore_rest_of_line ();
-    }
-
-  p = frag_more (4);
-  md_number_to_chars (p, 0, 4);
-  fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &ex, FALSE,
-	       BFD_RELOC_MIPS_EH);
-
-  demand_empty_rest_of_line ();
-}
-
 /* Handle the .cpadd pseudo-op.  This is used when dealing with switch
    tables in SVR4 PIC code.  */
 
@@ -19108,3 +19078,13 @@ md_mips_end (void)
 				Tag_GNU_MIPS_ABI_FP, fpabi);
     }
 }
+
+/*  Returns the relocation type required for a particular CFI encoding.  */
+
+bfd_reloc_code_real_type
+mips_cfi_reloc_for_encoding (int encoding)
+{
+  if (encoding == (DW_EH_PE_sdata4 | DW_EH_PE_pcrel))
+    return BFD_RELOC_32_PCREL;
+  else return BFD_RELOC_NONE;
+}
diff --git a/gas/config/tc-mips.h b/gas/config/tc-mips.h
index 0b8e607..6b89e1c 100644
--- a/gas/config/tc-mips.h
+++ b/gas/config/tc-mips.h
@@ -176,7 +176,9 @@ extern enum dwarf2_format mips_dwarf2_format (asection *);
 
 extern int mips_dwarf2_addr_size (void);
 #define DWARF2_ADDR_SIZE(bfd) mips_dwarf2_addr_size ()
-#define DWARF2_FDE_RELOC_SIZE mips_dwarf2_addr_size ()
+#define DWARF2_FDE_RELOC_SIZE (compact_eh ? 4 : mips_dwarf2_addr_size ())
+#define DWARF2_FDE_RELOC_ENCODING(enc) \
+  (enc | (compact_eh ? DW_EH_PE_pcrel : 0))
 
 #define TARGET_USE_CFIPOP 1
 
@@ -197,4 +199,10 @@ extern int tc_mips_regname_to_dw2regnum (char *regname);
 #define CONVERT_SYMBOLIC_ATTRIBUTE(name) mips_convert_symbolic_attribute (name)
 extern int mips_convert_symbolic_attribute (const char *);
 
+#define tc_cfi_reloc_for_encoding mips_cfi_reloc_for_encoding
+extern bfd_reloc_code_real_type mips_cfi_reloc_for_encoding (int encoding);
+
+#define tc_compact_eh_opcode_stop 0x5c
+#define tc_compact_eh_opcode_pad 0x5f
+
 #endif /* TC_MIPS */
diff --git a/gas/doc/as.texinfo b/gas/doc/as.texinfo
index 243851b..575b97c 100644
--- a/gas/doc/as.texinfo
+++ b/gas/doc/as.texinfo
@@ -4595,6 +4595,7 @@ unwind entry previously opened by
 @code{.cfi_startproc}, and emits it to @code{.eh_frame}.
 
 @section @code{.cfi_personality @var{encoding} [, @var{exp}]}
+@cindex @code{cfi_personality} directive
 @code{.cfi_personality} defines personality routine and its encoding.
 @var{encoding} must be a constant determining how the personality
 should be encoded.  If it is 255 (@code{DW_EH_PE_omit}), second
@@ -4605,13 +4606,29 @@ can be loaded from, not the personality routine itself.
 The default after @code{.cfi_startproc} is @code{.cfi_personality 0xff},
 no personality routine.
 
+@section @code{.cfi_personality_id @var{id}}
+@cindex @code{cfi_personality_id} directive
+@code{cfi_personality_id} defines a personality routine by its index as
+defined in a compact unwinding format.
+Only valid when generating compact EH frames (i.e.
+with @code{.cfi_sections eh_frame_entry}.
+
+@section @code{.cfi_fde_data [@var{opcode1} [, @dots{}]]}
+@cindex @code{cfi_fde_data} directive
+@code{cfi_fde_data} is used to describe the compact unwind opcodes to be
+used for the current function.  These are emitted inline in the
+@code{.eh_frame_entry} section if small enough and there is no LSDA, or
+in the @code{.gnu.extab} section otherwise.
+Only valid when generating compact EH frames (i.e.
+with @code{.cfi_sections eh_frame_entry}.
+
 @section @code{.cfi_lsda @var{encoding} [, @var{exp}]}
 @code{.cfi_lsda} defines LSDA and its encoding.
 @var{encoding} must be a constant determining how the LSDA
-should be encoded.  If it is 255 (@code{DW_EH_PE_omit}), second
-argument is not present, otherwise second argument should be a constant
+should be encoded.  If it is 255 (@code{DW_EH_PE_omit}), the second
+argument is not present, otherwise the second argument should be a constant
 or a symbol name.  The default after @code{.cfi_startproc} is @code{.cfi_lsda 0xff},
-no LSDA.
+meaning that no LSDA is present.
 
 @section @code{.cfi_def_cfa @var{register}, @var{offset}}
 @code{.cfi_def_cfa} defines a rule for computing CFA as: @i{take
diff --git a/gas/doc/internals.texi b/gas/doc/internals.texi
index cc4089b..9dd0bd0 100644
--- a/gas/doc/internals.texi
+++ b/gas/doc/internals.texi
@@ -1473,6 +1473,10 @@ completed, but before the relocations have been generated.
 If you define this macro, GAS will call it after the relocs have been
 generated.
 
+@item tc_cfi_reloc_for_encoding
+@cindex tc_cfi_reloc_for_encoding
+You may define this macro to indicate whether a cfi encoding requires a relocation.
+
 @item md_post_relax_hook
 If you define this macro, GAS will call it after relaxing and sizing the
 segments.
diff --git a/gas/dw2gencfi.c b/gas/dw2gencfi.c
index 0f54fec..0d8cac0 100644
--- a/gas/dw2gencfi.c
+++ b/gas/dw2gencfi.c
@@ -75,6 +75,8 @@
 # define tc_cfi_endproc(fde) ((void) (fde))
 #endif
 
+#define EH_FRAME_LINKONCE (SUPPORT_FRAME_LINKONCE || compact_eh)
+
 #ifndef DWARF2_FORMAT
 #define DWARF2_FORMAT(SEC) dwarf2_format_32bit
 #endif
@@ -83,7 +85,7 @@
 #define DWARF2_ADDR_SIZE(bfd) (bfd_arch_bits_per_address (bfd) / 8)
 #endif
 
-#if SUPPORT_FRAME_LINKONCE
+#if MULTIPLE_FRAME_SECTIONS
 #define CUR_SEG(structp) structp->cur_seg
 #define SET_CUR_SEG(structp, seg) structp->cur_seg = seg
 #define HANDLED(structp) structp->handled
@@ -95,6 +97,10 @@
 #define SET_HANDLED(structp, val) (void) (0 && val)
 #endif
 
+#ifndef tc_cfi_reloc_for_encoding
+#define tc_cfi_reloc_for_encoding(e) BFD_RELOC_NONE
+#endif
+
 /* Private segment collection list.  */
 struct dwcfi_seg_list
 {
@@ -103,10 +109,117 @@ struct dwcfi_seg_list
   char * seg_name;
 };
 
-#define FRAME_NAME ".eh_frame"
+#ifdef SUPPORT_COMPACT_EH
+static bfd_boolean compact_eh;
+#else
+#define compact_eh 0
+#endif
 
 static struct hash_control *dwcfi_hash;
+\f
+/* Emit a single byte into the current segment.  */
+
+static inline void
+out_one (int byte)
+{
+  FRAG_APPEND_1_CHAR (byte);
+}
+
+/* Emit a two-byte word into the current segment.  */
+
+static inline void
+out_two (int data)
+{
+  md_number_to_chars (frag_more (2), data, 2);
+}
+
+/* Emit a four byte word into the current segment.  */
+
+static inline void
+out_four (int data)
+{
+  md_number_to_chars (frag_more (4), data, 4);
+}
+
+/* Emit an unsigned "little-endian base 128" number.  */
+
+static void
+out_uleb128 (addressT value)
+{
+  output_leb128 (frag_more (sizeof_leb128 (value, 0)), value, 0);
+}
+
+/* Emit an unsigned "little-endian base 128" number.  */
+
+static void
+out_sleb128 (offsetT value)
+{
+  output_leb128 (frag_more (sizeof_leb128 (value, 1)), value, 1);
+}
+
+/* Return the value of the EH encoding size.  */
 
+static offsetT
+eh_encoding_size (unsigned char encoding)
+{
+  if (encoding == DW_EH_PE_omit)
+    return 0;
+  switch (encoding & 0x7)
+    {
+    case 0:
+      return bfd_get_arch_size (stdoutput) == 64 ? 8 : 4;
+    case DW_EH_PE_udata2:
+      return 2;
+    case DW_EH_PE_udata4:
+      return 4;
+    case DW_EH_PE_udata8:
+      return 8;
+    default:
+      abort ();
+    }
+}
+
+/* Emit expression EXP in ENCODING.  If EMIT_ENCODING is true, first
+   emit a byte containing ENCODING.  */
+
+static void
+emit_expr_encoded (expressionS *exp, int encoding, bfd_boolean emit_encoding)
+{
+  offsetT size = eh_encoding_size (encoding);
+  bfd_reloc_code_real_type code;
+
+  if (encoding == DW_EH_PE_omit)
+    return;
+
+  if (emit_encoding)
+    out_one (encoding);
+
+  code = tc_cfi_reloc_for_encoding (encoding);
+  if (code != BFD_RELOC_NONE)
+    {
+      reloc_howto_type *howto = bfd_reloc_type_lookup (stdoutput, code);
+      char *p = frag_more (size);
+      md_number_to_chars (p, 0, size);
+      fix_new (frag_now, p - frag_now->fr_literal, size, exp->X_add_symbol,
+	       exp->X_add_number, howto->pc_relative, code);
+    }
+  else if ((encoding & 0x70) == DW_EH_PE_pcrel)
+    {
+#if CFI_DIFF_EXPR_OK
+      expressionS tmp = *exp;
+      tmp.X_op = O_subtract;
+      tmp.X_op_symbol = symbol_temp_new_now ();
+      emit_expr (&tmp, size);
+#elif defined (tc_cfi_emit_pcrel_expr)
+      tc_cfi_emit_pcrel_expr (exp, size);
+#else
+      abort ();
+#endif
+    }
+  else
+    emit_expr (exp, size);
+}
+\f
 /* Build based on segment the derived .debug_...
    segment name containing origin segment's postfix name part.  */
 
@@ -128,7 +241,15 @@ get_debugseg_name (segT seg, const char *base_name)
       dot = strchr (name + 1, '.');
 
       if (!dollar && !dot)
-	name = "";
+	{
+	  /* Ensure that a uniquely named .eh_frame_entry section
+	     is created for each text section.  */
+	  if (compact_eh
+	       && !strcmp (base_name, ".eh_frame_entry")
+	       && strcmp (name, ".text") != 0)
+	    return concat (base_name, ".", name, NULL);
+	  name = "";
+	}
       else if (!dollar)
 	name = dot;
       else if (!dot)
@@ -160,6 +281,11 @@ alloc_debugseg_item (segT seg, int subseg, char *name)
 static segT
 is_now_linkonce_segment (void)
 {
+  /* We track the current segment in the cfi_insn_data struct and
+     the cfi_insn_data struct for Compact EH.  */
+  if (compact_eh)
+    return now_seg;
+
   if ((bfd_get_section_flags (stdoutput, now_seg)
        & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD
 	  | SEC_LINK_DUPLICATES_ONE_ONLY | SEC_LINK_DUPLICATES_SAME_SIZE
@@ -272,12 +398,13 @@ struct cfi_escape_data
 struct cie_entry
 {
   struct cie_entry *next;
-#if SUPPORT_FRAME_LINKONCE
+#if MULTIPLE_FRAME_SECTIONS
   segT cur_seg;
 #endif
   symbolS *start_address;
   unsigned int return_column;
   unsigned int signal_frame;
+  unsigned char fde_encoding;
   unsigned char per_encoding;
   unsigned char lsda_encoding;
   expressionS personality;
@@ -547,15 +674,19 @@ static void dot_cfi_escape (int);
 static void dot_cfi_sections (int);
 static void dot_cfi_startproc (int);
 static void dot_cfi_endproc (int);
+static void dot_cfi_fde_data (int);
 static void dot_cfi_personality (int);
+static void dot_cfi_personality_id (int);
 static void dot_cfi_lsda (int);
 static void dot_cfi_val_encoded_addr (int);
+static void dot_cfi_inline_lsda (int);
 
 const pseudo_typeS cfi_pseudo_table[] =
   {
     { "cfi_sections", dot_cfi_sections, 0 },
     { "cfi_startproc", dot_cfi_startproc, 0 },
     { "cfi_endproc", dot_cfi_endproc, 0 },
+    { "cfi_fde_data", dot_cfi_fde_data, 0 },
     { "cfi_def_cfa", dot_cfi, DW_CFA_def_cfa },
     { "cfi_def_cfa_register", dot_cfi, DW_CFA_def_cfa_register },
     { "cfi_def_cfa_offset", dot_cfi, DW_CFA_def_cfa_offset },
@@ -573,8 +704,10 @@ const pseudo_typeS cfi_pseudo_table[] =
     { "cfi_escape", dot_cfi_escape, 0 },
     { "cfi_signal_frame", dot_cfi, CFI_signal_frame },
     { "cfi_personality", dot_cfi_personality, 0 },
+    { "cfi_personality_id", dot_cfi_personality_id, 0 },
     { "cfi_lsda", dot_cfi_lsda, 0 },
     { "cfi_val_encoded_addr", dot_cfi_val_encoded_addr, 0 },
+    { "cfi_inline_lsda", dot_cfi_inline_lsda, 0 },
     { NULL, NULL, 0 }
   };
 
@@ -832,14 +965,15 @@ dot_cfi_personality (int ignored ATTRIBUTE_UNUSED)
     }
 
   if ((encoding & 0xff) != encoding
-      || ((encoding & 0x70) != 0
+      || ((((encoding & 0x70) != 0
 #if CFI_DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr
-	  && (encoding & 0x70) != DW_EH_PE_pcrel
+	   && (encoding & 0x70) != DW_EH_PE_pcrel
 #endif
 	  )
 	 /* leb128 can be handled, but does something actually need it?  */
-      || (encoding & 7) == DW_EH_PE_uleb128
-      || (encoding & 7) > DW_EH_PE_udata8)
+	   || (encoding & 7) == DW_EH_PE_uleb128
+	   || (encoding & 7) > DW_EH_PE_udata8)
+	&& tc_cfi_reloc_for_encoding (encoding) == BFD_RELOC_NONE))
     {
       as_bad (_("invalid or unsupported encoding in .cfi_personality"));
       ignore_rest_of_line ();
@@ -902,14 +1036,15 @@ dot_cfi_lsda (int ignored ATTRIBUTE_UNUSED)
     }
 
   if ((encoding & 0xff) != encoding
-      || ((encoding & 0x70) != 0
+      || ((((encoding & 0x70) != 0
 #if CFI_DIFF_LSDA_OK || defined tc_cfi_emit_pcrel_expr
-	  && (encoding & 0x70) != DW_EH_PE_pcrel
+	    && (encoding & 0x70) != DW_EH_PE_pcrel
 #endif
-	  )
+	   )
 	 /* leb128 can be handled, but does something actually need it?  */
-      || (encoding & 7) == DW_EH_PE_uleb128
-      || (encoding & 7) > DW_EH_PE_udata8)
+	   || (encoding & 7) == DW_EH_PE_uleb128
+	   || (encoding & 7) > DW_EH_PE_udata8)
+	  && tc_cfi_reloc_for_encoding (encoding) == BFD_RELOC_NONE))
     {
       as_bad (_("invalid or unsupported encoding in .cfi_lsda"));
       ignore_rest_of_line ();
@@ -1017,10 +1152,14 @@ 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)
+#define CFI_EMIT_eh_frame		(1 << 0)
+#define CFI_EMIT_debug_frame		(1 << 1)
+#define CFI_EMIT_target			(1 << 2)
+#define CFI_EMIT_eh_frame_compact	(1 << 3)
+static bfd_boolean cfi_sections_set = FALSE;
 static int cfi_sections = CFI_EMIT_eh_frame;
+static int all_cfi_sections = 0;
+static struct fde_entry *last_fde;
 
 static void
 dot_cfi_sections (int ignored ATTRIBUTE_UNUSED)
@@ -1041,6 +1180,13 @@ dot_cfi_sections (int ignored ATTRIBUTE_UNUSED)
 	  sections |= CFI_EMIT_eh_frame;
 	else if (strncmp (name, ".debug_frame", sizeof ".debug_frame") == 0)
 	  sections |= CFI_EMIT_debug_frame;
+#if SUPPORT_COMPACT_EH
+	else if (strncmp (name, ".eh_frame_entry", sizeof ".eh_frame_entry") == 0)
+	  {
+	    compact_eh = TRUE;
+	    sections |= CFI_EMIT_eh_frame_compact;
+	  }
+#endif
 #ifdef tc_cfi_section_name
 	else if (strcmp (name, tc_cfi_section_name) == 0)
 	  sections |= CFI_EMIT_target;
@@ -1069,6 +1215,9 @@ dot_cfi_sections (int ignored ATTRIBUTE_UNUSED)
       }
 
   demand_empty_rest_of_line ();
+  if (cfi_sections_set && cfi_sections != sections)
+    as_bad (_("inconsistent uses of .cfi_sections"));
+  cfi_sections_set = TRUE;
   cfi_sections = sections;
 }
 
@@ -1104,6 +1253,8 @@ dot_cfi_startproc (int ignored ATTRIBUTE_UNUSED)
     }
   demand_empty_rest_of_line ();
 
+  all_cfi_sections |= cfi_sections;
+  frchain_now->frch_cfi_data->cur_fde_data->sections = cfi_sections;
   frchain_now->frch_cfi_data->cur_cfa_offset = 0;
   if (!simple)
     tc_cfi_frame_initial_instructions ();
@@ -1115,8 +1266,6 @@ dot_cfi_startproc (int ignored ATTRIBUTE_UNUSED)
 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"));
@@ -1124,58 +1273,249 @@ dot_cfi_endproc (int ignored ATTRIBUTE_UNUSED)
       return;
     }
 
-  fde = frchain_now->frch_cfi_data->cur_fde_data;
+  last_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);
+    tc_cfi_endproc (last_fde);
 }
 
-\f
-/* Emit a single byte into the current segment.  */
-
-static inline void
-out_one (int byte)
+static segT
+get_cfi_seg (segT cseg, const char *base, flagword flags, int align)
 {
-  FRAG_APPEND_1_CHAR (byte);
+  if (SUPPORT_FRAME_LINKONCE || ((flags & SEC_DEBUGGING) == 0 && compact_eh))
+    {
+      struct dwcfi_seg_list *l;
+
+      l = dwcfi_hash_find_or_make (cseg, base, flags);
+
+      cseg = l->seg;
+      subseg_set (cseg, l->subseg);
+    }
+  else
+    {
+      cseg = subseg_new (base, 0);
+      bfd_set_section_flags (stdoutput, cseg, flags);
+    }
+  record_alignment (cseg, align);
+  return cseg;
 }
 
-/* Emit a two-byte word into the current segment.  */
+#if SUPPORT_COMPACT_EH
 
-static inline void
-out_two (int data)
+/* Set the personality id in the FDE structure.  */
+
+static void
+dot_cfi_personality_id (int ignored ATTRIBUTE_UNUSED)
 {
-  md_number_to_chars (frag_more (2), data, 2);
+  struct fde_entry *fde;
+
+  if (frchain_now->frch_cfi_data == NULL)
+    {
+      as_bad (_("CFI instruction used without previous .cfi_startproc"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  fde = frchain_now->frch_cfi_data->cur_fde_data;
+  fde->personality_id = cfi_parse_const ();
+  demand_empty_rest_of_line ();
+
+  if (fde->personality_id == 0 || fde->personality_id > 3)
+    as_bad (_("wrong argument to .cfi_personality_id"));
 }
 
-/* Emit a four byte word into the current segment.  */
+static void
+dot_cfi_fde_data (int ignored ATTRIBUTE_UNUSED)
+{
+  if (frchain_now->frch_cfi_data == NULL)
+    {
+      as_bad (_(".cfi_fde_data without corresponding .cfi_startproc"));
+      ignore_rest_of_line ();
+      return;
+    }
 
-static inline void
-out_four (int data)
+  last_fde = frchain_now->frch_cfi_data->cur_fde_data;
+
+/*  Check target here.  */
+  if ((cfi_sections & CFI_EMIT_target) != 0
+      || (cfi_sections & CFI_EMIT_eh_frame_compact) != 0)
+    {
+      struct cfi_escape_data *head, **tail, *e;
+      int num_ops = 0;
+
+      tail = &head;
+      if (!is_it_end_of_statement ())
+	{
+	  num_ops = 0;
+	  do
+	    {
+	      e = (struct cfi_escape_data *) xmalloc (sizeof (*e));
+	      do_parse_cons_expression (&e->exp, 1);
+	      *tail = e;
+	      tail = &e->next;
+	      num_ops++;
+	    }
+	  while (*input_line_pointer++ == ',');
+	  --input_line_pointer;
+	}
+      *tail = NULL;
+
+      if (last_fde->lsda_encoding != DW_EH_PE_omit)
+	last_fde->eh_header_type = EH_COMPACT_HAS_LSDA;
+      else if (num_ops <= 3 && last_fde->per_encoding == DW_EH_PE_omit)
+	last_fde->eh_header_type = EH_COMPACT_INLINE;
+      else
+	last_fde->eh_header_type = EH_COMPACT_OUTLINE;
+
+      if (last_fde->eh_header_type == EH_COMPACT_INLINE)
+	num_ops = 3;
+
+      last_fde->eh_data_size = num_ops;
+      last_fde->eh_data = (bfd_byte *) xmalloc (num_ops);
+      num_ops = 0;
+      while (head)
+	{
+	  e = head;
+	  head = e->next;
+	  last_fde->eh_data[num_ops++] = e->exp.X_add_number;
+	  free (e);
+	}
+      if (last_fde->eh_header_type == EH_COMPACT_INLINE)
+	while (num_ops < 3)
+	  last_fde->eh_data[num_ops++] = tc_compact_eh_opcode_stop;
+    }
+
+  demand_empty_rest_of_line ();
+}
+static void
+output_compact_unwind_data (struct fde_entry *fde, int align)
 {
-  md_number_to_chars (frag_more (4), data, 4);
+  /* Add two bytes for the personality encoding and the stop byte.  */
+  int data_size = fde->eh_data_size + 2;
+  int align_padding;
+  int amask;
+  char *p;
+
+  fde->eh_loc = symbol_temp_new_now ();
+
+  p = frag_more (1);
+  if (fde->personality_id != 0)
+    *p = fde->personality_id;
+  else if (fde->per_encoding != DW_EH_PE_omit)
+    {
+      *p = 0;
+      emit_expr_encoded (&fde->personality, fde->per_encoding, FALSE);
+      data_size += eh_encoding_size (fde->per_encoding);
+    }
+  else
+    *p = 1;
+
+  amask = (1 << align) - 1;
+  align_padding = -data_size & amask;
+
+  p = frag_more (fde->eh_data_size + 1 + align_padding);
+  memcpy (p, fde->eh_data, fde->eh_data_size);
+  p += fde->eh_data_size;
+
+  while (align_padding-- > 0)
+    *(p++) = tc_compact_eh_opcode_pad;
+
+  *(p++) = tc_compact_eh_opcode_stop;
+  fde->eh_header_type = EH_COMPACT_OUTLINE_DONE;
 }
 
-/* Emit an unsigned "little-endian base 128" number.  */
+/* Handle the .cfi_inline_lsda directive.  */
 
 static void
-out_uleb128 (addressT value)
+dot_cfi_inline_lsda (int ignored ATTRIBUTE_UNUSED)
 {
-  output_leb128 (frag_more (sizeof_leb128 (value, 0)), value, 0);
-}
+  segT ccseg;
+  int align;
+  long max_alignment = 28;
 
-/* Emit an unsigned "little-endian base 128" number.  */
+  if (!last_fde)
+    {
+      as_bad (_("unexpected .cfi_inline_lsda"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  if ((last_fde->sections & CFI_EMIT_eh_frame_compact) == 0)
+    {
+      as_bad (_(".cfi_inline_lsda not valid for this frame"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  if (last_fde->eh_header_type != EH_COMPACT_LEGACY
+      && last_fde->eh_header_type != EH_COMPACT_HAS_LSDA)
+    {
+      as_bad (_(".cfi_inline_lsda seen for frame without .cfi_lsda"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+#ifdef md_flush_pending_output
+  md_flush_pending_output ();
+#endif
+
+  align = get_absolute_expression ();
+  if (align > max_alignment)
+    {
+      align = max_alignment;
+      as_bad (_("Alignment too large: %d assumed."), align);
+    }
+  else if (align < 0)
+    {
+      as_warn (_("Alignment negative: 0 assumed."));
+      align = 0;
+    }
+
+  demand_empty_rest_of_line ();
+  ccseg = CUR_SEG (last_fde);
+  /* Open .gnu_extab section.  */
+  get_cfi_seg (ccseg, ".gnu_extab",
+	       (SEC_ALLOC | SEC_LOAD | SEC_DATA
+		| DWARF2_EH_FRAME_READ_ONLY),
+	       1);
 
+  frag_align (align, 0, 0);
+  record_alignment (now_seg, align);
+  if (last_fde->eh_header_type == EH_COMPACT_HAS_LSDA)
+    output_compact_unwind_data (last_fde, align);
+
+  last_fde = NULL;
+
+  return;
+}
+#else /* !SUPPORT_COMPACT_EH */
 static void
-out_sleb128 (offsetT value)
+dot_cfi_inline_lsda (int ignored ATTRIBUTE_UNUSED)
 {
-  output_leb128 (frag_more (sizeof_leb128 (value, 1)), value, 1);
+  as_bad (_(".cfi_inline_lsda is not supported for this target"));
+  ignore_rest_of_line ();
 }
 
 static void
+dot_cfi_fde_data (int ignored ATTRIBUTE_UNUSED)
+{
+  as_bad (_(".cfi_fde_data is not supported for this target"));
+  ignore_rest_of_line ();
+}
+
+static void
+dot_cfi_personality_id (int ignored ATTRIBUTE_UNUSED)
+{
+  as_bad (_(".cfi_personality_id is not supported for this target"));
+  ignore_rest_of_line ();
+}
+#endif
+\f
+static void
 output_cfi_insn (struct cfi_insn_data *insn)
 {
   offsetT offset;
@@ -1391,26 +1731,6 @@ output_cfi_insn (struct cfi_insn_data *insn)
     }
 }
 
-static offsetT
-encoding_size (unsigned char encoding)
-{
-  if (encoding == DW_EH_PE_omit)
-    return 0;
-  switch (encoding & 0x7)
-    {
-    case 0:
-      return bfd_get_arch_size (stdoutput) == 64 ? 8 : 4;
-    case DW_EH_PE_udata2:
-      return 2;
-    case DW_EH_PE_udata4:
-      return 4;
-    case DW_EH_PE_udata8:
-      return 8;
-    default:
-      abort ();
-    }
-}
-
 static void
 output_cie (struct cie_entry *cie, bfd_boolean eh_frame, int align)
 {
@@ -1470,29 +1790,10 @@ output_cie (struct cie_entry *cie, bfd_boolean eh_frame, int align)
     {
       augmentation_size = 1 + (cie->lsda_encoding != DW_EH_PE_omit);
       if (cie->per_encoding != DW_EH_PE_omit)
-	augmentation_size += 1 + encoding_size (cie->per_encoding);
+	augmentation_size += 1 + eh_encoding_size (cie->per_encoding);
       out_uleb128 (augmentation_size);		/* Augmentation size.  */
 
-      if (cie->per_encoding != DW_EH_PE_omit)
-	{
-	  offsetT size = encoding_size (cie->per_encoding);
-	  out_one (cie->per_encoding);
-	  exp = cie->personality;
-	  if ((cie->per_encoding & 0x70) == DW_EH_PE_pcrel)
-	    {
-#if CFI_DIFF_EXPR_OK
-	      exp.X_op = O_subtract;
-	      exp.X_op_symbol = symbol_temp_new_now ();
-	      emit_expr (&exp, size);
-#elif defined (tc_cfi_emit_pcrel_expr)
-	      tc_cfi_emit_pcrel_expr (&exp, size);
-#else
-	      abort ();
-#endif
-	    }
-	  else
-	    emit_expr (&exp, size);
-	}
+      emit_expr_encoded (&cie->personality, cie->per_encoding, TRUE);
 
       if (cie->lsda_encoding != DW_EH_PE_omit)
 	out_one (cie->lsda_encoding);
@@ -1515,6 +1816,11 @@ output_cie (struct cie_entry *cie, bfd_boolean eh_frame, int align)
 #if CFI_DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr
   enc |= DW_EH_PE_pcrel;
 #endif
+#ifdef DWARF2_FDE_RELOC_ENCODING
+  /* Allow target to override encoding.  */
+  enc = DWARF2_FDE_RELOC_ENCODING (enc);
+#endif
+  cie->fde_encoding = enc;
   if (eh_frame)
     out_one (enc);
 
@@ -1575,29 +1881,43 @@ output_fde (struct fde_entry *fde, struct cie_entry *cie,
       TC_DWARF2_EMIT_OFFSET (cie->start_address, offset_size);
     }
 
+  exp.X_op = O_symbol;
+  exp.X_add_symbol = fde->start_address;
   if (eh_frame)
     {
-      exp.X_op = O_subtract;
-      exp.X_add_number = 0;
+      bfd_reloc_code_real_type code
+	= tc_cfi_reloc_for_encoding (cie->fde_encoding);
+      if (code !=  BFD_RELOC_NONE)
+	{
+	  reloc_howto_type *howto = bfd_reloc_type_lookup (stdoutput, code);
+	  char *p = frag_more (4);
+	  md_number_to_chars (p, 0, 4);
+	  fix_new (frag_now, p - frag_now->fr_literal, 4, fde->start_address,
+		   0, howto->pc_relative, code);
+	}
+      else
+	{
+	  exp.X_op = O_subtract;
+	  exp.X_add_number = 0;
 #if CFI_DIFF_EXPR_OK
-      exp.X_add_symbol = fde->start_address;
-      exp.X_op_symbol = symbol_temp_new_now ();
-      emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code offset.  */
+	  exp.X_add_symbol = fde->start_address;
+	  exp.X_op_symbol = symbol_temp_new_now ();
+	  emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code offset.  */
 #else
-      exp.X_op = O_symbol;
-      exp.X_add_symbol = fde->start_address;
-#ifdef tc_cfi_emit_pcrel_expr
-      tc_cfi_emit_pcrel_expr (&exp, DWARF2_FDE_RELOC_SIZE);	 /* Code offset.  */
+	  exp.X_op = O_symbol;
+	  exp.X_add_symbol = fde->start_address;
+
+#if defined(tc_cfi_emit_pcrel_expr)
+	  tc_cfi_emit_pcrel_expr (&exp, DWARF2_FDE_RELOC_SIZE);	 /* Code offset.  */
 #else
-      emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code offset.  */
+	  emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code offset.  */
 #endif
 #endif
+	}
       addr_size = DWARF2_FDE_RELOC_SIZE;
     }
   else
     {
-      exp.X_op = O_symbol;
-      exp.X_add_symbol = fde->start_address;
       exp.X_add_number = 0;
       addr_size = DWARF2_ADDR_SIZE (stdoutput);
       emit_expr (&exp, addr_size);
@@ -1609,28 +1929,11 @@ output_fde (struct fde_entry *fde, struct cie_entry *cie,
   exp.X_add_number = 0;
   emit_expr (&exp, addr_size);
 
-  augmentation_size = encoding_size (fde->lsda_encoding);
+  augmentation_size = eh_encoding_size (fde->lsda_encoding);
   if (eh_frame)
     out_uleb128 (augmentation_size);		/* Augmentation size.  */
 
-  if (fde->lsda_encoding != DW_EH_PE_omit)
-    {
-      exp = fde->lsda;
-      if ((fde->lsda_encoding & 0x70) == DW_EH_PE_pcrel)
-	{
-#if CFI_DIFF_LSDA_OK
-	  exp.X_op = O_subtract;
-	  exp.X_op_symbol = symbol_temp_new_now ();
-	  emit_expr (&exp, augmentation_size);
-#elif defined (tc_cfi_emit_pcrel_expr)
-	  tc_cfi_emit_pcrel_expr (&exp, augmentation_size);
-#else
-	  abort ();
-#endif
-	}
-      else
-	emit_expr (&exp, augmentation_size);
-    }
+  emit_expr_encoded (&fde->lsda, cie->lsda_encoding, FALSE);
 
   for (; first; first = first->next)
     if (CUR_SEG (first) == CUR_SEG (fde))
@@ -1820,26 +2123,50 @@ cfi_change_reg_numbers (struct cfi_insn_data *insn, segT ccseg)
 #define cfi_change_reg_numbers(insn, cseg) do { } while (0)
 #endif
 
-static segT
-get_cfi_seg (segT cseg, const char *base, flagword flags, int align)
+#if SUPPORT_COMPACT_EH
+static void
+cfi_emit_eh_header (symbolS *sym, bfd_vma addend)
 {
-  if (SUPPORT_FRAME_LINKONCE)
-    {
-      struct dwcfi_seg_list *l;
+  expressionS exp;
 
-      l = dwcfi_hash_find_or_make (cseg, base, flags);
+  exp.X_add_number = addend;
+  exp.X_add_symbol = sym;
+  emit_expr_encoded (&exp, DW_EH_PE_sdata4 | DW_EH_PE_pcrel, FALSE);
+}
 
-      cseg = l->seg;
-      subseg_set (cseg, l->subseg);
+static void
+output_eh_header (struct fde_entry *fde)
+{
+  char *p;
+  bfd_vma addend;
+
+  if (fde->eh_header_type == EH_COMPACT_INLINE)
+    addend = 0;
+  else
+    addend = 1;
+
+  cfi_emit_eh_header (fde->start_address, addend);
+
+  if (fde->eh_header_type == EH_COMPACT_INLINE)
+    {
+      p = frag_more (4);
+      /* Inline entries always use PR1.  */
+      *(p++) = 1;
+      memcpy (p, fde->eh_data, 3);
     }
   else
     {
-      cseg = subseg_new (base, 0);
-      bfd_set_section_flags (stdoutput, cseg, flags);
+      if (fde->eh_header_type == EH_COMPACT_LEGACY)
+	addend = 1;
+      else if (fde->eh_header_type == EH_COMPACT_OUTLINE
+	       || fde->eh_header_type == EH_COMPACT_OUTLINE_DONE)
+	addend = 0;
+      else
+	abort ();
+      cfi_emit_eh_header (fde->eh_loc, addend);
     }
-  record_alignment (cseg, align);
-  return cseg;
 }
+#endif
 
 void
 cfi_finish (void)
@@ -1853,13 +2180,14 @@ cfi_finish (void)
   if (all_fde_data == 0)
     return;
 
-  if ((cfi_sections & CFI_EMIT_eh_frame) != 0)
+  if ((all_cfi_sections & CFI_EMIT_eh_frame) != 0
+      || (all_cfi_sections & CFI_EMIT_eh_frame_compact) != 0)
     {
       /* Make sure check_eh_frame doesn't do anything with our output.  */
       save_flag_traditional_format = flag_traditional_format;
       flag_traditional_format = 1;
 
-      if (!SUPPORT_FRAME_LINKONCE)
+      if (!EH_FRAME_LINKONCE)
 	{
 	  /* Open .eh_frame section.  */
 	  cfi_seg = get_cfi_seg (NULL, ".eh_frame",
@@ -1887,7 +2215,18 @@ cfi_finish (void)
 
 	  for (fde = all_fde_data; fde ; fde = fde->next)
 	    {
-	      if (SUPPORT_FRAME_LINKONCE)
+	      if ((fde->sections & CFI_EMIT_eh_frame) == 0
+		  && (fde->sections & CFI_EMIT_eh_frame_compact) == 0)
+		continue;
+
+#if SUPPORT_COMPACT_EH
+	      if (fde->eh_header_type == EH_COMPACT_HAS_LSDA)
+		fde->eh_header_type = EH_COMPACT_LEGACY;
+
+	      if (fde->eh_header_type != EH_COMPACT_LEGACY)
+		continue;
+#endif
+	      if (EH_FRAME_LINKONCE)
 		{
 		  if (HANDLED (fde))
 		    continue;
@@ -1921,16 +2260,104 @@ cfi_finish (void)
 		}
 
 	      cie = select_cie_for_fde (fde, TRUE, &first, 2);
+	      fde->eh_loc = symbol_temp_new_now ();
 	      output_fde (fde, cie, TRUE, first,
 			  fde->next == NULL ? EH_FRAME_ALIGNMENT : 2);
 	    }
 	}
-      while (SUPPORT_FRAME_LINKONCE && seek_next_seg == 2);
+      while (EH_FRAME_LINKONCE && seek_next_seg == 2);
 
-      if (SUPPORT_FRAME_LINKONCE)
+      if (EH_FRAME_LINKONCE)
 	for (fde = all_fde_data; fde ; fde = fde->next)
 	  SET_HANDLED (fde, 0);
 
+#if SUPPORT_COMPACT_EH
+      if (compact_eh)
+	{
+	  /* Create remaining out of line table entries.  */
+	  do
+	    {
+	      ccseg = NULL;
+	      seek_next_seg = 0;
+
+	      for (fde = all_fde_data; fde ; fde = fde->next)
+		{
+		  if ((fde->sections & CFI_EMIT_eh_frame) == 0
+		      && (fde->sections & CFI_EMIT_eh_frame_compact) == 0)
+		    continue;
+
+		  if (fde->eh_header_type != EH_COMPACT_OUTLINE)
+		    continue;
+		  if (HANDLED (fde))
+		    continue;
+		  if (seek_next_seg && CUR_SEG (fde) != ccseg)
+		    {
+		      seek_next_seg = 2;
+		      continue;
+		    }
+		  if (!seek_next_seg)
+		    {
+		      ccseg = CUR_SEG (fde);
+		      /* Open .gnu_extab section.  */
+		      get_cfi_seg (ccseg, ".gnu_extab",
+				   (SEC_ALLOC | SEC_LOAD | SEC_DATA
+				    | DWARF2_EH_FRAME_READ_ONLY),
+				   1);
+		      seek_next_seg = 1;
+		    }
+		  SET_HANDLED (fde, 1);
+
+		  frag_align (1, 0, 0);
+		  record_alignment (now_seg, 1);
+		  output_compact_unwind_data (fde, 1);
+		}
+	    }
+	  while (EH_FRAME_LINKONCE && seek_next_seg == 2);
+
+	  for (fde = all_fde_data; fde ; fde = fde->next)
+	    SET_HANDLED (fde, 0);
+
+	  /* Create index table fragments.  */
+	  do
+	    {
+	      ccseg = NULL;
+	      seek_next_seg = 0;
+
+	      for (fde = all_fde_data; fde ; fde = fde->next)
+		{
+		  if ((fde->sections & CFI_EMIT_eh_frame) == 0
+		      && (fde->sections & CFI_EMIT_eh_frame_compact) == 0)
+		    continue;
+
+		  if (HANDLED (fde))
+		    continue;
+		  if (seek_next_seg && CUR_SEG (fde) != ccseg)
+		    {
+		      seek_next_seg = 2;
+		      continue;
+		    }
+		  if (!seek_next_seg)
+		    {
+		      ccseg = CUR_SEG (fde);
+		      /* Open .eh_frame_entry section.  */
+		      cfi_seg = get_cfi_seg (ccseg, ".eh_frame_entry",
+					     (SEC_ALLOC | SEC_LOAD | SEC_DATA
+					      | DWARF2_EH_FRAME_READ_ONLY),
+					     2);
+		      seek_next_seg = 1;
+		    }
+		  SET_HANDLED (fde, 1);
+
+		  output_eh_header (fde);
+		}
+	    }
+	  while (seek_next_seg == 2);
+
+	  for (fde = all_fde_data; fde ; fde = fde->next)
+	    SET_HANDLED (fde, 0);
+	}
+#endif /* SUPPORT_COMPACT_EH */
+
       flag_traditional_format = save_flag_traditional_format;
     }
 
@@ -1957,6 +2384,9 @@ cfi_finish (void)
 
 	  for (fde = all_fde_data; fde ; fde = fde->next)
 	    {
+	      if ((fde->sections & CFI_EMIT_debug_frame) == 0)
+		continue;
+
 	      if (SUPPORT_FRAME_LINKONCE)
 		{
 		  if (HANDLED (fde))
@@ -2014,6 +2444,7 @@ const pseudo_typeS cfi_pseudo_table[] =
     { "cfi_sections", dot_cfi_dummy, 0 },
     { "cfi_startproc", dot_cfi_dummy, 0 },
     { "cfi_endproc", dot_cfi_dummy, 0 },
+    { "cfi_fde_data", dot_cfi_dummy, 0 },
     { "cfi_def_cfa", dot_cfi_dummy, 0 },
     { "cfi_def_cfa_register", dot_cfi_dummy, 0 },
     { "cfi_def_cfa_offset", dot_cfi_dummy, 0 },
@@ -2031,8 +2462,10 @@ const pseudo_typeS cfi_pseudo_table[] =
     { "cfi_escape", dot_cfi_dummy, 0 },
     { "cfi_signal_frame", dot_cfi_dummy, 0 },
     { "cfi_personality", dot_cfi_dummy, 0 },
+    { "cfi_personality_id", dot_cfi_dummy, 0 },
     { "cfi_lsda", dot_cfi_dummy, 0 },
     { "cfi_val_encoded_addr", dot_cfi_dummy, 0 },
+    { "cfi_inline_lsda", dot_cfi_dummy, 0 },
     { NULL, NULL, 0 }
   };
 
diff --git a/gas/dw2gencfi.h b/gas/dw2gencfi.h
index 8ce9819..3c1ce18 100644
--- a/gas/dw2gencfi.h
+++ b/gas/dw2gencfi.h
@@ -57,6 +57,12 @@ extern void cfi_add_CFA_restore_state (void);
 #define SUPPORT_FRAME_LINKONCE 0
 #endif
 
+#ifdef tc_cfi_reloc_for_encoding
+#define SUPPORT_COMPACT_EH 1
+#else
+#define SUPPORT_COMPACT_EH 0
+#endif
+
 struct cfi_insn_data
 {
   struct cfi_insn_data *next;
@@ -97,6 +103,15 @@ struct cfi_insn_data
   } u;
 };
 
+enum {
+    EH_COMPACT_LEGACY,
+    EH_COMPACT_INLINE,
+    EH_COMPACT_OUTLINE,
+    EH_COMPACT_OUTLINE_DONE,
+    /* Outline if .cfi_inline_lsda used, otherwise legacy FDE.  */
+    EH_COMPACT_HAS_LSDA
+};
+
 struct fde_entry
 {
   struct fde_entry *next;
@@ -109,6 +124,7 @@ struct fde_entry
   struct cfi_insn_data **last;
   unsigned char per_encoding;
   unsigned char lsda_encoding;
+  int personality_id;
   expressionS personality;
   expressionS lsda;
   unsigned int return_column;
@@ -116,6 +132,13 @@ struct fde_entry
 #if SUPPORT_FRAME_LINKONCE
   int handled;
 #endif
+  int eh_header_type;
+  /* Compact unwinding opcodes, not including the PR byte or LSDA.  */
+  int eh_data_size;
+  bfd_byte *eh_data;
+  /* For out of line tables and FDEs.  */
+  symbolS *eh_loc;
+  int sections;
 };
 
 /* The list of all FDEs that have been collected.  */
diff --git a/gas/testsuite/gas/mips/compact-eh-1.s b/gas/testsuite/gas/mips/compact-eh-1.s
new file mode 100644
index 0000000..9c4f8d2
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-1.s
@@ -0,0 +1,19 @@
+	.gnu_attribute 4, 1
+	.abicalls
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+$LFB0 = .
+	.cfi_startproc
+	.cfi_personality_id 0x2
+	.set	nomips16
+	.set	nomicromips
+	.ent	_Z3fooi
+	.type	_Z3fooi, @function
+_Z3fooi:
+	nop
+	.end	_Z3fooi
+	.size	_Z3fooi, .-_Z3fooi
+	.cfi_fde_data 0x4,0x40
+	.cfi_endproc
diff --git a/gas/testsuite/gas/mips/compact-eh-2.s b/gas/testsuite/gas/mips/compact-eh-2.s
new file mode 100644
index 0000000..beeebda
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-2.s
@@ -0,0 +1,28 @@
+	.gnu_attribute 4, 1
+	.abicalls
+        .hidden DW.ref.__gnu_compact_pr2
+        .weak   DW.ref.__gnu_compact_pr2
+        .section        .data.DW.ref.__gnu_compact_pr2,"awG",@progbits,DW.ref.__gnu_compact_pr2,comdat
+        .align  2
+        .type   DW.ref.__gnu_compact_pr2, @object
+        .size   DW.ref.__gnu_compact_pr2, 4
+DW.ref.__gnu_compact_pr2:
+        .word   __gnu_compact_pr2
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+$LFB0 = .
+	.cfi_startproc
+	.cfi_personality 0x1b, DW.ref.__gnu_compact_pr2
+	.set	nomips16
+	.set	nomicromips
+	.ent	_Z3fooi
+	.type	_Z3fooi, @function
+_Z3fooi:
+	nop
+	.end	_Z3fooi
+	.size	_Z3fooi, .-_Z3fooi
+	.cfi_fde_data 0x4,0x40
+	.cfi_endproc
+	.globl	__gnu_compact_pr2
diff --git a/gas/testsuite/gas/mips/compact-eh-3.s b/gas/testsuite/gas/mips/compact-eh-3.s
new file mode 100644
index 0000000..fd9def1
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-3.s
@@ -0,0 +1,19 @@
+	.gnu_attribute 4, 1
+	.abicalls
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+$LFB0 = .
+	.cfi_startproc
+	.cfi_personality_id 0x2
+	.set	nomips16
+	.set	nomicromips
+	.ent	_Z3fooi
+	.type	_Z3fooi, @function
+_Z3fooi:
+	nop
+	.end	_Z3fooi
+	.size	_Z3fooi, .-_Z3fooi
+	.cfi_fde_data 0x4,0x40,0x3,0x5
+	.cfi_endproc
diff --git a/gas/testsuite/gas/mips/compact-eh-4.s b/gas/testsuite/gas/mips/compact-eh-4.s
new file mode 100644
index 0000000..d0be1c7
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-4.s
@@ -0,0 +1,47 @@
+	.gnu_attribute 4, 1
+	.abicalls
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+$LFB0 = .
+	.cfi_startproc
+	.cfi_personality_id 0x2
+	.cfi_lsda 0x1b,$LLSDA0
+	.set	nomips16
+	.set	nomicromips
+	.ent	_Z3fooi
+	.type	_Z3fooi, @function
+_Z3fooi:
+$LEHB0 = .
+	nop
+$LEHE0 = .
+	nop
+$LEHB1 = .
+	nop
+$LEHE1 = .
+	nop
+$LEHB2 = .
+$L3:
+	nop
+$LEHE2 = .
+	.end	_Z3fooi
+	.size	_Z3fooi, .-_Z3fooi
+	.cfi_fde_data 0x4,0x40
+	.cfi_endproc
+	.cfi_inline_lsda 2
+$LLSDA0:
+	.byte	0x2
+	.uleb128 $LLSDACSE0-$LLSDACSB0
+$LLSDACSB0:
+	.uleb128 ($LEHB0-$LFB0)|1
+	.uleb128 ($LEHE0-$LEHB0)
+	.sleb128 -1
+	.uleb128 ($LEHB1-$LEHE0)|1
+	.uleb128 ($LEHE1-$LEHB1)
+	.sleb128 ($L3-($LEHE1))
+	.sleb128 (0<<2)|0
+	.uleb128 ($LEHB2-$LEHE1)|1
+	.uleb128 ($LEHE2-$LEHB2)
+	.sleb128 -1
+$LLSDACSE0:
diff --git a/gas/testsuite/gas/mips/compact-eh-5.s b/gas/testsuite/gas/mips/compact-eh-5.s
new file mode 100644
index 0000000..aa9bdda
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-5.s
@@ -0,0 +1,56 @@
+	.gnu_attribute 4, 1
+	.abicalls
+        .hidden DW.ref.__gnu_compact_pr2
+        .weak   DW.ref.__gnu_compact_pr2
+        .section        .data.DW.ref.__gnu_compact_pr2,"awG",@progbits,DW.ref.__gnu_compact_pr2,comdat
+        .align  2
+        .type   DW.ref.__gnu_compact_pr2, @object
+        .size   DW.ref.__gnu_compact_pr2, 4
+DW.ref.__gnu_compact_pr2:
+        .word   __gnu_compact_pr2
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+$LFB0 = .
+	.cfi_startproc
+	.cfi_personality 0x1b, DW.ref.__gnu_compact_pr2
+	.cfi_lsda 0x1b,$LLSDA0
+	.set	nomips16
+	.set	nomicromips
+	.ent	_Z3fooi
+	.type	_Z3fooi, @function
+_Z3fooi:
+$LEHB0 = .
+	nop
+$LEHE0 = .
+	nop
+$LEHB1 = .
+	nop
+$LEHE1 = .
+	nop
+$LEHB2 = .
+$L3:
+	nop
+$LEHE2 = .
+	.end	_Z3fooi
+	.size	_Z3fooi, .-_Z3fooi
+	.cfi_fde_data 0x4,0x40
+	.cfi_endproc
+	.globl	__gnu_compact_pr2
+	.cfi_inline_lsda 2
+$LLSDA0:
+	.byte	0x2
+	.uleb128 $LLSDACSE0-$LLSDACSB0
+$LLSDACSB0:
+	.uleb128 ($LEHB0-$LFB0)|1
+	.uleb128 ($LEHE0-$LEHB0)
+	.sleb128 -1
+	.uleb128 ($LEHB1-$LEHE0)|1
+	.uleb128 ($LEHE1-$LEHB1)
+	.sleb128 ($L3-($LEHE1))
+	.sleb128 (0<<2)|0
+	.uleb128 ($LEHB2-$LEHE1)|1
+	.uleb128 ($LEHE2-$LEHB2)
+	.sleb128 -1
+$LLSDACSE0:
diff --git a/gas/testsuite/gas/mips/compact-eh-6.s b/gas/testsuite/gas/mips/compact-eh-6.s
new file mode 100644
index 0000000..2e49054
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-6.s
@@ -0,0 +1,47 @@
+	.gnu_attribute 4, 1
+	.abicalls
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+$LFB0 = .
+	.cfi_startproc
+	.cfi_personality_id 0x2
+	.cfi_lsda 0x1b,$LLSDA0
+	.set	nomips16
+	.set	nomicromips
+	.ent	_Z3fooi
+	.type	_Z3fooi, @function
+_Z3fooi:
+$LEHB0 = .
+	nop
+$LEHE0 = .
+	nop
+$LEHB1 = .
+	nop
+$LEHE1 = .
+	nop
+$LEHB2 = .
+$L3:
+	nop
+$LEHE2 = .
+	.end	_Z3fooi
+	.size	_Z3fooi, .-_Z3fooi
+	.cfi_fde_data 0x4,0x40,0x3,0x5
+	.cfi_endproc
+	.cfi_inline_lsda 2
+$LLSDA0:
+	.byte	0x2
+	.uleb128 $LLSDACSE0-$LLSDACSB0
+$LLSDACSB0:
+	.uleb128 ($LEHB0-$LFB0)|1
+	.uleb128 ($LEHE0-$LEHB0)
+	.sleb128 -1
+	.uleb128 ($LEHB1-$LEHE0)|1
+	.uleb128 ($LEHE1-$LEHB1)
+	.sleb128 ($L3-($LEHE1))
+	.sleb128 (0<<2)|0
+	.uleb128 ($LEHB2-$LEHE1)|1
+	.uleb128 ($LEHE2-$LEHB2)
+	.sleb128 -1
+$LLSDACSE0:
diff --git a/gas/testsuite/gas/mips/compact-eh-7.s b/gas/testsuite/gas/mips/compact-eh-7.s
new file mode 100644
index 0000000..e7554d2
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-7.s
@@ -0,0 +1,22 @@
+	.gnu_attribute 4, 1
+	.abicalls
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+$LFB0 = .
+	.cfi_startproc
+	.cfi_personality_id 0x2
+	.set	nomips16
+	.set	nomicromips
+	.ent	_Z3fooi
+	.type	_Z3fooi, @function
+_Z3fooi:
+	nop
+	.cfi_def_cfa_offset -32
+	nop
+	.cfi_def_cfa_offset 0
+	
+	.end	_Z3fooi
+	.size	_Z3fooi, .-_Z3fooi
+	.cfi_endproc
diff --git a/gas/testsuite/gas/mips/compact-eh-eb-1.d b/gas/testsuite/gas/mips/compact-eh-eb-1.d
new file mode 100644
index 0000000..249e4f2
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-eb-1.d
@@ -0,0 +1,32 @@
+#objdump: -sr
+#name: Compact EH EB #1 with personality ID and FDE data
+#source: compact-eh-1.s
+#as: -EB
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+
+
+Contents of section .text:
+ 0000 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .eh_frame_entry:
+ 0000 00000000 0104405c                    .*
+Contents of section .gnu.attributes:
+ 0000 41000000 0f676e75 00010000 00070401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-eb-2.d b/gas/testsuite/gas/mips/compact-eh-eb-2.d
new file mode 100644
index 0000000..82b49e8
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-eb-2.d
@@ -0,0 +1,49 @@
+#objdump: -sr
+#name: Compact EH EB #2 with personality routine and FDE data
+#source: compact-eh-2.s
+#as: -EB
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.data.DW.ref.__gnu_compact_pr2\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         __gnu_compact_pr2
+
+
+RELOCATION RECORDS FOR \[.gnu_extab\]:
+OFFSET   TYPE              VALUE 
+00000001 R_MIPS_PC32       DW.ref.__gnu_compact_pr2
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .group:
+ 0000 00000001 00000009                   .*
+Contents of section .text:
+ 0000 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .data.DW.ref.__gnu_compact_pr2:
+ 0000 00000000                             .*
+Contents of section .gnu_extab:
+ 0000 00000000 0004405c                    .*
+Contents of section .eh_frame_entry:
+ 0000 00000001 00000000                    .*
+Contents of section .gnu.attributes:
+ 0000 41000000 0f676e75 00010000 00070401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-eb-3.d b/gas/testsuite/gas/mips/compact-eh-eb-3.d
new file mode 100644
index 0000000..21c8cab
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-eb-3.d
@@ -0,0 +1,35 @@
+#objdump: -sr
+#name: Compact EH EB #3 with personality id and large FDE data
+#source: compact-eh-3.s
+#as: -EB
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .text:
+ 0000 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .gnu_extab:
+ 0000 02044003 055c                        .*
+Contents of section .eh_frame_entry:
+ 0000 00000001 00000000                    .*
+Contents of section .gnu.attributes:
+ 0000 41000000 0f676e75 00010000 00070401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-eb-4.d b/gas/testsuite/gas/mips/compact-eh-eb-4.d
new file mode 100644
index 0000000..d7b9320
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-eb-4.d
@@ -0,0 +1,36 @@
+#objdump: -sr
+#name: Compact EH EB #4 with personality id, FDE data and LSDA
+#source: compact-eh-4.s
+#as: -EB
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .text:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .gnu_extab:
+ 0000 0204405c 020a0104 7f050404 0005047f  .*
+Contents of section .eh_frame_entry:
+ 0000 00000001 00000000                    .*
+Contents of section .gnu.attributes:
+ 0000 41000000 0f676e75 00010000 00070401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-eb-5.d b/gas/testsuite/gas/mips/compact-eh-eb-5.d
new file mode 100644
index 0000000..fbdd199
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-eb-5.d
@@ -0,0 +1,51 @@
+#objdump: -sr
+#name: Compact EH EB #5 with personality routine, FDE data and LSDA
+#source: compact-eh-5.s
+#as: -EB
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.data.DW.ref.__gnu_compact_pr2\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         __gnu_compact_pr2
+
+
+RELOCATION RECORDS FOR \[.gnu_extab\]:
+OFFSET   TYPE              VALUE 
+00000001 R_MIPS_PC32       DW.ref.__gnu_compact_pr2
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .group:
+ 0000 00000001 00000009                    .*
+Contents of section .text:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .data.DW.ref.__gnu_compact_pr2:
+ 0000 00000000                             .*
+Contents of section .gnu_extab:
+ 0000 00000000 0004405c 020a0104 7f050404  .*
+ 0010 0005047f                             .*
+Contents of section .eh_frame_entry:
+ 0000 00000001 00000000                    .*
+Contents of section .gnu.attributes:
+ 0000 41000000 0f676e75 00010000 00070401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-eb-6.d b/gas/testsuite/gas/mips/compact-eh-eb-6.d
new file mode 100644
index 0000000..4971ddf
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-eb-6.d
@@ -0,0 +1,37 @@
+#objdump: -sr
+#name: Compact EH EB #6 with personality id, LSDA and large FDE data
+#source: compact-eh-6.s
+#as: -EB
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .text:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .gnu_extab:
+ 0000 02044003 055f5f5c 020a0104 7f050404  .*
+ 0010 0005047f                             .*
+Contents of section .eh_frame_entry:
+ 0000 00000001 00000000                    .*
+Contents of section .gnu.attributes:
+ 0000 41000000 0f676e75 00010000 00070401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-eb-7.d b/gas/testsuite/gas/mips/compact-eh-eb-7.d
new file mode 100644
index 0000000..e5e5cdd
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-eb-7.d
@@ -0,0 +1,42 @@
+#objdump: -sr
+#name: Compact EH EB #7 with personality id and fallback FDE
+#source: compact-eh-7.s
+#as: -EB
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame\]:
+OFFSET   TYPE              VALUE 
+0000001c R_MIPS_PC32       .text.*
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .eh_frame.*
+
+
+Contents of section .text:
+ 0000 00000000 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .eh_frame:
+ 0000 00000010 00000000 017a5200 017c1f01  .*
+ 0010 1b0d1d00 00000014 00000018 00000000  .*
+ 0020 00000008 00441308 440e0000           .*
+Contents of section .eh_frame_entry:
+ 0000 00000001 00000015                    .*
+Contents of section .gnu.attributes:
+ 0000 41000000 0f676e75 00010000 00070401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-el-1.d b/gas/testsuite/gas/mips/compact-eh-el-1.d
new file mode 100644
index 0000000..2671d42
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-el-1.d
@@ -0,0 +1,32 @@
+#objdump: -sr
+#name: Compact EH EL #1 with personality ID and FDE data
+#source: compact-eh-1.s
+#as: -EL
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+
+
+Contents of section .text:
+ 0000 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .eh_frame_entry:
+ 0000 00000000 0104405c                    .*
+Contents of section .gnu.attributes:
+ 0000 410f0000 00676e75 00010700 00000401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-el-2.d b/gas/testsuite/gas/mips/compact-eh-el-2.d
new file mode 100644
index 0000000..190e516
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-el-2.d
@@ -0,0 +1,49 @@
+#objdump: -sr
+#name: Compact EH EL #2 with personality routine and FDE data
+#source: compact-eh-2.s
+#as: -EL
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.data.DW.ref.__gnu_compact_pr2\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         __gnu_compact_pr2
+
+
+RELOCATION RECORDS FOR \[.gnu_extab\]:
+OFFSET   TYPE              VALUE 
+00000001 R_MIPS_PC32       DW.ref.__gnu_compact_pr2
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .group:
+ 0000 01000000 09000000                    .*
+Contents of section .text:
+ 0000 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .data.DW.ref.__gnu_compact_pr2:
+ 0000 00000000                             .*
+Contents of section .gnu_extab:
+ 0000 00000000 0004405c                    .*
+Contents of section .eh_frame_entry:
+ 0000 01000000 00000000                    .*
+Contents of section .gnu.attributes:
+ 0000 410f0000 00676e75 00010700 00000401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-el-3.d b/gas/testsuite/gas/mips/compact-eh-el-3.d
new file mode 100644
index 0000000..f2e98a0
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-el-3.d
@@ -0,0 +1,35 @@
+#objdump: -sr
+#name: Compact EH EL #3 with personality id and large FDE data
+#source: compact-eh-3.s
+#as: -EL
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .text:
+ 0000 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .gnu_extab:
+ 0000 02044003 055c                        .*
+Contents of section .eh_frame_entry:
+ 0000 01000000 00000000                    .*
+Contents of section .gnu.attributes:
+ 0000 410f0000 00676e75 00010700 00000401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-el-4.d b/gas/testsuite/gas/mips/compact-eh-el-4.d
new file mode 100644
index 0000000..eca5f66
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-el-4.d
@@ -0,0 +1,36 @@
+#objdump: -sr
+#name: Compact EH EL #4 with personality id, FDE data and LSDA
+#source: compact-eh-4.s
+#as: -EL
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .text:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .gnu_extab:
+ 0000 0204405c 020a0104 7f050404 0005047f  .*
+Contents of section .eh_frame_entry:
+ 0000 01000000 00000000                    .*
+Contents of section .gnu.attributes:
+ 0000 410f0000 00676e75 00010700 00000401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-el-5.d b/gas/testsuite/gas/mips/compact-eh-el-5.d
new file mode 100644
index 0000000..cfbef4a
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-el-5.d
@@ -0,0 +1,51 @@
+#objdump: -sr
+#name: Compact EH EL #5 with personality routine, FDE data and LSDA
+#source: compact-eh-5.s
+#as: -EL
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.data.DW.ref.__gnu_compact_pr2\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         __gnu_compact_pr2
+
+
+RELOCATION RECORDS FOR \[.gnu_extab\]:
+OFFSET   TYPE              VALUE 
+00000001 R_MIPS_PC32       DW.ref.__gnu_compact_pr2
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .group:
+ 0000 01000000 09000000                    .*
+Contents of section .text:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .data.DW.ref.__gnu_compact_pr2:
+ 0000 00000000                             .*
+Contents of section .gnu_extab:
+ 0000 00000000 0004405c 020a0104 7f050404  .*
+ 0010 0005047f                             .*
+Contents of section .eh_frame_entry:
+ 0000 01000000 00000000                    .*
+Contents of section .gnu.attributes:
+ 0000 410f0000 00676e75 00010700 00000401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-el-6.d b/gas/testsuite/gas/mips/compact-eh-el-6.d
new file mode 100644
index 0000000..ed6614b
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-el-6.d
@@ -0,0 +1,37 @@
+#objdump: -sr
+#name: Compact EH EL #6 with personality id, LSDA and large FDE data
+#source: compact-eh-6.s
+#as: -EL
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .text:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .gnu_extab:
+ 0000 02044003 055f5f5c 020a0104 7f050404  .*
+ 0010 0005047f                             .*
+Contents of section .eh_frame_entry:
+ 0000 01000000 00000000                    .*
+Contents of section .gnu.attributes:
+ 0000 410f0000 00676e75 00010700 00000401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-el-7.d b/gas/testsuite/gas/mips/compact-eh-el-7.d
new file mode 100644
index 0000000..8f9c4fc
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-el-7.d
@@ -0,0 +1,42 @@
+#objdump: -sr
+#name: Compact EH EL #7 with personality id and fallback FDE
+#source: compact-eh-7.s
+#as: -EL
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame\]:
+OFFSET   TYPE              VALUE 
+0000001c R_MIPS_PC32       .text.*
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .eh_frame.*
+
+
+Contents of section .text:
+ 0000 00000000 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .eh_frame:
+ 0000 10000000 00000000 017a5200 017c1f01  .*
+ 0010 1b0d1d00 14000000 18000000 00000000  .*
+ 0020 08000000 00441308 440e0000           .*
+Contents of section .eh_frame_entry:
+ 0000 01000000 15000000                    .*
+Contents of section .gnu.attributes:
+ 0000 410f0000 00676e75 00010700 00000401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-err1.l b/gas/testsuite/gas/mips/compact-eh-err1.l
new file mode 100644
index 0000000..3ee03de
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-err1.l
@@ -0,0 +1,2 @@
+.*: Assembler messages:
+.*:20: Error: .cfi_inline_lsda seen for frame without .cfi_lsda
diff --git a/gas/testsuite/gas/mips/compact-eh-err1.s b/gas/testsuite/gas/mips/compact-eh-err1.s
new file mode 100644
index 0000000..967313f
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-err1.s
@@ -0,0 +1,20 @@
+	.gnu_attribute 4, 1
+	.abicalls
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+$LFB0 = .
+	.cfi_startproc
+	.cfi_personality_id 0x2
+	.set	nomips16
+	.set	nomicromips
+	.ent	_Z3fooi
+	.type	_Z3fooi, @function
+_Z3fooi:
+	nop
+	.end	_Z3fooi
+	.size	_Z3fooi, .-_Z3fooi
+	.cfi_fde_data 0x4,0x40
+	.cfi_endproc
+	.cfi_inline_lsda 1
diff --git a/gas/testsuite/gas/mips/compact-eh-err2.l b/gas/testsuite/gas/mips/compact-eh-err2.l
new file mode 100644
index 0000000..c52976a
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-err2.l
@@ -0,0 +1,2 @@
+.*: Assembler messages:
+.*:7: Error: inconsistent uses of .cfi_sections
diff --git a/gas/testsuite/gas/mips/compact-eh-err2.s b/gas/testsuite/gas/mips/compact-eh-err2.s
new file mode 100644
index 0000000..acf83d1
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-err2.s
@@ -0,0 +1,7 @@
+	.gnu_attribute 4, 1
+	.abicalls
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+	.cfi_sections .eh_frame
diff --git a/gas/testsuite/gas/mips/ehword.d b/gas/testsuite/gas/mips/ehword.d
deleted file mode 100644
index 4cbef12..0000000
--- a/gas/testsuite/gas/mips/ehword.d
+++ /dev/null
@@ -1,9 +0,0 @@
-#objdump: -r -j .text
-#name MIPS .ehword
-#source ehword.s
-
-.*: +file format .*mips.*
-
-RELOCATION RECORDS FOR \[\.text\]:
-OFFSET   TYPE              VALUE 
-00000000 R_MIPS_EH         _ZTI5myExc
diff --git a/gas/testsuite/gas/mips/ehword.s b/gas/testsuite/gas/mips/ehword.s
deleted file mode 100644
index 152ad30..0000000
--- a/gas/testsuite/gas/mips/ehword.s
+++ /dev/null
@@ -1 +0,0 @@
-	.ehword	_ZTI5myExc
diff --git a/gas/testsuite/gas/mips/mips.exp b/gas/testsuite/gas/mips/mips.exp
index b0de13a..7f56465 100644
--- a/gas/testsuite/gas/mips/mips.exp
+++ b/gas/testsuite/gas/mips/mips.exp
@@ -570,6 +570,23 @@ if { [istarget mips*-*-vxworks*] } {
 	    "MIPS branch swapping ($count)"
     }
 
+    run_dump_test "compact-eh-eb-1"
+    run_dump_test "compact-eh-eb-2"
+    run_dump_test "compact-eh-eb-3"
+    run_dump_test "compact-eh-eb-4"
+    run_dump_test "compact-eh-eb-5"
+    run_dump_test "compact-eh-eb-6"
+    run_dump_test "compact-eh-eb-7"
+    run_dump_test "compact-eh-el-1"
+    run_dump_test "compact-eh-el-2"
+    run_dump_test "compact-eh-el-3"
+    run_dump_test "compact-eh-el-4"
+    run_dump_test "compact-eh-el-5"
+    run_dump_test "compact-eh-el-6"
+    run_dump_test "compact-eh-el-7"
+    run_list_test "compact-eh-err1"
+    run_list_test "compact-eh-err2"
+
     run_dump_test "div"
 
     if { !$addr32 } {
@@ -1139,7 +1156,6 @@ if { [istarget mips*-*-vxworks*] } {
     run_dump_test "align2-el"
     run_dump_test "align3"
     run_dump_test "odd-float"
-    run_dump_test "ehword"
     run_dump_test "insn-opts"
 
     run_list_test_arches "mips-macro-ill-sfp" "-32 -msingle-float" \
diff --git a/include/bfdlink.h b/include/bfdlink.h
index 125683d..e8d8357 100644
--- a/include/bfdlink.h
+++ b/include/bfdlink.h
@@ -339,9 +339,9 @@ struct bfd_link_info
   /* TRUE if PT_GNU_RELRO segment should be created.  */
   unsigned int relro: 1;
 
-  /* TRUE if .eh_frame_hdr section and PT_GNU_EH_FRAME ELF segment
-     should be created.  */
-  unsigned int eh_frame_hdr: 1;
+  /* Nonzero if .eh_frame_hdr section and PT_GNU_EH_FRAME ELF segment
+     should be created.  1 for DWARF2 tables, 2 for compact tables.  */
+  unsigned int eh_frame_hdr_type: 2;
 
   /* TRUE if we should warn when adding a DT_TEXTREL to a shared object.  */
   unsigned int warn_shared_textrel: 1;
diff --git a/ld/emultempl/elf32.em b/ld/emultempl/elf32.em
index 67c437d..010abe9 100644
--- a/ld/emultempl/elf32.em
+++ b/ld/emultempl/elf32.em
@@ -1044,29 +1044,60 @@ gld${EMULATION_NAME}_after_open (void)
       return;
     }
 
-  if (link_info.eh_frame_hdr
-      && !link_info.traditional_format)
+  if (!link_info.traditional_format)
     {
-      bfd *abfd, *elfbfd = NULL;
+      bfd *abfd, *elfbfd = NULL, *seenbfd  = NULL;
       bfd_boolean warn_eh_frame = FALSE;
       asection *s;
+      int seen_type = 0;
 
       for (abfd = link_info.input_bfds; abfd; abfd = abfd->link.next)
 	{
-	  if (bfd_get_flavour (abfd) == bfd_target_elf_flavour)
-	    elfbfd = abfd;
-	  if (!warn_eh_frame)
+	  int type = 0;
+	  for (s = abfd->sections; s && type < COMPACT_EH_HDR; s = s->next)
 	    {
-	      s = bfd_get_section_by_name (abfd, ".eh_frame");
-	      while (s != NULL
-		     && (s->size <= 8
-			 || bfd_is_abs_section (s->output_section)))
-		s = bfd_get_next_section_by_name (s);
-	      warn_eh_frame = s != NULL;
+	      const char *name = bfd_get_section_name (abfd, s);
+
+	      if (bfd_is_abs_section (s->output_section))
+		continue;
+
+	      if (CONST_STRNEQ (name, ".eh_frame_entry"))
+		type = COMPACT_EH_HDR;
+	      else if (strcmp (name, ".eh_frame") == 0
+		       && s->size > 8)
+		type = DWARF2_EH_HDR;
+	    }
+
+	  if (type != 0)
+	    {
+	      if (!elfbfd
+		  && (type == COMPACT_EH_HDR || link_info.eh_frame_hdr_type != 0))
+		{
+		  if (bfd_get_flavour (abfd) == bfd_target_elf_flavour)
+		    elfbfd = abfd;
+
+		  warn_eh_frame = TRUE;
+		}
+
+	      if (seen_type == 0)
+                {
+		  seen_type = type;
+                  seenbfd = abfd;
+                }
+	      else if (seen_type != type)
+		{
+		  einfo (_("%P%F: compact frame descriptions from %B"
+                           " are incompatible with DWARF2 .eh_frame"
+                           " from %B\n"), seenbfd,
+			 type == DWARF2_EH_HDR ? abfd : elfbfd);
+		  break;
+		}
 	    }
-	  if (elfbfd && warn_eh_frame)
-	    break;
 	}
+
+      if (seen_type == COMPACT_EH_HDR)
+	link_info.eh_frame_hdr_type = COMPACT_EH_HDR;
+
       if (elfbfd)
 	{
 	  const struct elf_backend_data *bed;
@@ -2199,7 +2230,7 @@ fragment <<EOF
       break;
 
     case OPTION_EH_FRAME_HDR:
-      link_info.eh_frame_hdr = TRUE;
+      link_info.eh_frame_hdr_type = DWARF2_EH_HDR;
       break;
 
     case OPTION_GROUP:
diff --git a/ld/ldlang.c b/ld/ldlang.c
index c4da07f..6cfd54c 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -6389,6 +6389,15 @@ lang_reset_memory_regions (void)
     }
 }
 
+/* Parse the Compact EH .eh_frame_entry sections.  */
+
+static void
+lang_parse_eh_frame_entries (void)
+{
+  if (bfd_parse_eh_frame_entries (link_info.output_bfd, &link_info) == FALSE)
+    einfo (_("%P%F: Failed to parse EH frame entries.\n"));
+}
+
 /* Worker for lang_gc_sections_1.  */
 
 static void
@@ -6772,6 +6781,10 @@ lang_process (void)
   lang_do_assignments (lang_mark_phase_enum);
   expld.phase = lang_first_phase_enum;
 
+  /* Parse .eh_frame_entry sections.  */
+  if (!link_info.relocatable)
+    lang_parse_eh_frame_entries ();
+
   /* Remove unreferenced sections if asked to.  */
   lang_gc_sections ();
 
diff --git a/ld/scripttempl/elf.sc b/ld/scripttempl/elf.sc
index 4368fd9..1ac576a 100644
--- a/ld/scripttempl/elf.sc
+++ b/ld/scripttempl/elf.sc
@@ -533,10 +533,11 @@ cat <<EOF
   ${CREATE_SHLIB-${SDATA2}}
   ${CREATE_SHLIB-${SBSS2}}
   ${OTHER_READONLY_SECTIONS}
-  .eh_frame_hdr : { *(.eh_frame_hdr) }
-  .eh_frame     ${RELOCATING-0} : ONLY_IF_RO { KEEP (*(.eh_frame)) }
+  .eh_frame_hdr : { *(.eh_frame_hdr) ${RELOCATING+*(.eh_frame_entry .eh_frame_entry.*)} }
+  .eh_frame     ${RELOCATING-0} : ONLY_IF_RO { KEEP (*(.eh_frame)) ${RELOCATING+*(.eh_frame.*)} }
   .gcc_except_table ${RELOCATING-0} : ONLY_IF_RO { *(.gcc_except_table
   .gcc_except_table.*) }
+  .gnu_extab ${RELOCATING-0} : ONLY_IF_RO { *(.gnu_extab*) }
   /* These sections are generated by the Sun/Oracle C++ compiler.  */
   .exception_ranges ${RELOCATING-0} : ONLY_IF_RO { *(.exception_ranges
   .exception_ranges*) }
@@ -549,7 +550,8 @@ cat <<EOF
   ${CREATE_PIE+${RELOCATING+. = ${SHLIB_DATA_ADDR-${DATA_SEGMENT_ALIGN}};}}
 
   /* Exception handling  */
-  .eh_frame     ${RELOCATING-0} : ONLY_IF_RW { KEEP (*(.eh_frame)) }
+  .eh_frame     ${RELOCATING-0} : ONLY_IF_RW { KEEP (*(.eh_frame)) ${RELOCATING+*(.eh_frame.*)} }
+  .gnu_extab    ${RELOCATING-0} : ONLY_IF_RW { *(.gnu_extab) }
   .gcc_except_table ${RELOCATING-0} : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
   .exception_ranges ${RELOCATING-0} : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) }
 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Patch] Gas support for MIPS Compact EH
  2014-11-17 16:10       ` Moore, Catherine
@ 2014-11-22 14:42         ` Richard Sandiford
  2015-02-08 16:59           ` Moore, Catherine
  2015-02-08 17:00           ` Moore, Catherine
  0 siblings, 2 replies; 31+ messages in thread
From: Richard Sandiford @ 2014-11-22 14:42 UTC (permalink / raw)
  To: Moore, Catherine; +Cc: binutils

"Moore, Catherine" <Catherine_Moore@mentor.com> writes:
> Hi Richard,
> Please find the updated Compact EH  patch for binutils. 
> This will likely still be a hard patch to review, but I tried to address
> the many comments that you made earlier.
> The main difference is the exception handling relocation type for the
> Linux toolchain.  It is no longer gprel based.
> The ELF and Linux tools both use pcrel32 and the EH-specific relocation
> type has been removed.
> I ran into a few issues with the recent changes to eh_frame handling for
> DWARF, I hopefully covered those with the Compact EH implementation.
> Please let me know what you think.  I hope we are close to converging on
> an implementation.

OK, thanks.  I started by going through my previous comments and
looking at how you addressed them in the new patch.  I think there
are still some things that need to be sorted out.

When doing the next patch, could you reply to the main points and
say how you dealt with them?  That'd make it a lot easier to see
how this is evolving.

>> -----Original Message-----
>> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
>> Sent: Saturday, February 08, 2014 11:34 AM
>> To: Moore, Catherine
>> Cc: binutils@sourceware.org
>> Subject: Re: [Patch] Gas support for MIPS Compact EH
>> 
>> Thanks for the updates.
>> 
>> "Moore, Catherine" <Catherine_Moore@mentor.com> writes:
>> >> > @@ -4514,6 +4517,23 @@ argument is not present, otherwise secon  or
>> >> > a symbol name.  The default after @code{.cfi_startproc} is
>> >> > @code{.cfi_lsda 0xff},  no LSDA.
>> >> >
>> >> > +@section @code{.cfi_inline_lsda} [@var{align}]
>> >> > +@code{.cfi_inline_lsda} marks the start of a LSDA data section and
>> >> > +switches to the corresponding @code{.gnu.extab} section.
>> >> > +It must be preceded by a CFI block containing a @code{.cfi_lsda}
>> >> > +directive and is only valid when generating compact EH frames (i.e.
>> >> > +with @code{.cfi_sections eh_frame_entry}.
>> >> > +
>> >> > +If a compact encoding is being used then the table header and
>> >> > +unwinding opcodes will be generated at this point, so that they
>> >> > +are immediately followed by the LSDA data.  The symbol referenced
>> >> > +by the @code{.cfi_lsda} directive should still be defined in case
>> >> > +a fallback FDE based encoding is used.
>> >> > +
>> >> > +The optional @var{align} argument specifies the alignment required.
>> >> > +The alignment is specified as a power of two, as with the
>> >> > +@code{.p2align} directive.
>> >>
>> >> Hmm, switching sections and emitting data feels very different in
>> >> style from the other .cfi directives, which just annotate code
>> >> without changing the flow of assembly.  I'd like to know other people's
>> thoughts on this.
>> >>
>> >> Also, how do you terminate the LSDA?  The documentation doesn't say
>> >> (but should :-)) and I couldn't see this directive in the spec either.
>> >
>> > The LSDA is terminated by a section switch.
>> 
>> OK.  Like I say, please mention this in the documentation.

The new version of the patch doesn't have the .cfi_inline_lsda documentation
that the previous patch had.  Please add it back :-)  But like I say above,
please also document how the data is terminated.

>> >> TBH, without tests, and without an
>> >> explanation of what the code is doing, I found this patch pretty hard
>> >> to review.  E.g.:
>> >>
>> >> > @@ -129,7 +140,12 @@ get_debugseg_name (segT seg, const char
>> >> >        dot = strchr (name + 1, '.');
>> >> >
>> >> >        if (!dollar && !dot)
>> >> > -	name = "";
>> >> > +	{
>> >> > +	  if (compact_eh && strcmp (name, ".text") != 0)
>> >> > +	    return concat (base_name, ".", name, NULL);
>> >> > +
>> >> > +	  name = "";
>> >> > +	}
>> >>
>> >> why is this change needed?  I.e., for a text section called something
>> >> like .foobar, why does compact_eh need to put things in a section
>> >> name ending in "..foobar", rather than in the main EH section?  I
>> >> assume the double dots are deliberate, e.g. to avoid confusion with
>> ".text.foobar"?
>> >
>> > I'm not sure why Paul put this is and the next hunk.  There were test
>> > failures without.  I can revisit this For the next iteration if
>> > necessary.
>> 
>> Yeah, if you could that'd be great.  The code can't really go in if
> there's no-
>> one around who understands what it does.
>> 
>> I assume it's just to ensure that each text section has its own
>> .eh_frame_entry, but in that case I think we should check based on the
>> base_name rather than compact_eh.  Or do we need the same treatment
>> for .gnu_extab.  If so, why?
>> 
>> A comment is needed at the very least.

The new hunk for this is:

> @@ -128,7 +241,15 @@ get_debugseg_name (segT seg, const char *base_name)
>        dot = strchr (name + 1, '.');
>  
>        if (!dollar && !dot)
> -	name = "";
> +	{
> +	  /* Ensure that a uniquely named .eh_frame_entry section
> +	     is created for each text section.  */
> +	  if (compact_eh
> +	       && !strcmp (base_name, ".eh_frame_entry")
> +	       && strcmp (name, ".text") != 0)
> +	    return concat (base_name, ".", name, NULL);
> +	  name = "";
> +	}

I don't think we want the compact_eh test here; just the base_name
check should be enough.  (Nit, sorry, but watch the indentation.
One less space before "&&".)

>> >> > @@ -833,14 +859,15 @@ dot_cfi_personality (int ignored ATTRIBU
>> >> >      }
>> >> >
>> >> >    if ((encoding & 0xff) != encoding
>> >> > -      || ((encoding & 0x70) != 0
>> >> > +      || ((((encoding & 0x70) != 0
>> >> >  #if CFI_DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr
>> >> > -	  && (encoding & 0x70) != DW_EH_PE_pcrel
>> >> > +	   && (encoding & 0x70) != DW_EH_PE_pcrel
>> >> >  #endif
>> >> >  	  )
>> >> > /* leb128 can be handled, but does something actually need it?
> */
>> >> > -      || (encoding & 7) == DW_EH_PE_uleb128
>> >> > -      || (encoding & 7) > DW_EH_PE_udata8)
>> >> > +	   || (encoding & 7) == DW_EH_PE_uleb128
>> >> > +	   || (encoding & 7) > DW_EH_PE_udata8)
>> >> > +	&& !tc_cfi_special_encoding (encoding)))
>> >> >      {
>> >> >        as_bad (_("invalid or unsupported encoding in .cfi_personality"));
>> >> >        ignore_rest_of_line ();
>> >>
>> >> What does a "special" encoding mean?  Again, this hook should be
>> >> documented in internals.texi.  And do we really want to change the
>> >> set of encodings that are allowed for DWARF, even on MIPS systems
>> >> that predate compat EH?
>> >>
>> > Special means that we have code in the backend to emit a reloc for it.
>> > In the revised patch, it goes along with Tc_cfi_reloc_for_encoding.
>> > It also looks like internals.texi fails to document many of the
>> > tc_macros and it doesn't appear to be built into a .info fiie.
>> 
>> That's no reason not to document new hooks though.  I know I've used
>> internals.texi to read more about a hook in the past.  If you don't
> want to put
>> it there though then please at least put it in a comment instead.

The hunk for this is:

> diff --git a/gas/doc/internals.texi b/gas/doc/internals.texi
> index cc4089b..9dd0bd0 100644
> --- a/gas/doc/internals.texi
> +++ b/gas/doc/internals.texi
> @@ -1473,6 +1473,10 @@ completed, but before the relocations have been generated.
>  If you define this macro, GAS will call it after the relocs have been
>  generated.
>  
> +@item tc_cfi_reloc_for_encoding
> +@cindex tc_cfi_reloc_for_encoding
> +You may define this macro to indicate whether a cfi encoding requires a relocation.

That makes it sound like a boolean, whereas it returns a reloc.  Please
also mention that it controls whether compact EH is supported.  The 80-
character limit applies to documentation too.

>> >> > +  demand_empty_rest_of_line ();
>> >> > +  ccseg = CUR_SEG (last_fde);
>> >> > +  /* Open .gnu_extab section.  */
>> >> > +  cfi_seg = get_cfi_seg (ccseg, ".gnu_extab",
>> >> > +			 (SEC_ALLOC | SEC_LOAD | SEC_DATA
>> >> > +			  | DWARF2_EH_FRAME_READ_ONLY),
>> >> > +			 1);
>> >> > +#ifdef md_fix_up_eh_frame
>> >> > +  md_fix_up_eh_frame (cfi_seg);
>> >> > +#else
>> >> > +  (void) cfi_seg;
>> >> > +#endif
>> >> > +
>> >> > +  frag_align (align, 0, 0);
>> >> > +  record_alignment (now_seg, align);  if (last_fde->eh_header_type
>> >> > + == EH_COMPACT_HAS_LSDA)
>> >> > +    output_compact_unwind_data (last_fde, align);
>> >>
>> >> Please could you explain the EH_COMPACT_LEGACY handling here?
>> >
>> > Would you please clarify this question?  I don't see the reference to
>> > EH_COMPACT_LEGACY.
>> 
>> That was the problem :-)  Further up there's:
>> 
>>   if (last_fde->eh_header_type != EH_COMPACT_LEGACY
>>       && last_fde->eh_header_type != EH_COMPACT_HAS_LSDA)
>>     {
>>       as_bad (_(".cfi_inline_lsda seen for frame without .cfi_lsda"));
>>       ignore_rest_of_line ();
>>       return;
>>     }
>> 
>> So what does this code mean/do in the:
>> 
>>   last_fde->eh_header_type == EH_COMPACT_LEGACY
>> 
>> case?

Still not sure about this -- please could you clarify?  The context
is this part of dot_cfi_inline_lsda:

> +  if (last_fde->eh_header_type != EH_COMPACT_LEGACY
> +      && last_fde->eh_header_type != EH_COMPACT_HAS_LSDA)
> +    {
> +      as_bad (_(".cfi_inline_lsda seen for frame without .cfi_lsda"));
> +      ignore_rest_of_line ();
> +      return;
> +    }
> +
> +#ifdef md_flush_pending_output
> +  md_flush_pending_output ();
> +#endif
> +
> +  align = get_absolute_expression ();
> +  if (align > max_alignment)
> +    {
> +      align = max_alignment;
> +      as_bad (_("Alignment too large: %d assumed."), align);
> +    }
> +  else if (align < 0)
> +    {
> +      as_warn (_("Alignment negative: 0 assumed."));
> +      align = 0;
> +    }
> +
> +  demand_empty_rest_of_line ();
> +  ccseg = CUR_SEG (last_fde);
> +  /* Open .gnu_extab section.  */
> +  get_cfi_seg (ccseg, ".gnu_extab",
> +	       (SEC_ALLOC | SEC_LOAD | SEC_DATA
> +		| DWARF2_EH_FRAME_READ_ONLY),
> +	       1);
>  
> +  frag_align (align, 0, 0);
> +  record_alignment (now_seg, align);
> +  if (last_fde->eh_header_type == EH_COMPACT_HAS_LSDA)
> +    output_compact_unwind_data (last_fde, align);
> +
> +  last_fde = NULL;
> +
> +  return;

The first "if" statement allows the directive to be used for
EH_COMPACT_LEGACY, but what does the directive mean/do in that case?

>> > diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index 0aab5fa..b7d7df0
>> > 100644
>> > --- a/bfd/elf-bfd.h
>> > +++ b/bfd/elf-bfd.h
>> > @@ -381,8 +381,10 @@ struct eh_frame_hdr_info  {
>> >    struct htab *cies;
>> >    asection *hdr_sec;
>> > -  unsigned int fde_count, array_count;
>> > +  unsigned int fde_count, array_count, allocated_entries;
>> >    struct eh_frame_array_ent *array;
>> > +  /* eh_frame_entry fragments.  */
>> > +  asection **entries;
>> >    /* TRUE if we should try to merge CIEs between input sections.  */
>> >    bfd_boolean merge_cies;
>> >    /* TRUE if all .eh_frames have been parsd.  */
>> 
>> I'm not sure there's enough commonality between the DWARF and compact
>> versions to share this structure.  Either we should have separate structures
>> or use a union to separate out the format-specific bits.

I see you've done this, thanks, but:

> +struct dwarf_eh_frame_hdr_info
> +{
> +  struct eh_frame_array_ent *array;
> +};
> +
> +struct compact_eh_frame_hdr_info
> +{
> +  unsigned int allocated_entries;
> +  /* eh_frame_entry_fragments.  */
> +  asection **entries;
> +};
> +
>  struct eh_frame_hdr_info
>  {
>    struct htab *cies;
>    asection *hdr_sec;
>    unsigned int fde_count, array_count;
> -  struct eh_frame_array_ent *array;
>    /* TRUE if .eh_frame_hdr should contain the sorted search table.
>       We build it if we successfully read all .eh_frame input sections
>       and recognize them.  */
>    bfd_boolean table;
> +  bfd_boolean frame_hdr_is_compact;
> +  union
> +    {
> +      struct dwarf_eh_frame_hdr_info dwarf;
> +      struct compact_eh_frame_hdr_info compact;
> +    }
> +  u;
>  };
>  
>  /* Enum used to identify target specific extensions to the elf_obj_tdata

...aren't cies, fde_count, array_count and table specific to the DWARF
version too?  My point was that if we went for the union approach,
the only fields in the common structure should be those that are
needed by both formats.  It looks like that's just hdr_sec.

If moving all of them seems like too much work, I think we should go
for separate structures instead.

>> > +	}
>> > +
>> > +      BFD_ASSERT (hdr_info->entries);
>> > +    }
>> > +
>> > +  hdr_info->entries[hdr_info->array_count++] = sec; }
>> > +
>> > +/* Parse a .eh_frame_entry section.  Figure out which text section it
>> > +   references.  */
>> > +
>> > +void
>> > +_bfd_elf_parse_eh_frame_entry (bfd *abfd, struct bfd_link_info *info,
>> > +			       asection *sec, struct elf_reloc_cookie *cookie,
>> > +			       bfd_boolean remember)
>> 
>> This does more than the comment says and the name implies; the
>> REMEMBER stuff isn't mentioned.
>> 
>> The patch tries to do the parsing during bfd_elf_discard_info, but since the
>> parsing wants to be able to fail with an error, I think we need to do it in an
>> earlier pass.  We can then return a bfd_boolean success code and propagate
>> error returns up, which the current patch doesn't do.
>> Ideally we'd put the pass somewhere before GC, so that both the GC and
>> bfd_elf_discard_info stages can assume parsed .eh_frame_entry sections.
>> 
>> Having bfd_elf_discard_info add info (as per REMEMBER == TRUE) seems a
>> bit counterintuitive.  I think the earlier pass should record all
>> .eh_frame_entry sections and then the code currently in
>> _bfd_elf_end_eh_frame_parsing (but see below) should remove unwanted
>> entries from the eh_frame_hdr_info array.

I see you've added the earlier pass, thanks, but errors aren't always
reported back up.  The function:

> +/* Parse a .eh_frame_entry section.  Figure out which text section it
> +   references.  */
> +
> +void
> +_bfd_elf_parse_eh_frame_entry (struct bfd_link_info *info,
> +			       asection *sec, struct elf_reloc_cookie *cookie)
> +{
> [...]
> +fail:
> +  (*_bfd_error_handler) (_("%B: failed to process .eh_frame_entry"), sec->owner);
> +}

doesn't tell the caller (and eventually the ld code) that a problem
occured.  The function ought to return a bfd_boolean success code,
like its callers.  Also, the error here seems to duplicate the
eventual ld one:

    einfo (_("%P%F: Failed to parse EH frame entries.\n"));

without really providing any more information.

Don't shoot me, but it would probably be cleaner to make this an
ELF-specific pass and call it from elf32.em instead.  That'll avoid
having to do all the aout, bout, etc. stuff and would allow the ld code
to refer to ELFisms like .eh_frame_entry itself.

>> > +/* Finish a pass over all .eh_frame and eh_frame_entry sections.  */
>> > +
>> > +bfd_boolean
>> >  _bfd_elf_end_eh_frame_parsing (struct bfd_link_info *info)  {
>> >    struct eh_frame_hdr_info *hdr_info;
>> > +  unsigned int i;
>> >
>> >    hdr_info = &elf_hash_table (info)->eh_info;
>> >    hdr_info->parsed_eh_frames = TRUE;
>> > +
>> > +  if (hdr_info->array_count == 0 || info->eh_frame_hdr < 2)
>> > +    return FALSE;
>> > +
>> > +  qsort (hdr_info->entries, hdr_info->array_count,
>> > +	 sizeof (asection *), cmp_eh_frame_hdr);
>> > +
>> > +  for (i = 0; i < hdr_info->array_count - 1; i++)
>> > +    {
>> > +      add_eh_frame_hdr_terminator (hdr_info->entries[i],
>> > +				   hdr_info->entries[i + 1]);
>> > +    }
>> > +
>> > +  /* Add a CANTUNWIND terminator after the last entry.  */
>> > + add_eh_frame_hdr_terminator (hdr_info->entries[i], NULL);  return
>> > + TRUE;
>> 
>> This routine is called from both bfd_elf_gc_sections and
>> bfd_elf_discard_info but I think you only want it for bfd_elf_discard_info.
>> So perhaps this should be a separate function.
>> 
>> I wonder whether we could instead insert CANTUNWINDs earlier (say in the
>> non-dynamic part of size_dynamic_sections) based on the link order.
>> I.e. rather than comparing the start and end addresses of two sections, we
>> could just walk the sections in the order that they're going to be linked.
>> 
>> It sounds like doing it that way would be more direct and more efficient.
>> Returning TRUE here forces the linker to map sections twice.

Note that the generic version of _bfd_elf_end_eh_frame_parsing was removed
in the meantime.  Your patch adds it back, but I can't see where it gets
called.  Unless I'm missing something, this suggests that the tests
don't exercise this code and that extra tests might be needed.

>> > @@ -1271,6 +1465,45 @@ _bfd_elf_eh_frame_present (struct
>> bfd_link_info *info)
>> >    return FALSE;
>> >  }
>> >
>> > +/* Return true if there is at least one .eh_frame_entry section in
>> > +   input files.  */
>> > +bfd_boolean
>> > +_bfd_elf_eh_frame_entry_present (struct bfd_link_info *info) {
>> > +  asection *o;
>> > +  bfd *abfd;
>> > +
>> > +  for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link_next)
>> > +    {
>> > +      for (o = abfd->sections; o; o = o->next)
>> > +	{
>> > +	  const char *name = bfd_get_section_name (abfd, o);
>> > +
>> > +	  if (strcmp (name, ".eh_frame_entry")
>> > +	      && !bfd_is_abs_section (o->output_section))
>> > +	    {
>> > +	      if (strcmp (o->output_section->name, ".eh_frame_hdr"))
>> > +		return TRUE;
>> > +	      else
>> > +		{
>> > +		  (*_bfd_error_handler)
>> > +			(_("error: an '.eh_frame_entry'"
>> > +			   " input section that is not mapped to the"
>> > +			   " '.eh_frame_hdr' output section was seen"));
>> > +		  (*_bfd_error_handler)
>> > +			(_("note: try adding '*(.eh_frame_entry"
>> > +			   " .eh_frame_entry.*)' to the '.eh_frame_hdr'"
>> > +			   " output section description in the linker"
>> > +			   " script"));
>> > +		  bfd_set_error (bfd_error_invalid_operation);
>> > +		  return FALSE;
>> > +		}
>> > +	    }
>> > +	}
>> > +    }
>> > +  return FALSE;
>> 
>> The caller can't tell "FALSE because an error was reported" from "FALSE
>> because there was no .eh_frame_entry".  We should separate out the two
>> cases and propagate error returns up.

It looks like you dealt with this by removing the error checking.
I think we still want it, but it should be done separately and
in a context where the error can be propagated.

>> > @@ -1387,6 +1636,71 @@ _bfd_elf_eh_frame_section_offset (bfd
>> *output_bfd ATTRIBUTE_UNUSED,
>> >  	  + extra_augmentation_data_bytes (sec_info->entry + mid));  }
>> >
>> > +/* Write out .eh_frame_entry section.  Add CANTUNWIND terminator if
>> needed.
>> > +   Also check that the contents look sane.  */
>> > +
>> > +bfd_boolean
>> > +_bfd_elf_write_section_eh_frame_entry (bfd *abfd,
>> > +				       asection *sec,
>> > +				       bfd_byte *contents)
>> 
>> Formatting: first two arguments fit on a line.

Still present.

>> > +  text_sec = (asection *) elf_section_data (sec)->sec_info;
>> > +  addr = text_sec->output_section->vma + text_sec->output_offset
>> > +	 + text_sec->size;
>> > +  addr &= ~1;
>> > +  addr -= (sec->output_section->vma + sec->output_offset +
>> > +sec->rawsize);
>> > +  BFD_ASSERT ((addr & 1) == 0);
>> 
>> It looks like this could trigger for odd-sized input text sections.
>> I think it should be an error instead of an assert.

I see you've done this with:

> +  if (addr & 1)
> +    {
> +      (*_bfd_error_handler) (_("%B: %s invalid input section size"),
> +			     sec->owner, sec->name);
> +      return FALSE;
> +    }
> +  if (last_addr >= addr + sec->rawsize)
> +    {
> +      (*_bfd_error_handler) (_("%B: %s points past end of text section"),
> +			     sec->owner, sec->name);
> +      return FALSE;
> +    }

but I think we want "bfd_set_error (bfd_error_bad_value);" too.

>> > +  if (last_addr >= addr + sec->rawsize)
>> > +    {
>> > +      (*_bfd_error_handler) (_("%B: %s points past end of text section"),
>> > +			     sec->owner, sec->name);
>> > +      return FALSE;
>> > +    }
>> > +
>> > +  if (sec->size == sec->rawsize)
>> > +    return TRUE;
>> > +
>> > +  BFD_ASSERT (sec->size == sec->rawsize + 8);
>> > +  BFD_ASSERT ((addr & 1) == 0);
>> > +  bfd_put_32 (abfd, addr, cantunwind);
>> > +  bfd_put_32 (abfd, 0x015d5d01, cantunwind + 4);
>> > +  return bfd_set_section_contents (abfd, sec->output_section,
>> cantunwind,
>> > +				   sec->output_offset + sec->rawsize, 8);
>> 
>> ARM and c6x seem to use 0 rather than 0x5d as the "no unwind" opcode, is
>> that right?  If so, I think this should be a hook.

It doesn't look like you addressed this part.

>> The decision about whether to insert the CANTUNWIND is made during
>> bfd_elf_discard_info, but addresses can change after that “thanks”
>> to relaxation.  So this could in principle end up emitting a CANTUNWIND for
>> the same address as the following text section.

As above, it looks like nothing ever calls
_bfd_elf_write_section_eh_frame_entry in the new version of the patch.
If the tests don't pick that up then I think we need some more :-)

>> > @@ -10039,6 +10040,10 @@ elf_link_input_bfd (struct elf_final_link_info
>> *flinfo, bfd *input_bfd)
>> >  	      return FALSE;
>> >  	  }
>> >  	  break;
>> > +	case SEC_INFO_TYPE_EH_FRAME_ENTRY:
>> > +	    if (! _bfd_elf_write_section_eh_frame_entry (output_bfd, o,
>> contents))
>> > +	      return FALSE;
>> > +	  break;
>> 
>> Excess indentation of the "if".

Not sure: why is this no longer in the patch?

>> > @@ -11807,6 +11814,13 @@ _bfd_elf_gc_mark (struct bfd_link_info *info,
>> >  	}
>> >      }
>> >
>> > +  eh_frame = elf_section_eh_frame_entry (sec);  if (eh_frame &&
>> > + !eh_frame->gc_mark)
>> > +    {
>> > +    if (!_bfd_elf_gc_mark (info, eh_frame, gc_mark_hook))
>> > +      return FALSE;
>> > +    }
>> 
>> In this context we should be using "ret":
>> 
>>   eh_frame = elf_section_eh_frame_entry (sec);
>>   if (ret && eh_frame && !eh_frame->gc_mark)
>>     ret = _bfd_elf_gc_mark (info, eh_frame, gc_mark_hook);

You changed this to:

> +  if (eh_frame && !eh_frame->gc_mark)
> +    {
> +      if (!_bfd_elf_gc_mark (info, eh_frame, gc_mark_hook))
> +	ret = FALSE;
> +    }

but the point was also that we don't want to try another mark once "ret"
is already FALSE.  The "ret &&" part of the condition was important.

>> > @@ -12190,22 +12204,42 @@ bfd_elf_gc_sections (bfd *abfd, struct
>> bfd_link_info *info)
>> >    bed->gc_keep (info);
>> >
>> >    /* Try to parse each bfd's .eh_frame section.  Point
>> elf_eh_frame_section
>> > -     at the .eh_frame section if we can mark the FDEs individually.  */
>> > +     at the .eh_frame section if we can mark the FDEs individually.
>> > +     Establish links from text sections to their corresponding
>> > +     .eh_frame_entry sections.  */
>> >    _bfd_elf_begin_eh_frame_parsing (info);
>> >    for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
>> >      {
>> >        asection *sec;
>> >        struct elf_reloc_cookie cookie;
>> >
>> > -      sec = bfd_get_section_by_name (sub, ".eh_frame");
>> > -      while (sec && init_reloc_cookie_for_section (&cookie, info, sec))
>> > +      if (!init_reloc_cookie (&cookie, info, sub))
>> > +	return FALSE;
>> > +
>> > +      if (info->eh_frame_hdr < 2)
>> >  	{
>> > -	  _bfd_elf_parse_eh_frame (sub, info, sec, &cookie);
>> > -	  if (elf_section_data (sec)->sec_info
>> > -	      && (sec->flags & SEC_LINKER_CREATED) == 0)
>> > -	    elf_eh_frame_section (sub) = sec;
>> > -	  fini_reloc_cookie_for_section (&cookie, sec);
>> > -	  sec = bfd_get_next_section_by_name (sec);
>> > +	  sec = bfd_get_section_by_name (sub, ".eh_frame");
>> > +	  if (sec && init_reloc_cookie_rels (&cookie, info, sub, sec))
>> > +	    {
>> > +	      _bfd_elf_parse_eh_frame (sub, info, sec, &cookie);
>> > +	      if (elf_section_data (sec)->sec_info
>> > +		  && (sec->flags & SEC_LINKER_CREATED) == 0)
>> > +		elf_eh_frame_section (sub) = sec;
>> > +	      fini_reloc_cookie_for_section (&cookie, sec);
>> > +	    }
>> > +	}
>> > +      else
>> > +	{
>> > +	  for (sec = sub->sections; sec; sec = sec->next)
>> > +	    {
>> > +	      if (CONST_STRNEQ (bfd_section_name (sub, sec),
>> ".eh_frame_entry")
>> > +		  && init_reloc_cookie_rels (&cookie, info, sub, sec))
>> > +		{
>> > +		  _bfd_elf_parse_eh_frame_entry (sub, info, sec, &cookie,
>> > +						 FALSE);
>> > +		  fini_reloc_cookie_for_section (&cookie, sec);
>> > +		}
>> > +	    }
>> 
>> This changes the info->eh_frame_hdr != 2 case to only handle the first
>> .eh_frame section.  ("while" -> "if").
>> 
>> Also, the init/fini_reloc_cookie* calls don't match up.  I assume the
> idea is to
>> avoid excessive allocation and freeing of the locsyms, but in that case you
>> should use fini_reloc_cookie_rels instead of fini_reloc_cookie_for_section
>> and call fini_reloc_cookie at the end.
>> At the moment I think this leaks memory if there are no EH sections.
>> 
>> I don't know either way whether splitting the init_reloc_cookie_for_section
>> call up is a win or not for .eh_frame.  It will be a win if there are
> multiple EH
>> sections but a loss if there are none, since we then initialise and
> free the bfd-
>> level information unnecessarily.  So I think it might make sense to keep the
>> .eh_frame code as it is now and restrict the *_rels to the new code.

You changed this to:

> @@ -12171,7 +12256,9 @@ bfd_elf_gc_sections (bfd *abfd, struct bfd_link_info *info)
>  
>    /* Try to parse each bfd's .eh_frame section.  Point elf_eh_frame_section
>       at the .eh_frame section if we can mark the FDEs individually.  */
> -  for (sub = info->input_bfds; sub != NULL; sub = sub->link.next)
> +  for (sub = info->input_bfds;
> +       info->eh_frame_hdr_type != COMPACT_EH_HDR && sub != NULL;
> +       sub = sub->link.next)
>      {
>        asection *sec;
>        struct elf_reloc_cookie cookie;

where info->eh_frame_hdr_type is an invariant.  I assume this was just
to avoid reformatting the block, but please use:

  if (info->eh_frame_hdr_type != COMPACT_EH_HDR)
    for (...)

instead.  Fortunately the block's not that big. :-)

>> > diff --git a/gas/config/tc-mips.h b/gas/config/tc-mips.h index
>> > c7eaa04..a300062 100644
>> > --- a/gas/config/tc-mips.h
>> > +++ b/gas/config/tc-mips.h
>> > @@ -177,7 +177,9 @@ extern enum dwarf2_format mips_dwarf2_format
>> > (asection *);
>> >
>> >  extern int mips_dwarf2_addr_size (void);  #define
>> > DWARF2_ADDR_SIZE(bfd) mips_dwarf2_addr_size () -#define
>> > DWARF2_FDE_RELOC_SIZE mips_dwarf2_addr_size ()
>> > +#define DWARF2_FDE_RELOC_SIZE (compact_eh ? 4 :
>> mips_dwarf2_addr_size
>> > +()) #define DWARF2_FDE_RELOC_ENCODING(enc) \
>> > +  (enc | (compact_eh ? DW_EH_PE_pcrel : 0))
>> 
>> What case is this handling?  Please explain this a bit more.

Doesn't look like you've addressed this bit.

>> > @@ -4566,6 +4586,22 @@ argument is not present, otherwise second
>> > argument should be a constant  or a symbol name.  The default after
>> > @code{.cfi_startproc} is @code{.cfi_lsda 0xff},  no LSDA.
>> >
>> > +@section @code{.cfi_inline_lsda} [@var{align}]
>> > +@code{.cfi_inline_lsda} marks the start of a LSDA data section and
>> > +switches to the corresponding @code{.gnu.extab} section.
>> > +Must be preceded by a CFI block containing a @code{.cfi_lsda} directive.
>> > +Only valid when generating compact EH frames (i.e.
>> > +with @code{.cfi_sections eh_frame_entry}.
>> 
>> Missing ")".

As above, this part of the doc is no longer there.

>> > @@ -4643,6 +4679,20 @@ mark a code segment that has only one return
>> > address which is reached  by a direct branch and no copy of the return
>> > address exists in memory  or another register.
>> >
>> > +@section @code{.cfi_epilogue_begin}
>> > +A pseudo-operation which marks the beginning of the epilogue in a
>> > +given function.  This is currently used by the compact unwind-table
>> > +implementation (for exception handling), which assumes a single
>> > +register state for unwinding throughout the body of a function.  The
>> > +function is scanned, and CFI directives are rewritten in a compact
>> > +form.  However, recent versions of GCC emit unwind information for
>> > +function epilogues, which should not be represented in this compact
>> > +form: hence, any CFI directives which occur in a function after
>> > +@code{.cfi_epilogue_end} are not processed.
>> > +
>> > +This operation only affects the internals of the assembler, and is
>> > +not represented in the binary output.
>> 
>> Don't really follow this, sorry.  Can you give an example, or better yet, a
>> testcase?

Has .cfi_epilogue_begin been dropped?

>> 
>> > @@ -161,6 +278,9 @@ alloc_debugseg_item (segT seg, int subseg, char
>> > *name)  static segT  is_now_linkonce_segment (void)  {
>> > +  if (compact_eh)
>> > +    return now_seg;
>> > +
>> 
>> Please add a comment explaining why this is correct.

The hunk for this is:

> +  /* We track the current segment in the cfi_insn_data struct and
> +     the cfi_insn_data struct for Compact EH.  */
> +  if (compact_eh)
> +    return now_seg;

but the comment repeats "the cfi_insn_data struct".  TBH I'm still not
sure why/whether this is correct, but let's sort the other things out first.

>> > -/* Emit a single byte into the current segment.  */
>> > -
>> > -static inline void
>> > -out_one (int byte)
>> > +static segT
>> > +get_cfi_seg (segT cseg, const char *base, flagword flags, int align)
>> >  {
>> > -  FRAG_APPEND_1_CHAR (byte);
>> > +  if (SUPPORT_FRAME_LINKONCE || ((flags & SEC_DEBUGGING) == 0 &&
>> compact_eh))
>> > +    {
>> 
>> Please add a comment here too to explain the SEC_DEBUGGING test.

It doesn't look like you addressed this bit.

>> > +static void
>> > +output_compact_unwind_data (struct fde_entry *fde, int align)
>> 
>> Probably worth a comment here, since it wasn't obvious to me the "align" is
>> the alignment of the end of the data rather than the start.

I don't think you addressed this bit.  (In case it wasn't clear, I was
suggesting adding a function comment that says what the function does and
what its arguments are, etc.)

>> > +	  md_number_to_chars (p, 0, 4);
>> > +	  fix_new (frag_now, p - frag_now->fr_literal, 4, exp.X_add_symbol,
>> > +		   exp.X_add_number, howto->pc_relative, code);
>> 
>> What's the significance of exp.X_add_number here?  If it was supposed to
>> be initialised to 0 above then I think it would be easier to leave the "exp"
>> stuff alone and just use fde->start_address and 0 directly in this
> fix_new call.
>> Certainly...
>> 
>> >    else
>> >      {
>> > -      exp.X_op = O_symbol;
>> > -      exp.X_add_symbol = fde->start_address;
>> >        exp.X_add_number = 0;
>> >        addr_size = DWARF2_ADDR_SIZE (stdoutput);
>> >        emit_expr (&exp, addr_size);
>> 
>> ...separating the add_symbol and add_number feels odd.

This is now:

> @@ -1575,29 +1881,43 @@ output_fde (struct fde_entry *fde, struct cie_entry *cie,
>        TC_DWARF2_EMIT_OFFSET (cie->start_address, offset_size);
>      }
>  
> +  exp.X_op = O_symbol;
> +  exp.X_add_symbol = fde->start_address;

(A)

>    if (eh_frame)
>      {
> -      exp.X_op = O_subtract;
> -      exp.X_add_number = 0;
> +      bfd_reloc_code_real_type code
> +	= tc_cfi_reloc_for_encoding (cie->fde_encoding);
> +      if (code !=  BFD_RELOC_NONE)
> +	{
> +	  reloc_howto_type *howto = bfd_reloc_type_lookup (stdoutput, code);
> +	  char *p = frag_more (4);
> +	  md_number_to_chars (p, 0, 4);
> +	  fix_new (frag_now, p - frag_now->fr_literal, 4, fde->start_address,
> +		   0, howto->pc_relative, code);
> +	}
> +      else
> +	{
> +	  exp.X_op = O_subtract;
> +	  exp.X_add_number = 0;

(B)

>  #if CFI_DIFF_EXPR_OK
> -      exp.X_add_symbol = fde->start_address;
> -      exp.X_op_symbol = symbol_temp_new_now ();
> -      emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code offset.  */
> +	  exp.X_add_symbol = fde->start_address;
> +	  exp.X_op_symbol = symbol_temp_new_now ();
> +	  emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code offset.  */
>  #else
> -      exp.X_op = O_symbol;
> -      exp.X_add_symbol = fde->start_address;
> -#ifdef tc_cfi_emit_pcrel_expr
> -      tc_cfi_emit_pcrel_expr (&exp, DWARF2_FDE_RELOC_SIZE);	 /* Code offset.  */
> +	  exp.X_op = O_symbol;
> +	  exp.X_add_symbol = fde->start_address;
> +
> +#if defined(tc_cfi_emit_pcrel_expr)
> +	  tc_cfi_emit_pcrel_expr (&exp, DWARF2_FDE_RELOC_SIZE);	 /* Code offset.  */
>  #else
> -      emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code offset.  */
> +	  emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code offset.  */
>  #endif
>  #endif
> +	}
>        addr_size = DWARF2_FDE_RELOC_SIZE;
>      }
>    else
>      {
> -      exp.X_op = O_symbol;
> -      exp.X_add_symbol = fde->start_address;

(C)

>        exp.X_add_number = 0;
>        addr_size = DWARF2_ADDR_SIZE (stdoutput);
>        emit_expr (&exp, addr_size);

(C) is still hoisted to (A), but the only use of "exp" in the
"eh_frame" arm is (rightly) at (B), which overrides what (A) does.
Please just drop (A) and leave the "else" arm unmodified.

>> > @@ -1888,7 +2223,17 @@ cfi_finish (void)
>> >
>> >  	  for (fde = all_fde_data; fde ; fde = fde->next)
>> >  	    {
>> > -	      if (SUPPORT_FRAME_LINKONCE)
>> > +	      if ((fde->sections & CFI_EMIT_eh_frame) == 0)
>> > +		continue;
>> > +
>> > +#if SUPPORT_COMPACT_EH
>> > +	      if (fde->eh_header_type == EH_COMPACT_HAS_LSDA)
>> > +		fde->eh_header_type = EH_COMPACT_LEGACY;
>> > +
>> > +	      if (fde->eh_header_type != EH_COMPACT_LEGACY)
>> > +		continue;
>> > +#endif
>> 
>> Please add a comment explaining this.

I don't think you addressed this.

>> > @@ -1958,6 +2394,9 @@ cfi_finish (void)
>> >
>> >  	  for (fde = all_fde_data; fde ; fde = fde->next)
>> >  	    {
>> > +	      if ((fde->sections & CFI_EMIT_debug_frame) == 0)
>> > +		continue;
>> > +
>> >  	      if (SUPPORT_FRAME_LINKONCE)
>> >  		{
>> >  		  if (HANDLED (fde))
>> 
>> Please add a comment explaining why this is needed/correct.

Same here.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 31+ messages in thread

* RE: [Patch] Gas support for MIPS Compact EH
  2014-11-22 14:42         ` Richard Sandiford
@ 2015-02-08 16:59           ` Moore, Catherine
  2015-02-08 17:00           ` Moore, Catherine
  1 sibling, 0 replies; 31+ messages in thread
From: Moore, Catherine @ 2015-02-08 16:59 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: binutils

> -----Original Message-----
> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> Sent: Saturday, November 22, 2014 9:43 AM
> To: Moore, Catherine
> Cc: binutils@sourceware.org
> Subject: Re: [Patch] Gas support for MIPS Compact EH
> 
> "Moore, Catherine" <Catherine_Moore@mentor.com> writes:
> > Hi Richard,
> > Please find the updated Compact EH  patch for binutils.
> > This will likely still be a hard patch to review, but I tried to 
> > address the many comments that you made earlier.
> > The main difference is the exception handling relocation type for 
> > the Linux toolchain.  It is no longer gprel based.
> > The ELF and Linux tools both use pcrel32 and the EH-specific 
> > relocation type has been removed.
> > I ran into a few issues with the recent changes to eh_frame handling 
> > for DWARF, I hopefully covered those with the Compact EH
> implementation.
> > Please let me know what you think.  I hope we are close to 
> > converging on an implementation.
> 
> OK, thanks.  I started by going through my previous comments and 
> looking at how you addressed them in the new patch.  I think there are 
> still some things that need to be sorted out.
> 
> When doing the next patch, could you reply to the main points and say 
> how you dealt with them?  That'd make it a lot easier to see how this is evolving.

Yes, I'll do that.  I'm sorry for the missing pieces of the last patch.  My rebase had some unexpected results that I didn't catch before I posted.
Your comment that additional tests would have caught those is correct.  I've added some link tests to help out with that.  I think that extending the readelf -u (dump unwind option) would be useful for additional tests, but I don't want to make that additional development a requirement for approval of this patch.  There are still one or two outstanding bits, but I'd like to defer those until I see your comments on the current patch.
> 
> >> -----Original Message-----
> >> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> >> Sent: Saturday, February 08, 2014 11:34 AM
> >> To: Moore, Catherine
> >> Cc: binutils@sourceware.org
> >> Subject: Re: [Patch] Gas support for MIPS Compact EH
> >>
> >> Thanks for the updates.
> >>
> >> "Moore, Catherine" <Catherine_Moore@mentor.com> writes:
> >> >> > @@ -4514,6 +4517,23 @@ argument is not present, otherwise 
> >> >> > secon or a symbol name.  The default after 
> >> >> > @code{.cfi_startproc} is @code{.cfi_lsda 0xff},  no LSDA.
> >> >> >
> >> >> > +@section @code{.cfi_inline_lsda} [@var{align}] 
> >> >> > +@code{.cfi_inline_lsda} marks the start of a LSDA data 
> >> >> > +section and switches to the corresponding @code{.gnu.extab} section.
> >> >> > +It must be preceded by a CFI block containing a 
> >> >> > +@code{.cfi_lsda} directive and is only valid when generating
> compact EH frames (i.e.
> >> >> > +with @code{.cfi_sections eh_frame_entry}.
> >> >> > +
> >> >> > +If a compact encoding is being used then the table header and 
> >> >> > +unwinding opcodes will be generated at this point, so that 
> >> >> > +they are immediately followed by the LSDA data.  The symbol 
> >> >> > +referenced by the @code{.cfi_lsda} directive should still be 
> >> >> > +defined in case a fallback FDE based encoding is used.
> >> >> > +
> >> >> > +The optional @var{align} argument specifies the alignment
> required.
> >> >> > +The alignment is specified as a power of two, as with the 
> >> >> > +@code{.p2align} directive.
> >> >>
> >> >> Hmm, switching sections and emitting data feels very different 
> >> >> in style from the other .cfi directives, which just annotate 
> >> >> code without changing the flow of assembly.  I'd like to know 
> >> >> other people's
> >> thoughts on this.
> >> >>
> >> >> Also, how do you terminate the LSDA?  The documentation doesn't 
> >> >> say (but should :-)) and I couldn't see this directive in the spec either.
> >> >
> >> > The LSDA is terminated by a section switch.
> >>
> >> OK.  Like I say, please mention this in the documentation.
> 
> The new version of the patch doesn't have the .cfi_inline_lsda 
> documentation that the previous patch had.  Please add it back :-)  
> But like I say above, please also document how the data is terminated.

It's back.
> 
> >> >> TBH, without tests, and without an explanation of what the code 
> >> >> is doing, I found this patch pretty hard to review.  E.g.:
> >> >>
> >> >> > @@ -129,7 +140,12 @@ get_debugseg_name (segT seg, const char
> >> >> >        dot = strchr (name + 1, '.');
> >> >> >
> >> >> >        if (!dollar && !dot)
> >> >> > -	name = "";
> >> >> > +	{
> >> >> > +	  if (compact_eh && strcmp (name, ".text") != 0)
> >> >> > +	    return concat (base_name, ".", name, NULL);
> >> >> > +
> >> >> > +	  name = "";
> >> >> > +	}
> >> >>
> >> >> why is this change needed?  I.e., for a text section called 
> >> >> something like .foobar, why does compact_eh need to put things 
> >> >> in a section name ending in "..foobar", rather than in the main 
> >> >> EH section?  I assume the double dots are deliberate, e.g. to 
> >> >> avoid confusion with
> >> ".text.foobar"?
> >> >
> >> > I'm not sure why Paul put this is and the next hunk.  There were 
> >> > test failures without.  I can revisit this For the next iteration 
> >> > if necessary.
> >>
> >> Yeah, if you could that'd be great.  The code can't really go in if
> > there's no-
> >> one around who understands what it does.
> >>
> >> I assume it's just to ensure that each text section has its own 
> >> .eh_frame_entry, but in that case I think we should check based on 
> >> the base_name rather than compact_eh.  Or do we need the same 
> >> treatment for .gnu_extab.  If so, why?
> >>
> >> A comment is needed at the very least.
> 
> The new hunk for this is:
> 
> > @@ -128,7 +241,15 @@ get_debugseg_name (segT seg, const char
> *base_name)
> >        dot = strchr (name + 1, '.');
> >
> >        if (!dollar && !dot)
> > -	name = "";
> > +	{
> > +	  /* Ensure that a uniquely named .eh_frame_entry section
> > +	     is created for each text section.  */
> > +	  if (compact_eh
> > +	       && !strcmp (base_name, ".eh_frame_entry")
> > +	       && strcmp (name, ".text") != 0)
> > +	    return concat (base_name, ".", name, NULL);
> > +	  name = "";
> > +	}
> 
> I don't think we want the compact_eh test here; just the base_name 
> check should be enough.  (Nit, sorry, but watch the indentation.
> One less space before "&&".)
> 
Done.
> >> >> > @@ -833,14 +859,15 @@ dot_cfi_personality (int ignored ATTRIBU
> >> >> >      }
> >> >> >
> >> >> >    if ((encoding & 0xff) != encoding
> >> >> > -      || ((encoding & 0x70) != 0
> >> >> > +      || ((((encoding & 0x70) != 0
> >> >> >  #if CFI_DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr
> >> >> > -	  && (encoding & 0x70) != DW_EH_PE_pcrel
> >> >> > +	   && (encoding & 0x70) != DW_EH_PE_pcrel
> >> >> >  #endif
> >> >> >  	  )
> >> >> > /* leb128 can be handled, but does something actually need it?
> > */
> >> >> > -      || (encoding & 7) == DW_EH_PE_uleb128
> >> >> > -      || (encoding & 7) > DW_EH_PE_udata8)
> >> >> > +	   || (encoding & 7) == DW_EH_PE_uleb128
> >> >> > +	   || (encoding & 7) > DW_EH_PE_udata8)
> >> >> > +	&& !tc_cfi_special_encoding (encoding)))
> >> >> >      {
> >> >> >        as_bad (_("invalid or unsupported encoding in .cfi_personality"));
> >> >> >        ignore_rest_of_line ();
> >> >>
> >> >> What does a "special" encoding mean?  Again, this hook should be 
> >> >> documented in internals.texi.  And do we really want to change 
> >> >> the set of encodings that are allowed for DWARF, even on MIPS 
> >> >> systems that predate compat EH?
> >> >>
> >> > Special means that we have code in the backend to emit a reloc for it.
> >> > In the revised patch, it goes along with Tc_cfi_reloc_for_encoding.
> >> > It also looks like internals.texi fails to document many of the 
> >> > tc_macros and it doesn't appear to be built into a .info fiie.
> >>
> >> That's no reason not to document new hooks though.  I know I've 
> >> used internals.texi to read more about a hook in the past.  If you 
> >> don't
> > want to put
> >> it there though then please at least put it in a comment instead.
> 
> The hunk for this is:
> 
> > diff --git a/gas/doc/internals.texi b/gas/doc/internals.texi index
> > cc4089b..9dd0bd0 100644
> > --- a/gas/doc/internals.texi
> > +++ b/gas/doc/internals.texi
> > @@ -1473,6 +1473,10 @@ completed, but before the relocations have
> been generated.
> >  If you define this macro, GAS will call it after the relocs have 
> > been generated.
> >
> > +@item tc_cfi_reloc_for_encoding
> > +@cindex tc_cfi_reloc_for_encoding
> > +You may define this macro to indicate whether a cfi encoding 
> > +requires a
> relocation.
> 
> That makes it sound like a boolean, whereas it returns a reloc.  
> Please also mention that it controls whether compact EH is supported.  
> The 80- character limit applies to documentation too.
> 

Done.

> >> >> > +  demand_empty_rest_of_line ();
> >> >> > +  ccseg = CUR_SEG (last_fde);
> >> >> > +  /* Open .gnu_extab section.  */
> >> >> > +  cfi_seg = get_cfi_seg (ccseg, ".gnu_extab",
> >> >> > +			 (SEC_ALLOC | SEC_LOAD | SEC_DATA
> >> >> > +			  | DWARF2_EH_FRAME_READ_ONLY),
> >> >> > +			 1);
> >> >> > +#ifdef md_fix_up_eh_frame
> >> >> > +  md_fix_up_eh_frame (cfi_seg); #else
> >> >> > +  (void) cfi_seg;
> >> >> > +#endif
> >> >> > +
> >> >> > +  frag_align (align, 0, 0);
> >> >> > +  record_alignment (now_seg, align);  if 
> >> >> > + (last_fde->eh_header_type == EH_COMPACT_HAS_LSDA)
> >> >> > +    output_compact_unwind_data (last_fde, align);
> >> >>
> >> >> Please could you explain the EH_COMPACT_LEGACY handling here?
> >> >
> >> > Would you please clarify this question?  I don't see the 
> >> > reference to EH_COMPACT_LEGACY.
> >>
> >> That was the problem :-)  Further up there's:
> >>
> >>   if (last_fde->eh_header_type != EH_COMPACT_LEGACY
> >>       && last_fde->eh_header_type != EH_COMPACT_HAS_LSDA)
> >>     {
> >>       as_bad (_(".cfi_inline_lsda seen for frame without .cfi_lsda"));
> >>       ignore_rest_of_line ();
> >>       return;
> >>     }
> >>
> >> So what does this code mean/do in the:
> >>
> >>   last_fde->eh_header_type == EH_COMPACT_LEGACY
> >>
> >> case?
> 
> Still not sure about this -- please could you clarify?  The context is 
> this part of
> dot_cfi_inline_lsda:
> 
> > +  if (last_fde->eh_header_type != EH_COMPACT_LEGACY
> > +      && last_fde->eh_header_type != EH_COMPACT_HAS_LSDA)
> > +    {
> > +      as_bad (_(".cfi_inline_lsda seen for frame without .cfi_lsda"));
> > +      ignore_rest_of_line ();
> > +      return;
> > +    }
> > +
> > +#ifdef md_flush_pending_output
> > +  md_flush_pending_output ();
> > +#endif
> > +
> > +  align = get_absolute_expression ();  if (align > max_alignment)
> > +    {
> > +      align = max_alignment;
> > +      as_bad (_("Alignment too large: %d assumed."), align);
> > +    }
> > +  else if (align < 0)
> > +    {
> > +      as_warn (_("Alignment negative: 0 assumed."));
> > +      align = 0;
> > +    }
> > +
> > +  demand_empty_rest_of_line ();
> > +  ccseg = CUR_SEG (last_fde);
> > +  /* Open .gnu_extab section.  */
> > +  get_cfi_seg (ccseg, ".gnu_extab",
> > +	       (SEC_ALLOC | SEC_LOAD | SEC_DATA
> > +		| DWARF2_EH_FRAME_READ_ONLY),
> > +	       1);
> >
> > +  frag_align (align, 0, 0);
> > +  record_alignment (now_seg, align);  if (last_fde->eh_header_type 
> > + == EH_COMPACT_HAS_LSDA)
> > +    output_compact_unwind_data (last_fde, align);
> > +
> > +  last_fde = NULL;
> > +
> > +  return;
> 
> The first "if" statement allows the directive to be used for 
> EH_COMPACT_LEGACY, but what does the directive mean/do in that case?
> 

I've now added some commentary and extended the definition of the enumeration to include an EH_COMPACT_UNKNOWN value. 
The eh_header_type was never explicitly initialized; it's now initialized to EH_COMPACT_UNKNOWN.  The EH_COMPACT_LEGACY header type is then explicitly set when none of the COMPACT header type cases are discovered.

> >> > diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index 0aab5fa..b7d7df0
> >> > 100644
> >> > --- a/bfd/elf-bfd.h
> >> > +++ b/bfd/elf-bfd.h
> >> > @@ -381,8 +381,10 @@ struct eh_frame_hdr_info  {
> >> >    struct htab *cies;
> >> >    asection *hdr_sec;
> >> > -  unsigned int fde_count, array_count;
> >> > +  unsigned int fde_count, array_count, allocated_entries;
> >> >    struct eh_frame_array_ent *array;
> >> > +  /* eh_frame_entry fragments.  */  asection **entries;
> >> >    /* TRUE if we should try to merge CIEs between input sections.  */
> >> >    bfd_boolean merge_cies;
> >> >    /* TRUE if all .eh_frames have been parsd.  */
> >>
> >> I'm not sure there's enough commonality between the DWARF and
> compact
> >> versions to share this structure.  Either we should have separate 
> >> structures or use a union to separate out the format-specific bits.
> 
> I see you've done this, thanks, but:
> 
> > +struct dwarf_eh_frame_hdr_info
> > +{
> > +  struct eh_frame_array_ent *array; };
> > +
> > +struct compact_eh_frame_hdr_info
> > +{
> > +  unsigned int allocated_entries;
> > +  /* eh_frame_entry_fragments.  */
> > +  asection **entries;
> > +};
> > +
> >  struct eh_frame_hdr_info
> >  {
> >    struct htab *cies;
> >    asection *hdr_sec;
> >    unsigned int fde_count, array_count;
> > -  struct eh_frame_array_ent *array;
> >    /* TRUE if .eh_frame_hdr should contain the sorted search table.
> >       We build it if we successfully read all .eh_frame input sections
> >       and recognize them.  */
> >    bfd_boolean table;
> > +  bfd_boolean frame_hdr_is_compact;  union
> > +    {
> > +      struct dwarf_eh_frame_hdr_info dwarf;
> > +      struct compact_eh_frame_hdr_info compact;
> > +    }
> > +  u;
> >  };
> >
> >  /* Enum used to identify target specific extensions to the 
> > elf_obj_tdata
> 
> ...aren't cies, fde_count, array_count and table specific to the DWARF 
> version too?  My point was that if we went for the union approach, the 
> only fields in the common structure should be those that are needed by 
> both formats.  It looks like that's just hdr_sec.
> 
> If moving all of them seems like too much work, I think we should go 
> for separate structures instead.

I moved them and kept the union.

> 
> >> > +	}
> >> > +
> >> > +      BFD_ASSERT (hdr_info->entries);
> >> > +    }
> >> > +
> >> > +  hdr_info->entries[hdr_info->array_count++] = sec; }
> >> > +
> >> > +/* Parse a .eh_frame_entry section.  Figure out which text section it
> >> > +   references.  */
> >> > +
> >> > +void
> >> > +_bfd_elf_parse_eh_frame_entry (bfd *abfd, struct bfd_link_info
> *info,
> >> > +			       asection *sec, struct elf_reloc_cookie *cookie,
> >> > +			       bfd_boolean remember)
> >>
> >> This does more than the comment says and the name implies; the 
> >> REMEMBER stuff isn't mentioned.
> >>
> >> The patch tries to do the parsing during bfd_elf_discard_info, but 
> >> since the parsing wants to be able to fail with an error, I think 
> >> we need to do it in an earlier pass.  We can then return a 
> >> bfd_boolean success code and propagate error returns up, which the 
> >> current patch
> doesn't do.
> >> Ideally we'd put the pass somewhere before GC, so that both the GC 
> >> and bfd_elf_discard_info stages can assume parsed .eh_frame_entry
> sections.
> >>
> >> Having bfd_elf_discard_info add info (as per REMEMBER == TRUE) 
> >> seems a bit counterintuitive.  I think the earlier pass should 
> >> record all .eh_frame_entry sections and then the code currently in 
> >> _bfd_elf_end_eh_frame_parsing (but see below) should remove
> unwanted
> >> entries from the eh_frame_hdr_info array.
> 
> I see you've added the earlier pass, thanks, but errors aren't always 
> reported back up.  The function:
> 
> > +/* Parse a .eh_frame_entry section.  Figure out which text section it
> > +   references.  */
> > +
> > +void
> > +_bfd_elf_parse_eh_frame_entry (struct bfd_link_info *info,
> > +			       asection *sec, struct elf_reloc_cookie *cookie) {
> > [...]
> > +fail:
> > +  (*_bfd_error_handler) (_("%B: failed to process 
> > +.eh_frame_entry"),
> > +sec->owner); }
> 
> doesn't tell the caller (and eventually the ld code) that a problem occured.
> The function ought to return a bfd_boolean success code, like its callers.
> Also, the error here seems to duplicate the eventual ld one:
> 
>     einfo (_("%P%F: Failed to parse EH frame entries.\n"));
> 
> without really providing any more information.
> 
> Don't shoot me, but it would probably be cleaner to make this an 
> ELF-specific pass and call it from elf32.em instead.  That'll avoid 
> having to do all the aout, bout, etc. stuff and would allow the ld 
> code to refer to ELFisms like .eh_frame_entry itself.
> 
Okay, I've now moved this to elf32.em.  I fixed the error handling as well.

> >> > +/* Finish a pass over all .eh_frame and eh_frame_entry sections.
> >> > +*/
> >> > +
> >> > +bfd_boolean
> >> >  _bfd_elf_end_eh_frame_parsing (struct bfd_link_info *info)  {
> >> >    struct eh_frame_hdr_info *hdr_info;
> >> > +  unsigned int i;
> >> >
> >> >    hdr_info = &elf_hash_table (info)->eh_info;
> >> >    hdr_info->parsed_eh_frames = TRUE;
> >> > +
> >> > +  if (hdr_info->array_count == 0 || info->eh_frame_hdr < 2)
> >> > +    return FALSE;
> >> > +
> >> > +  qsort (hdr_info->entries, hdr_info->array_count,
> >> > +	 sizeof (asection *), cmp_eh_frame_hdr);
> >> > +
> >> > +  for (i = 0; i < hdr_info->array_count - 1; i++)
> >> > +    {
> >> > +      add_eh_frame_hdr_terminator (hdr_info->entries[i],
> >> > +				   hdr_info->entries[i + 1]);
> >> > +    }
> >> > +
> >> > +  /* Add a CANTUNWIND terminator after the last entry.  */ 
> >> > + add_eh_frame_hdr_terminator (hdr_info->entries[i], NULL);  
> >> > + return TRUE;
> >>
> >> This routine is called from both bfd_elf_gc_sections and 
> >> bfd_elf_discard_info but I think you only want it for bfd_elf_discard_info.
> >> So perhaps this should be a separate function.
> >>
> >> I wonder whether we could instead insert CANTUNWINDs earlier (say 
> >> in the non-dynamic part of size_dynamic_sections) based on the link order.
> >> I.e. rather than comparing the start and end addresses of two 
> >> sections, we could just walk the sections in the order that they're 
> >> going to
> be linked.
> >>
> >> It sounds like doing it that way would be more direct and more efficient.
> >> Returning TRUE here forces the linker to map sections twice.
> 
> Note that the generic version of _bfd_elf_end_eh_frame_parsing was 
> removed in the meantime.  Your patch adds it back, but I can't see 
> where it gets called.  Unless I'm missing something, this suggests 
> that the tests don't exercise this code and that extra tests might be needed.

I've now added link-time tests and the call _bfd_elf_end_eh_frame_parsing has been restored.
> 
> >> > @@ -1271,6 +1465,45 @@ _bfd_elf_eh_frame_present (struct
> >> bfd_link_info *info)
> >> >    return FALSE;
> >> >  }
> >> >
> >> > +/* Return true if there is at least one .eh_frame_entry section in
> >> > +   input files.  */
> >> > +bfd_boolean
> >> > +_bfd_elf_eh_frame_entry_present (struct bfd_link_info *info) {
> >> > +  asection *o;
> >> > +  bfd *abfd;
> >> > +
> >> > +  for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link_next)
> >> > +    {
> >> > +      for (o = abfd->sections; o; o = o->next)
> >> > +	{
> >> > +	  const char *name = bfd_get_section_name (abfd, o);
> >> > +
> >> > +	  if (strcmp (name, ".eh_frame_entry")
> >> > +	      && !bfd_is_abs_section (o->output_section))
> >> > +	    {
> >> > +	      if (strcmp (o->output_section->name, ".eh_frame_hdr"))
> >> > +		return TRUE;
> >> > +	      else
> >> > +		{
> >> > +		  (*_bfd_error_handler)
> >> > +			(_("error: an '.eh_frame_entry'"
> >> > +			   " input section that is not mapped to the"
> >> > +			   " '.eh_frame_hdr' output section was seen"));
> >> > +		  (*_bfd_error_handler)
> >> > +			(_("note: try adding '*(.eh_frame_entry"
> >> > +			   " .eh_frame_entry.*)' to the '.eh_frame_hdr'"
> >> > +			   " output section description in the linker"
> >> > +			   " script"));
> >> > +		  bfd_set_error (bfd_error_invalid_operation);
> >> > +		  return FALSE;
> >> > +		}
> >> > +	    }
> >> > +	}
> >> > +    }
> >> > +  return FALSE;
> >>
> >> The caller can't tell "FALSE because an error was reported" from 
> >> "FALSE because there was no .eh_frame_entry".  We should separate 
> >> out the two cases and propagate error returns up.
> 
> It looks like you dealt with this by removing the error checking.
> I think we still want it, but it should be done separately and in a 
> context where the error can be propagated.

I'm deferring this for now.
> 
> >> > @@ -1387,6 +1636,71 @@ _bfd_elf_eh_frame_section_offset (bfd
> >> *output_bfd ATTRIBUTE_UNUSED,
> >> >  	  + extra_augmentation_data_bytes (sec_info->entry + mid));  }
> >> >
> >> > +/* Write out .eh_frame_entry section.  Add CANTUNWIND terminator 
> >> > +if
> >> needed.
> >> > +   Also check that the contents look sane.  */
> >> > +
> >> > +bfd_boolean
> >> > +_bfd_elf_write_section_eh_frame_entry (bfd *abfd,
> >> > +				       asection *sec,
> >> > +				       bfd_byte *contents)
> >>
> >> Formatting: first two arguments fit on a line.
> 
> Still present.
Fixed.
> 
> >> > +  text_sec = (asection *) elf_section_data (sec)->sec_info;
> >> > +  addr = text_sec->output_section->vma + text_sec->output_offset
> >> > +	 + text_sec->size;
> >> > +  addr &= ~1;
> >> > +  addr -= (sec->output_section->vma + sec->output_offset +
> >> > +sec->rawsize);
> >> > +  BFD_ASSERT ((addr & 1) == 0);
> >>
> >> It looks like this could trigger for odd-sized input text sections.
> >> I think it should be an error instead of an assert.
> 
> I see you've done this with:
> 
> > +  if (addr & 1)
> > +    {
> > +      (*_bfd_error_handler) (_("%B: %s invalid input section size"),
> > +			     sec->owner, sec->name);
> > +      return FALSE;
> > +    }
> > +  if (last_addr >= addr + sec->rawsize)
> > +    {
> > +      (*_bfd_error_handler) (_("%B: %s points past end of text section"),
> > +			     sec->owner, sec->name);
> > +      return FALSE;
> > +    }
> 
> but I think we want "bfd_set_error (bfd_error_bad_value);" too.

Done.
> 
> >> > +  if (last_addr >= addr + sec->rawsize)
> >> > +    {
> >> > +      (*_bfd_error_handler) (_("%B: %s points past end of text section"),
> >> > +			     sec->owner, sec->name);
> >> > +      return FALSE;
> >> > +    }
> >> > +
> >> > +  if (sec->size == sec->rawsize)
> >> > +    return TRUE;
> >> > +
> >> > +  BFD_ASSERT (sec->size == sec->rawsize + 8);  BFD_ASSERT ((addr 
> >> > + &
> >> > + 1) == 0);
> >> > +  bfd_put_32 (abfd, addr, cantunwind);
> >> > +  bfd_put_32 (abfd, 0x015d5d01, cantunwind + 4);  return 
> >> > + bfd_set_section_contents (abfd, sec->output_section,
> >> cantunwind,
> >> > +				   sec->output_offset + sec->rawsize, 8);
> >>
> >> ARM and c6x seem to use 0 rather than 0x5d as the "no unwind" 
> >> opcode, is that right?  If so, I think this should be a hook.
> 
> It doesn't look like you addressed this part.

It looked to me like those ports used 1?  In any case, I've added the hook, but I haven't updated the arm and c6x ports to use it.
> 
> >> The decision about whether to insert the CANTUNWIND is made during 
> >> bfd_elf_discard_info, but addresses can change after that “thanks”
> >> to relaxation.  So this could in principle end up emitting a 
> >> CANTUNWIND for the same address as the following text section.
> 
> As above, it looks like nothing ever calls 
> _bfd_elf_write_section_eh_frame_entry in the new version of the patch.
> If the tests don't pick that up then I think we need some more :-)
> 
Sorry about this missing bit.  More tests have been added and the call to this function has been restored.

> >> > @@ -10039,6 +10040,10 @@ elf_link_input_bfd (struct 
> >> > elf_final_link_info
> >> *flinfo, bfd *input_bfd)
> >> >  	      return FALSE;
> >> >  	  }
> >> >  	  break;
> >> > +	case SEC_INFO_TYPE_EH_FRAME_ENTRY:
> >> > +	    if (! _bfd_elf_write_section_eh_frame_entry (output_bfd, o,
> >> contents))
> >> > +	      return FALSE;
> >> > +	  break;
> >>
> >> Excess indentation of the "if".
> 
> Not sure: why is this no longer in the patch?
Restored.
> 
> >> > @@ -11807,6 +11814,13 @@ _bfd_elf_gc_mark (struct bfd_link_info
> *info,
> >> >  	}
> >> >      }
> >> >
> >> > +  eh_frame = elf_section_eh_frame_entry (sec);  if (eh_frame &&
> >> > + !eh_frame->gc_mark)
> >> > +    {
> >> > +    if (!_bfd_elf_gc_mark (info, eh_frame, gc_mark_hook))
> >> > +      return FALSE;
> >> > +    }
> >>
> >> In this context we should be using "ret":
> >>
> >>   eh_frame = elf_section_eh_frame_entry (sec);
> >>   if (ret && eh_frame && !eh_frame->gc_mark)
> >>     ret = _bfd_elf_gc_mark (info, eh_frame, gc_mark_hook);
> 
> You changed this to:
> 
> > +  if (eh_frame && !eh_frame->gc_mark)
> > +    {
> > +      if (!_bfd_elf_gc_mark (info, eh_frame, gc_mark_hook))
> > +	ret = FALSE;
> > +    }
> 
> but the point was also that we don't want to try another mark once "ret"
> is already FALSE.  The "ret &&" part of the condition was important.
Another rebase problem.  Now fixed.
> 
> >> > @@ -12190,22 +12204,42 @@ bfd_elf_gc_sections (bfd *abfd, struct
> >> bfd_link_info *info)
> >> >    bed->gc_keep (info);
> >> >
> >> >    /* Try to parse each bfd's .eh_frame section.  Point
> >> elf_eh_frame_section
> >> > -     at the .eh_frame section if we can mark the FDEs individually.  */
> >> > +     at the .eh_frame section if we can mark the FDEs individually.
> >> > +     Establish links from text sections to their corresponding
> >> > +     .eh_frame_entry sections.  */
> >> >    _bfd_elf_begin_eh_frame_parsing (info);
> >> >    for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
> >> >      {
> >> >        asection *sec;
> >> >        struct elf_reloc_cookie cookie;
> >> >
> >> > -      sec = bfd_get_section_by_name (sub, ".eh_frame");
> >> > -      while (sec && init_reloc_cookie_for_section (&cookie, info, sec))
> >> > +      if (!init_reloc_cookie (&cookie, info, sub))
> >> > +	return FALSE;
> >> > +
> >> > +      if (info->eh_frame_hdr < 2)
> >> >  	{
> >> > -	  _bfd_elf_parse_eh_frame (sub, info, sec, &cookie);
> >> > -	  if (elf_section_data (sec)->sec_info
> >> > -	      && (sec->flags & SEC_LINKER_CREATED) == 0)
> >> > -	    elf_eh_frame_section (sub) = sec;
> >> > -	  fini_reloc_cookie_for_section (&cookie, sec);
> >> > -	  sec = bfd_get_next_section_by_name (sec);
> >> > +	  sec = bfd_get_section_by_name (sub, ".eh_frame");
> >> > +	  if (sec && init_reloc_cookie_rels (&cookie, info, sub, sec))
> >> > +	    {
> >> > +	      _bfd_elf_parse_eh_frame (sub, info, sec, &cookie);
> >> > +	      if (elf_section_data (sec)->sec_info
> >> > +		  && (sec->flags & SEC_LINKER_CREATED) == 0)
> >> > +		elf_eh_frame_section (sub) = sec;
> >> > +	      fini_reloc_cookie_for_section (&cookie, sec);
> >> > +	    }
> >> > +	}
> >> > +      else
> >> > +	{
> >> > +	  for (sec = sub->sections; sec; sec = sec->next)
> >> > +	    {
> >> > +	      if (CONST_STRNEQ (bfd_section_name (sub, sec),
> >> ".eh_frame_entry")
> >> > +		  && init_reloc_cookie_rels (&cookie, info, sub, sec))
> >> > +		{
> >> > +		  _bfd_elf_parse_eh_frame_entry (sub, info, sec, &cookie,
> >> > +						 FALSE);
> >> > +		  fini_reloc_cookie_for_section (&cookie, sec);
> >> > +		}
> >> > +	    }
> >>
> >> This changes the info->eh_frame_hdr != 2 case to only handle the 
> >> first .eh_frame section.  ("while" -> "if").
> >>
> >> Also, the init/fini_reloc_cookie* calls don't match up.  I assume 
> >> the
> > idea is to
> >> avoid excessive allocation and freeing of the locsyms, but in that 
> >> case you should use fini_reloc_cookie_rels instead of 
> >> fini_reloc_cookie_for_section and call fini_reloc_cookie at the end.
> >> At the moment I think this leaks memory if there are no EH sections.
> >>
> >> I don't know either way whether splitting the 
> >> init_reloc_cookie_for_section call up is a win or not for .eh_frame.
> >> It will be a win if there are
> > multiple EH
> >> sections but a loss if there are none, since we then initialise and
> > free the bfd-
> >> level information unnecessarily.  So I think it might make sense to 
> >> keep the .eh_frame code as it is now and restrict the *_rels to the 
> >> new
> code.
> 
> You changed this to:
> 
> > @@ -12171,7 +12256,9 @@ bfd_elf_gc_sections (bfd *abfd, struct 
> > bfd_link_info *info)
> >
> >    /* Try to parse each bfd's .eh_frame section.  Point
> elf_eh_frame_section
> >       at the .eh_frame section if we can mark the FDEs individually.
> > */
> > -  for (sub = info->input_bfds; sub != NULL; sub = sub->link.next)
> > +  for (sub = info->input_bfds;
> > +       info->eh_frame_hdr_type != COMPACT_EH_HDR && sub != NULL;
> > +       sub = sub->link.next)
> >      {
> >        asection *sec;
> >        struct elf_reloc_cookie cookie;
> 
> where info->eh_frame_hdr_type is an invariant.  I assume this was just 
> to avoid reformatting the block, but please use:
> 
>   if (info->eh_frame_hdr_type != COMPACT_EH_HDR)
>     for (...)
> 
> instead.  Fortunately the block's not that big. :-)

Done.
> 
> >> > diff --git a/gas/config/tc-mips.h b/gas/config/tc-mips.h index
> >> > c7eaa04..a300062 100644
> >> > --- a/gas/config/tc-mips.h
> >> > +++ b/gas/config/tc-mips.h
> >> > @@ -177,7 +177,9 @@ extern enum dwarf2_format
> mips_dwarf2_format
> >> > (asection *);
> >> >
> >> >  extern int mips_dwarf2_addr_size (void);  #define
> >> > DWARF2_ADDR_SIZE(bfd) mips_dwarf2_addr_size () -#define 
> >> > DWARF2_FDE_RELOC_SIZE mips_dwarf2_addr_size ()
> >> > +#define DWARF2_FDE_RELOC_SIZE (compact_eh ? 4 :
> >> mips_dwarf2_addr_size
> >> > +()) #define DWARF2_FDE_RELOC_ENCODING(enc) \
> >> > +  (enc | (compact_eh ? DW_EH_PE_pcrel : 0))
> >>
> >> What case is this handling?  Please explain this a bit more.
> 
> Doesn't look like you've addressed this bit.

Deferring for now.
> 
> >> > @@ -4566,6 +4586,22 @@ argument is not present, otherwise second 
> >> > argument should be a constant  or a symbol name.  The default 
> >> > after @code{.cfi_startproc} is @code{.cfi_lsda 0xff},  no LSDA.
> >> >
> >> > +@section @code{.cfi_inline_lsda} [@var{align}] 
> >> > +@code{.cfi_inline_lsda} marks the start of a LSDA data section 
> >> > +and switches to the corresponding @code{.gnu.extab} section.
> >> > +Must be preceded by a CFI block containing a @code{.cfi_lsda}
> directive.
> >> > +Only valid when generating compact EH frames (i.e.
> >> > +with @code{.cfi_sections eh_frame_entry}.
> >>
> >> Missing ")".
> 
> As above, this part of the doc is no longer there.

Restored.
> 
> >> > @@ -4643,6 +4679,20 @@ mark a code segment that has only one
> return
> >> > address which is reached  by a direct branch and no copy of the 
> >> > return address exists in memory  or another register.
> >> >
> >> > +@section @code{.cfi_epilogue_begin} A pseudo-operation which
> marks
> >> > +the beginning of the epilogue in a given function.  This is 
> >> > +currently used by the compact unwind-table implementation (for 
> >> > +exception handling), which assumes a single register state for 
> >> > +unwinding throughout the body of a function.  The function is 
> >> > +scanned, and CFI directives are rewritten in a compact form.
> >> > +However, recent versions of GCC emit unwind information for 
> >> > +function epilogues, which should not be represented in this 
> >> > +compact
> >> > +form: hence, any CFI directives which occur in a function after 
> >> > +@code{.cfi_epilogue_end} are not processed.
> >> > +
> >> > +This operation only affects the internals of the assembler, and 
> >> > +is not represented in the binary output.
> >>
> >> Don't really follow this, sorry.  Can you give an example, or 
> >> better yet, a testcase?
> 
> Has .cfi_epilogue_begin been dropped?

Yes.
> 
> >>
> >> > @@ -161,6 +278,9 @@ alloc_debugseg_item (segT seg, int subseg, 
> >> > char
> >> > *name)  static segT  is_now_linkonce_segment (void)  {
> >> > +  if (compact_eh)
> >> > +    return now_seg;
> >> > +
> >>
> >> Please add a comment explaining why this is correct.
> 
> The hunk for this is:
> 
> > +  /* We track the current segment in the cfi_insn_data struct and
> > +     the cfi_insn_data struct for Compact EH.  */  if (compact_eh)
> > +    return now_seg;
> 
> but the comment repeats "the cfi_insn_data struct".  TBH I'm still not 
> sure why/whether this is correct, but let's sort the other things out first.

Okay.
> 
> >> > -/* Emit a single byte into the current segment.  */
> >> > -
> >> > -static inline void
> >> > -out_one (int byte)
> >> > +static segT
> >> > +get_cfi_seg (segT cseg, const char *base, flagword flags, int
> >> > +align)
> >> >  {
> >> > -  FRAG_APPEND_1_CHAR (byte);
> >> > +  if (SUPPORT_FRAME_LINKONCE || ((flags & SEC_DEBUGGING) == 0
> &&
> >> compact_eh))
> >> > +    {
> >>
> >> Please add a comment here too to explain the SEC_DEBUGGING test.
> 
> It doesn't look like you addressed this bit.
> 
> >> > +static void
> >> > +output_compact_unwind_data (struct fde_entry *fde, int align)
> >>
> >> Probably worth a comment here, since it wasn't obvious to me the 
> >> "align" is the alignment of the end of the data rather than the start.
> 
> I don't think you addressed this bit.  (In case it wasn't clear, I was 
> suggesting adding a function comment that says what the function does 
> and what its arguments are, etc.)

Done.
> 
> >> > +	  md_number_to_chars (p, 0, 4);
> >> > +	  fix_new (frag_now, p - frag_now->fr_literal, 4, exp.X_add_symbol,
> >> > +		   exp.X_add_number, howto->pc_relative, code);
> >>
> >> What's the significance of exp.X_add_number here?  If it was 
> >> supposed to be initialised to 0 above then I think it would be 
> >> easier to leave the
> "exp"
> >> stuff alone and just use fde->start_address and 0 directly in this
> > fix_new call.
> >> Certainly...
> >>
> >> >    else
> >> >      {
> >> > -      exp.X_op = O_symbol;
> >> > -      exp.X_add_symbol = fde->start_address;
> >> >        exp.X_add_number = 0;
> >> >        addr_size = DWARF2_ADDR_SIZE (stdoutput);
> >> >        emit_expr (&exp, addr_size);
> >>
> >> ...separating the add_symbol and add_number feels odd.
> 
> This is now:
> 
> > @@ -1575,29 +1881,43 @@ output_fde (struct fde_entry *fde, struct
> cie_entry *cie,
> >        TC_DWARF2_EMIT_OFFSET (cie->start_address, offset_size);
> >      }
> >
> > +  exp.X_op = O_symbol;
> > +  exp.X_add_symbol = fde->start_address;
> 
> (A)
> 
> >    if (eh_frame)
> >      {
> > -      exp.X_op = O_subtract;
> > -      exp.X_add_number = 0;
> > +      bfd_reloc_code_real_type code
> > +	= tc_cfi_reloc_for_encoding (cie->fde_encoding);
> > +      if (code !=  BFD_RELOC_NONE)
> > +	{
> > +	  reloc_howto_type *howto = bfd_reloc_type_lookup (stdoutput,
> code);
> > +	  char *p = frag_more (4);
> > +	  md_number_to_chars (p, 0, 4);
> > +	  fix_new (frag_now, p - frag_now->fr_literal, 4, fde->start_address,
> > +		   0, howto->pc_relative, code);
> > +	}
> > +      else
> > +	{
> > +	  exp.X_op = O_subtract;
> > +	  exp.X_add_number = 0;
> 
> (B)
> 
> >  #if CFI_DIFF_EXPR_OK
> > -      exp.X_add_symbol = fde->start_address;
> > -      exp.X_op_symbol = symbol_temp_new_now ();
> > -      emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code offset.  */
> > +	  exp.X_add_symbol = fde->start_address;
> > +	  exp.X_op_symbol = symbol_temp_new_now ();
> > +	  emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code
> offset.  */
> >  #else
> > -      exp.X_op = O_symbol;
> > -      exp.X_add_symbol = fde->start_address;
> > -#ifdef tc_cfi_emit_pcrel_expr
> > -      tc_cfi_emit_pcrel_expr (&exp, DWARF2_FDE_RELOC_SIZE);	 /*
> Code offset.  */
> > +	  exp.X_op = O_symbol;
> > +	  exp.X_add_symbol = fde->start_address;
> > +
> > +#if defined(tc_cfi_emit_pcrel_expr)
> > +	  tc_cfi_emit_pcrel_expr (&exp, DWARF2_FDE_RELOC_SIZE);	 /*
> Code offset.  */
> >  #else
> > -      emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code offset.  */
> > +	  emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code
> offset.  */
> >  #endif
> >  #endif
> > +	}
> >        addr_size = DWARF2_FDE_RELOC_SIZE;
> >      }
> >    else
> >      {
> > -      exp.X_op = O_symbol;
> > -      exp.X_add_symbol = fde->start_address;
> 
> (C)
> 
> >        exp.X_add_number = 0;
> >        addr_size = DWARF2_ADDR_SIZE (stdoutput);
> >        emit_expr (&exp, addr_size);
> 
> (C) is still hoisted to (A), but the only use of "exp" in the 
> "eh_frame" arm is
> (rightly) at (B), which overrides what (A) does.
> Please just drop (A) and leave the "else" arm unmodified.

Done.
> 
> >> > @@ -1888,7 +2223,17 @@ cfi_finish (void)
> >> >
> >> >  	  for (fde = all_fde_data; fde ; fde = fde->next)
> >> >  	    {
> >> > -	      if (SUPPORT_FRAME_LINKONCE)
> >> > +	      if ((fde->sections & CFI_EMIT_eh_frame) == 0)
> >> > +		continue;
> >> > +
> >> > +#if SUPPORT_COMPACT_EH
> >> > +	      if (fde->eh_header_type == EH_COMPACT_HAS_LSDA)
> >> > +		fde->eh_header_type = EH_COMPACT_LEGACY;
> >> > +
> >> > +	      if (fde->eh_header_type != EH_COMPACT_LEGACY)
> >> > +		continue;
> >> > +#endif
> >>
> >> Please add a comment explaining this.
> 
> I don't think you addressed this.
> 

Done.
> >> > @@ -1958,6 +2394,9 @@ cfi_finish (void)
> >> >
> >> >  	  for (fde = all_fde_data; fde ; fde = fde->next)
> >> >  	    {
> >> > +	      if ((fde->sections & CFI_EMIT_debug_frame) == 0)
> >> > +		continue;
> >> > +
> >> >  	      if (SUPPORT_FRAME_LINKONCE)
> >> >  		{
> >> >  		  if (HANDLED (fde))
> >>
> >> Please add a comment explaining why this is needed/correct.
> 
> Same here.
> 
Deferred.

^ permalink raw reply	[flat|nested] 31+ messages in thread

* RE: [Patch] Gas support for MIPS Compact EH
  2014-11-22 14:42         ` Richard Sandiford
  2015-02-08 16:59           ` Moore, Catherine
@ 2015-02-08 17:00           ` Moore, Catherine
  2015-04-16 13:28             ` Moore, Catherine
  1 sibling, 1 reply; 31+ messages in thread
From: Moore, Catherine @ 2015-02-08 17:00 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: binutils

[-- Attachment #1: Type: text/plain, Size: 40934 bytes --]

Sorry, forgot the patch in last message.

> -----Original Message-----
> From: Moore, Catherine
> Sent: Sunday, February 08, 2015 11:59 AM
> To: 'Richard Sandiford'
> Cc: binutils@sourceware.org
> Subject: RE: [Patch] Gas support for MIPS Compact EH
> 
> > -----Original Message-----
> > From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> > Sent: Saturday, November 22, 2014 9:43 AM
> > To: Moore, Catherine
> > Cc: binutils@sourceware.org
> > Subject: Re: [Patch] Gas support for MIPS Compact EH
> >
> > "Moore, Catherine" <Catherine_Moore@mentor.com> writes:
> > > Hi Richard,
> > > Please find the updated Compact EH  patch for binutils.
> > > This will likely still be a hard patch to review, but I tried to
> > > address the many comments that you made earlier.
> > > The main difference is the exception handling relocation type for
> > > the Linux toolchain.  It is no longer gprel based.
> > > The ELF and Linux tools both use pcrel32 and the EH-specific
> > > relocation type has been removed.
> > > I ran into a few issues with the recent changes to eh_frame handling
> > > for DWARF, I hopefully covered those with the Compact EH
> > implementation.
> > > Please let me know what you think.  I hope we are close to
> > > converging on an implementation.
> >
> > OK, thanks.  I started by going through my previous comments and
> > looking at how you addressed them in the new patch.  I think there are
> > still some things that need to be sorted out.
> >
> > When doing the next patch, could you reply to the main points and say
> > how you dealt with them?  That'd make it a lot easier to see how this is
> evolving.
> 
> Yes, I'll do that.  I'm sorry for the missing pieces of the last patch.  My rebase
> had some unexpected results that I didn't catch before I posted.
> Your comment that additional tests would have caught those is correct.  I've
> added some link tests to help out with that.  I think that extending the
> readelf -u (dump unwind option) would be useful for additional tests, but I
> don't want to make that additional development a requirement for approval
> of this patch.  There are still one or two outstanding bits, but I'd like to defer
> those until I see your comments on the current patch.
> >
> > >> -----Original Message-----
> > >> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> > >> Sent: Saturday, February 08, 2014 11:34 AM
> > >> To: Moore, Catherine
> > >> Cc: binutils@sourceware.org
> > >> Subject: Re: [Patch] Gas support for MIPS Compact EH
> > >>
> > >> Thanks for the updates.
> > >>
> > >> "Moore, Catherine" <Catherine_Moore@mentor.com> writes:
> > >> >> > @@ -4514,6 +4517,23 @@ argument is not present, otherwise
> > >> >> > secon or a symbol name.  The default after
> > >> >> > @code{.cfi_startproc} is @code{.cfi_lsda 0xff},  no LSDA.
> > >> >> >
> > >> >> > +@section @code{.cfi_inline_lsda} [@var{align}]
> > >> >> > +@code{.cfi_inline_lsda} marks the start of a LSDA data
> > >> >> > +section and switches to the corresponding @code{.gnu.extab}
> section.
> > >> >> > +It must be preceded by a CFI block containing a
> > >> >> > +@code{.cfi_lsda} directive and is only valid when generating
> > compact EH frames (i.e.
> > >> >> > +with @code{.cfi_sections eh_frame_entry}.
> > >> >> > +
> > >> >> > +If a compact encoding is being used then the table header and
> > >> >> > +unwinding opcodes will be generated at this point, so that
> > >> >> > +they are immediately followed by the LSDA data.  The symbol
> > >> >> > +referenced by the @code{.cfi_lsda} directive should still be
> > >> >> > +defined in case a fallback FDE based encoding is used.
> > >> >> > +
> > >> >> > +The optional @var{align} argument specifies the alignment
> > required.
> > >> >> > +The alignment is specified as a power of two, as with the
> > >> >> > +@code{.p2align} directive.
> > >> >>
> > >> >> Hmm, switching sections and emitting data feels very different
> > >> >> in style from the other .cfi directives, which just annotate
> > >> >> code without changing the flow of assembly.  I'd like to know
> > >> >> other people's
> > >> thoughts on this.
> > >> >>
> > >> >> Also, how do you terminate the LSDA?  The documentation doesn't
> > >> >> say (but should :-)) and I couldn't see this directive in the spec either.
> > >> >
> > >> > The LSDA is terminated by a section switch.
> > >>
> > >> OK.  Like I say, please mention this in the documentation.
> >
> > The new version of the patch doesn't have the .cfi_inline_lsda
> > documentation that the previous patch had.  Please add it back :-) But
> > like I say above, please also document how the data is terminated.
> 
> It's back.
> >
> > >> >> TBH, without tests, and without an explanation of what the code
> > >> >> is doing, I found this patch pretty hard to review.  E.g.:
> > >> >>
> > >> >> > @@ -129,7 +140,12 @@ get_debugseg_name (segT seg, const char
> > >> >> >        dot = strchr (name + 1, '.');
> > >> >> >
> > >> >> >        if (!dollar && !dot)
> > >> >> > -	name = "";
> > >> >> > +	{
> > >> >> > +	  if (compact_eh && strcmp (name, ".text") != 0)
> > >> >> > +	    return concat (base_name, ".", name, NULL);
> > >> >> > +
> > >> >> > +	  name = "";
> > >> >> > +	}
> > >> >>
> > >> >> why is this change needed?  I.e., for a text section called
> > >> >> something like .foobar, why does compact_eh need to put things
> > >> >> in a section name ending in "..foobar", rather than in the main
> > >> >> EH section?  I assume the double dots are deliberate, e.g. to
> > >> >> avoid confusion with
> > >> ".text.foobar"?
> > >> >
> > >> > I'm not sure why Paul put this is and the next hunk.  There were
> > >> > test failures without.  I can revisit this For the next iteration
> > >> > if necessary.
> > >>
> > >> Yeah, if you could that'd be great.  The code can't really go in if
> > > there's no-
> > >> one around who understands what it does.
> > >>
> > >> I assume it's just to ensure that each text section has its own
> > >> .eh_frame_entry, but in that case I think we should check based on
> > >> the base_name rather than compact_eh.  Or do we need the same
> > >> treatment for .gnu_extab.  If so, why?
> > >>
> > >> A comment is needed at the very least.
> >
> > The new hunk for this is:
> >
> > > @@ -128,7 +241,15 @@ get_debugseg_name (segT seg, const char
> > *base_name)
> > >        dot = strchr (name + 1, '.');
> > >
> > >        if (!dollar && !dot)
> > > -	name = "";
> > > +	{
> > > +	  /* Ensure that a uniquely named .eh_frame_entry section
> > > +	     is created for each text section.  */
> > > +	  if (compact_eh
> > > +	       && !strcmp (base_name, ".eh_frame_entry")
> > > +	       && strcmp (name, ".text") != 0)
> > > +	    return concat (base_name, ".", name, NULL);
> > > +	  name = "";
> > > +	}
> >
> > I don't think we want the compact_eh test here; just the base_name
> > check should be enough.  (Nit, sorry, but watch the indentation.
> > One less space before "&&".)
> >
> Done.
> > >> >> > @@ -833,14 +859,15 @@ dot_cfi_personality (int ignored ATTRIBU
> > >> >> >      }
> > >> >> >
> > >> >> >    if ((encoding & 0xff) != encoding
> > >> >> > -      || ((encoding & 0x70) != 0
> > >> >> > +      || ((((encoding & 0x70) != 0
> > >> >> >  #if CFI_DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr
> > >> >> > -	  && (encoding & 0x70) != DW_EH_PE_pcrel
> > >> >> > +	   && (encoding & 0x70) != DW_EH_PE_pcrel
> > >> >> >  #endif
> > >> >> >  	  )
> > >> >> > /* leb128 can be handled, but does something actually need it?
> > > */
> > >> >> > -      || (encoding & 7) == DW_EH_PE_uleb128
> > >> >> > -      || (encoding & 7) > DW_EH_PE_udata8)
> > >> >> > +	   || (encoding & 7) == DW_EH_PE_uleb128
> > >> >> > +	   || (encoding & 7) > DW_EH_PE_udata8)
> > >> >> > +	&& !tc_cfi_special_encoding (encoding)))
> > >> >> >      {
> > >> >> >        as_bad (_("invalid or unsupported encoding in
> .cfi_personality"));
> > >> >> >        ignore_rest_of_line ();
> > >> >>
> > >> >> What does a "special" encoding mean?  Again, this hook should be
> > >> >> documented in internals.texi.  And do we really want to change
> > >> >> the set of encodings that are allowed for DWARF, even on MIPS
> > >> >> systems that predate compat EH?
> > >> >>
> > >> > Special means that we have code in the backend to emit a reloc for it.
> > >> > In the revised patch, it goes along with Tc_cfi_reloc_for_encoding.
> > >> > It also looks like internals.texi fails to document many of the
> > >> > tc_macros and it doesn't appear to be built into a .info fiie.
> > >>
> > >> That's no reason not to document new hooks though.  I know I've
> > >> used internals.texi to read more about a hook in the past.  If you
> > >> don't
> > > want to put
> > >> it there though then please at least put it in a comment instead.
> >
> > The hunk for this is:
> >
> > > diff --git a/gas/doc/internals.texi b/gas/doc/internals.texi index
> > > cc4089b..9dd0bd0 100644
> > > --- a/gas/doc/internals.texi
> > > +++ b/gas/doc/internals.texi
> > > @@ -1473,6 +1473,10 @@ completed, but before the relocations have
> > been generated.
> > >  If you define this macro, GAS will call it after the relocs have
> > > been generated.
> > >
> > > +@item tc_cfi_reloc_for_encoding
> > > +@cindex tc_cfi_reloc_for_encoding
> > > +You may define this macro to indicate whether a cfi encoding
> > > +requires a
> > relocation.
> >
> > That makes it sound like a boolean, whereas it returns a reloc.
> > Please also mention that it controls whether compact EH is supported.
> > The 80- character limit applies to documentation too.
> >
> 
> Done.
> 
> > >> >> > +  demand_empty_rest_of_line ();
> > >> >> > +  ccseg = CUR_SEG (last_fde);
> > >> >> > +  /* Open .gnu_extab section.  */
> > >> >> > +  cfi_seg = get_cfi_seg (ccseg, ".gnu_extab",
> > >> >> > +			 (SEC_ALLOC | SEC_LOAD | SEC_DATA
> > >> >> > +			  | DWARF2_EH_FRAME_READ_ONLY),
> > >> >> > +			 1);
> > >> >> > +#ifdef md_fix_up_eh_frame
> > >> >> > +  md_fix_up_eh_frame (cfi_seg); #else
> > >> >> > +  (void) cfi_seg;
> > >> >> > +#endif
> > >> >> > +
> > >> >> > +  frag_align (align, 0, 0);
> > >> >> > +  record_alignment (now_seg, align);  if
> > >> >> > + (last_fde->eh_header_type == EH_COMPACT_HAS_LSDA)
> > >> >> > +    output_compact_unwind_data (last_fde, align);
> > >> >>
> > >> >> Please could you explain the EH_COMPACT_LEGACY handling here?
> > >> >
> > >> > Would you please clarify this question?  I don't see the
> > >> > reference to EH_COMPACT_LEGACY.
> > >>
> > >> That was the problem :-)  Further up there's:
> > >>
> > >>   if (last_fde->eh_header_type != EH_COMPACT_LEGACY
> > >>       && last_fde->eh_header_type != EH_COMPACT_HAS_LSDA)
> > >>     {
> > >>       as_bad (_(".cfi_inline_lsda seen for frame without .cfi_lsda"));
> > >>       ignore_rest_of_line ();
> > >>       return;
> > >>     }
> > >>
> > >> So what does this code mean/do in the:
> > >>
> > >>   last_fde->eh_header_type == EH_COMPACT_LEGACY
> > >>
> > >> case?
> >
> > Still not sure about this -- please could you clarify?  The context is
> > this part of
> > dot_cfi_inline_lsda:
> >
> > > +  if (last_fde->eh_header_type != EH_COMPACT_LEGACY
> > > +      && last_fde->eh_header_type != EH_COMPACT_HAS_LSDA)
> > > +    {
> > > +      as_bad (_(".cfi_inline_lsda seen for frame without .cfi_lsda"));
> > > +      ignore_rest_of_line ();
> > > +      return;
> > > +    }
> > > +
> > > +#ifdef md_flush_pending_output
> > > +  md_flush_pending_output ();
> > > +#endif
> > > +
> > > +  align = get_absolute_expression ();  if (align > max_alignment)
> > > +    {
> > > +      align = max_alignment;
> > > +      as_bad (_("Alignment too large: %d assumed."), align);
> > > +    }
> > > +  else if (align < 0)
> > > +    {
> > > +      as_warn (_("Alignment negative: 0 assumed."));
> > > +      align = 0;
> > > +    }
> > > +
> > > +  demand_empty_rest_of_line ();
> > > +  ccseg = CUR_SEG (last_fde);
> > > +  /* Open .gnu_extab section.  */
> > > +  get_cfi_seg (ccseg, ".gnu_extab",
> > > +	       (SEC_ALLOC | SEC_LOAD | SEC_DATA
> > > +		| DWARF2_EH_FRAME_READ_ONLY),
> > > +	       1);
> > >
> > > +  frag_align (align, 0, 0);
> > > +  record_alignment (now_seg, align);  if (last_fde->eh_header_type
> > > + == EH_COMPACT_HAS_LSDA)
> > > +    output_compact_unwind_data (last_fde, align);
> > > +
> > > +  last_fde = NULL;
> > > +
> > > +  return;
> >
> > The first "if" statement allows the directive to be used for
> > EH_COMPACT_LEGACY, but what does the directive mean/do in that case?
> >
> 
> I've now added some commentary and extended the definition of the
> enumeration to include an EH_COMPACT_UNKNOWN value.
> The eh_header_type was never explicitly initialized; it's now initialized to
> EH_COMPACT_UNKNOWN.  The EH_COMPACT_LEGACY header type is then
> explicitly set when none of the COMPACT header type cases are discovered.
> 
> > >> > diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index 0aab5fa..b7d7df0
> > >> > 100644
> > >> > --- a/bfd/elf-bfd.h
> > >> > +++ b/bfd/elf-bfd.h
> > >> > @@ -381,8 +381,10 @@ struct eh_frame_hdr_info  {
> > >> >    struct htab *cies;
> > >> >    asection *hdr_sec;
> > >> > -  unsigned int fde_count, array_count;
> > >> > +  unsigned int fde_count, array_count, allocated_entries;
> > >> >    struct eh_frame_array_ent *array;
> > >> > +  /* eh_frame_entry fragments.  */  asection **entries;
> > >> >    /* TRUE if we should try to merge CIEs between input sections.  */
> > >> >    bfd_boolean merge_cies;
> > >> >    /* TRUE if all .eh_frames have been parsd.  */
> > >>
> > >> I'm not sure there's enough commonality between the DWARF and
> > compact
> > >> versions to share this structure.  Either we should have separate
> > >> structures or use a union to separate out the format-specific bits.
> >
> > I see you've done this, thanks, but:
> >
> > > +struct dwarf_eh_frame_hdr_info
> > > +{
> > > +  struct eh_frame_array_ent *array; };
> > > +
> > > +struct compact_eh_frame_hdr_info
> > > +{
> > > +  unsigned int allocated_entries;
> > > +  /* eh_frame_entry_fragments.  */
> > > +  asection **entries;
> > > +};
> > > +
> > >  struct eh_frame_hdr_info
> > >  {
> > >    struct htab *cies;
> > >    asection *hdr_sec;
> > >    unsigned int fde_count, array_count;
> > > -  struct eh_frame_array_ent *array;
> > >    /* TRUE if .eh_frame_hdr should contain the sorted search table.
> > >       We build it if we successfully read all .eh_frame input sections
> > >       and recognize them.  */
> > >    bfd_boolean table;
> > > +  bfd_boolean frame_hdr_is_compact;  union
> > > +    {
> > > +      struct dwarf_eh_frame_hdr_info dwarf;
> > > +      struct compact_eh_frame_hdr_info compact;
> > > +    }
> > > +  u;
> > >  };
> > >
> > >  /* Enum used to identify target specific extensions to the
> > > elf_obj_tdata
> >
> > ...aren't cies, fde_count, array_count and table specific to the DWARF
> > version too?  My point was that if we went for the union approach, the
> > only fields in the common structure should be those that are needed by
> > both formats.  It looks like that's just hdr_sec.
> >
> > If moving all of them seems like too much work, I think we should go
> > for separate structures instead.
> 
> I moved them and kept the union.
> 
> >
> > >> > +	}
> > >> > +
> > >> > +      BFD_ASSERT (hdr_info->entries);
> > >> > +    }
> > >> > +
> > >> > +  hdr_info->entries[hdr_info->array_count++] = sec; }
> > >> > +
> > >> > +/* Parse a .eh_frame_entry section.  Figure out which text section it
> > >> > +   references.  */
> > >> > +
> > >> > +void
> > >> > +_bfd_elf_parse_eh_frame_entry (bfd *abfd, struct bfd_link_info
> > *info,
> > >> > +			       asection *sec, struct elf_reloc_cookie
> *cookie,
> > >> > +			       bfd_boolean remember)
> > >>
> > >> This does more than the comment says and the name implies; the
> > >> REMEMBER stuff isn't mentioned.
> > >>
> > >> The patch tries to do the parsing during bfd_elf_discard_info, but
> > >> since the parsing wants to be able to fail with an error, I think
> > >> we need to do it in an earlier pass.  We can then return a
> > >> bfd_boolean success code and propagate error returns up, which the
> > >> current patch
> > doesn't do.
> > >> Ideally we'd put the pass somewhere before GC, so that both the GC
> > >> and bfd_elf_discard_info stages can assume parsed .eh_frame_entry
> > sections.
> > >>
> > >> Having bfd_elf_discard_info add info (as per REMEMBER == TRUE)
> > >> seems a bit counterintuitive.  I think the earlier pass should
> > >> record all .eh_frame_entry sections and then the code currently in
> > >> _bfd_elf_end_eh_frame_parsing (but see below) should remove
> > unwanted
> > >> entries from the eh_frame_hdr_info array.
> >
> > I see you've added the earlier pass, thanks, but errors aren't always
> > reported back up.  The function:
> >
> > > +/* Parse a .eh_frame_entry section.  Figure out which text section it
> > > +   references.  */
> > > +
> > > +void
> > > +_bfd_elf_parse_eh_frame_entry (struct bfd_link_info *info,
> > > +			       asection *sec, struct elf_reloc_cookie *cookie) {
> > > [...]
> > > +fail:
> > > +  (*_bfd_error_handler) (_("%B: failed to process
> > > +.eh_frame_entry"),
> > > +sec->owner); }
> >
> > doesn't tell the caller (and eventually the ld code) that a problem occured.
> > The function ought to return a bfd_boolean success code, like its callers.
> > Also, the error here seems to duplicate the eventual ld one:
> >
> >     einfo (_("%P%F: Failed to parse EH frame entries.\n"));
> >
> > without really providing any more information.
> >
> > Don't shoot me, but it would probably be cleaner to make this an
> > ELF-specific pass and call it from elf32.em instead.  That'll avoid
> > having to do all the aout, bout, etc. stuff and would allow the ld
> > code to refer to ELFisms like .eh_frame_entry itself.
> >
> Okay, I've now moved this to elf32.em.  I fixed the error handling as well.
> 
> > >> > +/* Finish a pass over all .eh_frame and eh_frame_entry sections.
> > >> > +*/
> > >> > +
> > >> > +bfd_boolean
> > >> >  _bfd_elf_end_eh_frame_parsing (struct bfd_link_info *info)  {
> > >> >    struct eh_frame_hdr_info *hdr_info;
> > >> > +  unsigned int i;
> > >> >
> > >> >    hdr_info = &elf_hash_table (info)->eh_info;
> > >> >    hdr_info->parsed_eh_frames = TRUE;
> > >> > +
> > >> > +  if (hdr_info->array_count == 0 || info->eh_frame_hdr < 2)
> > >> > +    return FALSE;
> > >> > +
> > >> > +  qsort (hdr_info->entries, hdr_info->array_count,
> > >> > +	 sizeof (asection *), cmp_eh_frame_hdr);
> > >> > +
> > >> > +  for (i = 0; i < hdr_info->array_count - 1; i++)
> > >> > +    {
> > >> > +      add_eh_frame_hdr_terminator (hdr_info->entries[i],
> > >> > +				   hdr_info->entries[i + 1]);
> > >> > +    }
> > >> > +
> > >> > +  /* Add a CANTUNWIND terminator after the last entry.  */
> > >> > + add_eh_frame_hdr_terminator (hdr_info->entries[i], NULL);
> > >> > + return TRUE;
> > >>
> > >> This routine is called from both bfd_elf_gc_sections and
> > >> bfd_elf_discard_info but I think you only want it for
> bfd_elf_discard_info.
> > >> So perhaps this should be a separate function.
> > >>
> > >> I wonder whether we could instead insert CANTUNWINDs earlier (say
> > >> in the non-dynamic part of size_dynamic_sections) based on the link
> order.
> > >> I.e. rather than comparing the start and end addresses of two
> > >> sections, we could just walk the sections in the order that they're
> > >> going to
> > be linked.
> > >>
> > >> It sounds like doing it that way would be more direct and more efficient.
> > >> Returning TRUE here forces the linker to map sections twice.
> >
> > Note that the generic version of _bfd_elf_end_eh_frame_parsing was
> > removed in the meantime.  Your patch adds it back, but I can't see
> > where it gets called.  Unless I'm missing something, this suggests
> > that the tests don't exercise this code and that extra tests might be
> needed.
> 
> I've now added link-time tests and the call _bfd_elf_end_eh_frame_parsing
> has been restored.
> >
> > >> > @@ -1271,6 +1465,45 @@ _bfd_elf_eh_frame_present (struct
> > >> bfd_link_info *info)
> > >> >    return FALSE;
> > >> >  }
> > >> >
> > >> > +/* Return true if there is at least one .eh_frame_entry section in
> > >> > +   input files.  */
> > >> > +bfd_boolean
> > >> > +_bfd_elf_eh_frame_entry_present (struct bfd_link_info *info) {
> > >> > +  asection *o;
> > >> > +  bfd *abfd;
> > >> > +
> > >> > +  for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link_next)
> > >> > +    {
> > >> > +      for (o = abfd->sections; o; o = o->next)
> > >> > +	{
> > >> > +	  const char *name = bfd_get_section_name (abfd, o);
> > >> > +
> > >> > +	  if (strcmp (name, ".eh_frame_entry")
> > >> > +	      && !bfd_is_abs_section (o->output_section))
> > >> > +	    {
> > >> > +	      if (strcmp (o->output_section->name, ".eh_frame_hdr"))
> > >> > +		return TRUE;
> > >> > +	      else
> > >> > +		{
> > >> > +		  (*_bfd_error_handler)
> > >> > +			(_("error: an '.eh_frame_entry'"
> > >> > +			   " input section that is not mapped to the"
> > >> > +			   " '.eh_frame_hdr' output section was
> seen"));
> > >> > +		  (*_bfd_error_handler)
> > >> > +			(_("note: try adding '*(.eh_frame_entry"
> > >> > +			   " .eh_frame_entry.*)' to the
> '.eh_frame_hdr'"
> > >> > +			   " output section description in the linker"
> > >> > +			   " script"));
> > >> > +		  bfd_set_error (bfd_error_invalid_operation);
> > >> > +		  return FALSE;
> > >> > +		}
> > >> > +	    }
> > >> > +	}
> > >> > +    }
> > >> > +  return FALSE;
> > >>
> > >> The caller can't tell "FALSE because an error was reported" from
> > >> "FALSE because there was no .eh_frame_entry".  We should separate
> > >> out the two cases and propagate error returns up.
> >
> > It looks like you dealt with this by removing the error checking.
> > I think we still want it, but it should be done separately and in a
> > context where the error can be propagated.
> 
> I'm deferring this for now.
> >
> > >> > @@ -1387,6 +1636,71 @@ _bfd_elf_eh_frame_section_offset (bfd
> > >> *output_bfd ATTRIBUTE_UNUSED,
> > >> >  	  + extra_augmentation_data_bytes (sec_info->entry + mid));  }
> > >> >
> > >> > +/* Write out .eh_frame_entry section.  Add CANTUNWIND
> terminator
> > >> > +if
> > >> needed.
> > >> > +   Also check that the contents look sane.  */
> > >> > +
> > >> > +bfd_boolean
> > >> > +_bfd_elf_write_section_eh_frame_entry (bfd *abfd,
> > >> > +				       asection *sec,
> > >> > +				       bfd_byte *contents)
> > >>
> > >> Formatting: first two arguments fit on a line.
> >
> > Still present.
> Fixed.
> >
> > >> > +  text_sec = (asection *) elf_section_data (sec)->sec_info;
> > >> > +  addr = text_sec->output_section->vma + text_sec->output_offset
> > >> > +	 + text_sec->size;
> > >> > +  addr &= ~1;
> > >> > +  addr -= (sec->output_section->vma + sec->output_offset +
> > >> > +sec->rawsize);
> > >> > +  BFD_ASSERT ((addr & 1) == 0);
> > >>
> > >> It looks like this could trigger for odd-sized input text sections.
> > >> I think it should be an error instead of an assert.
> >
> > I see you've done this with:
> >
> > > +  if (addr & 1)
> > > +    {
> > > +      (*_bfd_error_handler) (_("%B: %s invalid input section size"),
> > > +			     sec->owner, sec->name);
> > > +      return FALSE;
> > > +    }
> > > +  if (last_addr >= addr + sec->rawsize)
> > > +    {
> > > +      (*_bfd_error_handler) (_("%B: %s points past end of text section"),
> > > +			     sec->owner, sec->name);
> > > +      return FALSE;
> > > +    }
> >
> > but I think we want "bfd_set_error (bfd_error_bad_value);" too.
> 
> Done.
> >
> > >> > +  if (last_addr >= addr + sec->rawsize)
> > >> > +    {
> > >> > +      (*_bfd_error_handler) (_("%B: %s points past end of text
> section"),
> > >> > +			     sec->owner, sec->name);
> > >> > +      return FALSE;
> > >> > +    }
> > >> > +
> > >> > +  if (sec->size == sec->rawsize)
> > >> > +    return TRUE;
> > >> > +
> > >> > +  BFD_ASSERT (sec->size == sec->rawsize + 8);  BFD_ASSERT ((addr
> > >> > + &
> > >> > + 1) == 0);
> > >> > +  bfd_put_32 (abfd, addr, cantunwind);
> > >> > +  bfd_put_32 (abfd, 0x015d5d01, cantunwind + 4);  return
> > >> > + bfd_set_section_contents (abfd, sec->output_section,
> > >> cantunwind,
> > >> > +				   sec->output_offset + sec->rawsize,
> 8);
> > >>
> > >> ARM and c6x seem to use 0 rather than 0x5d as the "no unwind"
> > >> opcode, is that right?  If so, I think this should be a hook.
> >
> > It doesn't look like you addressed this part.
> 
> It looked to me like those ports used 1?  In any case, I've added the hook, but
> I haven't updated the arm and c6x ports to use it.
> >
> > >> The decision about whether to insert the CANTUNWIND is made during
> > >> bfd_elf_discard_info, but addresses can change after that “thanks”
> > >> to relaxation.  So this could in principle end up emitting a
> > >> CANTUNWIND for the same address as the following text section.
> >
> > As above, it looks like nothing ever calls
> > _bfd_elf_write_section_eh_frame_entry in the new version of the patch.
> > If the tests don't pick that up then I think we need some more :-)
> >
> Sorry about this missing bit.  More tests have been added and the call to this
> function has been restored.
> 
> > >> > @@ -10039,6 +10040,10 @@ elf_link_input_bfd (struct
> > >> > elf_final_link_info
> > >> *flinfo, bfd *input_bfd)
> > >> >  	      return FALSE;
> > >> >  	  }
> > >> >  	  break;
> > >> > +	case SEC_INFO_TYPE_EH_FRAME_ENTRY:
> > >> > +	    if (! _bfd_elf_write_section_eh_frame_entry (output_bfd,
> o,
> > >> contents))
> > >> > +	      return FALSE;
> > >> > +	  break;
> > >>
> > >> Excess indentation of the "if".
> >
> > Not sure: why is this no longer in the patch?
> Restored.
> >
> > >> > @@ -11807,6 +11814,13 @@ _bfd_elf_gc_mark (struct bfd_link_info
> > *info,
> > >> >  	}
> > >> >      }
> > >> >
> > >> > +  eh_frame = elf_section_eh_frame_entry (sec);  if (eh_frame &&
> > >> > + !eh_frame->gc_mark)
> > >> > +    {
> > >> > +    if (!_bfd_elf_gc_mark (info, eh_frame, gc_mark_hook))
> > >> > +      return FALSE;
> > >> > +    }
> > >>
> > >> In this context we should be using "ret":
> > >>
> > >>   eh_frame = elf_section_eh_frame_entry (sec);
> > >>   if (ret && eh_frame && !eh_frame->gc_mark)
> > >>     ret = _bfd_elf_gc_mark (info, eh_frame, gc_mark_hook);
> >
> > You changed this to:
> >
> > > +  if (eh_frame && !eh_frame->gc_mark)
> > > +    {
> > > +      if (!_bfd_elf_gc_mark (info, eh_frame, gc_mark_hook))
> > > +	ret = FALSE;
> > > +    }
> >
> > but the point was also that we don't want to try another mark once "ret"
> > is already FALSE.  The "ret &&" part of the condition was important.
> Another rebase problem.  Now fixed.
> >
> > >> > @@ -12190,22 +12204,42 @@ bfd_elf_gc_sections (bfd *abfd, struct
> > >> bfd_link_info *info)
> > >> >    bed->gc_keep (info);
> > >> >
> > >> >    /* Try to parse each bfd's .eh_frame section.  Point
> > >> elf_eh_frame_section
> > >> > -     at the .eh_frame section if we can mark the FDEs individually.  */
> > >> > +     at the .eh_frame section if we can mark the FDEs individually.
> > >> > +     Establish links from text sections to their corresponding
> > >> > +     .eh_frame_entry sections.  */
> > >> >    _bfd_elf_begin_eh_frame_parsing (info);
> > >> >    for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
> > >> >      {
> > >> >        asection *sec;
> > >> >        struct elf_reloc_cookie cookie;
> > >> >
> > >> > -      sec = bfd_get_section_by_name (sub, ".eh_frame");
> > >> > -      while (sec && init_reloc_cookie_for_section (&cookie, info, sec))
> > >> > +      if (!init_reloc_cookie (&cookie, info, sub))
> > >> > +	return FALSE;
> > >> > +
> > >> > +      if (info->eh_frame_hdr < 2)
> > >> >  	{
> > >> > -	  _bfd_elf_parse_eh_frame (sub, info, sec, &cookie);
> > >> > -	  if (elf_section_data (sec)->sec_info
> > >> > -	      && (sec->flags & SEC_LINKER_CREATED) == 0)
> > >> > -	    elf_eh_frame_section (sub) = sec;
> > >> > -	  fini_reloc_cookie_for_section (&cookie, sec);
> > >> > -	  sec = bfd_get_next_section_by_name (sec);
> > >> > +	  sec = bfd_get_section_by_name (sub, ".eh_frame");
> > >> > +	  if (sec && init_reloc_cookie_rels (&cookie, info, sub, sec))
> > >> > +	    {
> > >> > +	      _bfd_elf_parse_eh_frame (sub, info, sec, &cookie);
> > >> > +	      if (elf_section_data (sec)->sec_info
> > >> > +		  && (sec->flags & SEC_LINKER_CREATED) == 0)
> > >> > +		elf_eh_frame_section (sub) = sec;
> > >> > +	      fini_reloc_cookie_for_section (&cookie, sec);
> > >> > +	    }
> > >> > +	}
> > >> > +      else
> > >> > +	{
> > >> > +	  for (sec = sub->sections; sec; sec = sec->next)
> > >> > +	    {
> > >> > +	      if (CONST_STRNEQ (bfd_section_name (sub, sec),
> > >> ".eh_frame_entry")
> > >> > +		  && init_reloc_cookie_rels (&cookie, info, sub, sec))
> > >> > +		{
> > >> > +		  _bfd_elf_parse_eh_frame_entry (sub, info, sec,
> &cookie,
> > >> > +						 FALSE);
> > >> > +		  fini_reloc_cookie_for_section (&cookie, sec);
> > >> > +		}
> > >> > +	    }
> > >>
> > >> This changes the info->eh_frame_hdr != 2 case to only handle the
> > >> first .eh_frame section.  ("while" -> "if").
> > >>
> > >> Also, the init/fini_reloc_cookie* calls don't match up.  I assume
> > >> the
> > > idea is to
> > >> avoid excessive allocation and freeing of the locsyms, but in that
> > >> case you should use fini_reloc_cookie_rels instead of
> > >> fini_reloc_cookie_for_section and call fini_reloc_cookie at the end.
> > >> At the moment I think this leaks memory if there are no EH sections.
> > >>
> > >> I don't know either way whether splitting the
> > >> init_reloc_cookie_for_section call up is a win or not for .eh_frame.
> > >> It will be a win if there are
> > > multiple EH
> > >> sections but a loss if there are none, since we then initialise and
> > > free the bfd-
> > >> level information unnecessarily.  So I think it might make sense to
> > >> keep the .eh_frame code as it is now and restrict the *_rels to the
> > >> new
> > code.
> >
> > You changed this to:
> >
> > > @@ -12171,7 +12256,9 @@ bfd_elf_gc_sections (bfd *abfd, struct
> > > bfd_link_info *info)
> > >
> > >    /* Try to parse each bfd's .eh_frame section.  Point
> > elf_eh_frame_section
> > >       at the .eh_frame section if we can mark the FDEs individually.
> > > */
> > > -  for (sub = info->input_bfds; sub != NULL; sub = sub->link.next)
> > > +  for (sub = info->input_bfds;
> > > +       info->eh_frame_hdr_type != COMPACT_EH_HDR && sub != NULL;
> > > +       sub = sub->link.next)
> > >      {
> > >        asection *sec;
> > >        struct elf_reloc_cookie cookie;
> >
> > where info->eh_frame_hdr_type is an invariant.  I assume this was just
> > to avoid reformatting the block, but please use:
> >
> >   if (info->eh_frame_hdr_type != COMPACT_EH_HDR)
> >     for (...)
> >
> > instead.  Fortunately the block's not that big. :-)
> 
> Done.
> >
> > >> > diff --git a/gas/config/tc-mips.h b/gas/config/tc-mips.h index
> > >> > c7eaa04..a300062 100644
> > >> > --- a/gas/config/tc-mips.h
> > >> > +++ b/gas/config/tc-mips.h
> > >> > @@ -177,7 +177,9 @@ extern enum dwarf2_format
> > mips_dwarf2_format
> > >> > (asection *);
> > >> >
> > >> >  extern int mips_dwarf2_addr_size (void);  #define
> > >> > DWARF2_ADDR_SIZE(bfd) mips_dwarf2_addr_size () -#define
> > >> > DWARF2_FDE_RELOC_SIZE mips_dwarf2_addr_size ()
> > >> > +#define DWARF2_FDE_RELOC_SIZE (compact_eh ? 4 :
> > >> mips_dwarf2_addr_size
> > >> > +()) #define DWARF2_FDE_RELOC_ENCODING(enc) \
> > >> > +  (enc | (compact_eh ? DW_EH_PE_pcrel : 0))
> > >>
> > >> What case is this handling?  Please explain this a bit more.
> >
> > Doesn't look like you've addressed this bit.
> 
> Deferring for now.
> >
> > >> > @@ -4566,6 +4586,22 @@ argument is not present, otherwise second
> > >> > argument should be a constant  or a symbol name.  The default
> > >> > after @code{.cfi_startproc} is @code{.cfi_lsda 0xff},  no LSDA.
> > >> >
> > >> > +@section @code{.cfi_inline_lsda} [@var{align}]
> > >> > +@code{.cfi_inline_lsda} marks the start of a LSDA data section
> > >> > +and switches to the corresponding @code{.gnu.extab} section.
> > >> > +Must be preceded by a CFI block containing a @code{.cfi_lsda}
> > directive.
> > >> > +Only valid when generating compact EH frames (i.e.
> > >> > +with @code{.cfi_sections eh_frame_entry}.
> > >>
> > >> Missing ")".
> >
> > As above, this part of the doc is no longer there.
> 
> Restored.
> >
> > >> > @@ -4643,6 +4679,20 @@ mark a code segment that has only one
> > return
> > >> > address which is reached  by a direct branch and no copy of the
> > >> > return address exists in memory  or another register.
> > >> >
> > >> > +@section @code{.cfi_epilogue_begin} A pseudo-operation which
> > marks
> > >> > +the beginning of the epilogue in a given function.  This is
> > >> > +currently used by the compact unwind-table implementation (for
> > >> > +exception handling), which assumes a single register state for
> > >> > +unwinding throughout the body of a function.  The function is
> > >> > +scanned, and CFI directives are rewritten in a compact form.
> > >> > +However, recent versions of GCC emit unwind information for
> > >> > +function epilogues, which should not be represented in this
> > >> > +compact
> > >> > +form: hence, any CFI directives which occur in a function after
> > >> > +@code{.cfi_epilogue_end} are not processed.
> > >> > +
> > >> > +This operation only affects the internals of the assembler, and
> > >> > +is not represented in the binary output.
> > >>
> > >> Don't really follow this, sorry.  Can you give an example, or
> > >> better yet, a testcase?
> >
> > Has .cfi_epilogue_begin been dropped?
> 
> Yes.
> >
> > >>
> > >> > @@ -161,6 +278,9 @@ alloc_debugseg_item (segT seg, int subseg,
> > >> > char
> > >> > *name)  static segT  is_now_linkonce_segment (void)  {
> > >> > +  if (compact_eh)
> > >> > +    return now_seg;
> > >> > +
> > >>
> > >> Please add a comment explaining why this is correct.
> >
> > The hunk for this is:
> >
> > > +  /* We track the current segment in the cfi_insn_data struct and
> > > +     the cfi_insn_data struct for Compact EH.  */  if (compact_eh)
> > > +    return now_seg;
> >
> > but the comment repeats "the cfi_insn_data struct".  TBH I'm still not
> > sure why/whether this is correct, but let's sort the other things out first.
> 
> Okay.
> >
> > >> > -/* Emit a single byte into the current segment.  */
> > >> > -
> > >> > -static inline void
> > >> > -out_one (int byte)
> > >> > +static segT
> > >> > +get_cfi_seg (segT cseg, const char *base, flagword flags, int
> > >> > +align)
> > >> >  {
> > >> > -  FRAG_APPEND_1_CHAR (byte);
> > >> > +  if (SUPPORT_FRAME_LINKONCE || ((flags & SEC_DEBUGGING) == 0
> > &&
> > >> compact_eh))
> > >> > +    {
> > >>
> > >> Please add a comment here too to explain the SEC_DEBUGGING test.
> >
> > It doesn't look like you addressed this bit.
> >
> > >> > +static void
> > >> > +output_compact_unwind_data (struct fde_entry *fde, int align)
> > >>
> > >> Probably worth a comment here, since it wasn't obvious to me the
> > >> "align" is the alignment of the end of the data rather than the start.
> >
> > I don't think you addressed this bit.  (In case it wasn't clear, I was
> > suggesting adding a function comment that says what the function does
> > and what its arguments are, etc.)
> 
> Done.
> >
> > >> > +	  md_number_to_chars (p, 0, 4);
> > >> > +	  fix_new (frag_now, p - frag_now->fr_literal, 4,
> exp.X_add_symbol,
> > >> > +		   exp.X_add_number, howto->pc_relative, code);
> > >>
> > >> What's the significance of exp.X_add_number here?  If it was
> > >> supposed to be initialised to 0 above then I think it would be
> > >> easier to leave the
> > "exp"
> > >> stuff alone and just use fde->start_address and 0 directly in this
> > > fix_new call.
> > >> Certainly...
> > >>
> > >> >    else
> > >> >      {
> > >> > -      exp.X_op = O_symbol;
> > >> > -      exp.X_add_symbol = fde->start_address;
> > >> >        exp.X_add_number = 0;
> > >> >        addr_size = DWARF2_ADDR_SIZE (stdoutput);
> > >> >        emit_expr (&exp, addr_size);
> > >>
> > >> ...separating the add_symbol and add_number feels odd.
> >
> > This is now:
> >
> > > @@ -1575,29 +1881,43 @@ output_fde (struct fde_entry *fde, struct
> > cie_entry *cie,
> > >        TC_DWARF2_EMIT_OFFSET (cie->start_address, offset_size);
> > >      }
> > >
> > > +  exp.X_op = O_symbol;
> > > +  exp.X_add_symbol = fde->start_address;
> >
> > (A)
> >
> > >    if (eh_frame)
> > >      {
> > > -      exp.X_op = O_subtract;
> > > -      exp.X_add_number = 0;
> > > +      bfd_reloc_code_real_type code
> > > +	= tc_cfi_reloc_for_encoding (cie->fde_encoding);
> > > +      if (code !=  BFD_RELOC_NONE)
> > > +	{
> > > +	  reloc_howto_type *howto = bfd_reloc_type_lookup (stdoutput,
> > code);
> > > +	  char *p = frag_more (4);
> > > +	  md_number_to_chars (p, 0, 4);
> > > +	  fix_new (frag_now, p - frag_now->fr_literal, 4, fde->start_address,
> > > +		   0, howto->pc_relative, code);
> > > +	}
> > > +      else
> > > +	{
> > > +	  exp.X_op = O_subtract;
> > > +	  exp.X_add_number = 0;
> >
> > (B)
> >
> > >  #if CFI_DIFF_EXPR_OK
> > > -      exp.X_add_symbol = fde->start_address;
> > > -      exp.X_op_symbol = symbol_temp_new_now ();
> > > -      emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code offset.  */
> > > +	  exp.X_add_symbol = fde->start_address;
> > > +	  exp.X_op_symbol = symbol_temp_new_now ();
> > > +	  emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code
> > offset.  */
> > >  #else
> > > -      exp.X_op = O_symbol;
> > > -      exp.X_add_symbol = fde->start_address;
> > > -#ifdef tc_cfi_emit_pcrel_expr
> > > -      tc_cfi_emit_pcrel_expr (&exp, DWARF2_FDE_RELOC_SIZE);	 /*
> > Code offset.  */
> > > +	  exp.X_op = O_symbol;
> > > +	  exp.X_add_symbol = fde->start_address;
> > > +
> > > +#if defined(tc_cfi_emit_pcrel_expr)
> > > +	  tc_cfi_emit_pcrel_expr (&exp, DWARF2_FDE_RELOC_SIZE);	 /*
> > Code offset.  */
> > >  #else
> > > -      emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code offset.  */
> > > +	  emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code
> > offset.  */
> > >  #endif
> > >  #endif
> > > +	}
> > >        addr_size = DWARF2_FDE_RELOC_SIZE;
> > >      }
> > >    else
> > >      {
> > > -      exp.X_op = O_symbol;
> > > -      exp.X_add_symbol = fde->start_address;
> >
> > (C)
> >
> > >        exp.X_add_number = 0;
> > >        addr_size = DWARF2_ADDR_SIZE (stdoutput);
> > >        emit_expr (&exp, addr_size);
> >
> > (C) is still hoisted to (A), but the only use of "exp" in the
> > "eh_frame" arm is
> > (rightly) at (B), which overrides what (A) does.
> > Please just drop (A) and leave the "else" arm unmodified.
> 
> Done.
> >
> > >> > @@ -1888,7 +2223,17 @@ cfi_finish (void)
> > >> >
> > >> >  	  for (fde = all_fde_data; fde ; fde = fde->next)
> > >> >  	    {
> > >> > -	      if (SUPPORT_FRAME_LINKONCE)
> > >> > +	      if ((fde->sections & CFI_EMIT_eh_frame) == 0)
> > >> > +		continue;
> > >> > +
> > >> > +#if SUPPORT_COMPACT_EH
> > >> > +	      if (fde->eh_header_type == EH_COMPACT_HAS_LSDA)
> > >> > +		fde->eh_header_type = EH_COMPACT_LEGACY;
> > >> > +
> > >> > +	      if (fde->eh_header_type != EH_COMPACT_LEGACY)
> > >> > +		continue;
> > >> > +#endif
> > >>
> > >> Please add a comment explaining this.
> >
> > I don't think you addressed this.
> >
> 
> Done.
> > >> > @@ -1958,6 +2394,9 @@ cfi_finish (void)
> > >> >
> > >> >  	  for (fde = all_fde_data; fde ; fde = fde->next)
> > >> >  	    {
> > >> > +	      if ((fde->sections & CFI_EMIT_debug_frame) == 0)
> > >> > +		continue;
> > >> > +
> > >> >  	      if (SUPPORT_FRAME_LINKONCE)
> > >> >  		{
> > >> >  		  if (HANDLED (fde))
> > >>
> > >> Please add a comment explaining why this is needed/correct.
> >
> > Same here.
> >
> Deferred.

[-- Attachment #2: compact-eh-2015-02-08.cl --]
[-- Type: application/octet-stream, Size: 6134 bytes --]

2015-02-08  Catherine Moore  <clm@codesourcery.com>
	    Bernd Schmidt <bernds@codesourcery.com>
	    Paul Brook <paul@codesourcery.com>

	bfd/
	* bfd-in2.h (SEC_INFO_TYPE_EH_FRAME_ENTRY): Define.
	* elf-bfd.h (DWARF2_EH_HDR, COMPACT_EH_HDR): Define.
	(COMPACT_EH_CANT_UNWIND_OPCODE): Define.
	(dwarf_eh_frame_hdr_info): New struct.
	(compact_eh_frame_hdr_info): New struct.
	(eh_frame_hdr_info): Separate dwarf and compact fields.
	(elf_backend_data): Add compact_eh_encoding and cant_unwind_opcode
	fields.
	(bfd_elf_section_data): Add eh_frame_entry field.
	(elf_section_eh_frame_entry): Define.
	(bfd_elf_parse_eh_frame_entries): Declare.
	(_bfd_elf_parse_eh_frame_entry): Declare.
	(_bfd_elf_end_eh_frame_parsing): Declare.
	(_bfd_elf_write_section_eh_frame_entry): Declare.
	(_bfd_elf_fixup_eh_frame_hdr): Declare.
	(_bfd_elf_eh_frame_entry_present): Declare.
	(_bfd_elf_section_for_symbol): Declare.
	* elf-eh-frame.c (bfd_elf_discard_eh_frame_entry): New function.
	(bfd_elf_record_eh_frame_entry): New function.
	(_bfd_elf_parse_eh_frame_entry): New function.
	(_bfd_elf_parse_eh_frame): Update hdr_info field references.
	(cmp_eh_frame_hdr): New function.
	(add_eh_frame_hdr_terminator): New function.
	(_bfd_elf_end_eh_frame_parsing): New function.
	(find_merged_cie): Update hdr_info field references.
	(_bfd_elf_discard_section_eh_frame): Likewise.
	(_bfd_elf_discard_section_eh_frame_hdr): Likewise.
	Add Compact EH support.
	(_bfd_elf_eh_frame_entry_present): New function.
	(_bfd_elf_maybe_strip_eh_frame_hdr): Add Compact EH support.
	(_bfd_elf_write_section_eh_frame_entry): New function.
	(_bfd_elf_write_section_eh_frame): Update hdr_info field references.
	(_bfd_elf_write_section_eh_frame): Likewise.
	(_bfd_elf_fixup_eh_frame_hdr): New function.
	(write_compact_eh_frame_hdr): New function.
	(_bfd_elf_write_section_eh_frame_hdr): Compact EH support.  Update
	hdr_info field references.
	* elflink.c (_bfd_elf_section_for_symbol): New function.
	(elf_section_ignore_discarded_relocs, elf_link_input_bfd):
	Handle SEC_INFO_TYPE_EH_FRAME_ENTRY.
	(bfd_elf_final_link): Call _bfd_elf_fixup_eh_frame_hdr.
	(_bfd_elf_gc_mark):  Support Compact EH.
	(bfd_elf_parse_eh_frame_entries): New function.
	(bfd_elf_gc_sections): Support Compact EH.
	(bfd_elf_discard_info): Likewise.
	* elfxx-mips.c (_bfd_mips_elf_compact_eh_encoding): New function.
	(_bfd_mips_elf_cant_unwind_opcode): New function.
	* elfxx-mips.h (_bfd_mips_elf_compact_eh_encoding): Declare.
	(_bfd_mips_elf_cant_unwind_opcode): Declare.
	(elf_backend_compact_eh_encoding): Define.
	(elf_backend_cant_unwind_opcode): Define.
	* elfxx-target.h (elf_backend_compact_eh_encoding):
	Provide default definition.
	(elf_backend_cant_unwind_opcode): Provide default definition.
	* section.c (SEC_INFO_TYPE_EH_FRAME_ENTRY): Define.

	gas/
	* config/tc-mips.c (md_apply_fix): Handle BFD_RELOC_NONE.
	(s_ehword): Use BFD_RELOC_32_PCREL.
	(mips_fix_adjustable): Handle BFD_RELOC_32_PCREL.
	(mips_cfi_reloc_for_encoding): New function.
	* tc-mips.h
	(DWARF2_FDE_RELOC_SIZE): Define.
	(DWARF2_FDE_RELOC_ENCODING): Define.
	(tc_cfi_reloc_for_encoding): Define.
	(mips_cfi_reloc_for_encoding): Define.
	(tc_compact_eh_opcode_stop): Define.
	(tc_compact_eh_opcode_pad): Define. 0x5f
	* doc/as.texinfo: Document Compact EH.
	* doc/internals.texi (tc_cfi_reloc_for_encoding): Document.
	* dw2gencfi.c (EH_FRAME_LINKONCE): Define.
	(tc_cfi_reloc_for_encoding): Provide default definition.
	(compact_eh): New.
	(emit_expr_encoded): New function.
	(get_debugseg_name): Support Compact EH.
	(alloc_debugseg_item): Likewise.
	(alloc_fde_entry): Initialize fde->eh_header_type.
	(dot_cfi_fde_data): Declare.
	(dot_cfi_personality_id): Declare.
	(dot_cfi_inline_lsda): Declare.
	(cfi_pseudo_table): Add "cfe_fde_data", "cfi_personality_id",
	and "cfi_inline_lsda".
	(dot_cfi_personality): Support Compact EH.
	(dot_cfi_lsda): Likewise.
	(CFI_EMIT_eh_frame_compact): Define.
	(dot_cfi_sections): Support Compact EH.
	(dot_cfi_startproc): Likewise.
	(dot_cfi_endproc): Likewise.
	(get_cfi_seg): Likewise.
	(output_cfi_insn): Rename encoding_size to enc_size.  Update
	all uses.
	(output_cie): Support Compact EH.
	* dw2gencfi.h (SUPPORT_COMPACT_EH): Define.
	(MULTIPLE_FRAME_SECTIONS): Define.
	* enum describing the Compact EH header format.
	(fde_entry): Add fields personality_id, eh_header_type,
	eh_data and eh_loc.

	gas/testsuite/
	* gas/mips/mips.exp: Run new tests.

	* gas/mips/compact-eh-1.s: New file.
	* gas/mips/compact-eh-2.s: New file.
	* gas/mips/compact-eh-3.s: New file.
	* gas/mips/compact-eh-4.s: New file.
	* gas/mips/compact-eh-5.s: New file.
	* gas/mips/compact-eh-6.s: New file.
	* gas/mips/compact-eh-7.s: New file.
	* gas/mips/compact-eh-eb-1.d: New file.
	* gas/mips/compact-eh-eb-2.d: New file.
	* gas/mips/compact-eh-eb-3.d: New file.
	* gas/mips/compact-eh-eb-4.d: New file.
	* gas/mips/compact-eh-eb-5.d: New file.
	* gas/mips/compact-eh-eb-6.d: New file.
	* gas/mips/compact-eh-eb-7.d: New file.
	* gas/mips/compact-eh-el-1.d: New file.
	* gas/mips/compact-eh-el-2.d: New file.
	* gas/mips/compact-eh-el-3.d: New file.
	* gas/mips/compact-eh-el-4.d: New file.
	* gas/mips/compact-eh-el-5.d: New file.
	* gas/mips/compact-eh-el-6.d: New file.
	* gas/mips/compact-eh-el-7.d: New file.
	* gas/mips/compact-eh-err1.l: New file.
	* gas/mips/compact-eh-err1.s: New file.
	* gas/mips/compact-eh-err2.l: New file.
	* gas/mips/compact-eh-err2.s: New file.

	include/
	* bfdlink.h: Rename eh_frame_hdr to eh_frame_hdr_type.

	ld/
	* emultempl/elf32.em (gld${EMULATION_NAME}_after_open):
	Add Compact EH support.
	* scripttempl/elf.sc: Handle .eh_frarme_entry and .gnu_extab
	sections.

	ld/testsuite/
	* compact-eh.ld: New.
	* compact-eh1.d: New.
	* compact-eh1.s: New.
	* compact-eh1a.s: New.
	* compact-eh1b.s: New.
	* compact-eh2.d: New.
	* compact-eh2.s: New.
	* compact-eh3.d: New.
	* compact-eh3.s: New.
	* compact-eh3a.s: New.
	* compact-eh4.d: New.
	* compact-eh5.d: New.
	* compact-eh6.d: New.

[-- Attachment #3: compact-eh-2015-02-08.patch --]
[-- Type: application/octet-stream, Size: 124430 bytes --]

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 91dbf80..703e0c0 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -1423,6 +1423,7 @@ typedef struct bfd_section
 #define SEC_INFO_TYPE_EH_FRAME  3
 #define SEC_INFO_TYPE_JUST_SYMS 4
 #define SEC_INFO_TYPE_TARGET    5
+#define SEC_INFO_TYPE_EH_FRAME_ENTRY 6
 
   /* Nonzero if this section uses RELA relocations, rather than REL.  */
   unsigned int use_rela_p:1;
diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index 49ffe79..38987b6 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -383,16 +383,41 @@ struct eh_frame_array_ent
 
 struct htab;
 
-struct eh_frame_hdr_info
+#define DWARF2_EH_HDR 1
+#define COMPACT_EH_HDR 2
+
+/* Endian-neutral code indicating that a function cannot be unwound.  */
+#define COMPACT_EH_CANT_UNWIND_OPCODE 0x015d5d01
+
+struct dwarf_eh_frame_hdr_info
 {
   struct htab *cies;
-  asection *hdr_sec;
-  unsigned int fde_count, array_count;
-  struct eh_frame_array_ent *array;
+  unsigned int fde_count;
   /* TRUE if .eh_frame_hdr should contain the sorted search table.
      We build it if we successfully read all .eh_frame input sections
      and recognize them.  */
   bfd_boolean table;
+  struct eh_frame_array_ent *array;
+};
+
+struct compact_eh_frame_hdr_info
+{
+  unsigned int allocated_entries;
+  /* eh_frame_entry fragments.  */
+  asection **entries;
+};
+
+struct eh_frame_hdr_info
+{
+  asection *hdr_sec;
+  unsigned int array_count;
+  bfd_boolean frame_hdr_is_compact;
+  union
+    {
+      struct dwarf_eh_frame_hdr_info dwarf;
+      struct compact_eh_frame_hdr_info compact;
+    }
+  u;
 };
 
 /* Enum used to identify target specific extensions to the elf_obj_tdata
@@ -1283,6 +1308,12 @@ struct elf_backend_data
      or give an error and return FALSE.  */
   bfd_boolean (*obj_attrs_handle_unknown) (bfd *, int);
 
+  /* Encoding used for compact EH tables.  */
+  int (*compact_eh_encoding) (struct bfd_link_info *);
+
+  /* Opcode representing no unwind.  */
+  int (*cant_unwind_opcode) (struct bfd_link_info *);
+
   /* This is non-zero if static TLS segments require a special alignment.  */
   unsigned static_tls_alignment;
 
@@ -1433,6 +1464,9 @@ struct bfd_elf_section_data
      field acts as a chain pointer.  */
   struct eh_cie_fde *fde_list;
 
+  /* Link from a text section to its .eh_frame_entry section.  */
+  asection *eh_frame_entry;
+
   /* A pointer used for various section optimizations.  */
   void *sec_info;
 };
@@ -1446,6 +1480,7 @@ struct bfd_elf_section_data
 #define elf_next_in_group(sec)	(elf_section_data(sec)->next_in_group)
 #define elf_fde_list(sec)	(elf_section_data(sec)->fde_list)
 #define elf_sec_group(sec)	(elf_section_data(sec)->sec_group)
+#define elf_section_eh_frame_entry(sec)	(elf_section_data(sec)->eh_frame_entry)
 
 #define xvec_get_elf_backend_data(xvec) \
   ((const struct elf_backend_data *) (xvec)->backend_data)
@@ -1971,8 +2006,15 @@ extern bfd_boolean _bfd_elf_strtab_emit
 extern void _bfd_elf_strtab_finalize
   (struct elf_strtab_hash *);
 
+extern bfd_boolean bfd_elf_parse_eh_frame_entries
+  (bfd *, struct bfd_link_info *);
+extern bfd_boolean _bfd_elf_parse_eh_frame_entry
+  (struct bfd_link_info *, asection *, struct elf_reloc_cookie *);
 extern void _bfd_elf_parse_eh_frame
   (bfd *, struct bfd_link_info *, asection *, struct elf_reloc_cookie *);
+extern bfd_boolean _bfd_elf_end_eh_frame_parsing
+  (struct bfd_link_info *info);
+
 extern bfd_boolean _bfd_elf_discard_section_eh_frame
   (bfd *, struct bfd_link_info *, asection *,
    bfd_boolean (*) (bfd_vma, void *), struct elf_reloc_cookie *);
@@ -1982,10 +2024,15 @@ extern bfd_vma _bfd_elf_eh_frame_section_offset
   (bfd *, struct bfd_link_info *, asection *, bfd_vma);
 extern bfd_boolean _bfd_elf_write_section_eh_frame
   (bfd *, struct bfd_link_info *, asection *, bfd_byte *);
+bfd_boolean _bfd_elf_write_section_eh_frame_entry
+  (bfd *, struct bfd_link_info *, asection *, bfd_byte *);
+extern bfd_boolean _bfd_elf_fixup_eh_frame_hdr (struct bfd_link_info *);
 extern bfd_boolean _bfd_elf_write_section_eh_frame_hdr
   (bfd *, struct bfd_link_info *);
 extern bfd_boolean _bfd_elf_eh_frame_present
   (struct bfd_link_info *);
+extern bfd_boolean _bfd_elf_eh_frame_entry_present
+  (struct bfd_link_info *);
 extern bfd_boolean _bfd_elf_maybe_strip_eh_frame_hdr
   (struct bfd_link_info *);
 
@@ -2009,6 +2056,8 @@ extern bfd_boolean _bfd_elf_create_dynamic_sections
   (bfd *, struct bfd_link_info *);
 extern bfd_boolean _bfd_elf_create_got_section
   (bfd *, struct bfd_link_info *);
+extern asection *_bfd_elf_section_for_symbol
+  (struct elf_reloc_cookie *, unsigned long, bfd_boolean);
 extern struct elf_link_hash_entry *_bfd_elf_define_linkage_sym
   (bfd *, struct bfd_link_info *, asection *, const char *);
 extern void _bfd_elf_init_1_index_section
diff --git a/bfd/elf-eh-frame.c b/bfd/elf-eh-frame.c
index faa0461..024a25f 100644
--- a/bfd/elf-eh-frame.c
+++ b/bfd/elf-eh-frame.c
@@ -452,6 +452,111 @@ make_pc_relative (unsigned char encoding, unsigned int ptr_size)
   return encoding | DW_EH_PE_pcrel;
 }
 
+/*  Examine each .eh_frame_entry section and discard those
+    those that are marked SEC_EXCLUDE.  */
+
+static void
+bfd_elf_discard_eh_frame_entry (struct eh_frame_hdr_info *hdr_info)
+{
+  unsigned int i;
+  for (i = 0; i < hdr_info->array_count; i++)
+    {
+      if (hdr_info->u.compact.entries[i]->flags & SEC_EXCLUDE)
+	{
+	  unsigned int j;
+	  for (j = i + 1; j < hdr_info->array_count; j++)
+	    hdr_info->u.compact.entries[j-1] = hdr_info->u.compact.entries[j];
+
+	  hdr_info->array_count--;
+	  hdr_info->u.compact.entries[hdr_info->array_count] = NULL;
+	  i--;
+        }
+    }
+}
+
+/* Add a .eh_frame_entry section.  */
+
+static void
+bfd_elf_record_eh_frame_entry (struct eh_frame_hdr_info *hdr_info,
+				 asection *sec)
+{
+  if (hdr_info->array_count == hdr_info->u.compact.allocated_entries)
+    {
+      if (hdr_info->u.compact.allocated_entries == 0)
+	{
+	  hdr_info->frame_hdr_is_compact = TRUE;
+	  hdr_info->u.compact.allocated_entries = 2;
+	  hdr_info->u.compact.entries =
+	    bfd_malloc (hdr_info->u.compact.allocated_entries
+			* sizeof (hdr_info->u.compact.entries[0]));
+	}
+      else
+	{
+	  hdr_info->u.compact.allocated_entries *= 2;
+	  hdr_info->u.compact.entries =
+	    bfd_realloc (hdr_info->u.compact.entries,
+			 hdr_info->u.compact.allocated_entries
+			   * sizeof (hdr_info->u.compact.entries[0]));
+	}
+
+      BFD_ASSERT (hdr_info->u.compact.entries);
+    }
+
+  hdr_info->u.compact.entries[hdr_info->array_count++] = sec;
+}
+
+/* Parse a .eh_frame_entry section.  Figure out which text section it
+   references.  */
+
+bfd_boolean
+_bfd_elf_parse_eh_frame_entry (struct bfd_link_info *info,
+			       asection *sec, struct elf_reloc_cookie *cookie)
+{
+  struct elf_link_hash_table *htab;
+  struct eh_frame_hdr_info *hdr_info;
+  unsigned long r_symndx;
+  asection *text_sec;
+
+  htab = elf_hash_table (info);
+  hdr_info = &htab->eh_info;
+
+  if (sec->size == 0
+      || sec->sec_info_type != SEC_INFO_TYPE_NONE)
+    {
+      return TRUE;
+    }
+
+  if (sec->output_section && bfd_is_abs_section (sec->output_section))
+    {
+      /* At least one of the sections is being discarded from the
+	 link, so we should just ignore them.  */
+      return TRUE;
+    }
+
+  if (cookie->rel == cookie->relend)
+    return FALSE;
+
+  /* The first relocation is the function start.  */
+  r_symndx = cookie->rel->r_info >> cookie->r_sym_shift;
+  if (r_symndx == STN_UNDEF)
+    return FALSE;
+
+  text_sec = _bfd_elf_section_for_symbol (cookie, r_symndx, FALSE);
+
+  if (text_sec == NULL)
+    return FALSE;
+
+  elf_section_eh_frame_entry (text_sec) = sec;
+  if (text_sec->output_section
+      && bfd_is_abs_section (text_sec->output_section))
+    sec->flags |= SEC_EXCLUDE;
+
+  sec->sec_info_type = SEC_INFO_TYPE_EH_FRAME_ENTRY;
+  elf_section_data (sec)->sec_info = text_sec;
+  bfd_elf_record_eh_frame_entry (hdr_info, sec);
+  return TRUE;
+}
+
 /* Try to parse .eh_frame section SEC, which belongs to ABFD.  Store the
    information in the section's sec_info field on success.  COOKIE
    describes the relocations in SEC.  */
@@ -925,7 +1030,7 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info,
   (*info->callbacks->einfo)
     (_("%P: error in %B(%A); no .eh_frame_hdr table will be created.\n"),
      abfd, sec);
-  hdr_info->table = FALSE;
+  hdr_info->u.dwarf.table = FALSE;
   if (sec_info)
     free (sec_info);
  success:
@@ -936,6 +1041,89 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info,
 #undef REQUIRE
 }
 
+/* Order eh_frame_hdr entries by the VMA of their text section.  */
+
+static int
+cmp_eh_frame_hdr (const void *a, const void *b)
+{
+  bfd_vma text_a;
+  bfd_vma text_b;
+  asection *sec;
+
+  sec = *(asection *const *)a;
+  sec = (asection *) elf_section_data (sec)->sec_info;
+  text_a = sec->output_section->vma + sec->output_offset;
+  sec = *(asection *const *)b;
+  sec = (asection *) elf_section_data (sec)->sec_info;
+  text_b = sec->output_section->vma + sec->output_offset;
+
+  if (text_a < text_b)
+    return -1;
+  return text_a > text_b;
+
+}
+
+/* Add space for a CANTUNWIND terminator to SEC if the text sections
+   referenced by it and NEXT are not contiguous, or NEXT is NULL.  */
+
+static void
+add_eh_frame_hdr_terminator (asection *sec,
+			     asection *next)
+{
+  bfd_vma end;
+  bfd_vma next_start;
+  asection *text_sec;
+
+  if (next)
+    {
+      /* See if there is a gap (presumably a text section without unwind info)
+	 between these two entries.  */
+      text_sec = (asection *) elf_section_data (sec)->sec_info;
+      end = text_sec->output_section->vma + text_sec->output_offset
+	    + text_sec->size;
+      text_sec = (asection *) elf_section_data (next)->sec_info;
+      next_start = text_sec->output_section->vma + text_sec->output_offset;
+      if (end == next_start)
+	return;
+    }
+
+  /* Add space for a CANTUNWIND terminator.  */
+  if (!sec->rawsize)
+    sec->rawsize = sec->size;
+
+  bfd_set_section_size (sec->owner, sec, sec->size + 8);
+}
+
+/* Finish a pass over all .eh_frame_entry sections.  */
+
+bfd_boolean
+_bfd_elf_end_eh_frame_parsing (struct bfd_link_info *info)
+{
+  struct eh_frame_hdr_info *hdr_info;
+  unsigned int i;
+
+  hdr_info = &elf_hash_table (info)->eh_info;
+
+  if (info->eh_frame_hdr_type != COMPACT_EH_HDR
+      || hdr_info->array_count == 0)
+    return FALSE;
+
+  bfd_elf_discard_eh_frame_entry (hdr_info);
+
+  qsort (hdr_info->u.compact.entries, hdr_info->array_count,
+	 sizeof (asection *), cmp_eh_frame_hdr);
+
+  for (i = 0; i < hdr_info->array_count - 1; i++)
+    {
+      add_eh_frame_hdr_terminator (hdr_info->u.compact.entries[i],
+				   hdr_info->u.compact.entries[i + 1]);
+    }
+
+  /* Add a CANTUNWIND terminator after the last entry.  */
+  add_eh_frame_hdr_terminator (hdr_info->u.compact.entries[i], NULL);
+  return TRUE;
+}
+
 /* Mark all relocations against CIE or FDE ENT, which occurs in
    .eh_frame section SEC.  COOKIE describes the relocations in SEC;
    its "rel" field can be changed freely.  */
@@ -1089,13 +1277,14 @@ find_merged_cie (bfd *abfd, struct bfd_link_info *info, asection *sec,
 
   /* See if we can merge this CIE with an earlier one.  */
   cie_compute_hash (cie);
-  if (hdr_info->cies == NULL)
+  if (hdr_info->u.dwarf.cies == NULL)
     {
-      hdr_info->cies = htab_try_create (1, cie_hash, cie_eq, free);
-      if (hdr_info->cies == NULL)
+      hdr_info->u.dwarf.cies = htab_try_create (1, cie_hash, cie_eq, free);
+      if (hdr_info->u.dwarf.cies == NULL)
 	return cie_inf;
     }
-  loc = htab_find_slot_with_hash (hdr_info->cies, cie, cie->hash, INSERT);
+  loc = htab_find_slot_with_hash (hdr_info->u.dwarf.cies, cie,
+				  cie->hash, INSERT);
   if (loc == NULL)
     return cie_inf;
 
@@ -1185,13 +1374,13 @@ _bfd_elf_discard_section_eh_frame
 		   which we cannot turn into PC relative,
 		   don't create the binary search table,
 		   since it is affected by runtime relocations.  */
-		hdr_info->table = FALSE;
+		hdr_info->u.dwarf.table = FALSE;
 		(*info->callbacks->einfo)
 		  (_("%P: FDE encoding in %B(%A) prevents .eh_frame_hdr"
 		     " table being created.\n"), abfd, sec);
 	      }
 	    ent->removed = 0;
-	    hdr_info->fde_count++;
+	    hdr_info->u.dwarf.fde_count++;
 	    ent->u.fde.cie_inf = find_merged_cie (abfd, info, sec, hdr_info,
 						  cookie, ent->u.fde.cie_inf);
 	  }
@@ -1230,19 +1419,28 @@ _bfd_elf_discard_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
   htab = elf_hash_table (info);
   hdr_info = &htab->eh_info;
 
-  if (hdr_info->cies != NULL)
+  if (!hdr_info->frame_hdr_is_compact && hdr_info->u.dwarf.cies != NULL)
     {
-      htab_delete (hdr_info->cies);
-      hdr_info->cies = NULL;
+      htab_delete (hdr_info->u.dwarf.cies);
+      hdr_info->u.dwarf.cies = NULL;
     }
 
   sec = hdr_info->hdr_sec;
   if (sec == NULL)
     return FALSE;
 
-  sec->size = EH_FRAME_HDR_SIZE;
-  if (hdr_info->table)
-    sec->size += 4 + hdr_info->fde_count * 8;
+  if (info->eh_frame_hdr_type == COMPACT_EH_HDR)
+    {
+      /* For compact frame we only add the header.  The actual table comes
+         from the .eh_frame_entry sections.  */
+      sec->size = 8;
+    }
+  else
+    {
+      sec->size = EH_FRAME_HDR_SIZE;
+      if (hdr_info->u.dwarf.table)
+	sec->size += 4 + hdr_info->u.dwarf.fde_count * 8;
+    }
 
   elf_eh_frame_hdr (abfd) = sec;
   return TRUE;
@@ -1268,6 +1466,29 @@ _bfd_elf_eh_frame_present (struct bfd_link_info *info)
   return FALSE;
 }
 
+/* Return true if there is at least one .eh_frame_entry section in
+   input files.  */
+
+bfd_boolean
+_bfd_elf_eh_frame_entry_present (struct bfd_link_info *info)
+{
+  asection *o;
+  bfd *abfd;
+
+  for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link.next)
+    {
+      for (o = abfd->sections; o; o = o->next)
+	{
+	  const char *name = bfd_get_section_name (abfd, o);
+
+	  if (strcmp (name, ".eh_frame_entry")
+	      && !bfd_is_abs_section (o->output_section))
+	    return TRUE;
+	}
+    }
+  return FALSE;
+}
+
 /* This function is called from size_dynamic_sections.
    It needs to decide whether .eh_frame_hdr should be output or not,
    because when the dynamic symbol table has been sized it is too late
@@ -1278,6 +1499,8 @@ _bfd_elf_maybe_strip_eh_frame_hdr (struct bfd_link_info *info)
 {
   struct elf_link_hash_table *htab;
   struct eh_frame_hdr_info *hdr_info;
+  struct bfd_link_hash_entry *bh = NULL;
+  struct elf_link_hash_entry *h;
 
   htab = elf_hash_table (info);
   hdr_info = &htab->eh_info;
@@ -1285,15 +1508,32 @@ _bfd_elf_maybe_strip_eh_frame_hdr (struct bfd_link_info *info)
     return TRUE;
 
   if (bfd_is_abs_section (hdr_info->hdr_sec->output_section)
-      || !info->eh_frame_hdr
-      || !_bfd_elf_eh_frame_present (info))
+      || info->eh_frame_hdr_type == 0
+      || (info->eh_frame_hdr_type == DWARF2_EH_HDR
+	  && !_bfd_elf_eh_frame_present (info))
+      || (info->eh_frame_hdr_type == COMPACT_EH_HDR
+	  && !_bfd_elf_eh_frame_entry_present (info)))
     {
       hdr_info->hdr_sec->flags |= SEC_EXCLUDE;
       hdr_info->hdr_sec = NULL;
       return TRUE;
     }
 
-  hdr_info->table = TRUE;
+  /* Add a hidden symbol so that systems without access to PHDRs can
+     find the table.  */
+  if (! (_bfd_generic_link_add_one_symbol
+	 (info, info->output_bfd, "__GNU_EH_FRAME_HDR", BSF_LOCAL,
+	  hdr_info->hdr_sec, 0, NULL, FALSE, FALSE, &bh)))
+    return FALSE;
+
+  h = (struct elf_link_hash_entry *) bh;
+  h->def_regular = 1;
+  h->other = STV_HIDDEN;
+  get_elf_backend_data
+    (info->output_bfd)->elf_backend_hide_symbol (info, h, TRUE);
+
+  if (!hdr_info->frame_hdr_is_compact)
+    hdr_info->u.dwarf.table = TRUE;
   return TRUE;
 }
 
@@ -1384,6 +1624,80 @@ _bfd_elf_eh_frame_section_offset (bfd *output_bfd ATTRIBUTE_UNUSED,
 	  + extra_augmentation_data_bytes (sec_info->entry + mid));
 }
 
+/* Write out .eh_frame_entry section.  Add CANTUNWIND terminator if needed.
+   Also check that the contents look sane.  */
+
+bfd_boolean
+_bfd_elf_write_section_eh_frame_entry (bfd *abfd, struct bfd_link_info *info,
+				       asection *sec, bfd_byte *contents)
+{
+  const struct elf_backend_data *bed;
+  bfd_byte cantunwind[8];
+  bfd_vma addr;
+  bfd_vma last_addr;
+  asection *text_sec;
+  bfd_vma offset;
+
+  if (!sec->rawsize)
+    sec->rawsize = sec->size;
+
+  BFD_ASSERT (sec->sec_info_type == SEC_INFO_TYPE_EH_FRAME_ENTRY);
+
+  if (sec->flags & SEC_EXCLUDE)
+    return TRUE;
+
+  if (!bfd_set_section_contents (abfd, sec->output_section, contents,
+				 sec->output_offset, sec->rawsize))
+      return FALSE;
+
+  last_addr = bfd_get_signed_32 (abfd, contents);
+  /* Check that all the entries are in order.  */
+  for (offset = 8; offset < sec->rawsize; offset += 8)
+    {
+      addr = bfd_get_signed_32 (abfd, contents + offset) + offset;
+      if (addr <= last_addr)
+	{
+	  (*_bfd_error_handler) (_("%B: %s not in order"), sec->owner, sec->name);
+	  return FALSE;
+	}
+
+      last_addr = addr;
+    }
+
+  text_sec = (asection *) elf_section_data (sec)->sec_info;
+  addr = text_sec->output_section->vma + text_sec->output_offset
+	 + text_sec->size;
+  addr &= ~1;
+  addr -= (sec->output_section->vma + sec->output_offset + sec->rawsize);
+  if (addr & 1)
+    {
+      (*_bfd_error_handler) (_("%B: %s invalid input section size"),
+			     sec->owner, sec->name);
+      bfd_set_error (bfd_error_bad_value);
+      return FALSE;
+    }
+  if (last_addr >= addr + sec->rawsize)
+    {
+      (*_bfd_error_handler) (_("%B: %s points past end of text section"),
+			     sec->owner, sec->name);
+      bfd_set_error (bfd_error_bad_value);
+      return FALSE;
+    }
+
+  if (sec->size == sec->rawsize)
+    return TRUE;
+
+  bed = get_elf_backend_data (abfd);
+  BFD_ASSERT (sec->size == sec->rawsize + 8);
+  BFD_ASSERT ((addr & 1) == 0);
+  BFD_ASSERT (bed->cant_unwind_opcode);
+
+  bfd_put_32 (abfd, addr, cantunwind);
+  bfd_put_32 (abfd, (*bed->cant_unwind_opcode) (info), cantunwind + 4);
+  return bfd_set_section_contents (abfd, sec->output_section, cantunwind,
+				   sec->output_offset + sec->rawsize, 8);
+}
+
 /* Write out .eh_frame section.  This is called with the relocated
    contents.  */
 
@@ -1413,10 +1727,14 @@ _bfd_elf_write_section_eh_frame (bfd *abfd,
   htab = elf_hash_table (info);
   hdr_info = &htab->eh_info;
 
-  if (hdr_info->table && hdr_info->array == NULL)
-    hdr_info->array = (struct eh_frame_array_ent *)
-        bfd_malloc (hdr_info->fde_count * sizeof(*hdr_info->array));
-  if (hdr_info->array == NULL)
+  if (hdr_info->u.dwarf.table && hdr_info->u.dwarf.array == NULL)
+    {
+      hdr_info->frame_hdr_is_compact = FALSE;
+      hdr_info->u.dwarf.array = (struct eh_frame_array_ent *)
+        bfd_malloc (hdr_info->u.dwarf.fde_count
+		    * sizeof (*hdr_info->u.dwarf.array));
+    }
+  if (hdr_info->u.dwarf.array == NULL)
     hdr_info = NULL;
 
   /* The new offsets can be bigger or smaller than the original offsets.
@@ -1651,10 +1969,11 @@ _bfd_elf_write_section_eh_frame (bfd *abfd,
 		 dwarf_vma is 64-bit.  */
 	      if (sizeof (address) > 4 && ptr_size == 4)
 		address &= 0xffffffff;
-	      hdr_info->array[hdr_info->array_count].initial_loc = address;
-	      hdr_info->array[hdr_info->array_count].range
+	      hdr_info->u.dwarf.array[hdr_info->array_count].initial_loc
+		= address;
+	      hdr_info->u.dwarf.array[hdr_info->array_count].range
 		= read_value (abfd, buf + width, width, FALSE);
-	      hdr_info->array[hdr_info->array_count++].fde
+	      hdr_info->u.dwarf.array[hdr_info->array_count++].fde
 		= (sec->output_section->vma
 		   + sec->output_offset
 		   + ent->new_offset);
@@ -1756,10 +2075,111 @@ vma_compare (const void *a, const void *b)
   return 0;
 }
 
-/* Write out .eh_frame_hdr section.  This must be called after
-   _bfd_elf_write_section_eh_frame has been called on all input
-   .eh_frame sections.
-   .eh_frame_hdr format:
+/* Reorder .eh_frame_entry sections to match the associated text sections.
+   This routine is called during the final linking step, just before writing
+   the contents.  At this stage, sections in the eh_frame_hdr_info are already
+   sorted in order of increasing text section address and so we simply need
+   to make the .eh_frame_entrys follow that same order.  Note that it is
+   invalid for a linker script to try to force a particular order of
+   .eh_frame_entry sections.  */
+
+bfd_boolean
+_bfd_elf_fixup_eh_frame_hdr (struct bfd_link_info *info)
+{
+  asection *sec = NULL;
+  asection *osec;
+  struct eh_frame_hdr_info *hdr_info;
+  unsigned int i;
+  bfd_vma offset;
+  struct bfd_link_order *p;
+
+  hdr_info = &elf_hash_table (info)->eh_info;
+
+  if (hdr_info->hdr_sec == NULL
+      || info->eh_frame_hdr_type != COMPACT_EH_HDR
+      || hdr_info->array_count == 0)
+    return TRUE;
+
+  /* Change section output offsets to be in text section order.  */
+  offset = 8;
+  osec = hdr_info->u.compact.entries[0]->output_section;
+  for (i = 0; i < hdr_info->array_count; i++)
+    {
+      sec = hdr_info->u.compact.entries[i];
+      if (sec->output_section != osec)
+	{
+	  (*_bfd_error_handler)
+	    (_("Invalid output section for .eh_frame_entry: %s"),
+	     sec->output_section->name);
+	  return FALSE;
+	}
+      sec->output_offset = offset;
+      offset += sec->size;
+    }
+
+
+  /* Fix the link_order to match.  */
+  for (p = sec->output_section->map_head.link_order; p != NULL; p = p->next)
+    {
+      if (p->type != bfd_indirect_link_order)
+	abort();
+
+      p->offset = p->u.indirect.section->output_offset;
+      if (p->next != NULL)
+        i--;
+    }
+
+  if (i != 0)
+    {
+      (*_bfd_error_handler)
+	(_("Invalid contents in %s section"), osec->name);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+/* The .eh_frame_hdr format for Compact EH frames:
+   ubyte version		(2)
+   ubyte eh_ref_enc		(DW_EH_PE_* encoding of typinfo references)
+   uint32_t count		(Number of entries in table)
+   [array from .eh_frame_entry sections]  */
+
+static bfd_boolean
+write_compact_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
+{
+  struct elf_link_hash_table *htab;
+  struct eh_frame_hdr_info *hdr_info;
+  asection *sec;
+  const struct elf_backend_data *bed;
+  bfd_vma count;
+  bfd_byte contents[8];
+  unsigned int i;
+
+  htab = elf_hash_table (info);
+  hdr_info = &htab->eh_info;
+  sec = hdr_info->hdr_sec;
+
+  if (sec->size != 8)
+    abort();
+
+  for (i = 0; i < sizeof (contents); i++)
+    contents[i] = 0;
+
+  contents[0] = COMPACT_EH_HDR;
+  bed = get_elf_backend_data (abfd);
+
+  BFD_ASSERT (bed->compact_eh_encoding);
+  contents[1] = (*bed->compact_eh_encoding) (info);
+
+  count = (sec->output_section->size - 8) / 8;
+  bfd_put_32 (abfd, count, contents + 4);
+  return bfd_set_section_contents (abfd, sec->output_section, contents,
+				   (file_ptr) sec->output_offset, sec->size);
+}
+
+/* The .eh_frame_hdr format for DWARF frames:
+
    ubyte version		(currently 1)
    ubyte eh_frame_ptr_enc  	(DW_EH_PE_* encoding of pointer to start of
 				 .eh_frame section)
@@ -1778,8 +2198,8 @@ vma_compare (const void *a, const void *b)
 				 FDE initial_location field and FDE address,
 				 sorted by increasing initial_loc).  */
 
-bfd_boolean
-_bfd_elf_write_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
+static bfd_boolean
+write_dwarf_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
 {
   struct elf_link_hash_table *htab;
   struct eh_frame_hdr_info *hdr_info;
@@ -1789,100 +2209,130 @@ _bfd_elf_write_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
   htab = elf_hash_table (info);
   hdr_info = &htab->eh_info;
   sec = hdr_info->hdr_sec;
+  bfd_byte *contents;
+  asection *eh_frame_sec;
+  bfd_size_type size;
+  bfd_vma encoded_eh_frame;
+
+  size = EH_FRAME_HDR_SIZE;
+  if (hdr_info->u.dwarf.array
+      && hdr_info->array_count == hdr_info->u.dwarf.fde_count)
+    size += 4 + hdr_info->u.dwarf.fde_count * 8;
+  contents = (bfd_byte *) bfd_malloc (size);
+  if (contents == NULL)
+    return FALSE;
 
-  if (info->eh_frame_hdr && sec != NULL)
+  eh_frame_sec = bfd_get_section_by_name (abfd, ".eh_frame");
+  if (eh_frame_sec == NULL)
     {
-      bfd_byte *contents;
-      asection *eh_frame_sec;
-      bfd_size_type size;
-      bfd_vma encoded_eh_frame;
-
-      size = EH_FRAME_HDR_SIZE;
-      if (hdr_info->array && hdr_info->array_count == hdr_info->fde_count)
-	size += 4 + hdr_info->fde_count * 8;
-      contents = (bfd_byte *) bfd_malloc (size);
-      if (contents == NULL)
-	return FALSE;
+      free (contents);
+      return FALSE;
+    }
 
-      eh_frame_sec = bfd_get_section_by_name (abfd, ".eh_frame");
-      if (eh_frame_sec == NULL)
-	{
-	  free (contents);
-	  return FALSE;
-	}
+  memset (contents, 0, EH_FRAME_HDR_SIZE);
+  /* Version.  */
+  contents[0] = 1;
+  /* .eh_frame offset.  */
+  contents[1] = get_elf_backend_data (abfd)->elf_backend_encode_eh_address
+    (abfd, info, eh_frame_sec, 0, sec, 4, &encoded_eh_frame);
 
-      memset (contents, 0, EH_FRAME_HDR_SIZE);
-      /* Version.  */
-      contents[0] = 1;
-      /* .eh_frame offset.  */
-      contents[1] = get_elf_backend_data (abfd)->elf_backend_encode_eh_address
-	(abfd, info, eh_frame_sec, 0, sec, 4, &encoded_eh_frame);
+  if (hdr_info->u.dwarf.array
+      && hdr_info->array_count == hdr_info->u.dwarf.fde_count)
+    {
+      /* FDE count encoding.  */
+      contents[2] = DW_EH_PE_udata4;
+      /* Search table encoding.  */
+      contents[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4;
+    }
+  else
+    {
+      contents[2] = DW_EH_PE_omit;
+      contents[3] = DW_EH_PE_omit;
+    }
+  bfd_put_32 (abfd, encoded_eh_frame, contents + 4);
 
-      if (hdr_info->array && hdr_info->array_count == hdr_info->fde_count)
+  if (contents[2] != DW_EH_PE_omit)
+    {
+      unsigned int i;
+      bfd_boolean overlap, overflow;
+
+      bfd_put_32 (abfd, hdr_info->u.dwarf.fde_count,
+		  contents + EH_FRAME_HDR_SIZE);
+      qsort (hdr_info->u.dwarf.array, hdr_info->u.dwarf.fde_count,
+	     sizeof (*hdr_info->u.dwarf.array), vma_compare);
+      overlap = FALSE;
+      overflow = FALSE;
+      for (i = 0; i < hdr_info->u.dwarf.fde_count; i++)
 	{
-	  /* FDE count encoding.  */
-	  contents[2] = DW_EH_PE_udata4;
-	  /* Search table encoding.  */
-	  contents[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4;
+	  bfd_vma val;
+
+	  val = hdr_info->u.dwarf.array[i].initial_loc
+	    - sec->output_section->vma;
+	  val = ((val & 0xffffffff) ^ 0x80000000) - 0x80000000;
+	  if (elf_elfheader (abfd)->e_ident[EI_CLASS] == ELFCLASS64
+	      && (hdr_info->u.dwarf.array[i].initial_loc
+		  != sec->output_section->vma + val))
+	    overflow = TRUE;
+	  bfd_put_32 (abfd, val, contents + EH_FRAME_HDR_SIZE + i * 8 + 4);
+	  val = hdr_info->u.dwarf.array[i].fde - sec->output_section->vma;
+	  val = ((val & 0xffffffff) ^ 0x80000000) - 0x80000000;
+	  if (elf_elfheader (abfd)->e_ident[EI_CLASS] == ELFCLASS64
+	      && (hdr_info->u.dwarf.array[i].fde
+		  != sec->output_section->vma + val))
+	    overflow = TRUE;
+	  bfd_put_32 (abfd, val, contents + EH_FRAME_HDR_SIZE + i * 8 + 8);
+	  if (i != 0
+	      && (hdr_info->u.dwarf.array[i].initial_loc
+		  < (hdr_info->u.dwarf.array[i - 1].initial_loc
+		     + hdr_info->u.dwarf.array[i - 1].range)))
+	    overlap = TRUE;
 	}
-      else
+      if (overflow)
+	(*info->callbacks->einfo) (_("%P: .eh_frame_hdr entry overflow.\n"));
+      if (overlap)
+	(*info->callbacks->einfo)
+	  (_("%P: .eh_frame_hdr refers to overlapping FDEs.\n"));
+      if (overflow || overlap)
 	{
-	  contents[2] = DW_EH_PE_omit;
-	  contents[3] = DW_EH_PE_omit;
+	  bfd_set_error (bfd_error_bad_value);
+	  retval = FALSE;
 	}
-      bfd_put_32 (abfd, encoded_eh_frame, contents + 4);
+    }
 
-      if (contents[2] != DW_EH_PE_omit)
-	{
-	  unsigned int i;
+  /* FIXME: octets_per_byte.  */
+  if (!bfd_set_section_contents (abfd, sec->output_section, contents,
+				 (file_ptr) sec->output_offset,
+				 sec->size))
+    retval = FALSE;
+  free (contents);
+
+  if (hdr_info->u.dwarf.array != NULL)
+    free (hdr_info->u.dwarf.array);
+  return retval;
+}
 
-	  bfd_put_32 (abfd, hdr_info->fde_count, contents + EH_FRAME_HDR_SIZE);
-	  qsort (hdr_info->array, hdr_info->fde_count,
-		 sizeof (*hdr_info->array), vma_compare);
-	  for (i = 0; i < hdr_info->fde_count; i++)
-	    {
-	      bfd_vma val;
+/* Write out .eh_frame_hdr section.  This must be called after
+   _bfd_elf_write_section_eh_frame has been called on all input
+   .eh_frame sections.  */
 
-	      val = hdr_info->array[i].initial_loc - sec->output_section->vma;
-	      val = ((val & 0xffffffff) ^ 0x80000000) - 0x80000000;
-	      if (elf_elfheader (abfd)->e_ident[EI_CLASS] == ELFCLASS64
-		  && (hdr_info->array[i].initial_loc
-		      != sec->output_section->vma + val))
-		(*info->callbacks->einfo)
-		  (_("%X%P: .eh_frame_hdr table[%u] PC overflow.\n"), i);
-	      bfd_put_32 (abfd, val, contents + EH_FRAME_HDR_SIZE + i * 8 + 4);
-
-	      val = hdr_info->array[i].fde - sec->output_section->vma;
-	      val = ((val & 0xffffffff) ^ 0x80000000) - 0x80000000;
-	      if (elf_elfheader (abfd)->e_ident[EI_CLASS] == ELFCLASS64
-		  && (hdr_info->array[i].fde
-		      != sec->output_section->vma + val))
-		(*info->callbacks->einfo)
-		  (_("%X%P: .eh_frame_hdr table[%u] FDE overflow.\n"), i);
-	      bfd_put_32 (abfd, val, contents + EH_FRAME_HDR_SIZE + i * 8 + 8);
+bfd_boolean
+_bfd_elf_write_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
+{
+  struct elf_link_hash_table *htab;
+  struct eh_frame_hdr_info *hdr_info;
+  asection *sec;
 
-	      if (i != 0
-		  && (hdr_info->array[i].initial_loc
-		      < (hdr_info->array[i - 1].initial_loc
-			 + hdr_info->array[i - 1].range)))
-		(*info->callbacks->einfo)
-		  (_("%X%P: .eh_frame_hdr table[%u] FDE at %V overlaps "
-		     "table[%u] FDE at %V.\n"),
-		   i - 1, hdr_info->array[i - 1].fde,
-		   i, hdr_info->array[i].fde);
-	    }
-	}
+  htab = elf_hash_table (info);
+  hdr_info = &htab->eh_info;
+  sec = hdr_info->hdr_sec;
 
-      /* FIXME: octets_per_byte.  */
-      if (!bfd_set_section_contents (abfd, sec->output_section, contents,
-				     (file_ptr) sec->output_offset,
-				     sec->size))
-	retval = FALSE;
-      free (contents);
-    }
-  if (hdr_info->array != NULL)
-    free (hdr_info->array);
-  return retval;
+  if (info->eh_frame_hdr_type == 0 || sec == NULL)
+    return TRUE;
+
+  if (info->eh_frame_hdr_type == COMPACT_EH_HDR)
+    return write_compact_eh_frame_hdr (abfd, info);
+  else
+    return write_dwarf_eh_frame_hdr (abfd, info);
 }
 
 /* Return the width of FDE addresses.  This is the default implementation.  */
diff --git a/bfd/elflink.c b/bfd/elflink.c
index 604cfb6..c900929 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -54,6 +54,47 @@ struct elf_find_verdep_info
 static bfd_boolean _bfd_elf_fix_symbol_flags
   (struct elf_link_hash_entry *, struct elf_info_failed *);
 
+asection *
+_bfd_elf_section_for_symbol (struct elf_reloc_cookie *cookie,
+			     unsigned long r_symndx,
+			     bfd_boolean discard)
+{
+  if (r_symndx >= cookie->locsymcount
+      || ELF_ST_BIND (cookie->locsyms[r_symndx].st_info) != STB_LOCAL)
+    {
+      struct elf_link_hash_entry *h;
+
+      h = cookie->sym_hashes[r_symndx - cookie->extsymoff];
+
+      while (h->root.type == bfd_link_hash_indirect
+	     || h->root.type == bfd_link_hash_warning)
+	h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+      if ((h->root.type == bfd_link_hash_defined
+	   || h->root.type == bfd_link_hash_defweak)
+	   && discarded_section (h->root.u.def.section))
+        return h->root.u.def.section;
+      else
+	return NULL;
+    }
+  else
+    {
+      /* It's not a relocation against a global symbol,
+	 but it could be a relocation against a local
+	 symbol for a discarded section.  */
+      asection *isec;
+      Elf_Internal_Sym *isym;
+
+      /* Need to: get the symbol; get the section.  */
+      isym = &cookie->locsyms[r_symndx];
+      isec = bfd_section_from_elf_index (cookie->abfd, isym->st_shndx);
+      if (isec != NULL
+	  && discard ? discarded_section (isec) : 1)
+	return isec;
+     }
+  return NULL;
+}
+
 /* Define a symbol in a dynamic linkage section.  */
 
 struct elf_link_hash_entry *
@@ -9294,6 +9335,7 @@ elf_section_ignore_discarded_relocs (asection *sec)
     {
     case SEC_INFO_TYPE_STABS:
     case SEC_INFO_TYPE_EH_FRAME:
+    case SEC_INFO_TYPE_EH_FRAME_ENTRY:
       return TRUE;
     default:
       break;
@@ -10200,6 +10242,14 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
 	      return FALSE;
 	  }
 	  break;
+	case SEC_INFO_TYPE_EH_FRAME_ENTRY:
+	  {
+	    if (! _bfd_elf_write_section_eh_frame_entry (output_bfd,
+							 flinfo->info,
+							 o, contents))
+	      return FALSE;
+	  }
+	  break;
 	default:
 	  {
 	    /* FIXME: octets_per_byte.  */
@@ -11055,6 +11105,9 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
 	return FALSE;
     }
 
+  if (!_bfd_elf_fixup_eh_frame_hdr (info))
+    return FALSE;
+
   /* Since ELF permits relocations to be against local symbols, we
      must have the local symbols available when we do the relocations.
      Since we would rather only read the local symbols once, and we
@@ -11974,6 +12027,11 @@ _bfd_elf_gc_mark (struct bfd_link_info *info,
 	}
     }
 
+  eh_frame = elf_section_eh_frame_entry (sec);
+  if (ret && eh_frame && !eh_frame->gc_mark)
+    if (!_bfd_elf_gc_mark (info, eh_frame, gc_mark_hook))
+      ret = FALSE;
+
   return ret;
 }
 
@@ -12389,6 +12447,36 @@ _bfd_elf_gc_keep (struct bfd_link_info *info)
     }
 }
 
+bfd_boolean
+bfd_elf_parse_eh_frame_entries (bfd *abfd ATTRIBUTE_UNUSED,
+				struct bfd_link_info *info)
+{
+  bfd *ibfd = info->input_bfds;
+
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+    {
+      asection *sec;
+      struct elf_reloc_cookie cookie;
+
+      if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour)
+	continue;
+
+      if (!init_reloc_cookie (&cookie, info, ibfd))
+	return FALSE;
+
+      for (sec = ibfd->sections; sec; sec = sec->next)
+	{
+	  if (CONST_STRNEQ (bfd_section_name (ibfd, sec), ".eh_frame_entry")
+	      && init_reloc_cookie_rels (&cookie, info, ibfd, sec))
+	    {
+	      _bfd_elf_parse_eh_frame_entry (info, sec, &cookie);
+	      fini_reloc_cookie_rels (&cookie, sec);
+	    }
+	}
+    }
+  return TRUE;
+}
+
 /* Do mark and sweep of unused sections.  */
 
 bfd_boolean
@@ -12412,7 +12500,9 @@ bfd_elf_gc_sections (bfd *abfd, struct bfd_link_info *info)
 
   /* Try to parse each bfd's .eh_frame section.  Point elf_eh_frame_section
      at the .eh_frame section if we can mark the FDEs individually.  */
-  for (sub = info->input_bfds; sub != NULL; sub = sub->link.next)
+  for (sub = info->input_bfds;
+       info->eh_frame_hdr_type != COMPACT_EH_HDR && sub != NULL;
+       sub = sub->link.next)
     {
       asection *sec;
       struct elf_reloc_cookie cookie;
@@ -12915,7 +13005,9 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
 	}
     }
 
-  o = bfd_get_section_by_name (output_bfd, ".eh_frame");
+  o = NULL;
+  if (info->eh_frame_hdr_type != COMPACT_EH_HDR)
+    o = bfd_get_section_by_name (output_bfd, ".eh_frame");
   if (o != NULL)
     {
       asection *i;
@@ -12963,7 +13055,10 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
 	}
     }
 
-  if (info->eh_frame_hdr
+  if (info->eh_frame_hdr_type == COMPACT_EH_HDR)
+    _bfd_elf_end_eh_frame_parsing (info);
+
+  if (info->eh_frame_hdr_type
       && !info->relocatable
       && _bfd_elf_discard_section_eh_frame_hdr (output_bfd, info))
     changed = 1;
diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
index 50ee9ad..ca66f96 100644
--- a/bfd/elfxx-mips.c
+++ b/bfd/elfxx-mips.c
@@ -36,6 +36,7 @@
 #include "elfxx-mips.h"
 #include "elf/mips.h"
 #include "elf-vxworks.h"
+#include "dwarf2.h"
 
 /* Get the ECOFF swapping routines.  */
 #include "coff/sym.h"
@@ -16109,3 +16110,16 @@ _bfd_mips_post_process_headers (bfd *abfd, struct bfd_link_info *link_info)
       || mips_elf_tdata (abfd)->abiflags.fp_abi == Val_GNU_MIPS_ABI_FP_64A)
     i_ehdrp->e_ident[EI_ABIVERSION] = 3;
 }
+
+int
+_bfd_mips_elf_compact_eh_encoding (struct bfd_link_info *link_info ATTRIBUTE_UNUSED)
+{
+  return DW_EH_PE_pcrel | DW_EH_PE_sdata4;
+}
+
+/* Return the opcode for can't unwind.  */
+int
+_bfd_mips_elf_cant_unwind_opcode (struct bfd_link_info *link_info ATTRIBUTE_UNUSED)
+{
+    return COMPACT_EH_CANT_UNWIND_OPCODE;
+}
diff --git a/bfd/elfxx-mips.h b/bfd/elfxx-mips.h
index 8151d3b..03ff4fb 100644
--- a/bfd/elfxx-mips.h
+++ b/bfd/elfxx-mips.h
@@ -168,6 +168,9 @@ extern const struct bfd_elf_special_section _bfd_mips_elf_special_sections [];
 
 extern bfd_boolean _bfd_mips_elf_common_definition (Elf_Internal_Sym *);
 
+extern int _bfd_mips_elf_compact_eh_encoding (struct bfd_link_info *);
+extern int _bfd_mips_elf_cant_unwind_opcode (struct bfd_link_info *);
+
 static inline bfd_boolean
 gprel16_reloc_p (unsigned int r_type)
 {
@@ -191,3 +194,5 @@ literal_reloc_p (int r_type)
 #define elf_backend_merge_symbol_attribute  _bfd_mips_elf_merge_symbol_attribute
 #define elf_backend_ignore_undef_symbol _bfd_mips_elf_ignore_undef_symbol
 #define elf_backend_post_process_headers _bfd_mips_post_process_headers
+#define elf_backend_compact_eh_encoding _bfd_mips_elf_compact_eh_encoding
+#define elf_backend_cant_unwind_opcode _bfd_mips_elf_cant_unwind_opcode
diff --git a/bfd/elfxx-target.h b/bfd/elfxx-target.h
index 211c0a1..3e15b48 100644
--- a/bfd/elfxx-target.h
+++ b/bfd/elfxx-target.h
@@ -669,6 +669,14 @@
 #define elf_backend_maybe_function_sym _bfd_elf_maybe_function_sym
 #endif
 
+#ifndef elf_backend_compact_eh_encoding
+#define elf_backend_compact_eh_encoding NULL
+#endif
+
+#ifndef elf_backend_cant_unwind_opcode
+#define elf_backend_cant_unwind_opcode 0
+#endif
+
 #ifndef elf_match_priority
 #define elf_match_priority \
   (ELF_ARCH == bfd_arch_unknown ? 2 : ELF_OSABI == ELFOSABI_NONE ? 1 : 0)
@@ -780,6 +788,8 @@ static struct elf_backend_data elfNN_bed =
   elf_backend_obj_attrs_section_type,
   elf_backend_obj_attrs_order,
   elf_backend_obj_attrs_handle_unknown,
+  elf_backend_compact_eh_encoding,
+  elf_backend_cant_unwind_opcode,
   elf_backend_static_tls_alignment,
   elf_backend_stack_align,
   elf_backend_collect,
diff --git a/bfd/section.c b/bfd/section.c
index 4a6f2e4..0c2b669 100644
--- a/bfd/section.c
+++ b/bfd/section.c
@@ -386,6 +386,7 @@ CODE_FRAGMENT
 .#define SEC_INFO_TYPE_EH_FRAME  3
 .#define SEC_INFO_TYPE_JUST_SYMS 4
 .#define SEC_INFO_TYPE_TARGET    5
+.#define SEC_INFO_TYPE_EH_FRAME_ENTRY 6
 .
 .  {* Nonzero if this section uses RELA relocations, rather than REL.  *}
 .  unsigned int use_rela_p:1;
diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c
index af18430..3087630 100644
--- a/gas/config/tc-mips.c
+++ b/gas/config/tc-mips.c
@@ -14838,7 +14838,8 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
 	      || fixP->fx_r_type == BFD_RELOC_MICROMIPS_SUB
 	      || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
 	      || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY
-	      || fixP->fx_r_type == BFD_RELOC_MIPS_TLS_DTPREL64);
+	      || fixP->fx_r_type == BFD_RELOC_MIPS_TLS_DTPREL64
+	      || fixP->fx_r_type == BFD_RELOC_NONE);
 
   buf = fixP->fx_frag->fr_literal + fixP->fx_where;
 
@@ -15108,6 +15109,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
         S_SET_WEAK (fixP->fx_addsy);
       break;
 
+    case BFD_RELOC_NONE:
     case BFD_RELOC_VTABLE_ENTRY:
       fixP->fx_done = 0;
       break;
@@ -16302,7 +16304,7 @@ s_ehword (int ignore ATTRIBUTE_UNUSED)
   p = frag_more (4);
   md_number_to_chars (p, 0, 4);
   fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &ex, FALSE,
-	       BFD_RELOC_MIPS_EH);
+	       BFD_RELOC_32_PCREL);
 
   demand_empty_rest_of_line ();
 }
@@ -17054,6 +17056,10 @@ mips_fix_adjustable (fixS *fixp)
   if (fixp->fx_addsy == NULL)
     return 1;
 
+  /* Allow relocs used for EH tables.  */
+  if (fixp->fx_r_type == BFD_RELOC_32_PCREL)
+    return 1;
+
   /* If symbol SYM is in a mergeable section, relocations of the form
      SYM + 0 can usually be made section-relative.  The mergeable data
      is then identified by the section offset rather than by the symbol.
@@ -19112,3 +19118,13 @@ md_mips_end (void)
 				Tag_GNU_MIPS_ABI_FP, fpabi);
     }
 }
+
+/*  Returns the relocation type required for a particular CFI encoding.  */
+
+bfd_reloc_code_real_type
+mips_cfi_reloc_for_encoding (int encoding)
+{
+  if (encoding == (DW_EH_PE_sdata4 | DW_EH_PE_pcrel))
+    return BFD_RELOC_32_PCREL;
+  else return BFD_RELOC_NONE;
+}
diff --git a/gas/config/tc-mips.h b/gas/config/tc-mips.h
index dd8350c..6cb3868 100644
--- a/gas/config/tc-mips.h
+++ b/gas/config/tc-mips.h
@@ -176,7 +176,9 @@ extern enum dwarf2_format mips_dwarf2_format (asection *);
 
 extern int mips_dwarf2_addr_size (void);
 #define DWARF2_ADDR_SIZE(bfd) mips_dwarf2_addr_size ()
-#define DWARF2_FDE_RELOC_SIZE mips_dwarf2_addr_size ()
+#define DWARF2_FDE_RELOC_SIZE (compact_eh ? 4 : mips_dwarf2_addr_size ())
+#define DWARF2_FDE_RELOC_ENCODING(enc) \
+  (enc | (compact_eh ? DW_EH_PE_pcrel : 0))
 
 #define TARGET_USE_CFIPOP 1
 
@@ -189,6 +191,15 @@ extern int tc_mips_regname_to_dw2regnum (char *regname);
 #define DWARF2_DEFAULT_RETURN_COLUMN 31
 #define DWARF2_CIE_DATA_ALIGNMENT (-4)
 
+#if defined(OBJ_ELF)
+
+#define tc_cfi_reloc_for_encoding mips_cfi_reloc_for_encoding
+extern bfd_reloc_code_real_type mips_cfi_reloc_for_encoding (int encoding);
+
+#define tc_compact_eh_opcode_stop 0x5c
+#define tc_compact_eh_opcode_pad 0x5f
+
+#endif
 #define DIFF_EXPR_OK
 /* We define DIFF_EXPR_OK because of R_MIPS_PC32, but we have no
    64-bit form for n64 CFIs.  */
diff --git a/gas/doc/as.texinfo b/gas/doc/as.texinfo
index 9668039..facd1d7 100644
--- a/gas/doc/as.texinfo
+++ b/gas/doc/as.texinfo
@@ -4599,6 +4599,9 @@ if @var{section_list} is @code{.debug_frame}, @code{.debug_frame} is emitted.
 To emit both use @code{.eh_frame, .debug_frame}.  The default if this
 directive is not used is @code{.cfi_sections .eh_frame}.
 
+On targets that support compact unwinding tables these can be generated
+by specifying @code{.eh_frame_entry} instead of @code{.eh_frame}.
+
 @section @code{.cfi_startproc [simple]}
 @cindex @code{cfi_startproc} directive
 @code{.cfi_startproc} is used at the beginning of each function that
@@ -4616,6 +4619,7 @@ unwind entry previously opened by
 @code{.cfi_startproc}, and emits it to @code{.eh_frame}.
 
 @section @code{.cfi_personality @var{encoding} [, @var{exp}]}
+@cindex @code{cfi_personality} directive
 @code{.cfi_personality} defines personality routine and its encoding.
 @var{encoding} must be a constant determining how the personality
 should be encoded.  If it is 255 (@code{DW_EH_PE_omit}), second
@@ -4626,13 +4630,46 @@ can be loaded from, not the personality routine itself.
 The default after @code{.cfi_startproc} is @code{.cfi_personality 0xff},
 no personality routine.
 
+@section @code{.cfi_personality_id @var{id}}
+@cindex @code{cfi_personality_id} directive
+@code{cfi_personality_id} defines a personality routine by its index as
+defined in a compact unwinding format.
+Only valid when generating compact EH frames (i.e.
+with @code{.cfi_sections eh_frame_entry}.
+
+@section @code{.cfi_fde_data [@var{opcode1} [, @dots{}]]}
+@cindex @code{cfi_fde_data} directive
+@code{cfi_fde_data} is used to describe the compact unwind opcodes to be
+used for the current function.  These are emitted inline in the
+@code{.eh_frame_entry} section if small enough and there is no LSDA, or
+in the @code{.gnu.extab} section otherwise.
+Only valid when generating compact EH frames (i.e.
+with @code{.cfi_sections eh_frame_entry}.
+
 @section @code{.cfi_lsda @var{encoding} [, @var{exp}]}
 @code{.cfi_lsda} defines LSDA and its encoding.
 @var{encoding} must be a constant determining how the LSDA
-should be encoded.  If it is 255 (@code{DW_EH_PE_omit}), second
-argument is not present, otherwise second argument should be a constant
+should be encoded.  If it is 255 (@code{DW_EH_PE_omit}), the second
+argument is not present, otherwise the second argument should be a constant
 or a symbol name.  The default after @code{.cfi_startproc} is @code{.cfi_lsda 0xff},
-no LSDA.
+meaning that no LSDA is present.
+
+@section @code{.cfi_inline_lsda} [@var{align}]
+@code{.cfi_inline_lsda} marks the start of a LSDA data section and
+switches to the corresponding @code{.gnu.extab} section.
+Must be preceded by a CFI block containing a @code{.cfi_lsda} directive.
+Only valid when generating compact EH frames (i.e.
+with @code{.cfi_sections eh_frame_entry}.
+
+The table header and unwinding opcodes will be generated at this point,
+so that they are immediately followed by the LSDA data.  The symbol
+referenced by the @code{.cfi_lsda} directive should still be defined
+in case a fallback FDE based encoding is used.  The LSDA data is terminated
+by a section directive.
+
+The optional @var{align} argument specifies the alignment required.
+The alignment is specified as a power of two, as with the
+@code{.p2align} directive.
 
 @section @code{.cfi_def_cfa @var{register}, @var{offset}}
 @code{.cfi_def_cfa} defines a rule for computing CFA as: @i{take
diff --git a/gas/doc/internals.texi b/gas/doc/internals.texi
index 3dc3148..7649ce5 100644
--- a/gas/doc/internals.texi
+++ b/gas/doc/internals.texi
@@ -1473,6 +1473,12 @@ completed, but before the relocations have been generated.
 If you define this macro, GAS will call it after the relocs have been
 generated.
 
+@item tc_cfi_reloc_for_encoding
+@cindex tc_cfi_reloc_for_encoding
+This macro is used to indicate whether a cfi encoding requires a relocation.
+It should return the required relocation type.  Defining this macro implies
+that Compact EH is supported.
+
 @item md_post_relax_hook
 If you define this macro, GAS will call it after relaxing and sizing the
 segments.
diff --git a/gas/dw2gencfi.c b/gas/dw2gencfi.c
index 6a80d0b..c98767a 100644
--- a/gas/dw2gencfi.c
+++ b/gas/dw2gencfi.c
@@ -75,6 +75,8 @@
 # define tc_cfi_endproc(fde) ((void) (fde))
 #endif
 
+#define EH_FRAME_LINKONCE (SUPPORT_FRAME_LINKONCE || compact_eh)
+
 #ifndef DWARF2_FORMAT
 #define DWARF2_FORMAT(SEC) dwarf2_format_32bit
 #endif
@@ -83,7 +85,7 @@
 #define DWARF2_ADDR_SIZE(bfd) (bfd_arch_bits_per_address (bfd) / 8)
 #endif
 
-#if SUPPORT_FRAME_LINKONCE
+#if MULTIPLE_FRAME_SECTIONS
 #define CUR_SEG(structp) structp->cur_seg
 #define SET_CUR_SEG(structp, seg) structp->cur_seg = seg
 #define HANDLED(structp) structp->handled
@@ -95,6 +97,10 @@
 #define SET_HANDLED(structp, val) (void) (0 && val)
 #endif
 
+#ifndef tc_cfi_reloc_for_encoding
+#define tc_cfi_reloc_for_encoding(e) BFD_RELOC_NONE
+#endif
+
 /* Private segment collection list.  */
 struct dwcfi_seg_list
 {
@@ -103,10 +109,115 @@ struct dwcfi_seg_list
   char * seg_name;
 };
 
-#define FRAME_NAME ".eh_frame"
+#ifdef SUPPORT_COMPACT_EH
+static bfd_boolean compact_eh;
+#else
+#define compact_eh 0
+#endif
 
 static struct hash_control *dwcfi_hash;
+\f
+/* Emit a single byte into the current segment.  */
+
+static inline void
+out_one (int byte)
+{
+  FRAG_APPEND_1_CHAR (byte);
+}
+
+/* Emit a two-byte word into the current segment.  */
+
+static inline void
+out_two (int data)
+{
+  md_number_to_chars (frag_more (2), data, 2);
+}
+
+/* Emit a four byte word into the current segment.  */
+
+static inline void
+out_four (int data)
+{
+  md_number_to_chars (frag_more (4), data, 4);
+}
+
+/* Emit an unsigned "little-endian base 128" number.  */
+
+static void
+out_uleb128 (addressT value)
+{
+  output_leb128 (frag_more (sizeof_leb128 (value, 0)), value, 0);
+}
+
+/* Emit an unsigned "little-endian base 128" number.  */
+
+static void
+out_sleb128 (offsetT value)
+{
+  output_leb128 (frag_more (sizeof_leb128 (value, 1)), value, 1);
+}
+
+static offsetT
+encoding_size (unsigned char encoding)
+{
+  if (encoding == DW_EH_PE_omit)
+    return 0;
+  switch (encoding & 0x7)
+    {
+    case 0:
+      return bfd_get_arch_size (stdoutput) == 64 ? 8 : 4;
+    case DW_EH_PE_udata2:
+      return 2;
+    case DW_EH_PE_udata4:
+      return 4;
+    case DW_EH_PE_udata8:
+      return 8;
+    default:
+      abort ();
+    }
+}
 
+/* Emit expression EXP in ENCODING.  If EMIT_ENCODING is true, first
+   emit a byte containing ENCODING.  */
+
+static void
+emit_expr_encoded (expressionS *exp, int encoding, bfd_boolean emit_encoding)
+{
+  offsetT size = encoding_size (encoding);
+  bfd_reloc_code_real_type code;
+
+  if (encoding == DW_EH_PE_omit)
+    return;
+
+  if (emit_encoding)
+    out_one (encoding);
+
+  code = tc_cfi_reloc_for_encoding (encoding);
+  if (code != BFD_RELOC_NONE)
+    {
+      reloc_howto_type *howto = bfd_reloc_type_lookup (stdoutput, code);
+      char *p = frag_more (size);
+      md_number_to_chars (p, 0, size);
+      fix_new (frag_now, p - frag_now->fr_literal, size, exp->X_add_symbol,
+	       exp->X_add_number, howto->pc_relative, code);
+    }
+  else if ((encoding & 0x70) == DW_EH_PE_pcrel)
+    {
+#if CFI_DIFF_EXPR_OK
+      expressionS tmp = *exp;
+      tmp.X_op = O_subtract;
+      tmp.X_op_symbol = symbol_temp_new_now ();
+      emit_expr (&tmp, size);
+#elif defined (tc_cfi_emit_pcrel_expr)
+      tc_cfi_emit_pcrel_expr (exp, size);
+#else
+      abort ();
+#endif
+    }
+  else
+    emit_expr (exp, size);
+}
+\f
 /* Build based on segment the derived .debug_...
    segment name containing origin segment's postfix name part.  */
 
@@ -128,7 +239,13 @@ get_debugseg_name (segT seg, const char *base_name)
       dot = strchr (name + 1, '.');
 
       if (!dollar && !dot)
-	name = "";
+	{
+	  if (!strcmp (base_name, ".eh_frame_entry")
+	      && strcmp (name, ".text") != 0)
+	    return concat (base_name, ".", name, NULL);
+
+	  name = "";
+	}
       else if (!dollar)
 	name = dot;
       else if (!dot)
@@ -160,6 +277,9 @@ alloc_debugseg_item (segT seg, int subseg, char *name)
 static segT
 is_now_linkonce_segment (void)
 {
+  if (compact_eh)
+    return now_seg;
+
   if ((bfd_get_section_flags (stdoutput, now_seg)
        & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD
 	  | SEC_LINK_DUPLICATES_ONE_ONLY | SEC_LINK_DUPLICATES_SAME_SIZE
@@ -272,12 +392,13 @@ struct cfi_escape_data
 struct cie_entry
 {
   struct cie_entry *next;
-#if SUPPORT_FRAME_LINKONCE
+#if MULTIPLE_FRAME_SECTIONS
   segT cur_seg;
 #endif
   symbolS *start_address;
   unsigned int return_column;
   unsigned int signal_frame;
+  unsigned char fde_encoding;
   unsigned char per_encoding;
   unsigned char lsda_encoding;
   expressionS personality;
@@ -327,6 +448,7 @@ alloc_fde_entry (void)
   fde->return_column = DWARF2_DEFAULT_RETURN_COLUMN;
   fde->per_encoding = DW_EH_PE_omit;
   fde->lsda_encoding = DW_EH_PE_omit;
+  fde->eh_header_type = EH_COMPACT_UNKNOWN;
 
   return fde;
 }
@@ -560,9 +682,12 @@ static void dot_cfi_escape (int);
 static void dot_cfi_sections (int);
 static void dot_cfi_startproc (int);
 static void dot_cfi_endproc (int);
+static void dot_cfi_fde_data (int);
 static void dot_cfi_personality (int);
+static void dot_cfi_personality_id (int);
 static void dot_cfi_lsda (int);
 static void dot_cfi_val_encoded_addr (int);
+static void dot_cfi_inline_lsda (int);
 static void dot_cfi_label (int);
 
 const pseudo_typeS cfi_pseudo_table[] =
@@ -570,6 +695,7 @@ const pseudo_typeS cfi_pseudo_table[] =
     { "cfi_sections", dot_cfi_sections, 0 },
     { "cfi_startproc", dot_cfi_startproc, 0 },
     { "cfi_endproc", dot_cfi_endproc, 0 },
+    { "cfi_fde_data", dot_cfi_fde_data, 0 },
     { "cfi_def_cfa", dot_cfi, DW_CFA_def_cfa },
     { "cfi_def_cfa_register", dot_cfi, DW_CFA_def_cfa_register },
     { "cfi_def_cfa_offset", dot_cfi, DW_CFA_def_cfa_offset },
@@ -587,8 +713,10 @@ const pseudo_typeS cfi_pseudo_table[] =
     { "cfi_escape", dot_cfi_escape, 0 },
     { "cfi_signal_frame", dot_cfi, CFI_signal_frame },
     { "cfi_personality", dot_cfi_personality, 0 },
+    { "cfi_personality_id", dot_cfi_personality_id, 0 },
     { "cfi_lsda", dot_cfi_lsda, 0 },
     { "cfi_val_encoded_addr", dot_cfi_val_encoded_addr, 0 },
+    { "cfi_inline_lsda", dot_cfi_inline_lsda, 0 },
     { "cfi_label", dot_cfi_label, 0 },
     { NULL, NULL, 0 }
   };
@@ -847,14 +975,15 @@ dot_cfi_personality (int ignored ATTRIBUTE_UNUSED)
     }
 
   if ((encoding & 0xff) != encoding
-      || ((encoding & 0x70) != 0
+      || ((((encoding & 0x70) != 0
 #if CFI_DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr
-	  && (encoding & 0x70) != DW_EH_PE_pcrel
+	   && (encoding & 0x70) != DW_EH_PE_pcrel
 #endif
 	  )
 	 /* leb128 can be handled, but does something actually need it?  */
-      || (encoding & 7) == DW_EH_PE_uleb128
-      || (encoding & 7) > DW_EH_PE_udata8)
+	   || (encoding & 7) == DW_EH_PE_uleb128
+	   || (encoding & 7) > DW_EH_PE_udata8)
+	&& tc_cfi_reloc_for_encoding (encoding) == BFD_RELOC_NONE))
     {
       as_bad (_("invalid or unsupported encoding in .cfi_personality"));
       ignore_rest_of_line ();
@@ -917,14 +1046,15 @@ dot_cfi_lsda (int ignored ATTRIBUTE_UNUSED)
     }
 
   if ((encoding & 0xff) != encoding
-      || ((encoding & 0x70) != 0
+      || ((((encoding & 0x70) != 0
 #if CFI_DIFF_LSDA_OK || defined tc_cfi_emit_pcrel_expr
-	  && (encoding & 0x70) != DW_EH_PE_pcrel
+	    && (encoding & 0x70) != DW_EH_PE_pcrel
 #endif
-	  )
+	   )
 	 /* leb128 can be handled, but does something actually need it?  */
-      || (encoding & 7) == DW_EH_PE_uleb128
-      || (encoding & 7) > DW_EH_PE_udata8)
+	   || (encoding & 7) == DW_EH_PE_uleb128
+	   || (encoding & 7) > DW_EH_PE_udata8)
+	  && tc_cfi_reloc_for_encoding (encoding) == BFD_RELOC_NONE))
     {
       as_bad (_("invalid or unsupported encoding in .cfi_lsda"));
       ignore_rest_of_line ();
@@ -1051,10 +1181,14 @@ dot_cfi_label (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)
+#define CFI_EMIT_eh_frame		(1 << 0)
+#define CFI_EMIT_debug_frame		(1 << 1)
+#define CFI_EMIT_target			(1 << 2)
+#define CFI_EMIT_eh_frame_compact	(1 << 3)
+static bfd_boolean cfi_sections_set = FALSE;
 static int cfi_sections = CFI_EMIT_eh_frame;
+static int all_cfi_sections = 0;
+static struct fde_entry *last_fde;
 
 static void
 dot_cfi_sections (int ignored ATTRIBUTE_UNUSED)
@@ -1075,6 +1209,13 @@ dot_cfi_sections (int ignored ATTRIBUTE_UNUSED)
 	  sections |= CFI_EMIT_eh_frame;
 	else if (strncmp (name, ".debug_frame", sizeof ".debug_frame") == 0)
 	  sections |= CFI_EMIT_debug_frame;
+#if SUPPORT_COMPACT_EH
+	else if (strncmp (name, ".eh_frame_entry", sizeof ".eh_frame_entry") == 0)
+	  {
+	    compact_eh = TRUE;
+	    sections |= CFI_EMIT_eh_frame_compact;
+	  }
+#endif
 #ifdef tc_cfi_section_name
 	else if (strcmp (name, tc_cfi_section_name) == 0)
 	  sections |= CFI_EMIT_target;
@@ -1103,6 +1244,9 @@ dot_cfi_sections (int ignored ATTRIBUTE_UNUSED)
       }
 
   demand_empty_rest_of_line ();
+  if (cfi_sections_set && cfi_sections != sections)
+    as_bad (_("inconsistent uses of .cfi_sections"));
+  cfi_sections_set = TRUE;
   cfi_sections = sections;
 }
 
@@ -1138,6 +1282,8 @@ dot_cfi_startproc (int ignored ATTRIBUTE_UNUSED)
     }
   demand_empty_rest_of_line ();
 
+  all_cfi_sections |= cfi_sections;
+  frchain_now->frch_cfi_data->cur_fde_data->sections = cfi_sections;
   frchain_now->frch_cfi_data->cur_cfa_offset = 0;
   if (!simple)
     tc_cfi_frame_initial_instructions ();
@@ -1149,8 +1295,6 @@ dot_cfi_startproc (int ignored ATTRIBUTE_UNUSED)
 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"));
@@ -1158,58 +1302,253 @@ dot_cfi_endproc (int ignored ATTRIBUTE_UNUSED)
       return;
     }
 
-  fde = frchain_now->frch_cfi_data->cur_fde_data;
+  last_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);
+    tc_cfi_endproc (last_fde);
 }
 
-\f
-/* Emit a single byte into the current segment.  */
-
-static inline void
-out_one (int byte)
+static segT
+get_cfi_seg (segT cseg, const char *base, flagword flags, int align)
 {
-  FRAG_APPEND_1_CHAR (byte);
+  /* Exclude .debug_frame sections for Compact EH.  */
+  if (SUPPORT_FRAME_LINKONCE || ((flags & SEC_DEBUGGING) == 0 && compact_eh))
+    {
+      struct dwcfi_seg_list *l;
+
+      l = dwcfi_hash_find_or_make (cseg, base, flags);
+
+      cseg = l->seg;
+      subseg_set (cseg, l->subseg);
+    }
+  else
+    {
+      cseg = subseg_new (base, 0);
+      bfd_set_section_flags (stdoutput, cseg, flags);
+    }
+  record_alignment (cseg, align);
+  return cseg;
 }
 
-/* Emit a two-byte word into the current segment.  */
+#if SUPPORT_COMPACT_EH
+static void
+dot_cfi_personality_id (int ignored ATTRIBUTE_UNUSED)
+{
+  struct fde_entry *fde;
 
-static inline void
-out_two (int data)
+  if (frchain_now->frch_cfi_data == NULL)
+    {
+      as_bad (_("CFI instruction used without previous .cfi_startproc"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  fde = frchain_now->frch_cfi_data->cur_fde_data;
+  fde->personality_id = cfi_parse_const ();
+  demand_empty_rest_of_line ();
+
+  if (fde->personality_id == 0 || fde->personality_id > 3)
+    {
+      as_bad (_("wrong argument to .cfi_personality_id"));
+      return;
+    }
+}
+
+static void
+dot_cfi_fde_data (int ignored ATTRIBUTE_UNUSED)
 {
-  md_number_to_chars (frag_more (2), data, 2);
+  if (frchain_now->frch_cfi_data == NULL)
+    {
+      as_bad (_(".cfi_fde_data without corresponding .cfi_startproc"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  last_fde = frchain_now->frch_cfi_data->cur_fde_data;
+
+  if ((cfi_sections & CFI_EMIT_target) != 0
+      || (cfi_sections & CFI_EMIT_eh_frame_compact) != 0)
+    {
+      struct cfi_escape_data *head, **tail, *e;
+      int num_ops = 0;
+
+      tail = &head;
+      if (!is_it_end_of_statement ())
+	{
+	  num_ops = 0;
+	  do
+	    {
+	      e = (struct cfi_escape_data *) xmalloc (sizeof (*e));
+	      do_parse_cons_expression (&e->exp, 1);
+	      *tail = e;
+	      tail = &e->next;
+	      num_ops++;
+	    }
+	  while (*input_line_pointer++ == ',');
+	  --input_line_pointer;
+	}
+      *tail = NULL;
+
+      if (last_fde->lsda_encoding != DW_EH_PE_omit)
+	last_fde->eh_header_type = EH_COMPACT_HAS_LSDA;
+      else if (num_ops <= 3 && last_fde->per_encoding == DW_EH_PE_omit)
+	last_fde->eh_header_type = EH_COMPACT_INLINE;
+      else
+	last_fde->eh_header_type = EH_COMPACT_OUTLINE;
+
+      if (last_fde->eh_header_type == EH_COMPACT_INLINE)
+	num_ops = 3;
+
+      last_fde->eh_data_size = num_ops;
+      last_fde->eh_data = (bfd_byte *) xmalloc (num_ops);
+      num_ops = 0;
+      while (head)
+	{
+	  e = head;
+	  head = e->next;
+	  last_fde->eh_data[num_ops++] = e->exp.X_add_number;
+	  free (e);
+	}
+      if (last_fde->eh_header_type == EH_COMPACT_INLINE)
+	while (num_ops < 3)
+	  last_fde->eh_data[num_ops++] = tc_compact_eh_opcode_stop;
+    }
+
+  demand_empty_rest_of_line ();
 }
 
-/* Emit a four byte word into the current segment.  */
+/* Function to emit the compact unwinding opcodes stored in the
+   fde's eh_data field.  The end of the opcode data will be
+   padded to the value in align.  */
 
-static inline void
-out_four (int data)
+static void
+output_compact_unwind_data (struct fde_entry *fde, int align)
 {
-  md_number_to_chars (frag_more (4), data, 4);
+  int data_size = fde->eh_data_size + 2;
+  int align_padding;
+  int amask;
+  char *p;
+
+  fde->eh_loc = symbol_temp_new_now ();
+
+  p = frag_more (1);
+  if (fde->personality_id != 0)
+    *p = fde->personality_id;
+  else if (fde->per_encoding != DW_EH_PE_omit)
+    {
+      *p = 0;
+      emit_expr_encoded (&fde->personality, fde->per_encoding, FALSE);
+      data_size += encoding_size (fde->per_encoding);
+    }
+  else
+    *p = 1;
+
+  amask = (1 << align) - 1;
+  align_padding = ((data_size + amask) & ~amask) - data_size;
+
+  p = frag_more (fde->eh_data_size + 1 + align_padding);
+  memcpy (p, fde->eh_data, fde->eh_data_size);
+  p += fde->eh_data_size;
+
+  while (align_padding-- > 0)
+    *(p++) = tc_compact_eh_opcode_pad;
+
+  *(p++) = tc_compact_eh_opcode_stop;
+  fde->eh_header_type = EH_COMPACT_OUTLINE_DONE;
 }
 
-/* Emit an unsigned "little-endian base 128" number.  */
+/* Handle the .cfi_inline_lsda directive.  */
+static void
+dot_cfi_inline_lsda (int ignored ATTRIBUTE_UNUSED)
+{
+  segT ccseg;
+  int align;
+  long max_alignment = 28;
+
+  if (!last_fde)
+    {
+      as_bad (_("unexpected .cfi_inline_lsda"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  if ((last_fde->sections & CFI_EMIT_eh_frame_compact) == 0)
+    {
+      as_bad (_(".cfi_inline_lsda not valid for this frame"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  if (last_fde->eh_header_type != EH_COMPACT_UNKNOWN
+      && last_fde->eh_header_type != EH_COMPACT_HAS_LSDA)
+    {
+      as_bad (_(".cfi_inline_lsda seen for frame without .cfi_lsda"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+#ifdef md_flush_pending_output
+  md_flush_pending_output ();
+#endif
+
+  align = get_absolute_expression ();
+  if (align > max_alignment)
+    {
+      align = max_alignment;
+      as_bad (_("Alignment too large: %d. assumed."), align);
+    }
+  else if (align < 0)
+    {
+      as_warn (_("Alignment negative: 0 assumed."));
+      align = 0;
+    }
 
+  demand_empty_rest_of_line ();
+  ccseg = CUR_SEG (last_fde);
+
+  /* Open .gnu_extab section.  */
+  get_cfi_seg (ccseg, ".gnu_extab",
+	       (SEC_ALLOC | SEC_LOAD | SEC_DATA
+		| DWARF2_EH_FRAME_READ_ONLY),
+	       1);
+
+  frag_align (align, 0, 0);
+  record_alignment (now_seg, align);
+  if (last_fde->eh_header_type == EH_COMPACT_HAS_LSDA)
+    output_compact_unwind_data (last_fde, align);
+
+  last_fde = NULL;
+
+  return;
+}
+#else /* !SUPPORT_COMPACT_EH */
 static void
-out_uleb128 (addressT value)
+dot_cfi_inline_lsda (int ignored ATTRIBUTE_UNUSED)
 {
-  output_leb128 (frag_more (sizeof_leb128 (value, 0)), value, 0);
+  as_bad (_(".cfi_inline_lsda is not supported for this target"));
+  ignore_rest_of_line ();
 }
 
-/* Emit an unsigned "little-endian base 128" number.  */
-
 static void
-out_sleb128 (offsetT value)
+dot_cfi_fde_data (int ignored ATTRIBUTE_UNUSED)
 {
-  output_leb128 (frag_more (sizeof_leb128 (value, 1)), value, 1);
+  as_bad (_(".cfi_fde_data is not supported for this target"));
+  ignore_rest_of_line ();
 }
 
 static void
+dot_cfi_personality_id (int ignored ATTRIBUTE_UNUSED)
+{
+  as_bad (_(".cfi_personality_id is not supported for this target"));
+  ignore_rest_of_line ();
+}
+#endif
+\f
+static void
 output_cfi_insn (struct cfi_insn_data *insn)
 {
   offsetT offset;
@@ -1365,7 +1704,7 @@ output_cfi_insn (struct cfi_insn_data *insn)
     case CFI_val_encoded_addr:
       {
 	unsigned encoding = insn->u.ea.encoding;
-	offsetT encoding_size;
+	offsetT enc_size;
 
 	if (encoding == DW_EH_PE_omit)
 	  break;
@@ -1375,16 +1714,16 @@ output_cfi_insn (struct cfi_insn_data *insn)
 	switch (encoding & 0x7)
 	  {
 	  case DW_EH_PE_absptr:
-	    encoding_size = DWARF2_ADDR_SIZE (stdoutput);
+	    enc_size = DWARF2_ADDR_SIZE (stdoutput);
 	    break;
 	  case DW_EH_PE_udata2:
-	    encoding_size = 2;
+	    enc_size = 2;
 	    break;
 	  case DW_EH_PE_udata4:
-	    encoding_size = 4;
+	    enc_size = 4;
 	    break;
 	  case DW_EH_PE_udata8:
-	    encoding_size = 8;
+	    enc_size = 8;
 	    break;
 	  default:
 	    abort ();
@@ -1394,12 +1733,12 @@ output_cfi_insn (struct cfi_insn_data *insn)
 	   then use the smaller DW_OP_addr encoding.  */
 	if (insn->u.ea.encoding == DW_EH_PE_absptr)
 	  {
-	    out_uleb128 (1 + encoding_size);
+	    out_uleb128 (1 + enc_size);
 	    out_one (DW_OP_addr);
 	  }
 	else
 	  {
-	    out_uleb128 (1 + 1 + encoding_size);
+	    out_uleb128 (1 + 1 + enc_size);
 	    out_one (DW_OP_GNU_encoded_addr);
 	    out_one (encoding);
 
@@ -1409,14 +1748,14 @@ output_cfi_insn (struct cfi_insn_data *insn)
 		insn->u.ea.exp.X_op = O_subtract;
 		insn->u.ea.exp.X_op_symbol = symbol_temp_new_now ();
 #elif defined (tc_cfi_emit_pcrel_expr)
-		tc_cfi_emit_pcrel_expr (&insn->u.ea.exp, encoding_size);
+		tc_cfi_emit_pcrel_expr (&insn->u.ea.exp, enc_size);
 		break;
 #else
 		abort ();
 #endif
 	      }
 	  }
-	emit_expr (&insn->u.ea.exp, encoding_size);
+	emit_expr (&insn->u.ea.exp, enc_size);
       }
       break;
 
@@ -1429,26 +1768,6 @@ output_cfi_insn (struct cfi_insn_data *insn)
     }
 }
 
-static offsetT
-encoding_size (unsigned char encoding)
-{
-  if (encoding == DW_EH_PE_omit)
-    return 0;
-  switch (encoding & 0x7)
-    {
-    case 0:
-      return bfd_get_arch_size (stdoutput) == 64 ? 8 : 4;
-    case DW_EH_PE_udata2:
-      return 2;
-    case DW_EH_PE_udata4:
-      return 4;
-    case DW_EH_PE_udata8:
-      return 8;
-    default:
-      abort ();
-    }
-}
-
 static void
 output_cie (struct cie_entry *cie, bfd_boolean eh_frame, int align)
 {
@@ -1511,26 +1830,7 @@ output_cie (struct cie_entry *cie, bfd_boolean eh_frame, int align)
 	augmentation_size += 1 + encoding_size (cie->per_encoding);
       out_uleb128 (augmentation_size);		/* Augmentation size.  */
 
-      if (cie->per_encoding != DW_EH_PE_omit)
-	{
-	  offsetT size = encoding_size (cie->per_encoding);
-	  out_one (cie->per_encoding);
-	  exp = cie->personality;
-	  if ((cie->per_encoding & 0x70) == DW_EH_PE_pcrel)
-	    {
-#if CFI_DIFF_EXPR_OK
-	      exp.X_op = O_subtract;
-	      exp.X_op_symbol = symbol_temp_new_now ();
-	      emit_expr (&exp, size);
-#elif defined (tc_cfi_emit_pcrel_expr)
-	      tc_cfi_emit_pcrel_expr (&exp, size);
-#else
-	      abort ();
-#endif
-	    }
-	  else
-	    emit_expr (&exp, size);
-	}
+      emit_expr_encoded (&cie->personality, cie->per_encoding, TRUE);
 
       if (cie->lsda_encoding != DW_EH_PE_omit)
 	out_one (cie->lsda_encoding);
@@ -1553,6 +1853,11 @@ output_cie (struct cie_entry *cie, bfd_boolean eh_frame, int align)
 #if CFI_DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr
   enc |= DW_EH_PE_pcrel;
 #endif
+#ifdef DWARF2_FDE_RELOC_ENCODING
+  /* Allow target to override encoding.  */
+  enc = DWARF2_FDE_RELOC_ENCODING (enc);
+#endif
+  cie->fde_encoding = enc;
   if (eh_frame)
     out_one (enc);
 
@@ -1613,30 +1918,44 @@ output_fde (struct fde_entry *fde, struct cie_entry *cie,
       TC_DWARF2_EMIT_OFFSET (cie->start_address, offset_size);
     }
 
+  exp.X_op = O_symbol;
   if (eh_frame)
     {
-      exp.X_op = O_subtract;
-      exp.X_add_number = 0;
+      bfd_reloc_code_real_type code
+	= tc_cfi_reloc_for_encoding (cie->fde_encoding);
+      if (code != BFD_RELOC_NONE)
+	{
+	  reloc_howto_type *howto = bfd_reloc_type_lookup (stdoutput, code);
+	  char *p = frag_more (4);
+	  md_number_to_chars (p, 0, 4);
+	  fix_new (frag_now, p - frag_now->fr_literal, 4, fde->start_address,
+		   0, howto->pc_relative, code);
+	}
+      else
+	{
+	  exp.X_op = O_subtract;
+	  exp.X_add_number = 0;
 #if CFI_DIFF_EXPR_OK
-      exp.X_add_symbol = fde->start_address;
-      exp.X_op_symbol = symbol_temp_new_now ();
-      emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code offset.  */
+	  exp.X_add_symbol = fde->start_address;
+	  exp.X_op_symbol = symbol_temp_new_now ();
+	  emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code offset.  */
 #else
-      exp.X_op = O_symbol;
-      exp.X_add_symbol = fde->start_address;
-#ifdef tc_cfi_emit_pcrel_expr
-      tc_cfi_emit_pcrel_expr (&exp, DWARF2_FDE_RELOC_SIZE);	 /* Code offset.  */
+	  exp.X_op = O_symbol;
+	  exp.X_add_symbol = fde->start_address;
+
+#if defined(tc_cfi_emit_pcrel_expr)
+	  tc_cfi_emit_pcrel_expr (&exp, DWARF2_FDE_RELOC_SIZE);	 /* Code offset.  */
 #else
-      emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code offset.  */
+	  emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code offset.  */
 #endif
 #endif
+	}
       addr_size = DWARF2_FDE_RELOC_SIZE;
     }
   else
     {
-      exp.X_op = O_symbol;
-      exp.X_add_symbol = fde->start_address;
       exp.X_add_number = 0;
+      exp.X_add_symbol = fde->start_address;
       addr_size = DWARF2_ADDR_SIZE (stdoutput);
       emit_expr (&exp, addr_size);
     }
@@ -1651,24 +1970,7 @@ output_fde (struct fde_entry *fde, struct cie_entry *cie,
   if (eh_frame)
     out_uleb128 (augmentation_size);		/* Augmentation size.  */
 
-  if (fde->lsda_encoding != DW_EH_PE_omit)
-    {
-      exp = fde->lsda;
-      if ((fde->lsda_encoding & 0x70) == DW_EH_PE_pcrel)
-	{
-#if CFI_DIFF_LSDA_OK
-	  exp.X_op = O_subtract;
-	  exp.X_op_symbol = symbol_temp_new_now ();
-	  emit_expr (&exp, augmentation_size);
-#elif defined (tc_cfi_emit_pcrel_expr)
-	  tc_cfi_emit_pcrel_expr (&exp, augmentation_size);
-#else
-	  abort ();
-#endif
-	}
-      else
-	emit_expr (&exp, augmentation_size);
-    }
+  emit_expr_encoded (&fde->lsda, cie->lsda_encoding, FALSE);
 
   for (; first; first = first->next)
     if (CUR_SEG (first) == CUR_SEG (fde))
@@ -1859,26 +2161,50 @@ cfi_change_reg_numbers (struct cfi_insn_data *insn, segT ccseg)
 #define cfi_change_reg_numbers(insn, cseg) do { } while (0)
 #endif
 
-static segT
-get_cfi_seg (segT cseg, const char *base, flagword flags, int align)
+#if SUPPORT_COMPACT_EH
+static void
+cfi_emit_eh_header (symbolS *sym, bfd_vma addend)
 {
-  if (SUPPORT_FRAME_LINKONCE)
-    {
-      struct dwcfi_seg_list *l;
+  expressionS exp;
 
-      l = dwcfi_hash_find_or_make (cseg, base, flags);
+  exp.X_add_number = addend;
+  exp.X_add_symbol = sym;
+  emit_expr_encoded (&exp, DW_EH_PE_sdata4 | DW_EH_PE_pcrel, FALSE);
+}
 
-      cseg = l->seg;
-      subseg_set (cseg, l->subseg);
+static void
+output_eh_header (struct fde_entry *fde)
+{
+  char *p;
+  bfd_vma addend;
+
+  if (fde->eh_header_type == EH_COMPACT_INLINE)
+    addend = 0;
+  else
+    addend = 1;
+
+  cfi_emit_eh_header (fde->start_address, addend);
+
+  if (fde->eh_header_type == EH_COMPACT_INLINE)
+    {
+      p = frag_more (4);
+      /* Inline entries always use PR1.  */
+      *(p++) = 1;
+      memcpy(p, fde->eh_data, 3);
     }
   else
     {
-      cseg = subseg_new (base, 0);
-      bfd_set_section_flags (stdoutput, cseg, flags);
+      if (fde->eh_header_type == EH_COMPACT_LEGACY)
+	addend = 1;
+      else if (fde->eh_header_type == EH_COMPACT_OUTLINE
+	       || fde->eh_header_type == EH_COMPACT_OUTLINE_DONE)
+	addend = 0;
+      else
+	abort ();
+      cfi_emit_eh_header (fde->eh_loc, addend);
     }
-  record_alignment (cseg, align);
-  return cseg;
 }
+#endif
 
 void
 cfi_finish (void)
@@ -1892,13 +2218,14 @@ cfi_finish (void)
   if (all_fde_data == 0)
     return;
 
-  if ((cfi_sections & CFI_EMIT_eh_frame) != 0)
+  if ((all_cfi_sections & CFI_EMIT_eh_frame) != 0
+      || (all_cfi_sections & CFI_EMIT_eh_frame_compact) != 0)
     {
       /* Make sure check_eh_frame doesn't do anything with our output.  */
       save_flag_traditional_format = flag_traditional_format;
       flag_traditional_format = 1;
 
-      if (!SUPPORT_FRAME_LINKONCE)
+      if (!EH_FRAME_LINKONCE)
 	{
 	  /* Open .eh_frame section.  */
 	  cfi_seg = get_cfi_seg (NULL, ".eh_frame",
@@ -1926,7 +2253,22 @@ cfi_finish (void)
 
 	  for (fde = all_fde_data; fde ; fde = fde->next)
 	    {
-	      if (SUPPORT_FRAME_LINKONCE)
+	      if ((fde->sections & CFI_EMIT_eh_frame) == 0
+		  && (fde->sections & CFI_EMIT_eh_frame_compact) == 0)
+		continue;
+
+#if SUPPORT_COMPACT_EH
+	      /* Emit a LEGACY format header if we have processed all
+	         of the .cfi directives without encountering either inline or
+		 out-of-line compact unwinding opcodes.  */
+	      if (fde->eh_header_type == EH_COMPACT_HAS_LSDA
+		  || fde->eh_header_type == EH_COMPACT_UNKNOWN)
+		fde->eh_header_type = EH_COMPACT_LEGACY;
+
+	      if (fde->eh_header_type != EH_COMPACT_LEGACY)
+		continue;
+#endif
+	      if (EH_FRAME_LINKONCE)
 		{
 		  if (HANDLED (fde))
 		    continue;
@@ -1960,20 +2302,108 @@ cfi_finish (void)
 		}
 
 	      cie = select_cie_for_fde (fde, TRUE, &first, 2);
+	      fde->eh_loc = symbol_temp_new_now ();
 	      output_fde (fde, cie, TRUE, first,
 			  fde->next == NULL ? EH_FRAME_ALIGNMENT : 2);
 	    }
 	}
-      while (SUPPORT_FRAME_LINKONCE && seek_next_seg == 2);
+      while (EH_FRAME_LINKONCE && seek_next_seg == 2);
 
-      if (SUPPORT_FRAME_LINKONCE)
+      if (EH_FRAME_LINKONCE)
 	for (fde = all_fde_data; fde ; fde = fde->next)
 	  SET_HANDLED (fde, 0);
 
+#if SUPPORT_COMPACT_EH
+      if (compact_eh)
+	{
+	  /* Create remaining out of line table entries.  */
+	  do
+	    {
+	      ccseg = NULL;
+	      seek_next_seg = 0;
+
+	      for (fde = all_fde_data; fde ; fde = fde->next)
+		{
+		  if ((fde->sections & CFI_EMIT_eh_frame) == 0
+		      && (fde->sections & CFI_EMIT_eh_frame_compact) == 0)
+		    continue;
+
+		  if (fde->eh_header_type != EH_COMPACT_OUTLINE)
+		    continue;
+		  if (HANDLED (fde))
+		    continue;
+		  if (seek_next_seg && CUR_SEG (fde) != ccseg)
+		    {
+		      seek_next_seg = 2;
+		      continue;
+		    }
+		  if (!seek_next_seg)
+		    {
+		      ccseg = CUR_SEG (fde);
+		      /* Open .gnu_extab section.  */
+		      get_cfi_seg (ccseg, ".gnu_extab",
+				   (SEC_ALLOC | SEC_LOAD | SEC_DATA
+				    | DWARF2_EH_FRAME_READ_ONLY),
+				   1);
+		      seek_next_seg = 1;
+		    }
+		  SET_HANDLED (fde, 1);
+
+		  frag_align (1, 0, 0);
+		  record_alignment (now_seg, 1);
+		  output_compact_unwind_data (fde, 1);
+		}
+	    }
+	  while (EH_FRAME_LINKONCE && seek_next_seg == 2);
+
+	  for (fde = all_fde_data; fde ; fde = fde->next)
+	    SET_HANDLED (fde, 0);
+
+	  /* Create index table fragments.  */
+	  do
+	    {
+	      ccseg = NULL;
+	      seek_next_seg = 0;
+
+	      for (fde = all_fde_data; fde ; fde = fde->next)
+		{
+		  if ((fde->sections & CFI_EMIT_eh_frame) == 0
+		      && (fde->sections & CFI_EMIT_eh_frame_compact) == 0)
+		    continue;
+
+		  if (HANDLED (fde))
+		    continue;
+		  if (seek_next_seg && CUR_SEG (fde) != ccseg)
+		    {
+		      seek_next_seg = 2;
+		      continue;
+		    }
+		  if (!seek_next_seg)
+		    {
+		      ccseg = CUR_SEG (fde);
+		      /* Open .eh_frame_entry section.  */
+		      cfi_seg = get_cfi_seg (ccseg, ".eh_frame_entry",
+					     (SEC_ALLOC | SEC_LOAD | SEC_DATA
+					      | DWARF2_EH_FRAME_READ_ONLY),
+					     2);
+		      seek_next_seg = 1;
+		    }
+		  SET_HANDLED (fde, 1);
+
+		  output_eh_header (fde);
+		}
+	    }
+	  while (seek_next_seg == 2);
+
+	  for (fde = all_fde_data; fde ; fde = fde->next)
+	    SET_HANDLED (fde, 0);
+	}
+#endif /* SUPPORT_COMPACT_EH */
+
       flag_traditional_format = save_flag_traditional_format;
     }
 
-  if ((cfi_sections & CFI_EMIT_debug_frame) != 0)
+  if ((all_cfi_sections & CFI_EMIT_debug_frame) != 0)
     {
       int alignment = ffs (DWARF2_ADDR_SIZE (stdoutput)) - 1;
 
@@ -1996,6 +2426,9 @@ cfi_finish (void)
 
 	  for (fde = all_fde_data; fde ; fde = fde->next)
 	    {
+	      if ((fde->sections & CFI_EMIT_debug_frame) == 0)
+		continue;
+
 	      if (SUPPORT_FRAME_LINKONCE)
 		{
 		  if (HANDLED (fde))
@@ -2053,6 +2486,7 @@ const pseudo_typeS cfi_pseudo_table[] =
     { "cfi_sections", dot_cfi_dummy, 0 },
     { "cfi_startproc", dot_cfi_dummy, 0 },
     { "cfi_endproc", dot_cfi_dummy, 0 },
+    { "cfi_fde_data", dot_cfi_dummy, 0 },
     { "cfi_def_cfa", dot_cfi_dummy, 0 },
     { "cfi_def_cfa_register", dot_cfi_dummy, 0 },
     { "cfi_def_cfa_offset", dot_cfi_dummy, 0 },
@@ -2070,8 +2504,10 @@ const pseudo_typeS cfi_pseudo_table[] =
     { "cfi_escape", dot_cfi_dummy, 0 },
     { "cfi_signal_frame", dot_cfi_dummy, 0 },
     { "cfi_personality", dot_cfi_dummy, 0 },
+    { "cfi_personality_id", dot_cfi_dummy, 0 },
     { "cfi_lsda", dot_cfi_dummy, 0 },
     { "cfi_val_encoded_addr", dot_cfi_dummy, 0 },
+    { "cfi_inline_lsda", dot_cfi_dummy, 0 },
     { NULL, NULL, 0 }
   };
 
diff --git a/gas/dw2gencfi.h b/gas/dw2gencfi.h
index 6e2c50e..c83334a 100644
--- a/gas/dw2gencfi.h
+++ b/gas/dw2gencfi.h
@@ -58,10 +58,18 @@ extern void cfi_add_CFA_restore_state (void);
 #define SUPPORT_FRAME_LINKONCE 0
 #endif
 
+#ifdef tc_cfi_reloc_for_encoding
+#define SUPPORT_COMPACT_EH 1
+#else
+#define SUPPORT_COMPACT_EH 0
+#endif
+
+#define MULTIPLE_FRAME_SECTIONS (SUPPORT_FRAME_LINKONCE || SUPPORT_COMPACT_EH)
+
 struct cfi_insn_data
 {
   struct cfi_insn_data *next;
-#if SUPPORT_FRAME_LINKONCE
+#if MULTIPLE_FRAME_SECTIONS
   segT cur_seg;
 #endif
   int insn;
@@ -100,10 +108,35 @@ struct cfi_insn_data
   } u;
 };
 
+/* An enumeration describing the Compact EH header format.  The least
+   significant bit is used to distinguish the entries.
+
+   Inline Compact:			Function offset [0]
+					Four chars of unwind data.
+   Out-of-line Compact:			Function offset [1]
+					Compact unwind data offset [0]
+   Legacy:				Function offset [1]
+					Unwind data offset [1]
+
+   The header type is initialized to EH_COMPACT_UNKNOWN until the
+   format is discovered by encountering a .fde_data entry.
+   Failure to find a .fde_data entry will cause an EH_COMPACT_LEGACY
+   header to be generated.  */
+
+enum {
+  EH_COMPACT_UNKNOWN,
+  EH_COMPACT_LEGACY,
+  EH_COMPACT_INLINE,
+  EH_COMPACT_OUTLINE,
+  EH_COMPACT_OUTLINE_DONE,
+  /* Outline if .cfi_inline_lsda used, otherwise legacy FDE.  */
+  EH_COMPACT_HAS_LSDA
+};
+
 struct fde_entry
 {
   struct fde_entry *next;
-#if SUPPORT_FRAME_LINKONCE
+#if MULTIPLE_FRAME_SECTIONS
   segT cur_seg;
 #endif
   symbolS *start_address;
@@ -112,13 +145,21 @@ struct fde_entry
   struct cfi_insn_data **last;
   unsigned char per_encoding;
   unsigned char lsda_encoding;
+  int personality_id;
   expressionS personality;
   expressionS lsda;
   unsigned int return_column;
   unsigned int signal_frame;
-#if SUPPORT_FRAME_LINKONCE
+#if MULTIPLE_FRAME_SECTIONS
   int handled;
 #endif
+  int eh_header_type;
+  /* Compact unwinding opcodes, not including the PR byte or LSDA.  */
+  int eh_data_size;
+  bfd_byte *eh_data;
+  /* For out of line tables and FDEs.  */
+  symbolS *eh_loc;
+  int sections;
 };
 
 /* The list of all FDEs that have been collected.  */
diff --git a/gas/testsuite/gas/mips/mips.exp b/gas/testsuite/gas/mips/mips.exp
index 34414e1..7bc924c 100644
--- a/gas/testsuite/gas/mips/mips.exp
+++ b/gas/testsuite/gas/mips/mips.exp
@@ -570,6 +570,23 @@ if { [istarget mips*-*-vxworks*] } {
 	    "MIPS branch swapping ($count)"
     }
 
+    run_dump_test "compact-eh-eb-1"
+    run_dump_test "compact-eh-eb-2"
+    run_dump_test "compact-eh-eb-3"
+    run_dump_test "compact-eh-eb-4"
+    run_dump_test "compact-eh-eb-5"
+    run_dump_test "compact-eh-eb-6"
+    run_dump_test "compact-eh-eb-7"
+    run_dump_test "compact-eh-el-1"
+    run_dump_test "compact-eh-el-2"
+    run_dump_test "compact-eh-el-3"
+    run_dump_test "compact-eh-el-4"
+    run_dump_test "compact-eh-el-5"
+    run_dump_test "compact-eh-el-6"
+    run_dump_test "compact-eh-el-7"
+    run_list_test "compact-eh-err1"
+    run_list_test "compact-eh-err2"
+
     run_dump_test "div"
 
     if { !$addr32 } {
@@ -1139,7 +1156,6 @@ if { [istarget mips*-*-vxworks*] } {
     run_dump_test "align2-el"
     run_dump_test "align3"
     run_dump_test "odd-float"
-    run_dump_test "ehword"
     run_dump_test "insn-opts"
 
     run_list_test_arches "mips-macro-ill-sfp" "-32 -msingle-float" \
diff --git a/include/bfdlink.h b/include/bfdlink.h
index add4971..5118748 100644
--- a/include/bfdlink.h
+++ b/include/bfdlink.h
@@ -345,9 +345,9 @@ struct bfd_link_info
   /* TRUE if PT_GNU_RELRO segment should be created.  */
   unsigned int relro: 1;
 
-  /* TRUE if .eh_frame_hdr section and PT_GNU_EH_FRAME ELF segment
-     should be created.  */
-  unsigned int eh_frame_hdr: 1;
+  /* Nonzero if .eh_frame_hdr section and PT_GNU_EH_FRAME ELF segment
+     should be created.  1 for DWARF2 tables, 2 for compact tables.  */
+  unsigned int eh_frame_hdr_type: 2;
 
   /* TRUE if we should warn when adding a DT_TEXTREL to a shared object.  */
   unsigned int warn_shared_textrel: 1;
diff --git a/ld/emultempl/elf32.em b/ld/emultempl/elf32.em
index 34a729e..2bb9b52 100644
--- a/ld/emultempl/elf32.em
+++ b/ld/emultempl/elf32.em
@@ -1045,30 +1045,57 @@ gld${EMULATION_NAME}_after_open (void)
       return;
     }
 
-  if (link_info.eh_frame_hdr
-      && !link_info.traditional_format)
+  if (!link_info.traditional_format)
     {
       bfd *abfd, *elfbfd = NULL;
       bfd_boolean warn_eh_frame = FALSE;
       asection *s;
+      int seen_type = 0;
 
       for (abfd = link_info.input_bfds; abfd; abfd = abfd->link.next)
 	{
-	  if (bfd_count_sections (abfd) == 0)
-	    continue;
-	  if (bfd_get_flavour (abfd) == bfd_target_elf_flavour)
-	    elfbfd = abfd;
-	  if (!warn_eh_frame)
+	  int type = 0;
+	  for (s = abfd->sections; s && type < COMPACT_EH_HDR; s = s->next)
 	    {
-	      s = bfd_get_section_by_name (abfd, ".eh_frame");
-	      while (s != NULL
-		     && (s->size <= 8
-			 || bfd_is_abs_section (s->output_section)))
-		s = bfd_get_next_section_by_name (s);
-	      warn_eh_frame = s != NULL;
+	      const char *name = bfd_get_section_name (abfd, s);
+
+	      if (bfd_is_abs_section (s->output_section))
+		continue;
+	      if (CONST_STRNEQ (name, ".eh_frame_entry"))
+		type = COMPACT_EH_HDR;
+	      else if (strcmp (name, ".eh_frame") == 0 && s->size > 8)
+		type = DWARF2_EH_HDR;
 	    }
-	  if (elfbfd && warn_eh_frame)
-	    break;
+
+	  if (type != 0)
+	    {
+	      if (seen_type == 0)
+		{
+		  seen_type = type;
+		}
+	      else if (seen_type != type)
+		{
+		  einfo (_("%P%F: compact frame descriptions incompatible with"
+			 " DWARF2 .eh_frame from %B\n"),
+			 type == DWARF2_EH_HDR ? abfd : elfbfd);
+		  break;
+		}
+
+	      if (!elfbfd
+		  && (type == COMPACT_EH_HDR || link_info.eh_frame_hdr_type != 0))
+		{
+		  if (bfd_get_flavour (abfd) == bfd_target_elf_flavour)
+		    elfbfd = abfd;
+
+		  warn_eh_frame = TRUE;
+		}
+	    }
+
+	  if (seen_type == COMPACT_EH_HDR)
+	    link_info.eh_frame_hdr_type = COMPACT_EH_HDR;
+
+	  if (bfd_count_sections (abfd) == 0)
+	    continue;
 	}
       if (elfbfd)
 	{
@@ -1272,6 +1299,10 @@ fragment <<EOF
       einfo ("%P: warning: %s, needed by %B, not found (try using -rpath or -rpath-link)\n",
 	     l->name, l->by);
     }
+
+  if (link_info.eh_frame_hdr_type == COMPACT_EH_HDR)
+    if (bfd_elf_parse_eh_frame_entries (NULL, &link_info) == FALSE)
+      einfo (_("%P%F: Failed to parse EH frame entries.\n"));
 }
 
 EOF
@@ -2202,7 +2233,7 @@ fragment <<EOF
       break;
 
     case OPTION_EH_FRAME_HDR:
-      link_info.eh_frame_hdr = TRUE;
+      link_info.eh_frame_hdr_type = DWARF2_EH_HDR;
       break;
 
     case OPTION_GROUP:
diff --git a/ld/scripttempl/elf.sc b/ld/scripttempl/elf.sc
index c9c80b0..b9b1019 100644
--- a/ld/scripttempl/elf.sc
+++ b/ld/scripttempl/elf.sc
@@ -534,10 +534,11 @@ cat <<EOF
   ${CREATE_SHLIB-${SDATA2}}
   ${CREATE_SHLIB-${SBSS2}}
   ${OTHER_READONLY_SECTIONS}
-  .eh_frame_hdr : { *(.eh_frame_hdr) }
-  .eh_frame     ${RELOCATING-0} : ONLY_IF_RO { KEEP (*(.eh_frame)) }
+  .eh_frame_hdr : { *(.eh_frame_hdr) ${RELOCATING+*(.eh_frame_entry .eh_frame_entry.*)} }
+  .eh_frame     ${RELOCATING-0} : ONLY_IF_RO { KEEP (*(.eh_frame)) ${RELOCATING+*(.eh_frame.*)} }
   .gcc_except_table ${RELOCATING-0} : ONLY_IF_RO { *(.gcc_except_table
   .gcc_except_table.*) }
+  .gnu_extab ${RELOCATING-0} : ONLY_IF_RO { *(.gnu_extab*) }
   /* These sections are generated by the Sun/Oracle C++ compiler.  */
   .exception_ranges ${RELOCATING-0} : ONLY_IF_RO { *(.exception_ranges
   .exception_ranges*) }
@@ -550,7 +551,8 @@ cat <<EOF
   ${CREATE_PIE+${RELOCATING+. = ${SHLIB_DATA_ADDR-${DATA_SEGMENT_ALIGN}};}}
 
   /* Exception handling  */
-  .eh_frame     ${RELOCATING-0} : ONLY_IF_RW { KEEP (*(.eh_frame)) }
+  .eh_frame     ${RELOCATING-0} : ONLY_IF_RW { KEEP (*(.eh_frame)) ${RELOCATING+*(.eh_frame.*)} }
+  .gnu_extab    ${RELOCATING-0} : ONLY_IF_RW { *(.gnu_extab) }
   .gcc_except_table ${RELOCATING-0} : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
   .exception_ranges ${RELOCATING-0} : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) }
 
diff --git a/ld/testsuite/ld-mips-elf/mips-elf.exp b/ld/testsuite/ld-mips-elf/mips-elf.exp
index 63c55b8..7057e2f 100644
--- a/ld/testsuite/ld-mips-elf/mips-elf.exp
+++ b/ld/testsuite/ld-mips-elf/mips-elf.exp
@@ -432,6 +432,18 @@ if {$linux_gnu} {
     run_ld_link_tests $eh_frame5_test
 }
 
+if {$embedded_elf} {
+    run_dump_test "compact-eh1"
+    run_dump_test "compact-eh2"
+    run_dump_test "compact-eh3"
+}
+
+if {$linux_gnu} {
+    run_dump_test "compact-eh4"
+    run_dump_test "compact-eh5"
+    run_dump_test "compact-eh6"
+}
+
 run_dump_test "jaloverflow"
 run_dump_test "jaloverflow-2"
 run_dump_test "undefweak-overflow" [list [list as $abi_asflags(o32)] \
diff --git a/gas/testsuite/gas/mips/compact-eh-1.s b/gas/testsuite/gas/mips/compact-eh-1.s
new file mode 100644
index 0000000..9c4f8d2
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-1.s
@@ -0,0 +1,19 @@
+	.gnu_attribute 4, 1
+	.abicalls
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+$LFB0 = .
+	.cfi_startproc
+	.cfi_personality_id 0x2
+	.set	nomips16
+	.set	nomicromips
+	.ent	_Z3fooi
+	.type	_Z3fooi, @function
+_Z3fooi:
+	nop
+	.end	_Z3fooi
+	.size	_Z3fooi, .-_Z3fooi
+	.cfi_fde_data 0x4,0x40
+	.cfi_endproc
diff --git a/gas/testsuite/gas/mips/compact-eh-2.s b/gas/testsuite/gas/mips/compact-eh-2.s
new file mode 100644
index 0000000..beeebda
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-2.s
@@ -0,0 +1,28 @@
+	.gnu_attribute 4, 1
+	.abicalls
+        .hidden DW.ref.__gnu_compact_pr2
+        .weak   DW.ref.__gnu_compact_pr2
+        .section        .data.DW.ref.__gnu_compact_pr2,"awG",@progbits,DW.ref.__gnu_compact_pr2,comdat
+        .align  2
+        .type   DW.ref.__gnu_compact_pr2, @object
+        .size   DW.ref.__gnu_compact_pr2, 4
+DW.ref.__gnu_compact_pr2:
+        .word   __gnu_compact_pr2
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+$LFB0 = .
+	.cfi_startproc
+	.cfi_personality 0x1b, DW.ref.__gnu_compact_pr2
+	.set	nomips16
+	.set	nomicromips
+	.ent	_Z3fooi
+	.type	_Z3fooi, @function
+_Z3fooi:
+	nop
+	.end	_Z3fooi
+	.size	_Z3fooi, .-_Z3fooi
+	.cfi_fde_data 0x4,0x40
+	.cfi_endproc
+	.globl	__gnu_compact_pr2
diff --git a/gas/testsuite/gas/mips/compact-eh-3.s b/gas/testsuite/gas/mips/compact-eh-3.s
new file mode 100644
index 0000000..fd9def1
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-3.s
@@ -0,0 +1,19 @@
+	.gnu_attribute 4, 1
+	.abicalls
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+$LFB0 = .
+	.cfi_startproc
+	.cfi_personality_id 0x2
+	.set	nomips16
+	.set	nomicromips
+	.ent	_Z3fooi
+	.type	_Z3fooi, @function
+_Z3fooi:
+	nop
+	.end	_Z3fooi
+	.size	_Z3fooi, .-_Z3fooi
+	.cfi_fde_data 0x4,0x40,0x3,0x5
+	.cfi_endproc
diff --git a/gas/testsuite/gas/mips/compact-eh-4.s b/gas/testsuite/gas/mips/compact-eh-4.s
new file mode 100644
index 0000000..d0be1c7
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-4.s
@@ -0,0 +1,47 @@
+	.gnu_attribute 4, 1
+	.abicalls
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+$LFB0 = .
+	.cfi_startproc
+	.cfi_personality_id 0x2
+	.cfi_lsda 0x1b,$LLSDA0
+	.set	nomips16
+	.set	nomicromips
+	.ent	_Z3fooi
+	.type	_Z3fooi, @function
+_Z3fooi:
+$LEHB0 = .
+	nop
+$LEHE0 = .
+	nop
+$LEHB1 = .
+	nop
+$LEHE1 = .
+	nop
+$LEHB2 = .
+$L3:
+	nop
+$LEHE2 = .
+	.end	_Z3fooi
+	.size	_Z3fooi, .-_Z3fooi
+	.cfi_fde_data 0x4,0x40
+	.cfi_endproc
+	.cfi_inline_lsda 2
+$LLSDA0:
+	.byte	0x2
+	.uleb128 $LLSDACSE0-$LLSDACSB0
+$LLSDACSB0:
+	.uleb128 ($LEHB0-$LFB0)|1
+	.uleb128 ($LEHE0-$LEHB0)
+	.sleb128 -1
+	.uleb128 ($LEHB1-$LEHE0)|1
+	.uleb128 ($LEHE1-$LEHB1)
+	.sleb128 ($L3-($LEHE1))
+	.sleb128 (0<<2)|0
+	.uleb128 ($LEHB2-$LEHE1)|1
+	.uleb128 ($LEHE2-$LEHB2)
+	.sleb128 -1
+$LLSDACSE0:
diff --git a/gas/testsuite/gas/mips/compact-eh-5.s b/gas/testsuite/gas/mips/compact-eh-5.s
new file mode 100644
index 0000000..aa9bdda
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-5.s
@@ -0,0 +1,56 @@
+	.gnu_attribute 4, 1
+	.abicalls
+        .hidden DW.ref.__gnu_compact_pr2
+        .weak   DW.ref.__gnu_compact_pr2
+        .section        .data.DW.ref.__gnu_compact_pr2,"awG",@progbits,DW.ref.__gnu_compact_pr2,comdat
+        .align  2
+        .type   DW.ref.__gnu_compact_pr2, @object
+        .size   DW.ref.__gnu_compact_pr2, 4
+DW.ref.__gnu_compact_pr2:
+        .word   __gnu_compact_pr2
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+$LFB0 = .
+	.cfi_startproc
+	.cfi_personality 0x1b, DW.ref.__gnu_compact_pr2
+	.cfi_lsda 0x1b,$LLSDA0
+	.set	nomips16
+	.set	nomicromips
+	.ent	_Z3fooi
+	.type	_Z3fooi, @function
+_Z3fooi:
+$LEHB0 = .
+	nop
+$LEHE0 = .
+	nop
+$LEHB1 = .
+	nop
+$LEHE1 = .
+	nop
+$LEHB2 = .
+$L3:
+	nop
+$LEHE2 = .
+	.end	_Z3fooi
+	.size	_Z3fooi, .-_Z3fooi
+	.cfi_fde_data 0x4,0x40
+	.cfi_endproc
+	.globl	__gnu_compact_pr2
+	.cfi_inline_lsda 2
+$LLSDA0:
+	.byte	0x2
+	.uleb128 $LLSDACSE0-$LLSDACSB0
+$LLSDACSB0:
+	.uleb128 ($LEHB0-$LFB0)|1
+	.uleb128 ($LEHE0-$LEHB0)
+	.sleb128 -1
+	.uleb128 ($LEHB1-$LEHE0)|1
+	.uleb128 ($LEHE1-$LEHB1)
+	.sleb128 ($L3-($LEHE1))
+	.sleb128 (0<<2)|0
+	.uleb128 ($LEHB2-$LEHE1)|1
+	.uleb128 ($LEHE2-$LEHB2)
+	.sleb128 -1
+$LLSDACSE0:
diff --git a/gas/testsuite/gas/mips/compact-eh-6.s b/gas/testsuite/gas/mips/compact-eh-6.s
new file mode 100644
index 0000000..2e49054
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-6.s
@@ -0,0 +1,47 @@
+	.gnu_attribute 4, 1
+	.abicalls
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+$LFB0 = .
+	.cfi_startproc
+	.cfi_personality_id 0x2
+	.cfi_lsda 0x1b,$LLSDA0
+	.set	nomips16
+	.set	nomicromips
+	.ent	_Z3fooi
+	.type	_Z3fooi, @function
+_Z3fooi:
+$LEHB0 = .
+	nop
+$LEHE0 = .
+	nop
+$LEHB1 = .
+	nop
+$LEHE1 = .
+	nop
+$LEHB2 = .
+$L3:
+	nop
+$LEHE2 = .
+	.end	_Z3fooi
+	.size	_Z3fooi, .-_Z3fooi
+	.cfi_fde_data 0x4,0x40,0x3,0x5
+	.cfi_endproc
+	.cfi_inline_lsda 2
+$LLSDA0:
+	.byte	0x2
+	.uleb128 $LLSDACSE0-$LLSDACSB0
+$LLSDACSB0:
+	.uleb128 ($LEHB0-$LFB0)|1
+	.uleb128 ($LEHE0-$LEHB0)
+	.sleb128 -1
+	.uleb128 ($LEHB1-$LEHE0)|1
+	.uleb128 ($LEHE1-$LEHB1)
+	.sleb128 ($L3-($LEHE1))
+	.sleb128 (0<<2)|0
+	.uleb128 ($LEHB2-$LEHE1)|1
+	.uleb128 ($LEHE2-$LEHB2)
+	.sleb128 -1
+$LLSDACSE0:
diff --git a/gas/testsuite/gas/mips/compact-eh-7.s b/gas/testsuite/gas/mips/compact-eh-7.s
new file mode 100644
index 0000000..e7554d2
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-7.s
@@ -0,0 +1,22 @@
+	.gnu_attribute 4, 1
+	.abicalls
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+$LFB0 = .
+	.cfi_startproc
+	.cfi_personality_id 0x2
+	.set	nomips16
+	.set	nomicromips
+	.ent	_Z3fooi
+	.type	_Z3fooi, @function
+_Z3fooi:
+	nop
+	.cfi_def_cfa_offset -32
+	nop
+	.cfi_def_cfa_offset 0
+	
+	.end	_Z3fooi
+	.size	_Z3fooi, .-_Z3fooi
+	.cfi_endproc
diff --git a/gas/testsuite/gas/mips/compact-eh-eb-1.d b/gas/testsuite/gas/mips/compact-eh-eb-1.d
new file mode 100644
index 0000000..249e4f2
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-eb-1.d
@@ -0,0 +1,32 @@
+#objdump: -sr
+#name: Compact EH EB #1 with personality ID and FDE data
+#source: compact-eh-1.s
+#as: -EB
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+
+
+Contents of section .text:
+ 0000 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .eh_frame_entry:
+ 0000 00000000 0104405c                    .*
+Contents of section .gnu.attributes:
+ 0000 41000000 0f676e75 00010000 00070401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-eb-2.d b/gas/testsuite/gas/mips/compact-eh-eb-2.d
new file mode 100644
index 0000000..82b49e8
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-eb-2.d
@@ -0,0 +1,49 @@
+#objdump: -sr
+#name: Compact EH EB #2 with personality routine and FDE data
+#source: compact-eh-2.s
+#as: -EB
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.data.DW.ref.__gnu_compact_pr2\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         __gnu_compact_pr2
+
+
+RELOCATION RECORDS FOR \[.gnu_extab\]:
+OFFSET   TYPE              VALUE 
+00000001 R_MIPS_PC32       DW.ref.__gnu_compact_pr2
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .group:
+ 0000 00000001 00000009                   .*
+Contents of section .text:
+ 0000 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .data.DW.ref.__gnu_compact_pr2:
+ 0000 00000000                             .*
+Contents of section .gnu_extab:
+ 0000 00000000 0004405c                    .*
+Contents of section .eh_frame_entry:
+ 0000 00000001 00000000                    .*
+Contents of section .gnu.attributes:
+ 0000 41000000 0f676e75 00010000 00070401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-eb-3.d b/gas/testsuite/gas/mips/compact-eh-eb-3.d
new file mode 100644
index 0000000..21c8cab
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-eb-3.d
@@ -0,0 +1,35 @@
+#objdump: -sr
+#name: Compact EH EB #3 with personality id and large FDE data
+#source: compact-eh-3.s
+#as: -EB
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .text:
+ 0000 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .gnu_extab:
+ 0000 02044003 055c                        .*
+Contents of section .eh_frame_entry:
+ 0000 00000001 00000000                    .*
+Contents of section .gnu.attributes:
+ 0000 41000000 0f676e75 00010000 00070401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-eb-4.d b/gas/testsuite/gas/mips/compact-eh-eb-4.d
new file mode 100644
index 0000000..d7b9320
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-eb-4.d
@@ -0,0 +1,36 @@
+#objdump: -sr
+#name: Compact EH EB #4 with personality id, FDE data and LSDA
+#source: compact-eh-4.s
+#as: -EB
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .text:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .gnu_extab:
+ 0000 0204405c 020a0104 7f050404 0005047f  .*
+Contents of section .eh_frame_entry:
+ 0000 00000001 00000000                    .*
+Contents of section .gnu.attributes:
+ 0000 41000000 0f676e75 00010000 00070401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-eb-5.d b/gas/testsuite/gas/mips/compact-eh-eb-5.d
new file mode 100644
index 0000000..fbdd199
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-eb-5.d
@@ -0,0 +1,51 @@
+#objdump: -sr
+#name: Compact EH EB #5 with personality routine, FDE data and LSDA
+#source: compact-eh-5.s
+#as: -EB
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.data.DW.ref.__gnu_compact_pr2\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         __gnu_compact_pr2
+
+
+RELOCATION RECORDS FOR \[.gnu_extab\]:
+OFFSET   TYPE              VALUE 
+00000001 R_MIPS_PC32       DW.ref.__gnu_compact_pr2
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .group:
+ 0000 00000001 00000009                    .*
+Contents of section .text:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .data.DW.ref.__gnu_compact_pr2:
+ 0000 00000000                             .*
+Contents of section .gnu_extab:
+ 0000 00000000 0004405c 020a0104 7f050404  .*
+ 0010 0005047f                             .*
+Contents of section .eh_frame_entry:
+ 0000 00000001 00000000                    .*
+Contents of section .gnu.attributes:
+ 0000 41000000 0f676e75 00010000 00070401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-eb-6.d b/gas/testsuite/gas/mips/compact-eh-eb-6.d
new file mode 100644
index 0000000..4971ddf
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-eb-6.d
@@ -0,0 +1,37 @@
+#objdump: -sr
+#name: Compact EH EB #6 with personality id, LSDA and large FDE data
+#source: compact-eh-6.s
+#as: -EB
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .text:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .gnu_extab:
+ 0000 02044003 055f5f5c 020a0104 7f050404  .*
+ 0010 0005047f                             .*
+Contents of section .eh_frame_entry:
+ 0000 00000001 00000000                    .*
+Contents of section .gnu.attributes:
+ 0000 41000000 0f676e75 00010000 00070401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-eb-7.d b/gas/testsuite/gas/mips/compact-eh-eb-7.d
new file mode 100644
index 0000000..e5e5cdd
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-eb-7.d
@@ -0,0 +1,42 @@
+#objdump: -sr
+#name: Compact EH EB #7 with personality id and fallback FDE
+#source: compact-eh-7.s
+#as: -EB
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame\]:
+OFFSET   TYPE              VALUE 
+0000001c R_MIPS_PC32       .text.*
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .eh_frame.*
+
+
+Contents of section .text:
+ 0000 00000000 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .eh_frame:
+ 0000 00000010 00000000 017a5200 017c1f01  .*
+ 0010 1b0d1d00 00000014 00000018 00000000  .*
+ 0020 00000008 00441308 440e0000           .*
+Contents of section .eh_frame_entry:
+ 0000 00000001 00000015                    .*
+Contents of section .gnu.attributes:
+ 0000 41000000 0f676e75 00010000 00070401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-el-1.d b/gas/testsuite/gas/mips/compact-eh-el-1.d
new file mode 100644
index 0000000..2671d42
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-el-1.d
@@ -0,0 +1,32 @@
+#objdump: -sr
+#name: Compact EH EL #1 with personality ID and FDE data
+#source: compact-eh-1.s
+#as: -EL
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+
+
+Contents of section .text:
+ 0000 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .eh_frame_entry:
+ 0000 00000000 0104405c                    .*
+Contents of section .gnu.attributes:
+ 0000 410f0000 00676e75 00010700 00000401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-el-2.d b/gas/testsuite/gas/mips/compact-eh-el-2.d
new file mode 100644
index 0000000..190e516
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-el-2.d
@@ -0,0 +1,49 @@
+#objdump: -sr
+#name: Compact EH EL #2 with personality routine and FDE data
+#source: compact-eh-2.s
+#as: -EL
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.data.DW.ref.__gnu_compact_pr2\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         __gnu_compact_pr2
+
+
+RELOCATION RECORDS FOR \[.gnu_extab\]:
+OFFSET   TYPE              VALUE 
+00000001 R_MIPS_PC32       DW.ref.__gnu_compact_pr2
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .group:
+ 0000 01000000 09000000                    .*
+Contents of section .text:
+ 0000 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .data.DW.ref.__gnu_compact_pr2:
+ 0000 00000000                             .*
+Contents of section .gnu_extab:
+ 0000 00000000 0004405c                    .*
+Contents of section .eh_frame_entry:
+ 0000 01000000 00000000                    .*
+Contents of section .gnu.attributes:
+ 0000 410f0000 00676e75 00010700 00000401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-el-3.d b/gas/testsuite/gas/mips/compact-eh-el-3.d
new file mode 100644
index 0000000..f2e98a0
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-el-3.d
@@ -0,0 +1,35 @@
+#objdump: -sr
+#name: Compact EH EL #3 with personality id and large FDE data
+#source: compact-eh-3.s
+#as: -EL
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .text:
+ 0000 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .gnu_extab:
+ 0000 02044003 055c                        .*
+Contents of section .eh_frame_entry:
+ 0000 01000000 00000000                    .*
+Contents of section .gnu.attributes:
+ 0000 410f0000 00676e75 00010700 00000401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-el-4.d b/gas/testsuite/gas/mips/compact-eh-el-4.d
new file mode 100644
index 0000000..eca5f66
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-el-4.d
@@ -0,0 +1,36 @@
+#objdump: -sr
+#name: Compact EH EL #4 with personality id, FDE data and LSDA
+#source: compact-eh-4.s
+#as: -EL
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .text:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .gnu_extab:
+ 0000 0204405c 020a0104 7f050404 0005047f  .*
+Contents of section .eh_frame_entry:
+ 0000 01000000 00000000                    .*
+Contents of section .gnu.attributes:
+ 0000 410f0000 00676e75 00010700 00000401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-el-5.d b/gas/testsuite/gas/mips/compact-eh-el-5.d
new file mode 100644
index 0000000..cfbef4a
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-el-5.d
@@ -0,0 +1,51 @@
+#objdump: -sr
+#name: Compact EH EL #5 with personality routine, FDE data and LSDA
+#source: compact-eh-5.s
+#as: -EL
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.data.DW.ref.__gnu_compact_pr2\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         __gnu_compact_pr2
+
+
+RELOCATION RECORDS FOR \[.gnu_extab\]:
+OFFSET   TYPE              VALUE 
+00000001 R_MIPS_PC32       DW.ref.__gnu_compact_pr2
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .group:
+ 0000 01000000 09000000                    .*
+Contents of section .text:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .data.DW.ref.__gnu_compact_pr2:
+ 0000 00000000                             .*
+Contents of section .gnu_extab:
+ 0000 00000000 0004405c 020a0104 7f050404  .*
+ 0010 0005047f                             .*
+Contents of section .eh_frame_entry:
+ 0000 01000000 00000000                    .*
+Contents of section .gnu.attributes:
+ 0000 410f0000 00676e75 00010700 00000401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-el-6.d b/gas/testsuite/gas/mips/compact-eh-el-6.d
new file mode 100644
index 0000000..ed6614b
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-el-6.d
@@ -0,0 +1,37 @@
+#objdump: -sr
+#name: Compact EH EL #6 with personality id, LSDA and large FDE data
+#source: compact-eh-6.s
+#as: -EL
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .gnu_extab
+
+
+Contents of section .text:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .gnu_extab:
+ 0000 02044003 055f5f5c 020a0104 7f050404  .*
+ 0010 0005047f                             .*
+Contents of section .eh_frame_entry:
+ 0000 01000000 00000000                    .*
+Contents of section .gnu.attributes:
+ 0000 410f0000 00676e75 00010700 00000401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-el-7.d b/gas/testsuite/gas/mips/compact-eh-el-7.d
new file mode 100644
index 0000000..8f9c4fc
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-el-7.d
@@ -0,0 +1,42 @@
+#objdump: -sr
+#name: Compact EH EL #7 with personality id and fallback FDE
+#source: compact-eh-7.s
+#as: -EL
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.pdr\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_32         _Z3fooi
+
+
+RELOCATION RECORDS FOR \[.eh_frame\]:
+OFFSET   TYPE              VALUE 
+0000001c R_MIPS_PC32       .text.*
+
+
+RELOCATION RECORDS FOR \[.eh_frame_entry\]:
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_PC32       .text.*
+00000004 R_MIPS_PC32       .eh_frame.*
+
+
+Contents of section .text:
+ 0000 00000000 00000000.*
+Contents of section .reginfo:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000                    .*
+Contents of section .MIPS.abiflags:
+ .*
+ .*
+Contents of section .pdr:
+ 0000 00000000 00000000 00000000 00000000  .*
+ 0010 00000000 00000000 00000000 00000000  .*
+Contents of section .eh_frame:
+ 0000 10000000 00000000 017a5200 017c1f01  .*
+ 0010 1b0d1d00 14000000 18000000 00000000  .*
+ 0020 08000000 00441308 440e0000           .*
+Contents of section .eh_frame_entry:
+ 0000 01000000 15000000                    .*
+Contents of section .gnu.attributes:
+ 0000 410f0000 00676e75 00010700 00000401  .*
diff --git a/gas/testsuite/gas/mips/compact-eh-err1.l b/gas/testsuite/gas/mips/compact-eh-err1.l
new file mode 100644
index 0000000..3ee03de
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-err1.l
@@ -0,0 +1,2 @@
+.*: Assembler messages:
+.*:20: Error: .cfi_inline_lsda seen for frame without .cfi_lsda
diff --git a/gas/testsuite/gas/mips/compact-eh-err1.s b/gas/testsuite/gas/mips/compact-eh-err1.s
new file mode 100644
index 0000000..967313f
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-err1.s
@@ -0,0 +1,20 @@
+	.gnu_attribute 4, 1
+	.abicalls
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+$LFB0 = .
+	.cfi_startproc
+	.cfi_personality_id 0x2
+	.set	nomips16
+	.set	nomicromips
+	.ent	_Z3fooi
+	.type	_Z3fooi, @function
+_Z3fooi:
+	nop
+	.end	_Z3fooi
+	.size	_Z3fooi, .-_Z3fooi
+	.cfi_fde_data 0x4,0x40
+	.cfi_endproc
+	.cfi_inline_lsda 1
diff --git a/gas/testsuite/gas/mips/compact-eh-err2.l b/gas/testsuite/gas/mips/compact-eh-err2.l
new file mode 100644
index 0000000..c52976a
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-err2.l
@@ -0,0 +1,2 @@
+.*: Assembler messages:
+.*:7: Error: inconsistent uses of .cfi_sections
diff --git a/gas/testsuite/gas/mips/compact-eh-err2.s b/gas/testsuite/gas/mips/compact-eh-err2.s
new file mode 100644
index 0000000..acf83d1
--- /dev/null
+++ b/gas/testsuite/gas/mips/compact-eh-err2.s
@@ -0,0 +1,7 @@
+	.gnu_attribute 4, 1
+	.abicalls
+	.text
+	.align	2
+	.globl	_Z3fooi
+	.cfi_sections .eh_frame_entry
+	.cfi_sections .eh_frame
diff --git a/ld/testsuite/ld-mips-elf/compact-eh.ld b/ld/testsuite/ld-mips-elf/compact-eh.ld
new file mode 100644
index 0000000..e395fa6
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/compact-eh.ld
@@ -0,0 +1,15 @@
+ENTRY (__start)
+SECTIONS
+{
+  .text :
+  {
+    *(.text .text.*)
+    *(.gnu_extab .gnu_extab.*)
+  }
+  .eh_frame_hdr :
+  {
+    KEEP (*(.eh_frame_hdr))
+    *(.eh_frame_entry .eh_frame_entry.*)
+  }
+  .data : {*(.data) }
+}
diff --git a/ld/testsuite/ld-mips-elf/compact-eh1.d b/ld/testsuite/ld-mips-elf/compact-eh1.d
new file mode 100644
index 0000000..b3faafa
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/compact-eh1.d
@@ -0,0 +1,15 @@
+#name: MIPS Compact EH 1
+#source: compact-eh1.s
+#source: compact-eh1a.s
+#source: compact-eh1b.s
+#as: -EB
+#readelf: -x .eh_frame_hdr
+#ld: -EB -Tcompact-eh.ld  -e main
+#
+
+Hex dump of section \'\.eh_frame_hdr\':
+
+  0x[0-9a-f]+ 021b0000 00000005 ffffff[0-9a-f]+ ffffff[0-9a-f]+.*
+  0x[0-9a-f]+ ffffff[0-9a-f]+ ffffff[0-9a-f]+ ffffff[0-9a-f]+ 01555c5c.*
+  0x[0-9a-f]+ ffffff[0-9a-f]+ 01555c5c ffffff[0-9a-f]+ 015d5d01.*
+
diff --git a/ld/testsuite/ld-mips-elf/compact-eh1.s b/ld/testsuite/ld-mips-elf/compact-eh1.s
new file mode 100644
index 0000000..941ac8f
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/compact-eh1.s
@@ -0,0 +1,37 @@
+	.section	.text.startup,"ax",@progbits
+	.align	2
+	.cfi_sections .eh_frame_entry
+.LFB3 = .
+	.cfi_startproc
+	.cfi_personality_id 0x2
+	.cfi_lsda 0x1b,.LLSDA3
+	.global main
+main:
+.LEHB0 = .
+	jal	compact1a
+	move	$4,$2
+
+.LEHE0 = .
+.L11:
+	nop
+
+	lw	$31,28($sp)
+	nop
+
+	jal	compact1b
+	move	$4,$2
+	.cfi_fde_data 0x3,0x42
+	.cfi_endproc
+	.globl	__gnu_compact_pr2
+	.cfi_inline_lsda 2
+.LLSDA3:
+	.byte	0x2	 
+	.uleb128 .LLSDACSE3-.LLSDACSB3	 
+.LLSDACSB3:
+	 # Region 0 -- NoThrow
+	.uleb128 (.LEHB0-.LFB3)|1	 # Length
+	 # Region 1 -- Action Chain
+	.uleb128 (.LEHE0-.LEHB0)	 # Length
+	.sleb128 (.L11-(.LEHE0))	 # Landing Pad Offset
+	.sleb128 (0<<2)|0x1	 # Action/Chain Pair
+.LLSDACSE3:
diff --git a/ld/testsuite/ld-mips-elf/compact-eh1a.s b/ld/testsuite/ld-mips-elf/compact-eh1a.s
new file mode 100644
index 0000000..3f5c7ec
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/compact-eh1a.s
@@ -0,0 +1,11 @@
+	.cfi_sections .eh_frame_entry
+	.section .text.compact1a,"ax",@progbits
+	.globl compact1a
+	.cfi_startproc
+compact1a:
+	sw	$2,16($fp)
+	lw	$2,16($fp)
+	lw	$5,4($3)
+	addiu	$3,$2,1
+	.cfi_fde_data 0x5e,0x3,0x59,0xf1
+	.cfi_endproc
diff --git a/ld/testsuite/ld-mips-elf/compact-eh1b.s b/ld/testsuite/ld-mips-elf/compact-eh1b.s
new file mode 100644
index 0000000..e96289a
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/compact-eh1b.s
@@ -0,0 +1,21 @@
+	.cfi_sections .eh_frame_entry
+	.section	.text.compact1b,"ax",@progbits
+	.globl	compact1b
+	.cfi_startproc
+compact1b:
+	lw	$31,44($sp)
+	lw	$fp,40($sp)
+	addiu	$sp,$sp,48
+	j	$31
+	nop
+	.cfi_fde_data 0x55
+	.cfi_endproc
+	.globl	e22
+	.cfi_startproc
+__e22:
+	sw	$2,24($fp)
+	lw	$2,24($fp)
+	xori	$2,$2,0x1
+	andi	$2,$2,0x00ff
+	.cfi_fde_data 0x55
+	.cfi_endproc
diff --git a/ld/testsuite/ld-mips-elf/compact-eh2.d b/ld/testsuite/ld-mips-elf/compact-eh2.d
new file mode 100644
index 0000000..e9764d5
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/compact-eh2.d
@@ -0,0 +1,11 @@
+#name: MIPS Compact EH 2
+#source: compact-eh2.s
+#as: -EB
+#readelf: -x .eh_frame_hdr
+#ld: -EB -Tcompact-eh.ld  -e main
+#
+
+Hex dump of section \'\.eh_frame_hdr\':
+
+  0x[0-9a-f]+ 021b0000 00000002 ffffff[0-9a-f]+ 00000041.*
+  0x[0-9a-f]+ ffffff[0-9a-f]+ 015d5d01.*
diff --git a/ld/testsuite/ld-mips-elf/compact-eh2.s b/ld/testsuite/ld-mips-elf/compact-eh2.s
new file mode 100644
index 0000000..b899e56
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/compact-eh2.s
@@ -0,0 +1,34 @@
+	.section	.text.startup,"ax",@progbits
+	.align	2
+	.cfi_sections .eh_frame_entry
+.LFB3 = .
+	.cfi_startproc
+	.cfi_personality_id 0x2
+	.cfi_lsda 0x1b,.LLSDA3
+	.global main
+main:
+.LEHB0 = .
+	move	$4,$2
+
+.LEHE0 = .
+.L11:
+	nop
+
+	lw	$31,28($sp)
+	nop
+
+	move	$4,$2
+	.cfi_endproc
+	.globl	__gnu_compact_pr2
+	.cfi_inline_lsda 2
+.LLSDA3:
+	.byte	0x2	 
+	.uleb128 .LLSDACSE3-.LLSDACSB3	 
+.LLSDACSB3:
+	 # Region 0 -- NoThrow
+	.uleb128 (.LEHB0-.LFB3)|1	 # Length
+	 # Region 1 -- Action Chain
+	.uleb128 (.LEHE0-.LEHB0)	 # Length
+	.sleb128 (.L11-(.LEHE0))	 # Landing Pad Offset
+	.sleb128 (0<<2)|0x1	 # Action/Chain Pair
+.LLSDACSE3:
diff --git a/ld/testsuite/ld-mips-elf/compact-eh3.d b/ld/testsuite/ld-mips-elf/compact-eh3.d
new file mode 100644
index 0000000..7a0ecd6
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/compact-eh3.d
@@ -0,0 +1,13 @@
+#name: MIPS Compact EH 3
+#source: compact-eh3.s
+#source: compact-eh3a.s
+#as: -EB
+#readelf: -x .eh_frame_hdr
+#ld: -EB -Tcompact-eh.ld  -e main
+#
+
+Hex dump of section \'\.eh_frame_hdr\':
+
+  0x[0-9a-f]+ 021b0000 00000004 ffffff[0-9a-f]+ ffffff[0-9a-f][0-9a-f].*
+  0x[0-9a-f]+ ffffff[0-9a-f]+ 00000041 ffffff[0-9a-f]+ 0000004d.*
+  0x[0-9a-f]+ ffffff[0-9a-f]+ 015d5d01.*
diff --git a/ld/testsuite/ld-mips-elf/compact-eh3.s b/ld/testsuite/ld-mips-elf/compact-eh3.s
new file mode 100644
index 0000000..2bcf5ca
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/compact-eh3.s
@@ -0,0 +1,36 @@
+	.section	.text.startup,"ax",@progbits
+	.align	2
+	.cfi_sections .eh_frame_entry
+.LFB3 = .
+	.cfi_startproc
+	.cfi_personality_id 0x2
+	.cfi_lsda 0x1b,.LLSDA3
+	.global main
+main:
+.LEHB0 = .
+	move	$4,$2
+
+.LEHE0 = .
+.L11:
+	nop
+
+	lw	$31,28($sp)
+	nop
+
+	jal	compact3a
+	move	$4,$2
+	.cfi_fde_data 0x3,0x42
+	.cfi_endproc
+	.globl	__gnu_compact_pr2
+	.cfi_inline_lsda 2
+.LLSDA3:
+	.byte	0x2	 
+	.uleb128 .LLSDACSE3-.LLSDACSB3	 
+.LLSDACSB3:
+	 # Region 0 -- NoThrow
+	.uleb128 (.LEHB0-.LFB3)|1	 # Length
+	 # Region 1 -- Action Chain
+	.uleb128 (.LEHE0-.LEHB0)	 # Length
+	.sleb128 (.L11-(.LEHE0))	 # Landing Pad Offset
+	.sleb128 (0<<2)|0x1	 # Action/Chain Pair
+.LLSDACSE3:
diff --git a/ld/testsuite/ld-mips-elf/compact-eh3a.s b/ld/testsuite/ld-mips-elf/compact-eh3a.s
new file mode 100644
index 0000000..3780cf2
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/compact-eh3a.s
@@ -0,0 +1,19 @@
+	.cfi_sections .eh_frame_entry
+	.section	.text.compact3a,"ax",@progbits
+	.globl	compact3a
+	.cfi_startproc
+compact3a:
+	lw	$31,44($sp)
+	lw	$fp,40($sp)
+	addiu	$sp,$sp,48
+	j	$31
+	nop
+	.cfi_endproc
+	.globl	e22
+	.cfi_startproc
+__e22:
+	sw	$2,24($fp)
+	lw	$2,24($fp)
+	xori	$3,$4,0x1
+	andi	$5,$6,0x00ff
+	.cfi_endproc
diff --git a/ld/testsuite/ld-mips-elf/compact-eh4.d b/ld/testsuite/ld-mips-elf/compact-eh4.d
new file mode 100644
index 0000000..4608ac8
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/compact-eh4.d
@@ -0,0 +1,13 @@
+#name: MIPS Compact EH 4
+#source: compact-eh1.s
+#source: compact-eh1a.s
+#source: compact-eh1b.s
+#as: -EB
+#readelf: -x .eh_frame_hdr
+#ld: -EB -e main
+
+Hex dump of section \'\.eh_frame_hdr\':
+
+  0x[0-9a-f]+ 021b0000 00000005 ffffff[0-9a-f][0-9a-f] 00000024.*
+  0x[0-9a-f]+ ffffff[0-9a-f][0-9a-f] 00000028 ffffff[0-9a-f][0-9a-f] 01555c5c.*
+  0x[0-9a-f]+ ffffff[0-9a-f][0-9a-f] 01555c5c ffffff[0-9a-f][0-9a-f] 015d5d01.*
diff --git a/ld/testsuite/ld-mips-elf/compact-eh5.d b/ld/testsuite/ld-mips-elf/compact-eh5.d
new file mode 100644
index 0000000..7f0141f
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/compact-eh5.d
@@ -0,0 +1,10 @@
+#name: MIPS Compact EH 5
+#source: compact-eh2.s
+#as: -EB
+#readelf: -x .eh_frame_hdr
+#ld: -EB -e main
+
+Hex dump of section \'\.eh_frame_hdr\':
+
+  0x[0-9a-f]+ 021b0000 00000002 ffffff[0-9a-f]+ 00000025.*
+  0x[0-9a-f]+ ffffff[0-9a-f]+ 015d5d01.*
diff --git a/ld/testsuite/ld-mips-elf/compact-eh6.d b/ld/testsuite/ld-mips-elf/compact-eh6.d
new file mode 100644
index 0000000..e81285a
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/compact-eh6.d
@@ -0,0 +1,13 @@
+#name: MIPS Compact EH 6
+#source: compact-eh3.s
+#source: compact-eh3a.s
+#as: -EB
+#readelf: -x .eh_frame_hdr
+#ld: -EB -e main
+#
+
+Hex dump of section \'\.eh_frame_hdr\':
+
+  0x[0-9a-f]+ 021b0000 00000005 ffffff[0-9a-f]+ 00000060.*
+  0x[0-9a-f]+ ffffff[0-9a-f]+ 015d5d01 ffffff[0-9a-f]+ 00000029.*
+  0x[0-9a-f]+ ffffff[0-9a-f]+ 00000035 ffffff[0-9a-f]+ 015d5d01.*

^ permalink raw reply	[flat|nested] 31+ messages in thread

* RE: [Patch] Gas support for MIPS Compact EH
  2015-02-08 17:00           ` Moore, Catherine
@ 2015-04-16 13:28             ` Moore, Catherine
  2015-05-01 13:54               ` FW: " Moore, Catherine
  0 siblings, 1 reply; 31+ messages in thread
From: Moore, Catherine @ 2015-04-16 13:28 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: binutils

Hi Richard,
Is this patch something that you are going to be able to handle sometime soon?
If not, perhaps one of the other maintainers would be able to review this.
Thanks,
Catherine

> -----Original Message-----
> From: binutils-owner@sourceware.org [mailto:binutils-
> owner@sourceware.org] On Behalf Of Moore, Catherine
> Sent: Sunday, February 08, 2015 12:00 PM
> To: Richard Sandiford
> Cc: binutils@sourceware.org
> Subject: RE: [Patch] Gas support for MIPS Compact EH
> 
> Sorry, forgot the patch in last message.
> 
> > -----Original Message-----
> > From: Moore, Catherine
> > Sent: Sunday, February 08, 2015 11:59 AM
> > To: 'Richard Sandiford'
> > Cc: binutils@sourceware.org
> > Subject: RE: [Patch] Gas support for MIPS Compact EH
> >
> > > -----Original Message-----
> > > From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> > > Sent: Saturday, November 22, 2014 9:43 AM
> > > To: Moore, Catherine
> > > Cc: binutils@sourceware.org
> > > Subject: Re: [Patch] Gas support for MIPS Compact EH
> > >
> > > "Moore, Catherine" <Catherine_Moore@mentor.com> writes:
> > > > Hi Richard,
> > > > Please find the updated Compact EH  patch for binutils.
> > > > This will likely still be a hard patch to review, but I tried to
> > > > address the many comments that you made earlier.
> > > > The main difference is the exception handling relocation type for
> > > > the Linux toolchain.  It is no longer gprel based.
> > > > The ELF and Linux tools both use pcrel32 and the EH-specific
> > > > relocation type has been removed.
> > > > I ran into a few issues with the recent changes to eh_frame handling
> > > > for DWARF, I hopefully covered those with the Compact EH
> > > implementation.
> > > > Please let me know what you think.  I hope we are close to
> > > > converging on an implementation.
> > >
> > > OK, thanks.  I started by going through my previous comments and
> > > looking at how you addressed them in the new patch.  I think there are
> > > still some things that need to be sorted out.
> > >
> > > When doing the next patch, could you reply to the main points and say
> > > how you dealt with them?  That'd make it a lot easier to see how this is
> > evolving.
> >
> > Yes, I'll do that.  I'm sorry for the missing pieces of the last patch.  My
> rebase
> > had some unexpected results that I didn't catch before I posted.
> > Your comment that additional tests would have caught those is correct.
> I've
> > added some link tests to help out with that.  I think that extending the
> > readelf -u (dump unwind option) would be useful for additional tests, but I
> > don't want to make that additional development a requirement for
> approval
> > of this patch.  There are still one or two outstanding bits, but I'd like to
> defer
> > those until I see your comments on the current patch.
> > >
> > > >> -----Original Message-----
> > > >> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> > > >> Sent: Saturday, February 08, 2014 11:34 AM
> > > >> To: Moore, Catherine
> > > >> Cc: binutils@sourceware.org
> > > >> Subject: Re: [Patch] Gas support for MIPS Compact EH
> > > >>
> > > >> Thanks for the updates.
> > > >>
> > > >> "Moore, Catherine" <Catherine_Moore@mentor.com> writes:
> > > >> >> > @@ -4514,6 +4517,23 @@ argument is not present, otherwise
> > > >> >> > secon or a symbol name.  The default after
> > > >> >> > @code{.cfi_startproc} is @code{.cfi_lsda 0xff},  no LSDA.
> > > >> >> >
> > > >> >> > +@section @code{.cfi_inline_lsda} [@var{align}]
> > > >> >> > +@code{.cfi_inline_lsda} marks the start of a LSDA data
> > > >> >> > +section and switches to the corresponding @code{.gnu.extab}
> > section.
> > > >> >> > +It must be preceded by a CFI block containing a
> > > >> >> > +@code{.cfi_lsda} directive and is only valid when generating
> > > compact EH frames (i.e.
> > > >> >> > +with @code{.cfi_sections eh_frame_entry}.
> > > >> >> > +
> > > >> >> > +If a compact encoding is being used then the table header and
> > > >> >> > +unwinding opcodes will be generated at this point, so that
> > > >> >> > +they are immediately followed by the LSDA data.  The symbol
> > > >> >> > +referenced by the @code{.cfi_lsda} directive should still be
> > > >> >> > +defined in case a fallback FDE based encoding is used.
> > > >> >> > +
> > > >> >> > +The optional @var{align} argument specifies the alignment
> > > required.
> > > >> >> > +The alignment is specified as a power of two, as with the
> > > >> >> > +@code{.p2align} directive.
> > > >> >>
> > > >> >> Hmm, switching sections and emitting data feels very different
> > > >> >> in style from the other .cfi directives, which just annotate
> > > >> >> code without changing the flow of assembly.  I'd like to know
> > > >> >> other people's
> > > >> thoughts on this.
> > > >> >>
> > > >> >> Also, how do you terminate the LSDA?  The documentation doesn't
> > > >> >> say (but should :-)) and I couldn't see this directive in the spec
> either.
> > > >> >
> > > >> > The LSDA is terminated by a section switch.
> > > >>
> > > >> OK.  Like I say, please mention this in the documentation.
> > >
> > > The new version of the patch doesn't have the .cfi_inline_lsda
> > > documentation that the previous patch had.  Please add it back :-) But
> > > like I say above, please also document how the data is terminated.
> >
> > It's back.
> > >
> > > >> >> TBH, without tests, and without an explanation of what the code
> > > >> >> is doing, I found this patch pretty hard to review.  E.g.:
> > > >> >>
> > > >> >> > @@ -129,7 +140,12 @@ get_debugseg_name (segT seg, const
> char
> > > >> >> >        dot = strchr (name + 1, '.');
> > > >> >> >
> > > >> >> >        if (!dollar && !dot)
> > > >> >> > -	name = "";
> > > >> >> > +	{
> > > >> >> > +	  if (compact_eh && strcmp (name, ".text") != 0)
> > > >> >> > +	    return concat (base_name, ".", name, NULL);
> > > >> >> > +
> > > >> >> > +	  name = "";
> > > >> >> > +	}
> > > >> >>
> > > >> >> why is this change needed?  I.e., for a text section called
> > > >> >> something like .foobar, why does compact_eh need to put things
> > > >> >> in a section name ending in "..foobar", rather than in the main
> > > >> >> EH section?  I assume the double dots are deliberate, e.g. to
> > > >> >> avoid confusion with
> > > >> ".text.foobar"?
> > > >> >
> > > >> > I'm not sure why Paul put this is and the next hunk.  There were
> > > >> > test failures without.  I can revisit this For the next iteration
> > > >> > if necessary.
> > > >>
> > > >> Yeah, if you could that'd be great.  The code can't really go in if
> > > > there's no-
> > > >> one around who understands what it does.
> > > >>
> > > >> I assume it's just to ensure that each text section has its own
> > > >> .eh_frame_entry, but in that case I think we should check based on
> > > >> the base_name rather than compact_eh.  Or do we need the same
> > > >> treatment for .gnu_extab.  If so, why?
> > > >>
> > > >> A comment is needed at the very least.
> > >
> > > The new hunk for this is:
> > >
> > > > @@ -128,7 +241,15 @@ get_debugseg_name (segT seg, const char
> > > *base_name)
> > > >        dot = strchr (name + 1, '.');
> > > >
> > > >        if (!dollar && !dot)
> > > > -	name = "";
> > > > +	{
> > > > +	  /* Ensure that a uniquely named .eh_frame_entry section
> > > > +	     is created for each text section.  */
> > > > +	  if (compact_eh
> > > > +	       && !strcmp (base_name, ".eh_frame_entry")
> > > > +	       && strcmp (name, ".text") != 0)
> > > > +	    return concat (base_name, ".", name, NULL);
> > > > +	  name = "";
> > > > +	}
> > >
> > > I don't think we want the compact_eh test here; just the base_name
> > > check should be enough.  (Nit, sorry, but watch the indentation.
> > > One less space before "&&".)
> > >
> > Done.
> > > >> >> > @@ -833,14 +859,15 @@ dot_cfi_personality (int ignored
> ATTRIBU
> > > >> >> >      }
> > > >> >> >
> > > >> >> >    if ((encoding & 0xff) != encoding
> > > >> >> > -      || ((encoding & 0x70) != 0
> > > >> >> > +      || ((((encoding & 0x70) != 0
> > > >> >> >  #if CFI_DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr
> > > >> >> > -	  && (encoding & 0x70) != DW_EH_PE_pcrel
> > > >> >> > +	   && (encoding & 0x70) != DW_EH_PE_pcrel
> > > >> >> >  #endif
> > > >> >> >  	  )
> > > >> >> > /* leb128 can be handled, but does something actually need it?
> > > > */
> > > >> >> > -      || (encoding & 7) == DW_EH_PE_uleb128
> > > >> >> > -      || (encoding & 7) > DW_EH_PE_udata8)
> > > >> >> > +	   || (encoding & 7) == DW_EH_PE_uleb128
> > > >> >> > +	   || (encoding & 7) > DW_EH_PE_udata8)
> > > >> >> > +	&& !tc_cfi_special_encoding (encoding)))
> > > >> >> >      {
> > > >> >> >        as_bad (_("invalid or unsupported encoding in
> > .cfi_personality"));
> > > >> >> >        ignore_rest_of_line ();
> > > >> >>
> > > >> >> What does a "special" encoding mean?  Again, this hook should be
> > > >> >> documented in internals.texi.  And do we really want to change
> > > >> >> the set of encodings that are allowed for DWARF, even on MIPS
> > > >> >> systems that predate compat EH?
> > > >> >>
> > > >> > Special means that we have code in the backend to emit a reloc for
> it.
> > > >> > In the revised patch, it goes along with Tc_cfi_reloc_for_encoding.
> > > >> > It also looks like internals.texi fails to document many of the
> > > >> > tc_macros and it doesn't appear to be built into a .info fiie.
> > > >>
> > > >> That's no reason not to document new hooks though.  I know I've
> > > >> used internals.texi to read more about a hook in the past.  If you
> > > >> don't
> > > > want to put
> > > >> it there though then please at least put it in a comment instead.
> > >
> > > The hunk for this is:
> > >
> > > > diff --git a/gas/doc/internals.texi b/gas/doc/internals.texi index
> > > > cc4089b..9dd0bd0 100644
> > > > --- a/gas/doc/internals.texi
> > > > +++ b/gas/doc/internals.texi
> > > > @@ -1473,6 +1473,10 @@ completed, but before the relocations have
> > > been generated.
> > > >  If you define this macro, GAS will call it after the relocs have
> > > > been generated.
> > > >
> > > > +@item tc_cfi_reloc_for_encoding
> > > > +@cindex tc_cfi_reloc_for_encoding
> > > > +You may define this macro to indicate whether a cfi encoding
> > > > +requires a
> > > relocation.
> > >
> > > That makes it sound like a boolean, whereas it returns a reloc.
> > > Please also mention that it controls whether compact EH is supported.
> > > The 80- character limit applies to documentation too.
> > >
> >
> > Done.
> >
> > > >> >> > +  demand_empty_rest_of_line ();
> > > >> >> > +  ccseg = CUR_SEG (last_fde);
> > > >> >> > +  /* Open .gnu_extab section.  */
> > > >> >> > +  cfi_seg = get_cfi_seg (ccseg, ".gnu_extab",
> > > >> >> > +			 (SEC_ALLOC | SEC_LOAD | SEC_DATA
> > > >> >> > +			  | DWARF2_EH_FRAME_READ_ONLY),
> > > >> >> > +			 1);
> > > >> >> > +#ifdef md_fix_up_eh_frame
> > > >> >> > +  md_fix_up_eh_frame (cfi_seg); #else
> > > >> >> > +  (void) cfi_seg;
> > > >> >> > +#endif
> > > >> >> > +
> > > >> >> > +  frag_align (align, 0, 0);
> > > >> >> > +  record_alignment (now_seg, align);  if
> > > >> >> > + (last_fde->eh_header_type == EH_COMPACT_HAS_LSDA)
> > > >> >> > +    output_compact_unwind_data (last_fde, align);
> > > >> >>
> > > >> >> Please could you explain the EH_COMPACT_LEGACY handling
> here?
> > > >> >
> > > >> > Would you please clarify this question?  I don't see the
> > > >> > reference to EH_COMPACT_LEGACY.
> > > >>
> > > >> That was the problem :-)  Further up there's:
> > > >>
> > > >>   if (last_fde->eh_header_type != EH_COMPACT_LEGACY
> > > >>       && last_fde->eh_header_type != EH_COMPACT_HAS_LSDA)
> > > >>     {
> > > >>       as_bad (_(".cfi_inline_lsda seen for frame without .cfi_lsda"));
> > > >>       ignore_rest_of_line ();
> > > >>       return;
> > > >>     }
> > > >>
> > > >> So what does this code mean/do in the:
> > > >>
> > > >>   last_fde->eh_header_type == EH_COMPACT_LEGACY
> > > >>
> > > >> case?
> > >
> > > Still not sure about this -- please could you clarify?  The context is
> > > this part of
> > > dot_cfi_inline_lsda:
> > >
> > > > +  if (last_fde->eh_header_type != EH_COMPACT_LEGACY
> > > > +      && last_fde->eh_header_type != EH_COMPACT_HAS_LSDA)
> > > > +    {
> > > > +      as_bad (_(".cfi_inline_lsda seen for frame without .cfi_lsda"));
> > > > +      ignore_rest_of_line ();
> > > > +      return;
> > > > +    }
> > > > +
> > > > +#ifdef md_flush_pending_output
> > > > +  md_flush_pending_output ();
> > > > +#endif
> > > > +
> > > > +  align = get_absolute_expression ();  if (align > max_alignment)
> > > > +    {
> > > > +      align = max_alignment;
> > > > +      as_bad (_("Alignment too large: %d assumed."), align);
> > > > +    }
> > > > +  else if (align < 0)
> > > > +    {
> > > > +      as_warn (_("Alignment negative: 0 assumed."));
> > > > +      align = 0;
> > > > +    }
> > > > +
> > > > +  demand_empty_rest_of_line ();
> > > > +  ccseg = CUR_SEG (last_fde);
> > > > +  /* Open .gnu_extab section.  */
> > > > +  get_cfi_seg (ccseg, ".gnu_extab",
> > > > +	       (SEC_ALLOC | SEC_LOAD | SEC_DATA
> > > > +		| DWARF2_EH_FRAME_READ_ONLY),
> > > > +	       1);
> > > >
> > > > +  frag_align (align, 0, 0);
> > > > +  record_alignment (now_seg, align);  if (last_fde->eh_header_type
> > > > + == EH_COMPACT_HAS_LSDA)
> > > > +    output_compact_unwind_data (last_fde, align);
> > > > +
> > > > +  last_fde = NULL;
> > > > +
> > > > +  return;
> > >
> > > The first "if" statement allows the directive to be used for
> > > EH_COMPACT_LEGACY, but what does the directive mean/do in that
> case?
> > >
> >
> > I've now added some commentary and extended the definition of the
> > enumeration to include an EH_COMPACT_UNKNOWN value.
> > The eh_header_type was never explicitly initialized; it's now initialized to
> > EH_COMPACT_UNKNOWN.  The EH_COMPACT_LEGACY header type is
> then
> > explicitly set when none of the COMPACT header type cases are
> discovered.
> >
> > > >> > diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index 0aab5fa..b7d7df0
> > > >> > 100644
> > > >> > --- a/bfd/elf-bfd.h
> > > >> > +++ b/bfd/elf-bfd.h
> > > >> > @@ -381,8 +381,10 @@ struct eh_frame_hdr_info  {
> > > >> >    struct htab *cies;
> > > >> >    asection *hdr_sec;
> > > >> > -  unsigned int fde_count, array_count;
> > > >> > +  unsigned int fde_count, array_count, allocated_entries;
> > > >> >    struct eh_frame_array_ent *array;
> > > >> > +  /* eh_frame_entry fragments.  */  asection **entries;
> > > >> >    /* TRUE if we should try to merge CIEs between input sections.  */
> > > >> >    bfd_boolean merge_cies;
> > > >> >    /* TRUE if all .eh_frames have been parsd.  */
> > > >>
> > > >> I'm not sure there's enough commonality between the DWARF and
> > > compact
> > > >> versions to share this structure.  Either we should have separate
> > > >> structures or use a union to separate out the format-specific bits.
> > >
> > > I see you've done this, thanks, but:
> > >
> > > > +struct dwarf_eh_frame_hdr_info
> > > > +{
> > > > +  struct eh_frame_array_ent *array; };
> > > > +
> > > > +struct compact_eh_frame_hdr_info
> > > > +{
> > > > +  unsigned int allocated_entries;
> > > > +  /* eh_frame_entry_fragments.  */
> > > > +  asection **entries;
> > > > +};
> > > > +
> > > >  struct eh_frame_hdr_info
> > > >  {
> > > >    struct htab *cies;
> > > >    asection *hdr_sec;
> > > >    unsigned int fde_count, array_count;
> > > > -  struct eh_frame_array_ent *array;
> > > >    /* TRUE if .eh_frame_hdr should contain the sorted search table.
> > > >       We build it if we successfully read all .eh_frame input sections
> > > >       and recognize them.  */
> > > >    bfd_boolean table;
> > > > +  bfd_boolean frame_hdr_is_compact;  union
> > > > +    {
> > > > +      struct dwarf_eh_frame_hdr_info dwarf;
> > > > +      struct compact_eh_frame_hdr_info compact;
> > > > +    }
> > > > +  u;
> > > >  };
> > > >
> > > >  /* Enum used to identify target specific extensions to the
> > > > elf_obj_tdata
> > >
> > > ...aren't cies, fde_count, array_count and table specific to the DWARF
> > > version too?  My point was that if we went for the union approach, the
> > > only fields in the common structure should be those that are needed by
> > > both formats.  It looks like that's just hdr_sec.
> > >
> > > If moving all of them seems like too much work, I think we should go
> > > for separate structures instead.
> >
> > I moved them and kept the union.
> >
> > >
> > > >> > +	}
> > > >> > +
> > > >> > +      BFD_ASSERT (hdr_info->entries);
> > > >> > +    }
> > > >> > +
> > > >> > +  hdr_info->entries[hdr_info->array_count++] = sec; }
> > > >> > +
> > > >> > +/* Parse a .eh_frame_entry section.  Figure out which text section
> it
> > > >> > +   references.  */
> > > >> > +
> > > >> > +void
> > > >> > +_bfd_elf_parse_eh_frame_entry (bfd *abfd, struct bfd_link_info
> > > *info,
> > > >> > +			       asection *sec, struct elf_reloc_cookie
> > *cookie,
> > > >> > +			       bfd_boolean remember)
> > > >>
> > > >> This does more than the comment says and the name implies; the
> > > >> REMEMBER stuff isn't mentioned.
> > > >>
> > > >> The patch tries to do the parsing during bfd_elf_discard_info, but
> > > >> since the parsing wants to be able to fail with an error, I think
> > > >> we need to do it in an earlier pass.  We can then return a
> > > >> bfd_boolean success code and propagate error returns up, which the
> > > >> current patch
> > > doesn't do.
> > > >> Ideally we'd put the pass somewhere before GC, so that both the GC
> > > >> and bfd_elf_discard_info stages can assume parsed .eh_frame_entry
> > > sections.
> > > >>
> > > >> Having bfd_elf_discard_info add info (as per REMEMBER == TRUE)
> > > >> seems a bit counterintuitive.  I think the earlier pass should
> > > >> record all .eh_frame_entry sections and then the code currently in
> > > >> _bfd_elf_end_eh_frame_parsing (but see below) should remove
> > > unwanted
> > > >> entries from the eh_frame_hdr_info array.
> > >
> > > I see you've added the earlier pass, thanks, but errors aren't always
> > > reported back up.  The function:
> > >
> > > > +/* Parse a .eh_frame_entry section.  Figure out which text section it
> > > > +   references.  */
> > > > +
> > > > +void
> > > > +_bfd_elf_parse_eh_frame_entry (struct bfd_link_info *info,
> > > > +			       asection *sec, struct elf_reloc_cookie *cookie) {
> > > > [...]
> > > > +fail:
> > > > +  (*_bfd_error_handler) (_("%B: failed to process
> > > > +.eh_frame_entry"),
> > > > +sec->owner); }
> > >
> > > doesn't tell the caller (and eventually the ld code) that a problem
> occured.
> > > The function ought to return a bfd_boolean success code, like its callers.
> > > Also, the error here seems to duplicate the eventual ld one:
> > >
> > >     einfo (_("%P%F: Failed to parse EH frame entries.\n"));
> > >
> > > without really providing any more information.
> > >
> > > Don't shoot me, but it would probably be cleaner to make this an
> > > ELF-specific pass and call it from elf32.em instead.  That'll avoid
> > > having to do all the aout, bout, etc. stuff and would allow the ld
> > > code to refer to ELFisms like .eh_frame_entry itself.
> > >
> > Okay, I've now moved this to elf32.em.  I fixed the error handling as well.
> >
> > > >> > +/* Finish a pass over all .eh_frame and eh_frame_entry sections.
> > > >> > +*/
> > > >> > +
> > > >> > +bfd_boolean
> > > >> >  _bfd_elf_end_eh_frame_parsing (struct bfd_link_info *info)  {
> > > >> >    struct eh_frame_hdr_info *hdr_info;
> > > >> > +  unsigned int i;
> > > >> >
> > > >> >    hdr_info = &elf_hash_table (info)->eh_info;
> > > >> >    hdr_info->parsed_eh_frames = TRUE;
> > > >> > +
> > > >> > +  if (hdr_info->array_count == 0 || info->eh_frame_hdr < 2)
> > > >> > +    return FALSE;
> > > >> > +
> > > >> > +  qsort (hdr_info->entries, hdr_info->array_count,
> > > >> > +	 sizeof (asection *), cmp_eh_frame_hdr);
> > > >> > +
> > > >> > +  for (i = 0; i < hdr_info->array_count - 1; i++)
> > > >> > +    {
> > > >> > +      add_eh_frame_hdr_terminator (hdr_info->entries[i],
> > > >> > +				   hdr_info->entries[i + 1]);
> > > >> > +    }
> > > >> > +
> > > >> > +  /* Add a CANTUNWIND terminator after the last entry.  */
> > > >> > + add_eh_frame_hdr_terminator (hdr_info->entries[i], NULL);
> > > >> > + return TRUE;
> > > >>
> > > >> This routine is called from both bfd_elf_gc_sections and
> > > >> bfd_elf_discard_info but I think you only want it for
> > bfd_elf_discard_info.
> > > >> So perhaps this should be a separate function.
> > > >>
> > > >> I wonder whether we could instead insert CANTUNWINDs earlier (say
> > > >> in the non-dynamic part of size_dynamic_sections) based on the link
> > order.
> > > >> I.e. rather than comparing the start and end addresses of two
> > > >> sections, we could just walk the sections in the order that they're
> > > >> going to
> > > be linked.
> > > >>
> > > >> It sounds like doing it that way would be more direct and more
> efficient.
> > > >> Returning TRUE here forces the linker to map sections twice.
> > >
> > > Note that the generic version of _bfd_elf_end_eh_frame_parsing was
> > > removed in the meantime.  Your patch adds it back, but I can't see
> > > where it gets called.  Unless I'm missing something, this suggests
> > > that the tests don't exercise this code and that extra tests might be
> > needed.
> >
> > I've now added link-time tests and the call
> _bfd_elf_end_eh_frame_parsing
> > has been restored.
> > >
> > > >> > @@ -1271,6 +1465,45 @@ _bfd_elf_eh_frame_present (struct
> > > >> bfd_link_info *info)
> > > >> >    return FALSE;
> > > >> >  }
> > > >> >
> > > >> > +/* Return true if there is at least one .eh_frame_entry section in
> > > >> > +   input files.  */
> > > >> > +bfd_boolean
> > > >> > +_bfd_elf_eh_frame_entry_present (struct bfd_link_info *info) {
> > > >> > +  asection *o;
> > > >> > +  bfd *abfd;
> > > >> > +
> > > >> > +  for (abfd = info->input_bfds; abfd != NULL; abfd = abfd-
> >link_next)
> > > >> > +    {
> > > >> > +      for (o = abfd->sections; o; o = o->next)
> > > >> > +	{
> > > >> > +	  const char *name = bfd_get_section_name (abfd, o);
> > > >> > +
> > > >> > +	  if (strcmp (name, ".eh_frame_entry")
> > > >> > +	      && !bfd_is_abs_section (o->output_section))
> > > >> > +	    {
> > > >> > +	      if (strcmp (o->output_section->name, ".eh_frame_hdr"))
> > > >> > +		return TRUE;
> > > >> > +	      else
> > > >> > +		{
> > > >> > +		  (*_bfd_error_handler)
> > > >> > +			(_("error: an '.eh_frame_entry'"
> > > >> > +			   " input section that is not mapped to the"
> > > >> > +			   " '.eh_frame_hdr' output section was
> > seen"));
> > > >> > +		  (*_bfd_error_handler)
> > > >> > +			(_("note: try adding '*(.eh_frame_entry"
> > > >> > +			   " .eh_frame_entry.*)' to the
> > '.eh_frame_hdr'"
> > > >> > +			   " output section description in the linker"
> > > >> > +			   " script"));
> > > >> > +		  bfd_set_error (bfd_error_invalid_operation);
> > > >> > +		  return FALSE;
> > > >> > +		}
> > > >> > +	    }
> > > >> > +	}
> > > >> > +    }
> > > >> > +  return FALSE;
> > > >>
> > > >> The caller can't tell "FALSE because an error was reported" from
> > > >> "FALSE because there was no .eh_frame_entry".  We should separate
> > > >> out the two cases and propagate error returns up.
> > >
> > > It looks like you dealt with this by removing the error checking.
> > > I think we still want it, but it should be done separately and in a
> > > context where the error can be propagated.
> >
> > I'm deferring this for now.
> > >
> > > >> > @@ -1387,6 +1636,71 @@ _bfd_elf_eh_frame_section_offset (bfd
> > > >> *output_bfd ATTRIBUTE_UNUSED,
> > > >> >  	  + extra_augmentation_data_bytes (sec_info->entry +
> mid));  }
> > > >> >
> > > >> > +/* Write out .eh_frame_entry section.  Add CANTUNWIND
> > terminator
> > > >> > +if
> > > >> needed.
> > > >> > +   Also check that the contents look sane.  */
> > > >> > +
> > > >> > +bfd_boolean
> > > >> > +_bfd_elf_write_section_eh_frame_entry (bfd *abfd,
> > > >> > +				       asection *sec,
> > > >> > +				       bfd_byte *contents)
> > > >>
> > > >> Formatting: first two arguments fit on a line.
> > >
> > > Still present.
> > Fixed.
> > >
> > > >> > +  text_sec = (asection *) elf_section_data (sec)->sec_info;
> > > >> > +  addr = text_sec->output_section->vma + text_sec-
> >output_offset
> > > >> > +	 + text_sec->size;
> > > >> > +  addr &= ~1;
> > > >> > +  addr -= (sec->output_section->vma + sec->output_offset +
> > > >> > +sec->rawsize);
> > > >> > +  BFD_ASSERT ((addr & 1) == 0);
> > > >>
> > > >> It looks like this could trigger for odd-sized input text sections.
> > > >> I think it should be an error instead of an assert.
> > >
> > > I see you've done this with:
> > >
> > > > +  if (addr & 1)
> > > > +    {
> > > > +      (*_bfd_error_handler) (_("%B: %s invalid input section size"),
> > > > +			     sec->owner, sec->name);
> > > > +      return FALSE;
> > > > +    }
> > > > +  if (last_addr >= addr + sec->rawsize)
> > > > +    {
> > > > +      (*_bfd_error_handler) (_("%B: %s points past end of text
> section"),
> > > > +			     sec->owner, sec->name);
> > > > +      return FALSE;
> > > > +    }
> > >
> > > but I think we want "bfd_set_error (bfd_error_bad_value);" too.
> >
> > Done.
> > >
> > > >> > +  if (last_addr >= addr + sec->rawsize)
> > > >> > +    {
> > > >> > +      (*_bfd_error_handler) (_("%B: %s points past end of text
> > section"),
> > > >> > +			     sec->owner, sec->name);
> > > >> > +      return FALSE;
> > > >> > +    }
> > > >> > +
> > > >> > +  if (sec->size == sec->rawsize)
> > > >> > +    return TRUE;
> > > >> > +
> > > >> > +  BFD_ASSERT (sec->size == sec->rawsize + 8);  BFD_ASSERT ((addr
> > > >> > + &
> > > >> > + 1) == 0);
> > > >> > +  bfd_put_32 (abfd, addr, cantunwind);
> > > >> > +  bfd_put_32 (abfd, 0x015d5d01, cantunwind + 4);  return
> > > >> > + bfd_set_section_contents (abfd, sec->output_section,
> > > >> cantunwind,
> > > >> > +				   sec->output_offset + sec->rawsize,
> > 8);
> > > >>
> > > >> ARM and c6x seem to use 0 rather than 0x5d as the "no unwind"
> > > >> opcode, is that right?  If so, I think this should be a hook.
> > >
> > > It doesn't look like you addressed this part.
> >
> > It looked to me like those ports used 1?  In any case, I've added the hook,
> but
> > I haven't updated the arm and c6x ports to use it.
> > >
> > > >> The decision about whether to insert the CANTUNWIND is made
> during
> > > >> bfd_elf_discard_info, but addresses can change after that “thanks”
> > > >> to relaxation.  So this could in principle end up emitting a
> > > >> CANTUNWIND for the same address as the following text section.
> > >
> > > As above, it looks like nothing ever calls
> > > _bfd_elf_write_section_eh_frame_entry in the new version of the
> patch.
> > > If the tests don't pick that up then I think we need some more :-)
> > >
> > Sorry about this missing bit.  More tests have been added and the call to
> this
> > function has been restored.
> >
> > > >> > @@ -10039,6 +10040,10 @@ elf_link_input_bfd (struct
> > > >> > elf_final_link_info
> > > >> *flinfo, bfd *input_bfd)
> > > >> >  	      return FALSE;
> > > >> >  	  }
> > > >> >  	  break;
> > > >> > +	case SEC_INFO_TYPE_EH_FRAME_ENTRY:
> > > >> > +	    if (! _bfd_elf_write_section_eh_frame_entry (output_bfd,
> > o,
> > > >> contents))
> > > >> > +	      return FALSE;
> > > >> > +	  break;
> > > >>
> > > >> Excess indentation of the "if".
> > >
> > > Not sure: why is this no longer in the patch?
> > Restored.
> > >
> > > >> > @@ -11807,6 +11814,13 @@ _bfd_elf_gc_mark (struct bfd_link_info
> > > *info,
> > > >> >  	}
> > > >> >      }
> > > >> >
> > > >> > +  eh_frame = elf_section_eh_frame_entry (sec);  if (eh_frame &&
> > > >> > + !eh_frame->gc_mark)
> > > >> > +    {
> > > >> > +    if (!_bfd_elf_gc_mark (info, eh_frame, gc_mark_hook))
> > > >> > +      return FALSE;
> > > >> > +    }
> > > >>
> > > >> In this context we should be using "ret":
> > > >>
> > > >>   eh_frame = elf_section_eh_frame_entry (sec);
> > > >>   if (ret && eh_frame && !eh_frame->gc_mark)
> > > >>     ret = _bfd_elf_gc_mark (info, eh_frame, gc_mark_hook);
> > >
> > > You changed this to:
> > >
> > > > +  if (eh_frame && !eh_frame->gc_mark)
> > > > +    {
> > > > +      if (!_bfd_elf_gc_mark (info, eh_frame, gc_mark_hook))
> > > > +	ret = FALSE;
> > > > +    }
> > >
> > > but the point was also that we don't want to try another mark once "ret"
> > > is already FALSE.  The "ret &&" part of the condition was important.
> > Another rebase problem.  Now fixed.
> > >
> > > >> > @@ -12190,22 +12204,42 @@ bfd_elf_gc_sections (bfd *abfd, struct
> > > >> bfd_link_info *info)
> > > >> >    bed->gc_keep (info);
> > > >> >
> > > >> >    /* Try to parse each bfd's .eh_frame section.  Point
> > > >> elf_eh_frame_section
> > > >> > -     at the .eh_frame section if we can mark the FDEs individually.  */
> > > >> > +     at the .eh_frame section if we can mark the FDEs individually.
> > > >> > +     Establish links from text sections to their corresponding
> > > >> > +     .eh_frame_entry sections.  */
> > > >> >    _bfd_elf_begin_eh_frame_parsing (info);
> > > >> >    for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
> > > >> >      {
> > > >> >        asection *sec;
> > > >> >        struct elf_reloc_cookie cookie;
> > > >> >
> > > >> > -      sec = bfd_get_section_by_name (sub, ".eh_frame");
> > > >> > -      while (sec && init_reloc_cookie_for_section (&cookie, info,
> sec))
> > > >> > +      if (!init_reloc_cookie (&cookie, info, sub))
> > > >> > +	return FALSE;
> > > >> > +
> > > >> > +      if (info->eh_frame_hdr < 2)
> > > >> >  	{
> > > >> > -	  _bfd_elf_parse_eh_frame (sub, info, sec, &cookie);
> > > >> > -	  if (elf_section_data (sec)->sec_info
> > > >> > -	      && (sec->flags & SEC_LINKER_CREATED) == 0)
> > > >> > -	    elf_eh_frame_section (sub) = sec;
> > > >> > -	  fini_reloc_cookie_for_section (&cookie, sec);
> > > >> > -	  sec = bfd_get_next_section_by_name (sec);
> > > >> > +	  sec = bfd_get_section_by_name (sub, ".eh_frame");
> > > >> > +	  if (sec && init_reloc_cookie_rels (&cookie, info, sub, sec))
> > > >> > +	    {
> > > >> > +	      _bfd_elf_parse_eh_frame (sub, info, sec, &cookie);
> > > >> > +	      if (elf_section_data (sec)->sec_info
> > > >> > +		  && (sec->flags & SEC_LINKER_CREATED) == 0)
> > > >> > +		elf_eh_frame_section (sub) = sec;
> > > >> > +	      fini_reloc_cookie_for_section (&cookie, sec);
> > > >> > +	    }
> > > >> > +	}
> > > >> > +      else
> > > >> > +	{
> > > >> > +	  for (sec = sub->sections; sec; sec = sec->next)
> > > >> > +	    {
> > > >> > +	      if (CONST_STRNEQ (bfd_section_name (sub, sec),
> > > >> ".eh_frame_entry")
> > > >> > +		  && init_reloc_cookie_rels (&cookie, info, sub, sec))
> > > >> > +		{
> > > >> > +		  _bfd_elf_parse_eh_frame_entry (sub, info, sec,
> > &cookie,
> > > >> > +						 FALSE);
> > > >> > +		  fini_reloc_cookie_for_section (&cookie, sec);
> > > >> > +		}
> > > >> > +	    }
> > > >>
> > > >> This changes the info->eh_frame_hdr != 2 case to only handle the
> > > >> first .eh_frame section.  ("while" -> "if").
> > > >>
> > > >> Also, the init/fini_reloc_cookie* calls don't match up.  I assume
> > > >> the
> > > > idea is to
> > > >> avoid excessive allocation and freeing of the locsyms, but in that
> > > >> case you should use fini_reloc_cookie_rels instead of
> > > >> fini_reloc_cookie_for_section and call fini_reloc_cookie at the end.
> > > >> At the moment I think this leaks memory if there are no EH sections.
> > > >>
> > > >> I don't know either way whether splitting the
> > > >> init_reloc_cookie_for_section call up is a win or not for .eh_frame.
> > > >> It will be a win if there are
> > > > multiple EH
> > > >> sections but a loss if there are none, since we then initialise and
> > > > free the bfd-
> > > >> level information unnecessarily.  So I think it might make sense to
> > > >> keep the .eh_frame code as it is now and restrict the *_rels to the
> > > >> new
> > > code.
> > >
> > > You changed this to:
> > >
> > > > @@ -12171,7 +12256,9 @@ bfd_elf_gc_sections (bfd *abfd, struct
> > > > bfd_link_info *info)
> > > >
> > > >    /* Try to parse each bfd's .eh_frame section.  Point
> > > elf_eh_frame_section
> > > >       at the .eh_frame section if we can mark the FDEs individually.
> > > > */
> > > > -  for (sub = info->input_bfds; sub != NULL; sub = sub->link.next)
> > > > +  for (sub = info->input_bfds;
> > > > +       info->eh_frame_hdr_type != COMPACT_EH_HDR && sub != NULL;
> > > > +       sub = sub->link.next)
> > > >      {
> > > >        asection *sec;
> > > >        struct elf_reloc_cookie cookie;
> > >
> > > where info->eh_frame_hdr_type is an invariant.  I assume this was just
> > > to avoid reformatting the block, but please use:
> > >
> > >   if (info->eh_frame_hdr_type != COMPACT_EH_HDR)
> > >     for (...)
> > >
> > > instead.  Fortunately the block's not that big. :-)
> >
> > Done.
> > >
> > > >> > diff --git a/gas/config/tc-mips.h b/gas/config/tc-mips.h index
> > > >> > c7eaa04..a300062 100644
> > > >> > --- a/gas/config/tc-mips.h
> > > >> > +++ b/gas/config/tc-mips.h
> > > >> > @@ -177,7 +177,9 @@ extern enum dwarf2_format
> > > mips_dwarf2_format
> > > >> > (asection *);
> > > >> >
> > > >> >  extern int mips_dwarf2_addr_size (void);  #define
> > > >> > DWARF2_ADDR_SIZE(bfd) mips_dwarf2_addr_size () -#define
> > > >> > DWARF2_FDE_RELOC_SIZE mips_dwarf2_addr_size ()
> > > >> > +#define DWARF2_FDE_RELOC_SIZE (compact_eh ? 4 :
> > > >> mips_dwarf2_addr_size
> > > >> > +()) #define DWARF2_FDE_RELOC_ENCODING(enc) \
> > > >> > +  (enc | (compact_eh ? DW_EH_PE_pcrel : 0))
> > > >>
> > > >> What case is this handling?  Please explain this a bit more.
> > >
> > > Doesn't look like you've addressed this bit.
> >
> > Deferring for now.
> > >
> > > >> > @@ -4566,6 +4586,22 @@ argument is not present, otherwise
> second
> > > >> > argument should be a constant  or a symbol name.  The default
> > > >> > after @code{.cfi_startproc} is @code{.cfi_lsda 0xff},  no LSDA.
> > > >> >
> > > >> > +@section @code{.cfi_inline_lsda} [@var{align}]
> > > >> > +@code{.cfi_inline_lsda} marks the start of a LSDA data section
> > > >> > +and switches to the corresponding @code{.gnu.extab} section.
> > > >> > +Must be preceded by a CFI block containing a @code{.cfi_lsda}
> > > directive.
> > > >> > +Only valid when generating compact EH frames (i.e.
> > > >> > +with @code{.cfi_sections eh_frame_entry}.
> > > >>
> > > >> Missing ")".
> > >
> > > As above, this part of the doc is no longer there.
> >
> > Restored.
> > >
> > > >> > @@ -4643,6 +4679,20 @@ mark a code segment that has only one
> > > return
> > > >> > address which is reached  by a direct branch and no copy of the
> > > >> > return address exists in memory  or another register.
> > > >> >
> > > >> > +@section @code{.cfi_epilogue_begin} A pseudo-operation which
> > > marks
> > > >> > +the beginning of the epilogue in a given function.  This is
> > > >> > +currently used by the compact unwind-table implementation (for
> > > >> > +exception handling), which assumes a single register state for
> > > >> > +unwinding throughout the body of a function.  The function is
> > > >> > +scanned, and CFI directives are rewritten in a compact form.
> > > >> > +However, recent versions of GCC emit unwind information for
> > > >> > +function epilogues, which should not be represented in this
> > > >> > +compact
> > > >> > +form: hence, any CFI directives which occur in a function after
> > > >> > +@code{.cfi_epilogue_end} are not processed.
> > > >> > +
> > > >> > +This operation only affects the internals of the assembler, and
> > > >> > +is not represented in the binary output.
> > > >>
> > > >> Don't really follow this, sorry.  Can you give an example, or
> > > >> better yet, a testcase?
> > >
> > > Has .cfi_epilogue_begin been dropped?
> >
> > Yes.
> > >
> > > >>
> > > >> > @@ -161,6 +278,9 @@ alloc_debugseg_item (segT seg, int subseg,
> > > >> > char
> > > >> > *name)  static segT  is_now_linkonce_segment (void)  {
> > > >> > +  if (compact_eh)
> > > >> > +    return now_seg;
> > > >> > +
> > > >>
> > > >> Please add a comment explaining why this is correct.
> > >
> > > The hunk for this is:
> > >
> > > > +  /* We track the current segment in the cfi_insn_data struct and
> > > > +     the cfi_insn_data struct for Compact EH.  */  if (compact_eh)
> > > > +    return now_seg;
> > >
> > > but the comment repeats "the cfi_insn_data struct".  TBH I'm still not
> > > sure why/whether this is correct, but let's sort the other things out first.
> >
> > Okay.
> > >
> > > >> > -/* Emit a single byte into the current segment.  */
> > > >> > -
> > > >> > -static inline void
> > > >> > -out_one (int byte)
> > > >> > +static segT
> > > >> > +get_cfi_seg (segT cseg, const char *base, flagword flags, int
> > > >> > +align)
> > > >> >  {
> > > >> > -  FRAG_APPEND_1_CHAR (byte);
> > > >> > +  if (SUPPORT_FRAME_LINKONCE || ((flags & SEC_DEBUGGING) ==
> 0
> > > &&
> > > >> compact_eh))
> > > >> > +    {
> > > >>
> > > >> Please add a comment here too to explain the SEC_DEBUGGING test.
> > >
> > > It doesn't look like you addressed this bit.
> > >
> > > >> > +static void
> > > >> > +output_compact_unwind_data (struct fde_entry *fde, int align)
> > > >>
> > > >> Probably worth a comment here, since it wasn't obvious to me the
> > > >> "align" is the alignment of the end of the data rather than the start.
> > >
> > > I don't think you addressed this bit.  (In case it wasn't clear, I was
> > > suggesting adding a function comment that says what the function does
> > > and what its arguments are, etc.)
> >
> > Done.
> > >
> > > >> > +	  md_number_to_chars (p, 0, 4);
> > > >> > +	  fix_new (frag_now, p - frag_now->fr_literal, 4,
> > exp.X_add_symbol,
> > > >> > +		   exp.X_add_number, howto->pc_relative, code);
> > > >>
> > > >> What's the significance of exp.X_add_number here?  If it was
> > > >> supposed to be initialised to 0 above then I think it would be
> > > >> easier to leave the
> > > "exp"
> > > >> stuff alone and just use fde->start_address and 0 directly in this
> > > > fix_new call.
> > > >> Certainly...
> > > >>
> > > >> >    else
> > > >> >      {
> > > >> > -      exp.X_op = O_symbol;
> > > >> > -      exp.X_add_symbol = fde->start_address;
> > > >> >        exp.X_add_number = 0;
> > > >> >        addr_size = DWARF2_ADDR_SIZE (stdoutput);
> > > >> >        emit_expr (&exp, addr_size);
> > > >>
> > > >> ...separating the add_symbol and add_number feels odd.
> > >
> > > This is now:
> > >
> > > > @@ -1575,29 +1881,43 @@ output_fde (struct fde_entry *fde, struct
> > > cie_entry *cie,
> > > >        TC_DWARF2_EMIT_OFFSET (cie->start_address, offset_size);
> > > >      }
> > > >
> > > > +  exp.X_op = O_symbol;
> > > > +  exp.X_add_symbol = fde->start_address;
> > >
> > > (A)
> > >
> > > >    if (eh_frame)
> > > >      {
> > > > -      exp.X_op = O_subtract;
> > > > -      exp.X_add_number = 0;
> > > > +      bfd_reloc_code_real_type code
> > > > +	= tc_cfi_reloc_for_encoding (cie->fde_encoding);
> > > > +      if (code !=  BFD_RELOC_NONE)
> > > > +	{
> > > > +	  reloc_howto_type *howto = bfd_reloc_type_lookup (stdoutput,
> > > code);
> > > > +	  char *p = frag_more (4);
> > > > +	  md_number_to_chars (p, 0, 4);
> > > > +	  fix_new (frag_now, p - frag_now->fr_literal, 4, fde->start_address,
> > > > +		   0, howto->pc_relative, code);
> > > > +	}
> > > > +      else
> > > > +	{
> > > > +	  exp.X_op = O_subtract;
> > > > +	  exp.X_add_number = 0;
> > >
> > > (B)
> > >
> > > >  #if CFI_DIFF_EXPR_OK
> > > > -      exp.X_add_symbol = fde->start_address;
> > > > -      exp.X_op_symbol = symbol_temp_new_now ();
> > > > -      emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code
> offset.  */
> > > > +	  exp.X_add_symbol = fde->start_address;
> > > > +	  exp.X_op_symbol = symbol_temp_new_now ();
> > > > +	  emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code
> > > offset.  */
> > > >  #else
> > > > -      exp.X_op = O_symbol;
> > > > -      exp.X_add_symbol = fde->start_address;
> > > > -#ifdef tc_cfi_emit_pcrel_expr
> > > > -      tc_cfi_emit_pcrel_expr (&exp, DWARF2_FDE_RELOC_SIZE);	 /*
> > > Code offset.  */
> > > > +	  exp.X_op = O_symbol;
> > > > +	  exp.X_add_symbol = fde->start_address;
> > > > +
> > > > +#if defined(tc_cfi_emit_pcrel_expr)
> > > > +	  tc_cfi_emit_pcrel_expr (&exp, DWARF2_FDE_RELOC_SIZE);	 /*
> > > Code offset.  */
> > > >  #else
> > > > -      emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code
> offset.  */
> > > > +	  emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code
> > > offset.  */
> > > >  #endif
> > > >  #endif
> > > > +	}
> > > >        addr_size = DWARF2_FDE_RELOC_SIZE;
> > > >      }
> > > >    else
> > > >      {
> > > > -      exp.X_op = O_symbol;
> > > > -      exp.X_add_symbol = fde->start_address;
> > >
> > > (C)
> > >
> > > >        exp.X_add_number = 0;
> > > >        addr_size = DWARF2_ADDR_SIZE (stdoutput);
> > > >        emit_expr (&exp, addr_size);
> > >
> > > (C) is still hoisted to (A), but the only use of "exp" in the
> > > "eh_frame" arm is
> > > (rightly) at (B), which overrides what (A) does.
> > > Please just drop (A) and leave the "else" arm unmodified.
> >
> > Done.
> > >
> > > >> > @@ -1888,7 +2223,17 @@ cfi_finish (void)
> > > >> >
> > > >> >  	  for (fde = all_fde_data; fde ; fde = fde->next)
> > > >> >  	    {
> > > >> > -	      if (SUPPORT_FRAME_LINKONCE)
> > > >> > +	      if ((fde->sections & CFI_EMIT_eh_frame) == 0)
> > > >> > +		continue;
> > > >> > +
> > > >> > +#if SUPPORT_COMPACT_EH
> > > >> > +	      if (fde->eh_header_type == EH_COMPACT_HAS_LSDA)
> > > >> > +		fde->eh_header_type = EH_COMPACT_LEGACY;
> > > >> > +
> > > >> > +	      if (fde->eh_header_type != EH_COMPACT_LEGACY)
> > > >> > +		continue;
> > > >> > +#endif
> > > >>
> > > >> Please add a comment explaining this.
> > >
> > > I don't think you addressed this.
> > >
> >
> > Done.
> > > >> > @@ -1958,6 +2394,9 @@ cfi_finish (void)
> > > >> >
> > > >> >  	  for (fde = all_fde_data; fde ; fde = fde->next)
> > > >> >  	    {
> > > >> > +	      if ((fde->sections & CFI_EMIT_debug_frame) == 0)
> > > >> > +		continue;
> > > >> > +
> > > >> >  	      if (SUPPORT_FRAME_LINKONCE)
> > > >> >  		{
> > > >> >  		  if (HANDLED (fde))
> > > >>
> > > >> Please add a comment explaining why this is needed/correct.
> > >
> > > Same here.
> > >
> > Deferred.

^ permalink raw reply	[flat|nested] 31+ messages in thread

* FW: [Patch] Gas support for MIPS Compact EH
  2015-04-16 13:28             ` Moore, Catherine
@ 2015-05-01 13:54               ` Moore, Catherine
  2015-05-05 10:04                 ` Matthew Fortune
  0 siblings, 1 reply; 31+ messages in thread
From: Moore, Catherine @ 2015-05-01 13:54 UTC (permalink / raw)
  To: binutils

Ping.  I'd really like to move forward with this patch.   A large portion of the patch is not MIPS-specific, although there are some MIPS-specific pieces.
Would someone be able to review this?
Thanks,
Catherine

-----Original Message-----
From: binutils-owner@sourceware.org [mailto:binutils-owner@sourceware.org] On Behalf Of Moore, Catherine
Sent: Thursday, April 16, 2015 9:28 AM
To: Richard Sandiford
Cc: binutils@sourceware.org
Subject: RE: [Patch] Gas support for MIPS Compact EH

Hi Richard,
Is this patch something that you are going to be able to handle sometime soon?
If not, perhaps one of the other maintainers would be able to review this.
Thanks,
Catherine

> -----Original Message-----
> From: binutils-owner@sourceware.org [mailto:binutils- 
> owner@sourceware.org] On Behalf Of Moore, Catherine
> Sent: Sunday, February 08, 2015 12:00 PM
> To: Richard Sandiford
> Cc: binutils@sourceware.org
> Subject: RE: [Patch] Gas support for MIPS Compact EH
> 
> Sorry, forgot the patch in last message.
> 
> > -----Original Message-----
> > From: Moore, Catherine
> > Sent: Sunday, February 08, 2015 11:59 AM
> > To: 'Richard Sandiford'
> > Cc: binutils@sourceware.org
> > Subject: RE: [Patch] Gas support for MIPS Compact EH
> >
> > > -----Original Message-----
> > > From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> > > Sent: Saturday, November 22, 2014 9:43 AM
> > > To: Moore, Catherine
> > > Cc: binutils@sourceware.org
> > > Subject: Re: [Patch] Gas support for MIPS Compact EH
> > >
> > > "Moore, Catherine" <Catherine_Moore@mentor.com> writes:
> > > > Hi Richard,
> > > > Please find the updated Compact EH  patch for binutils.
> > > > This will likely still be a hard patch to review, but I tried to 
> > > > address the many comments that you made earlier.
> > > > The main difference is the exception handling relocation type 
> > > > for the Linux toolchain.  It is no longer gprel based.
> > > > The ELF and Linux tools both use pcrel32 and the EH-specific 
> > > > relocation type has been removed.
> > > > I ran into a few issues with the recent changes to eh_frame 
> > > > handling for DWARF, I hopefully covered those with the Compact 
> > > > EH
> > > implementation.
> > > > Please let me know what you think.  I hope we are close to 
> > > > converging on an implementation.
> > >
> > > OK, thanks.  I started by going through my previous comments and 
> > > looking at how you addressed them in the new patch.  I think there 
> > > are still some things that need to be sorted out.
> > >
> > > When doing the next patch, could you reply to the main points and 
> > > say how you dealt with them?  That'd make it a lot easier to see 
> > > how this is
> > evolving.
> >
> > Yes, I'll do that.  I'm sorry for the missing pieces of the last 
> > patch.  My
> rebase
> > had some unexpected results that I didn't catch before I posted.
> > Your comment that additional tests would have caught those is correct.
> I've
> > added some link tests to help out with that.  I think that extending 
> > the readelf -u (dump unwind option) would be useful for additional 
> > tests, but I don't want to make that additional development a 
> > requirement for
> approval
> > of this patch.  There are still one or two outstanding bits, but I'd 
> > like to
> defer
> > those until I see your comments on the current patch.
> > >
> > > >> -----Original Message-----
> > > >> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> > > >> Sent: Saturday, February 08, 2014 11:34 AM
> > > >> To: Moore, Catherine
> > > >> Cc: binutils@sourceware.org
> > > >> Subject: Re: [Patch] Gas support for MIPS Compact EH
> > > >>
> > > >> Thanks for the updates.
> > > >>
> > > >> "Moore, Catherine" <Catherine_Moore@mentor.com> writes:
> > > >> >> > @@ -4514,6 +4517,23 @@ argument is not present, otherwise 
> > > >> >> > secon or a symbol name.  The default after 
> > > >> >> > @code{.cfi_startproc} is @code{.cfi_lsda 0xff},  no LSDA.
> > > >> >> >
> > > >> >> > +@section @code{.cfi_inline_lsda} [@var{align}] 
> > > >> >> > +@code{.cfi_inline_lsda} marks the start of a LSDA data 
> > > >> >> > +section and switches to the corresponding 
> > > >> >> > +@code{.gnu.extab}
> > section.
> > > >> >> > +It must be preceded by a CFI block containing a 
> > > >> >> > +@code{.cfi_lsda} directive and is only valid when 
> > > >> >> > +generating
> > > compact EH frames (i.e.
> > > >> >> > +with @code{.cfi_sections eh_frame_entry}.
> > > >> >> > +
> > > >> >> > +If a compact encoding is being used then the table header 
> > > >> >> > +and unwinding opcodes will be generated at this point, so 
> > > >> >> > +that they are immediately followed by the LSDA data.  The 
> > > >> >> > +symbol referenced by the @code{.cfi_lsda} directive 
> > > >> >> > +should still be defined in case a fallback FDE based encoding is used.
> > > >> >> > +
> > > >> >> > +The optional @var{align} argument specifies the alignment
> > > required.
> > > >> >> > +The alignment is specified as a power of two, as with the 
> > > >> >> > +@code{.p2align} directive.
> > > >> >>
> > > >> >> Hmm, switching sections and emitting data feels very 
> > > >> >> different in style from the other .cfi directives, which 
> > > >> >> just annotate code without changing the flow of assembly.  
> > > >> >> I'd like to know other people's
> > > >> thoughts on this.
> > > >> >>
> > > >> >> Also, how do you terminate the LSDA?  The documentation 
> > > >> >> doesn't say (but should :-)) and I couldn't see this 
> > > >> >> directive in the spec
> either.
> > > >> >
> > > >> > The LSDA is terminated by a section switch.
> > > >>
> > > >> OK.  Like I say, please mention this in the documentation.
> > >
> > > The new version of the patch doesn't have the .cfi_inline_lsda 
> > > documentation that the previous patch had.  Please add it back :-) 
> > > But like I say above, please also document how the data is terminated.
> >
> > It's back.
> > >
> > > >> >> TBH, without tests, and without an explanation of what the 
> > > >> >> code is doing, I found this patch pretty hard to review.  E.g.:
> > > >> >>
> > > >> >> > @@ -129,7 +140,12 @@ get_debugseg_name (segT seg, const
> char
> > > >> >> >        dot = strchr (name + 1, '.');
> > > >> >> >
> > > >> >> >        if (!dollar && !dot)
> > > >> >> > -	name = "";
> > > >> >> > +	{
> > > >> >> > +	  if (compact_eh && strcmp (name, ".text") != 0)
> > > >> >> > +	    return concat (base_name, ".", name, NULL);
> > > >> >> > +
> > > >> >> > +	  name = "";
> > > >> >> > +	}
> > > >> >>
> > > >> >> why is this change needed?  I.e., for a text section called 
> > > >> >> something like .foobar, why does compact_eh need to put 
> > > >> >> things in a section name ending in "..foobar", rather than 
> > > >> >> in the main EH section?  I assume the double dots are 
> > > >> >> deliberate, e.g. to avoid confusion with
> > > >> ".text.foobar"?
> > > >> >
> > > >> > I'm not sure why Paul put this is and the next hunk.  There 
> > > >> > were test failures without.  I can revisit this For the next 
> > > >> > iteration if necessary.
> > > >>
> > > >> Yeah, if you could that'd be great.  The code can't really go 
> > > >> in if
> > > > there's no-
> > > >> one around who understands what it does.
> > > >>
> > > >> I assume it's just to ensure that each text section has its own 
> > > >> .eh_frame_entry, but in that case I think we should check based 
> > > >> on the base_name rather than compact_eh.  Or do we need the 
> > > >> same treatment for .gnu_extab.  If so, why?
> > > >>
> > > >> A comment is needed at the very least.
> > >
> > > The new hunk for this is:
> > >
> > > > @@ -128,7 +241,15 @@ get_debugseg_name (segT seg, const char
> > > *base_name)
> > > >        dot = strchr (name + 1, '.');
> > > >
> > > >        if (!dollar && !dot)
> > > > -	name = "";
> > > > +	{
> > > > +	  /* Ensure that a uniquely named .eh_frame_entry section
> > > > +	     is created for each text section.  */
> > > > +	  if (compact_eh
> > > > +	       && !strcmp (base_name, ".eh_frame_entry")
> > > > +	       && strcmp (name, ".text") != 0)
> > > > +	    return concat (base_name, ".", name, NULL);
> > > > +	  name = "";
> > > > +	}
> > >
> > > I don't think we want the compact_eh test here; just the base_name 
> > > check should be enough.  (Nit, sorry, but watch the indentation.
> > > One less space before "&&".)
> > >
> > Done.
> > > >> >> > @@ -833,14 +859,15 @@ dot_cfi_personality (int ignored
> ATTRIBU
> > > >> >> >      }
> > > >> >> >
> > > >> >> >    if ((encoding & 0xff) != encoding
> > > >> >> > -      || ((encoding & 0x70) != 0
> > > >> >> > +      || ((((encoding & 0x70) != 0
> > > >> >> >  #if CFI_DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr
> > > >> >> > -	  && (encoding & 0x70) != DW_EH_PE_pcrel
> > > >> >> > +	   && (encoding & 0x70) != DW_EH_PE_pcrel
> > > >> >> >  #endif
> > > >> >> >  	  )
> > > >> >> > /* leb128 can be handled, but does something actually need it?
> > > > */
> > > >> >> > -      || (encoding & 7) == DW_EH_PE_uleb128
> > > >> >> > -      || (encoding & 7) > DW_EH_PE_udata8)
> > > >> >> > +	   || (encoding & 7) == DW_EH_PE_uleb128
> > > >> >> > +	   || (encoding & 7) > DW_EH_PE_udata8)
> > > >> >> > +	&& !tc_cfi_special_encoding (encoding)))
> > > >> >> >      {
> > > >> >> >        as_bad (_("invalid or unsupported encoding in
> > .cfi_personality"));
> > > >> >> >        ignore_rest_of_line ();
> > > >> >>
> > > >> >> What does a "special" encoding mean?  Again, this hook 
> > > >> >> should be documented in internals.texi.  And do we really 
> > > >> >> want to change the set of encodings that are allowed for 
> > > >> >> DWARF, even on MIPS systems that predate compat EH?
> > > >> >>
> > > >> > Special means that we have code in the backend to emit a 
> > > >> > reloc for
> it.
> > > >> > In the revised patch, it goes along with Tc_cfi_reloc_for_encoding.
> > > >> > It also looks like internals.texi fails to document many of 
> > > >> > the tc_macros and it doesn't appear to be built into a .info fiie.
> > > >>
> > > >> That's no reason not to document new hooks though.  I know I've 
> > > >> used internals.texi to read more about a hook in the past.  If 
> > > >> you don't
> > > > want to put
> > > >> it there though then please at least put it in a comment instead.
> > >
> > > The hunk for this is:
> > >
> > > > diff --git a/gas/doc/internals.texi b/gas/doc/internals.texi 
> > > > index
> > > > cc4089b..9dd0bd0 100644
> > > > --- a/gas/doc/internals.texi
> > > > +++ b/gas/doc/internals.texi
> > > > @@ -1473,6 +1473,10 @@ completed, but before the relocations 
> > > > have
> > > been generated.
> > > >  If you define this macro, GAS will call it after the relocs 
> > > > have been generated.
> > > >
> > > > +@item tc_cfi_reloc_for_encoding @cindex 
> > > > +tc_cfi_reloc_for_encoding You may define this macro to indicate 
> > > > +whether a cfi encoding requires a
> > > relocation.
> > >
> > > That makes it sound like a boolean, whereas it returns a reloc.
> > > Please also mention that it controls whether compact EH is supported.
> > > The 80- character limit applies to documentation too.
> > >
> >
> > Done.
> >
> > > >> >> > +  demand_empty_rest_of_line ();
> > > >> >> > +  ccseg = CUR_SEG (last_fde);
> > > >> >> > +  /* Open .gnu_extab section.  */
> > > >> >> > +  cfi_seg = get_cfi_seg (ccseg, ".gnu_extab",
> > > >> >> > +			 (SEC_ALLOC | SEC_LOAD | SEC_DATA
> > > >> >> > +			  | DWARF2_EH_FRAME_READ_ONLY),
> > > >> >> > +			 1);
> > > >> >> > +#ifdef md_fix_up_eh_frame
> > > >> >> > +  md_fix_up_eh_frame (cfi_seg); #else
> > > >> >> > +  (void) cfi_seg;
> > > >> >> > +#endif
> > > >> >> > +
> > > >> >> > +  frag_align (align, 0, 0);  record_alignment (now_seg, 
> > > >> >> > + align);  if (last_fde->eh_header_type == 
> > > >> >> > + EH_COMPACT_HAS_LSDA)
> > > >> >> > +    output_compact_unwind_data (last_fde, align);
> > > >> >>
> > > >> >> Please could you explain the EH_COMPACT_LEGACY handling
> here?
> > > >> >
> > > >> > Would you please clarify this question?  I don't see the 
> > > >> > reference to EH_COMPACT_LEGACY.
> > > >>
> > > >> That was the problem :-)  Further up there's:
> > > >>
> > > >>   if (last_fde->eh_header_type != EH_COMPACT_LEGACY
> > > >>       && last_fde->eh_header_type != EH_COMPACT_HAS_LSDA)
> > > >>     {
> > > >>       as_bad (_(".cfi_inline_lsda seen for frame without .cfi_lsda"));
> > > >>       ignore_rest_of_line ();
> > > >>       return;
> > > >>     }
> > > >>
> > > >> So what does this code mean/do in the:
> > > >>
> > > >>   last_fde->eh_header_type == EH_COMPACT_LEGACY
> > > >>
> > > >> case?
> > >
> > > Still not sure about this -- please could you clarify?  The 
> > > context is this part of
> > > dot_cfi_inline_lsda:
> > >
> > > > +  if (last_fde->eh_header_type != EH_COMPACT_LEGACY
> > > > +      && last_fde->eh_header_type != EH_COMPACT_HAS_LSDA)
> > > > +    {
> > > > +      as_bad (_(".cfi_inline_lsda seen for frame without .cfi_lsda"));
> > > > +      ignore_rest_of_line ();
> > > > +      return;
> > > > +    }
> > > > +
> > > > +#ifdef md_flush_pending_output
> > > > +  md_flush_pending_output ();
> > > > +#endif
> > > > +
> > > > +  align = get_absolute_expression ();  if (align > max_alignment)
> > > > +    {
> > > > +      align = max_alignment;
> > > > +      as_bad (_("Alignment too large: %d assumed."), align);
> > > > +    }
> > > > +  else if (align < 0)
> > > > +    {
> > > > +      as_warn (_("Alignment negative: 0 assumed."));
> > > > +      align = 0;
> > > > +    }
> > > > +
> > > > +  demand_empty_rest_of_line ();
> > > > +  ccseg = CUR_SEG (last_fde);
> > > > +  /* Open .gnu_extab section.  */
> > > > +  get_cfi_seg (ccseg, ".gnu_extab",
> > > > +	       (SEC_ALLOC | SEC_LOAD | SEC_DATA
> > > > +		| DWARF2_EH_FRAME_READ_ONLY),
> > > > +	       1);
> > > >
> > > > +  frag_align (align, 0, 0);
> > > > +  record_alignment (now_seg, align);  if 
> > > > + (last_fde->eh_header_type == EH_COMPACT_HAS_LSDA)
> > > > +    output_compact_unwind_data (last_fde, align);
> > > > +
> > > > +  last_fde = NULL;
> > > > +
> > > > +  return;
> > >
> > > The first "if" statement allows the directive to be used for 
> > > EH_COMPACT_LEGACY, but what does the directive mean/do in that
> case?
> > >
> >
> > I've now added some commentary and extended the definition of the 
> > enumeration to include an EH_COMPACT_UNKNOWN value.
> > The eh_header_type was never explicitly initialized; it's now 
> > initialized to EH_COMPACT_UNKNOWN.  The EH_COMPACT_LEGACY header 
> > type is
> then
> > explicitly set when none of the COMPACT header type cases are
> discovered.
> >
> > > >> > diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index 
> > > >> > 0aab5fa..b7d7df0
> > > >> > 100644
> > > >> > --- a/bfd/elf-bfd.h
> > > >> > +++ b/bfd/elf-bfd.h
> > > >> > @@ -381,8 +381,10 @@ struct eh_frame_hdr_info  {
> > > >> >    struct htab *cies;
> > > >> >    asection *hdr_sec;
> > > >> > -  unsigned int fde_count, array_count;
> > > >> > +  unsigned int fde_count, array_count, allocated_entries;
> > > >> >    struct eh_frame_array_ent *array;
> > > >> > +  /* eh_frame_entry fragments.  */  asection **entries;
> > > >> >    /* TRUE if we should try to merge CIEs between input sections.  */
> > > >> >    bfd_boolean merge_cies;
> > > >> >    /* TRUE if all .eh_frames have been parsd.  */
> > > >>
> > > >> I'm not sure there's enough commonality between the DWARF and
> > > compact
> > > >> versions to share this structure.  Either we should have 
> > > >> separate structures or use a union to separate out the format-specific bits.
> > >
> > > I see you've done this, thanks, but:
> > >
> > > > +struct dwarf_eh_frame_hdr_info
> > > > +{
> > > > +  struct eh_frame_array_ent *array; };
> > > > +
> > > > +struct compact_eh_frame_hdr_info {
> > > > +  unsigned int allocated_entries;
> > > > +  /* eh_frame_entry_fragments.  */
> > > > +  asection **entries;
> > > > +};
> > > > +
> > > >  struct eh_frame_hdr_info
> > > >  {
> > > >    struct htab *cies;
> > > >    asection *hdr_sec;
> > > >    unsigned int fde_count, array_count;
> > > > -  struct eh_frame_array_ent *array;
> > > >    /* TRUE if .eh_frame_hdr should contain the sorted search table.
> > > >       We build it if we successfully read all .eh_frame input sections
> > > >       and recognize them.  */
> > > >    bfd_boolean table;
> > > > +  bfd_boolean frame_hdr_is_compact;  union
> > > > +    {
> > > > +      struct dwarf_eh_frame_hdr_info dwarf;
> > > > +      struct compact_eh_frame_hdr_info compact;
> > > > +    }
> > > > +  u;
> > > >  };
> > > >
> > > >  /* Enum used to identify target specific extensions to the 
> > > > elf_obj_tdata
> > >
> > > ...aren't cies, fde_count, array_count and table specific to the 
> > > DWARF version too?  My point was that if we went for the union 
> > > approach, the only fields in the common structure should be those 
> > > that are needed by both formats.  It looks like that's just hdr_sec.
> > >
> > > If moving all of them seems like too much work, I think we should 
> > > go for separate structures instead.
> >
> > I moved them and kept the union.
> >
> > >
> > > >> > +	}
> > > >> > +
> > > >> > +      BFD_ASSERT (hdr_info->entries);
> > > >> > +    }
> > > >> > +
> > > >> > +  hdr_info->entries[hdr_info->array_count++] = sec; }
> > > >> > +
> > > >> > +/* Parse a .eh_frame_entry section.  Figure out which text 
> > > >> > +section
> it
> > > >> > +   references.  */
> > > >> > +
> > > >> > +void
> > > >> > +_bfd_elf_parse_eh_frame_entry (bfd *abfd, struct 
> > > >> > +bfd_link_info
> > > *info,
> > > >> > +			       asection *sec, struct elf_reloc_cookie
> > *cookie,
> > > >> > +			       bfd_boolean remember)
> > > >>
> > > >> This does more than the comment says and the name implies; the 
> > > >> REMEMBER stuff isn't mentioned.
> > > >>
> > > >> The patch tries to do the parsing during bfd_elf_discard_info, 
> > > >> but since the parsing wants to be able to fail with an error, I 
> > > >> think we need to do it in an earlier pass.  We can then return 
> > > >> a bfd_boolean success code and propagate error returns up, 
> > > >> which the current patch
> > > doesn't do.
> > > >> Ideally we'd put the pass somewhere before GC, so that both the 
> > > >> GC and bfd_elf_discard_info stages can assume parsed 
> > > >> .eh_frame_entry
> > > sections.
> > > >>
> > > >> Having bfd_elf_discard_info add info (as per REMEMBER == TRUE) 
> > > >> seems a bit counterintuitive.  I think the earlier pass should 
> > > >> record all .eh_frame_entry sections and then the code currently 
> > > >> in _bfd_elf_end_eh_frame_parsing (but see below) should remove
> > > unwanted
> > > >> entries from the eh_frame_hdr_info array.
> > >
> > > I see you've added the earlier pass, thanks, but errors aren't 
> > > always reported back up.  The function:
> > >
> > > > +/* Parse a .eh_frame_entry section.  Figure out which text section it
> > > > +   references.  */
> > > > +
> > > > +void
> > > > +_bfd_elf_parse_eh_frame_entry (struct bfd_link_info *info,
> > > > +			       asection *sec, struct elf_reloc_cookie *cookie) {
> > > > [...]
> > > > +fail:
> > > > +  (*_bfd_error_handler) (_("%B: failed to process 
> > > > +.eh_frame_entry"),
> > > > +sec->owner); }
> > >
> > > doesn't tell the caller (and eventually the ld code) that a 
> > > problem
> occured.
> > > The function ought to return a bfd_boolean success code, like its callers.
> > > Also, the error here seems to duplicate the eventual ld one:
> > >
> > >     einfo (_("%P%F: Failed to parse EH frame entries.\n"));
> > >
> > > without really providing any more information.
> > >
> > > Don't shoot me, but it would probably be cleaner to make this an 
> > > ELF-specific pass and call it from elf32.em instead.  That'll 
> > > avoid having to do all the aout, bout, etc. stuff and would allow 
> > > the ld code to refer to ELFisms like .eh_frame_entry itself.
> > >
> > Okay, I've now moved this to elf32.em.  I fixed the error handling as well.
> >
> > > >> > +/* Finish a pass over all .eh_frame and eh_frame_entry sections.
> > > >> > +*/
> > > >> > +
> > > >> > +bfd_boolean
> > > >> >  _bfd_elf_end_eh_frame_parsing (struct bfd_link_info *info)  {
> > > >> >    struct eh_frame_hdr_info *hdr_info;
> > > >> > +  unsigned int i;
> > > >> >
> > > >> >    hdr_info = &elf_hash_table (info)->eh_info;
> > > >> >    hdr_info->parsed_eh_frames = TRUE;
> > > >> > +
> > > >> > +  if (hdr_info->array_count == 0 || info->eh_frame_hdr < 2)
> > > >> > +    return FALSE;
> > > >> > +
> > > >> > +  qsort (hdr_info->entries, hdr_info->array_count,
> > > >> > +	 sizeof (asection *), cmp_eh_frame_hdr);
> > > >> > +
> > > >> > +  for (i = 0; i < hdr_info->array_count - 1; i++)
> > > >> > +    {
> > > >> > +      add_eh_frame_hdr_terminator (hdr_info->entries[i],
> > > >> > +				   hdr_info->entries[i + 1]);
> > > >> > +    }
> > > >> > +
> > > >> > +  /* Add a CANTUNWIND terminator after the last entry.  */ 
> > > >> > + add_eh_frame_hdr_terminator (hdr_info->entries[i], NULL); 
> > > >> > + return TRUE;
> > > >>
> > > >> This routine is called from both bfd_elf_gc_sections and 
> > > >> bfd_elf_discard_info but I think you only want it for
> > bfd_elf_discard_info.
> > > >> So perhaps this should be a separate function.
> > > >>
> > > >> I wonder whether we could instead insert CANTUNWINDs earlier 
> > > >> (say in the non-dynamic part of size_dynamic_sections) based on 
> > > >> the link
> > order.
> > > >> I.e. rather than comparing the start and end addresses of two 
> > > >> sections, we could just walk the sections in the order that 
> > > >> they're going to
> > > be linked.
> > > >>
> > > >> It sounds like doing it that way would be more direct and more
> efficient.
> > > >> Returning TRUE here forces the linker to map sections twice.
> > >
> > > Note that the generic version of _bfd_elf_end_eh_frame_parsing was 
> > > removed in the meantime.  Your patch adds it back, but I can't see 
> > > where it gets called.  Unless I'm missing something, this suggests 
> > > that the tests don't exercise this code and that extra tests might 
> > > be
> > needed.
> >
> > I've now added link-time tests and the call
> _bfd_elf_end_eh_frame_parsing
> > has been restored.
> > >
> > > >> > @@ -1271,6 +1465,45 @@ _bfd_elf_eh_frame_present (struct
> > > >> bfd_link_info *info)
> > > >> >    return FALSE;
> > > >> >  }
> > > >> >
> > > >> > +/* Return true if there is at least one .eh_frame_entry section in
> > > >> > +   input files.  */
> > > >> > +bfd_boolean
> > > >> > +_bfd_elf_eh_frame_entry_present (struct bfd_link_info *info) 
> > > >> > +{
> > > >> > +  asection *o;
> > > >> > +  bfd *abfd;
> > > >> > +
> > > >> > +  for (abfd = info->input_bfds; abfd != NULL; abfd = abfd-
> >link_next)
> > > >> > +    {
> > > >> > +      for (o = abfd->sections; o; o = o->next)
> > > >> > +	{
> > > >> > +	  const char *name = bfd_get_section_name (abfd, o);
> > > >> > +
> > > >> > +	  if (strcmp (name, ".eh_frame_entry")
> > > >> > +	      && !bfd_is_abs_section (o->output_section))
> > > >> > +	    {
> > > >> > +	      if (strcmp (o->output_section->name, ".eh_frame_hdr"))
> > > >> > +		return TRUE;
> > > >> > +	      else
> > > >> > +		{
> > > >> > +		  (*_bfd_error_handler)
> > > >> > +			(_("error: an '.eh_frame_entry'"
> > > >> > +			   " input section that is not mapped to the"
> > > >> > +			   " '.eh_frame_hdr' output section was
> > seen"));
> > > >> > +		  (*_bfd_error_handler)
> > > >> > +			(_("note: try adding '*(.eh_frame_entry"
> > > >> > +			   " .eh_frame_entry.*)' to the
> > '.eh_frame_hdr'"
> > > >> > +			   " output section description in the linker"
> > > >> > +			   " script"));
> > > >> > +		  bfd_set_error (bfd_error_invalid_operation);
> > > >> > +		  return FALSE;
> > > >> > +		}
> > > >> > +	    }
> > > >> > +	}
> > > >> > +    }
> > > >> > +  return FALSE;
> > > >>
> > > >> The caller can't tell "FALSE because an error was reported" 
> > > >> from "FALSE because there was no .eh_frame_entry".  We should 
> > > >> separate out the two cases and propagate error returns up.
> > >
> > > It looks like you dealt with this by removing the error checking.
> > > I think we still want it, but it should be done separately and in 
> > > a context where the error can be propagated.
> >
> > I'm deferring this for now.
> > >
> > > >> > @@ -1387,6 +1636,71 @@ _bfd_elf_eh_frame_section_offset (bfd
> > > >> *output_bfd ATTRIBUTE_UNUSED,
> > > >> >  	  + extra_augmentation_data_bytes (sec_info->entry +
> mid));  }
> > > >> >
> > > >> > +/* Write out .eh_frame_entry section.  Add CANTUNWIND
> > terminator
> > > >> > +if
> > > >> needed.
> > > >> > +   Also check that the contents look sane.  */
> > > >> > +
> > > >> > +bfd_boolean
> > > >> > +_bfd_elf_write_section_eh_frame_entry (bfd *abfd,
> > > >> > +				       asection *sec,
> > > >> > +				       bfd_byte *contents)
> > > >>
> > > >> Formatting: first two arguments fit on a line.
> > >
> > > Still present.
> > Fixed.
> > >
> > > >> > +  text_sec = (asection *) elf_section_data (sec)->sec_info;  
> > > >> > + addr = text_sec->output_section->vma + text_sec-
> >output_offset
> > > >> > +	 + text_sec->size;
> > > >> > +  addr &= ~1;
> > > >> > +  addr -= (sec->output_section->vma + sec->output_offset +
> > > >> > +sec->rawsize);
> > > >> > +  BFD_ASSERT ((addr & 1) == 0);
> > > >>
> > > >> It looks like this could trigger for odd-sized input text sections.
> > > >> I think it should be an error instead of an assert.
> > >
> > > I see you've done this with:
> > >
> > > > +  if (addr & 1)
> > > > +    {
> > > > +      (*_bfd_error_handler) (_("%B: %s invalid input section size"),
> > > > +			     sec->owner, sec->name);
> > > > +      return FALSE;
> > > > +    }
> > > > +  if (last_addr >= addr + sec->rawsize)
> > > > +    {
> > > > +      (*_bfd_error_handler) (_("%B: %s points past end of text
> section"),
> > > > +			     sec->owner, sec->name);
> > > > +      return FALSE;
> > > > +    }
> > >
> > > but I think we want "bfd_set_error (bfd_error_bad_value);" too.
> >
> > Done.
> > >
> > > >> > +  if (last_addr >= addr + sec->rawsize)
> > > >> > +    {
> > > >> > +      (*_bfd_error_handler) (_("%B: %s points past end of 
> > > >> > + text
> > section"),
> > > >> > +			     sec->owner, sec->name);
> > > >> > +      return FALSE;
> > > >> > +    }
> > > >> > +
> > > >> > +  if (sec->size == sec->rawsize)
> > > >> > +    return TRUE;
> > > >> > +
> > > >> > +  BFD_ASSERT (sec->size == sec->rawsize + 8);  BFD_ASSERT 
> > > >> > + ((addr &
> > > >> > + 1) == 0);
> > > >> > +  bfd_put_32 (abfd, addr, cantunwind);
> > > >> > +  bfd_put_32 (abfd, 0x015d5d01, cantunwind + 4);  return 
> > > >> > + bfd_set_section_contents (abfd, sec->output_section,
> > > >> cantunwind,
> > > >> > +				   sec->output_offset + sec->rawsize,
> > 8);
> > > >>
> > > >> ARM and c6x seem to use 0 rather than 0x5d as the "no unwind"
> > > >> opcode, is that right?  If so, I think this should be a hook.
> > >
> > > It doesn't look like you addressed this part.
> >
> > It looked to me like those ports used 1?  In any case, I've added 
> > the hook,
> but
> > I haven't updated the arm and c6x ports to use it.
> > >
> > > >> The decision about whether to insert the CANTUNWIND is made
> during
> > > >> bfd_elf_discard_info, but addresses can change after that “thanks”
> > > >> to relaxation.  So this could in principle end up emitting a 
> > > >> CANTUNWIND for the same address as the following text section.
> > >
> > > As above, it looks like nothing ever calls 
> > > _bfd_elf_write_section_eh_frame_entry in the new version of the
> patch.
> > > If the tests don't pick that up then I think we need some more :-)
> > >
> > Sorry about this missing bit.  More tests have been added and the 
> > call to
> this
> > function has been restored.
> >
> > > >> > @@ -10039,6 +10040,10 @@ elf_link_input_bfd (struct 
> > > >> > elf_final_link_info
> > > >> *flinfo, bfd *input_bfd)
> > > >> >  	      return FALSE;
> > > >> >  	  }
> > > >> >  	  break;
> > > >> > +	case SEC_INFO_TYPE_EH_FRAME_ENTRY:
> > > >> > +	    if (! _bfd_elf_write_section_eh_frame_entry 
> > > >> > +(output_bfd,
> > o,
> > > >> contents))
> > > >> > +	      return FALSE;
> > > >> > +	  break;
> > > >>
> > > >> Excess indentation of the "if".
> > >
> > > Not sure: why is this no longer in the patch?
> > Restored.
> > >
> > > >> > @@ -11807,6 +11814,13 @@ _bfd_elf_gc_mark (struct 
> > > >> > bfd_link_info
> > > *info,
> > > >> >  	}
> > > >> >      }
> > > >> >
> > > >> > +  eh_frame = elf_section_eh_frame_entry (sec);  if (eh_frame 
> > > >> > + &&
> > > >> > + !eh_frame->gc_mark)
> > > >> > +    {
> > > >> > +    if (!_bfd_elf_gc_mark (info, eh_frame, gc_mark_hook))
> > > >> > +      return FALSE;
> > > >> > +    }
> > > >>
> > > >> In this context we should be using "ret":
> > > >>
> > > >>   eh_frame = elf_section_eh_frame_entry (sec);
> > > >>   if (ret && eh_frame && !eh_frame->gc_mark)
> > > >>     ret = _bfd_elf_gc_mark (info, eh_frame, gc_mark_hook);
> > >
> > > You changed this to:
> > >
> > > > +  if (eh_frame && !eh_frame->gc_mark)
> > > > +    {
> > > > +      if (!_bfd_elf_gc_mark (info, eh_frame, gc_mark_hook))
> > > > +	ret = FALSE;
> > > > +    }
> > >
> > > but the point was also that we don't want to try another mark once "ret"
> > > is already FALSE.  The "ret &&" part of the condition was important.
> > Another rebase problem.  Now fixed.
> > >
> > > >> > @@ -12190,22 +12204,42 @@ bfd_elf_gc_sections (bfd *abfd, 
> > > >> > struct
> > > >> bfd_link_info *info)
> > > >> >    bed->gc_keep (info);
> > > >> >
> > > >> >    /* Try to parse each bfd's .eh_frame section.  Point
> > > >> elf_eh_frame_section
> > > >> > -     at the .eh_frame section if we can mark the FDEs individually.  */
> > > >> > +     at the .eh_frame section if we can mark the FDEs individually.
> > > >> > +     Establish links from text sections to their corresponding
> > > >> > +     .eh_frame_entry sections.  */
> > > >> >    _bfd_elf_begin_eh_frame_parsing (info);
> > > >> >    for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
> > > >> >      {
> > > >> >        asection *sec;
> > > >> >        struct elf_reloc_cookie cookie;
> > > >> >
> > > >> > -      sec = bfd_get_section_by_name (sub, ".eh_frame");
> > > >> > -      while (sec && init_reloc_cookie_for_section (&cookie, info,
> sec))
> > > >> > +      if (!init_reloc_cookie (&cookie, info, sub))
> > > >> > +	return FALSE;
> > > >> > +
> > > >> > +      if (info->eh_frame_hdr < 2)
> > > >> >  	{
> > > >> > -	  _bfd_elf_parse_eh_frame (sub, info, sec, &cookie);
> > > >> > -	  if (elf_section_data (sec)->sec_info
> > > >> > -	      && (sec->flags & SEC_LINKER_CREATED) == 0)
> > > >> > -	    elf_eh_frame_section (sub) = sec;
> > > >> > -	  fini_reloc_cookie_for_section (&cookie, sec);
> > > >> > -	  sec = bfd_get_next_section_by_name (sec);
> > > >> > +	  sec = bfd_get_section_by_name (sub, ".eh_frame");
> > > >> > +	  if (sec && init_reloc_cookie_rels (&cookie, info, sub, sec))
> > > >> > +	    {
> > > >> > +	      _bfd_elf_parse_eh_frame (sub, info, sec, &cookie);
> > > >> > +	      if (elf_section_data (sec)->sec_info
> > > >> > +		  && (sec->flags & SEC_LINKER_CREATED) == 0)
> > > >> > +		elf_eh_frame_section (sub) = sec;
> > > >> > +	      fini_reloc_cookie_for_section (&cookie, sec);
> > > >> > +	    }
> > > >> > +	}
> > > >> > +      else
> > > >> > +	{
> > > >> > +	  for (sec = sub->sections; sec; sec = sec->next)
> > > >> > +	    {
> > > >> > +	      if (CONST_STRNEQ (bfd_section_name (sub, sec),
> > > >> ".eh_frame_entry")
> > > >> > +		  && init_reloc_cookie_rels (&cookie, info, sub, sec))
> > > >> > +		{
> > > >> > +		  _bfd_elf_parse_eh_frame_entry (sub, info, sec,
> > &cookie,
> > > >> > +						 FALSE);
> > > >> > +		  fini_reloc_cookie_for_section (&cookie, sec);
> > > >> > +		}
> > > >> > +	    }
> > > >>
> > > >> This changes the info->eh_frame_hdr != 2 case to only handle 
> > > >> the first .eh_frame section.  ("while" -> "if").
> > > >>
> > > >> Also, the init/fini_reloc_cookie* calls don't match up.  I 
> > > >> assume the
> > > > idea is to
> > > >> avoid excessive allocation and freeing of the locsyms, but in 
> > > >> that case you should use fini_reloc_cookie_rels instead of 
> > > >> fini_reloc_cookie_for_section and call fini_reloc_cookie at the end.
> > > >> At the moment I think this leaks memory if there are no EH sections.
> > > >>
> > > >> I don't know either way whether splitting the 
> > > >> init_reloc_cookie_for_section call up is a win or not for .eh_frame.
> > > >> It will be a win if there are
> > > > multiple EH
> > > >> sections but a loss if there are none, since we then initialise 
> > > >> and
> > > > free the bfd-
> > > >> level information unnecessarily.  So I think it might make 
> > > >> sense to keep the .eh_frame code as it is now and restrict the 
> > > >> *_rels to the new
> > > code.
> > >
> > > You changed this to:
> > >
> > > > @@ -12171,7 +12256,9 @@ bfd_elf_gc_sections (bfd *abfd, struct 
> > > > bfd_link_info *info)
> > > >
> > > >    /* Try to parse each bfd's .eh_frame section.  Point
> > > elf_eh_frame_section
> > > >       at the .eh_frame section if we can mark the FDEs individually.
> > > > */
> > > > -  for (sub = info->input_bfds; sub != NULL; sub = 
> > > > sub->link.next)
> > > > +  for (sub = info->input_bfds;
> > > > +       info->eh_frame_hdr_type != COMPACT_EH_HDR && sub != NULL;
> > > > +       sub = sub->link.next)
> > > >      {
> > > >        asection *sec;
> > > >        struct elf_reloc_cookie cookie;
> > >
> > > where info->eh_frame_hdr_type is an invariant.  I assume this was 
> > > just to avoid reformatting the block, but please use:
> > >
> > >   if (info->eh_frame_hdr_type != COMPACT_EH_HDR)
> > >     for (...)
> > >
> > > instead.  Fortunately the block's not that big. :-)
> >
> > Done.
> > >
> > > >> > diff --git a/gas/config/tc-mips.h b/gas/config/tc-mips.h 
> > > >> > index
> > > >> > c7eaa04..a300062 100644
> > > >> > --- a/gas/config/tc-mips.h
> > > >> > +++ b/gas/config/tc-mips.h
> > > >> > @@ -177,7 +177,9 @@ extern enum dwarf2_format
> > > mips_dwarf2_format
> > > >> > (asection *);
> > > >> >
> > > >> >  extern int mips_dwarf2_addr_size (void);  #define
> > > >> > DWARF2_ADDR_SIZE(bfd) mips_dwarf2_addr_size () -#define 
> > > >> > DWARF2_FDE_RELOC_SIZE mips_dwarf2_addr_size ()
> > > >> > +#define DWARF2_FDE_RELOC_SIZE (compact_eh ? 4 :
> > > >> mips_dwarf2_addr_size
> > > >> > +()) #define DWARF2_FDE_RELOC_ENCODING(enc) \
> > > >> > +  (enc | (compact_eh ? DW_EH_PE_pcrel : 0))
> > > >>
> > > >> What case is this handling?  Please explain this a bit more.
> > >
> > > Doesn't look like you've addressed this bit.
> >
> > Deferring for now.
> > >
> > > >> > @@ -4566,6 +4586,22 @@ argument is not present, otherwise
> second
> > > >> > argument should be a constant  or a symbol name.  The default 
> > > >> > after @code{.cfi_startproc} is @code{.cfi_lsda 0xff},  no LSDA.
> > > >> >
> > > >> > +@section @code{.cfi_inline_lsda} [@var{align}] 
> > > >> > +@code{.cfi_inline_lsda} marks the start of a LSDA data 
> > > >> > +section and switches to the corresponding @code{.gnu.extab} section.
> > > >> > +Must be preceded by a CFI block containing a 
> > > >> > +@code{.cfi_lsda}
> > > directive.
> > > >> > +Only valid when generating compact EH frames (i.e.
> > > >> > +with @code{.cfi_sections eh_frame_entry}.
> > > >>
> > > >> Missing ")".
> > >
> > > As above, this part of the doc is no longer there.
> >
> > Restored.
> > >
> > > >> > @@ -4643,6 +4679,20 @@ mark a code segment that has only one
> > > return
> > > >> > address which is reached  by a direct branch and no copy of 
> > > >> > the return address exists in memory  or another register.
> > > >> >
> > > >> > +@section @code{.cfi_epilogue_begin} A pseudo-operation which
> > > marks
> > > >> > +the beginning of the epilogue in a given function.  This is 
> > > >> > +currently used by the compact unwind-table implementation 
> > > >> > +(for exception handling), which assumes a single register 
> > > >> > +state for unwinding throughout the body of a function.  The 
> > > >> > +function is scanned, and CFI directives are rewritten in a compact form.
> > > >> > +However, recent versions of GCC emit unwind information for 
> > > >> > +function epilogues, which should not be represented in this 
> > > >> > +compact
> > > >> > +form: hence, any CFI directives which occur in a function 
> > > >> > +after @code{.cfi_epilogue_end} are not processed.
> > > >> > +
> > > >> > +This operation only affects the internals of the assembler, 
> > > >> > +and is not represented in the binary output.
> > > >>
> > > >> Don't really follow this, sorry.  Can you give an example, or 
> > > >> better yet, a testcase?
> > >
> > > Has .cfi_epilogue_begin been dropped?
> >
> > Yes.
> > >
> > > >>
> > > >> > @@ -161,6 +278,9 @@ alloc_debugseg_item (segT seg, int 
> > > >> > subseg, char
> > > >> > *name)  static segT  is_now_linkonce_segment (void)  {
> > > >> > +  if (compact_eh)
> > > >> > +    return now_seg;
> > > >> > +
> > > >>
> > > >> Please add a comment explaining why this is correct.
> > >
> > > The hunk for this is:
> > >
> > > > +  /* We track the current segment in the cfi_insn_data struct and
> > > > +     the cfi_insn_data struct for Compact EH.  */  if (compact_eh)
> > > > +    return now_seg;
> > >
> > > but the comment repeats "the cfi_insn_data struct".  TBH I'm still 
> > > not sure why/whether this is correct, but let's sort the other things out first.
> >
> > Okay.
> > >
> > > >> > -/* Emit a single byte into the current segment.  */
> > > >> > -
> > > >> > -static inline void
> > > >> > -out_one (int byte)
> > > >> > +static segT
> > > >> > +get_cfi_seg (segT cseg, const char *base, flagword flags, 
> > > >> > +int
> > > >> > +align)
> > > >> >  {
> > > >> > -  FRAG_APPEND_1_CHAR (byte);
> > > >> > +  if (SUPPORT_FRAME_LINKONCE || ((flags & SEC_DEBUGGING) ==
> 0
> > > &&
> > > >> compact_eh))
> > > >> > +    {
> > > >>
> > > >> Please add a comment here too to explain the SEC_DEBUGGING test.
> > >
> > > It doesn't look like you addressed this bit.
> > >
> > > >> > +static void
> > > >> > +output_compact_unwind_data (struct fde_entry *fde, int 
> > > >> > +align)
> > > >>
> > > >> Probably worth a comment here, since it wasn't obvious to me 
> > > >> the "align" is the alignment of the end of the data rather than the start.
> > >
> > > I don't think you addressed this bit.  (In case it wasn't clear, I 
> > > was suggesting adding a function comment that says what the 
> > > function does and what its arguments are, etc.)
> >
> > Done.
> > >
> > > >> > +	  md_number_to_chars (p, 0, 4);
> > > >> > +	  fix_new (frag_now, p - frag_now->fr_literal, 4,
> > exp.X_add_symbol,
> > > >> > +		   exp.X_add_number, howto->pc_relative, code);
> > > >>
> > > >> What's the significance of exp.X_add_number here?  If it was 
> > > >> supposed to be initialised to 0 above then I think it would be 
> > > >> easier to leave the
> > > "exp"
> > > >> stuff alone and just use fde->start_address and 0 directly in 
> > > >> this
> > > > fix_new call.
> > > >> Certainly...
> > > >>
> > > >> >    else
> > > >> >      {
> > > >> > -      exp.X_op = O_symbol;
> > > >> > -      exp.X_add_symbol = fde->start_address;
> > > >> >        exp.X_add_number = 0;
> > > >> >        addr_size = DWARF2_ADDR_SIZE (stdoutput);
> > > >> >        emit_expr (&exp, addr_size);
> > > >>
> > > >> ...separating the add_symbol and add_number feels odd.
> > >
> > > This is now:
> > >
> > > > @@ -1575,29 +1881,43 @@ output_fde (struct fde_entry *fde, 
> > > > struct
> > > cie_entry *cie,
> > > >        TC_DWARF2_EMIT_OFFSET (cie->start_address, offset_size);
> > > >      }
> > > >
> > > > +  exp.X_op = O_symbol;
> > > > +  exp.X_add_symbol = fde->start_address;
> > >
> > > (A)
> > >
> > > >    if (eh_frame)
> > > >      {
> > > > -      exp.X_op = O_subtract;
> > > > -      exp.X_add_number = 0;
> > > > +      bfd_reloc_code_real_type code
> > > > +	= tc_cfi_reloc_for_encoding (cie->fde_encoding);
> > > > +      if (code !=  BFD_RELOC_NONE)
> > > > +	{
> > > > +	  reloc_howto_type *howto = bfd_reloc_type_lookup (stdoutput,
> > > code);
> > > > +	  char *p = frag_more (4);
> > > > +	  md_number_to_chars (p, 0, 4);
> > > > +	  fix_new (frag_now, p - frag_now->fr_literal, 4, fde->start_address,
> > > > +		   0, howto->pc_relative, code);
> > > > +	}
> > > > +      else
> > > > +	{
> > > > +	  exp.X_op = O_subtract;
> > > > +	  exp.X_add_number = 0;
> > >
> > > (B)
> > >
> > > >  #if CFI_DIFF_EXPR_OK
> > > > -      exp.X_add_symbol = fde->start_address;
> > > > -      exp.X_op_symbol = symbol_temp_new_now ();
> > > > -      emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code
> offset.  */
> > > > +	  exp.X_add_symbol = fde->start_address;
> > > > +	  exp.X_op_symbol = symbol_temp_new_now ();
> > > > +	  emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code
> > > offset.  */
> > > >  #else
> > > > -      exp.X_op = O_symbol;
> > > > -      exp.X_add_symbol = fde->start_address;
> > > > -#ifdef tc_cfi_emit_pcrel_expr
> > > > -      tc_cfi_emit_pcrel_expr (&exp, DWARF2_FDE_RELOC_SIZE);	 /*
> > > Code offset.  */
> > > > +	  exp.X_op = O_symbol;
> > > > +	  exp.X_add_symbol = fde->start_address;
> > > > +
> > > > +#if defined(tc_cfi_emit_pcrel_expr)
> > > > +	  tc_cfi_emit_pcrel_expr (&exp, DWARF2_FDE_RELOC_SIZE);	 /*
> > > Code offset.  */
> > > >  #else
> > > > -      emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code
> offset.  */
> > > > +	  emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code
> > > offset.  */
> > > >  #endif
> > > >  #endif
> > > > +	}
> > > >        addr_size = DWARF2_FDE_RELOC_SIZE;
> > > >      }
> > > >    else
> > > >      {
> > > > -      exp.X_op = O_symbol;
> > > > -      exp.X_add_symbol = fde->start_address;
> > >
> > > (C)
> > >
> > > >        exp.X_add_number = 0;
> > > >        addr_size = DWARF2_ADDR_SIZE (stdoutput);
> > > >        emit_expr (&exp, addr_size);
> > >
> > > (C) is still hoisted to (A), but the only use of "exp" in the 
> > > "eh_frame" arm is
> > > (rightly) at (B), which overrides what (A) does.
> > > Please just drop (A) and leave the "else" arm unmodified.
> >
> > Done.
> > >
> > > >> > @@ -1888,7 +2223,17 @@ cfi_finish (void)
> > > >> >
> > > >> >  	  for (fde = all_fde_data; fde ; fde = fde->next)
> > > >> >  	    {
> > > >> > -	      if (SUPPORT_FRAME_LINKONCE)
> > > >> > +	      if ((fde->sections & CFI_EMIT_eh_frame) == 0)
> > > >> > +		continue;
> > > >> > +
> > > >> > +#if SUPPORT_COMPACT_EH
> > > >> > +	      if (fde->eh_header_type == EH_COMPACT_HAS_LSDA)
> > > >> > +		fde->eh_header_type = EH_COMPACT_LEGACY;
> > > >> > +
> > > >> > +	      if (fde->eh_header_type != EH_COMPACT_LEGACY)
> > > >> > +		continue;
> > > >> > +#endif
> > > >>
> > > >> Please add a comment explaining this.
> > >
> > > I don't think you addressed this.
> > >
> >
> > Done.
> > > >> > @@ -1958,6 +2394,9 @@ cfi_finish (void)
> > > >> >
> > > >> >  	  for (fde = all_fde_data; fde ; fde = fde->next)
> > > >> >  	    {
> > > >> > +	      if ((fde->sections & CFI_EMIT_debug_frame) == 0)
> > > >> > +		continue;
> > > >> > +
> > > >> >  	      if (SUPPORT_FRAME_LINKONCE)
> > > >> >  		{
> > > >> >  		  if (HANDLED (fde))
> > > >>
> > > >> Please add a comment explaining why this is needed/correct.
> > >
> > > Same here.
> > >
> > Deferred.

^ permalink raw reply	[flat|nested] 31+ messages in thread

* RE: [Patch] Gas support for MIPS Compact EH
  2015-05-01 13:54               ` FW: " Moore, Catherine
@ 2015-05-05 10:04                 ` Matthew Fortune
  0 siblings, 0 replies; 31+ messages in thread
From: Matthew Fortune @ 2015-05-05 10:04 UTC (permalink / raw)
  To: Moore, Catherine, binutils

Hi Catherine,

For what it's worth I'm reading the spec for this for the first time
and will aim to look through the patch. I won’t be able to approve it
but it may help give some assurance of the patch for other reviewers.

Thanks,
Matthew

> -----Original Message-----
> From: binutils-owner@sourceware.org [mailto:binutils-
> owner@sourceware.org] On Behalf Of Moore, Catherine
> Sent: 01 May 2015 14:55
> To: binutils@sourceware.org
> Subject: FW: [Patch] Gas support for MIPS Compact EH
> 
> Ping.  I'd really like to move forward with this patch.   A large
> portion of the patch is not MIPS-specific, although there are some
> MIPS-specific pieces.
> Would someone be able to review this?
> Thanks,
> Catherine
> 
> -----Original Message-----
> From: binutils-owner@sourceware.org [mailto:binutils-
> owner@sourceware.org] On Behalf Of Moore, Catherine
> Sent: Thursday, April 16, 2015 9:28 AM
> To: Richard Sandiford
> Cc: binutils@sourceware.org
> Subject: RE: [Patch] Gas support for MIPS Compact EH
> 
> Hi Richard,
> Is this patch something that you are going to be able to handle
> sometime soon?
> If not, perhaps one of the other maintainers would be able to review
> this.
> Thanks,
> Catherine
> 
> > -----Original Message-----
> > From: binutils-owner@sourceware.org [mailto:binutils-
> > owner@sourceware.org] On Behalf Of Moore, Catherine
> > Sent: Sunday, February 08, 2015 12:00 PM
> > To: Richard Sandiford
> > Cc: binutils@sourceware.org
> > Subject: RE: [Patch] Gas support for MIPS Compact EH
> >
> > Sorry, forgot the patch in last message.
> >
> > > -----Original Message-----
> > > From: Moore, Catherine
> > > Sent: Sunday, February 08, 2015 11:59 AM
> > > To: 'Richard Sandiford'
> > > Cc: binutils@sourceware.org
> > > Subject: RE: [Patch] Gas support for MIPS Compact EH
> > >
> > > > -----Original Message-----
> > > > From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> > > > Sent: Saturday, November 22, 2014 9:43 AM
> > > > To: Moore, Catherine
> > > > Cc: binutils@sourceware.org
> > > > Subject: Re: [Patch] Gas support for MIPS Compact EH
> > > >
> > > > "Moore, Catherine" <Catherine_Moore@mentor.com> writes:
> > > > > Hi Richard,
> > > > > Please find the updated Compact EH  patch for binutils.
> > > > > This will likely still be a hard patch to review, but I tried
> to
> > > > > address the many comments that you made earlier.
> > > > > The main difference is the exception handling relocation type
> > > > > for the Linux toolchain.  It is no longer gprel based.
> > > > > The ELF and Linux tools both use pcrel32 and the EH-specific
> > > > > relocation type has been removed.
> > > > > I ran into a few issues with the recent changes to eh_frame
> > > > > handling for DWARF, I hopefully covered those with the Compact
> > > > > EH
> > > > implementation.
> > > > > Please let me know what you think.  I hope we are close to
> > > > > converging on an implementation.
> > > >
> > > > OK, thanks.  I started by going through my previous comments and
> > > > looking at how you addressed them in the new patch.  I think
> there
> > > > are still some things that need to be sorted out.
> > > >
> > > > When doing the next patch, could you reply to the main points and
> > > > say how you dealt with them?  That'd make it a lot easier to see
> > > > how this is
> > > evolving.
> > >
> > > Yes, I'll do that.  I'm sorry for the missing pieces of the last
> > > patch.  My
> > rebase
> > > had some unexpected results that I didn't catch before I posted.
> > > Your comment that additional tests would have caught those is
> correct.
> > I've
> > > added some link tests to help out with that.  I think that
> extending
> > > the readelf -u (dump unwind option) would be useful for additional
> > > tests, but I don't want to make that additional development a
> > > requirement for
> > approval
> > > of this patch.  There are still one or two outstanding bits, but
> I'd
> > > like to
> > defer
> > > those until I see your comments on the current patch.
> > > >
> > > > >> -----Original Message-----
> > > > >> From: Richard Sandiford [mailto:rdsandiford@googlemail.com]
> > > > >> Sent: Saturday, February 08, 2014 11:34 AM
> > > > >> To: Moore, Catherine
> > > > >> Cc: binutils@sourceware.org
> > > > >> Subject: Re: [Patch] Gas support for MIPS Compact EH
> > > > >>
> > > > >> Thanks for the updates.
> > > > >>
> > > > >> "Moore, Catherine" <Catherine_Moore@mentor.com> writes:
> > > > >> >> > @@ -4514,6 +4517,23 @@ argument is not present, otherwise
> > > > >> >> > secon or a symbol name.  The default after
> > > > >> >> > @code{.cfi_startproc} is @code{.cfi_lsda 0xff},  no LSDA.
> > > > >> >> >
> > > > >> >> > +@section @code{.cfi_inline_lsda} [@var{align}]
> > > > >> >> > +@code{.cfi_inline_lsda} marks the start of a LSDA data
> > > > >> >> > +section and switches to the corresponding
> > > > >> >> > +@code{.gnu.extab}
> > > section.
> > > > >> >> > +It must be preceded by a CFI block containing a
> > > > >> >> > +@code{.cfi_lsda} directive and is only valid when
> > > > >> >> > +generating
> > > > compact EH frames (i.e.
> > > > >> >> > +with @code{.cfi_sections eh_frame_entry}.
> > > > >> >> > +
> > > > >> >> > +If a compact encoding is being used then the table
> header
> > > > >> >> > +and unwinding opcodes will be generated at this point,
> so
> > > > >> >> > +that they are immediately followed by the LSDA data.
> The
> > > > >> >> > +symbol referenced by the @code{.cfi_lsda} directive
> > > > >> >> > +should still be defined in case a fallback FDE based
> encoding is used.
> > > > >> >> > +
> > > > >> >> > +The optional @var{align} argument specifies the
> alignment
> > > > required.
> > > > >> >> > +The alignment is specified as a power of two, as with
> the
> > > > >> >> > +@code{.p2align} directive.
> > > > >> >>
> > > > >> >> Hmm, switching sections and emitting data feels very
> > > > >> >> different in style from the other .cfi directives, which
> > > > >> >> just annotate code without changing the flow of assembly.
> > > > >> >> I'd like to know other people's
> > > > >> thoughts on this.
> > > > >> >>
> > > > >> >> Also, how do you terminate the LSDA?  The documentation
> > > > >> >> doesn't say (but should :-)) and I couldn't see this
> > > > >> >> directive in the spec
> > either.
> > > > >> >
> > > > >> > The LSDA is terminated by a section switch.
> > > > >>
> > > > >> OK.  Like I say, please mention this in the documentation.
> > > >
> > > > The new version of the patch doesn't have the .cfi_inline_lsda
> > > > documentation that the previous patch had.  Please add it back :-
> )
> > > > But like I say above, please also document how the data is
> terminated.
> > >
> > > It's back.
> > > >
> > > > >> >> TBH, without tests, and without an explanation of what the
> > > > >> >> code is doing, I found this patch pretty hard to review.
> E.g.:
> > > > >> >>
> > > > >> >> > @@ -129,7 +140,12 @@ get_debugseg_name (segT seg, const
> > char
> > > > >> >> >        dot = strchr (name + 1, '.');
> > > > >> >> >
> > > > >> >> >        if (!dollar && !dot)
> > > > >> >> > -	name = "";
> > > > >> >> > +	{
> > > > >> >> > +	  if (compact_eh && strcmp (name, ".text") != 0)
> > > > >> >> > +	    return concat (base_name, ".", name, NULL);
> > > > >> >> > +
> > > > >> >> > +	  name = "";
> > > > >> >> > +	}
> > > > >> >>
> > > > >> >> why is this change needed?  I.e., for a text section called
> > > > >> >> something like .foobar, why does compact_eh need to put
> > > > >> >> things in a section name ending in "..foobar", rather than
> > > > >> >> in the main EH section?  I assume the double dots are
> > > > >> >> deliberate, e.g. to avoid confusion with
> > > > >> ".text.foobar"?
> > > > >> >
> > > > >> > I'm not sure why Paul put this is and the next hunk.  There
> > > > >> > were test failures without.  I can revisit this For the next
> > > > >> > iteration if necessary.
> > > > >>
> > > > >> Yeah, if you could that'd be great.  The code can't really go
> > > > >> in if
> > > > > there's no-
> > > > >> one around who understands what it does.
> > > > >>
> > > > >> I assume it's just to ensure that each text section has its
> own
> > > > >> .eh_frame_entry, but in that case I think we should check
> based
> > > > >> on the base_name rather than compact_eh.  Or do we need the
> > > > >> same treatment for .gnu_extab.  If so, why?
> > > > >>
> > > > >> A comment is needed at the very least.
> > > >
> > > > The new hunk for this is:
> > > >
> > > > > @@ -128,7 +241,15 @@ get_debugseg_name (segT seg, const char
> > > > *base_name)
> > > > >        dot = strchr (name + 1, '.');
> > > > >
> > > > >        if (!dollar && !dot)
> > > > > -	name = "";
> > > > > +	{
> > > > > +	  /* Ensure that a uniquely named .eh_frame_entry section
> > > > > +	     is created for each text section.  */
> > > > > +	  if (compact_eh
> > > > > +	       && !strcmp (base_name, ".eh_frame_entry")
> > > > > +	       && strcmp (name, ".text") != 0)
> > > > > +	    return concat (base_name, ".", name, NULL);
> > > > > +	  name = "";
> > > > > +	}
> > > >
> > > > I don't think we want the compact_eh test here; just the
> base_name
> > > > check should be enough.  (Nit, sorry, but watch the indentation.
> > > > One less space before "&&".)
> > > >
> > > Done.
> > > > >> >> > @@ -833,14 +859,15 @@ dot_cfi_personality (int ignored
> > ATTRIBU
> > > > >> >> >      }
> > > > >> >> >
> > > > >> >> >    if ((encoding & 0xff) != encoding
> > > > >> >> > -      || ((encoding & 0x70) != 0
> > > > >> >> > +      || ((((encoding & 0x70) != 0
> > > > >> >> >  #if CFI_DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr
> > > > >> >> > -	  && (encoding & 0x70) != DW_EH_PE_pcrel
> > > > >> >> > +	   && (encoding & 0x70) != DW_EH_PE_pcrel
> > > > >> >> >  #endif
> > > > >> >> >  	  )
> > > > >> >> > /* leb128 can be handled, but does something actually
> need it?
> > > > > */
> > > > >> >> > -      || (encoding & 7) == DW_EH_PE_uleb128
> > > > >> >> > -      || (encoding & 7) > DW_EH_PE_udata8)
> > > > >> >> > +	   || (encoding & 7) == DW_EH_PE_uleb128
> > > > >> >> > +	   || (encoding & 7) > DW_EH_PE_udata8)
> > > > >> >> > +	&& !tc_cfi_special_encoding (encoding)))
> > > > >> >> >      {
> > > > >> >> >        as_bad (_("invalid or unsupported encoding in
> > > .cfi_personality"));
> > > > >> >> >        ignore_rest_of_line ();
> > > > >> >>
> > > > >> >> What does a "special" encoding mean?  Again, this hook
> > > > >> >> should be documented in internals.texi.  And do we really
> > > > >> >> want to change the set of encodings that are allowed for
> > > > >> >> DWARF, even on MIPS systems that predate compat EH?
> > > > >> >>
> > > > >> > Special means that we have code in the backend to emit a
> > > > >> > reloc for
> > it.
> > > > >> > In the revised patch, it goes along with
> Tc_cfi_reloc_for_encoding.
> > > > >> > It also looks like internals.texi fails to document many of
> > > > >> > the tc_macros and it doesn't appear to be built into a .info
> fiie.
> > > > >>
> > > > >> That's no reason not to document new hooks though.  I know
> I've
> > > > >> used internals.texi to read more about a hook in the past.  If
> > > > >> you don't
> > > > > want to put
> > > > >> it there though then please at least put it in a comment
> instead.
> > > >
> > > > The hunk for this is:
> > > >
> > > > > diff --git a/gas/doc/internals.texi b/gas/doc/internals.texi
> > > > > index
> > > > > cc4089b..9dd0bd0 100644
> > > > > --- a/gas/doc/internals.texi
> > > > > +++ b/gas/doc/internals.texi
> > > > > @@ -1473,6 +1473,10 @@ completed, but before the relocations
> > > > > have
> > > > been generated.
> > > > >  If you define this macro, GAS will call it after the relocs
> > > > > have been generated.
> > > > >
> > > > > +@item tc_cfi_reloc_for_encoding @cindex
> > > > > +tc_cfi_reloc_for_encoding You may define this macro to
> indicate
> > > > > +whether a cfi encoding requires a
> > > > relocation.
> > > >
> > > > That makes it sound like a boolean, whereas it returns a reloc.
> > > > Please also mention that it controls whether compact EH is
> supported.
> > > > The 80- character limit applies to documentation too.
> > > >
> > >
> > > Done.
> > >
> > > > >> >> > +  demand_empty_rest_of_line ();
> > > > >> >> > +  ccseg = CUR_SEG (last_fde);
> > > > >> >> > +  /* Open .gnu_extab section.  */
> > > > >> >> > +  cfi_seg = get_cfi_seg (ccseg, ".gnu_extab",
> > > > >> >> > +			 (SEC_ALLOC | SEC_LOAD | SEC_DATA
> > > > >> >> > +			  | DWARF2_EH_FRAME_READ_ONLY),
> > > > >> >> > +			 1);
> > > > >> >> > +#ifdef md_fix_up_eh_frame
> > > > >> >> > +  md_fix_up_eh_frame (cfi_seg); #else
> > > > >> >> > +  (void) cfi_seg;
> > > > >> >> > +#endif
> > > > >> >> > +
> > > > >> >> > +  frag_align (align, 0, 0);  record_alignment (now_seg,
> > > > >> >> > + align);  if (last_fde->eh_header_type ==
> > > > >> >> > + EH_COMPACT_HAS_LSDA)
> > > > >> >> > +    output_compact_unwind_data (last_fde, align);
> > > > >> >>
> > > > >> >> Please could you explain the EH_COMPACT_LEGACY handling
> > here?
> > > > >> >
> > > > >> > Would you please clarify this question?  I don't see the
> > > > >> > reference to EH_COMPACT_LEGACY.
> > > > >>
> > > > >> That was the problem :-)  Further up there's:
> > > > >>
> > > > >>   if (last_fde->eh_header_type != EH_COMPACT_LEGACY
> > > > >>       && last_fde->eh_header_type != EH_COMPACT_HAS_LSDA)
> > > > >>     {
> > > > >>       as_bad (_(".cfi_inline_lsda seen for frame without
> .cfi_lsda"));
> > > > >>       ignore_rest_of_line ();
> > > > >>       return;
> > > > >>     }
> > > > >>
> > > > >> So what does this code mean/do in the:
> > > > >>
> > > > >>   last_fde->eh_header_type == EH_COMPACT_LEGACY
> > > > >>
> > > > >> case?
> > > >
> > > > Still not sure about this -- please could you clarify?  The
> > > > context is this part of
> > > > dot_cfi_inline_lsda:
> > > >
> > > > > +  if (last_fde->eh_header_type != EH_COMPACT_LEGACY
> > > > > +      && last_fde->eh_header_type != EH_COMPACT_HAS_LSDA)
> > > > > +    {
> > > > > +      as_bad (_(".cfi_inline_lsda seen for frame without
> .cfi_lsda"));
> > > > > +      ignore_rest_of_line ();
> > > > > +      return;
> > > > > +    }
> > > > > +
> > > > > +#ifdef md_flush_pending_output
> > > > > +  md_flush_pending_output ();
> > > > > +#endif
> > > > > +
> > > > > +  align = get_absolute_expression ();  if (align >
> max_alignment)
> > > > > +    {
> > > > > +      align = max_alignment;
> > > > > +      as_bad (_("Alignment too large: %d assumed."), align);
> > > > > +    }
> > > > > +  else if (align < 0)
> > > > > +    {
> > > > > +      as_warn (_("Alignment negative: 0 assumed."));
> > > > > +      align = 0;
> > > > > +    }
> > > > > +
> > > > > +  demand_empty_rest_of_line ();
> > > > > +  ccseg = CUR_SEG (last_fde);
> > > > > +  /* Open .gnu_extab section.  */
> > > > > +  get_cfi_seg (ccseg, ".gnu_extab",
> > > > > +	       (SEC_ALLOC | SEC_LOAD | SEC_DATA
> > > > > +		| DWARF2_EH_FRAME_READ_ONLY),
> > > > > +	       1);
> > > > >
> > > > > +  frag_align (align, 0, 0);
> > > > > +  record_alignment (now_seg, align);  if
> > > > > + (last_fde->eh_header_type == EH_COMPACT_HAS_LSDA)
> > > > > +    output_compact_unwind_data (last_fde, align);
> > > > > +
> > > > > +  last_fde = NULL;
> > > > > +
> > > > > +  return;
> > > >
> > > > The first "if" statement allows the directive to be used for
> > > > EH_COMPACT_LEGACY, but what does the directive mean/do in that
> > case?
> > > >
> > >
> > > I've now added some commentary and extended the definition of the
> > > enumeration to include an EH_COMPACT_UNKNOWN value.
> > > The eh_header_type was never explicitly initialized; it's now
> > > initialized to EH_COMPACT_UNKNOWN.  The EH_COMPACT_LEGACY header
> > > type is
> > then
> > > explicitly set when none of the COMPACT header type cases are
> > discovered.
> > >
> > > > >> > diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index
> > > > >> > 0aab5fa..b7d7df0
> > > > >> > 100644
> > > > >> > --- a/bfd/elf-bfd.h
> > > > >> > +++ b/bfd/elf-bfd.h
> > > > >> > @@ -381,8 +381,10 @@ struct eh_frame_hdr_info  {
> > > > >> >    struct htab *cies;
> > > > >> >    asection *hdr_sec;
> > > > >> > -  unsigned int fde_count, array_count;
> > > > >> > +  unsigned int fde_count, array_count, allocated_entries;
> > > > >> >    struct eh_frame_array_ent *array;
> > > > >> > +  /* eh_frame_entry fragments.  */  asection **entries;
> > > > >> >    /* TRUE if we should try to merge CIEs between input
> sections.  */
> > > > >> >    bfd_boolean merge_cies;
> > > > >> >    /* TRUE if all .eh_frames have been parsd.  */
> > > > >>
> > > > >> I'm not sure there's enough commonality between the DWARF and
> > > > compact
> > > > >> versions to share this structure.  Either we should have
> > > > >> separate structures or use a union to separate out the format-
> specific bits.
> > > >
> > > > I see you've done this, thanks, but:
> > > >
> > > > > +struct dwarf_eh_frame_hdr_info
> > > > > +{
> > > > > +  struct eh_frame_array_ent *array; };
> > > > > +
> > > > > +struct compact_eh_frame_hdr_info {
> > > > > +  unsigned int allocated_entries;
> > > > > +  /* eh_frame_entry_fragments.  */
> > > > > +  asection **entries;
> > > > > +};
> > > > > +
> > > > >  struct eh_frame_hdr_info
> > > > >  {
> > > > >    struct htab *cies;
> > > > >    asection *hdr_sec;
> > > > >    unsigned int fde_count, array_count;
> > > > > -  struct eh_frame_array_ent *array;
> > > > >    /* TRUE if .eh_frame_hdr should contain the sorted search
> table.
> > > > >       We build it if we successfully read all .eh_frame input
> sections
> > > > >       and recognize them.  */
> > > > >    bfd_boolean table;
> > > > > +  bfd_boolean frame_hdr_is_compact;  union
> > > > > +    {
> > > > > +      struct dwarf_eh_frame_hdr_info dwarf;
> > > > > +      struct compact_eh_frame_hdr_info compact;
> > > > > +    }
> > > > > +  u;
> > > > >  };
> > > > >
> > > > >  /* Enum used to identify target specific extensions to the
> > > > > elf_obj_tdata
> > > >
> > > > ...aren't cies, fde_count, array_count and table specific to the
> > > > DWARF version too?  My point was that if we went for the union
> > > > approach, the only fields in the common structure should be those
> > > > that are needed by both formats.  It looks like that's just
> hdr_sec.
> > > >
> > > > If moving all of them seems like too much work, I think we should
> > > > go for separate structures instead.
> > >
> > > I moved them and kept the union.
> > >
> > > >
> > > > >> > +	}
> > > > >> > +
> > > > >> > +      BFD_ASSERT (hdr_info->entries);
> > > > >> > +    }
> > > > >> > +
> > > > >> > +  hdr_info->entries[hdr_info->array_count++] = sec; }
> > > > >> > +
> > > > >> > +/* Parse a .eh_frame_entry section.  Figure out which text
> > > > >> > +section
> > it
> > > > >> > +   references.  */
> > > > >> > +
> > > > >> > +void
> > > > >> > +_bfd_elf_parse_eh_frame_entry (bfd *abfd, struct
> > > > >> > +bfd_link_info
> > > > *info,
> > > > >> > +			       asection *sec, struct elf_reloc_cookie
> > > *cookie,
> > > > >> > +			       bfd_boolean remember)
> > > > >>
> > > > >> This does more than the comment says and the name implies; the
> > > > >> REMEMBER stuff isn't mentioned.
> > > > >>
> > > > >> The patch tries to do the parsing during bfd_elf_discard_info,
> > > > >> but since the parsing wants to be able to fail with an error,
> I
> > > > >> think we need to do it in an earlier pass.  We can then return
> > > > >> a bfd_boolean success code and propagate error returns up,
> > > > >> which the current patch
> > > > doesn't do.
> > > > >> Ideally we'd put the pass somewhere before GC, so that both
> the
> > > > >> GC and bfd_elf_discard_info stages can assume parsed
> > > > >> .eh_frame_entry
> > > > sections.
> > > > >>
> > > > >> Having bfd_elf_discard_info add info (as per REMEMBER == TRUE)
> > > > >> seems a bit counterintuitive.  I think the earlier pass should
> > > > >> record all .eh_frame_entry sections and then the code
> currently
> > > > >> in _bfd_elf_end_eh_frame_parsing (but see below) should remove
> > > > unwanted
> > > > >> entries from the eh_frame_hdr_info array.
> > > >
> > > > I see you've added the earlier pass, thanks, but errors aren't
> > > > always reported back up.  The function:
> > > >
> > > > > +/* Parse a .eh_frame_entry section.  Figure out which text
> section it
> > > > > +   references.  */
> > > > > +
> > > > > +void
> > > > > +_bfd_elf_parse_eh_frame_entry (struct bfd_link_info *info,
> > > > > +			       asection *sec, struct elf_reloc_cookie
> *cookie) {
> > > > > [...]
> > > > > +fail:
> > > > > +  (*_bfd_error_handler) (_("%B: failed to process
> > > > > +.eh_frame_entry"),
> > > > > +sec->owner); }
> > > >
> > > > doesn't tell the caller (and eventually the ld code) that a
> > > > problem
> > occured.
> > > > The function ought to return a bfd_boolean success code, like its
> callers.
> > > > Also, the error here seems to duplicate the eventual ld one:
> > > >
> > > >     einfo (_("%P%F: Failed to parse EH frame entries.\n"));
> > > >
> > > > without really providing any more information.
> > > >
> > > > Don't shoot me, but it would probably be cleaner to make this an
> > > > ELF-specific pass and call it from elf32.em instead.  That'll
> > > > avoid having to do all the aout, bout, etc. stuff and would allow
> > > > the ld code to refer to ELFisms like .eh_frame_entry itself.
> > > >
> > > Okay, I've now moved this to elf32.em.  I fixed the error handling
> as well.
> > >
> > > > >> > +/* Finish a pass over all .eh_frame and eh_frame_entry
> sections.
> > > > >> > +*/
> > > > >> > +
> > > > >> > +bfd_boolean
> > > > >> >  _bfd_elf_end_eh_frame_parsing (struct bfd_link_info *info)
> {
> > > > >> >    struct eh_frame_hdr_info *hdr_info;
> > > > >> > +  unsigned int i;
> > > > >> >
> > > > >> >    hdr_info = &elf_hash_table (info)->eh_info;
> > > > >> >    hdr_info->parsed_eh_frames = TRUE;
> > > > >> > +
> > > > >> > +  if (hdr_info->array_count == 0 || info->eh_frame_hdr < 2)
> > > > >> > +    return FALSE;
> > > > >> > +
> > > > >> > +  qsort (hdr_info->entries, hdr_info->array_count,
> > > > >> > +	 sizeof (asection *), cmp_eh_frame_hdr);
> > > > >> > +
> > > > >> > +  for (i = 0; i < hdr_info->array_count - 1; i++)
> > > > >> > +    {
> > > > >> > +      add_eh_frame_hdr_terminator (hdr_info->entries[i],
> > > > >> > +				   hdr_info->entries[i + 1]);
> > > > >> > +    }
> > > > >> > +
> > > > >> > +  /* Add a CANTUNWIND terminator after the last entry.  */
> > > > >> > + add_eh_frame_hdr_terminator (hdr_info->entries[i], NULL);
> > > > >> > + return TRUE;
> > > > >>
> > > > >> This routine is called from both bfd_elf_gc_sections and
> > > > >> bfd_elf_discard_info but I think you only want it for
> > > bfd_elf_discard_info.
> > > > >> So perhaps this should be a separate function.
> > > > >>
> > > > >> I wonder whether we could instead insert CANTUNWINDs earlier
> > > > >> (say in the non-dynamic part of size_dynamic_sections) based
> on
> > > > >> the link
> > > order.
> > > > >> I.e. rather than comparing the start and end addresses of two
> > > > >> sections, we could just walk the sections in the order that
> > > > >> they're going to
> > > > be linked.
> > > > >>
> > > > >> It sounds like doing it that way would be more direct and more
> > efficient.
> > > > >> Returning TRUE here forces the linker to map sections twice.
> > > >
> > > > Note that the generic version of _bfd_elf_end_eh_frame_parsing
> was
> > > > removed in the meantime.  Your patch adds it back, but I can't
> see
> > > > where it gets called.  Unless I'm missing something, this
> suggests
> > > > that the tests don't exercise this code and that extra tests
> might
> > > > be
> > > needed.
> > >
> > > I've now added link-time tests and the call
> > _bfd_elf_end_eh_frame_parsing
> > > has been restored.
> > > >
> > > > >> > @@ -1271,6 +1465,45 @@ _bfd_elf_eh_frame_present (struct
> > > > >> bfd_link_info *info)
> > > > >> >    return FALSE;
> > > > >> >  }
> > > > >> >
> > > > >> > +/* Return true if there is at least one .eh_frame_entry
> section in
> > > > >> > +   input files.  */
> > > > >> > +bfd_boolean
> > > > >> > +_bfd_elf_eh_frame_entry_present (struct bfd_link_info
> *info)
> > > > >> > +{
> > > > >> > +  asection *o;
> > > > >> > +  bfd *abfd;
> > > > >> > +
> > > > >> > +  for (abfd = info->input_bfds; abfd != NULL; abfd = abfd-
> > >link_next)
> > > > >> > +    {
> > > > >> > +      for (o = abfd->sections; o; o = o->next)
> > > > >> > +	{
> > > > >> > +	  const char *name = bfd_get_section_name (abfd, o);
> > > > >> > +
> > > > >> > +	  if (strcmp (name, ".eh_frame_entry")
> > > > >> > +	      && !bfd_is_abs_section (o->output_section))
> > > > >> > +	    {
> > > > >> > +	      if (strcmp (o->output_section->name,
> ".eh_frame_hdr"))
> > > > >> > +		return TRUE;
> > > > >> > +	      else
> > > > >> > +		{
> > > > >> > +		  (*_bfd_error_handler)
> > > > >> > +			(_("error: an '.eh_frame_entry'"
> > > > >> > +			   " input section that is not mapped to the"
> > > > >> > +			   " '.eh_frame_hdr' output section was
> > > seen"));
> > > > >> > +		  (*_bfd_error_handler)
> > > > >> > +			(_("note: try adding '*(.eh_frame_entry"
> > > > >> > +			   " .eh_frame_entry.*)' to the
> > > '.eh_frame_hdr'"
> > > > >> > +			   " output section description in the
> linker"
> > > > >> > +			   " script"));
> > > > >> > +		  bfd_set_error (bfd_error_invalid_operation);
> > > > >> > +		  return FALSE;
> > > > >> > +		}
> > > > >> > +	    }
> > > > >> > +	}
> > > > >> > +    }
> > > > >> > +  return FALSE;
> > > > >>
> > > > >> The caller can't tell "FALSE because an error was reported"
> > > > >> from "FALSE because there was no .eh_frame_entry".  We should
> > > > >> separate out the two cases and propagate error returns up.
> > > >
> > > > It looks like you dealt with this by removing the error checking.
> > > > I think we still want it, but it should be done separately and in
> > > > a context where the error can be propagated.
> > >
> > > I'm deferring this for now.
> > > >
> > > > >> > @@ -1387,6 +1636,71 @@ _bfd_elf_eh_frame_section_offset (bfd
> > > > >> *output_bfd ATTRIBUTE_UNUSED,
> > > > >> >  	  + extra_augmentation_data_bytes (sec_info->entry +
> > mid));  }
> > > > >> >
> > > > >> > +/* Write out .eh_frame_entry section.  Add CANTUNWIND
> > > terminator
> > > > >> > +if
> > > > >> needed.
> > > > >> > +   Also check that the contents look sane.  */
> > > > >> > +
> > > > >> > +bfd_boolean
> > > > >> > +_bfd_elf_write_section_eh_frame_entry (bfd *abfd,
> > > > >> > +				       asection *sec,
> > > > >> > +				       bfd_byte *contents)
> > > > >>
> > > > >> Formatting: first two arguments fit on a line.
> > > >
> > > > Still present.
> > > Fixed.
> > > >
> > > > >> > +  text_sec = (asection *) elf_section_data (sec)->sec_info;
> > > > >> > + addr = text_sec->output_section->vma + text_sec-
> > >output_offset
> > > > >> > +	 + text_sec->size;
> > > > >> > +  addr &= ~1;
> > > > >> > +  addr -= (sec->output_section->vma + sec->output_offset +
> > > > >> > +sec->rawsize);
> > > > >> > +  BFD_ASSERT ((addr & 1) == 0);
> > > > >>
> > > > >> It looks like this could trigger for odd-sized input text
> sections.
> > > > >> I think it should be an error instead of an assert.
> > > >
> > > > I see you've done this with:
> > > >
> > > > > +  if (addr & 1)
> > > > > +    {
> > > > > +      (*_bfd_error_handler) (_("%B: %s invalid input section
> size"),
> > > > > +			     sec->owner, sec->name);
> > > > > +      return FALSE;
> > > > > +    }
> > > > > +  if (last_addr >= addr + sec->rawsize)
> > > > > +    {
> > > > > +      (*_bfd_error_handler) (_("%B: %s points past end of text
> > section"),
> > > > > +			     sec->owner, sec->name);
> > > > > +      return FALSE;
> > > > > +    }
> > > >
> > > > but I think we want "bfd_set_error (bfd_error_bad_value);" too.
> > >
> > > Done.
> > > >
> > > > >> > +  if (last_addr >= addr + sec->rawsize)
> > > > >> > +    {
> > > > >> > +      (*_bfd_error_handler) (_("%B: %s points past end of
> > > > >> > + text
> > > section"),
> > > > >> > +			     sec->owner, sec->name);
> > > > >> > +      return FALSE;
> > > > >> > +    }
> > > > >> > +
> > > > >> > +  if (sec->size == sec->rawsize)
> > > > >> > +    return TRUE;
> > > > >> > +
> > > > >> > +  BFD_ASSERT (sec->size == sec->rawsize + 8);  BFD_ASSERT
> > > > >> > + ((addr &
> > > > >> > + 1) == 0);
> > > > >> > +  bfd_put_32 (abfd, addr, cantunwind);
> > > > >> > +  bfd_put_32 (abfd, 0x015d5d01, cantunwind + 4);  return
> > > > >> > + bfd_set_section_contents (abfd, sec->output_section,
> > > > >> cantunwind,
> > > > >> > +				   sec->output_offset + sec->rawsize,
> > > 8);
> > > > >>
> > > > >> ARM and c6x seem to use 0 rather than 0x5d as the "no unwind"
> > > > >> opcode, is that right?  If so, I think this should be a hook.
> > > >
> > > > It doesn't look like you addressed this part.
> > >
> > > It looked to me like those ports used 1?  In any case, I've added
> > > the hook,
> > but
> > > I haven't updated the arm and c6x ports to use it.
> > > >
> > > > >> The decision about whether to insert the CANTUNWIND is made
> > during
> > > > >> bfd_elf_discard_info, but addresses can change after that
> “thanks”
> > > > >> to relaxation.  So this could in principle end up emitting a
> > > > >> CANTUNWIND for the same address as the following text section.
> > > >
> > > > As above, it looks like nothing ever calls
> > > > _bfd_elf_write_section_eh_frame_entry in the new version of the
> > patch.
> > > > If the tests don't pick that up then I think we need some more :-
> )
> > > >
> > > Sorry about this missing bit.  More tests have been added and the
> > > call to
> > this
> > > function has been restored.
> > >
> > > > >> > @@ -10039,6 +10040,10 @@ elf_link_input_bfd (struct
> > > > >> > elf_final_link_info
> > > > >> *flinfo, bfd *input_bfd)
> > > > >> >  	      return FALSE;
> > > > >> >  	  }
> > > > >> >  	  break;
> > > > >> > +	case SEC_INFO_TYPE_EH_FRAME_ENTRY:
> > > > >> > +	    if (! _bfd_elf_write_section_eh_frame_entry
> > > > >> > +(output_bfd,
> > > o,
> > > > >> contents))
> > > > >> > +	      return FALSE;
> > > > >> > +	  break;
> > > > >>
> > > > >> Excess indentation of the "if".
> > > >
> > > > Not sure: why is this no longer in the patch?
> > > Restored.
> > > >
> > > > >> > @@ -11807,6 +11814,13 @@ _bfd_elf_gc_mark (struct
> > > > >> > bfd_link_info
> > > > *info,
> > > > >> >  	}
> > > > >> >      }
> > > > >> >
> > > > >> > +  eh_frame = elf_section_eh_frame_entry (sec);  if
> (eh_frame
> > > > >> > + &&
> > > > >> > + !eh_frame->gc_mark)
> > > > >> > +    {
> > > > >> > +    if (!_bfd_elf_gc_mark (info, eh_frame, gc_mark_hook))
> > > > >> > +      return FALSE;
> > > > >> > +    }
> > > > >>
> > > > >> In this context we should be using "ret":
> > > > >>
> > > > >>   eh_frame = elf_section_eh_frame_entry (sec);
> > > > >>   if (ret && eh_frame && !eh_frame->gc_mark)
> > > > >>     ret = _bfd_elf_gc_mark (info, eh_frame, gc_mark_hook);
> > > >
> > > > You changed this to:
> > > >
> > > > > +  if (eh_frame && !eh_frame->gc_mark)
> > > > > +    {
> > > > > +      if (!_bfd_elf_gc_mark (info, eh_frame, gc_mark_hook))
> > > > > +	ret = FALSE;
> > > > > +    }
> > > >
> > > > but the point was also that we don't want to try another mark
> once "ret"
> > > > is already FALSE.  The "ret &&" part of the condition was
> important.
> > > Another rebase problem.  Now fixed.
> > > >
> > > > >> > @@ -12190,22 +12204,42 @@ bfd_elf_gc_sections (bfd *abfd,
> > > > >> > struct
> > > > >> bfd_link_info *info)
> > > > >> >    bed->gc_keep (info);
> > > > >> >
> > > > >> >    /* Try to parse each bfd's .eh_frame section.  Point
> > > > >> elf_eh_frame_section
> > > > >> > -     at the .eh_frame section if we can mark the FDEs
> individually.  */
> > > > >> > +     at the .eh_frame section if we can mark the FDEs
> individually.
> > > > >> > +     Establish links from text sections to their
> corresponding
> > > > >> > +     .eh_frame_entry sections.  */
> > > > >> >    _bfd_elf_begin_eh_frame_parsing (info);
> > > > >> >    for (sub = info->input_bfds; sub != NULL; sub = sub-
> >link_next)
> > > > >> >      {
> > > > >> >        asection *sec;
> > > > >> >        struct elf_reloc_cookie cookie;
> > > > >> >
> > > > >> > -      sec = bfd_get_section_by_name (sub, ".eh_frame");
> > > > >> > -      while (sec && init_reloc_cookie_for_section (&cookie,
> info,
> > sec))
> > > > >> > +      if (!init_reloc_cookie (&cookie, info, sub))
> > > > >> > +	return FALSE;
> > > > >> > +
> > > > >> > +      if (info->eh_frame_hdr < 2)
> > > > >> >  	{
> > > > >> > -	  _bfd_elf_parse_eh_frame (sub, info, sec, &cookie);
> > > > >> > -	  if (elf_section_data (sec)->sec_info
> > > > >> > -	      && (sec->flags & SEC_LINKER_CREATED) == 0)
> > > > >> > -	    elf_eh_frame_section (sub) = sec;
> > > > >> > -	  fini_reloc_cookie_for_section (&cookie, sec);
> > > > >> > -	  sec = bfd_get_next_section_by_name (sec);
> > > > >> > +	  sec = bfd_get_section_by_name (sub, ".eh_frame");
> > > > >> > +	  if (sec && init_reloc_cookie_rels (&cookie, info, sub,
> sec))
> > > > >> > +	    {
> > > > >> > +	      _bfd_elf_parse_eh_frame (sub, info, sec, &cookie);
> > > > >> > +	      if (elf_section_data (sec)->sec_info
> > > > >> > +		  && (sec->flags & SEC_LINKER_CREATED) == 0)
> > > > >> > +		elf_eh_frame_section (sub) = sec;
> > > > >> > +	      fini_reloc_cookie_for_section (&cookie, sec);
> > > > >> > +	    }
> > > > >> > +	}
> > > > >> > +      else
> > > > >> > +	{
> > > > >> > +	  for (sec = sub->sections; sec; sec = sec->next)
> > > > >> > +	    {
> > > > >> > +	      if (CONST_STRNEQ (bfd_section_name (sub, sec),
> > > > >> ".eh_frame_entry")
> > > > >> > +		  && init_reloc_cookie_rels (&cookie, info, sub,
> sec))
> > > > >> > +		{
> > > > >> > +		  _bfd_elf_parse_eh_frame_entry (sub, info, sec,
> > > &cookie,
> > > > >> > +						 FALSE);
> > > > >> > +		  fini_reloc_cookie_for_section (&cookie, sec);
> > > > >> > +		}
> > > > >> > +	    }
> > > > >>
> > > > >> This changes the info->eh_frame_hdr != 2 case to only handle
> > > > >> the first .eh_frame section.  ("while" -> "if").
> > > > >>
> > > > >> Also, the init/fini_reloc_cookie* calls don't match up.  I
> > > > >> assume the
> > > > > idea is to
> > > > >> avoid excessive allocation and freeing of the locsyms, but in
> > > > >> that case you should use fini_reloc_cookie_rels instead of
> > > > >> fini_reloc_cookie_for_section and call fini_reloc_cookie at
> the end.
> > > > >> At the moment I think this leaks memory if there are no EH
> sections.
> > > > >>
> > > > >> I don't know either way whether splitting the
> > > > >> init_reloc_cookie_for_section call up is a win or not for
> .eh_frame.
> > > > >> It will be a win if there are
> > > > > multiple EH
> > > > >> sections but a loss if there are none, since we then
> initialise
> > > > >> and
> > > > > free the bfd-
> > > > >> level information unnecessarily.  So I think it might make
> > > > >> sense to keep the .eh_frame code as it is now and restrict the
> > > > >> *_rels to the new
> > > > code.
> > > >
> > > > You changed this to:
> > > >
> > > > > @@ -12171,7 +12256,9 @@ bfd_elf_gc_sections (bfd *abfd, struct
> > > > > bfd_link_info *info)
> > > > >
> > > > >    /* Try to parse each bfd's .eh_frame section.  Point
> > > > elf_eh_frame_section
> > > > >       at the .eh_frame section if we can mark the FDEs
> individually.
> > > > > */
> > > > > -  for (sub = info->input_bfds; sub != NULL; sub =
> > > > > sub->link.next)
> > > > > +  for (sub = info->input_bfds;
> > > > > +       info->eh_frame_hdr_type != COMPACT_EH_HDR && sub !=
> NULL;
> > > > > +       sub = sub->link.next)
> > > > >      {
> > > > >        asection *sec;
> > > > >        struct elf_reloc_cookie cookie;
> > > >
> > > > where info->eh_frame_hdr_type is an invariant.  I assume this was
> > > > just to avoid reformatting the block, but please use:
> > > >
> > > >   if (info->eh_frame_hdr_type != COMPACT_EH_HDR)
> > > >     for (...)
> > > >
> > > > instead.  Fortunately the block's not that big. :-)
> > >
> > > Done.
> > > >
> > > > >> > diff --git a/gas/config/tc-mips.h b/gas/config/tc-mips.h
> > > > >> > index
> > > > >> > c7eaa04..a300062 100644
> > > > >> > --- a/gas/config/tc-mips.h
> > > > >> > +++ b/gas/config/tc-mips.h
> > > > >> > @@ -177,7 +177,9 @@ extern enum dwarf2_format
> > > > mips_dwarf2_format
> > > > >> > (asection *);
> > > > >> >
> > > > >> >  extern int mips_dwarf2_addr_size (void);  #define
> > > > >> > DWARF2_ADDR_SIZE(bfd) mips_dwarf2_addr_size () -#define
> > > > >> > DWARF2_FDE_RELOC_SIZE mips_dwarf2_addr_size ()
> > > > >> > +#define DWARF2_FDE_RELOC_SIZE (compact_eh ? 4 :
> > > > >> mips_dwarf2_addr_size
> > > > >> > +()) #define DWARF2_FDE_RELOC_ENCODING(enc) \
> > > > >> > +  (enc | (compact_eh ? DW_EH_PE_pcrel : 0))
> > > > >>
> > > > >> What case is this handling?  Please explain this a bit more.
> > > >
> > > > Doesn't look like you've addressed this bit.
> > >
> > > Deferring for now.
> > > >
> > > > >> > @@ -4566,6 +4586,22 @@ argument is not present, otherwise
> > second
> > > > >> > argument should be a constant  or a symbol name.  The
> default
> > > > >> > after @code{.cfi_startproc} is @code{.cfi_lsda 0xff},  no
> LSDA.
> > > > >> >
> > > > >> > +@section @code{.cfi_inline_lsda} [@var{align}]
> > > > >> > +@code{.cfi_inline_lsda} marks the start of a LSDA data
> > > > >> > +section and switches to the corresponding @code{.gnu.extab}
> section.
> > > > >> > +Must be preceded by a CFI block containing a
> > > > >> > +@code{.cfi_lsda}
> > > > directive.
> > > > >> > +Only valid when generating compact EH frames (i.e.
> > > > >> > +with @code{.cfi_sections eh_frame_entry}.
> > > > >>
> > > > >> Missing ")".
> > > >
> > > > As above, this part of the doc is no longer there.
> > >
> > > Restored.
> > > >
> > > > >> > @@ -4643,6 +4679,20 @@ mark a code segment that has only one
> > > > return
> > > > >> > address which is reached  by a direct branch and no copy of
> > > > >> > the return address exists in memory  or another register.
> > > > >> >
> > > > >> > +@section @code{.cfi_epilogue_begin} A pseudo-operation
> which
> > > > marks
> > > > >> > +the beginning of the epilogue in a given function.  This is
> > > > >> > +currently used by the compact unwind-table implementation
> > > > >> > +(for exception handling), which assumes a single register
> > > > >> > +state for unwinding throughout the body of a function.  The
> > > > >> > +function is scanned, and CFI directives are rewritten in a
> compact form.
> > > > >> > +However, recent versions of GCC emit unwind information for
> > > > >> > +function epilogues, which should not be represented in this
> > > > >> > +compact
> > > > >> > +form: hence, any CFI directives which occur in a function
> > > > >> > +after @code{.cfi_epilogue_end} are not processed.
> > > > >> > +
> > > > >> > +This operation only affects the internals of the assembler,
> > > > >> > +and is not represented in the binary output.
> > > > >>
> > > > >> Don't really follow this, sorry.  Can you give an example, or
> > > > >> better yet, a testcase?
> > > >
> > > > Has .cfi_epilogue_begin been dropped?
> > >
> > > Yes.
> > > >
> > > > >>
> > > > >> > @@ -161,6 +278,9 @@ alloc_debugseg_item (segT seg, int
> > > > >> > subseg, char
> > > > >> > *name)  static segT  is_now_linkonce_segment (void)  {
> > > > >> > +  if (compact_eh)
> > > > >> > +    return now_seg;
> > > > >> > +
> > > > >>
> > > > >> Please add a comment explaining why this is correct.
> > > >
> > > > The hunk for this is:
> > > >
> > > > > +  /* We track the current segment in the cfi_insn_data struct
> and
> > > > > +     the cfi_insn_data struct for Compact EH.  */  if
> (compact_eh)
> > > > > +    return now_seg;
> > > >
> > > > but the comment repeats "the cfi_insn_data struct".  TBH I'm
> still
> > > > not sure why/whether this is correct, but let's sort the other
> things out first.
> > >
> > > Okay.
> > > >
> > > > >> > -/* Emit a single byte into the current segment.  */
> > > > >> > -
> > > > >> > -static inline void
> > > > >> > -out_one (int byte)
> > > > >> > +static segT
> > > > >> > +get_cfi_seg (segT cseg, const char *base, flagword flags,
> > > > >> > +int
> > > > >> > +align)
> > > > >> >  {
> > > > >> > -  FRAG_APPEND_1_CHAR (byte);
> > > > >> > +  if (SUPPORT_FRAME_LINKONCE || ((flags & SEC_DEBUGGING) ==
> > 0
> > > > &&
> > > > >> compact_eh))
> > > > >> > +    {
> > > > >>
> > > > >> Please add a comment here too to explain the SEC_DEBUGGING
> test.
> > > >
> > > > It doesn't look like you addressed this bit.
> > > >
> > > > >> > +static void
> > > > >> > +output_compact_unwind_data (struct fde_entry *fde, int
> > > > >> > +align)
> > > > >>
> > > > >> Probably worth a comment here, since it wasn't obvious to me
> > > > >> the "align" is the alignment of the end of the data rather
> than the start.
> > > >
> > > > I don't think you addressed this bit.  (In case it wasn't clear,
> I
> > > > was suggesting adding a function comment that says what the
> > > > function does and what its arguments are, etc.)
> > >
> > > Done.
> > > >
> > > > >> > +	  md_number_to_chars (p, 0, 4);
> > > > >> > +	  fix_new (frag_now, p - frag_now->fr_literal, 4,
> > > exp.X_add_symbol,
> > > > >> > +		   exp.X_add_number, howto->pc_relative, code);
> > > > >>
> > > > >> What's the significance of exp.X_add_number here?  If it was
> > > > >> supposed to be initialised to 0 above then I think it would be
> > > > >> easier to leave the
> > > > "exp"
> > > > >> stuff alone and just use fde->start_address and 0 directly in
> > > > >> this
> > > > > fix_new call.
> > > > >> Certainly...
> > > > >>
> > > > >> >    else
> > > > >> >      {
> > > > >> > -      exp.X_op = O_symbol;
> > > > >> > -      exp.X_add_symbol = fde->start_address;
> > > > >> >        exp.X_add_number = 0;
> > > > >> >        addr_size = DWARF2_ADDR_SIZE (stdoutput);
> > > > >> >        emit_expr (&exp, addr_size);
> > > > >>
> > > > >> ...separating the add_symbol and add_number feels odd.
> > > >
> > > > This is now:
> > > >
> > > > > @@ -1575,29 +1881,43 @@ output_fde (struct fde_entry *fde,
> > > > > struct
> > > > cie_entry *cie,
> > > > >        TC_DWARF2_EMIT_OFFSET (cie->start_address, offset_size);
> > > > >      }
> > > > >
> > > > > +  exp.X_op = O_symbol;
> > > > > +  exp.X_add_symbol = fde->start_address;
> > > >
> > > > (A)
> > > >
> > > > >    if (eh_frame)
> > > > >      {
> > > > > -      exp.X_op = O_subtract;
> > > > > -      exp.X_add_number = 0;
> > > > > +      bfd_reloc_code_real_type code
> > > > > +	= tc_cfi_reloc_for_encoding (cie->fde_encoding);
> > > > > +      if (code !=  BFD_RELOC_NONE)
> > > > > +	{
> > > > > +	  reloc_howto_type *howto = bfd_reloc_type_lookup (stdoutput,
> > > > code);
> > > > > +	  char *p = frag_more (4);
> > > > > +	  md_number_to_chars (p, 0, 4);
> > > > > +	  fix_new (frag_now, p - frag_now->fr_literal, 4, fde-
> >start_address,
> > > > > +		   0, howto->pc_relative, code);
> > > > > +	}
> > > > > +      else
> > > > > +	{
> > > > > +	  exp.X_op = O_subtract;
> > > > > +	  exp.X_add_number = 0;
> > > >
> > > > (B)
> > > >
> > > > >  #if CFI_DIFF_EXPR_OK
> > > > > -      exp.X_add_symbol = fde->start_address;
> > > > > -      exp.X_op_symbol = symbol_temp_new_now ();
> > > > > -      emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code
> > offset.  */
> > > > > +	  exp.X_add_symbol = fde->start_address;
> > > > > +	  exp.X_op_symbol = symbol_temp_new_now ();
> > > > > +	  emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code
> > > > offset.  */
> > > > >  #else
> > > > > -      exp.X_op = O_symbol;
> > > > > -      exp.X_add_symbol = fde->start_address;
> > > > > -#ifdef tc_cfi_emit_pcrel_expr
> > > > > -      tc_cfi_emit_pcrel_expr (&exp, DWARF2_FDE_RELOC_SIZE);	 /*
> > > > Code offset.  */
> > > > > +	  exp.X_op = O_symbol;
> > > > > +	  exp.X_add_symbol = fde->start_address;
> > > > > +
> > > > > +#if defined(tc_cfi_emit_pcrel_expr)
> > > > > +	  tc_cfi_emit_pcrel_expr (&exp, DWARF2_FDE_RELOC_SIZE);	 /*
> > > > Code offset.  */
> > > > >  #else
> > > > > -      emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code
> > offset.  */
> > > > > +	  emit_expr (&exp, DWARF2_FDE_RELOC_SIZE);	/* Code
> > > > offset.  */
> > > > >  #endif
> > > > >  #endif
> > > > > +	}
> > > > >        addr_size = DWARF2_FDE_RELOC_SIZE;
> > > > >      }
> > > > >    else
> > > > >      {
> > > > > -      exp.X_op = O_symbol;
> > > > > -      exp.X_add_symbol = fde->start_address;
> > > >
> > > > (C)
> > > >
> > > > >        exp.X_add_number = 0;
> > > > >        addr_size = DWARF2_ADDR_SIZE (stdoutput);
> > > > >        emit_expr (&exp, addr_size);
> > > >
> > > > (C) is still hoisted to (A), but the only use of "exp" in the
> > > > "eh_frame" arm is
> > > > (rightly) at (B), which overrides what (A) does.
> > > > Please just drop (A) and leave the "else" arm unmodified.
> > >
> > > Done.
> > > >
> > > > >> > @@ -1888,7 +2223,17 @@ cfi_finish (void)
> > > > >> >
> > > > >> >  	  for (fde = all_fde_data; fde ; fde = fde->next)
> > > > >> >  	    {
> > > > >> > -	      if (SUPPORT_FRAME_LINKONCE)
> > > > >> > +	      if ((fde->sections & CFI_EMIT_eh_frame) == 0)
> > > > >> > +		continue;
> > > > >> > +
> > > > >> > +#if SUPPORT_COMPACT_EH
> > > > >> > +	      if (fde->eh_header_type == EH_COMPACT_HAS_LSDA)
> > > > >> > +		fde->eh_header_type = EH_COMPACT_LEGACY;
> > > > >> > +
> > > > >> > +	      if (fde->eh_header_type != EH_COMPACT_LEGACY)
> > > > >> > +		continue;
> > > > >> > +#endif
> > > > >>
> > > > >> Please add a comment explaining this.
> > > >
> > > > I don't think you addressed this.
> > > >
> > >
> > > Done.
> > > > >> > @@ -1958,6 +2394,9 @@ cfi_finish (void)
> > > > >> >
> > > > >> >  	  for (fde = all_fde_data; fde ; fde = fde->next)
> > > > >> >  	    {
> > > > >> > +	      if ((fde->sections & CFI_EMIT_debug_frame) == 0)
> > > > >> > +		continue;
> > > > >> > +
> > > > >> >  	      if (SUPPORT_FRAME_LINKONCE)
> > > > >> >  		{
> > > > >> >  		  if (HANDLED (fde))
> > > > >>
> > > > >> Please add a comment explaining why this is needed/correct.
> > > >
> > > > Same here.
> > > >
> > > Deferred.

^ permalink raw reply	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2015-05-05 10:04 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-05-31 18:56 [Patch] Gas support for MIPS Compact EH Moore, Catherine
2013-06-01 11:07 ` Richard Sandiford
2014-02-04 21:56   ` Moore, Catherine
2014-02-05 22:38     ` Maciej W. Rozycki
2014-02-08 16:34     ` Richard Sandiford
2014-02-08 18:48       ` Bernd Schmidt
2014-02-09 11:11         ` Richard Sandiford
2014-02-19 23:13           ` Moore, Catherine
2014-02-20 11:36             ` Richard Sandiford
2014-02-20 21:24               ` Moore, Catherine
2014-02-20 22:39                 ` Richard Sandiford
2014-02-24 20:54                   ` Moore, Catherine
2014-02-24 20:48               ` Moore, Catherine
2014-02-25  8:29                 ` Richard Sandiford
2014-03-17 13:22                   ` Moore, Catherine
2014-03-17 13:52                     ` Richard Sandiford
2014-03-19 21:12                       ` Moore, Catherine
2014-03-19 23:45                         ` Richard Sandiford
2014-03-20 14:29                           ` Moore, Catherine
2014-03-20 21:19                             ` Richard Sandiford
2014-03-06 17:44       ` Moore, Catherine
2014-03-06 22:18         ` Richard Sandiford
2014-03-25 13:50       ` Moore, Catherine
2014-03-25 14:06         ` Richard Sandiford
2014-11-17 16:10       ` Moore, Catherine
2014-11-22 14:42         ` Richard Sandiford
2015-02-08 16:59           ` Moore, Catherine
2015-02-08 17:00           ` Moore, Catherine
2015-04-16 13:28             ` Moore, Catherine
2015-05-01 13:54               ` FW: " Moore, Catherine
2015-05-05 10:04                 ` Matthew Fortune

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).