OSDN Git Service

From Cary Coutant: More support for generating shared libraries.
authorIan Lance Taylor <iant@google.com>
Thu, 6 Dec 2007 05:55:50 +0000 (05:55 +0000)
committerIan Lance Taylor <iant@google.com>
Thu, 6 Dec 2007 05:55:50 +0000 (05:55 +0000)
12 files changed:
gold/i386.cc
gold/layout.cc
gold/layout.h
gold/object.cc
gold/object.h
gold/output.cc
gold/output.h
gold/reloc.cc
gold/symtab.cc
gold/testsuite/Makefile.am
gold/testsuite/Makefile.in
gold/x86_64.cc

index 1bfc659..071940b 100644 (file)
@@ -792,7 +792,7 @@ Target_i386::Scan::local(const General_options&,
                         Output_section* output_section,
                         const elfcpp::Rel<32, false>& reloc,
                         unsigned int r_type,
-                        const elfcpp::Sym<32, false>&)
+                        const elfcpp::Sym<32, false>& lsym)
 {
   switch (r_type)
     {
@@ -856,13 +856,12 @@ Target_i386::Scan::local(const General_options&,
         if (got->add_local(object, r_sym))
           {
             // If we are generating a shared object, we need to add a
-            // dynamic RELATIVE relocation for this symbol.
+            // dynamic RELATIVE relocation for this symbol's GOT entry.
             if (parameters->output_is_position_independent())
               {
                 Reloc_section* rel_dyn = target->rel_dyn_section(layout);
                 rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE,
-                                   output_section, data_shndx,
-                                   reloc.get_r_offset());
+                                   got, object->local_got_offset(r_sym));
               }
           }
       }
@@ -909,18 +908,10 @@ Target_i386::Scan::local(const General_options&,
                 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);
-                 }
+                got->add_local_tls_with_rel(object, r_sym, 
+                                            lsym.get_st_shndx(), true,
+                                            target->rel_dyn_section(layout),
+                                            elfcpp::R_386_TLS_DTPMOD32);
              }
            else if (optimized_type != tls::TLSOPT_TO_LE)
              unsupported_reloc_local(object, r_type);
@@ -928,7 +919,10 @@ Target_i386::Scan::local(const General_options&,
 
          case elfcpp::R_386_TLS_GOTDESC:     // Global-dynamic (from ~oliva)
          case elfcpp::R_386_TLS_DESC_CALL:
-           unsupported_reloc_local(object, r_type);
+            // FIXME: If not relaxing to LE, we need to generate
+            // a GOT entry with an R_386_TLS_DESC reloc.
+            if (optimized_type != tls::TLSOPT_TO_LE)
+              unsupported_reloc_local(object, r_type);
            break;
 
          case elfcpp::R_386_TLS_LDM:         // Local-dynamic
@@ -938,15 +932,10 @@ Target_i386::Scan::local(const General_options&,
                 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);
-                 }
+                got->add_local_tls_with_rel(object, r_sym,
+                                            lsym.get_st_shndx(), false,
+                                            target->rel_dyn_section(layout),
+                                            elfcpp::R_386_TLS_DTPMOD32);
              }
            else if (optimized_type != tls::TLSOPT_TO_LE)
              unsupported_reloc_local(object, r_type);
@@ -960,21 +949,26 @@ Target_i386::Scan::local(const General_options&,
          case elfcpp::R_386_TLS_GOTIE:
            if (optimized_type == tls::TLSOPT_NONE)
              {
+               // For the R_386_TLS_IE relocation, we need to create a
+               // dynamic relocation when building a shared library.
+               if (r_type == elfcpp::R_386_TLS_IE
+                   && parameters->output_is_shared())
+                 {
+                    Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+                    rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE,
+                                       output_section, data_shndx,
+                                       reloc.get_r_offset());
+                 }
                // 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);
-                 }
+               unsigned int dyn_r_type = (r_type == elfcpp::R_386_TLS_IE_32
+                                          ? elfcpp::R_386_TLS_TPOFF32
+                                          : elfcpp::R_386_TLS_TPOFF);
+                got->add_local_with_rel(object, r_sym,
+                                        target->rel_dyn_section(layout),
+                                        dyn_r_type);
              }
            else if (optimized_type != tls::TLSOPT_TO_LE)
              unsupported_reloc_local(object, r_type);
@@ -983,7 +977,16 @@ Target_i386::Scan::local(const General_options&,
          case elfcpp::R_386_TLS_LE:          // Local-exec
          case elfcpp::R_386_TLS_LE_32:
            if (output_is_shared)
-             unsupported_reloc_local(object, r_type);
+             {
+               // We need to create a dynamic relocation.
+                unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
+                unsigned int dyn_r_type = (r_type == elfcpp::R_386_TLS_LE_32
+                                           ? elfcpp::R_386_TLS_TPOFF32
+                                           : elfcpp::R_386_TLS_TPOFF);
+                Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+                rel_dyn->add_local(object, r_sym, dyn_r_type, output_section,
+                                   data_shndx, reloc.get_r_offset());
+             }
            break;
 
          default:
@@ -1087,7 +1090,17 @@ Target_i386::Scan::global(const General_options& options,
       {
         // Make a PLT entry if necessary.
         if (gsym->needs_plt_entry())
-          target->make_plt_entry(symtab, layout, gsym);
+          {
+            // These relocations are used for function calls only in
+            // non-PIC code.  For a 32-bit relocation in a shared library,
+            // we'll need a text relocation anyway, so we can skip the
+            // PLT entry and let the dynamic linker bind the call directly
+            // to the target.  For smaller relocations, we should use a
+            // PLT entry to ensure that the call can reach.
+            if (!parameters->output_is_shared()
+                || r_type != elfcpp::R_386_PC32)
+              target->make_plt_entry(symtab, layout, gsym);
+          }
         // Make a dynamic relocation if necessary.
         bool is_function_call = (gsym->type() == elfcpp::STT_FUNC);
         if (gsym->needs_dynamic_reloc(false, is_function_call))
@@ -1111,18 +1124,18 @@ Target_i386::Scan::global(const General_options& options,
       {
         // The symbol requires a GOT entry.
         Output_data_got<32, false>* got = target->got_section(symtab, layout);
-        if (got->add_global(gsym))
-         {
+        if (gsym->final_value_is_known())
+          got->add_global(gsym);
+        else
+          {
             // If this symbol is not fully resolved, we need to add a
-            // dynamic relocation for it.
-            if (!gsym->final_value_is_known())
+            // GOT entry with a dynamic relocation.
+            Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+            if (gsym->is_from_dynobj() || gsym->is_preemptible())
+              got->add_global_with_rel(gsym, rel_dyn, elfcpp::R_386_GLOB_DAT);
+            else
               {
-                Reloc_section* rel_dyn = target->rel_dyn_section(layout);
-                if (gsym->is_from_dynobj()
-                   || gsym->is_preemptible())
-                 rel_dyn->add_global(gsym, elfcpp::R_386_GLOB_DAT, got,
-                                     gsym->got_offset());
-                else
+                if (got->add_global(gsym))
                   {
                     rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE,
                                        got, gsym->got_offset());
@@ -1195,28 +1208,18 @@ Target_i386::Scan::global(const General_options& options,
                // 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);
-                 }
+                got->add_global_tls_with_rel(gsym,
+                                             target->rel_dyn_section(layout),
+                                             elfcpp::R_386_TLS_DTPMOD32,
+                                             elfcpp::R_386_TLS_DTPOFF32);
              }
            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);
-                 }
+                got->add_global_with_rel(gsym, target->rel_dyn_section(layout),
+                                         elfcpp::R_386_TLS_TPOFF32);
              }
            else if (optimized_type != tls::TLSOPT_TO_LE)
              unsupported_reloc_global(object, r_type, gsym);
@@ -1224,6 +1227,10 @@ Target_i386::Scan::global(const General_options& options,
 
          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
+            // a GOT entry with an R_386_TLS_DESC reloc.
+            if (optimized_type != tls::TLSOPT_TO_LE)
+              unsupported_reloc_global(object, r_type, gsym);
             unsupported_reloc_global(object, r_type, gsym);
            break;
 
@@ -1235,13 +1242,9 @@ Target_i386::Scan::global(const General_options& options,
                // 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);
-                 }
+                got->add_global_tls_with_rel(gsym,
+                                             target->rel_dyn_section(layout),
+                                             elfcpp::R_386_TLS_DTPMOD32);
              }
            else if (optimized_type != tls::TLSOPT_TO_LE)
              unsupported_reloc_global(object, r_type, gsym);
@@ -1255,19 +1258,25 @@ Target_i386::Scan::global(const General_options& options,
          case elfcpp::R_386_TLS_GOTIE:
            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))
+               // For the R_386_TLS_IE relocation, we need to create a
+               // dynamic relocation when building a shared library.
+               if (r_type == elfcpp::R_386_TLS_IE
+                   && parameters->output_is_shared())
                  {
-                   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);
+                    rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE,
+                                       output_section, data_shndx,
+                                       reloc.get_r_offset());
                  }
+               // Create a GOT entry for the tp-relative offset.
+                Output_data_got<32, false>* got
+                    = target->got_section(symtab, layout);
+               unsigned int dyn_r_type = (r_type == elfcpp::R_386_TLS_IE_32
+                                          ? elfcpp::R_386_TLS_TPOFF32
+                                          : elfcpp::R_386_TLS_TPOFF);
+                got->add_global_with_rel(gsym,
+                                         target->rel_dyn_section(layout),
+                                         dyn_r_type);
              }
            else if (optimized_type != tls::TLSOPT_TO_LE)
              unsupported_reloc_global(object, r_type, gsym);
@@ -1276,7 +1285,15 @@ Target_i386::Scan::global(const General_options& options,
          case elfcpp::R_386_TLS_LE:          // Local-exec
          case elfcpp::R_386_TLS_LE_32:
            if (parameters->output_is_shared())
-             unsupported_reloc_global(object, r_type, gsym);
+             {
+               // We need to create a dynamic relocation.
+                unsigned int dyn_r_type = (r_type == elfcpp::R_386_TLS_LE_32
+                                           ? elfcpp::R_386_TLS_TPOFF32
+                                           : elfcpp::R_386_TLS_TPOFF);
+                Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+                rel_dyn->add_global(gsym, dyn_r_type, output_section, object,
+                                    data_shndx, reloc.get_r_offset());
+             }
            break;
 
          default:
@@ -1670,9 +1687,8 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
           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);
+             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)
@@ -1741,13 +1757,11 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
       // 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();
-      else if (this->local_dynamic_type_ == LOCAL_DYNAMIC_GNU)
-       value = value - (tls_segment->vaddr() + tls_segment->memsz());
-      else
-       value = tls_segment->vaddr() + tls_segment->memsz() - value;
+      if (this->local_dynamic_type_ == LOCAL_DYNAMIC_GNU)
+       value -= tls_segment->memsz();
+      else if (optimized_type == tls::TLSOPT_TO_LE
+              && this->local_dynamic_type_ != LOCAL_DYNAMIC_NONE)
+       value = tls_segment->memsz() - value;
       Relocate_functions<32, false>::rel32(view, value);
       break;
 
@@ -1793,15 +1807,25 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
       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);
+      // If we're creating a shared library, a dynamic relocation will
+      // have been created for this location, so do not apply it now.
+      if (!parameters->output_is_shared())
+        {
+          gold_assert(tls_segment != NULL);
+          value -= 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);
+      // If we're creating a shared library, a dynamic relocation will
+      // have been created for this location, so do not apply it now.
+      if (!parameters->output_is_shared())
+        {
+          gold_assert(tls_segment != NULL);
+          value = tls_segment->memsz() - value;
+          Relocate_functions<32, false>::rel32(view, value);
+        }
       break;
     }
 }
@@ -1862,7 +1886,7 @@ Target_i386::Relocate::tls_gd_to_le(const Relocate_info<32, false>* relinfo,
        }
     }
 
-  value = tls_segment->vaddr() + tls_segment->memsz() - value;
+  value = tls_segment->memsz() - value;
   Relocate_functions<32, false>::rel32(view + roff, value);
 
   // The next reloc should be a PLT32 reloc against __tls_get_addr.
@@ -1870,7 +1894,7 @@ 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
+// Do a relocation in which we convert a TLS General-Dynamic to an
 // Initial-Exec.
 
 inline void
