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]

Re: ld corrupting .cfi_label uses


On Fri, Mar 03, 2017 at 01:37:17AM -0700, Jan Beulich wrote:
> My problem here is that I don't see any way to know whether
> there are symbols (including local ones, but excluding the
> section one) or relocations referring to a particular section
> (other than going through the raw object file of course), for
> easy checking in _bfd_elf_write_section_eh_frame(), or
> even better already in _bfd_elf_parse_eh_frame_entry().

Here's a prototype patch.  It hasn't had much testing, and needs a few
testcases added, particularly for labels in odd parts of a CIE or FDE.
If you could run with this, that would be good.

diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index 9e3d6f5..ad97c61 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -326,6 +326,12 @@ struct eh_cie_fde
 	 or 0 if the CIE doesn't have any.  */
       unsigned int personality_offset : 8;
 
+      /* Length of augmentation.  aug_str_len is the length of the
+	 string including null terminator.  aug_data_len is the length
+	 of the rest up to the initial insns.  */
+      unsigned int aug_str_len : 3;
+      unsigned int aug_data_len : 5;
+
       /* True if we have marked relocations associated with this CIE.  */
       unsigned int gc_mark : 1;
 
@@ -354,7 +360,7 @@ struct eh_cie_fde
       unsigned int merged : 1;
 
       /* Unused bits.  */
-      unsigned int pad1 : 17;
+      unsigned int pad1 : 9;
     } cie;
   } u;
   unsigned int reloc_index;
@@ -2123,6 +2129,8 @@ extern bfd_boolean _bfd_elf_end_eh_frame_parsing
 extern bfd_boolean _bfd_elf_discard_section_eh_frame
   (bfd *, struct bfd_link_info *, asection *,
    bfd_boolean (*) (bfd_vma, void *), struct elf_reloc_cookie *);
+extern bfd_boolean _bfd_elf_adjust_eh_frame_global_symbol
+  (struct elf_link_hash_entry *, void *);
 extern bfd_boolean _bfd_elf_discard_section_eh_frame_hdr
   (bfd *, struct bfd_link_info *);
 extern bfd_vma _bfd_elf_eh_frame_section_offset
diff --git a/bfd/elf-eh-frame.c b/bfd/elf-eh-frame.c
index 6967f37..719b7c1 100644
--- a/bfd/elf-eh-frame.c
+++ b/bfd/elf-eh-frame.c
@@ -757,6 +757,7 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info,
 
 	  strcpy (cie->augmentation, (char *) buf);
 	  buf = (bfd_byte *) strchr ((char *) buf, '\0') + 1;
+	  this_inf->u.cie.aug_str_len = buf - start - 1;
 	  ENSURE_NO_RELOCS (buf);
 	  if (buf[0] == 'e' && buf[1] == 'h')
 	    {
@@ -845,6 +846,8 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info,
 		    goto free_no_table;
 		  }
 	    }
+	  this_inf->u.cie.aug_data_len
+	    = buf - start - 1 - this_inf->u.cie.aug_str_len;
 
 	  /* For shared libraries, try to get rid of as many RELATIVE relocs
 	     as possible.  */
@@ -1327,6 +1330,143 @@ find_merged_cie (bfd *abfd, struct bfd_link_info *info, asection *sec,
   return new_cie->cie_inf;
 }
 
