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]

gold patch committed: Finish GNU2 TLS support


The support in gold for GNU2 TLS wasn't quite right in two areas.
Firstly, the TLSDESC relocations should go into the lazily resolved
PLT relocations, following the JUMP_SLOT relocations.  Secondly, the
support for local symbols in shared libraries was just wrong.

On x86, local symbols in shared libraries use two GOT entries and an
absolute relocation.  The second GOT entry has to hold the offset of
the local symbol in the TLS segment.  The R_386_TLS_DESC relocation is
a simple absolute relocation which does not refer to any symbol.
Implementing this required adding the notion of an absolute
relocation.

On x86_64, local symbols in shared libraries also use two GOT entries
and an absolute relocation.  In this case, the offset of the local
symbol in the TLS segment is not stored in the GOT, but rather in the
addend of the absolute R_X86_64_TLSDESC relocation (it's tragic that
the name is TLS_DESC in one case and TLSDESC in the other).  In order
to implement this in gold I introduced the notion of a target specific
relocation, where the value in the addend comes from a target specific
method.  I did this in a rather C like fashion to keep memory usage
down, casting through void*, although these cases are actually fairly
rare and perhaps it would be better to use a base class with virtual
methods.

Committed to mainline.

Ian


2010-01-08  Ian Lance Taylor  <iant@google.com>

	PR 10287
	PR 11063
	* i386.cc (class Target_i386): Change return type of plt_section
	to be non-const.
	(class Output_data_plt_i386): Add tls_desc_rel_ field.
	(Output_data_plt_i386::Output_data_plt_i386): Initialize
	tls_desc_rel_ field.
	(Output_data_plt_i386::rel_tls_desc): New function.
	(Target_i386::rel_tls_desc_section): New function.
	(Target_i386::Scan::local): Rewrite R_386_TLS_GOTDESC handling.
	(Target_i386::Scan::global): For R_386_TLS_GOTDESC put
	R_386_TLS_DESC reloc in rel_tls_desc_section.
	* x86_64.cc (class Target_x86_64): Add tlsdesc_reloc_info_ field.
	Define struct Tlsdesc_info.
	(Target_x86_64::Target_x86_64): Initialize tlsdesc_reloc_info_.
	(Target_x86_64::do_reloc_symbol_index): New function.
	(Target_x86_64::add_tlsdesc_info): New function.
	(class Output_data_plt_x86_64): Add tlsdesc_rel_ field.
	(Output_data_plt_x86_64::Output_data_plt_x86_64): Initialize
	tlsdesc_rel_ field.
	(Output_data_plt_x86_64::rela_plt): Rename from rel_plt.  Change
	all callers.
	(Output_data_plt_x86_64::rela_tlsdesc): New function.
	(Target_x86_64::rela_tlsdesc_section): New function.
	(Target_x86_64::Scan::local): Rewrite R_X86_64_GOTPC32_TLSDESC
	handling.
	(Target_x86_64::Scan::global): For R_X86_64_GOTPC32_TLSDESC put
	(Target_x86_64::do_reloc_addend): New function.
	R_X86_64_TLSDESC reloc in rela_tlsdesc_section.
	* output.h (class Output_reloc) [SHT_REL]: Add new constructor
	declarations.  Define TARGET_CODE.  Add arg field to u1_ union.
	(Output_reloc::type): New function.
	(Output_reloc::is_local_section_symbol): Check for TARGET_CODE.
	(Output_reloc::is_target_specific): New function.
	(Output_reloc::target_arg): New function.
	(class Output_reloc) [SHT_RELA]: Add four new constructors for
	absolute relocs and target specific relocs.
	(class Output_data_reloc) [SHT_REL]: Add add_absolute and
	add_target_specific.
	(class Output_data_reloc) [SHT_RELA]: Likewise.
	* output.cc (Output_reloc::Output_reloc): Add four new versions
	for absolute relocs and target specific relocs.
	(Output_reloc::set_needs_dynsym_index): Add TARGET_CODE case.
	(Output_reloc::get_symbol_index): Likewise.
	(Output_reloc::local_section_offset): Check that local_sym_index_
	is not TARGET_CODE or 0.
	(Output_reloc::symbol_value): Likewise.
	(Output_reloc::write) [SHT_RELA]: Call target for target specific
	reloc.
	* target.h (class Target): Add reloc_symbol_index and reloc_addend
	functions.  Add do_reloc_symbol_index and do_reloc_addend virtual
	functions.
	* layout.cc (add_target_dynamic_tags): Use output section for
	DT_PLTRELSZ and DT_JMPREL


Index: i386.cc
===================================================================
RCS file: /cvs/src/src/gold/i386.cc,v
retrieving revision 1.110
diff -p -u -r1.110 i386.cc
--- i386.cc	7 Jan 2010 20:43:35 -0000	1.110
+++ i386.cc	8 Jan 2010 19:15:46 -0000
@@ -368,7 +368,7 @@ class Target_i386 : public Target_freebs
 		      Sized_relobj<32, false>* object);
 
   // Get the PLT section.
