[PATCH] Optimize .eh_frame sections in ld, create PT_GNU_EH_FRAME ELF segment

Jakub Jelinek jakub@redhat.com
Mon Dec 10 08:45:00 GMT 2001


Hi!

The following both optimizes .eh_frame sections and allows
non-__register_frame_info based EH registration on targets other than IA-64
(that's why I CCed gcc list too).

If ld is invoked with --eh-frame-hdr, it:
1) scans all input sections, for each one if it understands it fully:
   - if CIE is identical to previous CIE, it is removed and all FDEs
     get their CIE offsets adjusted (note also last CIE from last processed
     .eh_frame section before current one is taken into account)
   - if FDEs initial location points into discarded linkonce section,
     the whole FDE is deleted
   - if all FDEs referencing a CIE are removed, CIE is removed too
   - if compiler emitted 'R' augmentation and FDE encoding is still
     absolute, converts it into relative (this is for arches which have
     no appropriate relocs for pcrel encoding; note that gcc would need
     to put 'R' augmentation with DW_EH_absptr, as appending R to
     augmentation and adding one field would make ld code too complicated
     IMHO). Note that code for this is currently disabled, since it requires
     some ELF backend changes (basically performing relocation even if skip
     == true (= dynamic relocation has been cleared), while some backends
     just skip _bfd_final_link_relocate altogether in this case) and I
     wanted to keep this patch small
2) if there were some non-trivial input .eh_frame section (ie. other than
   CIE with length 0), creates .eh_frame_hdr section
3) if all input sections were understood, it will build a sorted table
   at the end of .eh_frame_hdr section
   (Richard complained that it the sorted table can be built even if
    .eh_frame sections are not fully understood, I looked into it quickly
    and most places really need to be understood, what could be perhaps
    later allowed is that if there are some unknown augmentation letters
    and R augmentation is either not present or present before all unknown
    augmentations, just CIE merging would be disabled and table could be
    still created)
4) add PT_GNU_EH_FRAME Phdr if .eh_frame_hdr section was created, containing
   just that section

Thus, if --eh-frame-hdr option is passed to the linker during final link,
.eh_frame_hdr is guaranteed to be present (provided there is at least one
FDE in .eh_frame sections) and contain at least address of .eh_frame section
(and, if possible, a sorted table for binary search), so gcc can avoid
__{,de}register*info* calls.

Notes:
- PT_GNU_EH_FRAME value was picked from PT_LOOS..PT_HIOS range as
PT_LOOS + ('G' << 20) + ('N' << 12) + ('U' << 4) + 0, if you have better ideas,
let me know.

- .gcc_exception_table portions related to removed FDEs are not yet
  removed, would like to do that in a separate patch later

I'll write the gcc part of this if this patch is accepted.

2001-12-10  Jakub Jelinek  <jakub@redhat.com>

	* elf-bfd.h (enum elf_link_info_type): New.
	(struct bfd_elf_section_data): Remove stab_info and merge_info
	fields, add sec_info and sec_info_type.
	(struct elf_obj_tdata): Add eh_frame_hdr field.
	(_bfd_elf_discard_section_eh_frame): New prototype.
	(_bfd_elf_discard_section_eh_frame_hdr): Likewise.
	(_bfd_elf_eh_frame_section_offset): Likewise.
	(_bfd_elf_write_section_eh_frame): Likewise.
	(_bfd_elf_write_section_eh_frame_hdr): Likewise.
	* Makefile.am (BFD32_BACKENDS): Add elf-eh-frame.lo.
	(BFD32_BACKENDS_CFILES): Add elf-eh-frame.c.
	(elf-eh-frame.lo): New.
	* Makefile.in: Rebuilt.
	* configure.in (elf): Add elf-eh-frame.lo.
	* configure: Rebuilt.
	* elf.c (_bfd_elf_print_private_bfd_data): Support PT_GNU_EH_FRAME.
	(map_sections_to_segments): Create PT_GNU_EH_FRAME if requested.
	(get_program_header_size): Take into account PT_GNU_EH_FRAME
	segment.
	(_bfd_elf_rela_local_sym): Use sec_info_type and sec_info.
	(_bfd_elf_rel_local_sym): Likewise.
	(_bfd_elf_section_offset): Likewise.  Call
	_bfd_elf_eh_frame_section_offset too.
	* elfxx-ia64.c (elfNN_ia64_relocate_section): Use sec_info_type and
	sec_info.
	* elf64-alpha.c (elf64_alpha_relocate_section): Likewise.
	* elf-eh-frame.c: New file.
	* elflink.h (elf_link_add_object_symbols): Don't optimize SHF_MERGE
	.stab sections.  Set sec_info_type, use sec_info instead
	of merge_info and stab_info.
	(elf_link_create_dynamic_sections): Create .eh_frame_hdr section
	if --eh-frame-hdr.
	(elf_bfd_final_link): Write .eh_frame_hdr section.
	(elf_link_sec_merge_syms): Use sec_info_type and sec_info.
	(elf_link_input_bfd): Likewise.
	Call _bfd_elf_write_section_eh_frame to write .eh_frame sections.
	(elf_bfd_discard_info): Add output_bfd argument.
	Call _bfd_elf_discard_section_eh_frame and
	_bfd_elf_discard_section_eh_frame_hdr.
	(elf_section_ignore_discarded_relocs): Use sec_info_type, not section
	names.
	* bfd-in.h (bfd_elf32_discard_info, bfd_elf64_discard_info): Adjust
	prototypes.
	* bfd-in2.h (bfd_elf32_discard_info, bfd_elf64_discard_info): Likewise.

	* elf/common.h (PT_GNU_EH_FRAME): Define.
	* bfdlink.h (struct bfd_link_info): Add eh_frame_hdr field.

	* emultempl/elf32.em (finish): Supply output_bfd
	to bfd_elf*_discard_info.
	(OPTION_EH_FRAME_HDR): Define.
	(longopts): Add --eh-frame-hdr.
	(parse_args): Handle it.
	(list_options): Add --eh-frame-hdr to help.
	* emultempl/hppaelf.em (finish): Supply output_bfd
	to bfd_elf*_discard_info.
	* scripttempl/elf.sc (.eh_frame_hdr): Add.

	* readelf.c (get_segment_type): Support PT_GNU_EH_FRAME.

--- binutils/readelf.c.jj	Fri Dec  7 12:59:51 2001
+++ binutils/readelf.c	Mon Dec 10 14:38:19 2001
@@ -1820,6 +1820,9 @@ get_segment_type (p_type)
     case PT_SHLIB:      return "SHLIB";
     case PT_PHDR:       return "PHDR";
 
+    case PT_GNU_EH_FRAME:
+			return "GNU_EH_FRAME";
+
     default:
       if ((p_type >= PT_LOPROC) && (p_type <= PT_HIPROC))
 	{
--- bfd/elf.c.jj	Fri Dec  7 13:22:19 2001
+++ bfd/elf.c	Mon Dec 10 15:18:40 2001
@@ -812,6 +812,7 @@ _bfd_elf_print_private_bfd_data (abfd, f
 	    case PT_NOTE: pt = "NOTE"; break;
 	    case PT_SHLIB: pt = "SHLIB"; break;
 	    case PT_PHDR: pt = "PHDR"; break;
+	    case PT_GNU_EH_FRAME: pt = "EH_FRAME"; break;
 	    default: sprintf (buf, "0x%lx", p->p_type); pt = buf; break;
 	    }
 	  fprintf (f, "%8s off    0x", pt);
@@ -2794,7 +2795,7 @@ map_sections_to_segments (abfd)
   asection **hdrpp;
   boolean phdr_in_segment = true;
   boolean writable;
-  asection *dynsec;
+  asection *dynsec, *eh_frame_hdr;
   bfd_size_type amt;
 
   if (elf_tdata (abfd)->segment_map != NULL)
@@ -3034,6 +3035,24 @@ map_sections_to_segments (abfd)
 	}
     }
 
+  /* If there is a .eh_frame_hdr section, throw in a PT_GNU_EH_FRAME
+     segment.  */
+  eh_frame_hdr = bfd_get_section_by_name (abfd, ".eh_frame_hdr");
+  if (eh_frame_hdr != NULL && (eh_frame_hdr->flags & SEC_LOAD))
+    {
+      amt = sizeof (struct elf_segment_map);
+      m = (struct elf_segment_map *) bfd_zalloc (abfd, amt);
+      if (m == NULL)
+	goto error_return;
+      m->next = NULL;
+      m->p_type = PT_GNU_EH_FRAME;
+      m->count = 1;
+      m->sections[0] = eh_frame_hdr;
+
+      *pm = m;
+      pm = &m->next;
+    }
+
   free (sections);
   sections = NULL;
 
@@ -3550,6 +3569,13 @@ get_program_header_size (abfd)
       ++segs;
     }
 