@@ -1930,7 +1954,7 @@ Target_i386::Relocate::tls_gd_to_ie(const Relocate_info<32, false>* relinfo,
        }
     }
 
-  value = tls_segment->vaddr() + tls_segment->memsz() - value;
+  value = tls_segment->memsz() - value;
   Relocate_functions<32, false>::rel32(view + roff, value);
 
   // The next reloc should be a PLT32 reloc against __tls_get_addr.
@@ -2059,7 +2083,7 @@ Target_i386::Relocate::tls_ie_to_le(const Relocate_info<32, false>* relinfo,
        tls::check_tls(relinfo, relnum, rel.get_r_offset(), 0);
     }
 
-  value = tls_segment->vaddr() + tls_segment->memsz() - value;
+  value = tls_segment->memsz() - value;
   if (r_type == elfcpp::R_386_TLS_IE || r_type == elfcpp::R_386_TLS_GOTIE)
     value = - value;
 
index 1139cf1..68ebc6b 100644 (file)
@@ -658,6 +658,8 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab)
 
   target->finalize_sections(this);
 
+  this->count_local_symbols(input_objects);
+
   this->create_gold_note();
   this->create_executable_stack_info(target);
 
@@ -677,7 +679,7 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab)
       std::vector<Symbol*> dynamic_symbols;
       unsigned int local_dynamic_count;
       Versions versions;
-      this->create_dynamic_symtab(target, symtab, &dynstr,
+      this->create_dynamic_symtab(input_objects, target, symtab, &dynstr,
                                  &local_dynamic_count, &dynamic_symbols,
                                  &versions);
 
@@ -728,6 +730,8 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab)
 
   // Create the symbol table sections.
   this->create_symtab_sections(input_objects, symtab, &off);
+  if (!parameters->doing_static_link())
+    this->assign_local_dynsym_offsets(input_objects);
 
   // Create the .shstrtab section.
   Output_section* shstrtab_section = this->create_shstrtab();
@@ -1076,6 +1080,10 @@ Layout::set_segment_offsets(const Target* target, Output_segment* load_seg,
        (*p)->set_offset();
     }
 
+  // Set the TLS offsets for each section in the PT_TLS segment.
+  if (this->tls_segment_ != NULL)
+    this->tls_segment_->set_tls_offsets();
+
   return off;
 }
 
@@ -1137,6 +1145,21 @@ Layout::set_section_indexes(unsigned int shndx)
   return shndx;
 }
 
+// Count the local symbols in the regular symbol table and the dynamic
+// symbol table, and build the respective string pools.
+
+void
+Layout::count_local_symbols(const Input_objects* input_objects)
+{
+  for (Input_objects::Relobj_iterator p = input_objects->relobj_begin();
+       p != input_objects->relobj_end();
+       ++p)
+    {
+      Task_lock_obj<Object> tlo(**p);
+      (*p)->count_local_symbols(&this->sympool_, &this->dynpool_);
+    }
+}
+
 // Create the symbol table sections.  Here we also set the final
 // values of the symbols.  At this point all the loadable sections are
 // fully laid out.
@@ -1189,10 +1212,8 @@ Layout::create_symtab_sections(const Input_objects* input_objects,
        p != input_objects->relobj_end();
        ++p)
     {
-      Task_lock_obj<Object> tlo(**p);
       unsigned int index = (*p)->finalize_local_symbols(local_symbol_index,
-                                                       off,
-                                                       &this->sympool_);
+                                                        off);
       off += (index - local_symbol_index) * symsize;
       local_symbol_index = index;
     }
@@ -1300,7 +1321,8 @@ Layout::create_shdrs(off_t* poff)
 // Create the dynamic symbol table.
 
 void