-  const Output_data_plt_i386*
+  Output_data_plt_i386*
   plt_section() const
   {
     gold_assert(this->plt_ != NULL);
@@ -379,6 +379,10 @@ class Target_i386 : public Target_freebs
   Reloc_section*
   rel_dyn_section(Layout*);
 
+  // Get the section to use for TLS_DESC relocations.
+  Reloc_section*
+  rel_tls_desc_section(Layout*) const;
+
   // Add a potential copy relocation.
   void
   copy_reloc(Symbol_table* symtab, Layout* layout,
@@ -527,6 +531,10 @@ class Output_data_plt_i386 : public Outp
   rel_plt() const
   { return this->rel_; }
 
+  // Return where the TLS_DESC relocations should go.
+  Reloc_section*
+  rel_tls_desc(Layout*);
+
  protected:
   void
   do_adjust_output_section(Output_section* os);
@@ -563,6 +571,9 @@ class Output_data_plt_i386 : public Outp
 
   // The reloc section.
   Reloc_section* rel_;
+  // The TLS_DESC relocations, if necessary.  These must follow the
+  // regular PLT relocs.
+  Reloc_section* tls_desc_rel_;
   // The .got.plt section.
   Output_data_space* got_plt_;
   // The number of PLT entries.
@@ -575,7 +586,7 @@ class Output_data_plt_i386 : public Outp
 
 Output_data_plt_i386::Output_data_plt_i386(Layout* layout,
 					   Output_data_space* got_plt)
-  : Output_section_data(4), got_plt_(got_plt), count_(0)
+  : Output_section_data(4), tls_desc_rel_(NULL), got_plt_(got_plt), count_(0)
 {
   this->rel_ = new Reloc_section(false);
   layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL,
@@ -621,6 +632,24 @@ Output_data_plt_i386::add_entry(Symbol* 
   // appear in the relocations.
 }
 
+// Return where the TLS_DESC relocations should go, creating it if
+// necessary. These follow the JUMP_SLOT relocations.
+
+Output_data_plt_i386::Reloc_section*
+Output_data_plt_i386::rel_tls_desc(Layout* layout)
+{
+  if (this->tls_desc_rel_ == NULL)
+    {
+      this->tls_desc_rel_ = new Reloc_section(false);
+      layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL,
+				      elfcpp::SHF_ALLOC, this->tls_desc_rel_,
+				      true, false, false, false);
+      gold_assert(this->tls_desc_rel_->output_section() ==
+		  this->rel_->output_section());
+    }
+  return this->tls_desc_rel_;
+}
+
 // The first entry in the PLT for an executable.
 
 unsigned char Output_data_plt_i386::exec_first_plt_entry[plt_entry_size] =
@@ -771,6 +800,14 @@ Target_i386::make_plt_entry(Symbol_table
   this->plt_->add_entry(gsym);
 }
 
+// Get the section to use for TLS_DESC relocations.
+
+Target_i386::Reloc_section*
+Target_i386::rel_tls_desc_section(Layout* layout) const
+{
+  return this->plt_section()->rel_tls_desc(layout);
+}
+
 // Define the _TLS_MODULE_BASE_ symbol in the TLS segment.
 
 void
@@ -1055,17 +1092,20 @@ Target_i386::Scan::local(Symbol_table* s
                 Output_data_got<32, false>* got
                     = target->got_section(symtab, layout);
                 unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
-		unsigned int shndx = lsym.get_st_shndx();
-		bool is_ordinary;
-		shndx = object->adjust_sym_shndx(r_sym, shndx, &is_ordinary);
-		if (!is_ordinary)
-		  object->error(_("local symbol %u has bad shndx %u"),
-			      r_sym, shndx);
-                else
-		  got->add_local_pair_with_rel(object, r_sym, shndx,
-					       GOT_TYPE_TLS_DESC,
-					       target->rel_dyn_section(layout),
-					       elfcpp::R_386_TLS_DESC, 0);
+		if (!object->local_has_got_offset(r_sym, GOT_TYPE_TLS_DESC))
+		  {
+		    unsigned int got_offset = got->add_constant(0);
+		    // The local symbol value is stored in the second
+		    // GOT entry.
+		    got->add_local(object, r_sym, GOT_TYPE_TLS_DESC);
+		    // That set the GOT offset of the local symbol to
+		    // point to the second entry, but we want it to
+		    // point to the first.
+		    object->set_local_got_offset(r_sym, GOT_TYPE_TLS_DESC,
+						 got_offset);
+		    Reloc_section* rt = target->rel_tls_desc_section(layout);
+		    rt->add_absolute(elfcpp::R_386_TLS_DESC, got, got_offset);
+		  }
               }
             else if (optimized_type != tls::TLSOPT_TO_LE)
               unsupported_reloc_local(object, r_type);
@@ -1386,8 +1426,8 @@ Target_i386::Scan::global(Symbol_table* 
                 // Create a double GOT entry with an R_386_TLS_DESC reloc.
                 Output_data_got<32, false>* got
                     = target->got_section(symtab, layout);
-                got->add_global_pair_with_rel(gsym, GOT_TYPE_TLS_DESC,
-                                             target->rel_dyn_section(layout),
+		Reloc_section* rt = target->rel_tls_desc_section(layout);
+                got->add_global_pair_with_rel(gsym, GOT_TYPE_TLS_DESC, rt,
                                              elfcpp::R_386_TLS_DESC, 0);
               }
             else if (optimized_type == tls::TLSOPT_TO_IE)
Index: layout.cc
===================================================================
RCS file: /cvs/src/src/gold/layout.cc,v
retrieving revision 1.161
diff -p -u -r1.161 layout.cc
--- layout.cc	7 Jan 2010 21:09:31 -0000	1.161
+++ layout.cc	8 Jan 2010 19:15:47 -0000
@@ -3215,7 +3215,8 @@ Layout::create_interp(const Target* targ
 // If PLT_GOT is not NULL, then DT_PLTGOT points to it.
 
 // If PLT_REL is not NULL, it is used for DT_PLTRELSZ, and DT_JMPREL,
-// and we also set DT_PLTREL.
+// and we also set DT_PLTREL.  We use PLT_REL's output section, since
+// some targets have multiple reloc sections in PLT_REL.
 
 // If DYN_REL is not NULL, it is used for DT_REL/DT_RELA,
 // DT_RELSZ/DT_RELASZ, DT_RELENT/DT_RELAENT.
@@ -3238,8 +3239,8 @@ Layout::add_target_dynamic_tags(bool use
 
   if (plt_rel != NULL && plt_rel->output_section() != NULL)
     {
-      odyn->add_section_size(elfcpp::DT_PLTRELSZ, plt_rel);
-      odyn->add_section_address(elfcpp::DT_JMPREL, plt_rel);
+      odyn->add_section_size(elfcpp::DT_PLTRELSZ, plt_rel->output_section());
+      odyn->add_section_address(elfcpp::DT_JMPREL, plt_rel->output_section());
       odyn->add_constant(elfcpp::DT_PLTREL,
 			 use_rel ? elfcpp::DT_REL : elfcpp::DT_RELA);
     }
Index: output.cc
===================================================================
RCS file: /cvs/src/src/gold/output.cc,v
retrieving revision 1.113
diff -p -u -r1.113 output.cc
--- output.cc	7 Jan 2010 21:09:31 -0000	1.113
+++ output.cc	8 Jan 2010 19:15:47 -0000
@@ -756,6 +756,72 @@ Output_reloc<elfcpp::SHT_REL, dynamic, s
     os->set_needs_symtab_index();
 }
 
+// An absolute relocation.
+
+template<bool dynamic, int size, bool big_endian>
+Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
+    unsigned int type,
+    Output_data* od,
+    Address address)
+  : address_(address), local_sym_index_(0), type_(type),
+    is_relative_(false), is_section_symbol_(false), shndx_(INVALID_CODE)
+{
+  // this->type_ is a bitfield; make sure TYPE fits.
+  gold_assert(this->type_ == type);
+  this->u1_.relobj = NULL;
+  this->u2_.od = od;
+}
+
+template<bool dynamic, int size, bool big_endian>
+Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
+    unsigned int type,
+    Sized_relobj<size, big_endian>* relobj,
+    unsigned int shndx,
+    Address address)
+  : address_(address), local_sym_index_(0), type_(type),
+    is_relative_(false), is_section_symbol_(false), shndx_(shndx)
+{
+  gold_assert(shndx != INVALID_CODE);
+  // this->type_ is a bitfield; make sure TYPE fits.
+  gold_assert(this->type_ == type);
+  this->u1_.relobj = NULL;
+  this->u2_.relobj = relobj;
+}
+
+// A target specific relocation.
+
+template<bool dynamic, int size, bool big_endian>
+Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
+    unsigned int type,
+    void* arg,
+    Output_data* od,
+    Address address)
+  : address_(address), local_sym_index_(TARGET_CODE), type_(type),
+    is_relative_(false), is_section_symbol_(false), shndx_(INVALID_CODE)
+{
+  // this->type_ is a bitfield; make sure TYPE fits.
+  gold_assert(this->type_ == type);
+  this->u1_.arg = arg;
+  this->u2_.od = od;
+}
+
+template<bool dynamic, int size, bool big_endian>
+Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
+    unsigned int type,
+    void* arg,
+    Sized_relobj<size, big_endian>* relobj,
+    unsigned int shndx,
+    Address address)
+  : address_(address), local_sym_index_(TARGET_CODE), type_(type),
+    is_relative_(false), is_section_symbol_(false), shndx_(shndx)
+{
+  gold_assert(shndx != INVALID_CODE);
+  // this->type_ is a bitfield; make sure TYPE fits.
+  gold_assert(this->type_ == type);
+  this->u1_.arg = arg;
+  this->u2_.relobj = relobj;
+}
+
 // Record that we need a dynamic symbol index for this relocation.
 
 template<bool dynamic, int size, bool big_endian>
