]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
From Cary Coutant: Improve i386 shared library TLS support.
authorIan Lance Taylor <iant@google.com>
Wed, 14 Nov 2007 22:31:02 +0000 (22:31 +0000)
committerIan Lance Taylor <iant@google.com>
Wed, 14 Nov 2007 22:31:02 +0000 (22:31 +0000)
gold/i386.cc
gold/layout.cc
gold/layout.h
gold/object.h
gold/output.cc
gold/output.h
gold/symtab.h
gold/target-reloc.h
gold/x86_64.cc

index 7bd5a3a30bd36bb99114a1317165792b8fd0bc82..b4c7e42160208710e1efdaaa946153629701fa08 100644 (file)
@@ -122,6 +122,7 @@ class Target_i386 : public Sized_target<32, false>
          Layout* layout, Target_i386* target,
          Sized_relobj<32, false>* object,
          unsigned int data_shndx,
+         Output_section* output_section,
          const elfcpp::Rel<32, false>& reloc, unsigned int r_type,
          const elfcpp::Sym<32, false>& lsym);
 
@@ -130,6 +131,7 @@ class Target_i386 : public Sized_target<32, false>
           Layout* layout, Target_i386* target,
           Sized_relobj<32, false>* object,
           unsigned int data_shndx,
+          Output_section* output_section,
           const elfcpp::Rel<32, false>& reloc, unsigned int r_type,
           Symbol* gsym);
 
@@ -179,12 +181,21 @@ class Target_i386 : public Sized_target<32, false>
    private:
     // Do a TLS relocation.
     inline void
-    relocate_tls(const Relocate_info<32, false>*, size_t relnum,
-                const elfcpp::Rel<32, false>&,
+    relocate_tls(const Relocate_info<32, false>*, Target_i386* target,
+                 size_t relnum, const elfcpp::Rel<32, false>&,
                 unsigned int r_type, const Sized_symbol<32>*,
                 const Symbol_value<32>*,
                 unsigned char*, elfcpp::Elf_types<32>::Elf_Addr, off_t);
 
+    // Do a TLS General-Dynamic to Initial-Exec transition.
+    inline void
+    tls_gd_to_ie(const Relocate_info<32, false>*, size_t relnum,
+                Output_segment* tls_segment,
+                const elfcpp::Rel<32, false>&, unsigned int r_type,
+                elfcpp::Elf_types<32>::Elf_Addr value,
+                unsigned char* view,
+                off_t view_size);
+
     // Do a TLS General-Dynamic to Local-Exec transition.
     inline void
     tls_gd_to_le(const Relocate_info<32, false>*, size_t relnum,
@@ -776,6 +787,7 @@ Target_i386::Scan::local(const General_options&,
                         Target_i386* target,
                         Sized_relobj<32, false>* object,
                         unsigned int data_shndx,
+                        Output_section* output_section,
                         const elfcpp::Rel<32, false>& reloc,
                         unsigned int r_type,
                         const elfcpp::Sym<32, false>&)
@@ -799,6 +811,8 @@ Target_i386::Scan::local(const General_options&,
           Reloc_section* rel_dyn = target->rel_dyn_section(layout);
           rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE, data_shndx,
                              reloc.get_r_offset());
+          if (!output_section->is_section_flag_set(elfcpp::SHF_WRITE))
+           layout->set_have_textrel();
         }
       break;
 
@@ -815,6 +829,8 @@ Target_i386::Scan::local(const General_options&,
           unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
           rel_dyn->add_local(object, r_sym, r_type, data_shndx,
                              reloc.get_r_offset());
+          if (!output_section->is_section_flag_set(elfcpp::SHF_WRITE))
+           layout->set_have_textrel();
         }
       break;
 
@@ -848,6 +864,8 @@ Target_i386::Scan::local(const General_options&,
                 Reloc_section* rel_dyn = target->rel_dyn_section(layout);
                 rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE,
                                    data_shndx, reloc.get_r_offset());
+                if (!output_section->is_section_flag_set(elfcpp::SHF_WRITE))
+                 layout->set_have_textrel();
               }
           }
       }