+/* For a given OFFSET in SEC, return the delta to the new location
+   after .eh_frame editing.  */
+
+static bfd_signed_vma
+offset_adjust (bfd_vma offset, asection *sec)
+{
+  struct eh_frame_sec_info *sec_info
+    = (struct eh_frame_sec_info *) elf_section_data (sec)->sec_info;
+  unsigned int lo, hi, mid;
+  struct eh_cie_fde *ent;
+  bfd_signed_vma delta;
+
+  lo = 0;
+  hi = sec_info->count;
+  if (hi == 0)
+    return 0;
+
+  while (lo < hi)
+    {
+      mid = (lo + hi) / 2;
+      ent = &sec_info->entry[mid];
+      if (offset < ent->offset)
+	hi = mid;
+      else if (mid + 1 >= hi)
+	break;
+      else if (offset >= ent[1].offset)
+	lo = mid + 1;
+      else
+	break;
+    }
+
+  if (!ent->removed)
+    delta = (bfd_vma) ent->new_offset - (bfd_vma) ent->offset;
+  else if (ent->cie && ent->u.cie.merged)
+    {
+      struct eh_cie_fde *cie = ent->u.cie.u.merged_with;
+      delta = ((bfd_vma) cie->new_offset + cie->u.cie.u.sec->output_offset
+	       - (bfd_vma) ent->offset - sec->output_offset);
+    }
+  else
+    {
+      /* Is putting the symbol on the next entry best for a deleted
+	 CIE/FDE?  */
+      struct eh_cie_fde *last = sec_info->entry + sec_info->count;
+      delta = ((bfd_vma) next_cie_fde_offset (ent, last, sec)
+	       - (bfd_vma) ent->offset);
+      return delta;
+    }
+
+  /* Account for editing within this CIE/FDE.  */
+  offset -= ent->offset;
+  if (ent->cie)
+    {
+      unsigned int extra
+	= ent->add_augmentation_size + ent->u.cie.add_fde_encoding;
+      if (extra == 0
+	  || offset <= 9u + ent->u.cie.aug_str_len)
+	return delta;
+      delta += extra;
+      if (offset <= 9u + ent->u.cie.aug_str_len + ent->u.cie.aug_data_len)
+	return delta;
+      delta += extra;
+    }
+  else
+    {
+      unsigned int ptr_size, width, extra = ent->add_augmentation_size;
+      if (offset <= 12 || extra == 0)
+	return delta;
+      ptr_size = (get_elf_backend_data (sec->owner)
+		  ->elf_backend_eh_frame_address_size (sec->owner, sec));
+      width = get_DW_EH_PE_width (ent->fde_encoding, ptr_size);
+      if (offset <= 8 + 2 * width)
+	return delta;
+      delta += extra;
+    }
+
+  return delta;
+}
+
+/* Adjust a global symbol defined in .eh_frame, so that it stays
+   relative to its original CIE/FDE.  It is assumed that a symbol
+   defined at the beginning of a CIE/FDE belongs to that CIE/FDE
+   rather than marking the end of the previous CIE/FDE.  This matters
+   when a CIE is merged with a previous CIE, since the symbol is
+   moved to the merged CIE.  */
+
+bfd_boolean
+_bfd_elf_adjust_eh_frame_global_symbol (struct elf_link_hash_entry *h,
+					void *arg ATTRIBUTE_UNUSED)
+{
+  asection *sym_sec;
+  bfd_signed_vma delta;
+
+  if (h->root.type != bfd_link_hash_defined
+      && h->root.type != bfd_link_hash_defweak)
+    return TRUE;
+
+  sym_sec = h->root.u.def.section;
+  if (sym_sec->sec_info_type != SEC_INFO_TYPE_EH_FRAME
+      || elf_section_data (sym_sec)->sec_info == NULL)
+    return TRUE;
+
+  delta = offset_adjust (h->root.u.def.value, sym_sec);
+  h->root.u.def.value += delta;
+
+  return TRUE;
+}
+
+/* The same for all local symbols defined in .eh_frame.  Returns true
+   if any symbol was changed.  */
+
+static int
+adjust_eh_frame_local_symbols (asection *sec,
+			       struct elf_reloc_cookie *cookie)
+{
+  unsigned int shndx;
+  Elf_Internal_Sym *sym;
+  Elf_Internal_Sym *end_sym;
+  int adjusted = 0;
+
+  shndx = elf_section_data (sec)->this_idx;
+  end_sym = cookie->locsyms + cookie->locsymcount;
+  for (sym = cookie->locsyms + 1; sym < end_sym; ++sym)
+    if (sym->st_info <= ELF_ST_INFO (STB_LOCAL, STT_OBJECT)
+	&& sym->st_shndx == shndx)
+      {
+	bfd_signed_vma delta = offset_adjust (sym->st_value, sec);
+
+	if (delta != 0)
+	  {
+	    adjusted = 1;
+	    sym->st_value += delta;
+	  }
+      }
+  return adjusted;
+}
+
 /* 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
@@ -1342,6 +1482,7 @@ _bfd_elf_discard_section_eh_frame
   struct eh_frame_sec_info *sec_info;
   struct eh_frame_hdr_info *hdr_info;
   unsigned int ptr_size, offset, eh_alignment;
+  int changed;
 
   if (sec->sec_info_type != SEC_INFO_TYPE_EH_FRAME)
     return FALSE;
@@ -1428,6 +1569,7 @@ _bfd_elf_discard_section_eh_frame
      last FDE instead.  For other FDEs we align according to their
      encoding, in order to align FDE address range entries naturally.  */
   offset = 0;
+  changed = 0;
   for (ent = sec_info->entry; ent < sec_info->entry + sec_info->count; ++ent)
     if (!ent->removed)
       {
@@ -1447,6 +1589,8 @@ _bfd_elf_discard_section_eh_frame
 	  }
 	offset = (offset + eh_alignment - 1) & -eh_alignment;
 	ent->new_offset = offset;
+	if (ent->new_offset != ent->offset)
+	  changed = 1;
 	offset += size_of_output_cie_fde (ent);
       }
 