@@ -778,6 +844,10 @@ set_needs_dynsym_index()
       this->u1_.os->set_needs_dynsym_index();
       break;
 
+    case TARGET_CODE:
+      // The target must take care of this if necessary.
+      break;
+
     case 0:
       break;
 
@@ -822,6 +892,11 @@ Output_reloc<elfcpp::SHT_REL, dynamic, s
 	index = this->u1_.os->symtab_index();
       break;
 
+    case TARGET_CODE:
+      index = parameters->target().reloc_symbol_index(this->u1_.arg,
+						      this->type_);
+      break;
+
     case 0:
       // Relocations without symbols use a symbol index of 0.
       index = 0;
@@ -863,7 +938,9 @@ Output_reloc<elfcpp::SHT_REL, dynamic, s
 {
   gold_assert(this->local_sym_index_ != GSYM_CODE
               && this->local_sym_index_ != SECTION_CODE
+	      && this->local_sym_index_ != TARGET_CODE
               && this->local_sym_index_ != INVALID_CODE
+	      && this->local_sym_index_ != 0
               && this->is_section_symbol_);
   const unsigned int lsi = this->local_sym_index_;
   Output_section* os = this->u1_.relobj->output_section(lsi);
@@ -942,7 +1019,9 @@ Output_reloc<elfcpp::SHT_REL, dynamic, s
       return sym->value() + addend;
     }
   gold_assert(this->local_sym_index_ != SECTION_CODE
+	      && this->local_sym_index_ != TARGET_CODE
               && this->local_sym_index_ != INVALID_CODE
+	      && this->local_sym_index_ != 0
               && !this->is_section_symbol_);
   const unsigned int lsi = this->local_sym_index_;
   const Symbol_value<size>* symval = this->u1_.relobj->local_symbol(lsi);
