PR 10287
authorIan Lance Taylor <ian@airs.com>
Fri, 8 Jan 2010 19:33:18 +0000 (19:33 +0000)
committerIan Lance Taylor <ian@airs.com>
Fri, 8 Jan 2010 19:33:18 +0000 (19:33 +0000)
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.

gold/ChangeLog
gold/i386.cc
gold/layout.cc
gold/output.cc
gold/output.h
gold/target.h
gold/x86_64.cc

index 928af4cd9d47387f25765cec0b68c285753e40ef..038a278abd9e9b45f6e60acb2fbd021c0758c766 100644 (file)
@@ -1,3 +1,60 @@
+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.
+
 2010-01-07  Ian Lance Taylor  <iant@google.com>
 
        PR 11061
index e1b32e7e330da14e9988c1e4bc5a66416c70c9c5..2eab3f86c08c655ff026dccffac334d421d579c6 100644 (file)
@@ -368,7 +368,7 @@ class Target_i386 : public Target_freebsd<32, false>
                      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_freebsd<32, false>
   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 Output_section_data
   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 Output_section_data
 
   // 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 Output_section_data
 
 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* gsym)
   // 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* symtab, Layout* layout, Symbol* gsym)
   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* symtab,
                 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* symtab,
                 // 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 dc7cd13a3cf771264bc563a5c952689df62bcf36..a86590932ec74c88ce5ccacb450a91b083a2a957 100644 (file)
@@ -3215,7 +3215,8 @@ Layout::create_interp(const Target* target)
 // 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_rel, const Output_data* plt_got,
 
   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 b9ded6e07f4cfde6da4524b9067c30578b817dbb..bb61d866da2c6cd8dad9f049e1b3c1930526abf3 100644 (file)
@@ -756,6 +756,72 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
     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, size, big_endian>::get_symbol_index()
        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, size, big_endian>::
 {
   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, size, big_endian>::symbol_value(
       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, size, big_endian>::write(
   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 796575a32505bff376be5951572047a5385b1f1d..7a356526cb61e9232e15a893632eee631b730867 100644 (file)
@@ -1057,7 +1057,30 @@ class Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>
                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, dynamic, size, big_endian>
     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, dynamic, size, big_endian>
     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, dynamic, size, big_endian>
     // 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, dynamic, size, big_endian>
   // 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, dynamic, size, big_endian>
     : 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, dynamic, size, big_endian>
                     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, dynamic, size, big_endian>
                     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 c1c58a4e3eda70f066a4966ea85926f23b1af4da..88cc973e6b7c81f21730858440a9c491625776e1 100644 (file)
@@ -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 f0aade4f49af61de08ca0a8c000a26445b3fa093..585a49995eeda03542ba816b4de9e5a83538e03d 100644 (file)
@@ -65,7 +65,8 @@ class Target_x86_64 : public Target_freebsd<64, false>
     : 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_freebsd<64, false>
   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_freebsd<64, false>
     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_freebsd<64, false>
   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_freebsd<64, false>
     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_freebsd<64, false>
   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 Output_section_data
   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 Output_section_data
 
   // 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 Output_section_data
 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* gsym)
   // 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_table* symtab, Layout* layout)
     }
 }
 
+// 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* symtab,
                 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* symtab,
                // 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_type length) const
   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