[gold patch] PR gold/13249: Fix RELATIVE relocs for IFUNC symbols

Cary Coutant ccoutant@google.com
Thu Oct 6 21:13:00 GMT 2011


This patch fixes PR gold/13249, where gold provides the wrong addend
field for RELATIVE relocations for references to IFUNC symbols. I
added a use_plt_offset field to the Output_reloc class, and pass that
flag from the target-specific Scan::local routine through
Output_data_reloc::add_local_relative. If the flag is set, the
relocation will put the address of the PLT entry into the addend field
(instead of the value of the IFUNC symbol), to match the value
statically written to the GOT entry or function pointer.

Tested on x86_64, also builds for sparc and powerpc targets. OK to commit?

-cary


	PR gold/13249
	* gold/output.cc (Output_reloc::Output_reloc): Add use_plt_offset flag.
	(Output_reloc::symbol_value): Return PLT offset if flag is set.
	* gold/output.h (class Output_reloc): Add use_plt_offset flag.
	(Output_reloc::type_): Adjust size of bit field.
	(Output_reloc::use_plt_offset_): New bit field.
	(class Output_data_reloc): Adjust all calls to Output_reloc_type.
	(Output_data_reloc::add_local_relative): (RELA only) Add use_plt_offset
	flag.  Adjust all callers.
	* gold/x86_64.cc (Target_x86_64::Scan::local): Check for IFUNC when
	creating RELATIVE relocations.
-------------- next part --------------
2011-10-06  Cary Coutant  <ccoutant@google.com>

	* gold/output.cc (Output_reloc::Output_reloc): Add use_plt_offset flag.
	(Output_reloc::symbol_value): Return PLT offset if flag is set.
	* gold/output.h (class Output_reloc): Add use_plt_offset flag.
	(Output_reloc::type_): Adjust size of bit field.
	(Output_reloc::use_plt_offset_): New bit field.
	(class Output_data_reloc): Adjust all calls to Output_reloc_type.
	(Output_data_reloc::add_local_relative): (RELA only) Add use_plt_offset
	flag.  Adjust all callers.
	* gold/x86_64.cc (Target_x86_64::Scan::local): Check for IFUNC when
	creating RELATIVE relocations.


commit 5e461e6b1cd615764a6a2925b7a7c01417bf3602
Author: Cary Coutant <ccoutant@google.com>
Date:   Thu Oct 6 13:53:56 2011 -0700

    Fix ifuncmain7pie failure (PR gold/13249).

diff --git a/gold/output.cc b/gold/output.cc
index 63f0109..e91f1ba 100644
--- a/gold/output.cc
+++ b/gold/output.cc
@@ -706,7 +706,7 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
     bool is_symbolless)
   : address_(address), local_sym_index_(GSYM_CODE), type_(type),
     is_relative_(is_relative), is_symbolless_(is_symbolless),
-    is_section_symbol_(false), shndx_(INVALID_CODE)
+    is_section_symbol_(false), use_plt_offset_(false), shndx_(INVALID_CODE)
 {
   // this->type_ is a bitfield; make sure TYPE fits.
   gold_assert(this->type_ == type);
@@ -727,7 +727,7 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
     bool is_symbolless)
   : address_(address), local_sym_index_(GSYM_CODE), type_(type),
     is_relative_(is_relative), is_symbolless_(is_symbolless),
-    is_section_symbol_(false), shndx_(shndx)
+    is_section_symbol_(false), use_plt_offset_(false), shndx_(shndx)
 {
   gold_assert(shndx != INVALID_CODE);
   // this->type_ is a bitfield; make sure TYPE fits.
@@ -749,10 +749,12 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
     Address address,
     bool is_relative,
     bool is_symbolless,
-    bool is_section_symbol)
+    bool is_section_symbol,
+    bool use_plt_offset)
   : address_(address), local_sym_index_(local_sym_index), type_(type),
     is_relative_(is_relative), is_symbolless_(is_symbolless),
-    is_section_symbol_(is_section_symbol), shndx_(INVALID_CODE)
+    is_section_symbol_(is_section_symbol), use_plt_offset_(use_plt_offset),
+    shndx_(INVALID_CODE)
 {
   gold_assert(local_sym_index != GSYM_CODE
               && local_sym_index != INVALID_CODE);
@@ -773,10 +775,12 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
     Address address,
     bool is_relative,
     bool is_symbolless,
-    bool is_section_symbol)
+    bool is_section_symbol,
+    bool use_plt_offset)
   : address_(address), local_sym_index_(local_sym_index), type_(type),
     is_relative_(is_relative), is_symbolless_(is_symbolless),