@@ -1010,7 +1089,10 @@ Output_reloc<elfcpp::SHT_RELA, dynamic, 
   elfcpp::Rela_write<size, big_endian> orel(pov);
   this->rel_.write_rel(&orel);
   Addend addend = this->addend_;
-  if (this->rel_.is_relative())
+  if (this->rel_.is_target_specific())
+    addend = parameters->target().reloc_addend(this->rel_.target_arg(),
+					       this->rel_.type(), addend);
+  else if (this->rel_.is_relative())
     addend = this->rel_.symbol_value(addend);
   else if (this->rel_.is_local_section_symbol())
     addend = this->rel_.local_section_offset(addend);
Index: output.h
===================================================================
RCS file: /cvs/src/src/gold/output.h,v
retrieving revision 1.96
diff -p -u -r1.96 output.h
--- output.h	7 Jan 2010 21:09:31 -0000	1.96
+++ output.h	8 Jan 2010 19:15:48 -0000
@@ -1057,7 +1057,30 @@ class Output_reloc<elfcpp::SHT_REL, dyna
                Sized_relobj<size, big_endian>* relobj,
 	       unsigned int shndx, Address address);
 
-  // Return TRUE if this is a RELATIVE relocation.
+  // An absolute relocation with no symbol.
+
+  Output_reloc(unsigned int type, Output_data* od, Address address);
+
+  Output_reloc(unsigned int type, Sized_relobj<size, big_endian>* relobj,
+	       unsigned int shndx, Address address);
+
+  // A target specific relocation.  The target will be called to get
+  // the symbol index, passing ARG.  The type and offset will be set
+  // as for other relocation types.
+
+  Output_reloc(unsigned int type, void* arg, Output_data* od,
+	       Address address);
+
+  Output_reloc(unsigned int type, void* arg,
+	       Sized_relobj<size, big_endian>* relobj,
+	       unsigned int shndx, Address address);
+
+  // Return the reloc type.
+  unsigned int
+  type() const
+  { return this->type_; }
+
+  // Return whether this is a RELATIVE relocation.
   bool
   is_relative() const
   { return this->is_relative_; }
@@ -1069,9 +1092,24 @@ class Output_reloc<elfcpp::SHT_REL, dyna
     return (this->local_sym_index_ != GSYM_CODE
             && this->local_sym_index_ != SECTION_CODE
             && this->local_sym_index_ != INVALID_CODE
+	    && this->local_sym_index_ != TARGET_CODE
             && this->is_section_symbol_);
   }
 
