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] Extract convert_load_reloc from x86 convert_load


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

commit c175a65796a6f1d6a69ed0d7e3bce6f048c8e983
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Thu May 5 04:13:47 2016 -0700

    Extract convert_load_reloc from x86 convert_load
    
    	* elf32-i386.c (elf_i386_convert_load): Extract the GOT load
    	conversion to ...
    	(elf_i386_convert_load_reloc): This.  New function.
    	* elf64-x86-64.c (elf_x86_64_convert_load): Extract the GOT load
    	conversion to ...
    	(elf_x86_64_convert_load_reloc): This.  New function.

Diff:
---
 bfd/ChangeLog      |   9 +
 bfd/elf32-i386.c   | 554 ++++++++++++++++++------------------
 bfd/elf64-x86-64.c | 821 +++++++++++++++++++++++++++--------------------------
 3 files changed, 714 insertions(+), 670 deletions(-)

diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index 6f5afee..093d7f5 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,5 +1,14 @@
 2016-05-05  H.J. Lu  <hongjiu.lu@intel.com>
 
+	* elf32-i386.c (elf_i386_convert_load): Extract the GOT load
+	conversion to ...
+	(elf_i386_convert_load_reloc): This.  New function.
+	* elf64-x86-64.c (elf_x86_64_convert_load): Extract the GOT load
+	conversion to ...
+	(elf_x86_64_convert_load_reloc): This.  New function.
+
+2016-05-05  H.J. Lu  <hongjiu.lu@intel.com>
+
 	* elf32-i386.c (elf_i386_check_tls_transition): Remove abfd.
 	Don't check if contents == NULL.
 	(elf_i386_tls_transition): Add from_relocate_section.  Check
diff --git a/bfd/elf32-i386.c b/bfd/elf32-i386.c
index ff0f140..483146b 100644
--- a/bfd/elf32-i386.c
+++ b/bfd/elf32-i386.c
@@ -1499,6 +1499,255 @@ elf_i386_tls_transition (struct bfd_link_info *info, bfd *abfd,
   return TRUE;
 }
 
+/* With the local symbol, foo, we convert
+   mov foo@GOT[(%reg1)], %reg2
+   to
+   lea foo[@GOTOFF(%reg1)], %reg2
+   and convert
+   call/jmp *foo@GOT[(%reg)]
+   to
+   nop call foo/jmp foo nop
+   When PIC is false, convert
+   test %reg1, foo@GOT[(%reg2)]
+   to
+   test $foo, %reg1
+   and convert
+   binop foo@GOT[(%reg1)], %reg2
+   to
+   binop $foo, %reg2
+   where binop is one of adc, add, and, cmp, or, sbb, sub, xor
+   instructions.  */
+
+static
+bfd_boolean
+elf_i386_convert_load_reloc (bfd *abfd, Elf_Internal_Shdr *symtab_hdr,
+			     bfd_byte *contents,
+			     Elf_Internal_Rela *irel,
+			     struct elf_link_hash_entry *h,
+			     bfd_boolean *converted,
+			     struct bfd_link_info *link_info)
+{
+  struct elf_i386_link_hash_table *htab;
+  unsigned int opcode;
+  unsigned int modrm;
+  bfd_boolean baseless;
+  Elf_Internal_Sym *isym;
+  unsigned int addend;
+  unsigned int nop;
+  bfd_vma nop_offset;
+  bfd_boolean is_pic;
+  bfd_boolean to_reloc_32;
+  unsigned int r_type;
+  unsigned int r_symndx;
+  bfd_vma roff = irel->r_offset;
+
+  if (roff < 2)
+    return TRUE;
+
+  /* Addend for R_386_GOT32 and R_386_GOT32X relocations must be 0.  */
+  addend = bfd_get_32 (abfd, contents + roff);
+  if (addend != 0)
+    return TRUE;
+
+  htab = elf_i386_hash_table (link_info);
+  is_pic = bfd_link_pic (link_info);
+
+  r_type = ELF32_R_TYPE (irel->r_info);
+  r_symndx = ELF32_R_SYM (irel->r_info);
+
+  modrm = bfd_get_8 (abfd, contents + roff - 1);
+  baseless = (modrm & 0xc7) == 0x5;
+
+  if (r_type == R_386_GOT32X && baseless && is_pic)
+    {
+      /* For PIC, disallow R_386_GOT32X without a base register
+	 since we don't know what the GOT base is.   Allow
+	 R_386_GOT32 for existing object files.  */
+      const char *name;
+
+      if (h == NULL)
+	{
+	  isym = bfd_sym_from_r_symndx (&htab->sym_cache, abfd,
+					r_symndx);
+	  name = bfd_elf_sym_name (abfd, symtab_hdr, isym, NULL);
+	}
+      else
+	name = h->root.root.string;
+
+      (*_bfd_error_handler)
+	(_("%B: direct GOT relocation R_386_GOT32X against `%s' without base register can not be used when making a shared object"),
+	 abfd, name);
+      return FALSE;
+    }
+
+  opcode = bfd_get_8 (abfd, contents + roff - 2);
+
+  /* Convert mov to lea since it has been done for a while.  */
+  if (opcode != 0x8b)
+    {
+      /* Only convert R_386_GOT32X relocation for call, jmp or
+	 one of adc, add, and, cmp, or, sbb, sub, test, xor
+	 instructions.  */
+      if (r_type != R_386_GOT32X)
+	return TRUE;
+    }
+
+  /* Convert to R_386_32 if PIC is false or there is no base
+     register.  */
+  to_reloc_32 = !is_pic || baseless;
+
+  /* Try to convert R_386_GOT32 and R_386_GOT32X.  Get the symbol
+     referred to by the reloc.  */
+  if (h == NULL)
+    {
+      if (opcode == 0x0ff)
+	/* Convert "call/jmp *foo@GOT[(%reg)]".  */
+	goto convert_branch;
+      else
+	/* Convert "mov foo@GOT[(%reg1)], %reg2",
+	   "test %reg1, foo@GOT(%reg2)" and
+	   "binop foo@GOT[(%reg1)], %reg2". */
+	goto convert_load;
+    }
+
+  /* Undefined weak symbol is only bound locally in executable
+     and its reference is resolved as 0.  */
+  if (UNDEFINED_WEAK_RESOLVED_TO_ZERO (link_info, TRUE,
+				       elf_i386_hash_entry (h)))
+    {
+      if (opcode == 0xff)
+	{
+	  /* No direct branch to 0 for PIC.  */
+	  if (is_pic)
+	    return TRUE;
+	  else
+	    goto convert_branch;
+	}
+      else
+	{
+	  /* We can convert load of address 0 to R_386_32.  */
+	  to_reloc_32 = TRUE;
+	  goto convert_load;
+	}
+    }
+
+  if (opcode == 0xff)
+    {
+      /* We have "call/jmp *foo@GOT[(%reg)]".  */
+      if ((h->root.type == bfd_link_hash_defined
+	   || h->root.type == bfd_link_hash_defweak)
+	  && SYMBOL_REFERENCES_LOCAL (link_info, h))
+	{
+	  /* The function is locally defined.   */
+convert_branch:
+	  /* Convert R_386_GOT32X to R_386_PC32.  */
+	  if (modrm == 0x15 || (modrm & 0xf8) == 0x90)
+	    {
+	      /* Convert to "nop call foo".  ADDR_PREFIX_OPCODE
+		 is a nop prefix.  */
+	      modrm = 0xe8;
+	      nop = link_info->call_nop_byte;
+	      if (link_info->call_nop_as_suffix)
+		{
+		  nop_offset = roff + 3;
+		  irel->r_offset -= 1;
+		}
+	      else
+		nop_offset = roff - 2;
+	    }
+	  else
+	    {
+	      /* Convert to "jmp foo nop".  */
+	      modrm = 0xe9;
+	      nop = NOP_OPCODE;
+	      nop_offset = roff + 3;
+	      irel->r_offset -= 1;
+	    }
+
+	  bfd_put_8 (abfd, nop, contents + nop_offset);
+	  bfd_put_8 (abfd, modrm, contents + irel->r_offset - 1);
+	  /* When converting to PC-relative relocation, we
+	     need to adjust addend by -4.  */
+	  bfd_put_32 (abfd, -4, contents + irel->r_offset);
+	  irel->r_info = ELF32_R_INFO (r_symndx, R_386_PC32);
+
+	  *converted = TRUE;
+	}
+    }
+  else
+    {
+      /* We have "mov foo@GOT[(%re1g)], %reg2",
+	 "test %reg1, foo@GOT(%reg2)" and
+	 "binop foo@GOT[(%reg1)], %reg2".
+
+	 Avoid optimizing _DYNAMIC since ld.so may use its
+	 link-time address.  */
+      if (h == htab->elf.hdynamic)
+	return TRUE;
+
+      /* def_regular is set by an assignment in a linker script in
+	 bfd_elf_record_link_assignment.  */
+      if ((h->def_regular
+	   || h->root.type == bfd_link_hash_defined
+	   || h->root.type == bfd_link_hash_defweak)
+	  && SYMBOL_REFERENCES_LOCAL (link_info, h))
+	{
+convert_load:
+	  if (opcode == 0x8b)
+	    {
+	      if (to_reloc_32)
+		{
+		  /* Convert "mov foo@GOT[(%reg1)], %reg2" to
+		     "mov $foo, %reg2" with R_386_32.  */
+		  r_type = R_386_32;
+		  modrm = 0xc0 | (modrm & 0x38) >> 3;
+		  bfd_put_8 (abfd, modrm, contents + roff - 1);
+		  opcode = 0xc7;
+		}
+	      else
+		{
+		  /* Convert "mov foo@GOT(%reg1), %reg2" to
+		     "lea foo@GOTOFF(%reg1), %reg2".  */
+		  r_type = R_386_GOTOFF;
+		  opcode = 0x8d;
+		}
+	    }
+	  else
+	    {
+	      /* Only R_386_32 is supported.  */
+	      if (!to_reloc_32)
+		return TRUE;
+
+	      if (opcode == 0x85)
+		{
+		  /* Convert "test %reg1, foo@GOT(%reg2)" to
+		     "test $foo, %reg1".  */
+		  modrm = 0xc0 | (modrm & 0x38) >> 3;
+		  opcode = 0xf7;
+		}
+	      else
+		{
+		  /* Convert "binop foo@GOT(%reg1), %reg2" to
+		     "binop $foo, %reg2".  */
+		  modrm = (0xc0
+			   | (modrm & 0x38) >> 3
+			   | (opcode & 0x3c));
+		  opcode = 0x81;
+		}
+	      bfd_put_8 (abfd, modrm, contents + roff - 1);
+	      r_type = R_386_32;
+	    }
+
+	  bfd_put_8 (abfd, opcode, contents + roff - 2);
+	  irel->r_info = ELF32_R_INFO (r_symndx, r_type);
+
+	  *converted = TRUE;
+	}
+    }
+
+  return TRUE;
+}
+
 /* Rename some of the generic section flags to better document how they
    are used here.  */
 #define need_convert_load	sec_flg0