-Layout::create_dynamic_symtab(const Target* target, Symbol_table* symtab,
+Layout::create_dynamic_symtab(const Input_objects* input_objects,
+                              const Target* target, Symbol_table* symtab,
                              Output_section **pdynstr,
                              unsigned int* plocal_dynamic_count,
                              std::vector<Symbol*>* pdynamic_symbols,
@@ -1326,10 +1348,15 @@ Layout::create_dynamic_symtab(const Target* target, Symbol_table* symtab,
        }
     }
 
-  // FIXME: Some targets apparently require local symbols in the
-  // dynamic symbol table.  Here is where we will have to count them,
-  // and set the dynamic symbol indexes, and add the names to
-  // this->dynpool_.
+  // Count the local symbols that need to go in the dynamic symbol table,
+  // and set the dynamic symbol indexes.
+  for (Input_objects::Relobj_iterator p = input_objects->relobj_begin();
+       p != input_objects->relobj_end();
+       ++p)
+    {
+      unsigned int new_index = (*p)->set_local_dynsym_indexes(index);
+      index = new_index;
+    }
 
   unsigned int local_symcount = index;
   *plocal_dynamic_count = local_symcount;
@@ -1419,6 +1446,28 @@ Layout::create_dynamic_symtab(const Target* target, Symbol_table* symtab,
   odyn->add_section_address(elfcpp::DT_HASH, hashsec);
 }
 
+// Assign offsets to each local portion of the dynamic symbol table.
+
+void
+Layout::assign_local_dynsym_offsets(const Input_objects* input_objects)
+{
+  Output_section* dynsym = this->dynsym_section_;
+  gold_assert(dynsym != NULL);
+
+  off_t off = dynsym->offset();
+
+  // Skip the dummy symbol at the start of the section.
+  off += dynsym->entsize();
+
+  for (Input_objects::Relobj_iterator p = input_objects->relobj_begin();
+       p != input_objects->relobj_end();
+       ++p)
+    {
+      unsigned int count = (*p)->set_local_dynsym_offset(off);
+      off += count * dynsym->entsize();
+    }
+}
+
 // Create the version sections.
 
 void
index a790892..12f703f 100644 (file)
@@ -270,6 +270,11 @@ class Layout
   Output_segment*
   find_first_load_seg();
 
+  // Count the local symbols in the regular symbol table and the dynamic
+  // symbol table, and build the respective string pools.
+  void
+  count_local_symbols(const Input_objects*);
+
   // Create the output sections for the symbol table.
   void
   create_symtab_sections(const Input_objects*, Symbol_table*, off_t*);
@@ -284,11 +289,16 @@ class Layout
 
   // Create the dynamic symbol table.
   void
-  create_dynamic_symtab(const Target*, Symbol_table*, Output_section** pdynstr,
+  create_dynamic_symtab(const Input_objects*, const Target*,
+                        Symbol_table*, Output_section** pdynstr,
                        unsigned int* plocal_dynamic_count,
                        std::vector<Symbol*>* pdynamic_symbols,
                        Versions* versions);
 
+  // Assign offsets to each local portion of the dynamic symbol table.
+  void
+  assign_local_dynsym_offsets(const Input_objects*);
+
   // Finish the .dynamic section and PT_DYNAMIC segment.
   void
   finish_dynamic_section(const Input_objects*, const Symbol_table*);
index fee249f..ab1323a 100644 (file)
@@ -142,8 +142,10 @@ Sized_relobj<size, big_endian>::Sized_relobj(
     symtab_shndx_(-1U),
     local_symbol_count_(0),
     output_local_symbol_count_(0),
+    output_local_dynsym_count_(0),
     symbols_(),
     local_symbol_offset_(0),
+    local_dynsym_offset_(0),
     local_values_(),
     local_got_offsets_(),
     has_eh_frame_(false)
@@ -290,6 +292,7 @@ Sized_relobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
   const int sym_size = This::sym_size;
   const unsigned int loccount = symtabshdr.get_sh_info();
   this->local_symbol_count_ = loccount;
+  this->local_values_.resize(loccount);
   off_t locsize = loccount * sym_size;
   off_t dataoff = symtabshdr.get_sh_offset();
   off_t datasize = symtabshdr.get_sh_size();
@@ -722,29 +725,23 @@ Sized_relobj<size, big_endian>::do_add_symbols(Symbol_table* symtab,
   sd->symbol_names = NULL;
 }
 
-// Finalize the local symbols.  Here we record the file offset at
-// which they should be output, we add their names to *POOL, and we
-// add their values to THIS->LOCAL_VALUES_.  Return the symbol index.
+// Finalize the local symbols.  Here we add their names to *POOL and
+// *DYNPOOL, and we add their values to THIS->LOCAL_VALUES_.
 // This function is always called from the main thread.  The actual
 // output of the local symbols will occur in a separate task.
 
 template<int size, bool big_endian>
-unsigned int
-Sized_relobj<size, big_endian>::do_finalize_local_symbols(unsigned int index,
-                                                         off_t off,
-                                                         Stringpool* pool)
+void
+Sized_relobj<size, big_endian>::do_count_local_symbols(Stringpool* pool,
+                                                      Stringpool* dynpool)
 {
   gold_assert(this->symtab_shndx_ != -1U);
   if (this->symtab_shndx_ == 0)
     {
       // This object has no symbols.  Weird but legal.
-      return index;
+      return;
     }
 
-  gold_assert(off == static_cast<off_t>(align_address(off, size >> 3)));
-
-  this->local_symbol_offset_ = off;
-
   // Read the symbol table section header.
   const unsigned int symtab_shndx = this->symtab_shndx_;
   typename This::Shdr symtabshdr(this,
@@ -759,8 +756,6 @@ Sized_relobj<size, big_endian>::do_finalize_local_symbols(unsigned int index,
   const unsigned char* psyms = this->get_view(symtabshdr.get_sh_offset(),
                                              locsize, true);
 
-  this->local_values_.resize(loccount);
-
   // Read the symbol names.
   const unsigned int strtab_shndx = symtabshdr.get_sh_link();
   off_t strtab_size;
@@ -774,6 +769,7 @@ Sized_relobj<size, big_endian>::do_finalize_local_symbols(unsigned int index,
   const std::vector<Map_to_output>& mo(this->map_to_output());
   unsigned int shnum = this->shnum();
   unsigned int count = 0;
+  unsigned int dyncount = 0;
   // Skip the first, dummy, symbol.
   psyms += sym_size;
   for (unsigned int i = 1; i < loccount; ++i, psyms += sym_size)
@@ -787,11 +783,82 @@ Sized_relobj<size, big_endian>::do_finalize_local_symbols(unsigned int index,
 
       if (sym.get_st_type() == elfcpp::STT_SECTION)
        lv.set_is_section_symbol();
+      else if (sym.get_st_type() == elfcpp::STT_TLS)
+       lv.set_is_tls_symbol();
+
+      // Save the input symbol value for use in do_finalize_local_symbols().
+      lv.set_input_value(sym.get_st_value());
+
+      // Decide whether this symbol should go into the output file.
+
+      if (shndx < shnum && mo[shndx].output_section == NULL)
+        {
+         lv.set_no_output_symtab_entry();
+          continue;
+        }
+
+      if (sym.get_st_type() == elfcpp::STT_SECTION)
+       {
+         lv.set_no_output_symtab_entry();
+         continue;
+       }
+
+      if (sym.get_st_name() >= strtab_size)
+       {
+         this->error(_("local symbol %u section name out of range: %u >= %u"),
+                     i, sym.get_st_name(),
+                     static_cast<unsigned int>(strtab_size));
+         lv.set_no_output_symtab_entry();
+         continue;
+       }
+
+      // Add the symbol to the symbol table string pool.
+      const char* name = pnames + sym.get_st_name();
+      pool->add(name, true, NULL);
+      ++count;
+
+      // If needed, add the symbol to the dynamic symbol table string pool.
+      if (lv.needs_output_dynsym_entry())
+        {
+          dynpool->add(name, true, NULL);
+          ++dyncount;
+        }
+    }
+
+  this->output_local_symbol_count_ = count;
+  this->output_local_dynsym_count_ = dyncount;
+}
+
+// Finalize the local symbols.  Here we add their values to
+// THIS->LOCAL_VALUES_ and set their output symbol table indexes.
+// This function is always called from the main thread.  The actual
+// output of the local symbols will occur in a separate task.
+
+template<int size, bool big_endian>
+unsigned int
+Sized_relobj<size, big_endian>::do_finalize_local_symbols(unsigned int index,
+                                                          off_t off)
+{
+  gold_assert(off == static_cast<off_t>(align_address(off, size >> 3)));
+
+  const unsigned int loccount = this->local_symbol_count_;
+  this->local_symbol_offset_ = off;
+
+  const std::vector<Map_to_output>& mo(this->map_to_output());
+  unsigned int shnum = this->shnum();
 
+  for (unsigned int i = 1; i < loccount; ++i)
+    {
+      Symbol_value<size>& lv(this->local_values_[i]);
+
+      unsigned int shndx = lv.input_shndx();
+
+      // Set the output symbol value.
+      
       if (shndx >= elfcpp::SHN_LORESERVE)
        {
          if (shndx == elfcpp::SHN_ABS)
-           lv.set_output_value(sym.get_st_value());
+           lv.set_output_value(lv.input_value());
          else
            {
              // FIXME: Handle SHN_XINDEX.
@@ -814,45 +881,61 @@ Sized_relobj<size, big_endian>::do_finalize_local_symbols(unsigned int index,
          if (os == NULL)
            {
              lv.set_output_value(0);
-             lv.set_no_output_symtab_entry();
              continue;
            }
-
-         if (mo[shndx].offset == -1)
-           lv.set_input_value(sym.get_st_value());
+         else if (mo[shndx].offset == -1)
+           {
+             // Leave the input value in place for SHF_MERGE sections.
+           }
+          else if (lv.is_tls_symbol())
+           lv.set_output_value(mo[shndx].output_section->tls_offset()
+                               + mo[shndx].offset
+                               + lv.input_value());
          else
            lv.set_output_value(mo[shndx].output_section->address()
                                + mo[shndx].offset
-                               + sym.get_st_value());
+                               + lv.input_value());
        }
 
-      // Decide whether this symbol should go into the output file.
-
-      if (sym.get_st_type() == elfcpp::STT_SECTION)
-       {
-         lv.set_no_output_symtab_entry();
-         continue;
-       }
+      if (lv.needs_output_symtab_entry())
+        {
+          lv.set_output_symtab_index(index);
+          ++index;
+        }
+    }
+  return index;
+}
 
-      if (sym.get_st_name() >= strtab_size)
-       {
-         this->error(_("local symbol %u section name out of range: %u >= %u"),
-                     i, sym.get_st_name(),
-                     static_cast<unsigned int>(strtab_size));
-         lv.set_no_output_symtab_entry();
-         continue;
-       }
+// Set the output dynamic symbol table indexes for the local variables.
 
-      const char* name = pnames + sym.get_st_name();
-      pool->add(name, true, NULL);
-      lv.set_output_symtab_index(index);
-      ++index;
-      ++count;
+template<int size, bool big_endian>
+unsigned int
+Sized_relobj<size, big_endian>::do_set_local_dynsym_indexes(unsigned int index)
+{
+  const unsigned int loccount = this->local_symbol_count_;
+  for (unsigned int i = 1; i < loccount; ++i)
+    {
+      Symbol_value<size>& lv(this->local_values_[i]);
+      if (lv.needs_output_dynsym_entry())
+        {
+          lv.set_output_dynsym_index(index);
+          ++index;
+        }
     }
+  return index;
+}
 
-  this->output_local_symbol_count_ = count;
+// Set the offset where local dynamic symbol information will be stored.
+// Returns the count of local symbols contributed to the symbol table by
+// this object.
 
-  return index;
+template<int size, bool big_endian>
+unsigned int
+Sized_relobj<size, big_endian>::do_set_local_dynsym_offset(off_t off)
+{
+  gold_assert(off == static_cast<off_t>(align_address(off, size >> 3)));
+  this->local_dynsym_offset_ = off;
+  return this->output_local_dynsym_count_;
 }
 
 // Return the value of the local symbol symndx.
@@ -905,9 +988,10 @@ Sized_relobj<size, big_endian>::local_value(unsigned int shndx,
 template<int size, bool big_endian>
 void
 Sized_relobj<size, big_endian>::write_local_symbols(Output_file* of,
-                                                   const Stringpool* sympool)
+                                                   const Stringpool* sympool,
+                                                   const Stringpool* dynpool)
 {
-  if (parameters->strip_all())
+  if (parameters->strip_all() && this->output_local_dynsym_count_ == 0)
     return;
 
   gold_assert(this->symtab_shndx_ != -1U);
@@ -939,24 +1023,30 @@ Sized_relobj<size, big_endian>::write_local_symbols(Output_file* of,
                                                        true);
   const char* pnames = reinterpret_cast<const char*>(pnamesu);
 
-  // Get a view into the output file.
+  // Get views into the output file for the portions of the symbol table
+  // and the dynamic symbol table that we will be writing.
   off_t output_size = this->output_local_symbol_count_ * sym_size;
-  unsigned char* oview = of->get_output_view(this->local_symbol_offset_,
-                                            output_size);
+  unsigned char* oview;
+  if (output_size > 0)
+    oview = of->get_output_view(this->local_symbol_offset_, output_size);
+
+  off_t dyn_output_size = this->output_local_dynsym_count_ * sym_size;
+  unsigned char* dyn_oview = NULL;
+  if (dyn_output_size > 0)
+    dyn_oview = of->get_output_view(this->local_dynsym_offset_,
+                                    dyn_output_size);
 
   const std::vector<Map_to_output>& mo(this->map_to_output());
 
   gold_assert(this->local_values_.size() == loccount);
 
   unsigned char* ov = oview;
+  unsigned char* dyn_ov = dyn_oview;
   psyms += sym_size;
   for (unsigned int i = 1; i < loccount; ++i, psyms += sym_size)
     {
       elfcpp::Sym<size, big_endian> isym(psyms);
 
-      if (!this->local_values_[i].needs_output_symtab_entry())
-       continue;
-
       unsigned int st_shndx = isym.get_st_shndx();
       if (st_shndx < elfcpp::SHN_LORESERVE)
        {
@@ -966,23 +1056,56 @@ Sized_relobj<size, big_endian>::write_local_symbols(Output_file* of,
          st_shndx = mo[st_shndx].output_section->out_shndx();
        }
 
-      elfcpp::Sym_write<size, big_endian> osym(ov);
-
-      gold_assert(isym.get_st_name() < strtab_size);
-      const char* name = pnames + isym.get_st_name();
-      osym.put_st_name(sympool->get_offset(name));
-      osym.put_st_value(this->local_values_[i].value(this, 0));
-      osym.put_st_size(isym.get_st_size());
-      osym.put_st_info(isym.get_st_info());
-      osym.put_st_other(isym.get_st_other());
-      osym.put_st_shndx(st_shndx);
+      // Write the symbol to the output symbol table.
+      if (!parameters->strip_all()
+         && this->local_values_[i].needs_output_symtab_entry())
+        {
+          elfcpp::Sym_write<size, big_endian> osym(ov);
+
+          gold_assert(isym.get_st_name() < strtab_size);
+          const char* name = pnames + isym.get_st_name();
+          osym.put_st_name(sympool->get_offset(name));
+          osym.put_st_value(this->local_values_[i].value(this, 0));
+          osym.put_st_size(isym.get_st_size());
+          osym.put_st_info(isym.get_st_info());
+          osym.put_st_other(isym.get_st_other());
+          osym.put_st_shndx(st_shndx);
+
+          ov += sym_size;
+        }
 
-      ov += sym_size;
+      // Write the symbol to the output dynamic symbol table.
+      if (this->local_values_[i].needs_output_dynsym_entry())
+        {
+          gold_assert(dyn_ov < dyn_oview + dyn_output_size);
+          elfcpp::Sym_write<size, big_endian> osym(dyn_ov);
+
+          gold_assert(isym.get_st_name() < strtab_size);
+          const char* name = pnames + isym.get_st_name();
+          osym.put_st_name(dynpool->get_offset(name));
+          osym.put_st_value(this->local_values_[i].value(this, 0));
+          osym.put_st_size(isym.get_st_size());
+          osym.put_st_info(isym.get_st_info());
+          osym.put_st_other(isym.get_st_other());
+          osym.put_st_shndx(st_shndx);
+
+          dyn_ov += sym_size;
+        }
     }
 
-  gold_assert(ov - oview == output_size);
 
-  of->write_output_view(this->local_symbol_offset_, output_size, oview);
+  if (output_size > 0)
+    {
+      gold_assert(ov - oview == output_size);
+      of->write_output_view(this->local_symbol_offset_, output_size, oview);
+    }
+
+  if (dyn_output_size > 0)
+    {
+      gold_assert(dyn_ov - dyn_oview == dyn_output_size);
+      of->write_output_view(this->local_dynsym_offset_, dyn_output_size,
+                            dyn_oview);
+    }
 }
 
 // Set *INFO to symbolic information about the offset OFFSET in the
index 40839bc..4b826cf 100644 (file)
@@ -429,13 +429,30 @@ class Relobj : public Object
              Layout* layout, Read_relocs_data* rd)
   { return this->do_scan_relocs(options, symtab, layout, rd); }
 
-  // Initial local symbol processing: set the offset where local
-  // symbol information will be stored; add local symbol names to
-  // *POOL; return the new local symbol index.
+  // Initial local symbol processing: count the number of local symbols
+  // in the output symbol table and dynamic symbol table; add local symbol
+  // names to *POOL and *DYNPOOL.
+  void
+  count_local_symbols(Stringpool_template<char>* pool,
+                      Stringpool_template<char>* dynpool)
+  { return this->do_count_local_symbols(pool, dynpool); }
+
+  // Set the values of the local symbols, set the output symbol table
+  // indexes for the local variables, and set the offset where local
+  // symbol information will be stored. Returns the new local symbol index.
+  unsigned int
+  finalize_local_symbols(unsigned int index, off_t off)
+  { return this->do_finalize_local_symbols(index, off); }
+
+  // Set the output dynamic symbol table indexes for the local variables.
   unsigned int
-  finalize_local_symbols(unsigned int index, off_t off,
-                        Stringpool_template<char>* pool)
-  { return this->do_finalize_local_symbols(index, off, pool); }
+  set_local_dynsym_indexes(unsigned int index)
+  { return this->do_set_local_dynsym_indexes(index); }
+
+  // Set the offset where local dynamic symbol information will be stored.
+  unsigned int
+  set_local_dynsym_offset(off_t off)
+  { return this->do_set_local_dynsym_offset(off); }
 
   // Relocate the input sections and write out the local symbols.
   void
@@ -521,11 +538,24 @@ class Relobj : public Object
   do_scan_relocs(const General_options&, Symbol_table*, Layout*,
                 Read_relocs_data*) = 0;
 
-  // Finalize local symbols--implemented by child class.
-  virtual unsigned int
-  do_finalize_local_symbols(unsigned int, off_t,
+  // Count local symbols--implemented by child class.
+  virtual void
+  do_count_local_symbols(Stringpool_template<char>*,
                            Stringpool_template<char>*) = 0;
 
+  // Finalize the local symbols.  Set the output symbol table indexes for the local variables, and set the
+  // offset where local symbol information will be stored.
+  virtual unsigned int
+  do_finalize_local_symbols(unsigned int, off_t) = 0;
+
+  // Set the output dynamic symbol table indexes for the local variables.
+  virtual unsigned int
+  do_set_local_dynsym_indexes(unsigned int) = 0;
+
+  // Set the offset where local dynamic symbol information will be stored.
+  virtual unsigned int
+  do_set_local_dynsym_offset(off_t) = 0;
+
   // Relocate the input sections and write out the local
   // symbols--implemented by child class.
   virtual void
@@ -580,7 +610,8 @@ class Symbol_value
   typedef typename elfcpp::Elf_types<size>::Elf_Addr Value;
 
   Symbol_value()
-    : output_symtab_index_(0), input_shndx_(0), is_section_symbol_(false),
+    : output_symtab_index_(0), output_dynsym_index_(-1U), input_shndx_(0),
+      is_section_symbol_(false), is_tls_symbol_(false),
       needs_output_address_(false), value_(0)
   { }
 
@@ -604,9 +635,11 @@ class Symbol_value
     this->needs_output_address_ = false;
   }
 
-  // If this symbol is mapped to an output section which requires
-  // special handling to determine the output value, we store the
-  // value of the symbol in the input file.  This is used for
+  // Set the value of the symbol from the input file.  This value
+  // will usually be replaced during finalization with the output
+  // value, but if the symbol is mapped to an output section which
+  // requires special handling to determine the output value, we
+  // leave the input value in place until later.  This is used for
   // SHF_MERGE sections.
   void
   set_input_value(Value value)
@@ -615,12 +648,20 @@ class Symbol_value
     this->needs_output_address_ = true;
   }
 
+  // Return the input value.
+  Value
+  input_value() const
+  {
+    gold_assert(this->needs_output_address_);
+    return this->value_;
+  }
+
   // Return whether this symbol should go into the output symbol
   // table.
   bool
   needs_output_symtab_entry() const
   {
-    gold_assert(this->output_symtab_index_ != 0);
+    // gold_assert(this->output_symtab_index_ != 0);
     return this->output_symtab_index_ != -1U;
   }
 
@@ -649,6 +690,37 @@ class Symbol_value
     this->output_symtab_index_ = -1U;
   }
 
+  // Set the index in the output dynamic symbol table.
+  void
+  set_needs_output_dynsym_entry()
+  {
+    this->output_dynsym_index_ = 0;
+  }
+
+  // Return whether this symbol should go into the output symbol
+  // table.
+  bool
+  needs_output_dynsym_entry() const
+  {
+    return this->output_dynsym_index_ != -1U;
+  }
+
+  // Record that this symbol should go into the dynamic symbol table.
+  void
+  set_output_dynsym_index(unsigned int i)
+  {
+    gold_assert(this->output_dynsym_index_ == 0);
+    this->output_dynsym_index_ = i;
+  }
+
+  // Return the index in the output dynamic symbol table.
+  unsigned int
+  output_dynsym_index() const
+  {
+    gold_assert(this->output_dynsym_index_ != 0);
+    return this->output_dynsym_index_;
+  }
+
   // Set the index of the input section in the input file.
   void
   set_input_shndx(unsigned int i)
@@ -657,20 +729,40 @@ class Symbol_value
     gold_assert(this->input_shndx_ == i);
   }
 
+  // Return the index of the input section in the input file.
+  unsigned int
+  input_shndx() const
+  { return this->input_shndx_; }
+
   // Record that this is a section symbol.
   void
   set_is_section_symbol()
   { this->is_section_symbol_ = true; }
 
+  // Record that this is a TLS symbol.
+  void
+  set_is_tls_symbol()
+  { this->is_tls_symbol_ = true; }
+
+  // Return TRUE if this is a TLS symbol.
+  bool
+  is_tls_symbol() const
+  { return this->is_tls_symbol_; }
+
  private:
   // The index of this local symbol in the output symbol table.  This
   // will be -1 if the symbol should not go into the symbol table.
   unsigned int output_symtab_index_;
+  // The index of this local symbol in the dynamic symbol table.  This
+  // will be -1 if the symbol should not go into the symbol table.
+  unsigned int output_dynsym_index_;
   // The section index in the input file in which this symbol is
   // defined.
-  unsigned int input_shndx_ : 30;
+  unsigned int input_shndx_ : 29;
   // Whether this is a STT_SECTION symbol.
   bool is_section_symbol_ : 1;
+  // Whether this is a STT_TLS symbol.
+  bool is_tls_symbol_ : 1;
   // Whether getting the value of this symbol requires calling an
   // Output_section method.  For example, this will be true of a
   // symbol in a SHF_MERGE section.
@@ -744,6 +836,15 @@ class Sized_relobj : public Relobj
     return this->local_values_[sym].output_symtab_index();
   }
 
+  // Return the index of local symbol SYM in the dynamic symbol
+  // table.  A value of -1U means that the symbol is not being output.
+  unsigned int
+  dynsym_index(unsigned int sym) const
+  {
+    gold_assert(sym < this->local_values_.size());
+    return this->local_values_[sym].output_dynsym_index();
+  }
+
   // Return the appropriate Sized_target structure.
   Sized_target<size, big_endian>*
   sized_target()
@@ -765,6 +866,13 @@ class Sized_relobj : public Relobj
   local_value(unsigned int shndx, Address value, bool is_section_symbol,
              Address addend) const;
 
+  void
+  set_needs_output_dynsym_entry(unsigned int sym)
+  {
+    gold_assert(sym < this->local_values_.size());
+    this->local_values_[sym].set_needs_output_dynsym_entry();
+  }
+
   // Return whether the local symbol SYMNDX has a GOT offset.
   // For TLS symbols, the GOT entry will hold its tp-relative offset.
   bool
@@ -878,10 +986,22 @@ class Sized_relobj : public Relobj
   do_scan_relocs(const General_options&, Symbol_table*, Layout*,
                 Read_relocs_data*);
 
+  // Count the local symbols.
+  void
+  do_count_local_symbols(Stringpool_template<char>*,
+                            Stringpool_template<char>*);
+
   // Finalize the local symbols.
   unsigned int
-  do_finalize_local_symbols(unsigned int, off_t,
-                           Stringpool_template<char>*);
+  do_finalize_local_symbols(unsigned int, off_t);
+
+  // Set the offset where local dynamic symbol information will be stored.
+  unsigned int
+  do_set_local_dynsym_indexes(unsigned int);
+
+  // Set the offset where local dynamic symbol information will be stored.
+  unsigned int
+  do_set_local_dynsym_offset(off_t);
 
   // Relocate the input sections and write out the local symbols.
   void
@@ -978,6 +1098,7 @@ class Sized_relobj : public Relobj
   // Write out the local symbols.
   void
   write_local_symbols(Output_file*,
+                     const Stringpool_template<char>*,
                      const Stringpool_template<char>*);
 
   // The GOT offsets of local symbols. This map also stores GOT offsets
@@ -1007,10 +1128,15 @@ class Sized_relobj : public Relobj
   unsigned int local_symbol_count_;
   // The number of local symbols which go into the output file.
   unsigned int output_local_symbol_count_;
+  // The number of local symbols which go into the output file's dynamic
+  // symbol table.
+  unsigned int output_local_dynsym_count_;
   // The entries in the symbol table for the external symbols.
   Symbols symbols_;
   // File offset for local symbols.
   off_t local_symbol_offset_;
+  // File offset for local dynamic symbols.
+  off_t local_dynsym_offset_;
   // Values of local symbols.
   Local_values local_values_;
   // GOT offsets for local non-TLS symbols, and tp-relative offsets
index 6c4ed5c..7fd901c 100644 (file)
@@ -505,6 +505,113 @@ Output_data_strtab::do_write(Output_file* of)
 
 // Output_reloc methods.
 
+// A reloc against a global symbol.
+
+template<bool dynamic, int size, bool big_endian>
+Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
+    Symbol* gsym,
+    unsigned int type,
+    Output_data* od,
+    Address address)
+  : address_(address), local_sym_index_(GSYM_CODE), type_(type),
+    shndx_(INVALID_CODE)
+{
+  this->u1_.gsym = gsym;
+  this->u2_.od = od;
+  if (dynamic)
+    gsym->set_needs_dynsym_entry();
+}
+
+template<bool dynamic, int size, bool big_endian>
+Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
+    Symbol* gsym,
+    unsigned int type,
+    Relobj* relobj,
+    unsigned int shndx,
+    Address address)
+  : address_(address), local_sym_index_(GSYM_CODE), type_(type),
+    shndx_(shndx)
+{
+  gold_assert(shndx != INVALID_CODE);
+  this->u1_.gsym = gsym;
+  this->u2_.relobj = relobj;
+  if (dynamic)
+    gsym->set_needs_dynsym_entry();
+}
+
+// A reloc against a local symbol.
+
+template<bool dynamic, int size, bool big_endian>
+Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
+    Sized_relobj<size, big_endian>* relobj,
+    unsigned int local_sym_index,
+    unsigned int type,
+    Output_data* od,
+    Address address)
+  : address_(address), local_sym_index_(local_sym_index), type_(type),
+    shndx_(INVALID_CODE)
+{
+  gold_assert(local_sym_index != GSYM_CODE
+              && local_sym_index != INVALID_CODE);
+  this->u1_.relobj = relobj;
+  this->u2_.od = od;
+  if (dynamic && local_sym_index > 0)
+    relobj->set_needs_output_dynsym_entry(local_sym_index);
+}
+
+template<bool dynamic, int size, bool big_endian>
+Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
+    Sized_relobj<size, big_endian>* relobj,
+    unsigned int local_sym_index,
+    unsigned int type,
+    unsigned int shndx,
+    Address address)
+  : address_(address), local_sym_index_(local_sym_index), type_(type),
+    shndx_(shndx)
+{
+  gold_assert(local_sym_index != GSYM_CODE
+              && local_sym_index != INVALID_CODE);
+  gold_assert(shndx != INVALID_CODE);
+  this->u1_.relobj = relobj;
+  this->u2_.relobj = relobj;
+  if (dynamic && local_sym_index > 0)
+    relobj->set_needs_output_dynsym_entry(local_sym_index);
+}
+
+// A reloc against the STT_SECTION symbol of an output section.
+
+template<bool dynamic, int size, bool big_endian>
+Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
+    Output_section* os,
+    unsigned int type,
+    Output_data* od,
+    Address address)
+  : address_(address), local_sym_index_(SECTION_CODE), type_(type),
+    shndx_(INVALID_CODE)
+{
+  this->u1_.os = os;
+  this->u2_.od = od;
+  if (dynamic)
+    os->set_needs_dynsym_index();
+}
+
+template<bool dynamic, int size, bool big_endian>
+Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
+    Output_section* os,
+    unsigned int type,
+    Relobj* relobj,
+    unsigned int shndx,
+    Address address)
+  : address_(address), local_sym_index_(SECTION_CODE), type_(type),
+    shndx_(shndx)
+{
+  gold_assert(shndx != INVALID_CODE);
+  this->u1_.os = os;
+  this->u2_.relobj = relobj;
+  if (dynamic)
+    os->set_needs_dynsym_index();
+}
+
 // Get the symbol index of a relocation.
 
 template<bool dynamic, int size, bool big_endian>