+  // Return whether this is a target specific relocation.
+  bool
+  is_target_specific() const
+  { return this->local_sym_index_ == TARGET_CODE; }
+
+  // Return the argument to pass to the target for a target specific
+  // relocation.
+  void*
+  target_arg() const
+  {
+    gold_assert(this->local_sym_index_ == TARGET_CODE);
+    return this->u1_.arg;
+  }
+
   // For a local section symbol, return the offset of the input
   // section within the output section.  ADDEND is the addend being
   // applied to the input section.
@@ -1124,8 +1162,10 @@ class Output_reloc<elfcpp::SHT_REL, dyna
     GSYM_CODE = -1U,
     // Output section.
     SECTION_CODE = -2U,
+    // Target specific.
+    TARGET_CODE = -3U,
     // Invalid uninitialized entry.
-    INVALID_CODE = -3U
+    INVALID_CODE = -4U
   };
 
   union
@@ -1143,6 +1183,9 @@ class Output_reloc<elfcpp::SHT_REL, dyna
     // For a relocation against an output section
     // (this->local_sym_index_ == SECTION_CODE), the output section.
     Output_section* os;
+    // For a target specific relocation, an argument to pass to the
+    // target.
+    void* arg;
   } u1_;
   union
   {
@@ -1157,11 +1200,12 @@ class Output_reloc<elfcpp::SHT_REL, dyna
   // The address offset within the input section or the Output_data.
   Address address_;
   // This is GSYM_CODE for a global symbol, or SECTION_CODE for a
-  // relocation against an output section, or INVALID_CODE for an
-  // uninitialized value.  Otherwise, for a local symbol
-  // (this->is_section_symbol_ is false), the local symbol index.  For
-  // a local section symbol (this->is_section_symbol_ is true), the
-  // section index in the input file.
+  // relocation against an output section, or TARGET_CODE for a target
+  // specific relocation, or INVALID_CODE for an uninitialized value.
+  // Otherwise, for a local symbol (this->is_section_symbol_ is
+  // false), the local symbol index.  For a local section symbol
+  // (this->is_section_symbol_ is true), the section index in the
+  // input file.
   unsigned int local_sym_index_;
   // The reloc type--a processor specific code.
   unsigned int type_ : 30;
@@ -1237,7 +1281,34 @@ class Output_reloc<elfcpp::SHT_RELA, dyn
     : rel_(os, type, relobj, shndx, address), addend_(addend)
   { }
 
-  // Return TRUE if this is a RELATIVE relocation.
+  // An absolute relocation with no symbol.
+
+  Output_reloc(unsigned int type, Output_data* od, Address address,
+	       Addend addend)
+    : rel_(type, od, address), addend_(addend)
+  { }
+
+  Output_reloc(unsigned int type, Sized_relobj<size, big_endian>* relobj,
+	       unsigned int shndx, Address address, Addend addend)
+    : rel_(type, relobj, shndx, address), addend_(addend)
+  { }
+
+  // A target specific relocation.  The target will be called to get
+  // the symbol index and the addend, passing ARG.  The type and
+  // offset will be set as for other relocation types.
+
+  Output_reloc(unsigned int type, void* arg, Output_data* od,
+	       Address address, Addend addend)
+    : rel_(type, arg, od, address), addend_(addend)
+  { }
+
+  Output_reloc(unsigned int type, void* arg,
+	       Sized_relobj<size, big_endian>* relobj,
+	       unsigned int shndx, Address address, Addend addend)
+    : rel_(type, arg, relobj, shndx, address), addend_(addend)
+  { }
+
+  // Return whether this is a RELATIVE relocation.
   bool
   is_relative() const
   { return this->rel_.is_relative(); }
@@ -1519,6 +1590,32 @@ class Output_data_reloc<elfcpp::SHT_REL,
 		     Sized_relobj<size, big_endian>* relobj,
                      unsigned int shndx, Address address)
   { this->add(od, Output_reloc_type(os, type, relobj, shndx, address)); }
+
+  // Add an absolute relocation.
+
+  void
+  add_absolute(unsigned int type, Output_data* od, Address address)
+  { this->add(od, Output_reloc_type(type, od, address)); }
+
+  void
+  add_absolute(unsigned int type, Output_data* od,
+	       Sized_relobj<size, big_endian>* relobj,
+	       unsigned int shndx, Address address)
+  { this->add(od, Output_reloc_type(type, relobj, shndx, address)); }
+
+  // Add a target specific relocation.  A target which calls this must
+  // define the reloc_symbol_index and reloc_addend virtual functions.
+
+  void
+  add_target_specific(unsigned int type, void* arg, Output_data* od,
+		      Address address)
+  { this->add(od, Output_reloc_type(type, arg, od, address)); }
+
+  void
+  add_target_specific(unsigned int type, void* arg, Output_data* od,
+		      Sized_relobj<size, big_endian>* relobj,
+		      unsigned int shndx, Address address)
+  { this->add(od, Output_reloc_type(type, arg, relobj, shndx, address)); }
 };
 
 // The SHT_RELA version of Output_data_reloc.