@@ -887,18 +905,53 @@ Target_i386::Scan::local(const General_options&,
        switch (r_type)
          {
          case elfcpp::R_386_TLS_GD:          // Global-dynamic
+           if (optimized_type == tls::TLSOPT_NONE)
+             {
+               // Create a pair of GOT entries for the module index and
+               // dtv-relative offset.
+                Output_data_got<32, false>* got
+                    = target->got_section(symtab, layout);
+                unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
+                if (got->add_local_tls(object, r_sym, true))
+                 {
+                    Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+                    unsigned int got_off
+                        = object->local_tls_got_offset(r_sym, true);
+                    rel_dyn->add_local(object, r_sym,
+                                       elfcpp::R_386_TLS_DTPMOD32,
+                                       got, got_off);
+                    rel_dyn->add_local(object, r_sym,
+                                       elfcpp::R_386_TLS_DTPOFF32,
+                                       got, got_off + 4);
+                 }
+             }
+           else if (optimized_type != tls::TLSOPT_TO_LE)
+             unsupported_reloc_local(object, r_type);
+           break;
+
          case elfcpp::R_386_TLS_GOTDESC:     // Global-dynamic (from ~oliva)
          case elfcpp::R_386_TLS_DESC_CALL:
-           // FIXME: If not relaxing to LE, we need to generate
-           // DTPMOD32 and DTPOFF32 relocs.
-           if (optimized_type != tls::TLSOPT_TO_LE)
-             unsupported_reloc_local(object, r_type);
+           unsupported_reloc_local(object, r_type);
            break;
 
          case elfcpp::R_386_TLS_LDM:         // Local-dynamic
-           // FIXME: If not relaxing to LE, we need to generate a
-           // DTPMOD32 reloc.
-           if (optimized_type != tls::TLSOPT_TO_LE)
+           if (optimized_type == tls::TLSOPT_NONE)
+             {
+               // Create a GOT entry for the module index.
+                Output_data_got<32, false>* got
+                    = target->got_section(symtab, layout);
+                unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
+                if (got->add_local_tls(object, r_sym, false))
+                 {
+                    Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+                    unsigned int got_off
+                        = object->local_tls_got_offset(r_sym, false);
+                    rel_dyn->add_local(object, r_sym,
+                                       elfcpp::R_386_TLS_DTPMOD32, got,
+                                       got_off);
+                 }
+             }
+           else if (optimized_type != tls::TLSOPT_TO_LE)
              unsupported_reloc_local(object, r_type);
            break;
 
@@ -908,17 +961,32 @@ Target_i386::Scan::local(const General_options&,
          case elfcpp::R_386_TLS_IE:          // Initial-exec
          case elfcpp::R_386_TLS_IE_32:
          case elfcpp::R_386_TLS_GOTIE:
-           // FIXME: If not relaxing to LE, we need to generate a
-           // TPOFF or TPOFF32 reloc.
-           if (optimized_type != tls::TLSOPT_TO_LE)
+           if (optimized_type == tls::TLSOPT_NONE)
+             {
+               // Create a GOT entry for the tp-relative offset.
+                Output_data_got<32, false>* got
+                    = target->got_section(symtab, layout);
+                unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
+                if (got->add_local(object, r_sym))
+                 {
+                   unsigned int dyn_r_type
+                       = (r_type == elfcpp::R_386_TLS_IE_32
+                          ? elfcpp::R_386_TLS_TPOFF32
+                          : elfcpp::R_386_TLS_TPOFF);
+                    Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+                    unsigned int got_off = object->local_got_offset(r_sym);
+                    rel_dyn->add_local(object, r_sym, dyn_r_type, got,
+                                       got_off);
+                 }
+             }
+           else if (optimized_type != tls::TLSOPT_TO_LE)
              unsupported_reloc_local(object, r_type);
            break;
 
          case elfcpp::R_386_TLS_LE:          // Local-exec
          case elfcpp::R_386_TLS_LE_32:
-           // FIXME: If generating a shared object, we need to copy
-           // this relocation into the object.
-           gold_assert(!output_is_shared);
+           if (output_is_shared)
+             unsupported_reloc_local(object, r_type);
            break;
 
          default:
@@ -963,6 +1031,7 @@ Target_i386::Scan::global(const General_options& options,
                          Target_i386* target,
                          Sized_relobj<32, false>* object,
                          unsigned int data_shndx,
+                          Output_section* output_section,
                          const elfcpp::Rel<32, false>& reloc,
                          unsigned int r_type,
                          Symbol* gsym)
@@ -994,21 +1063,25 @@ Target_i386::Scan::global(const General_options& options,
           {
             if (target->may_need_copy_reloc(gsym))
               {
-               target->copy_reloc(&options, symtab, layout, object, data_shndx,
-                                   gsym, reloc);
+               target->copy_reloc(&options, symtab, layout, object,
+                                  data_shndx, gsym, reloc);
               }
             else if (r_type == elfcpp::R_386_32
                      && gsym->can_use_relative_reloc(false))
               {
                 Reloc_section* rel_dyn = target->rel_dyn_section(layout);
-                rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE, data_shndx,
-                                   reloc.get_r_offset());
+                rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE,
+                                   data_shndx, reloc.get_r_offset());
+                if (!output_section->is_section_flag_set(elfcpp::SHF_WRITE))
+                 layout->set_have_textrel();
               }
             else
               {
                 Reloc_section* rel_dyn = target->rel_dyn_section(layout);
                 rel_dyn->add_global(gsym, r_type, object, data_shndx, 
                                     reloc.get_r_offset());
+                if (!output_section->is_section_flag_set(elfcpp::SHF_WRITE))
+                 layout->set_have_textrel();
               }
           }
       }
@@ -1027,14 +1100,16 @@ Target_i386::Scan::global(const General_options& options,
           {
             if (target->may_need_copy_reloc(gsym))
               {
-               target->copy_reloc(&options, symtab, layout, object, data_shndx,
-                                   gsym, reloc);
+               target->copy_reloc(&options, symtab, layout, object,
+                                  data_shndx, gsym, reloc);
               }
             else
               {
                 Reloc_section* rel_dyn = target->rel_dyn_section(layout);
                 rel_dyn->add_global(gsym, r_type, object, data_shndx, 
                                     reloc.get_r_offset());
+                if (!output_section->is_section_flag_set(elfcpp::SHF_WRITE))
+                 layout->set_have_textrel();
               }
           }
       }
@@ -1122,18 +1197,61 @@ Target_i386::Scan::global(const General_options& options,
        switch (r_type)
          {
          case elfcpp::R_386_TLS_GD:          // Global-dynamic
+           if (optimized_type == tls::TLSOPT_NONE)
+             {
+               // Create a pair of GOT entries for the module index and
+               // dtv-relative offset.
+                Output_data_got<32, false>* got
+                    = target->got_section(symtab, layout);
+                if (got->add_global_tls(gsym, true))
+                 {
+                    Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+                    unsigned int got_off = gsym->tls_got_offset(true);
+                    rel_dyn->add_global(gsym, elfcpp::R_386_TLS_DTPMOD32,
+                                        got, got_off);
+                    rel_dyn->add_global(gsym, elfcpp::R_386_TLS_DTPOFF32,
+                                        got, got_off + 4);
+                 }
+             }
+           else if (optimized_type == tls::TLSOPT_TO_IE)
+             {
+               // Create a GOT entry for the tp-relative offset.
+                Output_data_got<32, false>* got
+                    = target->got_section(symtab, layout);
+                if (got->add_global(gsym))
+                 {
+                    Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+                    unsigned int got_off = gsym->got_offset();
+                    rel_dyn->add_global(gsym, elfcpp::R_386_TLS_TPOFF32,
+                                        got, got_off);
+                 }
+             }
+           else if (optimized_type != tls::TLSOPT_TO_LE)
+             unsupported_reloc_global(object, r_type, gsym);
+           break;
+
          case elfcpp::R_386_TLS_GOTDESC:     // Global-dynamic (~oliva url)
          case elfcpp::R_386_TLS_DESC_CALL:
-           // FIXME: If not relaxing to LE, we need to generate
-           // DTPMOD32 and DTPOFF32 relocs.
-           if (optimized_type != tls::TLSOPT_TO_LE)
-             unsupported_reloc_global(object, r_type, gsym);
+            unsupported_reloc_global(object, r_type, gsym);
            break;
 
          case elfcpp::R_386_TLS_LDM:         // Local-dynamic
            // FIXME: If not relaxing to LE, we need to generate a
            // DTPMOD32 reloc.
-           if (optimized_type != tls::TLSOPT_TO_LE)
+           if (optimized_type == tls::TLSOPT_NONE)
+             {
+               // Create a GOT entry for the module index.
+                Output_data_got<32, false>* got
+                    = target->got_section(symtab, layout);
+                if (got->add_global_tls(gsym, false))
+                 {
+                    Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+                    unsigned int got_off = gsym->tls_got_offset(false);
+                    rel_dyn->add_global(gsym, elfcpp::R_386_TLS_DTPMOD32,
+                                        got, got_off);
+                 }
+             }
+           else if (optimized_type != tls::TLSOPT_TO_LE)
              unsupported_reloc_global(object, r_type, gsym);
            break;
 
@@ -1143,17 +1261,30 @@ Target_i386::Scan::global(const General_options& options,
          case elfcpp::R_386_TLS_IE:          // Initial-exec
          case elfcpp::R_386_TLS_IE_32:
          case elfcpp::R_386_TLS_GOTIE:
-           // FIXME: If not relaxing to LE, we need to generate a
-           // TPOFF or TPOFF32 reloc.
-           if (optimized_type != tls::TLSOPT_TO_LE)
+           if (optimized_type == tls::TLSOPT_NONE)
+             {
+               // Create a GOT entry for the tp-relative offset.
+                Output_data_got<32, false>* got
+                    = target->got_section(symtab, layout);
+                if (got->add_global(gsym))
+                 {
+                   unsigned int dyn_r_type
+                     = (r_type == elfcpp::R_386_TLS_IE_32
+                        ? elfcpp::R_386_TLS_TPOFF32
+                        : elfcpp::R_386_TLS_TPOFF);
+                    Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+                    unsigned int got_off = gsym->got_offset();
+                    rel_dyn->add_global(gsym, dyn_r_type, got, got_off);
+                 }
+             }
+           else if (optimized_type != tls::TLSOPT_TO_LE)
              unsupported_reloc_global(object, r_type, gsym);
            break;
 
          case elfcpp::R_386_TLS_LE:          // Local-exec
          case elfcpp::R_386_TLS_LE_32:
-           // FIXME: If generating a shared object, we need to copy
-           // this relocation into the object.
-           gold_assert(!parameters->output_is_shared());
+           if (parameters->output_is_shared())
+             unsupported_reloc_global(object, r_type, gsym);
            break;
 
          default:
@@ -1353,6 +1484,7 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
       else
         {
           unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info());
+          gold_assert(object->local_has_got_offset(r_sym));
           got_offset = object->local_got_offset(r_sym) - target->got_size();
         }
       have_got_offset = true;
@@ -1468,8 +1600,8 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
     case elfcpp::R_386_TLS_GOTIE:
     case elfcpp::R_386_TLS_LE:             // Local-exec
     case elfcpp::R_386_TLS_LE_32:
-      this->relocate_tls(relinfo, relnum, rel, r_type, gsym, psymval, view,
-                        address, view_size);
+      this->relocate_tls(relinfo, target, relnum, rel, r_type, gsym, psymval,
+                         view, address, view_size);
       break;
 
     case elfcpp::R_386_32PLT:
@@ -1496,6 +1628,7 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
 
 inline void
 Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
+                                    Target_i386* target,
                                    size_t relnum,
                                    const elfcpp::Rel<32, false>& rel,
                                    unsigned int r_type,
@@ -1506,14 +1639,10 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
                                    off_t view_size)
 {
   Output_segment* tls_segment = relinfo->layout->tls_segment();
-  if (tls_segment == NULL)
-    {
-      gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
-                            _("TLS reloc but no TLS segment"));
-      return;
-    }
 
-  elfcpp::Elf_types<32>::Elf_Addr value = psymval->value(relinfo->object, 0);
+  const Sized_relobj<32, false>* object = relinfo->object;
+
+  elfcpp::Elf_types<32>::Elf_Addr value = psymval->value(object, 0);
 
   const bool is_final = (gsym == NULL
                         ? !parameters->output_is_position_independent()
@@ -1525,11 +1654,43 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
     case elfcpp::R_386_TLS_GD:           // Global-dynamic
       if (optimized_type == tls::TLSOPT_TO_LE)
        {
+         gold_assert(tls_segment != NULL);
          this->tls_gd_to_le(relinfo, relnum, tls_segment,
                             rel, r_type, value, view,
                             view_size);
          break;
        }
+      else
+        {
+          unsigned int got_offset;
+          if (gsym != NULL)
+            {
+              gold_assert(gsym->has_tls_got_offset(true));
+              got_offset = gsym->tls_got_offset(true) - target->got_size();
+            }
+          else
+            {
+              unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info());
+              gold_assert(object->local_has_tls_got_offset(r_sym, true));
+              got_offset = (object->local_tls_got_offset(r_sym, true)
+                           - target->got_size());
+            }
+          if (optimized_type == tls::TLSOPT_TO_IE)
+           {
+              gold_assert(tls_segment != NULL);
+             this->tls_gd_to_ie(relinfo, relnum, tls_segment,
+                                 rel, r_type, got_offset, view,
+                                 view_size);
+              break;
+           }
+          else if (optimized_type == tls::TLSOPT_NONE)
+            {
+              // Relocate the field with the offset of the pair of GOT
+              // entries.
+              Relocate_functions<32, false>::rel32(view, got_offset);
+              break;
+            }
+        }
       gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
                             _("unsupported reloc %u"),
                             r_type);
@@ -1553,10 +1714,31 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
       this->local_dynamic_type_ = LOCAL_DYNAMIC_GNU;
       if (optimized_type == tls::TLSOPT_TO_LE)
        {
+          gold_assert(tls_segment != NULL);
          this->tls_ld_to_le(relinfo, relnum, tls_segment, rel, r_type,
                             value, view, view_size);
          break;
        }
+      else if (optimized_type == tls::TLSOPT_NONE)
+        {
+          // Relocate the field with the offset of the GOT entry for
+          // the module index.
+          unsigned int got_offset;
+          if (gsym != NULL)
+            {
+              gold_assert(gsym->has_tls_got_offset(false));
+              got_offset = gsym->tls_got_offset(false) - target->got_size();
+            }
+          else
+            {
+              unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info());
+              gold_assert(object->local_has_tls_got_offset(r_sym, false));
+              got_offset = (object->local_tls_got_offset(r_sym, false)
+                           - target->got_size());
+            }
+          Relocate_functions<32, false>::rel32(view, got_offset);
+          break;
+        }
       gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
                             _("unsupported reloc %u"),
                             r_type);
