This is the mail archive of the binutils-cvs@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]

[binutils-gdb] Don't convert R_X86_64_GOTPCREL if it will overflow


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=59cab532835904f368b0aa99267afba5fda5ded2

commit 59cab532835904f368b0aa99267afba5fda5ded2
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Wed Jun 24 10:13:55 2015 -0700

    Don't convert R_X86_64_GOTPCREL if it will overflow
    
    When converting "mov foo@GOTPCREL(%rip), %reg" to "lea foo(%rip), %reg"
    with R_X86_64_PC32 relocation, it may overflow if the target section
    is more than 2GB away.  This patch estimates distance between mov
    instruction and the target section.  We convert R_X86_64_GOTPCREL to
    R_X86_64_PC32 only if their distance is less than 2GB.
    
    	PR ld/18591
    	* elf64-x86-64.c (elf_x86_64_convert_mov_to_lea): Don't convert
    	R_X86_64_GOTPCREL to R_X86_64_PC32 if it will cause relocation
    	overflow.

Diff:
---
 bfd/ChangeLog      |   7 +++
 bfd/elf64-x86-64.c | 175 +++++++++++++++++++++++++++++++++++++++++++----------
 2 files changed, 151 insertions(+), 31 deletions(-)

diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index 157e7f7..8918851 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,3 +1,10 @@
+2015-06-24  H.J. Lu  <hongjiu.lu@intel.com>
+
+	PR ld/18591
+	* elf64-x86-64.c (elf_x86_64_convert_mov_to_lea): Don't convert
+	R_X86_64_GOTPCREL to R_X86_64_PC32 if it will cause relocation
+	overflow.
+
 2015-06-23  Jiong Wang  <jiong.wang@arm.com>
 
 	* elfnn-aarch64.c (aarch64_readonly_dynrelocs): New function.
diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
index 072c00b..f7aea98 100644
--- a/bfd/elf64-x86-64.c
+++ b/bfd/elf64-x86-64.c
@@ -2901,6 +2901,7 @@ elf_x86_64_convert_mov_to_lea (bfd *abfd, asection *sec,
   bfd_boolean changed_contents;
   bfd_boolean changed_relocs;
   bfd_signed_vma *local_got_refcounts;
+  bfd_vma maxpagesize;
 
   /* Don't even try to convert non-ELF outputs.  */
   if (!is_elf_hash_table (link_info->hash))
@@ -2925,6 +2926,7 @@ elf_x86_64_convert_mov_to_lea (bfd *abfd, asection *sec,
   changed_contents = FALSE;
   changed_relocs = FALSE;
   local_got_refcounts = elf_local_got_refcounts (abfd);
+  maxpagesize = get_elf_backend_data (abfd)->maxpagesize;
 
   /* Get the section contents.  */
   if (elf_section_data (sec)->this_hdr.contents != NULL)
@@ -2942,10 +2944,27 @@ elf_x86_64_convert_mov_to_lea (bfd *abfd, asection *sec,
       unsigned int r_symndx = htab->r_sym (irel->r_info);
       unsigned int indx;
       struct elf_link_hash_entry *h;
+      asection *tsec;
+      char symtype;
+      bfd_vma toff, roff;
+      enum {
+	none, local, global
+      } convert_mov_to_lea;
 
       if (r_type != R_X86_64_GOTPCREL)
 	continue;
 
+      roff = irel->r_offset;
+
+      /* Don't convert R_X86_64_GOTPCREL relocation if it isn't for mov
+	 instruction.  */
+      if (roff < 2
+	  || bfd_get_8 (abfd, contents + roff - 2) != 0x8b)
+	continue;
+
+      tsec = NULL;
+      convert_mov_to_lea = none;
+
       /* Get the symbol referred to by the reloc.  */
       if (r_symndx < symtab_hdr->sh_info)
 	{
@@ -2954,46 +2973,140 @@ elf_x86_64_convert_mov_to_lea (bfd *abfd, asection *sec,
 	  isym = bfd_sym_from_r_symndx (&htab->sym_cache,
 					abfd, r_symndx);
 
-	  /* STT_GNU_IFUNC must keep R_X86_64_GOTPCREL relocation.  */
-	  if (ELF_ST_TYPE (isym->st_info) != STT_GNU_IFUNC
-	      && irel->r_offset >= 2
-	      && bfd_get_8 (abfd, contents + irel->r_offset - 2) == 0x8b)
+	  symtype = ELF_ST_TYPE (isym->st_info);
+
+	  /* STT_GNU_IFUNC must keep R_X86_64_GOTPCREL relocation and
+	     skip relocation against undefined symbols.  */
+	  if (symtype != STT_GNU_IFUNC && isym->st_shndx != SHN_UNDEF)
 	    {
-	      bfd_put_8 (abfd, 0x8d, contents + irel->r_offset - 2);
-	      irel->r_info = htab->r_info (r_symndx, R_X86_64_PC32);
-	      if (local_got_refcounts != NULL
-		  && local_got_refcounts[r_symndx] > 0)
-		local_got_refcounts[r_symndx] -= 1;
-	      changed_contents = TRUE;
-	      changed_relocs = TRUE;
+	      if (isym->st_shndx == SHN_ABS)
+		tsec = bfd_abs_section_ptr;
+	      else if (isym->st_shndx == SHN_COMMON)
+		tsec = bfd_com_section_ptr;
+	      else if (isym->st_shndx == SHN_X86_64_LCOMMON)
+		tsec = &_bfd_elf_large_com_section;
+	      else
+		tsec = bfd_section_from_elf_index (abfd, isym->st_shndx);
+
+	      toff = isym->st_value;
+	      convert_mov_to_lea = local;
 	    }
-	  continue;
 	}
+      else
+	{
+	  indx = r_symndx - symtab_hdr->sh_info;
+	  h = elf_sym_hashes (abfd)[indx];
+	  BFD_ASSERT (h != NULL);
 
-      indx = r_symndx - symtab_hdr->sh_info;
-      h = elf_sym_hashes (abfd)[indx];
-      BFD_ASSERT (h != NULL);
+	  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;
 
-      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;
+	  /* STT_GNU_IFUNC must keep R_X86_64_GOTPCREL relocation.  We also
+	     avoid optimizing _DYNAMIC since ld.so may use its link-time
+	     address.  */
+	  if (h->def_regular
+	      && h->type != STT_GNU_IFUNC
+	      && h != htab->elf.hdynamic
+	      && SYMBOL_REFERENCES_LOCAL (link_info, h))
+	    {
+	      tsec = h->root.u.def.section;
+	      toff = h->root.u.def.value;
+	      symtype = h->type;
+	      convert_mov_to_lea = global;
+	    }
+	}
 
-      /* STT_GNU_IFUNC must keep R_X86_64_GOTPCREL relocation.  We also
-	 avoid optimizing _DYNAMIC since ld.so may use its link-time
-	 address.  */
-      if (h->def_regular
-	  && h->type != STT_GNU_IFUNC
-	  && h != htab->elf.hdynamic
-	  && SYMBOL_REFERENCES_LOCAL (link_info, h)
-	  && irel->r_offset >= 2
-	  && bfd_get_8 (abfd, contents + irel->r_offset - 2) == 0x8b)
+      if (convert_mov_to_lea == none)
+	continue;
+
+      if (tsec->sec_info_type == SEC_INFO_TYPE_MERGE)
+	{
+	  /* At this stage in linking, no SEC_MERGE symbol has been
+	     adjusted, so all references to such symbols need to be
+	     passed through _bfd_merged_section_offset.  (Later, in
+	     relocate_section, all SEC_MERGE symbols *except* for
+	     section symbols have been adjusted.)
+
+	     gas may reduce relocations against symbols in SEC_MERGE
+	     sections to a relocation against the section symbol when
+	     the original addend was zero.  When the reloc is against
+	     a section symbol we should include the addend in the
+	     offset passed to _bfd_merged_section_offset, since the
+	     location of interest is the original symbol.  On the
+	     other hand, an access to "sym+addend" where "sym" is not
+	     a section symbol should not include the addend;  Such an
+	     access is presumed to be an offset from "sym";  The
+	     location of interest is just "sym".  */
+	   if (symtype == STT_SECTION)
+	     toff += irel->r_addend;
+
+	   toff = _bfd_merged_section_offset (abfd, &tsec,
+					      elf_section_data (tsec)->sec_info,
+					      toff);
+
+	   if (symtype != STT_SECTION)
+	     toff += irel->r_addend;
+	}
+      else
+	toff += irel->r_addend;
+
+      /* Don't convert if R_X86_64_PC32 relocation overflows.  */
+      if (tsec->output_section == sec->output_section)
+	{
+	  if ((toff - roff + 0x80000000) > 0xffffffff)
+	    continue;
+	}
+      else
+	{
+	  asection *asect;
+	  bfd_size_type size;
+
+	  /* At this point, we don't know the load addresses of TSEC
+	     section nor SEC section.  We estimate the distrance between
+	     SEC and TSEC.  */
+	  size = 0;
+	  for (asect = sec->output_section;
+	       asect != NULL && asect != tsec->output_section;
+	       asect = asect->next)
+	    {
+	      asection *i;
+	      for (i = asect->output_section->map_head.s;
+		   i != NULL;
+		   i = i->map_head.s)
+		{
+		  size = align_power (size, i->alignment_power);
+		  size += i->size;
+		}
+	    }
+
+	  /* Don't convert R_X86_64_GOTPCREL if TSEC isn't placed after
+	     SEC.  */
+	  if (asect == NULL)
+	    continue;
+
+	  /* Take PT_GNU_RELRO segment into account by adding
+	     maxpagesize.  */
+	  if ((toff + size + maxpagesize - roff + 0x80000000)
+	      > 0xffffffff)
+	    continue;
+	}
+
+      bfd_put_8 (abfd, 0x8d, contents + roff - 2);
+      irel->r_info = htab->r_info (r_symndx, R_X86_64_PC32);
+      changed_contents = TRUE;
+      changed_relocs = TRUE;
+
+      if (convert_mov_to_lea == local)
+	{
+	  if (local_got_refcounts != NULL
+	      && local_got_refcounts[r_symndx] > 0)
+	    local_got_refcounts[r_symndx] -= 1;
+	}
+      else
 	{
-	  bfd_put_8 (abfd, 0x8d, contents + irel->r_offset - 2);
-	  irel->r_info = htab->r_info (r_symndx, R_X86_64_PC32);
 	  if (h->got.refcount > 0)
 	    h->got.refcount -= 1;
-	  changed_contents = TRUE;
-	  changed_relocs = TRUE;
 	}
     }


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