+  if (elf_tdata (abfd)->eh_frame_hdr
+      && bfd_get_section_by_name (abfd, ".eh_frame_hdr") != NULL)
+    {
+      /* We need a PT_GNU_EH_FRAME segment.  */
+      ++segs;
+    }
+
   for (s = abfd->sections; s != NULL; s = s->next)
     {
       if ((s->flags & SEC_LOAD) != 0
@@ -6394,14 +6420,14 @@ _bfd_elf_rela_local_sym (abfd, sym, sec,
 		+ sym->st_value);
   if ((sec->flags & SEC_MERGE)
       && ELF_ST_TYPE (sym->st_info) == STT_SECTION
-      && elf_section_data (sec)->merge_info)
+      && elf_section_data (sec)->sec_info_type == ELF_INFO_TYPE_MERGE)
     {
       asection *msec;
 
       msec = sec;
       rel->r_addend =
 	_bfd_merged_section_offset (abfd, &msec,
-				    elf_section_data (sec)->merge_info,
+				    elf_section_data (sec)->sec_info,
 				    sym->st_value + rel->r_addend,
 				    (bfd_vma) 0)
 	- relocation;
@@ -6419,11 +6445,11 @@ _bfd_elf_rel_local_sym (abfd, sym, psec,
 {     
   asection *sec = *psec;
 
-  if (elf_section_data (sec)->merge_info == NULL)
+  if (elf_section_data (sec)->sec_info_type != ELF_INFO_TYPE_MERGE)
     return sym->st_value + addend;
 
   return _bfd_merged_section_offset (abfd, psec,
-				     elf_section_data (sec)->merge_info,
+				     elf_section_data (sec)->sec_info,
 				     sym->st_value + addend, (bfd_vma) 0);
 }
 
@@ -6437,9 +6463,15 @@ _bfd_elf_section_offset (abfd, info, sec
   struct bfd_elf_section_data *sec_data;
 
   sec_data = elf_section_data (sec);
-  if (sec_data->stab_info != NULL)
-    return _bfd_stab_section_offset
-	   (abfd, &elf_hash_table (info)->stab_info,
-	    sec, &sec_data->stab_info, offset);
-  return offset;
+  switch (sec_data->sec_info_type)
+    {
+    case ELF_INFO_TYPE_STABS:
+      return _bfd_stab_section_offset
+	(abfd, &elf_hash_table (info)->merge_info, sec, &sec_data->sec_info,
+	 offset);
+    case ELF_INFO_TYPE_EH_FRAME:
+      return _bfd_elf_eh_frame_section_offset (abfd, sec, offset);
+    default:
+      return offset;
+    }
 }
--- bfd/elfxx-ia64.c.jj	Fri Dec  7 13:22:19 2001
+++ bfd/elfxx-ia64.c	Fri Dec  7 13:32:50 2001
@@ -3489,7 +3489,8 @@ elfNN_ia64_relocate_section (output_bfd,
 	  value = _bfd_elf_rela_local_sym (output_bfd, sym, sym_sec, rel);
 	  if ((sym_sec->flags & SEC_MERGE)
 	      && ELF_ST_TYPE (sym->st_info) == STT_SECTION
-	      && elf_section_data (sym_sec)->merge_info)
+	      && elf_section_data (sym_sec)->sec_info_type
+		 == ELF_INFO_TYPE_MERGE)
  	    {
 	      struct elfNN_ia64_local_hash_entry *loc_h;
       
@@ -3505,7 +3506,7 @@ elfNN_ia64_relocate_section (output_bfd,
 		      dynent->addend =
 			_bfd_merged_section_offset (output_bfd, &msec,
 						    elf_section_data (msec)->
-						    merge_info,
+						    sec_info,
 						    sym->st_value
 						    + dynent->addend,
 						    (bfd_vma) 0);
--- bfd/elf64-alpha.c.jj	Fri Dec  7 13:22:19 2001
+++ bfd/elf64-alpha.c	Fri Dec  7 13:32:50 2001
@@ -3406,7 +3406,8 @@ elf64_alpha_relocate_section (output_bfd
 		   unless it has been done already.  */
 		if ((sec->flags & SEC_MERGE)
 		    && ELF_ST_TYPE (sym->st_info) == STT_SECTION
-		    && elf_section_data (sec)->merge_info
+		    && elf_section_data (sec)->sec_info_type
+		       == ELF_INFO_TYPE_MERGE
 		    && (gotent->flags & ALPHA_ELF_GOT_ENTRY_RELOCS_XLATED) == 0)
 		  {
 		    struct alpha_elf_got_entry *ent;
@@ -3421,7 +3422,7 @@ elf64_alpha_relocate_section (output_bfd
 			ent->addend =
 			  _bfd_merged_section_offset (output_bfd, &msec,
 						      elf_section_data (sec)->
-						      merge_info,
+						      sec_info,
 						      sym->st_value
 						      + ent->addend,
 						      (bfd_vma) 0);
--- bfd/elf-eh-frame.c.jj	Wed Dec  5 16:37:34 2001
+++ bfd/elf-eh-frame.c	Mon Dec 10 18:13:19 2001
@@ -0,0 +1,1029 @@
+/* .eh_frame section optimization.
+   Copyright 2001 Free Software Foundation, Inc.
+   Written by Jakub Jelinek <jakub@redhat.com>.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+#include "elf-bfd.h"
+#include "elf/dwarf2.h"
+
+#define EH_FRAME_HDR_SIZE 8
+
+struct cie_header
+{
+  unsigned int length;
+  unsigned int id;
+};
+
+struct cie
+{
+  struct cie_header hdr;
+  unsigned char version;
+  unsigned char augmentation[20];
+  unsigned int code_align;
+  int data_align;
+  unsigned int ra_column;
+  unsigned int augmentation_size;
+  struct elf_link_hash_entry *personality;
+  unsigned char per_encoding;
+  unsigned char lsda_encoding;
+  unsigned char fde_encoding;
+  unsigned char initial_insn_length;
+  unsigned char make_relative;
+  unsigned char initial_instructions[50];
+};
+
+struct eh_cie_fde
+{
+  unsigned int offset;
+  unsigned int size;
+  asection *sec;
+  unsigned int new_offset;
+  unsigned char fde_encoding;
+  unsigned char cie : 1;
+  unsigned char removed : 1;
+  unsigned char make_relative : 1;
+};
+
+struct eh_frame_sec_info
+{
+  unsigned int count;
+  unsigned int alloced;
+  struct eh_cie_fde entry[1];
+};
+
+struct eh_frame_array_ent
+{
+  bfd_vma initial_loc;
+  bfd_vma fde;
+};
+
+struct eh_frame_hdr_info
+{
+  struct cie last_cie;
+  asection *last_cie_sec;
+  unsigned int last_cie_offset;
+  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.  */
+  boolean table;
+};
+
+static bfd_vma read_unsigned_leb128
+  PARAMS ((bfd *, char *, unsigned int *));
+static bfd_signed_vma read_signed_leb128
+  PARAMS ((bfd *, char *, unsigned int *));
+static int get_DW_EH_PE_width
+  PARAMS ((int, int));
+static int cie_compare
+  PARAMS ((struct cie *, struct cie *));
+static int vma_compare
+  PARAMS ((const PTR a, const PTR b));
+
+/* Helper function for reading uleb128 encoded data.  */
+
+static bfd_vma
+read_unsigned_leb128 (abfd, buf, bytes_read_ptr)
+     bfd *abfd ATTRIBUTE_UNUSED;
+     char *buf;
+     unsigned int *bytes_read_ptr;
+{
+  bfd_vma  result;
+  unsigned int  num_read;
+  int           shift;
+  unsigned char byte;
+
+  result   = 0;
+  shift    = 0;
+  num_read = 0;
+  do
+    {
+      byte = bfd_get_8 (abfd, (bfd_byte *) buf);
+      buf ++;
+      num_read ++;
+      result |= (((bfd_vma) byte & 0x7f) << shift);
+      shift += 7;
+    }
+  while (byte & 0x80);
+  * bytes_read_ptr = num_read;
+  return result;
+}
+
+/* Helper function for reading sleb128 encoded data.  */
+
+static bfd_signed_vma
+read_signed_leb128 (abfd, buf, bytes_read_ptr)
+     bfd *abfd ATTRIBUTE_UNUSED;
+     char *buf;
+     unsigned int * bytes_read_ptr;
+{
+  bfd_vma	result;
+  int           shift;
+  int           num_read;
+  unsigned char byte;
+
+  result = 0;
+  shift = 0;
+  num_read = 0;
+  do
+    {
+      byte = bfd_get_8 (abfd, (bfd_byte *) buf);
+      buf ++;
+      num_read ++;
+      result |= (((bfd_vma) byte & 0x7f) << shift);
+      shift += 7;
+    }
+  while (byte & 0x80);
+  if (byte & 0x40)
+    result |= (((bfd_vma) -1) << (shift - 7)) << 7;
+  * bytes_read_ptr = num_read;
+  return result;
+}
+
+#define read_uleb128(VAR, BUF)				\
+  (VAR) = read_unsigned_leb128 (abfd, buf, &leb128_tmp);\
+  (BUF) += leb128_tmp
+
+#define read_sleb128(VAR, BUF)				\
+  (VAR) = read_signed_leb128 (abfd, buf, &leb128_tmp);	\
+  (BUF) += leb128_tmp
+
+/* Return 0 if either encoding is variable width, or not yet known to bfd.  */
+
+static
+int get_DW_EH_PE_width (encoding, ptr_size)
+     int encoding, ptr_size;
+{
+  /* DW_EH_PE_ values of 0x60 and 0x70 weren't defined at the time .eh_frame
+     was added to bfd.  */
+  if ((encoding & 0x60) == 0x60)
+    return 0;
+
+  switch (encoding & 7)
+    {
+    case DW_EH_PE_udata2: return 2;
+    case DW_EH_PE_udata4: return 4;
+    case DW_EH_PE_udata8: return 8;
+    case DW_EH_PE_absptr: return ptr_size;
+    default:
+      break;
+    }
+
+  return 0;
+}
+
+/* Return zero if C1 and C2 CIEs can be merged.  */
+
+static
+int cie_compare (c1, c2)
+     struct cie *c1, *c2;
+{
+  if (c1->hdr.length == c2->hdr.length
+      && c1->version == c2->version
+      && strcmp (c1->augmentation, c2->augmentation) == 0
+      && strcmp (c1->augmentation, "eh") != 0
+      && c1->code_align == c2->code_align
+      && c1->data_align == c2->data_align
+      && c1->ra_column == c2->ra_column
+      && c1->augmentation_size == c2->augmentation_size
+      && c1->personality == c2->personality
+      && c1->per_encoding == c2->per_encoding
+      && c1->lsda_encoding == c2->lsda_encoding
+      && c1->fde_encoding == c2->fde_encoding
+      && c1->initial_insn_length
+	 == c2->initial_insn_length
+      && memcmp (c1->initial_instructions,
+		 c2->initial_instructions,
+		 c1->initial_insn_length) == 0)
+    return 0;
+
+  return 1;
+}
+
+/* This function is called for each input file before the .eh_frame
+   section is relocated.  It discards duplicate CIEs and FDEs for discarded
+   functions.  The function returns true iff any entries have been
+   deleted.  */
+
+boolean
+_bfd_elf_discard_section_eh_frame (abfd, info, sec, ehdrsec,
+				   reloc_symbol_deleted_p, cookie)
+     bfd *abfd;
+     struct bfd_link_info *info;
+     asection *sec, *ehdrsec;
+     boolean (*reloc_symbol_deleted_p) (bfd_vma, PTR);
+     struct elf_reloc_cookie *cookie;
+{
+  bfd_byte *ehbuf = NULL, *buf;
+  bfd_byte *last_cie, *last_fde;
+  struct cie_header hdr;
+  struct cie cie;
+  struct eh_frame_hdr_info *hdr_info;
+  struct eh_frame_sec_info *sec_info;
+  unsigned int leb128_tmp;
+  unsigned int cie_usage_count, last_cie_ndx, i, offset, make_relative;
+  Elf_Internal_Rela *rel;
+  bfd_size_type new_size;
+  unsigned int ptr_size;
+
+  if (sec->_raw_size == 0)
+    {
+      /* This file does not contain .eh_frame information.  */
+      return false;
+    }
+
+  if ((sec->output_section != NULL
+       && 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 false;
+    }
+
+  /* Read the frame unwind information from abfd.  */
+
+  ehbuf = (bfd_byte *) bfd_malloc (sec->_raw_size);
+  if (ehbuf == NULL
+      || ! bfd_get_section_contents (abfd, sec, ehbuf, (bfd_vma) 0,
+				     sec->_raw_size))
+    {
+      if (elf_section_data (ehdrsec)->sec_info_type
+	  != ELF_INFO_TYPE_EH_FRAME_HDR)
+	{
+	  elf_section_data (ehdrsec)->sec_info
+	    = bfd_zmalloc (sizeof (struct eh_frame_hdr_info));
+	  elf_section_data (ehdrsec)->sec_info_type
+	    = ELF_INFO_TYPE_EH_FRAME_HDR;
+	}
+      return false;
+    }
+
+  if (sec->_raw_size >= 4
+      && bfd_get_32 (abfd, ehbuf) == 0
+      && cookie->rel == cookie->relend)
+    {
+      /* Empty .eh_frame section.  */
+      free (ehbuf);
+      return false;
+    }
+
+  if (elf_section_data (ehdrsec)->sec_info_type
+      != ELF_INFO_TYPE_EH_FRAME_HDR)
+    {
+      hdr_info = (struct eh_frame_hdr_info *)      
+		 bfd_zmalloc (sizeof (struct eh_frame_hdr_info));
+      hdr_info->table = true;
+      elf_section_data (ehdrsec)->sec_info = hdr_info;
+      elf_section_data (ehdrsec)->sec_info_type
+	= ELF_INFO_TYPE_EH_FRAME_HDR;
+    }
+  else
+    hdr_info = (struct eh_frame_hdr_info *)
+	       elf_section_data (ehdrsec)->sec_info;
+
+  /* If .eh_frame section size doesn't fit into int, we cannot handle
+     it (it would need to use 64-bit .eh_frame format anyway).  */
+  if (sec->_raw_size != (unsigned int) sec->_raw_size)
+    return false;
+
+  ptr_size = (elf_elfheader (abfd)->e_ident[EI_CLASS]
+	      == ELFCLASS64) ? 8 : 4;
+  buf = ehbuf;
+  last_cie = NULL;
+  last_cie_ndx = 0;
+  memset (&cie, 0, sizeof (cie));
+  cie_usage_count = 0;
+  new_size = sec->_raw_size;
+  make_relative = hdr_info->last_cie.make_relative;
+  sec_info = bfd_zmalloc (sizeof (struct eh_frame_sec_info)
+			  + 99 * sizeof (struct eh_cie_fde));
+  if (sec_info == NULL)
+    goto free_no_table;
+  sec_info->alloced = 100;
+
+#define ENSURE_NO_RELOCS(buf)				\
+  if (cookie->rel < cookie->relend			\
+      && cookie->rel->r_offset				\
+	 < (bfd_size_type) ((buf) - ehbuf))		\
+    goto free_no_table
+
+#define SKIP_RELOCS(buf)				\
+  while (cookie->rel < cookie->relend			\
+         && cookie->rel->r_offset			\
+	    < (bfd_size_type) ((buf) - ehbuf))		\
+    cookie->rel++
+
+#define GET_RELOC(buf)					\
+  ((cookie->rel < cookie->relend			\
+    && cookie->rel->r_offset				\
+       == (bfd_size_type) ((buf) - ehbuf))		\
+   ? cookie->rel : NULL)
+
+  for (;;)
+    {
+      unsigned char *aug;
+
+      if (sec_info->count == sec_info->alloced)
+	{
+	  sec_info = bfd_realloc (sec_info,
+				  sizeof (struct eh_frame_sec_info)
+				  + (sec_info->alloced + 99)
+				    * sizeof (struct eh_cie_fde));
+	  if (sec_info == NULL)
+	    goto free_no_table;
+
+	  memset (&sec_info->entry[sec_info->alloced], 0,
+		  100 * sizeof (struct eh_cie_fde));
+	  sec_info->alloced += 100;
+	}
+
+      last_fde = buf;
+      /* If we are at the end of the section, we still need to decide
+	 on whether to output or discard last encountered CIE (if any).  */
+      if ((bfd_size_type) (buf - ehbuf) == sec->_raw_size)
+	hdr.id = (unsigned int) -1;
+      else
+	{
+	  if ((bfd_size_type) (buf + 4 - ehbuf) > sec->_raw_size)
+	    /* No space for CIE/FDE header length.  */
+	    goto free_no_table;
+
+	  hdr.length = bfd_get_32 (abfd, buf);
+	  if (hdr.length == 0xffffffff)
+	    /* 64-bit .eh_frame is not supported.  */
+	    goto free_no_table;
+	  buf += 4;
+	  if ((buf - ehbuf) + hdr.length > sec->_raw_size)
+	    /* CIE/FDE not contained fully in this .eh_frame input section.  */
+	    goto free_no_table;
+
+	  sec_info->entry[sec_info->count].offset = last_fde - ehbuf;
+	  sec_info->entry[sec_info->count].size = 4 + hdr.length;
+
+	  if (hdr.length == 0)
+	    {
+	      /* CIE with length 0 must be only the last in the section.  */
+	      if ((bfd_size_type) (buf - ehbuf) < sec->_raw_size)
+		goto free_no_table;
+	      ENSURE_NO_RELOCS (buf);
+	      sec_info->count++;
+	      /* Now just finish last encountered CIE processing and break
+		 the loop.  */
+	      hdr.id = (unsigned int) -1;
+	    }
+	  else
+	    {
+	      hdr.id = bfd_get_32 (abfd, buf);
+	      buf += 4;
+	      if (hdr.id == (unsigned int) -1)
+		goto free_no_table;
+	    }
+	}
+
+      if (hdr.id == 0 || hdr.id == (unsigned int) -1)
+	{
+	  unsigned int initial_insn_length;
+
+	  /* CIE  */
+	  if (last_cie != NULL)
+	    {
+	      /* Now check if this CIE is identical to last CIE, in which case
+		 we can remove it, provided we adjust all FDEs.
+		 Also, it can be removed if we have removed all FDEs using
+		 that. */
+	      if (cie_compare (&cie, &hdr_info->last_cie) == 0
+		  || cie_usage_count == 0)
+		{
+		  new_size -= cie.hdr.length + 4;
+		  sec_info->entry[last_cie_ndx].removed = 1;
+		  sec_info->entry[last_cie_ndx].sec = hdr_info->last_cie_sec;
+		  sec_info->entry[last_cie_ndx].new_offset
+		    = hdr_info->last_cie_offset;
+		}
+	      else
+		{
+		  hdr_info->last_cie = cie;
+		  hdr_info->last_cie_sec = sec;
+		  hdr_info->last_cie_offset = last_cie - ehbuf;
+		  sec_info->entry[last_cie_ndx].make_relative
+		    = cie.make_relative;
+		}
+	    }
+
+	  if (hdr.id == (unsigned int) -1)
+	    break;
+
+	  last_cie_ndx = sec_info->count;
+	  sec_info->entry[sec_info->count].cie = 1;
+
+	  cie_usage_count = 0;
+	  memset (&cie, 0, sizeof (cie));
+	  cie.hdr = hdr;
+	  cie.version = *buf++;
+
+	  /* Cannot handle unknown versions.  */
+	  if (cie.version != 1)
+	    goto free_no_table;
+	  if (strlen (buf) > sizeof (cie.augmentation) - 1)
+	    goto free_no_table;
+
+	  strcpy (cie.augmentation, buf);
+	  buf = strchr (buf, '\0') + 1;
+	  ENSURE_NO_RELOCS (buf);
+	  if (buf[0] == 'e' && buf[1] == 'h')
+	    {
+	      /* GCC < 3.0 .eh_frame CIE */
+	      /* We cannot merge "eh" CIEs because __EXCEPTION_TABLE__
+		 is private to each CIE, so we don't need it for anything.
+		 Just skip it.  */
+	      buf += ptr_size;
+	      SKIP_RELOCS (buf);
+	    }
+	  read_uleb128 (cie.code_align, buf);
+	  read_sleb128 (cie.data_align, buf);
+	  read_uleb128 (cie.ra_column, buf);
+	  ENSURE_NO_RELOCS (buf);
+	  cie.lsda_encoding = DW_EH_PE_omit;
+	  cie.fde_encoding = DW_EH_PE_omit;
+	  cie.per_encoding = DW_EH_PE_omit;
+	  aug = cie.augmentation;
+	  if (aug[0] != 'e' || aug[1] != 'h')
+	    {
+	      if (*aug == 'z')
+		{
+		  aug++;
+		  read_uleb128 (cie.augmentation_size, buf);
+	  	  ENSURE_NO_RELOCS (buf);
+		}
+
+	      while (*aug != '\0')
+		switch (*aug++)
+		  {
+		  case 'L':
+		    cie.lsda_encoding = *buf++;
+		    ENSURE_NO_RELOCS (buf);
+		    if (get_DW_EH_PE_width (cie.lsda_encoding, ptr_size) == 0)
+		      goto free_no_table;
+		    break;
+		  case 'R':
+		    cie.fde_encoding = *buf++;
+		    ENSURE_NO_RELOCS (buf);
+		    if (get_DW_EH_PE_width (cie.fde_encoding, ptr_size) == 0)
+		      goto free_no_table;
+		    break;
+		  case 'P':
+		    {
+		      int per_width;
+
+		      cie.per_encoding = *buf++;
+		      per_width = get_DW_EH_PE_width (cie.per_encoding,
+						      ptr_size);
+		      if (per_width == 0)
+			goto free_no_table;
+		      if ((cie.per_encoding & 0xf0) == DW_EH_PE_aligned)
+			buf = ehbuf
+			      + ((buf - ehbuf + per_width - 1)
+				 & ~((bfd_size_type) per_width - 1));
+		      ENSURE_NO_RELOCS (buf);
+		      rel = GET_RELOC (buf);
+		      /* Ensure we have a reloc here, against
+			 a global symbol.  */
+		      if (rel != NULL)
+			{
+			  unsigned long r_symndx;
+
+#ifdef BFD64
+			  if (ptr_size == 8)
+			    r_symndx = ELF64_R_SYM (cookie->rel->r_info);
+			  else
+#endif
+			    r_symndx = ELF32_R_SYM (cookie->rel->r_info);
+			  if (r_symndx >= cookie->locsymcount)
+			    {
+			      struct elf_link_hash_entry *h;
+
+			      r_symndx -= cookie->extsymoff;
+			      h = cookie->sym_hashes[r_symndx];
+
+			      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;
+
+			      cie.personality = h;
+			    }
+			  cookie->rel++;
+			}
+		      buf += per_width;
+		    }
+		    break;
+		  default:
+		    /* Unrecognized augmentation. Better bail out.  */
+		    goto free_no_table;
+		  }
+	    }
+
+	  /* For shared libraries, try to get rid of as many RELATIVE relocs
+	     as possible.
+	     FIXME: For this to work, ELF backends need to perform the
+	     relocation if omitting dynamic relocs, not skip it.  */
+          if (0
+	      && info->shared
+	      && (cie.fde_encoding & 0xf0) == DW_EH_PE_absptr)
+	    cie.make_relative = 1;
+
+	  /* If FDE encoding was not specified, it defaults to
+	     DW_EH_absptr.  */
+	  if (cie.fde_encoding == DW_EH_PE_omit)
+	    cie.fde_encoding = DW_EH_PE_absptr;
+
+	  initial_insn_length = cie.hdr.length - (buf - last_fde - 4);
+	  if (initial_insn_length <= 50)
+	    {
+	      cie.initial_insn_length = initial_insn_length;
+	      memcpy (cie.initial_instructions, buf, initial_insn_length);
+	    }
+	  buf += initial_insn_length;
+	  ENSURE_NO_RELOCS (buf);
+	  last_cie = last_fde;
+	}
+      else
+	{
+	  /* Ensure this FDE uses the last CIE encountered.  */
+	  if (last_cie == NULL
+	      || hdr.id != (unsigned int) (buf - 4 - last_cie))
+	    goto free_no_table;
+
+	  ENSURE_NO_RELOCS (buf);
+	  rel = GET_RELOC (buf);
+	  if (rel == NULL)
+	    /* This should not happen.  */
+	    goto free_no_table;
+	  if ((*reloc_symbol_deleted_p) (buf - ehbuf, cookie))
+	    {
+	      cookie->rel = rel;
+	      /* This is a FDE against discarded section, it should
+		 be deleted.  */
+	      new_size -= hdr.length + 4;
+	      sec_info->entry[sec_info->count].removed = 1;
+	    }
+	  else
+	    {
+	      cie_usage_count++;
+	      hdr_info->fde_count++;
+	    }
+	  cookie->rel = rel;
+	  buf = last_fde + 4 + hdr.length;
+	  SKIP_RELOCS (buf);
+	}
+
+      sec_info->entry[sec_info->count].fde_encoding = cie.fde_encoding;
+      sec_info->count++;
+    }
+
+  elf_section_data (sec)->sec_info = sec_info;
+  elf_section_data (sec)->sec_info_type = ELF_INFO_TYPE_EH_FRAME;
+
+  /* Ok, now we can assign new offsets.  */
+  offset = 0;
+  last_cie_ndx = 0;
+  for (i = 0; i < sec_info->count; i++)
+    {
+      if (! sec_info->entry[i].removed)
+	{
+	  sec_info->entry[i].new_offset = offset;
+	  offset += sec_info->entry[i].size;
+	  if (sec_info->entry[i].cie)
+	    {
+	      last_cie_ndx = i;
+	      make_relative = sec_info->entry[i].make_relative;
+	    }
+	  else
+	    sec_info->entry[i].make_relative = make_relative;
+	}
+      else if (sec_info->entry[i].cie && sec_info->entry[i].sec == sec)
+	{
+	  /* Need to adjust new_offset too.  */
+	  BFD_ASSERT (sec_info->entry[last_cie_ndx].offset
+		      == sec_info->entry[i].new_offset);
+	  sec_info->entry[i].new_offset
+	    = sec_info->entry[last_cie_ndx].new_offset;
+	}
+    }
+  if (hdr_info->last_cie_sec == sec)
+    {
+      BFD_ASSERT (sec_info->entry[last_cie_ndx].offset
+		  == hdr_info->last_cie_offset);
+      hdr_info->last_cie_offset = sec_info->entry[last_cie_ndx].new_offset;
+    }
+
+  /* Shrink the sec as needed.  */
+  
+  sec->_cooked_size = new_size;
+  if (sec->_cooked_size == 0)
+    sec->flags |= SEC_EXCLUDE;
+
+  return new_size != sec->_raw_size;
+
+free_no_table:
+  if (sec_info)
+    free (sec_info);
+  hdr_info->table = false;
+  hdr_info->last_cie.hdr.length = 0;
+  return false;
+}
+
+/* This function is called for .eh_frame_hdr section after
+   _bfd_elf_discard_section_eh_frame has been called on all .eh_frame
+   input sections.  It finalizes the size of .eh_frame_hdr section.  */
+
+boolean
+_bfd_elf_discard_section_eh_frame_hdr (abfd, info, sec)
+     bfd *abfd;
+     struct bfd_link_info *info;
+     asection *sec;
+{
+  struct eh_frame_hdr_info *hdr_info;
+  unsigned int ptr_size;
+
+  ptr_size = (elf_elfheader (abfd)->e_ident[EI_CLASS]
+	      == ELFCLASS64) ? 8 : 4;
+
+  if (elf_section_data (sec)->sec_info_type
+      != ELF_INFO_TYPE_EH_FRAME_HDR)
+    {
+      _bfd_strip_section_from_output (info, sec);
+      return false;
+    }
+
+  hdr_info = (struct eh_frame_hdr_info *)
+	     elf_section_data (sec)->sec_info;
+  sec->_cooked_size = EH_FRAME_HDR_SIZE;
+  if (hdr_info->table)
+    sec->_cooked_size += 4 + hdr_info->fde_count * 8;
+
+  /* Request program headers to be recalculated.  */
+  elf_tdata (abfd)->program_header_size = 0;
+  elf_tdata (abfd)->eh_frame_hdr = true;
+  return true;
+}
+
+/* Adjust an address in the .eh_frame section.  Given OFFSET within
+   SEC, this returns the new offset in the adjusted .eh_frame section,
+   or -1 if the address refers to a CIE/FDE which has been removed
+   or to offset with dynamic relocation which is no longer needed.  */
+
+bfd_vma
+_bfd_elf_eh_frame_section_offset (output_bfd, sec, offset)
+     bfd *output_bfd ATTRIBUTE_UNUSED;
+     asection *sec;
+     bfd_vma offset;
+{
+  struct eh_frame_sec_info *sec_info;
+  unsigned int lo, hi, mid;
+
+  if (elf_section_data (sec)->sec_info_type != ELF_INFO_TYPE_EH_FRAME)
+    return offset;
+  sec_info = (struct eh_frame_sec_info *)
+	     elf_section_data (sec)->sec_info;
+
+  if (offset >= sec->_raw_size)
+    return offset - (sec->_cooked_size - sec->_raw_size);
+
+  lo = 0;
+  hi = sec_info->count;
+  mid = 0;
+  while (lo < hi)
+    {
+      mid = (lo + hi) / 2;
+      if (offset < sec_info->entry[mid].offset)
+	hi = mid;
+      else if (offset
+	       >= sec_info->entry[mid].offset + sec_info->entry[mid].size)
+	lo = mid + 1;
+      else
+	break;
+    }
+
+  BFD_ASSERT (lo < hi);
+
+  /* FDE or CIE was removed.  */
+  if (sec_info->entry[mid].removed)
+    return (bfd_vma) -1;
+
+  /* If converting to DW_EH_PE_pcrel, there will be no need for run-time
+     relocation against FDE's initial_location field.  */
+  if (sec_info->entry[mid].make_relative
+      && ! sec_info->entry[mid].cie
+      && offset == sec_info->entry[mid].offset + 8)
+    return (bfd_vma) -1;
+
+  return offset
+	 + (sec_info->entry[mid].new_offset - sec_info->entry[mid].offset);
+}
+
+/* Write out .eh_frame section.  This is called with the relocated
+   contents.  */
+
+boolean
+_bfd_elf_write_section_eh_frame (abfd, sec, ehdrsec, contents)
+     bfd *abfd;
+     asection *sec, *ehdrsec;
+     bfd_byte *contents;
+{
+  struct eh_frame_sec_info *sec_info;
+  struct eh_frame_hdr_info *hdr_info;
+  unsigned int i;
+  bfd_byte *p, *buf;
+  unsigned int leb128_tmp;
+  unsigned int cie_offset = 0;
+  unsigned int ptr_size;
+
+  ptr_size = (elf_elfheader (sec->owner)->e_ident[EI_CLASS]
+	      == ELFCLASS64) ? 8 : 4;
+
+  if (elf_section_data (sec)->sec_info_type != ELF_INFO_TYPE_EH_FRAME)
+    return bfd_set_section_contents (abfd, sec->output_section,
+				     contents,
+				     (file_ptr) sec->output_offset,
+				     sec->_raw_size);
+  sec_info = (struct eh_frame_sec_info *)
+	     elf_section_data (sec)->sec_info;
+  hdr_info = NULL;
+  if (ehdrsec
+      && elf_section_data (ehdrsec)->sec_info_type
+	 == ELF_INFO_TYPE_EH_FRAME_HDR)
+    {
+      hdr_info = (struct eh_frame_hdr_info *)
+		 elf_section_data (ehdrsec)->sec_info;
+      if (hdr_info->table && hdr_info->array == NULL)
+	hdr_info->array
+	  = bfd_malloc (hdr_info->fde_count * sizeof(*hdr_info->array));
+      if (hdr_info->array == NULL)
+        hdr_info = NULL;
+    }
+
+  p = contents;
+  for (i = 0; i < sec_info->count; ++i)
+    {
+      if (sec_info->entry[i].removed)
+	{
+	  if (sec_info->entry[i].cie)
+	    {
+	      cie_offset = sec_info->entry[i].new_offset;
+	      cie_offset += sec_info->entry[i].sec->output_section->vma
+			    + sec_info->entry[i].sec->output_offset
+			    - sec->output_section->vma
+			    - sec->output_offset;
+	    }
+	  continue;
+	}
+      if (sec_info->entry[i].cie)
+	{
+	  /* CIE */
+	  cie_offset = sec_info->entry[i].new_offset;
+	  if (sec_info->entry[i].make_relative)
+	    {
+	      unsigned char *aug;
+	      unsigned int dummy, per_width, per_encoding;
+
+	      /* Need to find 'R' augmentation's argument and modify
+		 DW_EH_PE_* value.  */
+	      buf = contents + sec_info->entry[i].offset;
+	      /* Skip length, id and version.  */
+	      buf += 9;
+	      aug = buf;
+	      buf = strchr (buf, '\0') + 1;
+	      read_uleb128 (dummy, buf);
+	      read_sleb128 (dummy, buf);
+	      read_uleb128 (dummy, buf);
+	      if (*aug == 'z')
+		{
+		  read_uleb128 (dummy, buf);
+		  aug++;
+		}
+
+	      while (*aug != 'R')
+		switch (*aug++)
+		  {
+		  case 'L':
+		    buf++;
+		    break;
+		  case 'P':
+		    per_encoding = *buf++;
+                    per_width = get_DW_EH_PE_width (per_encoding,
+						    ptr_size);
+		    BFD_ASSERT (per_width != 0);
+		    if ((per_encoding & 0xf0) == DW_EH_PE_aligned)
+		      buf = contents
+			    + ((buf - contents + per_width - 1)
+			       & ~((bfd_size_type) per_width - 1));
+		    buf += per_width;
+		    break;
+		  default:
+		    BFD_FAIL ();
+		  }
+
+	      BFD_ASSERT (*buf == sec_info->entry[i].fde_encoding);
+	      *buf |= DW_EH_PE_pcrel;
+	    }
+	}
+      else
+	{
+	  /* FDE */
+	  bfd_vma value = 0, address;
+	  unsigned int fde_width;
+
+	  buf = contents + sec_info->entry[i].offset;
+	  /* Skip length.  */	
+	  buf += 4;
+	  bfd_put_32 (abfd,
+		      sec_info->entry[i].new_offset + 4 - cie_offset, buf);
+	  buf += 4;
+	  fde_width = get_DW_EH_PE_width (sec_info->entry[i].fde_encoding,
+					  ptr_size);
+	  switch (fde_width)
+	    {
+	    case 2: value = bfd_get_16 (abfd, buf); break;
+	    case 4: value = bfd_get_32 (abfd, buf); break;
+	    case 8: value = bfd_get_64 (abfd, buf); break;
+	    default: BFD_FAIL ();
+	    }
+	  address = value;
+	  switch (sec_info->entry[i].fde_encoding & 0xf0)
+	    {
+	    case DW_EH_PE_indirect:
+	    case DW_EH_PE_textrel:
+	      BFD_ASSERT (hdr_info == NULL);
+	      break;
+	    case DW_EH_PE_datarel:
+	      {
+		asection *got = bfd_get_section_by_name (abfd, ".got");
+
+	        BFD_ASSERT (got != NULL);
+		address += got->vma;
+	      }
+	      break;
+	    case DW_EH_PE_pcrel:
+	      value += sec_info->entry[i].offset
+		       - sec_info->entry[i].new_offset;
+	      address += sec->output_section->vma + sec->output_offset
+			 + sec_info->entry[i].new_offset + 8;
+	      break;
+	    }
+	  if (sec_info->entry[i].make_relative)
+	    value -= sec->output_section->vma + sec->output_offset
+		     + sec_info->entry[i].new_offset + 8;
+	  switch (fde_width)
+	    {
+	    case 2: bfd_put_16 (abfd, value, buf); break;
+	    case 4: bfd_put_32 (abfd, value, buf); break;
+	    case 8: bfd_put_64 (abfd, value, buf); break;
+	    }
+
+	  if (hdr_info)
+	    {
+	      hdr_info->array[hdr_info->array_count].initial_loc = address;
+	      hdr_info->array[hdr_info->array_count++].fde
+		= sec->output_section->vma + sec->output_offset
+		  + sec_info->entry[i].new_offset;
+	    }
+	}
+
+      BFD_ASSERT (p == contents + sec_info->entry[i].new_offset);
+      memmove (p, contents + sec_info->entry[i].offset,
+	       sec_info->entry[i].size);
+      p += sec_info->entry[i].size;
+    }
+
+  BFD_ASSERT ((bfd_size_type) (p - contents) == sec->_cooked_size);
+
+  return bfd_set_section_contents (abfd, sec->output_section,
+                                   contents, (file_ptr) sec->output_offset,
+                                   sec->_cooked_size);
+}
+
+/* Helper function used to sort .eh_frame_hdr search table by increasing
+   VMA of FDE initial location.  */
+
+static int
+vma_compare (a, b)
+     const PTR a;
+     const PTR b;
+{
+  struct eh_frame_array_ent *p = (struct eh_frame_array_ent *) a;
+  struct eh_frame_array_ent *q = (struct eh_frame_array_ent *) b;
+  if (p->initial_loc > q->initial_loc)
+    return 1;
+  if (p->initial_loc < q->initial_loc)
+    return -1;
+  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:
+   ubyte version		(currently 1)
+   ubyte eh_frame_ptr_enc  	(DW_EH_PE_* encoding of pointer to start of
+				 .eh_frame section)
+   ubyte fde_count_enc		(DW_EH_PE_* encoding of total FDE count
+				 number (or DW_EH_PE_omit if there is no
+				 binary search table computed))
+   ubyte table_enc		(DW_EH_PE_* encoding of binary search table,
+				 or DW_EH_PE_omit if not present.
+				 DW_EH_PE_datarel is using address of
+				 .eh_frame_hdr section start as base)
+   [encoded] eh_frame_ptr	(pointer to start of .eh_frame section)
+   optionally followed by:
+   [encoded] fde_count		(total number of FDEs in .eh_frame section)
+   fde_count x [encoded] initial_loc, fde
+				(array of encoded pairs containing
+				 FDE initial_location field and FDE address,
+				 sorted by increasing initial_loc)  */
+
+boolean
+_bfd_elf_write_section_eh_frame_hdr (abfd, sec)
+     bfd *abfd;
+     asection *sec;
+{
+  struct eh_frame_hdr_info *hdr_info;
+  unsigned int ptr_size;
+  bfd_byte *contents;
+  asection *eh_frame_sec;
+  bfd_size_type size;
+
+  ptr_size = (elf_elfheader (sec->owner)->e_ident[EI_CLASS]
+	      == ELFCLASS64) ? 8 : 4;
+
+  BFD_ASSERT (elf_section_data (sec)->sec_info_type
+	      == ELF_INFO_TYPE_EH_FRAME_HDR);
+  hdr_info = (struct eh_frame_hdr_info *)
+	     elf_section_data (sec)->sec_info;
+  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_malloc (size);
+  if (contents == NULL)
+    return false;
+
+  eh_frame_sec = bfd_get_section_by_name (abfd, ".eh_frame");
+  if (eh_frame_sec == NULL)
+    return false;
+
+  memset (contents, 0, EH_FRAME_HDR_SIZE);
+  contents[0] = 1;				/* Version  */
+  contents[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4; /* .eh_frame offset  */
+  if (hdr_info->array && hdr_info->array_count == hdr_info->fde_count)
+    {
+      contents[2] = DW_EH_PE_udata4;		/* FDE count encoding  */
+      contents[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4; /* search table enc  */
+    }
+  else
+    {
+      contents[2] = DW_EH_PE_omit;
+      contents[3] = DW_EH_PE_omit;
+    }
+  bfd_put_32 (abfd, eh_frame_sec->vma - sec->output_section->vma, contents + 4);
+  if (contents[2] != DW_EH_PE_omit)
+    {
+      unsigned int i;
+
+      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_put_32 (abfd,
+		      hdr_info->array[i].initial_loc
+		      - sec->output_section->vma,
+		      contents + EH_FRAME_HDR_SIZE + i * 8 + 4);
+	  bfd_put_32 (abfd,
+		      hdr_info->array[i].fde - sec->output_section->vma,
+		      contents + EH_FRAME_HDR_SIZE + i * 8 + 8);
+	}
+    }
+
+  return bfd_set_section_contents (abfd, sec->output_section,
+				   contents, (file_ptr) sec->output_offset,
+                                   sec->_cooked_size);
+}
--- bfd/elflink.h.jj	Wed Dec  5 14:42:28 2001
+++ bfd/elflink.h	Mon Dec 10 14:40:43 2001
@@ -2214,7 +2214,7 @@ elf_link_add_object_symbols (abfd, info)
       asection *stab, *stabstr;
 
       stab = bfd_get_section_by_name (abfd, ".stab");
-      if (stab != NULL)
+      if (stab != NULL && !(stab->flags & SEC_MERGE))
 	{
 	  stabstr = bfd_get_section_by_name (abfd, ".stabstr");
 
@@ -2226,8 +2226,10 @@ elf_link_add_object_symbols (abfd, info)
 	      if (! _bfd_link_section_stabs (abfd,
 					     & hash_table->stab_info,
 					     stab, stabstr,
-					     &secdata->stab_info))
+					     &secdata->sec_info))
 		goto error_return;
+	      if (secdata->sec_info)
+		secdata->sec_info_type = ELF_INFO_TYPE_STABS;
 	    }
 	}
     }
@@ -2238,10 +2240,18 @@ elf_link_add_object_symbols (abfd, info)
       asection *s;
 
       for (s = abfd->sections; s != NULL; s = s->next)
-	if ((s->flags & SEC_MERGE)
-	    && ! _bfd_merge_section (abfd, & hash_table->merge_info, s,
-				     & elf_section_data (s)->merge_info))
-	  goto error_return;
+	if (s->flags & SEC_MERGE)
+	  {
+	    struct bfd_elf_section_data *secdata;
+
+	    secdata = elf_section_data (s);
+	    if (! _bfd_merge_section (abfd,
+				      & hash_table->merge_info,
+				      s, &secdata->sec_info))
+	      goto error_return;
+	    else if (secdata->sec_info)
+	      secdata->sec_info_type = ELF_INFO_TYPE_MERGE;
+	  }
     }
 
   return true;
@@ -2300,6 +2310,15 @@ elf_link_create_dynamic_sections (abfd, 
 	return false;
     }
 
+  if (info->eh_frame_hdr)
+    {
+      s = bfd_make_section (abfd, ".eh_frame_hdr");
+      if (s == NULL
+	  || ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY)
+	  || ! bfd_set_section_alignment (abfd, s, 2))
+	return false;
+    }
+
   /* Create sections to hold version informations.  These are removed
      if they are not needed.  */
   s = bfd_make_section (abfd, ".gnu.version_d");
@@ -5522,6 +5541,13 @@ elf_bfd_final_link (abfd, info)
 	goto error_return;
     }
 
+  o = bfd_get_section_by_name (elf_hash_table (info)->dynobj, ".eh_frame_hdr");
+  if (o && elf_section_data (o)->sec_info_type == ELF_INFO_TYPE_EH_FRAME_HDR)
+    {
+      if (! _bfd_elf_write_section_eh_frame_hdr (abfd, o))
+	goto error_return;
+    }
+
   if (finfo.symstrtab != NULL)
     _bfd_stringtab_free (finfo.symstrtab);
   if (finfo.contents != NULL)
@@ -5671,14 +5697,14 @@ elf_link_sec_merge_syms (h, data)
   if ((h->root.type == bfd_link_hash_defined
        || h->root.type == bfd_link_hash_defweak)
       && ((sec = h->root.u.def.section)->flags & SEC_MERGE)
-      && elf_section_data (sec)->merge_info)
+      && elf_section_data (sec)->sec_info_type == ELF_INFO_TYPE_MERGE)
     {
       bfd *output_bfd = (bfd *) data;
 
       h->root.u.def.value =
 	_bfd_merged_section_offset (output_bfd,
 				    &h->root.u.def.section,
-				    elf_section_data (sec)->merge_info,
+				    elf_section_data (sec)->sec_info,
 				    h->root.u.def.value, (bfd_vma) 0);
     }
 
@@ -6170,11 +6196,12 @@ elf_link_input_bfd (finfo, input_bfd)
       else if (isym->st_shndx > 0 && isym->st_shndx < SHN_LORESERVE)
 	{
 	  isec = section_from_elf_index (input_bfd, isym->st_shndx);
-	  if (isec && elf_section_data (isec)->merge_info
+	  if (isec
+	      && elf_section_data (isec)->sec_info_type == ELF_INFO_TYPE_MERGE
 	      && ELF_ST_TYPE (isym->st_info) != STT_SECTION)
 	    isym->st_value =
 	      _bfd_merged_section_offset (output_bfd, &isec,
-					  elf_section_data (isec)->merge_info,
+					  elf_section_data (isec)->sec_info,
 					  isym->st_value, (bfd_vma) 0);
 	}
       else if (isym->st_shndx == SHN_ABS)
@@ -6358,8 +6385,8 @@ elf_link_input_bfd (finfo, input_bfd)
 			  && ! bfd_is_abs_section (h->root.u.def.section)
 			  && bfd_is_abs_section (h->root.u.def.section
 						 ->output_section)
-			  && elf_section_data (h->root.u.def.section)->merge_info
-			     == NULL)
+			  && elf_section_data (h->root.u.def.section)->sec_info_type
+			     != ELF_INFO_TYPE_MERGE)
 			{
 #if BFD_VERSION_DATE < 20031005
 			  if ((o->flags & SEC_DEBUGGING) != 0)
@@ -6391,7 +6418,8 @@ elf_link_input_bfd (finfo, input_bfd)
 		      if (sec != NULL
 			  && ! bfd_is_abs_section (sec)
 			  && bfd_is_abs_section (sec->output_section)
-			  && elf_section_data (sec)->merge_info == NULL)
+			  && elf_section_data (sec)->sec_info_type
+			     != ELF_INFO_TYPE_MERGE)
 			{
 #if BFD_VERSION_DATE < 20031005
 			  if ((o->flags & SEC_DEBUGGING) != 0
@@ -6640,30 +6668,45 @@ elf_link_input_bfd (finfo, input_bfd)
 	{
 	  /* Section written out.  */
 	}
-      else if (elf_section_data (o)->stab_info)
+      else switch (elf_section_data (o)->sec_info_type)
 	{
+	case ELF_INFO_TYPE_STABS:
 	  if (! (_bfd_write_section_stabs
-		 (output_bfd, &elf_hash_table (finfo->info)->stab_info,
-		  o, &elf_section_data (o)->stab_info, contents)))
+		 (output_bfd,
+		  &elf_hash_table (finfo->info)->stab_info,
+		  o, &elf_section_data (o)->sec_info, contents)))
 	    return false;
-	}
-      else if (elf_section_data (o)->merge_info)
-	{
+	  break;
+	case ELF_INFO_TYPE_MERGE:
 	  if (! (_bfd_write_merged_section
-		 (output_bfd, o, elf_section_data (o)->merge_info)))
+		 (output_bfd, o, elf_section_data (o)->sec_info)))
 	    return false;