@@ -1566,6 +1748,7 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
       // This reloc can appear in debugging sections, in which case we
       // won't see the TLS_LDM reloc.  The local_dynamic_type field
       // tells us this.
+      gold_assert(tls_segment != NULL);
       if (optimized_type != tls::TLSOPT_TO_LE
          || this->local_dynamic_type_ == LOCAL_DYNAMIC_NONE)
        value = value - tls_segment->vaddr();
@@ -1581,22 +1764,50 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
     case elfcpp::R_386_TLS_IE_32:
       if (optimized_type == tls::TLSOPT_TO_LE)
        {
+          gold_assert(tls_segment != NULL);
          Target_i386::Relocate::tls_ie_to_le(relinfo, relnum, tls_segment,
                                              rel, r_type, value, view,
                                              view_size);
          break;
        }
+      else if (optimized_type == tls::TLSOPT_NONE)
+        {
+          // Relocate the field with the offset of the GOT entry for
+          // the tp-relative offset of the symbol.
+          unsigned int got_offset;
+          if (gsym != NULL)
+            {
+              gold_assert(gsym->has_got_offset());
+              got_offset = gsym->got_offset();
+            }
+          else
+            {
+              unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info());
+              gold_assert(object->local_has_got_offset(r_sym));
+              got_offset = object->local_got_offset(r_sym);
+            }
+          // For the R_386_TLS_IE relocation, we need to apply the
+          // absolute address of the GOT entry.
+          if (r_type == elfcpp::R_386_TLS_IE)
+            got_offset += target->got_plt_section()->address();
+          // All GOT offsets are relative to the end of the GOT.
+          got_offset -= target->got_size();
+          Relocate_functions<32, false>::rel32(view, got_offset);
+          break;
+        }
       gold_error_at_location(relinfo, relnum, rel.get_r_offset(),
                             _("unsupported reloc %u"),
                             r_type);
       break;
 
     case elfcpp::R_386_TLS_LE:           // Local-exec
