This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


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

[PATCH] Better merging of CIEs in .eh_frame optimizations


Hi!

As has been mentioned in the
http://sources.redhat.com/ml/binutils/2006-10/msg00044.html
thread, elf-eh-frame.c required FDEs referencing last CIE, not arbitrary
CIE within the same input section, which is easily broken with
.cfi_* directive generated .eh_frame.
The following patch fixes that and in addition to that puts kept CIEs
into a hash table, so that all identical CIEs can be merged, not just
all consecutive identical ones.  With this patch i686 glibc's .eh_frame
shrunk by 7.5%, without this patch it contained 184 CIEs, with this
patch only 4 (FYI .debug_frame has 1093 CIEs, so some merging was successful
even before this patch).  Tested with make check on i686 glibc and
binutils i386-linux and x86_64-linux make check.

Ok to commit?

2006-11-15  Jakub Jelinek  <jakub@redhat.com>

	* elf-eh-frame.c (struct cie): New type.
	(cie_compare): Removed.
	(cie_eq, cie_hash, cie_compute_hash): New functions.
	(_bfd_elf_discard_section_eh_frame): Rewrite not to rely on FDEs
	pointing only to last CIE and allow merging of any duplicate CIEs,
	not just duplicate consecutive CIEs.
	(_bfd_elf_discard_section_eh_frame_hdr): Delete cies hash table.
	* elf-bfd.h (struct cie_header, struct cie): Removed.
	(struct eh_frame_sec_info): Remove alloced field.
	(struct eh_frame_hdr_info): Remove last_cie, last_cie_sec
	and last_cie_inf fields.  Add cies field.

	* ld-elf/eh5.d: New test.
	* ld-elf/eh5.s: New file.
	* ld-elf/eh5a.s: New file.
	* ld-elf/eh5b.s: New file.

--- bfd/elf-eh-frame.c.jj	2006-10-03 15:15:39.000000000 +0200
+++ bfd/elf-eh-frame.c	2006-11-15 20:16:10.000000000 +0100
@@ -26,6 +26,30 @@
 
 #define EH_FRAME_HDR_SIZE 8
 
+struct cie
+{
+  unsigned int length;
+  unsigned int hash;
+  unsigned char version;
+  char augmentation[20];
+  bfd_vma code_align;
+  bfd_signed_vma data_align;
+  bfd_vma ra_column;
+  bfd_vma augmentation_size;
+  struct elf_link_hash_entry *personality;
+  asection *output_sec;
+  struct eh_cie_fde *cie_inf;
+  unsigned char per_encoding;
+  unsigned char lsda_encoding;
+  unsigned char fde_encoding;
+  unsigned char initial_insn_length;
+  unsigned char make_relative;
+  unsigned char make_lsda_relative;
+  unsigned char initial_instructions[50];
+};
+
+
+
 /* If *ITER hasn't reached END yet, read the next byte into *RESULT and
    move onto the next byte.  Return true on success.  */
 