-	}
-      else
-	{
-	  bfd_size_type sec_size;
+	  break;
+	case ELF_INFO_TYPE_EH_FRAME:
+	  {
+	    asection *ehdrsec;
 
-	  sec_size = (o->_cooked_size != 0 ? o->_cooked_size : o->_raw_size);
-	  if (! (o->flags & SEC_EXCLUDE)
-	      && ! bfd_set_section_contents (output_bfd, o->output_section,
-					     contents,
-					     (file_ptr) o->output_offset,
-					     sec_size))
-	    return false;
+	    ehdrsec
+	      = bfd_get_section_by_name (elf_hash_table (finfo->info)->dynobj,
+					 ".eh_frame_hdr");
+	    if (! (_bfd_elf_write_section_eh_frame (output_bfd, o, ehdrsec,
+						    contents)))
+	      return false;
+	  }
+	  break;
+	default:
+	  {
+	    bfd_size_type sec_size;
+
+	    sec_size = (o->_cooked_size != 0 ? o->_cooked_size : o->_raw_size);
+	    if (! (o->flags & SEC_EXCLUDE)
+		&& ! bfd_set_section_contents (output_bfd, o->output_section,
+					       contents,
+					       (file_ptr) o->output_offset,
+					       sec_size))
+	      return false;
+	  }
+	  break;
 	}
     }
 