+      gold_assert(tls_segment != NULL);
       value = value - (tls_segment->vaddr() + tls_segment->memsz());
       Relocate_functions<32, false>::rel32(view, value);
       break;
 
     case elfcpp::R_386_TLS_LE_32:
+      gold_assert(tls_segment != NULL);
       value = tls_segment->vaddr() + tls_segment->memsz() - value;
       Relocate_functions<32, false>::rel32(view, value);
       break;
@@ -1667,6 +1878,74 @@ Target_i386::Relocate::tls_gd_to_le(const Relocate_info<32, false>* relinfo,
   this->skip_call_tls_get_addr_ = true;
 }
 
+// Do a relocation in which we convert a TLS General-Dynamic to a
+// Initial-Exec.
+
+inline void
+Target_i386::Relocate::tls_gd_to_ie(const Relocate_info<32, false>* relinfo,
+                                   size_t relnum,
+                                   Output_segment* tls_segment,
+                                   const elfcpp::Rel<32, false>& rel,
+                                   unsigned int,
+                                   elfcpp::Elf_types<32>::Elf_Addr value,
+                                   unsigned char* view,
+                                   off_t view_size)
+{
+  // leal foo(,%ebx,1),%eax; call ___tls_get_addr
+  //  ==> movl %gs:0,%eax; addl foo@gotntpoff(%ebx),%eax
+
+  tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, -2);
+  tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, 9);
+
+  unsigned char op1 = view[-1];
+  unsigned char op2 = view[-2];
+
+  tls::check_tls(relinfo, relnum, rel.get_r_offset(),
+                 op2 == 0x8d || op2 == 0x04);
+  tls::check_tls(relinfo, relnum, rel.get_r_offset(), view[4] == 0xe8);
+
+  int roff = 5;
+
+  // FIXME: For now, support only one form.
+  tls::check_tls(relinfo, relnum, rel.get_r_offset(),
+                 op1 == 0x8d && op2 == 0x04);
+
+  if (op2 == 0x04)
+    {
+      tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, -3);
+      tls::check_tls(relinfo, relnum, rel.get_r_offset(), view[-3] == 0x8d);
+      tls::check_tls(relinfo, relnum, rel.get_r_offset(),
+                     ((op1 & 0xc7) == 0x05 && op1 != (4 << 3)));
+      memcpy(view - 3, "\x65\xa1\0\0\0\0\x03\x83\0\0\0", 12);
+    }
+  else
+    {
+      tls::check_tls(relinfo, relnum, rel.get_r_offset(),
+                     (op1 & 0xf8) == 0x80 && (op1 & 7) != 4);
+      if (static_cast<off_t>(rel.get_r_offset() + 9) < view_size
+          && view[9] == 0x90)
+       {
+          // FIXME: This is not the right instruction sequence.
+         // There is a trailing nop.  Use the size byte subl.
+         memcpy(view - 2, "\x65\xa1\0\0\0\0\x81\xe8\0\0\0", 12);
+         roff = 6;
+       }
+      else
+       {
+          // FIXME: This is not the right instruction sequence.
+         // Use the five byte subl.
+         memcpy(view - 2, "\x65\xa1\0\0\0\0\x2d\0\0\0", 11);
+       }
+    }
+
+  value = tls_segment->vaddr() + tls_segment->memsz() - value;
+  Relocate_functions<32, false>::rel32(view + roff, value);
+
+  // The next reloc should be a PLT32 reloc against __tls_get_addr.
+  // We can skip it.
+  this->skip_call_tls_get_addr_ = true;
+}
+
 // Do a relocation in which we convert a TLS Local-Dynamic to a
 // Local-Exec.
 