@@ -1651,6 +1748,36 @@ class Output_data_reloc<elfcpp::SHT_RELA
 		     unsigned int shndx, Address address, Addend addend)
   { this->add(os, Output_reloc_type(os, type, relobj, shndx, address,
                                     addend)); }
+
+  // Add an absolute relocation.
+
+  void
+  add_absolute(unsigned int type, Output_data* od, Address address,
+	       Addend addend)
+  { this->add(od, Output_reloc_type(type, od, address, addend)); }
+
+  void
+  add_absolute(unsigned int type, Output_data* od,
+	       Sized_relobj<size, big_endian>* relobj,
+	       unsigned int shndx, Address address, Addend addend)
+  { this->add(od, Output_reloc_type(type, relobj, shndx, address, addend)); }
+
+  // Add a target specific relocation.  A target which calls this must
+  // define the reloc_symbol_index and reloc_addend virtual functions.
+
+  void
+  add_target_specific(unsigned int type, void* arg, Output_data* od,
+		      Address address, Addend addend)
+  { this->add(od, Output_reloc_type(type, arg, od, address, addend)); }
+
+  void
+  add_target_specific(unsigned int type, void* arg, Output_data* od,
+		      Sized_relobj<size, big_endian>* relobj,
+		      unsigned int shndx, Address address, Addend addend)
+  {
+    this->add(od, Output_reloc_type(type, arg, relobj, shndx, address,
+				    addend));
+  }
 };
 
 // Output_relocatable_relocs represents a relocation section in a
Index: target.h
===================================================================
RCS file: /cvs/src/src/gold/target.h,v
retrieving revision 1.42
diff -p -u -r1.42 target.h
--- target.h	6 Dec 2009 02:49:46 -0000	1.42
+++ target.h	8 Jan 2010 19:15:48 -0000
@@ -230,6 +230,16 @@ class Target
   is_local_label_name(const char* name) const
   { return this->do_is_local_label_name(name); }
 
+  // Get the symbol index to use for a target specific reloc.
+  unsigned int
+  reloc_symbol_index(void* arg, unsigned int type) const
+  { return this->do_reloc_symbol_index(arg, type); }
+
+  // Get the addend to use for a target specific reloc.
+  uint64_t
+  reloc_addend(void* arg, unsigned int type, uint64_t addend) const
+  { return this->do_reloc_addend(arg, type, addend); }
+
   // A function starts at OFFSET in section SHNDX in OBJECT.  That
   // function was compiled with -fsplit-stack, but it refers to a
   // function which was compiled without -fsplit-stack.  VIEW is a
@@ -405,6 +415,18 @@ class Target
   virtual bool
   do_is_local_label_name(const char*) const;
 