@@ -7881,24 +7924,30 @@ elf_reloc_symbol_deleted_p (offset, cook
    which is true for all known assemblers.  */
 
 boolean
-elf_bfd_discard_info (info)
+elf_bfd_discard_info (output_bfd, info)
+     bfd *output_bfd;
      struct bfd_link_info *info;
 {
   struct elf_reloc_cookie cookie;
-  asection *o;
+  asection *stab, *eh, *ehdr;
   Elf_Internal_Shdr *symtab_hdr;
   Elf_External_Sym *freesyms;
   struct elf_backend_data *bed;
   bfd *abfd;
   boolean ret = false;
+  boolean strip = info->strip == strip_all || info->strip == strip_debugger;
 
   if (info->relocateable
       || info->traditional_format
       || info->hash->creator->flavour != bfd_target_elf_flavour
-      || ! is_elf_hash_table (info)
-      || info->strip == strip_all
-      || info->strip == strip_debugger)
+      || ! is_elf_hash_table (info))
     return false;
+
+  ehdr = NULL;
+  if (info->eh_frame_hdr)
+    ehdr = bfd_get_section_by_name (elf_hash_table (info)->dynobj,
+				    ".eh_frame_hdr");
+
   for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link_next)
     {
       if (bfd_get_flavour (abfd) != bfd_target_elf_flavour)
@@ -7909,8 +7958,18 @@ elf_bfd_discard_info (info)
       if ((abfd->flags & DYNAMIC) != 0)
 	continue;
 
-      o = bfd_get_section_by_name (abfd, ".stab");
-      if (! o && ! bed->elf_backend_discard_info)
+      eh = NULL;
+      if (ehdr)
+	{
+	  eh = bfd_get_section_by_name (abfd, ".eh_frame");
+	  if (eh && eh->_raw_size == 0)
+	    eh = NULL;
+	}
+
+      stab = strip ? NULL : bfd_get_section_by_name (abfd, ".stab");
+      if ((! stab || elf_section_data(stab)->sec_info_type != ELF_INFO_TYPE_STABS)
+	  && ! eh
+	  && (strip || ! bed->elf_backend_discard_info))
 	continue;
 
       symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
@@ -7955,19 +8014,19 @@ elf_bfd_discard_info (info)
 	    }
         }
 