@@ -541,12 +648,7 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::get_symbol_index()
 
     default:
       if (dynamic)
-       {
-         // FIXME: It seems that some targets may need to generate
-         // dynamic relocations against local symbols for some
-         // reasons.  This will have to be addressed at some point.
-         gold_unreachable();
-       }
+        index = this->u1_.relobj->dynsym_index(this->local_sym_index_);
       else
        index = this->u1_.relobj->symtab_index(this->local_sym_index_);
       break;
@@ -725,6 +827,42 @@ Output_data_got<size, big_endian>::add_global(Symbol* gsym)
   return true;
 }
 
+// Add an entry for a global symbol to the GOT, and add a dynamic
+// relocation of type R_TYPE for the GOT entry.
+template<int size, bool big_endian>
+void
+Output_data_got<size, big_endian>::add_global_with_rel(
+    Symbol* gsym,
+    Rel_dyn* rel_dyn,
+    unsigned int r_type)
+{
+  if (gsym->has_got_offset())
+    return;
+
+  this->entries_.push_back(Got_entry());
+  this->set_got_size();
+  unsigned int got_offset = this->last_got_offset();
+  gsym->set_got_offset(got_offset);
+  rel_dyn->add_global(gsym, r_type, this, got_offset);
+}
+
+template<int size, bool big_endian>
+void
+Output_data_got<size, big_endian>::add_global_with_rela(
+    Symbol* gsym,
+    Rela_dyn* rela_dyn,
+    unsigned int r_type)
+{
+  if (gsym->has_got_offset())
+    return;
+
+  this->entries_.push_back(Got_entry());
+  this->set_got_size();
+  unsigned int got_offset = this->last_got_offset();
+  gsym->set_got_offset(got_offset);
+  rela_dyn->add_global(gsym, r_type, this, got_offset, 0);
+}
+
 // Add an entry for a local symbol to the GOT.  This returns true if
 // this is a new GOT entry, false if the symbol already has a GOT
 // entry.
@@ -744,6 +882,44 @@ Output_data_got<size, big_endian>::add_local(
   return true;
 }
 
+// Add an entry for a local symbol to the GOT, and add a dynamic
+// relocation of type R_TYPE for the GOT entry.
+template<int size, bool big_endian>
+void
+Output_data_got<size, big_endian>::add_local_with_rel(
+    Sized_relobj<size, big_endian>* object,
+    unsigned int symndx,
+    Rel_dyn* rel_dyn,
+    unsigned int r_type)
+{
+  if (object->local_has_got_offset(symndx))
+    return;
+
+  this->entries_.push_back(Got_entry());
+  this->set_got_size();
+  unsigned int got_offset = this->last_got_offset();
+  object->set_local_got_offset(symndx, got_offset);
+  rel_dyn->add_local(object, symndx, r_type, this, got_offset);
+}
+
+template<int size, bool big_endian>
+void
+Output_data_got<size, big_endian>::add_local_with_rela(
+    Sized_relobj<size, big_endian>* object,
+    unsigned int symndx,
+    Rela_dyn* rela_dyn,
+    unsigned int r_type)
+{
+  if (object->local_has_got_offset(symndx))
+    return;
+
+  this->entries_.push_back(Got_entry());
+  this->set_got_size();
+  unsigned int got_offset = this->last_got_offset();
+  object->set_local_got_offset(symndx, got_offset);
+  rela_dyn->add_local(object, symndx, r_type, this, got_offset, 0);
+}
+
 // 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