-    is_section_symbol_(is_section_symbol), shndx_(shndx)
+    is_section_symbol_(is_section_symbol), use_plt_offset_(use_plt_offset),
+    shndx_(shndx)
 {
   gold_assert(local_sym_index != GSYM_CODE
               && local_sym_index != INVALID_CODE);
@@ -799,7 +803,7 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
     Address address)
   : address_(address), local_sym_index_(SECTION_CODE), type_(type),
     is_relative_(false), is_symbolless_(false),
-    is_section_symbol_(true), shndx_(INVALID_CODE)
+    is_section_symbol_(true), use_plt_offset_(false), shndx_(INVALID_CODE)
 {
   // this->type_ is a bitfield; make sure TYPE fits.
   gold_assert(this->type_ == type);
@@ -820,7 +824,7 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
     Address address)
   : address_(address), local_sym_index_(SECTION_CODE), type_(type),
     is_relative_(false), is_symbolless_(false),
-    is_section_symbol_(true), shndx_(shndx)
+    is_section_symbol_(true), use_plt_offset_(false), shndx_(shndx)
 {
   gold_assert(shndx != INVALID_CODE);
   // this->type_ is a bitfield; make sure TYPE fits.
@@ -842,7 +846,7 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
     Address address)
   : address_(address), local_sym_index_(0), type_(type),
     is_relative_(false), is_symbolless_(false),
-    is_section_symbol_(false), shndx_(INVALID_CODE)
+    is_section_symbol_(false), use_plt_offset_(false), shndx_(INVALID_CODE)
 {
   // this->type_ is a bitfield; make sure TYPE fits.
   gold_assert(this->type_ == type);
@@ -858,7 +862,7 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
     Address address)
   : address_(address), local_sym_index_(0), type_(type),
     is_relative_(false), is_symbolless_(false),
-    is_section_symbol_(false), shndx_(shndx)
+    is_section_symbol_(false), use_plt_offset_(false), shndx_(shndx)
 {
   gold_assert(shndx != INVALID_CODE);
   // this->type_ is a bitfield; make sure TYPE fits.
@@ -877,7 +881,7 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
     Address address)
   : address_(address), local_sym_index_(TARGET_CODE), type_(type),
     is_relative_(false), is_symbolless_(false),
-    is_section_symbol_(false), shndx_(INVALID_CODE)
+    is_section_symbol_(false), use_plt_offset_(false), shndx_(INVALID_CODE)
 {
   // this->type_ is a bitfield; make sure TYPE fits.
   gold_assert(this->type_ == type);
@@ -894,7 +898,7 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
     Address address)
   : address_(address), local_sym_index_(TARGET_CODE), type_(type),
     is_relative_(false), is_symbolless_(false),
-    is_section_symbol_(false), shndx_(shndx)
+    is_section_symbol_(false), use_plt_offset_(false), shndx_(shndx)
 {
   gold_assert(shndx != INVALID_CODE);
   // this->type_ is a bitfield; make sure TYPE fits.
@@ -1122,6 +1126,12 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::symbol_value(
       this->u1_.relobj->sized_relobj();
   gold_assert(relobj != NULL);
   const Symbol_value<size>* symval = relobj->local_symbol(lsi);
+  if (this->use_plt_offset_)
+    {
+      uint64_t plt_address =
+	  parameters->target().plt_address_for_local(relobj, lsi);
+      return plt_address + relobj->local_plt_offset(lsi);
+    }
   return symval->value(relobj, addend);
 }
 
diff --git a/gold/output.h b/gold/output.h
index cabec5c..cfb1867 100644
--- a/gold/output.h
+++ b/gold/output.h
@@ -1033,12 +1033,14 @@ class Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>
   Output_reloc(Sized_relobj<size, big_endian>* relobj,
 	       unsigned int local_sym_index, unsigned int type,
 	       Output_data* od, Address address, bool is_relative,
-               bool is_symbolless, bool is_section_symbol);
+               bool is_symbolless, bool is_section_symbol,
+               bool use_plt_offset);
 
   Output_reloc(Sized_relobj<size, big_endian>* relobj,
 	       unsigned int local_sym_index, unsigned int type,
 	       unsigned int shndx, Address address, bool is_relative,
-               bool is_symbolless, bool is_section_symbol);
+               bool is_symbolless, bool is_section_symbol,
+               bool use_plt_offset);
 
   // A reloc against the STT_SECTION symbol of an output section.
 