index fea056d1a31650dcc17e61efe61b10713b9358fc..e4eda44a2d8eeff1dcd3234ffff2aa567f533089 100644 (file)
@@ -70,7 +70,8 @@ Layout::Layout(const General_options& options)
     eh_frame_section_(NULL), output_file_size_(-1),
     input_requires_executable_stack_(false),
     input_with_gnu_stack_note_(false),
-    input_without_gnu_stack_note_(false)
+    input_without_gnu_stack_note_(false),
+    have_textrel_(false)
 {
   // Make space for more than enough segments for a typical file.
   // This is just for efficiency--it's OK if we wind up needing more.
@@ -1582,6 +1583,13 @@ Layout::finish_dynamic_section(const Input_objects* input_objects,
 
       odyn->add_string(elfcpp::DT_RPATH, rpath_val);
     }
+    
+    // Add a DT_FLAGS entry. We add it even if no flags are set so that
+    // post-link tools can easily modify these flags if desired.
+    unsigned int flags = 0;
+    if (this->have_textrel_)
+      flags |= elfcpp::DF_TEXTREL;
+    odyn->add_constant(elfcpp::DT_FLAGS, flags);
 }
 
 // The mapping of .gnu.linkonce section names to real section names.
index 5b9f28defe6bea23055a7ae44f0196f38abb8960..cc07fa3d42963d89bc35d06d3795443edd04b7d5 100644 (file)
@@ -170,6 +170,11 @@ class Layout
   off_t
   finalize(const Input_objects*, Symbol_table*);
 