@@ -182,10 +206,14 @@ write_value (bfd *abfd, bfd_byte *buf, b
 
 /* Return zero if C1 and C2 CIEs can be merged.  */
 
-static
-int cie_compare (struct cie *c1, struct cie *c2)
+static int
+cie_eq (const void *e1, const void *e2)
 {
-  if (c1->hdr.length == c2->hdr.length
+  const struct cie *c1 = e1;
+  const struct cie *c2 = e2;
+
+  if (c1->hash == c2->hash
+      && c1->length == c2->length
       && c1->version == c2->version
       && strcmp (c1->augmentation, c2->augmentation) == 0
       && strcmp (c1->augmentation, "eh") != 0
@@ -194,6 +222,7 @@ int cie_compare (struct cie *c1, struct 
       && c1->ra_column == c2->ra_column
       && c1->augmentation_size == c2->augmentation_size
       && c1->personality == c2->personality
+      && c1->output_sec == c2->output_sec
       && c1->per_encoding == c2->per_encoding
       && c1->lsda_encoding == c2->lsda_encoding
       && c1->fde_encoding == c2->fde_encoding
@@ -201,9 +230,38 @@ int cie_compare (struct cie *c1, struct 
       && memcmp (c1->initial_instructions,
 		 c2->initial_instructions,
 		 c1->initial_insn_length) == 0)
-    return 0;
+    return 1;
+
+  return 0;
+}
 
-  return 1;
+static hashval_t
+cie_hash (const void *e)
+{
+  const struct cie *c = e;
+  return c->hash;
+}
+
+static hashval_t
+cie_compute_hash (struct cie *c)
+{
+  hashval_t h = 0;
+  h = iterative_hash_object (c->length, h);
+  h = iterative_hash_object (c->version, h);
+  h = iterative_hash (c->augmentation, strlen (c->augmentation) + 1, h);
+  h = iterative_hash_object (c->code_align, h);
+  h = iterative_hash_object (c->data_align, h);
+  h = iterative_hash_object (c->ra_column, h);
+  h = iterative_hash_object (c->augmentation_size, h);
+  h = iterative_hash_object (c->personality, h);
+  h = iterative_hash_object (c->output_sec, h);
+  h = iterative_hash_object (c->per_encoding, h);
+  h = iterative_hash_object (c->lsda_encoding, h);
+  h = iterative_hash_object (c->fde_encoding, h);
+  h = iterative_hash_object (c->initial_insn_length, h);
+  h = iterative_hash (c->initial_instructions, c->initial_insn_length, h);
+  c->hash = h;
+  return h;
 }
 
 /* Return the number of extra bytes that we'll be inserting into
@@ -383,15 +441,24 @@ _bfd_elf_discard_section_eh_frame
   while (0)
 
   bfd_byte *ehbuf = NULL, *buf;
-  bfd_byte *last_cie, *last_fde;
-  struct eh_cie_fde *ent, *last_cie_inf, *this_inf;
-  struct cie_header hdr;
-  struct cie cie;
+  bfd_byte *last_fde;
+  struct eh_cie_fde *ent, *this_inf;
+  unsigned int hdr_length, hdr_id;
+  struct extended_cie
+    {
+      struct cie cie;
+      unsigned int offset;
+      unsigned int usage_count;
+      unsigned int entry;
+    } *ecies = NULL, *ecie;
+  unsigned int ecie_count = 0, ecie_alloced = 0;
+  struct cie *cie;
   struct elf_link_hash_table *htab;
   struct eh_frame_hdr_info *hdr_info;
   struct eh_frame_sec_info *sec_info = NULL;
-  unsigned int cie_usage_count, offset;
+  unsigned int offset;
   unsigned int ptr_size;
+  unsigned int entry_alloced;
 
   if (sec->size == 0)
     {
@@ -409,6 +476,9 @@ _bfd_elf_discard_section_eh_frame
   htab = elf_hash_table (info);
   hdr_info = &htab->eh_info;
 
+  if (hdr_info->cies == NULL && !info->relocatable)
+    hdr_info->cies = htab_try_create (1, cie_hash, cie_eq, free);
+
   /* Read the frame unwind information from abfd.  */
 
   REQUIRE (bfd_malloc_and_get_section (abfd, sec, &ehbuf));
@@ -431,15 +501,11 @@ _bfd_elf_discard_section_eh_frame
   REQUIRE (ptr_size != 0);
 
   buf = ehbuf;
-  last_cie = NULL;
-  last_cie_inf = NULL;
-  memset (&cie, 0, sizeof (cie));
-  cie_usage_count = 0;
   sec_info = bfd_zmalloc (sizeof (struct eh_frame_sec_info)
 			  + 99 * sizeof (struct eh_cie_fde));
   REQUIRE (sec_info);
 
-  sec_info->alloced = 100;
+  entry_alloced = 100;
 
 #define ENSURE_NO_RELOCS(buf)				\
   REQUIRE (!(cookie->rel < cookie->relend		\
@@ -466,116 +532,80 @@ _bfd_elf_discard_section_eh_frame
       bfd_size_type length;
       unsigned int set_loc_count;
 
-      if (sec_info->count == sec_info->alloced)
+      if (sec_info->count == entry_alloced)
 	{
-	  struct eh_cie_fde *old_entry = sec_info->entry;
 	  sec_info = bfd_realloc (sec_info,
 				  sizeof (struct eh_frame_sec_info)
-				  + ((sec_info->alloced + 99)
+				  + ((entry_alloced + 99)
 				     * sizeof (struct eh_cie_fde)));
 	  REQUIRE (sec_info);
 
-	  memset (&sec_info->entry[sec_info->alloced], 0,
+	  memset (&sec_info->entry[entry_alloced], 0,
 		  100 * sizeof (struct eh_cie_fde));
-	  sec_info->alloced += 100;
-
-	  /* Now fix any pointers into the array.  */
-	  if (last_cie_inf >= old_entry
-	      && last_cie_inf < old_entry + sec_info->count)
-	    last_cie_inf = sec_info->entry + (last_cie_inf - old_entry);
+	  entry_alloced += 100;
 	}
 
       this_inf = sec_info->entry + sec_info->count;
       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->size)
-	{
-	  hdr.length = 0;
-	  hdr.id = (unsigned int) -1;
-	  end = buf;
-	}
-      else
-	{
-	  /* Read the length of the entry.  */
-	  REQUIRE (skip_bytes (&buf, ehbuf + sec->size, 4));
-	  hdr.length = bfd_get_32 (abfd, buf - 4);
+	break;
 
-	  /* 64-bit .eh_frame is not supported.  */
-	  REQUIRE (hdr.length != 0xffffffff);
+      /* Read the length of the entry.  */
+      REQUIRE (skip_bytes (&buf, ehbuf + sec->size, 4));
+      hdr_length = bfd_get_32 (abfd, buf - 4);
 
-	  /* The CIE/FDE must be fully contained in this input section.  */
-	  REQUIRE ((bfd_size_type) (buf - ehbuf) + hdr.length <= sec->size);
-	  end = buf + hdr.length;
+      /* 64-bit .eh_frame is not supported.  */
+      REQUIRE (hdr_length != 0xffffffff);
 
-	  this_inf->offset = last_fde - ehbuf;
-	  this_inf->size = 4 + hdr.length;
+      /* The CIE/FDE must be fully contained in this input section.  */
+      REQUIRE ((bfd_size_type) (buf - ehbuf) + hdr_length <= sec->size);
+      end = buf + hdr_length;
 
-	  if (hdr.length == 0)
-	    {
-	      /* A zero-length CIE should only be found at the end of
-		 the section.  */
-	      REQUIRE ((bfd_size_type) (buf - ehbuf) == sec->size);
-	      ENSURE_NO_RELOCS (buf);
-	      sec_info->count++;
-	      /* Now just finish last encountered CIE processing and break
-		 the loop.  */
-	      hdr.id = (unsigned int) -1;
-	    }
-	  else
-	    {
-	      REQUIRE (skip_bytes (&buf, end, 4));
-	      hdr.id = bfd_get_32 (abfd, buf - 4);
-	      REQUIRE (hdr.id != (unsigned int) -1);
-	    }
+      this_inf->offset = last_fde - ehbuf;
+      this_inf->size = 4 + hdr_length;
+
+      if (hdr_length == 0)
+	{
+	  /* A zero-length CIE should only be found at the end of
+	     the section.  */
+	  REQUIRE ((bfd_size_type) (buf - ehbuf) == sec->size);
+	  ENSURE_NO_RELOCS (buf);
+	  sec_info->count++;
+	  break;
 	}
 
-      if (hdr.id == 0 || hdr.id == (unsigned int) -1)
+      REQUIRE (skip_bytes (&buf, end, 4));
+      hdr_id = bfd_get_32 (abfd, buf - 4);
+
+      if (hdr_id == 0)
 	{
 	  unsigned int initial_insn_length;
 
 	  /* CIE  */
-	  if (last_cie != NULL)
+	  this_inf->cie = 1;
+
+	  if (ecie_count == ecie_alloced)
 	    {
-	      /* Now check if this CIE is identical to the 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 it.  */
-	      if ((!info->relocatable
-		   && hdr_info->last_cie_sec
-		   && (sec->output_section
-		       == hdr_info->last_cie_sec->output_section)
-		   && cie_compare (&cie, &hdr_info->last_cie) == 0)
-		  || cie_usage_count == 0)
-		last_cie_inf->removed = 1;
-	      else
-		{
-		  hdr_info->last_cie = cie;
-		  hdr_info->last_cie_sec = sec;
-		  last_cie_inf->make_relative = cie.make_relative;
-		  last_cie_inf->make_lsda_relative = cie.make_lsda_relative;
-		  last_cie_inf->per_encoding_relative
-		    = (cie.per_encoding & 0x70) == DW_EH_PE_pcrel;
-		}
+	      ecies = bfd_realloc (ecies,
+				   (ecie_alloced + 20) * sizeof (*ecies));
+	      REQUIRE (ecies);
+	      memset (&ecies[ecie_alloced], 0, 20 * sizeof (*ecies));
+	      ecie_alloced += 20;
 	    }
 
-	  if (hdr.id == (unsigned int) -1)
-	    break;
-
-	  last_cie_inf = this_inf;
-	  this_inf->cie = 1;
-
-	  cie_usage_count = 0;
-	  memset (&cie, 0, sizeof (cie));
-	  cie.hdr = hdr;
+	  cie = &ecies[ecie_count].cie;
+	  ecies[ecie_count].offset = this_inf->offset;
+	  ecies[ecie_count++].entry = sec_info->count;
+	  cie->length = hdr_length;
 	  start = buf;
-	  REQUIRE (read_byte (&buf, end, &cie.version));
+	  REQUIRE (read_byte (&buf, end, &cie->version));
 
 	  /* Cannot handle unknown versions.  */
-	  REQUIRE (cie.version == 1 || cie.version == 3);
-	  REQUIRE (strlen ((char *) buf) < sizeof (cie.augmentation));
+	  REQUIRE (cie->version == 1 || cie->version == 3);
+	  REQUIRE (strlen ((char *) buf) < sizeof (cie->augmentation));
 
-	  strcpy (cie.augmentation, (char *) buf);
+	  strcpy (cie->augmentation, (char *) buf);
 	  buf = (bfd_byte *) strchr ((char *) buf, '\0') + 1;
 	  ENSURE_NO_RELOCS (buf);
 	  if (buf[0] == 'e' && buf[1] == 'h')
@@ -587,26 +617,26 @@ _bfd_elf_discard_section_eh_frame
 	      REQUIRE (skip_bytes (&buf, end, ptr_size));
 	      SKIP_RELOCS (buf);
 	    }
-	  REQUIRE (read_uleb128 (&buf, end, &cie.code_align));
-	  REQUIRE (read_sleb128 (&buf, end, &cie.data_align));
-	  if (cie.version == 1)
+	  REQUIRE (read_uleb128 (&buf, end, &cie->code_align));
+	  REQUIRE (read_sleb128 (&buf, end, &cie->data_align));
+	  if (cie->version == 1)
 	    {
 	      REQUIRE (buf < end);
-	      cie.ra_column = *buf++;
+	      cie->ra_column = *buf++;
 	    }
 	  else
-	    REQUIRE (read_uleb128 (&buf, end, &cie.ra_column));
+	    REQUIRE (read_uleb128 (&buf, end, &cie->ra_column));
 	  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;
+	  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++;
-		  REQUIRE (read_uleb128 (&buf, end, &cie.augmentation_size));
+		  REQUIRE (read_uleb128 (&buf, end, &cie->augmentation_size));
 	  	  ENSURE_NO_RELOCS (buf);
 		}
 
@@ -614,14 +644,14 @@ _bfd_elf_discard_section_eh_frame
 		switch (*aug++)
 		  {
 		  case 'L':
-		    REQUIRE (read_byte (&buf, end, &cie.lsda_encoding));
+		    REQUIRE (read_byte (&buf, end, &cie->lsda_encoding));
 		    ENSURE_NO_RELOCS (buf);
-		    REQUIRE (get_DW_EH_PE_width (cie.lsda_encoding, ptr_size));
+		    REQUIRE (get_DW_EH_PE_width (cie->lsda_encoding, ptr_size));
 		    break;
 		  case 'R':
-		    REQUIRE (read_byte (&buf, end, &cie.fde_encoding));
+		    REQUIRE (read_byte (&buf, end, &cie->fde_encoding));
 		    ENSURE_NO_RELOCS (buf);
-		    REQUIRE (get_DW_EH_PE_width (cie.fde_encoding, ptr_size));
+		    REQUIRE (get_DW_EH_PE_width (cie->fde_encoding, ptr_size));
 		    break;
 		  case 'S':
 		    break;
@@ -629,11 +659,11 @@ _bfd_elf_discard_section_eh_frame
 		    {
 		      int per_width;
 
-		      REQUIRE (read_byte (&buf, end, &cie.per_encoding));
-		      per_width = get_DW_EH_PE_width (cie.per_encoding,
+		      REQUIRE (read_byte (&buf, end, &cie->per_encoding));
+		      per_width = get_DW_EH_PE_width (cie->per_encoding,
 						      ptr_size);
 		      REQUIRE (per_width);
-		      if ((cie.per_encoding & 0xf0) == DW_EH_PE_aligned)
+		      if ((cie->per_encoding & 0xf0) == DW_EH_PE_aligned)
 			{
 			  length = -(buf - ehbuf) & (per_width - 1);
 			  REQUIRE (skip_bytes (&buf, end, length));
@@ -663,7 +693,7 @@ _bfd_elf_discard_section_eh_frame
 				h = (struct elf_link_hash_entry *)
 				    h->root.u.i.link;
 
-			      cie.personality = h;
+			      cie->personality = h;
 			    }
 			  /* Cope with MIPS-style composite relocations.  */
 			  do
@@ -671,6 +701,7 @@ _bfd_elf_discard_section_eh_frame
 			  while (GET_RELOC (buf) != NULL);
 			}
 		      REQUIRE (skip_bytes (&buf, end, per_width));
+		      REQUIRE (cie->personality);
 		    }
 		    break;
 		  default:
@@ -686,18 +717,18 @@ _bfd_elf_discard_section_eh_frame
 		  ->elf_backend_can_make_relative_eh_frame
 		  (abfd, info, sec)))
 	    {
-	      if ((cie.fde_encoding & 0xf0) == DW_EH_PE_absptr)
-		cie.make_relative = 1;
+	      if ((cie->fde_encoding & 0xf0) == DW_EH_PE_absptr)
+		cie->make_relative = 1;
 	      /* If the CIE doesn't already have an 'R' entry, it's fairly
 		 easy to add one, provided that there's no aligned data
 		 after the augmentation string.  */
-	      else if (cie.fde_encoding == DW_EH_PE_omit
-		       && (cie.per_encoding & 0xf0) != DW_EH_PE_aligned)
+	      else if (cie->fde_encoding == DW_EH_PE_omit
+		       && (cie->per_encoding & 0xf0) != DW_EH_PE_aligned)
 		{
-		  if (*cie.augmentation == 0)
+		  if (*cie->augmentation == 0)
 		    this_inf->add_augmentation_size = 1;
 		  this_inf->add_fde_encoding = 1;
-		  cie.make_relative = 1;
+		  cie->make_relative = 1;
 		}
 	    }
 
@@ -705,30 +736,36 @@ _bfd_elf_discard_section_eh_frame
 	      && (get_elf_backend_data (abfd)
 		  ->elf_backend_can_make_lsda_relative_eh_frame
 		  (abfd, info, sec))
-	      && (cie.lsda_encoding & 0xf0) == DW_EH_PE_absptr)
-	    cie.make_lsda_relative = 1;
+	      && (cie->lsda_encoding & 0xf0) == DW_EH_PE_absptr)
+	    cie->make_lsda_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;
+	  if (cie->fde_encoding == DW_EH_PE_omit)
+	    cie->fde_encoding = DW_EH_PE_absptr;
 
 	  initial_insn_length = end - buf;
-	  if (initial_insn_length <= 50)
+	  if (initial_insn_length <= sizeof (cie->initial_instructions))
 	    {
-	      cie.initial_insn_length = initial_insn_length;
-	      memcpy (cie.initial_instructions, buf, initial_insn_length);
+	      cie->initial_insn_length = initial_insn_length;
+	      memcpy (cie->initial_instructions, buf, initial_insn_length);
 	    }
 	  insns = buf;
 	  buf += initial_insn_length;
 	  ENSURE_NO_RELOCS (buf);
-	  last_cie = last_fde;
 	}
       else
 	{
-	  /* Ensure this FDE uses the last CIE encountered.  */
-	  REQUIRE (last_cie);
-	  REQUIRE (hdr.id == (unsigned int) (buf - 4 - last_cie));
+	  /* Find the corresponding CIE.  */
+	  unsigned int cie_offset = this_inf->offset + 4 - hdr_id;
+	  for (ecie = ecies; ecie < ecies + ecie_count; ++ecie)
+	    if (cie_offset == ecie->offset)
+	      break;
+
+	  /* Ensure this FDE references one of the CIEs in this input
+	     section.  */
+	  REQUIRE (ecie != ecies + ecie_count);
+	  cie = &ecie->cie;
 
 	  ENSURE_NO_RELOCS (buf);
 	  REQUIRE (GET_RELOC (buf));
@@ -740,9 +777,9 @@ _bfd_elf_discard_section_eh_frame
 	  else
 	    {
 	      if (info->shared
-		  && (((cie.fde_encoding & 0xf0) == DW_EH_PE_absptr
-		       && cie.make_relative == 0)
-		      || (cie.fde_encoding & 0xf0) == DW_EH_PE_aligned))
+		  && (((cie->fde_encoding & 0xf0) == DW_EH_PE_absptr
+		       && cie->make_relative == 0)
+		      || (cie->fde_encoding & 0xf0) == DW_EH_PE_aligned))
 		{
 		  /* If a shared library uses absolute pointers
 		     which we cannot turn into PC relative,
@@ -750,16 +787,18 @@ _bfd_elf_discard_section_eh_frame
 		     since it is affected by runtime relocations.  */
 		  hdr_info->table = FALSE;
 		}
-	      cie_usage_count++;
+	      ecie->usage_count++;
 	      hdr_info->fde_count++;
+	      this_inf->cie_inf = (void *) (ecie - ecies);
 	    }
+
 	  /* Skip the initial location and address range.  */
 	  start = buf;
-	  length = get_DW_EH_PE_width (cie.fde_encoding, ptr_size);
+	  length = get_DW_EH_PE_width (cie->fde_encoding, ptr_size);
 	  REQUIRE (skip_bytes (&buf, end, 2 * length));
 
 	  /* Skip the augmentation size, if present.  */
-	  if (cie.augmentation[0] == 'z')
+	  if (cie->augmentation[0] == 'z')
 	    REQUIRE (read_uleb128 (&buf, end, &length));
 	  else
 	    length = 0;
@@ -767,12 +806,12 @@ _bfd_elf_discard_section_eh_frame
 	  /* Of the supported augmentation characters above, only 'L'
 	     adds augmentation data to the FDE.  This code would need to
 	     be adjusted if any future augmentations do the same thing.  */
-	  if (cie.lsda_encoding != DW_EH_PE_omit)
+	  if (cie->lsda_encoding != DW_EH_PE_omit)
 	    {
 	      this_inf->lsda_offset = buf - start;
 	      /* If there's no 'z' augmentation, we don't know where the
 		 CFA insns begin.  Assume no padding.  */
-	      if (cie.augmentation[0] != 'z')
+	      if (cie->augmentation[0] != 'z')
 		length = end - buf;
 	    }
 
@@ -780,14 +819,14 @@ _bfd_elf_discard_section_eh_frame
 	  REQUIRE (skip_bytes (&buf, end, length));
 	  insns = buf;
 
-	  buf = last_fde + 4 + hdr.length;
+	  buf = last_fde + 4 + hdr_length;
 	  SKIP_RELOCS (buf);
 	}
 
       /* Try to interpret the CFA instructions and find the first
 	 padding nop.  Shrink this_inf's size so that it doesn't
 	 include the padding.  */
-      length = get_DW_EH_PE_width (cie.fde_encoding, ptr_size);
+      length = get_DW_EH_PE_width (cie->fde_encoding, ptr_size);
       set_loc_count = 0;
       insns_end = skip_non_nops (insns, end, length, &set_loc_count);
       /* If we don't understand the CFA instructions, we can't know
@@ -798,9 +837,14 @@ _bfd_elf_discard_section_eh_frame
 	  || (set_loc_count && this_inf->cie))
 	goto free_no_table;
       this_inf->size -= end - insns_end;
+      if (insns_end != end && this_inf->cie)
+	{
+	  cie->initial_insn_length -= end - insns_end;
+	  cie->length -= end - insns_end;
+	}
       if (set_loc_count
-	  && ((cie.fde_encoding & 0xf0) == DW_EH_PE_pcrel
-	      || cie.make_relative))
+	  && ((cie->fde_encoding & 0xf0) == DW_EH_PE_pcrel
+	      || cie->make_relative))
 	{
 	  unsigned int cnt;
 	  bfd_byte *p;
@@ -819,28 +863,66 @@ _bfd_elf_discard_section_eh_frame
 	    }
 	}
 
-      this_inf->fde_encoding = cie.fde_encoding;
-      this_inf->lsda_encoding = cie.lsda_encoding;
+      this_inf->fde_encoding = cie->fde_encoding;
+      this_inf->lsda_encoding = cie->lsda_encoding;
       sec_info->count++;
     }
 
   elf_section_data (sec)->sec_info = sec_info;
   sec->sec_info_type = ELF_INFO_TYPE_EH_FRAME;
 
+  /* Look at all CIEs in this section and determine which can be
+     removed as unused, which can be merged with previous duplicate
+     CIEs and which need to be kept.  */
+  for (ecie = ecies; ecie < ecies + ecie_count; ++ecie)
+    {
+      if (ecie->usage_count == 0)
+	{
+	  sec_info->entry[ecie->entry].removed = 1;
+	  continue;
+	}
+      ecie->cie.output_sec = sec->output_section;
+      ecie->cie.cie_inf = sec_info->entry + ecie->entry;
+      cie_compute_hash (&ecie->cie);
+      if (hdr_info->cies != NULL)
+	{
+	  void **loc = htab_find_slot_with_hash (hdr_info->cies, &ecie->cie,
+						 ecie->cie.hash, INSERT);
+	  if (loc != NULL)
+	    {
+	      if (*loc != HTAB_EMPTY_ENTRY)
+		{
+		  sec_info->entry[ecie->entry].removed = 1;
+		  ecie->cie.cie_inf = ((struct cie *) *loc)->cie_inf;
+		  continue;
+		}
+
+	      *loc = malloc (sizeof (struct cie));
+	      if (*loc == NULL)
+		*loc = HTAB_DELETED_ENTRY;
+	      else
+		memcpy (*loc, &ecie->cie, sizeof (struct cie));
+	    }
+	}
+      ecie->cie.cie_inf->make_relative = ecie->cie.make_relative;
+      ecie->cie.cie_inf->make_lsda_relative = ecie->cie.make_lsda_relative;
+      ecie->cie.cie_inf->per_encoding_relative
+	= (ecie->cie.per_encoding & 0x70) == DW_EH_PE_pcrel;
+    }
+
   /* Ok, now we can assign new offsets.  */
   offset = 0;
-  last_cie_inf = hdr_info->last_cie_inf;
   for (ent = sec_info->entry; ent < sec_info->entry + sec_info->count; ++ent)
     if (!ent->removed)
       {
-	if (ent->cie)
-	  last_cie_inf = ent;
-	else
-	  ent->cie_inf = last_cie_inf;
+	if (!ent->cie)
+	  {
+	    ecie = ecies + (unsigned long) ent->cie_inf;
+	    ent->cie_inf = ecie->cie.cie_inf;
+	  }
 	ent->new_offset = offset;
 	offset += size_of_output_cie_fde (ent, ptr_size);
       }
-  hdr_info->last_cie_inf = last_cie_inf;
 
   /* Resize the sec as needed.  */
   sec->rawsize = sec->size;
@@ -849,6 +931,8 @@ _bfd_elf_discard_section_eh_frame
     sec->flags |= SEC_EXCLUDE;
 
   free (ehbuf);
+  if (ecies)
+    free (ecies);
   return offset != sec->rawsize;
 
 free_no_table:
@@ -856,8 +940,9 @@ free_no_table:
     free (ehbuf);
   if (sec_info)
     free (sec_info);
+  if (ecies)
+    free (ecies);
   hdr_info->table = FALSE;
-  hdr_info->last_cie.hdr.length = 0;
   return FALSE;
 
 #undef REQUIRE
@@ -876,6 +961,13 @@ _bfd_elf_discard_section_eh_frame_hdr (b
 
   htab = elf_hash_table (info);
   hdr_info = &htab->eh_info;
+
+  if (hdr_info->cies != NULL)
+    {
+      htab_delete (hdr_info->cies);
+      hdr_info->cies = NULL;
+    }
+
   sec = hdr_info->hdr_sec;
   if (sec == NULL)
     return FALSE;
--- bfd/elf-bfd.h.jj	2006-11-03 08:18:12.000000000 +0100
+++ bfd/elf-bfd.h	2006-11-15 16:51:56.000000000 +0100
@@ -261,31 +261,6 @@ struct elf_link_loaded_list
 };
 
 /* Structures used by the eh_frame optimization code.  */
-struct cie_header
-{
-  unsigned int length;
-  unsigned int id;
-};
-
-struct cie
-{
-  struct cie_header hdr;
-  unsigned char version;
-  char augmentation[20];
-  bfd_vma code_align;
-  bfd_signed_vma data_align;
-  bfd_vma ra_column;
-  bfd_vma 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 make_lsda_relative;
-  unsigned char initial_instructions[50];
-};
-
 struct eh_cie_fde
 {
   /* For FDEs, this points to the CIE used.  */
@@ -310,7 +285,6 @@ struct eh_cie_fde
 struct eh_frame_sec_info
 {
   unsigned int count;
-  unsigned int alloced;
   struct eh_cie_fde entry[1];
 };
 
@@ -320,11 +294,11 @@ struct eh_frame_array_ent
   bfd_vma fde;
 };
 
+struct htab;
+
 struct eh_frame_hdr_info
 {
-  struct cie last_cie;
-  asection *last_cie_sec;
-  struct eh_cie_fde *last_cie_inf;
+  struct htab *cies;
   asection *hdr_sec;
   unsigned int fde_count, array_count;
   struct eh_frame_array_ent *array;
--- ld/testsuite/ld-elf/eh5.d.jj	2006-11-16 10:56:35.000000000 +0100
+++ ld/testsuite/ld-elf/eh5.d	2006-11-16 12:09:11.000000000 +0100
@@ -0,0 +1,161 @@
+#source: eh5.s
+#source: eh5a.s
+#source: eh5b.s
+#ld:
+#readelf: -wf
+#target: x86_64-*-* i?86-*-*
+
+The section .eh_frame contains:
+
+00000000 0000001[04] 00000000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: .*
+  Return address column: .*
+  Augmentation data:     1b
+
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+#...
+0000001[48] 00000014 0000001[8c] FDE cie=00000000 pc=.*
+  DW_CFA_advance_loc: 4 to .*
+  DW_CFA_def_cfa: r0 ofs 16
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+
+000000(2c|30) 00000014 00000000 CIE
+  Version:               1
+  Augmentation:          "zPR"
+  Code alignment factor: 1
+  Data alignment factor: .*
+  Return address column: .*
+  Augmentation data:     03 .. .. .. .. 1b
+
+  DW_CFA_nop
+
+0000004[48] 00000014 0000001c FDE cie=000000(2c|30) pc=.*
+  DW_CFA_advance_loc: 4 to .*
+  DW_CFA_def_cfa: r0 ofs 16
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+
+000000(5c|60) 00000014 0000006[04] FDE cie=00000000 pc=.*
+  DW_CFA_advance_loc: 4 to .*
+  DW_CFA_def_cfa: r0 ofs 16
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+
+0000007[48] 0000001[8c] 00000000 CIE
+  Version:               1
+  Augmentation:          "zPLR"
+  Code alignment factor: 1
+  Data alignment factor: .*
+  Return address column: .*
+  Augmentation data:     03 .. .. .. .. 0c 1b
+
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+#...
+0000009[08] 0000001c 0000002[04] FDE cie=0000007[48] pc=.*
+  Augmentation data:     (ef be ad de 00 00 00 00|00 00 00 00 de ad be ef)
+
+  DW_CFA_advance_loc: 4 to .*
+  DW_CFA_def_cfa: r0 ofs 16
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+
+000000b[08] 0000001[04] 00000000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: .*
+  Return address column: .*
+  Augmentation data:     1b
+
+  DW_CFA_def_cfa: r0 ofs 16
+#...
+000000(c4|d0) 0000001[04] 0000001[8c] FDE cie=000000b[08] pc=.*
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+#...
+000000[de]8 00000014 00000000 CIE
+  Version:               1
+  Augmentation:          "zPR"
+  Code alignment factor: 1
+  Data alignment factor: .*
+  Return address column: .*
+  Augmentation data:     03 .. .. .. .. 1b
+
+  DW_CFA_nop
+
+00000(0f|10)0 00000014 0000001c FDE cie=000000[de]8 pc=.*
+  DW_CFA_advance_loc: 4 to .*
+  DW_CFA_def_cfa: r0 ofs 16
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+
+000001[01]8 0000001[04] 000000(5c|64) FDE cie=000000b[08] pc=.*
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+#...
+000001(1c|30) 0000001[8c] 00000000 CIE
+  Version:               1
+  Augmentation:          "zPLR"
+  Code alignment factor: 1
+  Data alignment factor: .*
+  Return address column: .*
+  Augmentation data:     03 .. .. .. .. 0c 1b
+
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+#...
+000001(38|50) 0000001c 0000002[04] FDE cie=000001(1c|30) pc=.*
+  Augmentation data:     (ef be ad de 00 00 00 00|00 00 00 00 de ad be ef)
+
+  DW_CFA_advance_loc: 4 to .*
+  DW_CFA_def_cfa: r0 ofs 16
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+
+000001(58|70) 00000014 000001(5c|74) FDE cie=00000000 pc=.*
+  DW_CFA_advance_loc: 4 to .*
+  DW_CFA_def_cfa: r0 ofs 16
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+
+000001(70|88) 00000014 000001(48|5c) FDE cie=000000(2c|30) pc=.*
+  DW_CFA_advance_loc: 4 to .*
+  DW_CFA_def_cfa: r0 ofs 16
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+
+000001(88|a0) 00000014 000001(8c|a4) FDE cie=00000000 pc=.*
+  DW_CFA_advance_loc: 4 to .*
+  DW_CFA_def_cfa: r0 ofs 16
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+
+000001(a0|b8) 0000001c 000001(30|44) FDE cie=0000007[48] pc=.*
+  Augmentation data:     (ef be ad de 00 00 00 00|00 00 00 00 de ad be ef)
+
+  DW_CFA_advance_loc: 4 to .*
+  DW_CFA_def_cfa: r0 ofs 16
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+
--- ld/testsuite/ld-elf/eh5.s.jj	2006-11-16 10:45:45.000000000 +0100
+++ ld/testsuite/ld-elf/eh5.s	2006-11-16 10:45:36.000000000 +0100
@@ -0,0 +1,29 @@
+	.text
+	.cfi_startproc simple
+	.long 0
+	.cfi_def_cfa 0, 16
+	.long 0
+	.cfi_endproc
+
+	.cfi_startproc simple
+	.cfi_personality 3, my_personality_v0
+	.long 0
+	.cfi_def_cfa 0, 16
+	.cfi_endproc
+
+	.cfi_startproc simple
+	.long 0
+	.cfi_def_cfa 0, 16
+	.long 0
+	.cfi_endproc
+
+	.cfi_startproc simple
+	.cfi_personality 3, my_personality_v0
+	.cfi_lsda 12, 0xdeadbeef
+	.long 0
+	.cfi_def_cfa 0, 16
+	.cfi_endproc
+
+	.globl my_personality_v0
+my_personality_v0:
+	.long 0
--- ld/testsuite/ld-elf/eh5a.s.jj	2006-11-16 10:45:48.000000000 +0100
+++ ld/testsuite/ld-elf/eh5a.s	2006-11-16 10:55:06.000000000 +0100
@@ -0,0 +1,27 @@
+	.text
+	.cfi_startproc simple
+	.cfi_def_cfa 0, 16
+	.long 0
+	.cfi_endproc
+
+	.cfi_startproc simple
+	.cfi_personality 3, my_personality_v1
+	.long 0
+	.cfi_def_cfa 0, 16
+	.cfi_endproc
+
+	.cfi_startproc simple
+	.cfi_def_cfa 0, 16
+	.long 0
+	.cfi_endproc
+
+	.cfi_startproc simple
+	.cfi_personality 3, my_personality_v1
+	.cfi_lsda 12, 0xdeadbeef
+	.long 0
+	.cfi_def_cfa 0, 16
+	.cfi_endproc
+
+	.globl my_personality_v1
+my_personality_v1:
+	.long 0
--- ld/testsuite/ld-elf/eh5b.s.jj	2006-11-16 10:55:15.000000000 +0100
+++ ld/testsuite/ld-elf/eh5b.s	2006-11-16 11:09:52.000000000 +0100
@@ -0,0 +1,29 @@
+	.text
+	.cfi_startproc simple
+	.long 0
+	.cfi_def_cfa 0, 16
+	.long 0
+	.cfi_endproc
+
+	.cfi_startproc simple
+	.cfi_personality 3, my_personality_v0
+	.long 0
+	.cfi_def_cfa 0, 16
+	.cfi_endproc
+
+	.cfi_startproc simple
+	.long 0
+	.cfi_def_cfa 0, 16
+	.long 0
+	.cfi_endproc
+
+	.cfi_startproc simple
+	.cfi_personality 3, my_personality_v0
+	.cfi_lsda 12, 0xdeadbeef
+	.long 0
+	.cfi_def_cfa 0, 16
+	.cfi_endproc
+
+	.globl _start
+_start:
+	.long 0

	Jakub


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