@@ -1216,7 +1218,7 @@ class Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>
   // input file.
   unsigned int local_sym_index_;
   // The reloc type--a processor specific code.
-  unsigned int type_ : 29;
+  unsigned int type_ : 28;
   // True if the relocation is a RELATIVE relocation.
   bool is_relative_ : 1;
   // True if the relocation is one which should not use
@@ -1224,6 +1226,9 @@ class Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>
   bool is_symbolless_ : 1;
   // True if the relocation is against a section symbol.
   bool is_section_symbol_ : 1;
+  // True if the addend should be the PLT offset.
+  // (Used only for RELA, but stored here for space.)
+  bool use_plt_offset_ : 1;
   // If the reloc address is an input section in an object, the
   // section index.  This is INVALID_CODE if the reloc address is
   // specified in some other way.
@@ -1268,9 +1273,10 @@ class Output_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian>
 	       unsigned int local_sym_index, unsigned int type,
 	       Output_data* od, Address address,
 	       Addend addend, bool is_relative,
-	       bool is_symbolless, bool is_section_symbol)
+	       bool is_symbolless, bool is_section_symbol,
+	       bool use_plt_offset)
     : rel_(relobj, local_sym_index, type, od, address, is_relative,
-           is_symbolless, is_section_symbol),
+           is_symbolless, is_section_symbol, use_plt_offset),
       addend_(addend)
   { }
 
@@ -1278,9 +1284,10 @@ class Output_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian>
 	       unsigned int local_sym_index, unsigned int type,
 	       unsigned int shndx, Address address,
 	       Addend addend, bool is_relative,
-	       bool is_symbolless, bool is_section_symbol)
+	       bool is_symbolless, bool is_section_symbol,
+	       bool use_plt_offset)
     : rel_(relobj, local_sym_index, type, shndx, address, is_relative,
-           is_symbolless, is_section_symbol),
+           is_symbolless, is_section_symbol, use_plt_offset),
       addend_(addend)
   { }
 
@@ -1571,7 +1578,7 @@ class Output_data_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>
 	    Output_data* od, Address address)
   {
     this->add(od, Output_reloc_type(relobj, local_sym_index, type, od,
-                                    address, false, false, false));
+                                    address, false, false, false, false));
   }
 
   void
@@ -1580,7 +1587,7 @@ class Output_data_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>
 	    Output_data* od, unsigned int shndx, Address address)
   {
     this->add(od, Output_reloc_type(relobj, local_sym_index, type, shndx,
-				    address, false, false, false));
+				    address, false, false, false, false));
   }
 
   // Add a RELATIVE reloc against a local symbol.
@@ -1591,7 +1598,7 @@ class Output_data_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>
 	             Output_data* od, Address address)
   {
     this->add(od, Output_reloc_type(relobj, local_sym_index, type, od,
-                                    address, true, true, false));
+                                    address, true, true, false, false));
   }
 
   void
@@ -1600,7 +1607,7 @@ class Output_data_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>
 	             Output_data* od, unsigned int shndx, Address address)
   {
     this->add(od, Output_reloc_type(relobj, local_sym_index, type, shndx,
-				    address, true, true, false));
+				    address, true, true, false, false));
   }
 
   // Add a local relocation which does not use a symbol for the relocation,
@@ -1612,7 +1619,7 @@ class Output_data_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>
 			      Output_data* od, Address address)
   {
     this->add(od, Output_reloc_type(relobj, local_sym_index, type, od,
-                                    address, false, true, false));
+                                    address, false, true, false, false));
   }
 
   void
@@ -1622,7 +1629,7 @@ class Output_data_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>
 			      Address address)
   {
     this->add(od, Output_reloc_type(relobj, local_sym_index, type, shndx,
-				    address, false, true, false));
+				    address, false, true, false, false));
   }
 
   // Add a reloc against a local section symbol.  This will be