+  // Virtual function that must be overridden by a target which uses
+  // target specific relocations.
+  virtual unsigned int
+  do_reloc_symbol_index(void*, unsigned int) const
+  { gold_unreachable(); }
+
+  // Virtual function that must be overidden by a target which uses
+  // target specific relocations.
+  virtual uint64_t
+  do_reloc_addend(void*, unsigned int, uint64_t) const
+  { gold_unreachable(); }
+
   // Virtual function which may be overridden by the child class.
   virtual void
   do_calls_non_split(Relobj* object, unsigned int, section_offset_type,
Index: x86_64.cc
===================================================================
RCS file: /cvs/src/src/gold/x86_64.cc,v
retrieving revision 1.103
diff -p -u -r1.103 x86_64.cc
--- x86_64.cc	7 Jan 2010 20:43:35 -0000	1.103
+++ x86_64.cc	8 Jan 2010 19:15:48 -0000
@@ -65,7 +65,8 @@ class Target_x86_64 : public Target_free
     : Target_freebsd<64, false>(&x86_64_info),
       got_(NULL), plt_(NULL), got_plt_(NULL), global_offset_table_(NULL),
       rela_dyn_(NULL), copy_relocs_(elfcpp::R_X86_64_COPY), dynbss_(NULL),
-      got_mod_index_offset_(-1U), tls_base_symbol_defined_(false)
+      got_mod_index_offset_(-1U), tlsdesc_reloc_info_(),
+      tls_base_symbol_defined_(false)
   { }
 
   // Hook for a new output section.
@@ -161,6 +162,20 @@ class Target_x86_64 : public Target_free
   do_is_defined_by_abi(const Symbol* sym) const
   { return strcmp(sym->name(), "__tls_get_addr") == 0; }
 
+  // Return the symbol index to use for a target specific relocation.
+  // The only target specific relocation is R_X86_64_TLSDESC for a
+  // local symbol, which is an absolute reloc.
+  unsigned int
+  do_reloc_symbol_index(void*, unsigned int r_type) const
+  {
+    gold_assert(r_type == elfcpp::R_X86_64_TLSDESC);
+    return 0;
+  }
+
+  // Return the addend to use for a target specific relocation.
+  uint64_t
+  do_reloc_addend(void* arg, unsigned int r_type, uint64_t addend) const;
+
   // Adjust -fstack-split code which calls non-stack-split code.
   void
   do_calls_non_split(Relobj* object, unsigned int shndx,
@@ -176,6 +191,14 @@ class Target_x86_64 : public Target_free
     return this->got_->data_size();
   }
 
+  // Add a new reloc argument, returning the index in the vector.
+  size_t
+  add_tlsdesc_info(Sized_relobj<64, false>* object, unsigned int r_sym)
+  {
+    this->tlsdesc_reloc_info_.push_back(Tlsdesc_info(object, r_sym));
+    return this->tlsdesc_reloc_info_.size() - 1;
+  }
+
  private:
   // The class which scans relocations.
   class Scan
@@ -379,6 +402,10 @@ class Target_x86_64 : public Target_free
   Reloc_section*
   rela_dyn_section(Layout*);
 
+  // Get the section to use for TLSDESC relocations.
+  Reloc_section*
+  rela_tlsdesc_section(Layout*) const;
+
   // Add a potential copy relocation.
   void
   copy_reloc(Symbol_table* symtab, Layout* layout,
@@ -404,6 +431,21 @@ class Target_x86_64 : public Target_free
     GOT_TYPE_TLS_DESC = 3       // GOT entry for TLS_DESC pair
   };
 
+  // This type is used as the argument to the target specific
+  // relocation routines.  The only target specific reloc is
+  // R_X86_64_TLSDESC against a local symbol.
+  struct Tlsdesc_info
+  {
+    Tlsdesc_info(Sized_relobj<64, false>* a_object, unsigned int a_r_sym)
+      : object(a_object), r_sym(a_r_sym)
+    { }
+
+    // The object in which the local symbol is defined.
+    Sized_relobj<64, false>* object;
+    // The local symbol index in the object.
+    unsigned int r_sym;
+  };
+
   // The GOT section.
   Output_data_got<64, false>* got_;
   // The PLT section.
@@ -420,6 +462,10 @@ class Target_x86_64 : public Target_free
   Output_data_space* dynbss_;
   // Offset of the GOT entry for the TLS module index.
   unsigned int got_mod_index_offset_;
+  // We handle R_X86_64_TLSDESC against a local symbol as a target
+  // specific relocation.  Here we store the object and local symbol
+  // index for the relocation.
+  std::vector<Tlsdesc_info> tlsdesc_reloc_info_;
   // True if the _TLS_MODULE_BASE_ symbol has been defined.
   bool tls_base_symbol_defined_;
 };
@@ -551,11 +597,15 @@ class Output_data_plt_x86_64 : public Ou
   get_tlsdesc_plt_offset() const
   { return (this->count_ + 1) * plt_entry_size; }
 
-  // Return the .rel.plt section data.
+  // Return the .rela.plt section data.
   const Reloc_section*
-  rel_plt() const
+  rela_plt() const
   { return this->rel_; }
 
+  // Return where the TLSDESC relocations should go.
+  Reloc_section*
+  rela_tlsdesc(Layout*);
+
  protected:
   void
   do_adjust_output_section(Output_section* os);
@@ -590,6 +640,9 @@ class Output_data_plt_x86_64 : public Ou
 
   // The reloc section.
   Reloc_section* rel_;
+  // The TLSDESC relocs, if necessary.  These must follow the regular
+  // PLT relocs.
+  Reloc_section* tlsdesc_rel_;
   // The .got section.
   Output_data_got<64, false>* got_;
   // The .got.plt section.
@@ -607,8 +660,8 @@ class Output_data_plt_x86_64 : public Ou
 Output_data_plt_x86_64::Output_data_plt_x86_64(Layout* layout,
                                                Output_data_got<64, false>* got,
                                                Output_data_space* got_plt)
-  : Output_section_data(8), got_(got), got_plt_(got_plt), count_(0),
-    tlsdesc_got_offset_(-1U)
+  : Output_section_data(8), tlsdesc_rel_(NULL), got_(got), got_plt_(got_plt),
+    count_(0), tlsdesc_got_offset_(-1U)
 {
   this->rel_ = new Reloc_section(false);
   layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA,
@@ -652,6 +705,24 @@ Output_data_plt_x86_64::add_entry(Symbol
   // appear in the relocations.
 }
 
+// Return where the TLSDESC relocations should go, creating it if
+// necessary.  These follow the JUMP_SLOT relocations.
+
+Output_data_plt_x86_64::Reloc_section*
+Output_data_plt_x86_64::rela_tlsdesc(Layout* layout)
+{
+  if (this->tlsdesc_rel_ == NULL)
+    {
+      this->tlsdesc_rel_ = new Reloc_section(false);
+      layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA,
+				      elfcpp::SHF_ALLOC, this->tlsdesc_rel_,
+				      true, false, false, false);
+      gold_assert(this->tlsdesc_rel_->output_section() ==
+		  this->rel_->output_section());
+    }
+  return this->tlsdesc_rel_;
+}
+
 // Set the final size.
 void
 Output_data_plt_x86_64::set_final_data_size()