@@ -1463,7 +1607,15 @@ _bfd_elf_discard_section_eh_frame
   offset = (offset + eh_alignment - 1) & -eh_alignment;
   sec->rawsize = sec->size;
   sec->size = offset;
-  return offset != sec->rawsize;
+  if (sec->size != sec->rawsize)
+    changed = 1;
+
+  if (changed && adjust_eh_frame_local_symbols (sec, cookie))
+    {
+      Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+      symtab_hdr->contents = (unsigned char *) cookie->locsyms;
+    }
+  return changed;
 }
 
 /* This function is called for .eh_frame_hdr section after
diff --git a/bfd/elflink.c b/bfd/elflink.c
index 776357f..f7bbfdb 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -13738,6 +13738,7 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
   if (o != NULL)
     {
       asection *i;
+      int eh_changed = 0;
 
       for (i = o->map_head.s; i != NULL; i = i->map_head.s)
 	{
@@ -13755,10 +13756,17 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
 	  if (_bfd_elf_discard_section_eh_frame (abfd, info, i,
 						 bfd_elf_reloc_symbol_deleted_p,
 						 &cookie))
-	    changed = 1;
+	    {
+	      eh_changed = 1;
+	      if (i->size != i->rawsize)
+		changed = 1;
+	    }
 
 	  fini_reloc_cookie_for_section (&cookie, i);
 	}
+      if (eh_changed)
+	elf_link_hash_traverse (elf_hash_table (info),
+				_bfd_elf_adjust_eh_frame_global_symbol, NULL);
     }
 
   for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link.next)
diff --git a/gas/symbols.c b/gas/symbols.c
index d4f09fc..a54e469 100644
--- a/gas/symbols.c
+++ b/gas/symbols.c
@@ -2092,12 +2092,18 @@ int
 S_FORCE_RELOC (symbolS *s, int strict)
 {
   if (LOCAL_SYMBOL_CHECK (s))
-    return ((struct local_symbol *) s)->lsy_section == undefined_section;
+    return (((struct local_symbol *) s)->lsy_section == undefined_section
+	    || (IS_ELF
+		&& strict
+		&& strcmp (((struct local_symbol *) s)->lsy_section->name,
+			   ".eh_frame") == 0));
 
   return ((strict
 	   && ((s->bsym->flags & BSF_WEAK) != 0
 	       || (EXTERN_FORCE_RELOC
-		   && (s->bsym->flags & BSF_GLOBAL) != 0)))
+		   && (s->bsym->flags & BSF_GLOBAL) != 0)
+	       || (IS_ELF
+		   && strcmp (s->bsym->section->name, ".eh_frame") == 0)))
 	  || (s->bsym->flags & BSF_GNU_INDIRECT_FUNCTION) != 0
 	  || s->bsym->section == undefined_section
 	  || bfd_is_com_section (s->bsym->section));
diff --git a/ld/testsuite/ld-elf/eh5a.s b/ld/testsuite/ld-elf/eh5a.s
index a74b2cc..5c4966a 100644
--- a/ld/testsuite/ld-elf/eh5a.s
+++ b/ld/testsuite/ld-elf/eh5a.s
@@ -15,11 +15,16 @@
 	.long 0
 	.cfi_endproc
 
+	.global G1, G2, G3, G4
 	.cfi_startproc simple
+	.cfi_label G1
 	.cfi_personality 3, my_personality_v1
+	.cfi_label G2
 	.cfi_lsda 12, 0xdeadbeef
 	.long 0
+	.cfi_label G3
 	.cfi_def_cfa 0, 16
+	.cfi_label G4
 	.cfi_endproc
 
 	.globl my_personality_v1
diff --git a/ld/testsuite/ld-elf/eh5b.s b/ld/testsuite/ld-elf/eh5b.s
index fa27edd..a7d81b5 100644
--- a/ld/testsuite/ld-elf/eh5b.s
+++ b/ld/testsuite/ld-elf/eh5b.s
@@ -18,10 +18,14 @@
 	.cfi_endproc
 
 	.cfi_startproc simple
+	.cfi_label L1
 	.cfi_personality 3, my_personality_v0
+	.cfi_label L2
 	.cfi_lsda 12, 0xdeadbeef
 	.long 0
+	.cfi_label L3
 	.cfi_def_cfa 0, 16
+	.cfi_label L4
 	.cfi_endproc
 
 	.globl main
@@ -32,4 +36,5 @@ main:
 start:
 _start:
 __start:
-	.long 0
+	.dc.a G1, G2, G3, G4
+	.dc.a L1, L2, L3, L4

-- 
Alan Modra
Australia Development Lab, IBM


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