@@ -1635,7 +1642,7 @@ class Output_data_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>
                     Output_data* od, Address address)
   {
     this->add(od, Output_reloc_type(relobj, input_shndx, type, od,
-                                    address, false, false, true));
+                                    address, false, false, true, false));
   }
 
   void
@@ -1644,7 +1651,7 @@ class Output_data_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>
                     Output_data* od, unsigned int shndx, Address address)
   {
     this->add(od, Output_reloc_type(relobj, input_shndx, type, shndx,
-                                    address, false, false, true));
+                                    address, false, false, true, false));
   }
 
   // A reloc against the STT_SECTION symbol of an output section.
@@ -1767,7 +1774,7 @@ class Output_data_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian>
 	    Output_data* od, Address address, Addend addend)
   {
     this->add(od, Output_reloc_type(relobj, local_sym_index, type, od, address,
-				    addend, false, false, false));
+				    addend, false, false, false, false));
   }
 
   void
@@ -1777,7 +1784,8 @@ class Output_data_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian>
 	    Addend addend)
   {
     this->add(od, Output_reloc_type(relobj, local_sym_index, type, shndx,
-                                    address, addend, false, false, false));
+                                    address, addend, false, false, false,
+                                    false));
   }
 
   // Add a RELATIVE reloc against a local symbol.
@@ -1785,20 +1793,23 @@ class Output_data_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian>
   void
   add_local_relative(Sized_relobj<size, big_endian>* relobj,
 	             unsigned int local_sym_index, unsigned int type,
-	             Output_data* od, Address address, Addend addend)
+	             Output_data* od, Address address, Addend addend,
+	             bool use_plt_offset)
   {
     this->add(od, Output_reloc_type(relobj, local_sym_index, type, od, address,
-				    addend, true, true, false));
+				    addend, true, true, false,
+				    use_plt_offset));
   }
 
   void
   add_local_relative(Sized_relobj<size, big_endian>* relobj,
 	             unsigned int local_sym_index, unsigned int type,
 	             Output_data* od, unsigned int shndx, Address address,
-	             Addend addend)
+	             Addend addend, bool use_plt_offset)
   {
     this->add(od, Output_reloc_type(relobj, local_sym_index, type, shndx,
-                                    address, addend, true, true, false));
+                                    address, addend, true, true, false,
+                                    use_plt_offset));
   }
 
   // Add a local relocation which does not use a symbol for the relocation,
@@ -1810,7 +1821,7 @@ class Output_data_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian>
 			      Output_data* od, Address address, Addend addend)
   {
     this->add(od, Output_reloc_type(relobj, local_sym_index, type, od, address,
-				    addend, false, true, false));
+				    addend, false, true, false, false));
   }
 
   void
@@ -1820,7 +1831,8 @@ class Output_data_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian>
 			      Address address, Addend addend)
   {
     this->add(od, Output_reloc_type(relobj, local_sym_index, type, shndx,
-                                    address, addend, false, true, false));
+                                    address, addend, false, true, false,
+                                    false));
   }
 
   // Add a reloc against a local section symbol.  This will be
@@ -1833,7 +1845,7 @@ class Output_data_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian>
                     Output_data* od, Address address, Addend addend)
   {
     this->add(od, Output_reloc_type(relobj, input_shndx, type, od, address,
-				    addend, false, false, true));
+				    addend, false, false, true, false));
   }
 
   void
@@ -1843,7 +1855,8 @@ class Output_data_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian>
 		    Addend addend)
   {
     this->add(od, Output_reloc_type(relobj, input_shndx, type, shndx,
-                                    address, addend, false, false, true));
+                                    address, addend, false, false, true,
+                                    false));
   }
 
   // A reloc against the STT_SECTION symbol of an output section.
diff --git a/gold/powerpc.cc b/gold/powerpc.cc
index 45783c3..62a17ca 100644
--- a/gold/powerpc.cc
+++ b/gold/powerpc.cc
@@ -1329,7 +1329,7 @@ Target_powerpc<size, big_endian>::Scan::local(
               rela_dyn->add_local_relative(object, r_sym, r_type,
 					   output_section, data_shndx,
 					   reloc.get_r_offset(),
-					   reloc.get_r_addend());
+					   reloc.get_r_addend(), false);
             }
         }
       break;