@@ -752,8 +928,7 @@ Output_data_got<size, big_endian>::add_local(
 
 template<int size, bool big_endian>
 bool
-Output_data_got<size, big_endian>::add_global_tls(Symbol* gsym,
-                                                 bool need_pair)
+Output_data_got<size, big_endian>::add_global_tls(Symbol* gsym, bool need_pair)
 {
   if (gsym->has_tls_got_offset(need_pair))
     return false;
@@ -766,6 +941,88 @@ Output_data_got<size, big_endian>::add_global_tls(Symbol* gsym,
   return true;
 }
 
+// Add an entry for a global TLS symbol to the GOT, and add a dynamic
+// relocation of type R_TYPE.
+template<int size, bool big_endian>
+void
+Output_data_got<size, big_endian>::add_global_tls_with_rel(
+    Symbol* gsym,
+    Rel_dyn* rel_dyn,
+    unsigned int r_type)
+{
+  if (gsym->has_tls_got_offset(false))
+    return;
+
+  this->entries_.push_back(Got_entry());
+  this->set_got_size();
+  unsigned int got_offset = this->last_got_offset();
+  gsym->set_tls_got_offset(got_offset, false);
+  rel_dyn->add_global(gsym, r_type, this, got_offset);
+}
+
+template<int size, bool big_endian>
+void
+Output_data_got<size, big_endian>::add_global_tls_with_rela(
+    Symbol* gsym,
+    Rela_dyn* rela_dyn,
+    unsigned int r_type)
+{
+  if (gsym->has_tls_got_offset(false))
+    return;
+
+  this->entries_.push_back(Got_entry());
+  this->set_got_size();
+  unsigned int got_offset = this->last_got_offset();
+  gsym->set_tls_got_offset(got_offset, false);
+  rela_dyn->add_global(gsym, r_type, this, got_offset, 0);
+}
+
+// Add a pair of entries for a global TLS symbol to the GOT, and add
+// dynamic relocations of type MOD_R_TYPE and DTV_R_TYPE, respectively.
+template<int size, bool big_endian>
+void
+Output_data_got<size, big_endian>::add_global_tls_with_rel(
+    Symbol* gsym,
+    Rel_dyn* rel_dyn,
+    unsigned int mod_r_type,
+    unsigned int dtv_r_type)
+{
+  if (gsym->has_tls_got_offset(true))
+    return;
+
+  this->entries_.push_back(Got_entry());
+  unsigned int got_offset = this->last_got_offset();
+  gsym->set_tls_got_offset(got_offset, true);
+  rel_dyn->add_global(gsym, mod_r_type, this, got_offset);
+
+  this->entries_.push_back(Got_entry());
+  this->set_got_size();
+  got_offset = this->last_got_offset();
+  rel_dyn->add_global(gsym, dtv_r_type, this, got_offset);
+}
+
+template<int size, bool big_endian>
+void
+Output_data_got<size, big_endian>::add_global_tls_with_rela(
+    Symbol* gsym,
+    Rela_dyn* rela_dyn,
+    unsigned int mod_r_type,
+    unsigned int dtv_r_type)
+{
+  if (gsym->has_tls_got_offset(true))
+    return;
+
+  this->entries_.push_back(Got_entry());
+  unsigned int got_offset = this->last_got_offset();
+  gsym->set_tls_got_offset(got_offset, true);
+  rela_dyn->add_global(gsym, mod_r_type, this, got_offset, 0);
+
+  this->entries_.push_back(Got_entry());
+  this->set_got_size();
+  got_offset = this->last_got_offset();
+  rela_dyn->add_global(gsym, dtv_r_type, this, got_offset, 0);
+}
+
 // 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
@@ -790,6 +1047,67 @@ Output_data_got<size, big_endian>::add_local_tls(
   return true;
 }
 
+// Add an entry (or pair of entries) for a local TLS symbol to the GOT,
+// and add a dynamic relocation of type R_TYPE for the first GOT entry.
+// Because this is a local symbol, the first GOT entry can be relocated
+// relative to a section symbol, and the second GOT entry will have an
+// dtv-relative value that can be computed at link time.
+template<int size, bool big_endian>
+void
+Output_data_got<size, big_endian>::add_local_tls_with_rel(
+    Sized_relobj<size, big_endian>* object,
+    unsigned int symndx,
+    unsigned int shndx,
+    bool need_pair,
+    Rel_dyn* rel_dyn,
+    unsigned int r_type)
+{
+  if (object->local_has_tls_got_offset(symndx, need_pair))
+    return;
+
+  this->entries_.push_back(Got_entry());
+  unsigned int got_offset = this->last_got_offset();
+  object->set_local_tls_got_offset(symndx, got_offset, need_pair);
+  off_t off;
+  Output_section* os = object->output_section(shndx, &off);
+  rel_dyn->add_output_section(os, r_type, this, got_offset);
+
+  // The second entry of the pair will be statically initialized
+  // with the TLS offset of the symbol.
+  if (need_pair)
+    this->entries_.push_back(Got_entry(object, symndx));
+
+  this->set_got_size();
+}
+
+template<int size, bool big_endian>
+void
+Output_data_got<size, big_endian>::add_local_tls_with_rela(
+    Sized_relobj<size, big_endian>* object,
+    unsigned int symndx,
+    unsigned int shndx,
+    bool need_pair,
+    Rela_dyn* rela_dyn,
+    unsigned int r_type)
+{
+  if (object->local_has_tls_got_offset(symndx, need_pair))
+    return;
+
+  this->entries_.push_back(Got_entry());
+  unsigned int got_offset = this->last_got_offset();
+  object->set_local_tls_got_offset(symndx, got_offset, need_pair);
+  off_t off;
+  Output_section* os = object->output_section(shndx, &off);
+  rela_dyn->add_output_section(os, r_type, this, got_offset, 0);
+
+  // The second entry of the pair will be statically initialized
+  // with the TLS offset of the symbol.
+  if (need_pair)
+    this->entries_.push_back(Got_entry(object, symndx));
+
+  this->set_got_size();
+}
+
 // Write out the GOT.
 
 template<int size, bool big_endian>
@@ -1083,7 +1401,8 @@ Output_section::Output_section(const char* name, elfcpp::Elf_Word type,
     should_link_to_symtab_(false),
     should_link_to_dynsym_(false),
     after_input_sections_(false),
-    requires_postprocessing_(false)
+    requires_postprocessing_(false),
+    tls_offset_(0)
 {
   // An unallocated section has no address.  Forcing this means that
   // we don't need special treatment for symbols defined in debug
@@ -1403,6 +1722,14 @@ Output_section::set_final_data_size()
   this->set_data_size(off - startoff);
 }
 
+// Set the TLS offset.  Called only for SHT_TLS sections.
+
+void
+Output_section::do_set_tls_offset(uint64_t tls_base)
+{
+  this->tls_offset_ = this->address() - tls_base;
+}
+
 // Write the section header to *OSHDR.
 
 template<int size, bool big_endian>
@@ -1829,6 +2156,24 @@ Output_segment::set_offset()
                  - this->vaddr_);
 }
 
+// Set the TLS offsets of the sections in the PT_TLS segment.
+
+void
+Output_segment::set_tls_offsets()
+{
+  gold_assert(this->type_ == elfcpp::PT_TLS);
+
+  for (Output_data_list::iterator p = this->output_data_.begin();
+       p != this->output_data_.end();
+       ++p)
+    (*p)->set_tls_offset(this->vaddr_);
+
+  for (Output_data_list::iterator p = this->output_bss_.begin();
+       p != this->output_bss_.end();
+       ++p)
+    (*p)->set_tls_offset(this->vaddr_);
+}
+
 // Return the number of Output_sections in an Output_segment.
 
 unsigned int
index fe42112..6c80d0a 100644 (file)
@@ -160,6 +160,17 @@ class Output_data
       }
   }
 
+  // Set the TLS offset.  Called only for SHT_TLS sections.
+  void
+  set_tls_offset(uint64_t tls_base)
+  { this->do_set_tls_offset(tls_base); }
+
+  // Return the TLS offset, relative to the base of the TLS segment.
+  // Valid only for SHT_TLS sections.
+  uint64_t
+  tls_offset() const
+  { return this->do_tls_offset(); }
+
   // Write the data to the output file.  This is called after
   // Layout::finalize is complete.
   void
@@ -232,6 +243,17 @@ class Output_data
   set_final_data_size()
   { gold_unreachable(); }
 
+  // Set the TLS offset.  Called only for SHT_TLS sections.
+  virtual void
+  do_set_tls_offset(uint64_t)
+  { gold_unreachable(); }
+
+  // Return the TLS offset, relative to the base of the TLS segment.
+  // Valid only for SHT_TLS sections.
+  virtual uint64_t
+  do_tls_offset() const
+  { gold_unreachable(); }
+
   // Functions that child classes may call.
 
   // Whether the address is valid.
@@ -681,75 +703,28 @@ class Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>
   // A reloc against a global symbol.
 
   Output_reloc(Symbol* gsym, unsigned int type, Output_data* od,
-              Address address)
-    : address_(address), local_sym_index_(GSYM_CODE), type_(type),
-      shndx_(INVALID_CODE)
-  {
-    this->u1_.gsym = gsym;
-    this->u2_.od = od;
-  }
+              Address address);
 
   Output_reloc(Symbol* gsym, unsigned int type, Relobj* relobj,
-              unsigned int shndx, Address address)
-    : address_(address), local_sym_index_(GSYM_CODE), type_(type),
-      shndx_(shndx)
-  {
-    gold_assert(shndx != INVALID_CODE);
-    this->u1_.gsym = gsym;
-    this->u2_.relobj = relobj;
-  }
+              unsigned int shndx, Address address);
 
   // A reloc against a local symbol.
 
   Output_reloc(Sized_relobj<size, big_endian>* relobj,
-              unsigned int local_sym_index,
-              unsigned int type,
-              Output_data* od,
-              Address address)
-    : address_(address), local_sym_index_(local_sym_index), type_(type),
-      shndx_(INVALID_CODE)
-  {
-    gold_assert(local_sym_index != GSYM_CODE
-               && local_sym_index != INVALID_CODE);
-    this->u1_.relobj = relobj;
-    this->u2_.od = od;
-  }
+              unsigned int local_sym_index, unsigned int type,
+              Output_data* od, Address address);
 
   Output_reloc(Sized_relobj<size, big_endian>* relobj,
-              unsigned int local_sym_index,
-              unsigned int type,
-              unsigned int shndx,
-              Address address)
-    : address_(address), local_sym_index_(local_sym_index), type_(type),
-      shndx_(shndx)
-  {
-    gold_assert(local_sym_index != GSYM_CODE
-               && local_sym_index != INVALID_CODE);
-    gold_assert(shndx != INVALID_CODE);
-    this->u1_.relobj = relobj;
-    this->u2_.relobj = relobj;
-  }
+              unsigned int local_sym_index, unsigned int type,
+              unsigned int shndx, Address address);
 
   // A reloc against the STT_SECTION symbol of an output section.
 
   Output_reloc(Output_section* os, unsigned int type, Output_data* od,
-              Address address)
-    : address_(address), local_sym_index_(SECTION_CODE), type_(type),
-      shndx_(INVALID_CODE)
-  {
-    this->u1_.os = os;
-    this->u2_.od = od;
-  }
+              Address address);
 
   Output_reloc(Output_section* os, unsigned int type, Relobj* relobj,
-              unsigned int shndx, Address address)
-    : address_(address), local_sym_index_(SECTION_CODE), type_(type),
-      shndx_(shndx)
-  {
-    gold_assert(shndx != INVALID_CODE);
-    this->u1_.os = os;
-    this->u2_.relobj = relobj;
-  }
+              unsigned int shndx, Address address);
 
   // Write the reloc entry to an output view.
   void