@@ -813,6 +884,14 @@ Target_x86_64::make_plt_section(Symbol_t
     }
 }
 
+// Return the section for TLSDESC relocations.
+
+Target_x86_64::Reloc_section*
+Target_x86_64::rela_tlsdesc_section(Layout* layout) const
+{
+  return this->plt_section()->rela_tlsdesc(layout);
+}
+
 // Create a PLT entry for a global symbol.
 
 void
@@ -1199,18 +1278,21 @@ Target_x86_64::Scan::local(Symbol_table*
                 Output_data_got<64, false>* got
                     = target->got_section(symtab, layout);
                 unsigned int r_sym = elfcpp::elf_r_sym<64>(reloc.get_r_info());
-		unsigned int shndx = lsym.get_st_shndx();
-		bool is_ordinary;
-		shndx = object->adjust_sym_shndx(r_sym, shndx, &is_ordinary);
-		if (!is_ordinary)
-		  object->error(_("local symbol %u has bad shndx %u"),
-			      r_sym, shndx);
-                else
-		  got->add_local_pair_with_rela(object, r_sym,
-						shndx,
-						GOT_TYPE_TLS_DESC,
-						target->rela_dyn_section(layout),
-						elfcpp::R_X86_64_TLSDESC, 0);
+		if (!object->local_has_got_offset(r_sym, GOT_TYPE_TLS_DESC))
+		  {
+		    unsigned int got_offset = got->add_constant(0);
+		    got->add_constant(0);
+		    object->set_local_got_offset(r_sym, GOT_TYPE_TLS_DESC,
+						 got_offset);
+		    Reloc_section* rt = target->rela_tlsdesc_section(layout);
+		    // We store the arguments we need in a vector, and
+		    // use the index into the vector as the parameter
+		    // to pass to the target specific routines.
+		    uintptr_t intarg = target->add_tlsdesc_info(object, r_sym);
+		    void* arg = reinterpret_cast<void*>(intarg);
+		    rt->add_target_specific(elfcpp::R_X86_64_TLSDESC, arg,
+					    got, got_offset, 0);
+		  }
 	      }
 	    else if (optimized_type != tls::TLSOPT_TO_LE)
 	      unsupported_reloc_local(object, r_type);
@@ -1505,8 +1587,8 @@ Target_x86_64::Scan::global(Symbol_table
 	        // Create a double GOT entry with an R_X86_64_TLSDESC reloc.
                 Output_data_got<64, false>* got
                     = target->got_section(symtab, layout);
-                got->add_global_pair_with_rela(gsym, GOT_TYPE_TLS_DESC,
-                                               target->rela_dyn_section(layout),
+		Reloc_section *rt = target->rela_tlsdesc_section(layout);
+                got->add_global_pair_with_rela(gsym, GOT_TYPE_TLS_DESC, rt,
                                                elfcpp::R_X86_64_TLSDESC, 0);
 	      }
 	    else if (optimized_type == tls::TLSOPT_TO_IE)
@@ -1657,7 +1739,7 @@ Target_x86_64::do_finalize_sections(
 {
   const Reloc_section* rel_plt = (this->plt_ == NULL
 				  ? NULL
-				  : this->plt_->rel_plt());
+				  : this->plt_->rela_plt());
   layout->add_target_dynamic_tags(false, this->got_plt_, rel_plt,
 				  this->rela_dyn_, true);
 				  
@@ -2658,6 +2740,25 @@ Target_x86_64::do_code_fill(section_size
   return std::string(nops[length], length);
 }
 
+// Return the addend to use for a target specific relocation.  The
+// only target specific relocation is R_X86_64_TLSDESC for a local
+// symbol.  We want to set the addend is the offset of the local
+// symbol in the TLS segment.
+
+uint64_t
+Target_x86_64::do_reloc_addend(void* arg, unsigned int r_type,
+			       uint64_t) const
+{
+  gold_assert(r_type == elfcpp::R_X86_64_TLSDESC);
+  uintptr_t intarg = reinterpret_cast<uintptr_t>(arg);
+  gold_assert(intarg < this->tlsdesc_reloc_info_.size());
+  const Tlsdesc_info& ti(this->tlsdesc_reloc_info_[intarg]);
+  const Symbol_value<64>* psymval = ti.object->local_symbol(ti.r_sym);
+  gold_assert(psymval->is_tls_symbol());
+  // The value of a TLS symbol is the offset in the TLS segment.
+  return psymval->value(ti.object, 0);
+}
+
 // FNOFFSET in section SHNDX in OBJECT is the start of a function
 // compiled with -fstack-split.  The function calls non-stack-split
 // code.  We have to change the function so that it always ensures

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