@@ -2718,37 +2967,18 @@ elf_i386_readonly_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   return TRUE;
 }
 
-/* With the local symbol, foo, we convert
-   mov foo@GOT[(%reg1)], %reg2
-   to
-   lea foo[@GOTOFF(%reg1)], %reg2
-   and convert
-   call/jmp *foo@GOT[(%reg)]
-   to
-   nop call foo/jmp foo nop
-   When PIC is false, convert
-   test %reg1, foo@GOT[(%reg2)]
-   to
-   test $foo, %reg1
-   and convert
-   binop foo@GOT[(%reg1)], %reg2
-   to
-   binop $foo, %reg2
-   where binop is one of adc, add, and, cmp, or, sbb, sub, xor
-   instructions.  */
+/* Convert load via the GOT slot to load immediate.  */
 
 static bfd_boolean
 elf_i386_convert_load (bfd *abfd, asection *sec,
 		       struct bfd_link_info *link_info)
 {
+  struct elf_i386_link_hash_table *htab;
   Elf_Internal_Shdr *symtab_hdr;
   Elf_Internal_Rela *internal_relocs;
   Elf_Internal_Rela *irel, *irelend;
   bfd_byte *contents;
-  struct elf_i386_link_hash_table *htab;
-  bfd_boolean changed_contents;
-  bfd_boolean changed_relocs;
-  bfd_boolean is_pic;
+  bfd_boolean changed;
   bfd_signed_vma *local_got_refcounts;
 
   /* Don't even try to convert non-ELF outputs.  */
@@ -2770,13 +3000,10 @@ elf_i386_convert_load (bfd *abfd, asection *sec,
   if (internal_relocs == NULL)
     return FALSE;
 
+  changed = FALSE;
   htab = elf_i386_hash_table (link_info);
-  changed_contents = FALSE;
-  changed_relocs = FALSE;
   local_got_refcounts = elf_local_got_refcounts (abfd);
 
-  is_pic = bfd_link_pic (link_info);
-
   /* Get the section contents.  */
   if (elf_section_data (sec)->this_hdr.contents != NULL)
     contents = elf_section_data (sec)->this_hdr.contents;
@@ -2790,269 +3017,48 @@ elf_i386_convert_load (bfd *abfd, asection *sec,
   for (irel = internal_relocs; irel < irelend; irel++)
     {
       unsigned int r_type = ELF32_R_TYPE (irel->r_info);
-      unsigned int r_symndx = ELF32_R_SYM (irel->r_info);
-      unsigned int indx;
+      unsigned int r_symndx;
       struct elf_link_hash_entry *h;
-      unsigned int opcode;
-      unsigned int modrm;
-      bfd_vma roff;
-      bfd_boolean baseless;
-      Elf_Internal_Sym *isym;
-      unsigned int addend;
-      unsigned int nop;
-      bfd_vma nop_offset;
-      bfd_boolean to_reloc_32;
+      bfd_boolean converted;
 
       if (r_type != R_386_GOT32 && r_type != R_386_GOT32X)
 	continue;
 
-      roff = irel->r_offset;
-      if (roff < 2)
-	continue;
-
-      /* Addend for R_386_GOT32 and R_386_GOT32X relocations must be 0.  */
-      addend = bfd_get_32 (abfd, contents + roff);
-      if (addend != 0)
-	continue;
-
-      modrm = bfd_get_8 (abfd, contents + roff - 1);
-      baseless = (modrm & 0xc7) == 0x5;
-
-      if (r_type == R_386_GOT32X && baseless && is_pic)
-	{
-	  /* For PIC, disallow R_386_GOT32X without a base register
-	     since we don't know what the GOT base is.   Allow
-	     R_386_GOT32 for existing object files.  */
-	  const char *name;
-
-	  if (r_symndx < symtab_hdr->sh_info)
-	    {
-	      isym = bfd_sym_from_r_symndx (&htab->sym_cache, abfd,
-					    r_symndx);
-	      name = bfd_elf_sym_name (abfd, symtab_hdr, isym, NULL);
-	    }
-	  else
-	    {
-	      indx = r_symndx - symtab_hdr->sh_info;
-	      h = elf_sym_hashes (abfd)[indx];
-	      BFD_ASSERT (h != NULL);
-	      name = h->root.root.string;
-	    }
-
-	  (*_bfd_error_handler)
-	    (_("%B: direct GOT relocation R_386_GOT32X against `%s' without base register can not be used when making a shared object"),
-	     abfd, name);
-	  goto error_return;
-	}
-
-      opcode = bfd_get_8 (abfd, contents + roff - 2);
-
-      /* Convert mov to lea since it has been done for a while.  */
-      if (opcode != 0x8b)
-	{
-	  /* Only convert R_386_GOT32X relocation for call, jmp or
-	     one of adc, add, and, cmp, or, sbb, sub, test, xor
-	     instructions.  */
-	  if (r_type != R_386_GOT32X)
-	    continue;
-	}
-
-      /* Convert to R_386_32 if PIC is false or there is no base
-	 register.  */
-      to_reloc_32 = !is_pic || baseless;
-
-      /* Try to convert R_386_GOT32 and R_386_GOT32X.  Get the symbol
-	 referred to by the reloc.  */
+      r_symndx = ELF32_R_SYM (irel->r_info);
       if (r_symndx < symtab_hdr->sh_info)
+	h = elf_i386_get_local_sym_hash (htab, sec->owner,
+					 (const Elf_Internal_Rela *) irel,
+					 FALSE);
+      else
 	{
-	  isym = bfd_sym_from_r_symndx (&htab->sym_cache,
-					abfd, r_symndx);
-
-	  /* STT_GNU_IFUNC must keep GOT32 relocations.  */
-	  if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
-	    continue;
-
-	  h = NULL;
-	  if (opcode == 0x0ff)
-	    /* Convert "call/jmp *foo@GOT[(%reg)]".  */
-	    goto convert_branch;
-	  else
-	    /* Convert "mov foo@GOT[(%reg1)], %reg2",
-	       "test %reg1, foo@GOT(%reg2)" and
-	       "binop foo@GOT[(%reg1)], %reg2". */
-	    goto convert_load;
+	  h = elf_sym_hashes (abfd)[r_symndx - symtab_hdr->sh_info];
+	   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;
 	}
 
-      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;
-
       /* STT_GNU_IFUNC must keep GOT32 relocations.  */
-      if (h->type == STT_GNU_IFUNC)
+      if (h != NULL && h->type == STT_GNU_IFUNC)
 	continue;
 
-      /* Undefined weak symbol is only bound locally in executable
-	 and its reference is resolved as 0.  */
-      if (UNDEFINED_WEAK_RESOLVED_TO_ZERO (link_info, TRUE,
-					   elf_i386_hash_entry (h)))
-	{
-	  if (opcode == 0xff)
-	    {
-	      /* No direct branch to 0 for PIC.  */
-	      if (is_pic)
-		continue;
-	      else
-		goto convert_branch;
-	    }
-	  else
-	    {
-	      /* We can convert load of address 0 to R_386_32.  */
-	      to_reloc_32 = TRUE;
-	      goto convert_load;
-	    }
-	}
+      converted = FALSE;
+      if (!elf_i386_convert_load_reloc (abfd, symtab_hdr, contents,
+					irel, h, &converted, link_info))
+	goto error_return;
 
-      if (opcode == 0xff)
+      if (converted)
 	{
-	  /* We have "call/jmp *foo@GOT[(%reg)]".  */
-	  if ((h->root.type == bfd_link_hash_defined
-	       || h->root.type == bfd_link_hash_defweak)
-	      && SYMBOL_REFERENCES_LOCAL (link_info, h))
+	  changed = converted;
+	  if (h)
 	    {
-	      /* The function is locally defined.   */
-convert_branch:
-	      /* Convert R_386_GOT32X to R_386_PC32.  */
-	      if (modrm == 0x15 || (modrm & 0xf8) == 0x90)
-		{
-		  /* Convert to "nop call foo".  ADDR_PREFIX_OPCODE
-		     is a nop prefix.  */
-		  modrm = 0xe8;
-		  nop = link_info->call_nop_byte;
-		  if (link_info->call_nop_as_suffix)
-		    {
-		      nop_offset = roff + 3;
-		      irel->r_offset -= 1;
-		    }
-		  else
-		    nop_offset = roff - 2;
-		}
-	      else
-		{
-		  /* Convert to "jmp foo nop".  */
-		  modrm = 0xe9;
-		  nop = NOP_OPCODE;
-		  nop_offset = roff + 3;
-		  irel->r_offset -= 1;
-		}
-
-	      bfd_put_8 (abfd, nop, contents + nop_offset);
-	      bfd_put_8 (abfd, modrm, contents + irel->r_offset - 1);
-	      /* When converting to PC-relative relocation, we
-		 need to adjust addend by -4.  */
-	      bfd_put_32 (abfd, -4, contents + irel->r_offset);
-	      irel->r_info = ELF32_R_INFO (r_symndx, R_386_PC32);
-
-	      if (h)
-		{
-		  if (h->got.refcount > 0)
-		    h->got.refcount -= 1;
-		}
-	      else
-		{
-		  if (local_got_refcounts != NULL
-		      && local_got_refcounts[r_symndx] > 0)
-		    local_got_refcounts[r_symndx] -= 1;
-		}
-
-	      changed_contents = TRUE;
-	      changed_relocs = TRUE;
+	      if (h->got.refcount > 0)
+		h->got.refcount -= 1;
 	    }
-	}
-      else
-	{
-	  /* We have "mov foo@GOT[(%re1g)], %reg2",
-	     "test %reg1, foo@GOT(%reg2)" and
-	     "binop foo@GOT[(%reg1)], %reg2".
-
-	     Avoid optimizing _DYNAMIC since ld.so may use its
-	     link-time address.  */
-	  if (h == htab->elf.hdynamic)
-	    continue;
-
-	  /* def_regular is set by an assignment in a linker script in
-	     bfd_elf_record_link_assignment.  */
-	  if ((h->def_regular
-	       || h->root.type == bfd_link_hash_defined
-	       || h->root.type == bfd_link_hash_defweak)
-	      && SYMBOL_REFERENCES_LOCAL (link_info, h))
+	  else
 	    {
-convert_load:
-	      if (opcode == 0x8b)
-		{
-		  if (to_reloc_32)
-		    {
-		      /* Convert "mov foo@GOT[(%reg1)], %reg2" to
-			 "mov $foo, %reg2" with R_386_32.  */
-		      r_type = R_386_32;
-		      modrm = 0xc0 | (modrm & 0x38) >> 3;
-		      bfd_put_8 (abfd, modrm, contents + roff - 1);
-		      opcode = 0xc7;
-		    }
-		  else
-		    {
-		      /* Convert "mov foo@GOT(%reg1), %reg2" to
-			 "lea foo@GOTOFF(%reg1), %reg2".  */
-		      r_type = R_386_GOTOFF;
-		      opcode = 0x8d;
-		    }
-		}
-	      else
-		{
-		  /* Only R_386_32 is supported.  */
-		  if (!to_reloc_32)
-		    continue;
-
-		  if (opcode == 0x85)
-		    {
-		      /* Convert "test %reg1, foo@GOT(%reg2)" to
-			 "test $foo, %reg1".  */
-		      modrm = 0xc0 | (modrm & 0x38) >> 3;
-		      opcode = 0xf7;
-		    }
-		  else
-		    {
-		      /* Convert "binop foo@GOT(%reg1), %reg2" to
-			 "binop $foo, %reg2".  */
-		      modrm = (0xc0
-			       | (modrm & 0x38) >> 3
-			       | (opcode & 0x3c));
-		      opcode = 0x81;
-		    }
-		  bfd_put_8 (abfd, modrm, contents + roff - 1);
-		  r_type = R_386_32;
-		}
-
-	      bfd_put_8 (abfd, opcode, contents + roff - 2);
-	      irel->r_info = ELF32_R_INFO (r_symndx, r_type);
-
-	      if (h)
-		{
-		  if (h->got.refcount > 0)
-		    h->got.refcount -= 1;
-		}
-	      else
-		{
-		  if (local_got_refcounts != NULL
-		      && local_got_refcounts[r_symndx] > 0)
-		    local_got_refcounts[r_symndx] -= 1;
-		}
-
-	      changed_contents = TRUE;
-	      changed_relocs = TRUE;
+	      if (local_got_refcounts != NULL
+		  && local_got_refcounts[r_symndx] > 0)
+		local_got_refcounts[r_symndx] -= 1;
 	    }
 	}
     }
@@ -3060,7 +3066,7 @@ convert_load:
   if (contents != NULL
       && elf_section_data (sec)->this_hdr.contents != contents)
     {
-      if (!changed_contents && !link_info->keep_memory)
+      if (!changed && !link_info->keep_memory)
 	free (contents);
       else
 	{
@@ -3071,7 +3077,7 @@ convert_load:
 
   if (elf_section_data (sec)->relocs != internal_relocs)
     {
-      if (!changed_relocs)
+      if (!changed)
 	free (internal_relocs);
       else
 	elf_section_data (sec)->relocs = internal_relocs;
diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
index 6c55a60..0bcd59d 100644
--- a/bfd/elf64-x86-64.c
+++ b/bfd/elf64-x86-64.c
@@ -1634,6 +1634,401 @@ elf_x86_64_need_pic (bfd *input_bfd, asection *sec,
   return FALSE;
 }
 
+/* With the local symbol, foo, we convert
+   mov foo@GOTPCREL(%rip), %reg
+   to
+   lea foo(%rip), %reg
+   and convert
+   call/jmp *foo@GOTPCREL(%rip)
+   to
+   nop call foo/jmp foo nop
+   When PIC is false, convert
+   test %reg, foo@GOTPCREL(%rip)
+   to
+   test $foo, %reg
+   and convert
+   binop foo@GOTPCREL(%rip), %reg
+   to
+   binop $foo, %reg
+   where binop is one of adc, add, and, cmp, or, sbb, sub, xor
+   instructions.  */
+
+static bfd_boolean
+elf_x86_64_convert_load_reloc (bfd *abfd, asection *sec,
+			       bfd_byte *contents,
+			       Elf_Internal_Rela *irel,
+			       struct elf_link_hash_entry *h,
+			       bfd_boolean *converted,
+			       struct bfd_link_info *link_info)
+{
+  struct elf_x86_64_link_hash_table *htab;
+  bfd_boolean is_pic;
+  bfd_boolean require_reloc_pc32;
+  bfd_boolean relocx;
+  bfd_boolean to_reloc_pc32;
+  asection *tsec;
+  char symtype;
+  bfd_signed_vma raddend;
+  unsigned int opcode;
+  unsigned int modrm;
+  unsigned int r_type = ELF32_R_TYPE (irel->r_info);
+  unsigned int r_symndx;
+  bfd_vma toff;
+  bfd_vma roff = irel->r_offset;
+
+  if (roff < (r_type == R_X86_64_REX_GOTPCRELX ? 3 : 2))
+    return TRUE;
+
+  raddend = irel->r_addend;
+  /* Addend for 32-bit PC-relative relocation must be -4.  */
+  if (raddend != -4)
+    return TRUE;
+
+  htab = elf_x86_64_hash_table (link_info);
+  is_pic = bfd_link_pic (link_info);
+
+  relocx = (r_type == R_X86_64_GOTPCRELX
+	    || r_type == R_X86_64_REX_GOTPCRELX);
+
+  /* TRUE if we can convert only to R_X86_64_PC32.  Enable it for
+     --no-relax.  */
+  require_reloc_pc32
+    = link_info->disable_target_specific_optimizations > 1;
+
+  r_symndx = htab->r_sym (irel->r_info);
+
+  opcode = bfd_get_8 (abfd, contents + roff - 2);
+
+  /* Convert mov to lea since it has been done for a while.  */
+  if (opcode != 0x8b)
+    {
+      /* Only convert R_X86_64_GOTPCRELX and R_X86_64_REX_GOTPCRELX
+	 for call, jmp or one of adc, add, and, cmp, or, sbb, sub,
+	 test, xor instructions.  */
+      if (!relocx)
+	return TRUE;
+    }
+
+  /* We convert only to R_X86_64_PC32:
+     1. Branch.
+     2. R_X86_64_GOTPCREL since we can't modify REX byte.
+     3. require_reloc_pc32 is true.
+     4. PIC.
+     */
+  to_reloc_pc32 = (opcode == 0xff
+		   || !relocx
+		   || require_reloc_pc32
+		   || is_pic);
+
+  /* Get the symbol referred to by the reloc.  */
+  if (h == NULL)
+    {
+      Elf_Internal_Sym *isym
+	= bfd_sym_from_r_symndx (&htab->sym_cache, abfd, r_symndx);
+
+      /* Skip relocation against undefined symbols.  */
+      if (isym->st_shndx == SHN_UNDEF)
+	return TRUE;
+
+      symtype = ELF_ST_TYPE (isym->st_info);
+
+      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;
+    }
+  else
+    {
+      /* Undefined weak symbol is only bound locally in executable
+	 and its reference is resolved as 0 without relocation
+	 overflow.  We can only perform this optimization for
+	 GOTPCRELX relocations since we need to modify REX byte.
+	 It is OK convert mov with R_X86_64_GOTPCREL to
+	 R_X86_64_PC32.  */
+      if ((relocx || opcode == 0x8b)
+	  && UNDEFINED_WEAK_RESOLVED_TO_ZERO (link_info,
+					      TRUE,
+					      elf_x86_64_hash_entry (h)))
+	{
+	  if (opcode == 0xff)
+	    {
+	      /* Skip for branch instructions since R_X86_64_PC32
+		 may overflow.  */
+	      if (require_reloc_pc32)
+		return TRUE;
+	    }
+	  else if (relocx)
+	    {
+	      /* For non-branch instructions, we can convert to
+		 R_X86_64_32/R_X86_64_32S since we know if there
+		 is a REX byte.  */
+	      to_reloc_pc32 = FALSE;
+	    }
+
+	  /* Since we don't know the current PC when PIC is true,
+	     we can't convert to R_X86_64_PC32.  */
+	  if (to_reloc_pc32 && is_pic)
+	    return TRUE;
+
+	  goto convert;
+	}
+      /* Avoid optimizing GOTPCREL relocations againt _DYNAMIC since
+	 ld.so may use its link-time address.  */
+      else if ((h->def_regular
+		|| h->root.type == bfd_link_hash_defined
+		|| h->root.type == bfd_link_hash_defweak)
+	       && h != htab->elf.hdynamic
+	       && SYMBOL_REFERENCES_LOCAL (link_info, h))
+	{
+	  /* bfd_link_hash_new or bfd_link_hash_undefined is
+	     set by an assignment in a linker script in
+	     bfd_elf_record_link_assignment.   */
+	  if (h->def_regular
+	      && (h->root.type == bfd_link_hash_new
+		  || h->root.type == bfd_link_hash_undefined))
+	    {
+	      /* Skip since R_X86_64_32/R_X86_64_32S may overflow.  */
+	      if (require_reloc_pc32)
+		return TRUE;
+	      goto convert;
+	    }
+	  tsec = h->root.u.def.section;
+	  toff = h->root.u.def.value;
+	  symtype = h->type;
+	}
+      else
+	return TRUE;
+    }
+
+  /* We can only estimate relocation overflow for R_X86_64_PC32.  */
+  if (!to_reloc_pc32)
+    goto convert;
+
+  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 += raddend;
+
+      toff = _bfd_merged_section_offset (abfd, &tsec,
+					 elf_section_data (tsec)->sec_info,
+					 toff);
+
+      if (symtype != STT_SECTION)
+	toff += raddend;
+    }
+  else
+    toff += raddend;
+
+  /* Don't convert if R_X86_64_PC32 relocation overflows.  */
+  if (tsec->output_section == sec->output_section)
+    {
+      if ((toff - roff + 0x80000000) > 0xffffffff)
+	return TRUE;
+    }
+  else
+    {
+      bfd_signed_vma distance;
+
+      /* At this point, we don't know the load addresses of TSEC
+	 section nor SEC section.  We estimate the distrance between
+	 SEC and TSEC.  We store the estimated distances in the
+	 compressed_size field of the output section, which is only
+	 used to decompress the compressed input section.  */
+      if (sec->output_section->compressed_size == 0)
+	{
+	  asection *asect;
+	  bfd_size_type size = 0;
+	  for (asect = link_info->output_bfd->sections;
+	       asect != NULL;
+	       asect = asect->next)
+	    /* Skip debug sections since compressed_size is used to
+	       compress debug sections.  */
+	    if ((asect->flags & SEC_DEBUGGING) == 0)
+	      {
+		asection *i;
+		for (i = asect->map_head.s;
+		     i != NULL;
+		     i = i->map_head.s)
+		  {
+		    size = align_power (size, i->alignment_power);
+		    size += i->size;
+		  }
+		asect->compressed_size = size;
+	      }
+	}
+
+      /* Don't convert GOTPCREL relocations if TSEC isn't placed
+	 after SEC.  */
+      distance = (tsec->output_section->compressed_size
+		  - sec->output_section->compressed_size);
+      if (distance < 0)
+	return TRUE;
+
+      /* Take PT_GNU_RELRO segment into account by adding
+	 maxpagesize.  */
+      if ((toff + distance + get_elf_backend_data (abfd)->maxpagesize
+	   - roff + 0x80000000) > 0xffffffff)
+	return TRUE;
+    }
+
+convert:
+  if (opcode == 0xff)
+    {
+      /* We have "call/jmp *foo@GOTPCREL(%rip)".  */
+      unsigned int nop;
+      unsigned int disp;
+      bfd_vma nop_offset;
+
+      /* Convert R_X86_64_GOTPCRELX and R_X86_64_REX_GOTPCRELX to
+	 R_X86_64_PC32.  */
+      modrm = bfd_get_8 (abfd, contents + roff - 1);
+      if (modrm == 0x25)
+	{
+	  /* Convert to "jmp foo nop".  */
+	  modrm = 0xe9;
+	  nop = NOP_OPCODE;
+	  nop_offset = irel->r_offset + 3;
+	  disp = bfd_get_32 (abfd, contents + irel->r_offset);
+	  irel->r_offset -= 1;
+	  bfd_put_32 (abfd, disp, contents + irel->r_offset);
+	}
+      else
+	{
+	  /* Convert to "nop call foo".  ADDR_PREFIX_OPCODE
+	     is a nop prefix.  */
+	  modrm = 0xe8;
+	  nop = link_info->call_nop_byte;
+	  if (link_info->call_nop_as_suffix)
+	    {
+	      nop_offset = irel->r_offset + 3;
+	      disp = bfd_get_32 (abfd, contents + irel->r_offset);
+	      irel->r_offset -= 1;
+	      bfd_put_32 (abfd, disp, contents + irel->r_offset);
+	    }
+	  else
+	    nop_offset = irel->r_offset - 2;
+	}
+      bfd_put_8 (abfd, nop, contents + nop_offset);
+      bfd_put_8 (abfd, modrm, contents + irel->r_offset - 1);
+      r_type = R_X86_64_PC32;
+    }
+  else
+    {
+      unsigned int rex;
+      unsigned int rex_mask = REX_R;
+
+      if (r_type == R_X86_64_REX_GOTPCRELX)
+	rex = bfd_get_8 (abfd, contents + roff - 3);
+      else
+	rex = 0;
+
+      if (opcode == 0x8b)
+	{
+	  if (to_reloc_pc32)
+	    {
+	      /* Convert "mov foo@GOTPCREL(%rip), %reg" to
+		 "lea foo(%rip), %reg".  */
+	      opcode = 0x8d;
+	      r_type = R_X86_64_PC32;
+	    }
+	  else
+	    {
+	      /* Convert "mov foo@GOTPCREL(%rip), %reg" to
+		 "mov $foo, %reg".  */
+	      opcode = 0xc7;
+	      modrm = bfd_get_8 (abfd, contents + roff - 1);
+	      modrm = 0xc0 | (modrm & 0x38) >> 3;
+	      if ((rex & REX_W) != 0
+		  && ABI_64_P (link_info->output_bfd))
+		{
+		  /* Keep the REX_W bit in REX byte for LP64.  */
+		  r_type = R_X86_64_32S;
+		  goto rewrite_modrm_rex;
+		}
+	      else
+		{
+		  /* If the REX_W bit in REX byte isn't needed,
+		     use R_X86_64_32 and clear the W bit to avoid
+		     sign-extend imm32 to imm64.  */
+		  r_type = R_X86_64_32;
+		  /* Clear the W bit in REX byte.  */
+		  rex_mask |= REX_W;
+		  goto rewrite_modrm_rex;
+		}
+	    }
+	}
+      else
+	{
+	  /* R_X86_64_PC32 isn't supported.  */
+	  if (to_reloc_pc32)
+	    return TRUE;
+
+	  modrm = bfd_get_8 (abfd, contents + roff - 1);
+	  if (opcode == 0x85)
+	    {
+	      /* Convert "test %reg, foo@GOTPCREL(%rip)" to
+		 "test $foo, %reg".  */
+	      modrm = 0xc0 | (modrm & 0x38) >> 3;
+	      opcode = 0xf7;
+	    }
+	  else
+	    {
+	      /* Convert "binop foo@GOTPCREL(%rip), %reg" to
+		 "binop $foo, %reg".  */
+	      modrm = 0xc0 | (modrm & 0x38) >> 3 | (opcode & 0x3c);
+	      opcode = 0x81;
+	    }
+
+	  /* Use R_X86_64_32 with 32-bit operand to avoid relocation
+	     overflow when sign-extending imm32 to imm64.  */
+	  r_type = (rex & REX_W) != 0 ? R_X86_64_32S : R_X86_64_32;
+
+rewrite_modrm_rex:
+	  bfd_put_8 (abfd, modrm, contents + roff - 1);
+
+	  if (rex)
+	    {
+	      /* Move the R bit to the B bit in REX byte.  */
+	      rex = (rex & ~rex_mask) | (rex & REX_R) >> 2;
+	      bfd_put_8 (abfd, rex, contents + roff - 3);
+	    }
+
+	  /* No addend for R_X86_64_32/R_X86_64_32S relocations.  */
+	  irel->r_addend = 0;
+	}
+
+      bfd_put_8 (abfd, opcode, contents + roff - 2);
+    }
+
+  irel->r_info = htab->r_info (r_symndx, r_type);
+
+  *converted = TRUE;
+
+  return TRUE;
+}
+
 /* Look through the relocs for a section during the first phase, and
    calculate needed space in the global offset table, procedure
    linkage table, and dynamic reloc sections.  */
@@ -2946,24 +3341,7 @@ elf_x86_64_readonly_dynrelocs (struct elf_link_hash_entry *h,
   return TRUE;
 }
 
-/* With the local symbol, foo, we convert
-   mov foo@GOTPCREL(%rip), %reg
-   to
-   lea foo(%rip), %reg
-   and convert
-   call/jmp *foo@GOTPCREL(%rip)
-   to
-   nop call foo/jmp foo nop
-   When PIC is false, convert
-   test %reg, foo@GOTPCREL(%rip)
-   to
-   test $foo, %reg
-   and convert
-   binop foo@GOTPCREL(%rip), %reg
-   to
-   binop $foo, %reg
-   where binop is one of adc, add, and, cmp, or, sbb, sub, xor
-   instructions.  */
+/* Convert load via the GOT slot to load immediate.  */
 
 static bfd_boolean
 elf_x86_64_convert_load (bfd *abfd, asection *sec,
@@ -2974,12 +3352,8 @@ elf_x86_64_convert_load (bfd *abfd, asection *sec,
   Elf_Internal_Rela *irel, *irelend;
   bfd_byte *contents;
   struct elf_x86_64_link_hash_table *htab;
-  bfd_boolean changed_contents;
-  bfd_boolean changed_relocs;
+  bfd_boolean changed;
   bfd_signed_vma *local_got_refcounts;
-  bfd_vma maxpagesize;
-  bfd_boolean is_pic;
-  bfd_boolean require_reloc_pc32;
 
   /* Don't even try to convert non-ELF outputs.  */
   if (!is_elf_hash_table (link_info->hash))
@@ -3000,11 +3374,9 @@ elf_x86_64_convert_load (bfd *abfd, asection *sec,
   if (internal_relocs == NULL)
     return FALSE;
 
+  changed = FALSE;
   htab = elf_x86_64_hash_table (link_info);
-  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)
@@ -3015,405 +3387,62 @@ elf_x86_64_convert_load (bfd *abfd, asection *sec,
 	goto error_return;
     }
 
-  is_pic = bfd_link_pic (link_info);
-
-  /* TRUE if we can convert only to R_X86_64_PC32.  Enable it for
-     --no-relax.  */
-  require_reloc_pc32
-    = link_info->disable_target_specific_optimizations > 1;
-
   irelend = internal_relocs + sec->reloc_count;
   for (irel = internal_relocs; irel < irelend; irel++)
     {
       unsigned int r_type = ELF32_R_TYPE (irel->r_info);
-      unsigned int r_symndx = htab->r_sym (irel->r_info);
-      unsigned int indx;
+      unsigned int r_symndx;
       struct elf_link_hash_entry *h;
-      asection *tsec;
-      char symtype;
-      bfd_vma toff, roff;
-      bfd_signed_vma raddend;
-      unsigned int opcode;
-      unsigned int modrm;
-      bfd_boolean relocx;
-      bfd_boolean to_reloc_pc32;
-
-      relocx = (r_type == R_X86_64_GOTPCRELX
-		|| r_type == R_X86_64_REX_GOTPCRELX);
-      if (!relocx && r_type != R_X86_64_GOTPCREL)
-	continue;
+      bfd_boolean converted;
 
-      roff = irel->r_offset;
-      if (roff < (r_type == R_X86_64_REX_GOTPCRELX ? 3 : 2))
+      if (r_type != R_X86_64_GOTPCRELX
+	  && r_type != R_X86_64_REX_GOTPCRELX
+	  && r_type != R_X86_64_GOTPCREL)
 	continue;
 
-      raddend = irel->r_addend;
-      /* Addend for 32-bit PC-relative relocation must be -4.  */
-      if (raddend != -4)
-	continue;
-
-      opcode = bfd_get_8 (abfd, contents + roff - 2);
-
-      /* Convert mov to lea since it has been done for a while.  */
-      if (opcode != 0x8b)
-	{
-	  /* Only convert R_X86_64_GOTPCRELX and R_X86_64_REX_GOTPCRELX
-	     for call, jmp or one of adc, add, and, cmp, or, sbb, sub,
-	     test, xor instructions.  */
-	  if (!relocx)
-	    continue;
-	}
-
-      /* We convert only to R_X86_64_PC32:
-	 1. Branch.
-	 2. R_X86_64_GOTPCREL since we can't modify REX byte.
-	 3. require_reloc_pc32 is true.
-	 4. PIC.
-       */
-      to_reloc_pc32 = (opcode == 0xff
-		       || !relocx
-		       || require_reloc_pc32
-		       || is_pic);
-
-      /* Get the symbol referred to by the reloc.  */
+      r_symndx = htab->r_sym (irel->r_info);
       if (r_symndx < symtab_hdr->sh_info)
-	{
-	  Elf_Internal_Sym *isym;
-
-	  isym = bfd_sym_from_r_symndx (&htab->sym_cache,
-					abfd, r_symndx);
-
-	  symtype = ELF_ST_TYPE (isym->st_info);
-
-	  /* STT_GNU_IFUNC must keep GOTPCREL relocations and skip
-	     relocation against undefined symbols.  */
-	  if (symtype == STT_GNU_IFUNC || isym->st_shndx == SHN_UNDEF)
-	    continue;
-
-	  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);
-
-	  h = NULL;
-	  toff = isym->st_value;
-	}
+	h = elf_x86_64_get_local_sym_hash (htab, sec->owner,
+					   (const Elf_Internal_Rela *) irel,
+					   FALSE);
       else
 	{
-	  indx = r_symndx - symtab_hdr->sh_info;
-	  h = elf_sym_hashes (abfd)[indx];
-	  BFD_ASSERT (h != NULL);
-
+	  h = elf_sym_hashes (abfd)[r_symndx - symtab_hdr->sh_info];
 	  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 GOTPCREL relocations.  We also
-	     avoid optimizing GOTPCREL relocations againt _DYNAMIC
-	     since ld.so may use its link-time address.  */
-	  if (h->type == STT_GNU_IFUNC)
-	    continue;
-
-	  /* Undefined weak symbol is only bound locally in executable
-	     and its reference is resolved as 0 without relocation
-	     overflow.  We can only perform this optimization for
-	     GOTPCRELX relocations since we need to modify REX byte.
-	     It is OK convert mov with R_X86_64_GOTPCREL to
-	     R_X86_64_PC32.  */
-	  if ((relocx || opcode == 0x8b)
-	      && UNDEFINED_WEAK_RESOLVED_TO_ZERO (link_info,
-						  TRUE,
-						  elf_x86_64_hash_entry (h)))
-	    {
-	      if (opcode == 0xff)
-		{
-		  /* Skip for branch instructions since R_X86_64_PC32
-		     may overflow.  */
-		  if (require_reloc_pc32)
-		    continue;
-		}
-	      else if (relocx)
-		{
-		  /* For non-branch instructions, we can convert to
-		     R_X86_64_32/R_X86_64_32S since we know if there
-		     is a REX byte.  */
-		  to_reloc_pc32 = FALSE;
-		}
-
-	      /* Since we don't know the current PC when PIC is true,
-		 we can't convert to R_X86_64_PC32.  */
-	      if (to_reloc_pc32 && is_pic)
-		continue;
-
-	      goto convert;
-	    }
-	  else if ((h->def_regular
-		    || h->root.type == bfd_link_hash_defined
-		    || h->root.type == bfd_link_hash_defweak)
-		   && h != htab->elf.hdynamic
-		   && SYMBOL_REFERENCES_LOCAL (link_info, h))
-	    {
-	      /* bfd_link_hash_new or bfd_link_hash_undefined is
-		 set by an assignment in a linker script in
-		 bfd_elf_record_link_assignment.   */
-	      if (h->def_regular
-		  && (h->root.type == bfd_link_hash_new
-		      || h->root.type == bfd_link_hash_undefined))
-		{
-		  /* Skip since R_X86_64_32/R_X86_64_32S may overflow.  */
-		  if (require_reloc_pc32)
-		    continue;
-		  goto convert;
-		}
-	      tsec = h->root.u.def.section;
-	      toff = h->root.u.def.value;
-	      symtype = h->type;
-	    }
-	  else
-	    continue;
-	}
-
-      /* We can only estimate relocation overflow for R_X86_64_PC32.  */
-      if (!to_reloc_pc32)
-	goto convert;
-
-      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 += raddend;
-
-	   toff = _bfd_merged_section_offset (abfd, &tsec,
-					      elf_section_data (tsec)->sec_info,
-					      toff);
-
-	   if (symtype != STT_SECTION)
-	     toff += raddend;
 	}
-      else
-	toff += raddend;
 
-      /* Don't convert if R_X86_64_PC32 relocation overflows.  */
-      if (tsec->output_section == sec->output_section)
-	{
-	  if ((toff - roff + 0x80000000) > 0xffffffff)
-	    continue;
-	}
-      else
-	{
-	  bfd_signed_vma distance;
-
-	  /* At this point, we don't know the load addresses of TSEC
-	     section nor SEC section.  We estimate the distrance between
-	     SEC and TSEC.  We store the estimated distances in the
-	     compressed_size field of the output section, which is only
-	     used to decompress the compressed input section.  */
-	  if (sec->output_section->compressed_size == 0)
-	    {
-	      asection *asect;
-	      bfd_size_type size = 0;
-	      for (asect = link_info->output_bfd->sections;
-		   asect != NULL;
-		   asect = asect->next)
-		/* Skip debug sections since compressed_size is used to
-		   compress debug sections.  */
-		if ((asect->flags & SEC_DEBUGGING) == 0)
-		  {
-		    asection *i;
-		    for (i = asect->map_head.s;
-			 i != NULL;
-			 i = i->map_head.s)
-		      {
-			size = align_power (size, i->alignment_power);
-			size += i->size;
-		      }
-		    asect->compressed_size = size;
-		  }
-	    }
-
-	  /* Don't convert GOTPCREL relocations if TSEC isn't placed
-	     after SEC.  */
-	  distance = (tsec->output_section->compressed_size
-		      - sec->output_section->compressed_size);
-	  if (distance < 0)
-	    continue;
+      /* STT_GNU_IFUNC must keep GOTPCREL relocations.  */
+      if (h != NULL && h->type == STT_GNU_IFUNC)
+	continue;
 
-	  /* Take PT_GNU_RELRO segment into account by adding
-	     maxpagesize.  */
-	  if ((toff + distance + maxpagesize - roff + 0x80000000)
-	      > 0xffffffff)
-	    continue;
-	}
+      converted = FALSE;
+      if (!elf_x86_64_convert_load_reloc (abfd, sec, contents, irel, h,
+					  &converted, link_info))
+	goto error_return;
 
-convert:
-      if (opcode == 0xff)
+      if (converted)
 	{
-	  /* We have "call/jmp *foo@GOTPCREL(%rip)".  */
-	  unsigned int nop;
-	  unsigned int disp;
-	  bfd_vma nop_offset;
-
-	  /* Convert R_X86_64_GOTPCRELX and R_X86_64_REX_GOTPCRELX to
-	     R_X86_64_PC32.  */
-	  modrm = bfd_get_8 (abfd, contents + roff - 1);
-	  if (modrm == 0x25)
+	  changed = converted;
+	  if (h)
 	    {
-	      /* Convert to "jmp foo nop".  */
-	      modrm = 0xe9;
-	      nop = NOP_OPCODE;
-	      nop_offset = irel->r_offset + 3;
-	      disp = bfd_get_32 (abfd, contents + irel->r_offset);
-	      irel->r_offset -= 1;
-	      bfd_put_32 (abfd, disp, contents + irel->r_offset);
+	      if (h->got.refcount > 0)
+		h->got.refcount -= 1;
 	    }
 	  else
 	    {
-	      /* Convert to "nop call foo".  ADDR_PREFIX_OPCODE
-		 is a nop prefix.  */
-	      modrm = 0xe8;
-	      nop = link_info->call_nop_byte;
-	      if (link_info->call_nop_as_suffix)
-		{
-		  nop_offset = irel->r_offset + 3;
-		  disp = bfd_get_32 (abfd, contents + irel->r_offset);
-		  irel->r_offset -= 1;
-		  bfd_put_32 (abfd, disp, contents + irel->r_offset);
-		}
-	      else
-		nop_offset = irel->r_offset - 2;
+	      if (local_got_refcounts != NULL
+		  && local_got_refcounts[r_symndx] > 0)
+		local_got_refcounts[r_symndx] -= 1;
 	    }
-	  bfd_put_8 (abfd, nop, contents + nop_offset);
-	  bfd_put_8 (abfd, modrm, contents + irel->r_offset - 1);
-	  r_type = R_X86_64_PC32;
-	}
-      else
-	{
-	  unsigned int rex;
-	  unsigned int rex_mask = REX_R;
-
-	  if (r_type == R_X86_64_REX_GOTPCRELX)
-	    rex = bfd_get_8 (abfd, contents + roff - 3);
-	  else
-	    rex = 0;
-
-	  if (opcode == 0x8b)
-	    {
-	      if (to_reloc_pc32)
-		{
-		  /* Convert "mov foo@GOTPCREL(%rip), %reg" to
-		     "lea foo(%rip), %reg".  */
-		  opcode = 0x8d;
-		  r_type = R_X86_64_PC32;
-		}
-	      else
-		{
-		  /* Convert "mov foo@GOTPCREL(%rip), %reg" to
-		     "mov $foo, %reg".  */
-		  opcode = 0xc7;
-		  modrm = bfd_get_8 (abfd, contents + roff - 1);
-		  modrm = 0xc0 | (modrm & 0x38) >> 3;
-		  if ((rex & REX_W) != 0
-		      && ABI_64_P (link_info->output_bfd))
-		    {
-		      /* Keep the REX_W bit in REX byte for LP64.  */
-		      r_type = R_X86_64_32S;
-		      goto rewrite_modrm_rex;
-		    }
-		  else
-		    {
-		      /* If the REX_W bit in REX byte isn't needed,
-			 use R_X86_64_32 and clear the W bit to avoid
-			 sign-extend imm32 to imm64.  */
-		      r_type = R_X86_64_32;
-		      /* Clear the W bit in REX byte.  */
-		      rex_mask |= REX_W;
-		      goto rewrite_modrm_rex;
-		    }
-		}
-	    }
-	  else
-	    {
-	      /* R_X86_64_PC32 isn't supported.  */
-	      if (to_reloc_pc32)
-		continue;
-
-	      modrm = bfd_get_8 (abfd, contents + roff - 1);
-	      if (opcode == 0x85)
-		{
-		  /* Convert "test %reg, foo@GOTPCREL(%rip)" to
-		     "test $foo, %reg".  */
-		  modrm = 0xc0 | (modrm & 0x38) >> 3;
-		  opcode = 0xf7;
-		}
-	      else
-		{
-		  /* Convert "binop foo@GOTPCREL(%rip), %reg" to
-		     "binop $foo, %reg".  */
-		  modrm = 0xc0 | (modrm & 0x38) >> 3 | (opcode & 0x3c);
-		  opcode = 0x81;
-		}
-
-	      /* Use R_X86_64_32 with 32-bit operand to avoid relocation
-		 overflow when sign-extending imm32 to imm64.  */
-	      r_type = (rex & REX_W) != 0 ? R_X86_64_32S : R_X86_64_32;
-
-rewrite_modrm_rex:
-	      bfd_put_8 (abfd, modrm, contents + roff - 1);
-
-	      if (rex)
-		{
-		  /* Move the R bit to the B bit in REX byte.  */
-		  rex = (rex & ~rex_mask) | (rex & REX_R) >> 2;
-		  bfd_put_8 (abfd, rex, contents + roff - 3);
-		}
-
-	      /* No addend for R_X86_64_32/R_X86_64_32S relocations.  */
-	      irel->r_addend = 0;
-	    }
-
-	  bfd_put_8 (abfd, opcode, contents + roff - 2);
-	}
-
-      irel->r_info = htab->r_info (r_symndx, r_type);
-      changed_contents = TRUE;
-      changed_relocs = TRUE;
-
-      if (h)
-	{
-	  if (h->got.refcount > 0)
-	    h->got.refcount -= 1;
-	}
-      else
-	{
-	  if (local_got_refcounts != NULL
-	      && local_got_refcounts[r_symndx] > 0)
-	    local_got_refcounts[r_symndx] -= 1;
 	}
     }
 
   if (contents != NULL
       && elf_section_data (sec)->this_hdr.contents != contents)
     {
-      if (!changed_contents && !link_info->keep_memory)
+      if (!changed && !link_info->keep_memory)
 	free (contents);
       else
 	{
@@ -3424,7 +3453,7 @@ rewrite_modrm_rex:
 
   if (elf_section_data (sec)->relocs != internal_relocs)
     {
-      if (!changed_relocs)
+      if (!changed)
 	free (internal_relocs);
       else
 	elf_section_data (sec)->relocs = internal_relocs;


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