@@ -1070,6 +1045,8 @@ class Output_data_got : public Output_section_data_build
 {
  public:
   typedef typename elfcpp::Elf_types<size>::Elf_Addr Valtype;
+  typedef Output_data_reloc<elfcpp::SHT_REL, true, size, big_endian> Rel_dyn;
+  typedef Output_data_reloc<elfcpp::SHT_RELA, true, size, big_endian> Rela_dyn;
 
   Output_data_got()
     : Output_section_data_build(Output_data::default_alignment_for_size(size)),
@@ -1081,18 +1058,60 @@ class Output_data_got : public Output_section_data_build
   bool
   add_global(Symbol* gsym);
 
+  // Add an entry for a global symbol to the GOT, and add a dynamic
+  // relocation of type R_TYPE for the GOT entry.
+  void
+  add_global_with_rel(Symbol* gsym, Rel_dyn* rel_dyn, unsigned int r_type);
+
+  void
+  add_global_with_rela(Symbol* gsym, Rela_dyn* rela_dyn, unsigned int r_type);
+
   // Add an entry for a local 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(Sized_relobj<size, big_endian>* object, unsigned int sym_index);
 
+  // Add an entry for a global symbol to the GOT, and add a dynamic
+  // relocation of type R_TYPE for the GOT entry.
+  void
+  add_local_with_rel(Sized_relobj<size, big_endian>* object,
+                     unsigned int sym_index, Rel_dyn* rel_dyn,
+                     unsigned int r_type);
+
+  void
+  add_local_with_rela(Sized_relobj<size, big_endian>* object,
+                      unsigned int sym_index, Rela_dyn* rela_dyn,
+                      unsigned int r_type);
+
   // 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 for a global TLS symbol to the GOT, and add a dynamic
+  // relocation of type R_TYPE.
+  void
+  add_global_tls_with_rel(Symbol* gsym, Rel_dyn* rel_dyn,
+                          unsigned int r_type);
+
+  void
+  add_global_tls_with_rela(Symbol* gsym, Rela_dyn* rela_dyn,
+                           unsigned int r_type);
+
+  // Add a pair of entries for a global TLS symbol to the GOT, and add
+  // dynamic relocations of type MOD_R_TYPE and DTV_R_TYPE, respectively.
+  void
+  add_global_tls_with_rel(Symbol* gsym, Rel_dyn* rel_dyn,
+                          unsigned int mod_r_type,
+                          unsigned int dtv_r_type);
+
+  void
+  add_global_tls_with_rela(Symbol* gsym, Rela_dyn* rela_dyn,
+                           unsigned int mod_r_type,
+                           unsigned int dtv_r_type);
+
   // 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.
@@ -1100,6 +1119,23 @@ class Output_data_got : public Output_section_data_build
   add_local_tls(Sized_relobj<size, big_endian>* object,
                unsigned int sym_index, bool need_pair);
 
+  // Add an entry (or pair of entries) for a local TLS symbol to the GOT,
+  // and add a dynamic relocation of type R_TYPE for the first GOT entry.
+  // Because this is a local symbol, the first GOT entry can be relocated
+  // relative to a section symbol, and the second GOT entry will have an
+  // dtv-relative value that can be computed at link time.
+  void
+  add_local_tls_with_rel(Sized_relobj<size, big_endian>* object,
+                         unsigned int sym_index, unsigned int shndx,
+                         bool need_pair, Rel_dyn* rel_dyn,
+                         unsigned int r_type);
+
+  void
+  add_local_tls_with_rela(Sized_relobj<size, big_endian>* object,
+                         unsigned int sym_index, unsigned int shndx,
+                         bool need_pair, Rela_dyn* rela_dyn,
+                         unsigned int r_type);
+
   // Add a constant to the GOT.  This returns the offset of the new
   // entry from the start of the GOT.
   unsigned int
@@ -1609,6 +1645,16 @@ class Output_section : public Output_data
   do_is_section_flag_set(elfcpp::Elf_Xword flag) const
   { return (this->flags_ & flag) != 0; }
 
+  // Set the TLS offset.  Called only for SHT_TLS sections.
+  void
+  do_set_tls_offset(uint64_t tls_base);
+
+  // Return the TLS offset, relative to the base of the TLS segment.
+  // Valid only for SHT_TLS sections.
+  uint64_t
+  do_tls_offset() const
+  { return this->tls_offset_; }
+
   // Modify the section name.  This is only permitted for an
   // unallocated section, and only before the size has been finalized.
   // Otherwise the name will not get into Layout::namepool_.
@@ -1933,6 +1979,9 @@ class Output_section : public Output_data
   // Whether this section requires post processing after all
   // relocations have been applied.
   bool requires_postprocessing_ : 1;
+  // For SHT_TLS sections, the offset of this section relative to the base
+  // of the TLS segment.
+  uint64_t tls_offset_;
 };
 
 // An output segment.  PT_LOAD segments are built from collections of
@@ -2021,6 +2070,10 @@ class Output_segment
   void
   set_offset();
 
+  // Set the TLS offsets of the sections contained in the PT_TLS segment.
+  void
+  set_tls_offsets();
+
   // Return the number of output sections.
   unsigned int
   output_section_count() const;
index 2763be5..e34cd04 100644 (file)
@@ -390,7 +390,7 @@ Sized_relobj<size, big_endian>::do_relocate(const General_options& options,
     }
 
   // Write out the local symbols.
-  this->write_local_symbols(of, layout->sympool());
+  this->write_local_symbols(of, layout->sympool(), layout->dynpool());
 }
 
 // Write section data to the output file.  PSHDRS points to the
index 40b3080..a18d3ae 100644 (file)
@@ -1507,7 +1507,10 @@ Symbol_table::sized_finalize(unsigned index, off_t off, Stringpool* pool)
                    continue;
                  }
 
-               value = sym->value() + os->address() + secoff;
+                if (sym->type() == elfcpp::STT_TLS)
+                  value = sym->value() + os->tls_offset() + secoff;
+                else
+                 value = sym->value() + os->address() + secoff;
              }
          }
          break;
@@ -1920,7 +1923,7 @@ Symbol_table::print_stats() const
 // that case.
 
 // This struct is used to compare line information, as returned by
-// Dwarf_line_info::one_addr2line.  It imlements a < comparison
+// Dwarf_line_info::one_addr2line.  It implements a < comparison
 // operator used with std::set.
 
 struct Odr_violation_compare
index e3b263d..b11db39 100644 (file)
@@ -177,8 +177,8 @@ debug_msg_ndebug.err: debug_msg_ndebug.so odr_violation1_ndebug.so odr_violation
 
 undef_symbol.o: undef_symbol.cc
        $(CXXCOMPILE) -O0 -g -c -fPIC $<
-undef_symbol.so: undef_symbol.o
-       $(CXXLINK) -shared undef_symbol.o
+undef_symbol.so: undef_symbol.o gcctestdir/ld
+       $(CXXLINK) -Bgcctestdir/ -shared undef_symbol.o
 undef_symbol.err: undef_symbol_main.o undef_symbol.so gcctestdir/ld
        @echo $(CXXLINK) -Bgcctestdir/ -o undef_symbol_test undef_symbol_main.o undef_symbol.so "2>$@"
        @if $(CXXLINK) -Bgcctestdir/ -o undef_symbol_test undef_symbol_main.o undef_symbol.so 2>$@; \
@@ -302,12 +302,12 @@ two_file_test_1_pic.o: two_file_test_1.cc
 two_file_test_2_pic.o: two_file_test_2.cc
        $(CXXCOMPILE) -c -fpic -o $@ $<
 
-two_file_shared_1.so: two_file_test_1_pic.o
-       $(CXXLINK) -shared two_file_test_1_pic.o
-two_file_shared_2.so: two_file_test_2_pic.o
-       $(CXXLINK) -shared two_file_test_2_pic.o
-two_file_shared.so: two_file_test_1_pic.o two_file_test_2_pic.o
-       $(CXXLINK) -shared two_file_test_1_pic.o two_file_test_2_pic.o
+two_file_shared_1.so: two_file_test_1_pic.o gcctestdir/ld
+       $(CXXLINK) -Bgcctestdir/ -shared two_file_test_1_pic.o
+two_file_shared_2.so: two_file_test_2_pic.o gcctestdir/ld
+       $(CXXLINK) -Bgcctestdir/ -shared two_file_test_2_pic.o
+two_file_shared.so: two_file_test_1_pic.o two_file_test_2_pic.o gcctestdir/ld
+       $(CXXLINK) -Bgcctestdir/ -shared two_file_test_1_pic.o two_file_test_2_pic.o
 
 # The nonpic tests will fail on platforms which can not put non-PIC
 # code into shared libraries, so we just don't run them in that case.
@@ -348,12 +348,12 @@ two_file_separate_shared_21_nonpic_test_LDFLAGS = -Bgcctestdir/ -Wl,-R,.
 two_file_separate_shared_21_nonpic_test_LDADD = \
        two_file_shared_2_nonpic.so two_file_shared_1_nonpic.so
 
-two_file_shared_1_nonpic.so: two_file_test_1.o
-       $(CXXLINK) -shared two_file_test_1.o
-two_file_shared_2_nonpic.so: two_file_test_2.o
-       $(CXXLINK) -shared two_file_test_2.o
-two_file_shared_nonpic.so: two_file_test_1.o two_file_test_2.o
-       $(CXXLINK) -shared two_file_test_1.o two_file_test_2.o
+two_file_shared_1_nonpic.so: two_file_test_1.o gcctestdir/ld
+       $(CXXLINK) -Bgcctestdir/ -shared two_file_test_1.o
+two_file_shared_2_nonpic.so: two_file_test_2.o gcctestdir/ld
+       $(CXXLINK) -Bgcctestdir/ -shared two_file_test_2.o
+two_file_shared_nonpic.so: two_file_test_1.o two_file_test_2.o gcctestdir/ld
+       $(CXXLINK) -Bgcctestdir/ -shared two_file_test_1.o two_file_test_2.o
 
 endif
 
@@ -406,12 +406,12 @@ exception_test_1_pic.o: exception_test_1.cc
 exception_test_2_pic.o: exception_test_2.cc
        $(CXXCOMPILE) -c -fpic -o $@ $<
 
-exception_shared_1.so: exception_test_1_pic.o
-       $(CXXLINK) -shared exception_test_1_pic.o
-exception_shared_2.so: exception_test_2_pic.o
-       $(CXXLINK) -shared exception_test_2_pic.o
-exception_shared.so: exception_test_1_pic.o exception_test_2_pic.o
-       $(CXXLINK) -shared exception_test_1_pic.o exception_test_2_pic.o
+exception_shared_1.so: exception_test_1_pic.o gcctestdir/ld
+       $(CXXLINK) -Bgcctestdir/ -shared exception_test_1_pic.o
+exception_shared_2.so: exception_test_2_pic.o gcctestdir/ld
+       $(CXXLINK) -Bgcctestdir/ -shared exception_test_2_pic.o
+exception_shared.so: exception_test_1_pic.o exception_test_2_pic.o gcctestdir/ld
+       $(CXXLINK) -Bgcctestdir/ -shared exception_test_1_pic.o exception_test_2_pic.o
 
 weak_test_SOURCES = weak_test.cc
 weak_test_LDFLAGS = -Bgcctestdir/
@@ -449,8 +449,8 @@ tls_test_pic.o: tls_test.cc
 tls_test_file2_pic.o: tls_test_file2.cc
        $(CXXCOMPILE) -c -fpic -o $@ $<
 
-tls_test_shared.so: tls_test_pic.o tls_test_file2_pic.o
-       $(CXXLINK) -shared tls_test_pic.o tls_test_file2_pic.o
+tls_test_shared.so: tls_test_pic.o tls_test_file2_pic.o gcctestdir/ld
+       $(CXXLINK) -Bgcctestdir/ -shared tls_test_pic.o tls_test_file2_pic.o
 
 if FN_PTRS_IN_SO_WITHOUT_PIC
 
@@ -459,8 +459,8 @@ tls_shared_nonpic_test_DEPENDENCIES = gcctestdir/ld tls_test_shared_nonpic.so
 tls_shared_nonpic_test_LDFLAGS = -Bgcctestdir/ -Wl,-R,.
 tls_shared_nonpic_test_LDADD = tls_test_shared_nonpic.so -lpthread
 