@@ -1372,7 +1372,7 @@ Target_powerpc<size, big_endian>::Scan::local(
 		object->set_local_got_offset(r_sym, GOT_TYPE_STANDARD, off);
 		rela_dyn->add_local_relative(object, r_sym,
 					     elfcpp::R_POWERPC_RELATIVE,
-					     got, off, 0);
+					     got, off, 0, false);
 	      }
           }
 	else
diff --git a/gold/sparc.cc b/gold/sparc.cc
index 9145fcb..1d2cbad 100644
--- a/gold/sparc.cc
+++ b/gold/sparc.cc
@@ -1852,7 +1852,7 @@ Target_sparc<size, big_endian>::Scan::local(
           rela_dyn->add_local_relative(object, r_sym, elfcpp::R_SPARC_RELATIVE,
 				       output_section, data_shndx,
 				       reloc.get_r_offset(),
-				       reloc.get_r_addend());
+				       reloc.get_r_addend(), false);
         }
       break;
 
@@ -1943,7 +1943,7 @@ Target_sparc<size, big_endian>::Scan::local(
 		object->set_local_got_offset(r_sym, GOT_TYPE_STANDARD, off);
 		rela_dyn->add_local_relative(object, r_sym,
 					     elfcpp::R_SPARC_RELATIVE,
-					     got, off, 0);
+					     got, off, 0, false);
 	      }
 	  }
 	else
diff --git a/gold/x86_64.cc b/gold/x86_64.cc
index e6b0021..e61d7d1 100644
--- a/gold/x86_64.cc
+++ b/gold/x86_64.cc
@@ -1549,7 +1549,7 @@ Target_x86_64::reserve_local_got_entry(
     case GOT_TYPE_STANDARD:
       if (parameters->options().output_is_position_independent())
 	rela_dyn->add_local_relative(obj, r_sym, elfcpp::R_X86_64_RELATIVE,
-				     this->got_, got_offset, 0);
+				     this->got_, got_offset, 0, false);
       break;
     case GOT_TYPE_TLS_OFFSET:
       rela_dyn->add_local(obj, r_sym, elfcpp::R_X86_64_TPOFF64,
@@ -1953,8 +1953,8 @@ Target_x86_64::Scan::local(Symbol_table* symtab,
                            const elfcpp::Sym<64, false>& lsym)
 {
   // A local STT_GNU_IFUNC symbol may require a PLT entry.
-  if (lsym.get_st_type() == elfcpp::STT_GNU_IFUNC
-      && this->reloc_needs_plt_for_ifunc(object, r_type))
+  bool is_ifunc = lsym.get_st_type() == elfcpp::STT_GNU_IFUNC;
+  if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type))
     {
       unsigned int r_sym = elfcpp::elf_r_sym<64>(reloc.get_r_info());
       target->make_local_ifunc_plt_entry(symtab, layout, object, r_sym);
@@ -1982,7 +1982,7 @@ Target_x86_64::Scan::local(Symbol_table* symtab,
 				       elfcpp::R_X86_64_RELATIVE,
 				       output_section, data_shndx,
 				       reloc.get_r_offset(),
-				       reloc.get_r_addend());
+				       reloc.get_r_addend(), is_ifunc);
         }
       break;
 
@@ -2058,7 +2058,7 @@ Target_x86_64::Scan::local(Symbol_table* symtab,
 	// lets function pointers compare correctly with shared
 	// libraries.  Otherwise we would need an IRELATIVE reloc.
 	bool is_new;
-	if (lsym.get_st_type() == elfcpp::STT_GNU_IFUNC)
+	if (is_ifunc)
 	  is_new = got->add_local_plt(object, r_sym, GOT_TYPE_STANDARD);
 	else
 	  is_new = got->add_local(object, r_sym, GOT_TYPE_STANDARD);
@@ -2076,7 +2076,7 @@ Target_x86_64::Scan::local(Symbol_table* symtab,
 		      object->local_got_offset(r_sym, GOT_TYPE_STANDARD);
 		    rela_dyn->add_local_relative(object, r_sym,
 						 elfcpp::R_X86_64_RELATIVE,
-						 got, got_offset, 0);
+						 got, got_offset, 0, is_ifunc);
 		  }
                 else
                   {


More information about the Binutils mailing list