+  // Record that we have seen a relocation in the text section.
+  void
+  set_have_textrel()
+  { this->have_textrel_ = true; }
+
   // Return the size of the output file.
   off_t
   output_file_size() const
@@ -434,6 +439,8 @@ class Layout
   // Whether we have seen at least one object file without an
   // executable stack marker.
   bool input_without_gnu_stack_note_;
+  // Whether we have seen a relocation in the text section.
+  bool have_textrel_;
 };
 
 // This task handles writing out data in output sections which is not
index 61b40358dde92734f921cce298b14d7a62112c00..6f06fa81fe0ea09ad5ec693544d85decf1872053 100644 (file)
@@ -746,6 +746,7 @@ class Sized_relobj : public Relobj
              Address addend) const;
 
   // Return whether the local symbol SYMNDX has a GOT offset.
+  // For TLS symbols, the GOT entry will hold its tp-relative offset.
   bool
   local_has_got_offset(unsigned int symndx) const
   {
@@ -772,6 +773,63 @@ class Sized_relobj : public Relobj
     gold_assert(ins.second);
   }
 
+  // Return whether the local TLS symbol SYMNDX has a GOT offset.
+  // The GOT entry at this offset will contain a module index. If
+  // NEED_PAIR is true, a second entry immediately following the first
+  // will contain the dtv-relative offset.
+  bool
+  local_has_tls_got_offset(unsigned int symndx, bool need_pair) const
+  {
+    typename Local_tls_got_offsets::const_iterator p =
+        this->local_tls_got_offsets_.find(symndx);
+    if (p == this->local_tls_got_offsets_.end()
+        || (need_pair && !p->second.have_pair_))
+      return false;
+    return true;
+  }
+
+  // Return the offset of the GOT entry for the local TLS symbol SYMNDX.
+  // If NEED_PAIR is true, we need the offset of a pair of GOT entries;
+  // otherwise we need the offset of the GOT entry for the module index.
+  unsigned int
+  local_tls_got_offset(unsigned int symndx, bool need_pair) const
+  {
+    typename Local_tls_got_offsets::const_iterator p =
+        this->local_tls_got_offsets_.find(symndx);
+    gold_assert(p != this->local_tls_got_offsets_.end());
+    gold_assert(!need_pair || p->second.have_pair_);
+    return p->second.got_offset_;
+  }
+
+  // Set the offset of the GOT entry for the local TLS symbol SYMNDX
+  // to GOT_OFFSET. If HAVE_PAIR is true, we have a pair of GOT entries;
+  // otherwise, we have just a single entry for the module index.
+  void
+  set_local_tls_got_offset(unsigned int symndx, unsigned int got_offset,
+                           bool have_pair)
+  {
+    typename Local_tls_got_offsets::iterator p =
+        this->local_tls_got_offsets_.find(symndx);
+    if (p != this->local_tls_got_offsets_.end())
+      {
+        // An entry already existed for this symbol. This can happen
+        // if we see a relocation asking for the module index before
+        // a relocation asking for the pair. In that case, the original
+        // GOT entry will remain, but won't get used by any further
+        // relocations.
+        p->second.got_offset_ = got_offset;
+       gold_assert(have_pair);
+        p->second.have_pair_ = true;
+      }
+    else
+      {
+        std::pair<typename Local_tls_got_offsets::iterator, bool> ins =
+            this->local_tls_got_offsets_.insert(
+              std::make_pair(symndx, Tls_got_entry(got_offset, have_pair)));
+        gold_assert(ins.second);
+      }
+  }
+
   // Return the name of the symbol that spans the given offset in the
   // specified section in this object.  This is used only for error
   // messages and is not particularly efficient.
@@ -901,9 +959,25 @@ class Sized_relobj : public Relobj
   write_local_symbols(Output_file*,
                      const Stringpool_template<char>*);
 
-  // The GOT offsets of local symbols.
+  // The GOT offsets of local symbols. This map also stores GOT offsets
+  // for tp-relative offsets for TLS symbols.
   typedef Unordered_map<unsigned int, unsigned int> Local_got_offsets;
 
+  // The TLS GOT offsets of local symbols. The map stores the offsets
+  // for either a single GOT entry that holds the module index of a TLS
+  // symbol, or a pair of GOT entries containing the module index and
+  // dtv-relative offset.
+  struct Tls_got_entry
+  {
+    Tls_got_entry(int got_offset, bool have_pair)
+      : got_offset_(got_offset),
+        have_pair_(have_pair)
+    { }
+    int got_offset_;
+    bool have_pair_;
+  };
+  typedef Unordered_map<unsigned int, Tls_got_entry> Local_tls_got_offsets;
+
   // General access to the ELF file.
   elfcpp::Elf_file<size, big_endian, Object> elf_file_;
   // Index of SHT_SYMTAB section.
@@ -918,8 +992,12 @@ class Sized_relobj : public Relobj
   off_t local_symbol_offset_;
   // Values of local symbols.
   Local_values local_values_;