-tls_test_shared_nonpic.so: tls_test.o tls_test_file2.o
-       $(CXXLINK) -shared tls_test.o tls_test_file2.o
+tls_test_shared_nonpic.so: tls_test.o tls_test_file2.o gcctestdir/ld
+       $(CXXLINK) -Bgcctestdir/ -shared tls_test.o tls_test_file2.o
 
 endif
 
index 15d5f19..a4293a9 100644 (file)
@@ -1330,8 +1330,8 @@ uninstall-am: uninstall-info-am
 
 @GCC_TRUE@@NATIVE_LINKER_TRUE@undef_symbol.o: undef_symbol.cc
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -O0 -g -c -fPIC $<
-@GCC_TRUE@@NATIVE_LINKER_TRUE@undef_symbol.so: undef_symbol.o
-@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -shared undef_symbol.o
+@GCC_TRUE@@NATIVE_LINKER_TRUE@undef_symbol.so: undef_symbol.o gcctestdir/ld
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ -shared undef_symbol.o
 @GCC_TRUE@@NATIVE_LINKER_TRUE@undef_symbol.err: undef_symbol_main.o undef_symbol.so gcctestdir/ld
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ @echo $(CXXLINK) -Bgcctestdir/ -o undef_symbol_test undef_symbol_main.o undef_symbol.so "2>$@"
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ @if $(CXXLINK) -Bgcctestdir/ -o undef_symbol_test undef_symbol_main.o undef_symbol.so 2>$@; \
@@ -1386,31 +1386,31 @@ uninstall-am: uninstall-info-am
 @GCC_TRUE@@NATIVE_LINKER_TRUE@two_file_test_2_pic.o: two_file_test_2.cc
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -c -fpic -o $@ $<
 
-@GCC_TRUE@@NATIVE_LINKER_TRUE@two_file_shared_1.so: two_file_test_1_pic.o
-@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -shared two_file_test_1_pic.o
-@GCC_TRUE@@NATIVE_LINKER_TRUE@two_file_shared_2.so: two_file_test_2_pic.o
-@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -shared two_file_test_2_pic.o
-@GCC_TRUE@@NATIVE_LINKER_TRUE@two_file_shared.so: two_file_test_1_pic.o two_file_test_2_pic.o
-@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -shared two_file_test_1_pic.o two_file_test_2_pic.o
+@GCC_TRUE@@NATIVE_LINKER_TRUE@two_file_shared_1.so: two_file_test_1_pic.o gcctestdir/ld
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ -shared two_file_test_1_pic.o
+@GCC_TRUE@@NATIVE_LINKER_TRUE@two_file_shared_2.so: two_file_test_2_pic.o gcctestdir/ld
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ -shared two_file_test_2_pic.o
+@GCC_TRUE@@NATIVE_LINKER_TRUE@two_file_shared.so: two_file_test_1_pic.o two_file_test_2_pic.o gcctestdir/ld
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ -shared two_file_test_1_pic.o two_file_test_2_pic.o
 
-@FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@two_file_shared_1_nonpic.so: two_file_test_1.o
-@FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -shared two_file_test_1.o
-@FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@two_file_shared_2_nonpic.so: two_file_test_2.o
-@FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -shared two_file_test_2.o
-@FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@two_file_shared_nonpic.so: two_file_test_1.o two_file_test_2.o
-@FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -shared two_file_test_1.o two_file_test_2.o
+@FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@two_file_shared_1_nonpic.so: two_file_test_1.o gcctestdir/ld
+@FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ -shared two_file_test_1.o
+@FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@two_file_shared_2_nonpic.so: two_file_test_2.o gcctestdir/ld
+@FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ -shared two_file_test_2.o
+@FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@two_file_shared_nonpic.so: two_file_test_1.o two_file_test_2.o gcctestdir/ld
+@FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ -shared two_file_test_1.o two_file_test_2.o
 
 @GCC_TRUE@@NATIVE_LINKER_TRUE@exception_test_1_pic.o: exception_test_1.cc
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -c -fpic -o $@ $<
 @GCC_TRUE@@NATIVE_LINKER_TRUE@exception_test_2_pic.o: exception_test_2.cc
 @GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -c -fpic -o $@ $<
 
-@GCC_TRUE@@NATIVE_LINKER_TRUE@exception_shared_1.so: exception_test_1_pic.o
-@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -shared exception_test_1_pic.o
-@GCC_TRUE@@NATIVE_LINKER_TRUE@exception_shared_2.so: exception_test_2_pic.o
-@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -shared exception_test_2_pic.o
-@GCC_TRUE@@NATIVE_LINKER_TRUE@exception_shared.so: exception_test_1_pic.o exception_test_2_pic.o
-@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -shared exception_test_1_pic.o exception_test_2_pic.o
+@GCC_TRUE@@NATIVE_LINKER_TRUE@exception_shared_1.so: exception_test_1_pic.o gcctestdir/ld
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ -shared exception_test_1_pic.o
+@GCC_TRUE@@NATIVE_LINKER_TRUE@exception_shared_2.so: exception_test_2_pic.o gcctestdir/ld
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ -shared exception_test_2_pic.o
+@GCC_TRUE@@NATIVE_LINKER_TRUE@exception_shared.so: exception_test_1_pic.o exception_test_2_pic.o gcctestdir/ld
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ -shared exception_test_1_pic.o exception_test_2_pic.o
 
 @GCC_TRUE@@NATIVE_LINKER_TRUE@@TLS_TRUE@tls_test_pic.o: tls_test.cc
 @GCC_TRUE@@NATIVE_LINKER_TRUE@@TLS_TRUE@       $(CXXCOMPILE) -c -fpic -o $@ $<
@@ -1418,11 +1418,11 @@ uninstall-am: uninstall-info-am
 @GCC_TRUE@@NATIVE_LINKER_TRUE@@TLS_TRUE@tls_test_file2_pic.o: tls_test_file2.cc
 @GCC_TRUE@@NATIVE_LINKER_TRUE@@TLS_TRUE@       $(CXXCOMPILE) -c -fpic -o $@ $<
 
-@GCC_TRUE@@NATIVE_LINKER_TRUE@@TLS_TRUE@tls_test_shared.so: tls_test_pic.o tls_test_file2_pic.o
-@GCC_TRUE@@NATIVE_LINKER_TRUE@@TLS_TRUE@       $(CXXLINK) -shared tls_test_pic.o tls_test_file2_pic.o
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@TLS_TRUE@tls_test_shared.so: tls_test_pic.o tls_test_file2_pic.o gcctestdir/ld
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@TLS_TRUE@       $(CXXLINK) -Bgcctestdir/ -shared tls_test_pic.o tls_test_file2_pic.o
 
-@FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@@TLS_TRUE@tls_test_shared_nonpic.so: tls_test.o tls_test_file2.o
-@FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@@TLS_TRUE@       $(CXXLINK) -shared tls_test.o tls_test_file2.o
+@FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@@TLS_TRUE@tls_test_shared_nonpic.so: tls_test.o tls_test_file2.o gcctestdir/ld
+@FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@@TLS_TRUE@       $(CXXLINK) -Bgcctestdir/ -shared tls_test.o tls_test_file2.o
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
 .NOEXPORT:
index f19fa9b..24f87b9 100644 (file)
@@ -186,14 +186,23 @@ class Target_x86_64 : public Sized_target<64, false>
    private:
     // Do a TLS relocation.
     inline void