-      if (o)
+      if (stab)
 	{
 	  cookie.rels = (NAME(_bfd_elf,link_read_relocs)
-			 (abfd, o, (PTR) NULL,
+			 (abfd, stab, (PTR) NULL,
 			  (Elf_Internal_Rela *) NULL,
 			  info->keep_memory));
 	  if (cookie.rels)
 	    {
 	      cookie.rel = cookie.rels;
 	      cookie.relend =
-		cookie.rels + o->reloc_count * bed->s->int_rels_per_ext_rel;
-	      if (_bfd_discard_section_stabs (abfd, o,
-					      elf_section_data (o)->stab_info,
+		cookie.rels + stab->reloc_count * bed->s->int_rels_per_ext_rel;
+	      if (_bfd_discard_section_stabs (abfd, stab,
+					      elf_section_data (stab)->sec_info,
 					      elf_reloc_symbol_deleted_p,
 					      &cookie))
 		ret = true;
@@ -7976,6 +8035,30 @@ elf_bfd_discard_info (info)
 	    }
 	}
 
+      if (eh)
+	{
+	  cookie.rels = NULL;
+	  cookie.rel = NULL;
+	  cookie.relend = NULL;
+	  if (eh->reloc_count)
+	    cookie.rels = (NAME(_bfd_elf,link_read_relocs)
+			   (abfd, eh, (PTR) NULL,
+			    (Elf_Internal_Rela *) NULL,
+			    info->keep_memory));
+	  if (cookie.rels)
+	    {
+	      cookie.rel = cookie.rels;
+	      cookie.relend =
+		cookie.rels + eh->reloc_count * bed->s->int_rels_per_ext_rel;
+	    }
+	  if (_bfd_elf_discard_section_eh_frame (abfd, info, eh, ehdr,
+						 elf_reloc_symbol_deleted_p,
+						 &cookie))
+	    ret = true;
+	  if (! info->keep_memory)
+	    free (cookie.rels);
+	}
+
       if (bed->elf_backend_discard_info)
 	{
 	  if (bed->elf_backend_discard_info (abfd, &cookie, info))
@@ -7985,6 +8068,11 @@ elf_bfd_discard_info (info)
       if (freesyms)
 	free (freesyms);
     }
+
+  if (ehdr
+      && _bfd_elf_discard_section_eh_frame_hdr (output_bfd,
+						info, ehdr))
+    ret = true;
   return ret;
 }
 
@@ -7992,13 +8080,19 @@ static boolean
 elf_section_ignore_discarded_relocs (sec)
      asection *sec;
 {
-  if (strcmp (sec->name, ".stab") == 0)
-    return true;
-  else if ((get_elf_backend_data (sec->owner)
-	    ->elf_backend_ignore_discarded_relocs != NULL)
-	   && (*get_elf_backend_data (sec->owner)
-	       ->elf_backend_ignore_discarded_relocs) (sec))
+  switch (elf_section_data (sec)->sec_info_type)
+    {
+    case ELF_INFO_TYPE_STABS:
+    case ELF_INFO_TYPE_EH_FRAME:
+      return true;
+    default:
+      break;
+    }
+  if ((get_elf_backend_data (sec->owner)->elf_backend_ignore_discarded_relocs
+       != NULL)
+      && (*get_elf_backend_data (sec->owner)
+	   ->elf_backend_ignore_discarded_relocs) (sec))
     return true;
-  else
-    return false;
+
+  return false;
 }
--- bfd/bfd-in.h.jj	Fri Nov 16 21:49:42 2001
+++ bfd/bfd-in.h	Fri Dec  7 13:32:51 2001
@@ -636,9 +636,9 @@ extern const char *bfd_elf_get_dt_soname
 extern struct bfd_link_needed_list *bfd_elf_get_runpath_list
   PARAMS ((bfd *, struct bfd_link_info *));
 extern boolean bfd_elf32_discard_info
-  PARAMS ((struct bfd_link_info *));
+  PARAMS ((bfd *, struct bfd_link_info *));
 extern boolean bfd_elf64_discard_info
-  PARAMS ((struct bfd_link_info *));
+  PARAMS ((bfd *, struct bfd_link_info *));
 
 /* Return an upper bound on the number of bytes required to store a
    copy of ABFD's program header table entries.  Return -1 if an error
--- bfd/bfd-in2.h.jj	Wed Dec  5 14:42:24 2001
+++ bfd/bfd-in2.h	Fri Dec  7 13:32:51 2001
@@ -642,9 +642,9 @@ extern const char *bfd_elf_get_dt_soname
 extern struct bfd_link_needed_list *bfd_elf_get_runpath_list
   PARAMS ((bfd *, struct bfd_link_info *));
 extern boolean bfd_elf32_discard_info
-  PARAMS ((struct bfd_link_info *));
+  PARAMS ((bfd *, struct bfd_link_info *));
 extern boolean bfd_elf64_discard_info
-  PARAMS ((struct bfd_link_info *));
+  PARAMS ((bfd *, struct bfd_link_info *));
 
 /* Return an upper bound on the number of bytes required to store a
    copy of ABFD's program header table entries.  Return -1 if an error
--- bfd/elf-bfd.h.jj	Fri Dec  7 13:22:19 2001
+++ bfd/elf-bfd.h	Mon Dec 10 14:17:42 2001
@@ -223,6 +223,16 @@ struct elf_link_local_dynamic_entry
   Elf_Internal_Sym isym;
 };
 
+enum elf_link_info_type
+{
+  ELF_INFO_TYPE_NONE,
+  ELF_INFO_TYPE_STABS,
+  ELF_INFO_TYPE_MERGE,
+  ELF_INFO_TYPE_EH_FRAME,
+  ELF_INFO_TYPE_EH_FRAME_HDR,
+  ELF_INFO_TYPE_LAST
+};
+
 /* ELF linker hash table.  */
 
 struct elf_link_hash_table
@@ -848,11 +858,11 @@ struct bfd_elf_section_data
      no dynamic symbol for this section.  */
   long dynindx;
 
-  /* A pointer used for .stab linking optimizations.  */
-  PTR stab_info;
+  /* A pointer used for various section optimizations.  */
+  PTR sec_info;
 
-  /* A pointer used for SEC_MERGE optimizations.  */
-  PTR merge_info;
+  /* Type of that information.  */
+  enum elf_link_info_type sec_info_type;
 
   /* Group name, if this section is part of a group.  */
   const char *group_name;
@@ -1043,6 +1053,10 @@ struct elf_obj_tdata
   /* Used to determine if the e_flags field has been initialized */
   boolean flags_init;
 
+  /* Used to determine if PT_GNU_EH_FRAME segment header should be
+     created.  */
+  boolean eh_frame_hdr;
+
   /* Number of symbol version definitions we are about to emit.  */
   unsigned int cverdefs;
 
@@ -1271,6 +1285,18 @@ extern boolean _bfd_elf_strtab_emit
 extern void _bfd_elf_strtab_finalize
   PARAMS ((struct elf_strtab_hash *));
 
+extern boolean _bfd_elf_discard_section_eh_frame
+  PARAMS ((bfd *, struct bfd_link_info *, asection *, asection *,
+	   boolean (*) (bfd_vma, PTR), struct elf_reloc_cookie *));
+extern boolean _bfd_elf_discard_section_eh_frame_hdr
+  PARAMS ((bfd *, struct bfd_link_info *, asection *));
+extern bfd_vma _bfd_elf_eh_frame_section_offset
+  PARAMS ((bfd *, asection *, bfd_vma));
+extern boolean _bfd_elf_write_section_eh_frame
+  PARAMS ((bfd *, asection *, asection *, bfd_byte *));
+extern boolean _bfd_elf_write_section_eh_frame_hdr
+  PARAMS ((bfd *, asection *));
+
 extern boolean _bfd_elf_link_record_dynamic_symbol
   PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *));
 extern long _bfd_elf_link_lookup_local_dynindx