-  // GOT offsets for local symbols, indexed by symbol number.
+  // GOT offsets for local non-TLS symbols, and tp-relative offsets
+  // for TLS symbols, indexed by symbol number.
   Local_got_offsets local_got_offsets_;
+  // GOT offsets for local TLS symbols, indexed by symbol number
+  // and GOT entry type.
+  Local_tls_got_offsets local_tls_got_offsets_;
   // Whether this object has a GNU style .eh_frame section.
   bool has_eh_frame_;
 };
index b1917257f2bdc8ffed4acbd1f606217737fcc6d3..ef5c5097c6f7d6cbad365474262358be3a6c8b2d 100644 (file)
@@ -743,12 +743,59 @@ Output_data_got<size, big_endian>::add_local(
 {
   if (object->local_has_got_offset(symndx))
     return false;
+
   this->entries_.push_back(Got_entry(object, symndx));
   this->set_got_size();
   object->set_local_got_offset(symndx, this->last_got_offset());
   return true;
 }
 
+// Add an entry (or a pair of entries) for a global TLS symbol to the GOT.
+// In a pair of entries, the first value in the pair will be used for the
+// module index, and the second value will be used for the dtv-relative
+// offset. This returns true if this is a new GOT entry, false if the symbol
+// already has a GOT entry.
+
+template<int size, bool big_endian>
+bool
+Output_data_got<size, big_endian>::add_global_tls(Symbol* gsym,
+                                                 bool need_pair)
+{
+  if (gsym->has_tls_got_offset(need_pair))
+    return false;
+
+  this->entries_.push_back(Got_entry(gsym));
+  gsym->set_tls_got_offset(this->last_got_offset(), need_pair);
+  if (need_pair)
+    this->entries_.push_back(Got_entry(gsym));
+  this->set_got_size();
+  return true;
+}
+
+// Add an entry (or a pair of entries) for a local TLS symbol to the GOT.
+// In a pair of entries, the first value in the pair will be used for the
+// module index, and the second value will be used for the dtv-relative
+// offset. This returns true if this is a new GOT entry, false if the symbol
+// already has a GOT entry.
+
+template<int size, bool big_endian>
+bool
+Output_data_got<size, big_endian>::add_local_tls(
+    Sized_relobj<size, big_endian>* object,
+    unsigned int symndx,
+    bool need_pair)
+{
+  if (object->local_has_tls_got_offset(symndx, need_pair))
+    return false;
+
+  this->entries_.push_back(Got_entry(object, symndx));
+  object->set_local_tls_got_offset(symndx, this->last_got_offset(), need_pair);
+  if (need_pair)
+    this->entries_.push_back(Got_entry(object, symndx));
+  this->set_got_size();
+  return true;
+}
+
 // Write out the GOT.
 
 template<int size, bool big_endian>
@@ -1432,8 +1479,12 @@ Output_segment::add_output_section(Output_section* os,
   // SHF_TLS sections.  An SHF_TLS/SHT_NOBITS section is a special
   // case: we group the SHF_TLS/SHT_NOBITS sections right after the
   // SHF_TLS/SHT_PROGBITS sections.  This lets us set up PT_TLS
-  // correctly.
-  if ((os->flags() & elfcpp::SHF_TLS) != 0 && !this->output_data_.empty())
+  // correctly.  SHF_TLS sections get added to both a PT_LOAD segment
+  // and the PT_TLS segment -- we do this grouping only for the
+  // PT_LOAD segment.
+  if (this->type_ != elfcpp::PT_TLS
+      && (os->flags() & elfcpp::SHF_TLS) != 0
+      && !this->output_data_.empty())
     {
       pdl = &this->output_data_;
       bool nobits = os->type() == elfcpp::SHT_NOBITS;
index 053579f9364f1f73da3ba7a503384ad0a24a9fc4..e90077ac2a1bf7c18f7bc990093872504bee5921 100644 (file)
@@ -920,6 +920,19 @@ class Output_data_got : public Output_section_data
   bool
   add_local(Sized_relobj<size, big_endian>* object, unsigned int sym_index);
 
+  // Add an entry (or pair of entries) for a global TLS symbol to the GOT.
+  // Return true if this is a new GOT entry, false if the symbol was
+  // already in the GOT.
+  bool
+  add_global_tls(Symbol* gsym, bool need_pair);
+
+  // Add an entry (or pair of entries) for a local TLS symbol to the GOT.
+  // This returns true if this is a new GOT entry, false if the symbol
+  // already has a GOT entry.
+  bool
+  add_local_tls(Sized_relobj<size, big_endian>* object,
+               unsigned int sym_index, bool need_pair);
+
   // Add a constant to the GOT.  This returns the offset of the new
   // entry from the start of the GOT.
   unsigned int
index 3d3adbcc5a68581f2adfe8225287977f98a74d6c..8206cbaa33a39874dd2e131cf686036476b89cb7 100644 (file)
@@ -299,6 +299,7 @@ class Symbol
   { return this->dynsym_index_ != 0; }
 
   // Return whether this symbol has an entry in the GOT section.
+  // For a TLS symbol, this GOT entry will hold its tp-relative offset.
   bool
   has_got_offset() const
   { return this->has_got_offset_; }
@@ -319,6 +320,35 @@ class Symbol
     this->got_offset_ = got_offset;
   }
 
+  // Return whether this TLS symbol has an entry in the GOT section for
+  // its module index or, if NEED_PAIR is true, has a pair of entries
+  // for its module index and dtv-relative offset.
+  bool
+  has_tls_got_offset(bool need_pair) const
+  {
+    return (this->has_tls_mod_got_offset_
+            && (!need_pair || this->has_tls_pair_got_offset_));
+  }
+
+  // Return the offset into the GOT section for this symbol's TLS module
+  // index or, if NEED_PAIR is true, for the pair of entries for the
+  // module index and dtv-relative offset.
+  unsigned int
+  tls_got_offset(bool need_pair) const
+  {
+    gold_assert(this->has_tls_got_offset(need_pair));
+    return this->tls_mod_got_offset_;
+  }
+
+  // Set the GOT offset of this symbol.
+  void
+  set_tls_got_offset(unsigned int got_offset, bool have_pair)
+  {
+    this->has_tls_mod_got_offset_ = true;
+    this->has_tls_pair_got_offset_ = have_pair;
+    this->tls_mod_got_offset_ = got_offset;
+  }
+
   // Return whether this symbol has an entry in the PLT section.
   bool
   has_plt_offset() const
@@ -620,8 +650,18 @@ class Symbol
 
   // If this symbol has an entry in the GOT section (has_got_offset_
   // is true), this is the offset from the start of the GOT section.
+  // For a TLS symbol, if has_tls_tpoff_got_offset_ is true, this
+  // serves as the GOT offset for the GOT entry that holds its
+  // TP-relative offset.
   unsigned int got_offset_;
 
+  // If this is a TLS symbol and has an entry in the GOT section
+  // for a module index or a pair of entries (module index,
+  // dtv-relative offset), these are the offsets from the start
+  // of the GOT section.
+  unsigned int tls_mod_got_offset_;
+  unsigned int tls_pair_got_offset_;
+
   // If this symbol has an entry in the PLT section (has_plt_offset_
   // is true), then this is the offset from the start of the PLT
   // section.
@@ -660,7 +700,14 @@ class Symbol
   // True if we've seen this symbol in a dynamic object.
   bool in_dyn_ : 1;
   // True if the symbol has an entry in the GOT section.
+  // For a TLS symbol, this GOT entry will hold its tp-relative offset.
   bool has_got_offset_ : 1;
+  // True if the symbol has an entry in the GOT section for its
+  // module index.
+  bool has_tls_mod_got_offset_ : 1;
+  // True if the symbol has a pair of entries in the GOT section for its
+  // module index and dtv-relative offset.
+  bool has_tls_pair_got_offset_ : 1;
   // True if the symbol has an entry in the PLT section.
   bool has_plt_offset_ : 1;
   // True if this is a dynamic symbol which needs a special value in
index 0498e9a3385fca4c9fd56d07fe15f715779a7f9f..7c1d327eace01044aea0c98be0d9d9b32b205c39 100644 (file)
@@ -100,7 +100,7 @@ scan_relocs(
            }
 
          scan.local(options, symtab, layout, target, object, data_shndx,
-                    reloc, r_type, lsym);
+                    output_section, reloc, r_type, lsym);
        }
       else
        {
@@ -110,7 +110,7 @@ scan_relocs(
            gsym = symtab->resolve_forwards(gsym);
 
          scan.global(options, symtab, layout, target, object, data_shndx,
-                     reloc, r_type, gsym);
+                     output_section, reloc, r_type, gsym);
        }
     }
 }