-    relocate_tls(const Relocate_info<64, false>*, size_t relnum,
-                const elfcpp::Rela<64, false>&,
+    relocate_tls(const Relocate_info<64, false>*, Target_x86_64*,
+                 size_t relnum, const elfcpp::Rela<64, false>&,
                 unsigned int r_type, const Sized_symbol<64>*,
                 const Symbol_value<64>*,
                 unsigned char*, elfcpp::Elf_types<64>::Elf_Addr, off_t);
 
     // Do a TLS General-Dynamic to Local-Exec transition.
     inline void
+    tls_gd_to_ie(const Relocate_info<64, false>*, size_t relnum,
+                Output_segment* tls_segment,
+                const elfcpp::Rela<64, false>&, unsigned int r_type,
+                elfcpp::Elf_types<64>::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<64, false>*, size_t relnum,
                 Output_segment* tls_segment,
                 const elfcpp::Rela<64, false>&, unsigned int r_type,
@@ -745,7 +754,7 @@ Target_x86_64::Scan::local(const General_options&,
                            Output_section* output_section,
                            const elfcpp::Rela<64, false>& reloc,
                            unsigned int r_type,
-                           const elfcpp::Sym<64, false>&)
+                           const elfcpp::Sym<64, false>& lsym)
 {
   switch (r_type)
     {
@@ -823,16 +832,17 @@ Target_x86_64::Scan::local(const General_options&,
         if (got->add_local(object, r_sym))
           {
             // If we are generating a shared object, we need to add a
-            // dynamic RELATIVE relocation for this symbol.
+            // dynamic relocation for this symbol's GOT entry.
             if (parameters->output_is_position_independent())
               {
-               // FIXME: R_X86_64_RELATIVE assumes a 64-bit relocation.
-               gold_assert(r_type != elfcpp::R_X86_64_GOT32);
-
                 Reloc_section* rela_dyn = target->rela_dyn_section(layout);
-                rela_dyn->add_local(object, 0, elfcpp::R_X86_64_RELATIVE,
-                                    output_section, data_shndx,
-                                    reloc.get_r_offset(), 0);
+               // R_X86_64_RELATIVE assumes a 64-bit relocation.
+               if (r_type != elfcpp::R_X86_64_GOT32)
+                  rela_dyn->add_local(object, 0, elfcpp::R_X86_64_RELATIVE,
+                                      got, object->local_got_offset(r_sym), 0);
+                else
+                  rela_dyn->add_local(object, r_sym, r_type,
+                                      got, object->local_got_offset(r_sym), 0);
               }
           }
         // For GOTPLT64, we'd normally want a PLT section, but since
@@ -868,34 +878,68 @@ Target_x86_64::Scan::local(const General_options&,
        switch (r_type)
          {
           case elfcpp::R_X86_64_TLSGD:       // General-dynamic
+            if (optimized_type == tls::TLSOPT_NONE)
+              {
+                // Create a pair of GOT entries for the module index and
+                // dtv-relative offset.
+                Output_data_got<64, false>* got
+                    = target->got_section(symtab, layout);
+                unsigned int r_sym = elfcpp::elf_r_sym<64>(reloc.get_r_info());
+                got->add_local_tls_with_rela(object, r_sym,
+                                             lsym.get_st_shndx(), true,
+                                             target->rela_dyn_section(layout),
+                                             elfcpp::R_X86_64_DTPMOD64);
+              }
+            else if (optimized_type != tls::TLSOPT_TO_LE)
+             unsupported_reloc_local(object, r_type);
+            break;
+
           case elfcpp::R_X86_64_GOTPC32_TLSDESC:
           case elfcpp::R_X86_64_TLSDESC_CALL:
            // FIXME: If not relaxing to LE, we need to generate
-           // DTPMOD64 and DTPOFF64 relocs.
+           // a GOT entry with a R_x86_64_TLSDESC reloc.
            if (optimized_type != tls::TLSOPT_TO_LE)
              unsupported_reloc_local(object, r_type);
            break;
 
           case elfcpp::R_X86_64_TLSLD:       // Local-dynamic
+           if (optimized_type == tls::TLSOPT_NONE)
+             {
+               // Create a GOT entry for the module index.
+               Output_data_got<64, false>* got
+                   = target->got_section(symtab, layout);
+               unsigned int r_sym = elfcpp::elf_r_sym<64>(reloc.get_r_info());
+                got->add_local_tls_with_rela(object, r_sym,
+                                             lsym.get_st_shndx(), false,
+                                             target->rela_dyn_section(layout),
+                                             elfcpp::R_X86_64_DTPMOD64);
+             }
+           else if (optimized_type != tls::TLSOPT_TO_LE)
+             unsupported_reloc_local(object, r_type);
+           break;
+
           case elfcpp::R_X86_64_DTPOFF32:
           case elfcpp::R_X86_64_DTPOFF64:
-           // FIXME: If not relaxing to LE, we need to generate a
-           // DTPMOD64 reloc.
-           if (optimized_type != tls::TLSOPT_TO_LE)
-             unsupported_reloc_local(object, r_type);
            break;
 
           case elfcpp::R_X86_64_GOTTPOFF:    // Initial-exec
-            // FIXME: If not relaxing to LE, we need to generate a
-            // TPOFF64 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<64, false>* got
+                   = target->got_section(symtab, layout);
+               unsigned int r_sym = elfcpp::elf_r_sym<64>(reloc.get_r_info());
+               got->add_local_with_rela(object, r_sym,
+                                        target->rela_dyn_section(layout),
+                                        elfcpp::R_X86_64_TPOFF64);
+              }
+            else if (optimized_type != tls::TLSOPT_TO_LE)
               unsupported_reloc_local(object, r_type);
             break;
 
           case elfcpp::R_X86_64_TPOFF32:     // Local-exec
-           // 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:
@@ -968,8 +1012,8 @@ Target_x86_64::Scan::global(const General_options& options,
           {
             if (target->may_need_copy_reloc(gsym))
               {
-                target->copy_reloc(&options, symtab, layout, object, data_shndx,
-                                   output_section, gsym, reloc);
+                target->copy_reloc(&options, symtab, layout, object,
+                                   data_shndx, output_section, gsym, reloc);
               }
             else if (r_type == elfcpp::R_X86_64_64
                      && gsym->can_use_relative_reloc(false))
@@ -1004,8 +1048,8 @@ Target_x86_64::Scan::global(const General_options& options,
           {
             if (target->may_need_copy_reloc(gsym))
               {
-                target->copy_reloc(&options, symtab, layout, object, data_shndx,
-                                   output_section, gsym, reloc);
+                target->copy_reloc(&options, symtab, layout, object,
+                                   data_shndx, output_section, gsym, reloc);
               }
             else
               {
@@ -1026,18 +1070,19 @@ Target_x86_64::Scan::global(const General_options& options,
       {
         // The symbol requires a GOT entry.
         Output_data_got<64, false>* got = target->got_section(symtab, layout);
-        if (got->add_global(gsym))
-         {
+        if (gsym->final_value_is_known())
+          got->add_global(gsym);
+        else
+          {
             // If this symbol is not fully resolved, we need to add a
             // dynamic relocation for it.
-            if (!gsym->final_value_is_known())
+            Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+            if (gsym->is_from_dynobj() || gsym->is_preemptible())
+              got->add_global_with_rela(gsym, rela_dyn,
+                                        elfcpp::R_X86_64_GLOB_DAT);
+            else
               {
-                Reloc_section* rela_dyn = target->rela_dyn_section(layout);
-                if (gsym->is_from_dynobj()
-                   || gsym->is_preemptible())
-                  rela_dyn->add_global(gsym, elfcpp::R_X86_64_GLOB_DAT, got,
-                                       gsym->got_offset(), 0);
-                else
+                if (got->add_global(gsym))
                   {
                     rela_dyn->add_local(object, 0, elfcpp::R_X86_64_RELATIVE,
                                         got, gsym->got_offset(), 0);
@@ -1110,6 +1155,30 @@ Target_x86_64::Scan::global(const General_options& options,
        switch (r_type)
          {
           case elfcpp::R_X86_64_TLSGD:       // General-dynamic
+           if (optimized_type == tls::TLSOPT_NONE)
+             {
+                // Create a pair of GOT entries for the module index and
+                // dtv-relative offset.
+                Output_data_got<64, false>* got
+                    = target->got_section(symtab, layout);
+                got->add_global_tls_with_rela(gsym,
+                                              target->rela_dyn_section(layout),
+                                              elfcpp::R_X86_64_DTPMOD64,
+                                              elfcpp::R_X86_64_DTPOFF64);
+             }
+           else if (optimized_type == tls::TLSOPT_TO_IE)
+             {
+                // Create a GOT entry for the tp-relative offset.
+                Output_data_got<64, false>* got
+                    = target->got_section(symtab, layout);
+                got->add_global_with_rela(gsym,
+                                          target->rela_dyn_section(layout),
+                                          elfcpp::R_X86_64_TPOFF64);
+             }
+           else if (optimized_type != tls::TLSOPT_TO_LE)
+             unsupported_reloc_global(object, r_type, gsym);
+           break;
+
           case elfcpp::R_X86_64_GOTPC32_TLSDESC:
           case elfcpp::R_X86_64_TLSDESC_CALL:
            // FIXME: If not relaxing to LE, we need to generate
@@ -1119,25 +1188,40 @@ Target_x86_64::Scan::global(const General_options& options,
            break;
 
           case elfcpp::R_X86_64_TLSLD:       // Local-dynamic
+           if (optimized_type == tls::TLSOPT_NONE)
+             {
+               // Create a GOT entry for the module index.
+               Output_data_got<64, false>* got
+                   = target->got_section(symtab, layout);
+                got->add_global_tls_with_rela(gsym,
+                                              target->rela_dyn_section(layout),
+                                              elfcpp::R_X86_64_DTPMOD64);
+             }
+           else if (optimized_type != tls::TLSOPT_TO_LE)
+             unsupported_reloc_global(object, r_type, gsym);
+           break;
+
           case elfcpp::R_X86_64_DTPOFF32:
           case elfcpp::R_X86_64_DTPOFF64:
-           // FIXME: If not relaxing to LE, we need to generate a
-           // DTPMOD64 reloc.
-           if (optimized_type != tls::TLSOPT_TO_LE)
-             unsupported_reloc_global(object, r_type, gsym);
            break;
 
           case elfcpp::R_X86_64_GOTTPOFF:    // Initial-exec
-            // FIXME: If not relaxing to LE, we need to generate a
-            // TPOFF64 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<64, false>* got
+                   = target->got_section(symtab, layout);
+               got->add_global_with_rela(gsym,
+                                         target->rela_dyn_section(layout),
+                                         elfcpp::R_X86_64_TPOFF64);
+              }
+            else if (optimized_type != tls::TLSOPT_TO_LE)
               unsupported_reloc_global(object, r_type, gsym);
             break;
 
           case elfcpp::R_X86_64_TPOFF32:     // Local-exec
-           // FIXME: If generating a shared object, we need to copy
-           // this relocation into the object.
-           gold_assert(is_final);
+            if (parameters->output_is_shared())
+              unsupported_reloc_local(object, r_type);
            break;
 
           default:
@@ -1312,6 +1396,7 @@ Target_x86_64::Relocate::relocate(const Relocate_info<64, false>* relinfo,
       else
         {
           unsigned int r_sym = elfcpp::elf_r_sym<64>(rela.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;
@@ -1477,8 +1562,8 @@ Target_x86_64::Relocate::relocate(const Relocate_info<64, false>* relinfo,
     case elfcpp::R_X86_64_DTPOFF64:
     case elfcpp::R_X86_64_GOTTPOFF:         // Initial-exec
     case elfcpp::R_X86_64_TPOFF32:          // Local-exec
-      this->relocate_tls(relinfo, relnum, rela, r_type, gsym, psymval, view,
-                        address, view_size);
+      this->relocate_tls(relinfo, target, relnum, rela, r_type, gsym, psymval,
+                         view, address, view_size);
       break;
 
     case elfcpp::R_X86_64_SIZE32:
@@ -1497,6 +1582,7 @@ Target_x86_64::Relocate::relocate(const Relocate_info<64, false>* relinfo,
 
 inline void
 Target_x86_64::Relocate::relocate_tls(const Relocate_info<64, false>* relinfo,
+                                      Target_x86_64* target,
                                       size_t relnum,
                                       const elfcpp::Rela<64, false>& rela,
                                       unsigned int r_type,
@@ -1507,12 +1593,8 @@ Target_x86_64::Relocate::relocate_tls(const Relocate_info<64, false>* relinfo,
                                       off_t view_size)
 {
   Output_segment* tls_segment = relinfo->layout->tls_segment();
-  if (tls_segment == NULL)
-    {
-      gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
-                            _("TLS reloc but no TLS segment"));
-      return;
-    }
+
+  const Sized_relobj<64, false>* object = relinfo->object;
 
   elfcpp::Elf_types<64>::Elf_Addr value = psymval->value(relinfo->object, 0);
 
@@ -1528,11 +1610,42 @@ Target_x86_64::Relocate::relocate_tls(const Relocate_info<64, false>* relinfo,
     case elfcpp::R_X86_64_TLSDESC_CALL:
       if (optimized_type == tls::TLSOPT_TO_LE)
        {
+         gold_assert(tls_segment != NULL);
          this->tls_gd_to_le(relinfo, relnum, tls_segment,
                             rela, 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<64>(rela.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, rela, 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<64, false>::rel64(view, got_offset);
+              break;
+            }
+        }
       gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
                             _("unsupported reloc %u"), r_type);
       break;
@@ -1540,15 +1653,37 @@ Target_x86_64::Relocate::relocate_tls(const Relocate_info<64, false>* relinfo,
     case elfcpp::R_X86_64_TLSLD:            // Local-dynamic
       if (optimized_type == tls::TLSOPT_TO_LE)
         {
+          gold_assert(tls_segment != NULL);
          this->tls_ld_to_le(relinfo, relnum, tls_segment, rela, 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<64>(rela.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<64, false>::rel64(view, got_offset);
+          break;
+        }
       gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
                             _("unsupported reloc %u"), r_type);
       break;
 
     case elfcpp::R_X86_64_DTPOFF32:
+      gold_assert(tls_segment != NULL);
       if (optimized_type == tls::TLSOPT_TO_LE)
         value = value - (tls_segment->vaddr() + tls_segment->memsz());
       else
@@ -1557,6 +1692,7 @@ Target_x86_64::Relocate::relocate_tls(const Relocate_info<64, false>* relinfo,
       break;
 
     case elfcpp::R_X86_64_DTPOFF64:
+      gold_assert(tls_segment != NULL);
       if (optimized_type == tls::TLSOPT_TO_LE)
         value = value - (tls_segment->vaddr() + tls_segment->memsz());
       else
@@ -1567,11 +1703,32 @@ Target_x86_64::Relocate::relocate_tls(const Relocate_info<64, false>* relinfo,
     case elfcpp::R_X86_64_GOTTPOFF:         // Initial-exec
       if (optimized_type == tls::TLSOPT_TO_LE)
        {
+          gold_assert(tls_segment != NULL);
          Target_x86_64::Relocate::tls_ie_to_le(relinfo, relnum, tls_segment,
                                                 rela, 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() - target->got_size();
+            }
+          else
+            {
+              unsigned int r_sym = elfcpp::elf_r_sym<64>(rela.get_r_info());
+              gold_assert(object->local_has_got_offset(r_sym));
+              got_offset = (object->local_got_offset(r_sym)
+                            - target->got_size());
+            }
+          Relocate_functions<64, false>::rel64(view, got_offset);
+          break;
+        }
       gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
                             _("unsupported reloc type %u"),
                             r_type);
@@ -1584,6 +1741,41 @@ Target_x86_64::Relocate::relocate_tls(const Relocate_info<64, false>* relinfo,
     }
 }
 
+// Do a relocation in which we convert a TLS General-Dynamic to an
+// Initial-Exec.
+
+inline void
+Target_x86_64::Relocate::tls_gd_to_ie(const Relocate_info<64, false>* relinfo,
+                                      size_t relnum,
+                                      Output_segment* tls_segment,
+                                      const elfcpp::Rela<64, false>& rela,
+                                      unsigned int,
+                                      elfcpp::Elf_types<64>::Elf_Addr value,
+                                      unsigned char* view,
+                                      off_t view_size)
+{
+  // .byte 0x66; leaq foo@tlsgd(%rip),%rdi;
+  // .word 0x6666; rex64; call __tls_get_addr
+  // ==> movq %fs:0,%rax; addq x@gottpoff(%rip),%rax
+
+  tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, -4);
+  tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, 12);
+
+  tls::check_tls(relinfo, relnum, rela.get_r_offset(),
+                 (memcmp(view - 4, "\x66\x48\x8d\x3d", 4) == 0));
+  tls::check_tls(relinfo, relnum, rela.get_r_offset(),
+                 (memcmp(view + 4, "\x66\x66\x48\xe8", 4) == 0));
+
+  memcpy(view - 4, "\x64\x48\x8b\x04\x25\0\0\0\0\x48\x03\x05\0\0\0\0", 16);
+
+  value = value - (tls_segment->vaddr() + tls_segment->memsz());
+  Relocate_functions<64, false>::rela32(view + 8, value, 0);
+
+  // 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 General-Dynamic to a
 // Local-Exec.