--- bfd/Makefile.am.jj	Fri Dec  7 12:59:23 2001
+++ bfd/Makefile.am	Fri Dec  7 16:37:25 2001
@@ -220,6 +220,7 @@ BFD32_BACKENDS = \
 	elf32.lo \
 	elflink.lo \
 	elf-strtab.lo \
+	elf-eh-frame.lo \
 	epoc-pe-arm.lo \
 	epoc-pei-arm.lo \
 	hp300bsd.lo \
@@ -361,6 +362,7 @@ BFD32_BACKENDS_CFILES = \
 	elf32.c \
 	elflink.c \
 	elf-strtab.c \
+	elf-eh-frame.c \
 	epoc-pe-arm.c \
 	epoc-pei-arm.c \
 	hp300bsd.c \
@@ -1140,6 +1142,7 @@ elflink.lo: elflink.c $(INCDIR)/filename
   elf-bfd.h $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h \
   $(INCDIR)/elf/external.h
 elf-strtab.lo: elf-strtab.c $(INCDIR)/filenames.h $(INCDIR)/hashtab.h
+elf-eh-frame.lo: elf-eh-frame.c
 epoc-pe-arm.lo: epoc-pe-arm.c pe-arm.c $(INCDIR)/filenames.h \
   coff-arm.c $(INCDIR)/coff/arm.h $(INCDIR)/coff/external.h \
   $(INCDIR)/coff/internal.h $(INCDIR)/coff/pe.h libcoff.h \