index 349d8c1407f02dec52a3ab21c29eecfd3db6ee62..5078237385ea062aa5e2b62b50c1253f0d4a3ec9 100644 (file)
@@ -135,6 +135,7 @@ class Target_x86_64 : public Sized_target<64, false>
          Layout* layout, Target_x86_64* target,
          Sized_relobj<64, false>* object,
          unsigned int data_shndx,
+         Output_section* output_section,
          const elfcpp::Rela<64, false>& reloc, unsigned int r_type,
          const elfcpp::Sym<64, false>& lsym);
 
@@ -143,6 +144,7 @@ class Target_x86_64 : public Sized_target<64, false>
           Layout* layout, Target_x86_64* target,
           Sized_relobj<64, false>* object,
           unsigned int data_shndx,
+          Output_section* output_section,
           const elfcpp::Rela<64, false>& reloc, unsigned int r_type,
           Symbol* gsym);
 
@@ -738,6 +740,7 @@ Target_x86_64::Scan::local(const General_options&,
                            Target_x86_64* target,
                            Sized_relobj<64, false>* object,
                            unsigned int data_shndx,
+                           Output_section*,
                            const elfcpp::Rela<64, false>& reloc,
                            unsigned int r_type,
                            const elfcpp::Sym<64, false>&)
@@ -927,6 +930,7 @@ Target_x86_64::Scan::global(const General_options& options,
                             Target_x86_64* target,
                             Sized_relobj<64, false>* object,
                             unsigned int data_shndx,
+                            Output_section*,
                             const elfcpp::Rela<64, false>& reloc,
                             unsigned int r_type,
                             Symbol* gsym)