--- bfd/Makefile.in.jj	Fri Dec  7 12:59:23 2001
+++ bfd/Makefile.in	Fri Dec  7 16:37:55 2001
@@ -348,6 +348,7 @@ BFD32_BACKENDS = \
 	elf32.lo \
 	elflink.lo \
 	elf-strtab.lo \
+	elf-eh-frame.lo \
 	epoc-pe-arm.lo \
 	epoc-pei-arm.lo \
 	hp300bsd.lo \
@@ -490,6 +491,7 @@ BFD32_BACKENDS_CFILES = \
 	elf32.c \
 	elflink.c \
 	elf-strtab.c \
+	elf-eh-frame.c \
 	epoc-pe-arm.c \
 	epoc-pei-arm.c \
 	hp300bsd.c \
@@ -1684,6 +1686,7 @@ elflink.lo: elflink.c $(INCDIR)/filename
   elf-bfd.h $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h \
   $(INCDIR)/elf/external.h
 elf-strtab.lo: elf-strtab.c $(INCDIR)/filenames.h $(INCDIR)/hashtab.h
+elf-eh-frame.lo: elf-eh-frame.c
 epoc-pe-arm.lo: epoc-pe-arm.c pe-arm.c $(INCDIR)/filenames.h \
   coff-arm.c $(INCDIR)/coff/arm.h $(INCDIR)/coff/external.h \
   $(INCDIR)/coff/internal.h $(INCDIR)/coff/pe.h libcoff.h \
--- bfd/configure.in.jj	Fri Dec  7 12:59:50 2001
+++ bfd/configure.in	Fri Dec  7 16:38:21 2001
@@ -503,7 +503,7 @@ selarchs="$f"
 # Target backend .o files.
 tb=
 
-elf="elf.lo elflink.lo elf-strtab.lo dwarf1.lo"
+elf="elf.lo elflink.lo elf-strtab.lo elf-eh-frame.lo dwarf1.lo"
 
 for vec in $selvecs
 do
--- bfd/configure.jj	Fri Dec  7 12:59:50 2001
+++ bfd/configure	Fri Dec  7 16:38:33 2001
@@ -5910,7 +5910,7 @@ selarchs="$f"
 # Target backend .o files.
 tb=
 
-elf="elf.lo elflink.lo elf-strtab.lo dwarf1.lo"
+elf="elf.lo elflink.lo elf-strtab.lo elf-eh-frame.lo dwarf1.lo"
 
 for vec in $selvecs
 do
--- include/elf/common.h.jj	Fri Nov 16 21:50:36 2001
+++ include/elf/common.h	Mon Dec 10 13:22:21 2001
@@ -254,6 +254,8 @@ Foundation, Inc., 59 Temple Place - Suit
 #define PT_LOPROC	0x70000000	/* Processor-specific */
 #define PT_HIPROC	0x7FFFFFFF	/* Processor-specific */
 
+#define PT_GNU_EH_FRAME	(PT_LOOS + 0x474e550)
+
 /* Program segment permissions, in program header p_flags field.  */
 
 #define PF_X		(1 << 0)	/* Segment is executable */
--- include/bfdlink.h.jj	Fri Oct  5 13:04:02 2001
+++ include/bfdlink.h	Fri Dec  7 13:32:51 2001
@@ -298,6 +298,10 @@ struct bfd_link_info
      Setting this true may result in a non-sharable text segment.  */
   boolean nocopyreloc;
 
+  /* True if .eh_frame_htr section and PT_GNU_EH_FRAME ELF segment
+     should be created.  */
+  boolean eh_frame_hdr;
+
   /* How many spare .dynamic DT_NULL entries should be added?  */
   unsigned int spare_dynamic_tags;
 };
--- ld/emultempl/elf32.em.jj	Thu Nov 22 11:05:04 2001
+++ ld/emultempl/elf32.em	Fri Dec  7 13:32:51 2001
@@ -1329,7 +1329,7 @@ cat >>e${EMULATION_NAME}.c <<EOF
 static void
 gld${EMULATION_NAME}_finish ()
 {
-  if (bfd_elf${ELFSIZE}_discard_info (&link_info))
+  if (bfd_elf${ELFSIZE}_discard_info (output_bfd, &link_info))
     {
       /* Resize the sections.  */
       lang_size_sections (stat_ptr->head, abs_output_section,
@@ -1431,6 +1431,7 @@ cat >>e${EMULATION_NAME}.c <<EOF
 #define OPTION_DISABLE_NEW_DTAGS	(400)
 #define OPTION_ENABLE_NEW_DTAGS		(OPTION_DISABLE_NEW_DTAGS + 1)
 #define OPTION_GROUP			(OPTION_ENABLE_NEW_DTAGS + 1)
+#define OPTION_EH_FRAME_HDR		(OPTION_GROUP + 1)
 
 static struct option longopts[] =
 {
@@ -1444,6 +1445,7 @@ cat >>e${EMULATION_NAME}.c <<EOF
   {"disable-new-dtags", no_argument, NULL, OPTION_DISABLE_NEW_DTAGS},
   {"enable-new-dtags", no_argument, NULL, OPTION_ENABLE_NEW_DTAGS},
   {"enable-new-dtags", no_argument, NULL, OPTION_ENABLE_NEW_DTAGS},
+  {"eh-frame-hdr", no_argument, NULL, OPTION_EH_FRAME_HDR},
   {"Bgroup", no_argument, NULL, OPTION_GROUP},
   {"Bgroup", no_argument, NULL, OPTION_GROUP},
 EOF
@@ -1504,6 +1506,10 @@ cat >>e${EMULATION_NAME}.c <<EOF
       link_info.new_dtags = true;
       break;
 
+    case OPTION_EH_FRAME_HDR:
+      link_info.eh_frame_hdr = true;
+      break;
+
     case OPTION_GROUP:
       link_info.flags_1 |= (bfd_vma) DF_1_GROUP;
       /* Groups must be self-contained.  */
@@ -1579,6 +1585,7 @@ cat >>e${EMULATION_NAME}.c <<EOF
   fprintf (file, _("  -Bgroup\t\tSelects group name lookup rules for DSO\n"));
   fprintf (file, _("  --disable-new-dtags\tDisable new dynamic tags\n"));
   fprintf (file, _("  --enable-new-dtags\tEnable new dynamic tags\n"));
+  fprintf (file, _("  --eh-frame-hdr\tCreate .eh_frame_hdr section\n"));
   fprintf (file, _("  -z combreloc\t\tMerge dynamic relocs into one section and sort\n"));
   fprintf (file, _("  -z defs\t\tDisallows undefined symbols\n"));
   fprintf (file, _("  -z initfirst\t\tMark DSO to be initialized first at runtime\n"));
--- ld/emultempl/hppaelf.em.jj	Thu Nov 22 11:05:04 2001
+++ ld/emultempl/hppaelf.em	Fri Dec  7 13:32:51 2001
@@ -254,7 +254,7 @@ gld${EMULATION_NAME}_finish ()
      ie. doesn't affect any code, so we can delay resizing the
      sections.  It's likely we'll resize everything in the process of
      adding stubs.  */
-  if (bfd_elf${ELFSIZE}_discard_info (&link_info))
+  if (bfd_elf${ELFSIZE}_discard_info (output_bfd, &link_info))
     need_laying_out = 1;
 
   /* Call into the BFD backend to do the real work.  */
--- ld/scripttempl/elf.sc.jj	Wed Nov 28 21:05:08 2001
+++ ld/scripttempl/elf.sc	Fri Dec  7 13:32:51 2001
@@ -265,6 +265,7 @@ cat <<EOF
   ${CREATE_SHLIB-${SDATA2}}
   ${CREATE_SHLIB-${SBSS2}}
   ${OTHER_READONLY_SECTIONS}
+  .eh_frame_hdr : { *(.eh_frame_hdr) }
 
   /* Adjust the address for the data segment.  We want to adjust up to
      the same address within the page on the next page up.  */

	Jakub



More information about the Binutils mailing list