From Cary Coutant: More shared library support, some refactorization.
authorIan Lance Taylor <iant@google.com>
Fri, 9 Nov 2007 19:45:08 +0000 (19:45 +0000)
committerIan Lance Taylor <iant@google.com>
Fri, 9 Nov 2007 19:45:08 +0000 (19:45 +0000)
gold/i386.cc
gold/output.cc
gold/symtab.cc
gold/symtab.h
gold/x86_64.cc

index d0b0b5128828635ae4184c21075d90d8fd0815e1..dcbb839ce1bb28ddd1cb91f8ae0dd0f3cdd66b36 100644 (file)
@@ -157,7 +157,8 @@ class Target_i386 : public Sized_target<32, false>
     // Return whether the static relocation needs to be applied.
     inline bool
     should_apply_static_reloc(const Sized_symbol<32>* gsym,
-                              bool is_pcrel,
+                              bool is_absolute_ref,
+                              bool is_function_call,
                               bool is_32bit);
 
     // Do a relocation.  Return false if the caller should not issue
@@ -256,6 +257,17 @@ class Target_i386 : public Sized_target<32, false>
   Reloc_section*
   rel_dyn_section(Layout*);
 
+  // Return true if the symbol may need a COPY relocation.
+  // References from an executable object to non-function symbols
+  // defined in a dynamic object may need a COPY relocation.
+  bool
+  may_need_copy_reloc(Symbol* gsym)
+  {
+    return (!parameters->output_is_shared()
+            && gsym->is_from_dynobj()
+            && gsym->type() != elfcpp::STT_FUNC);
+  }
+
   // Copy a relocation against a global symbol.
   void
   copy_reloc(const General_options*, Symbol_table*, Layout*,
@@ -771,8 +783,6 @@ Target_i386::Scan::local(const General_options&,
       break;
 
     case elfcpp::R_386_32:
-    case elfcpp::R_386_16:
-    case elfcpp::R_386_8:
       // If building a shared library (or a position-independent
       // executable), we need to create a dynamic relocation for
       // this location. The relocation applied at link time will
@@ -782,15 +792,24 @@ Target_i386::Scan::local(const General_options&,
       if (parameters->output_is_position_independent())
         {
           Reloc_section* rel_dyn = target->rel_dyn_section(layout);
-          if (r_type == elfcpp::R_386_32)
-            rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE, data_shndx,
-                               reloc.get_r_offset());
-          else
-            {
-              unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
-              rel_dyn->add_local(object, r_sym, r_type, data_shndx,
-                                 reloc.get_r_offset());
-            }
+          rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE, data_shndx,
+                             reloc.get_r_offset());
+        }
+      break;
+
+    case elfcpp::R_386_16:
+    case elfcpp::R_386_8:
+      // If building a shared library (or a position-independent
+      // executable), we need to create a dynamic relocation for
+      // this location. Because the addend needs to remain in the
+      // data section, we need to be careful not to apply this
+      // relocation statically.
+      if (parameters->output_is_position_independent())
+        {
+          Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+          unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
+          rel_dyn->add_local(object, r_sym, r_type, data_shndx,
+                             reloc.get_r_offset());
         }
       break;
 
@@ -951,71 +970,67 @@ Target_i386::Scan::global(const General_options& options,
       break;
 
     case elfcpp::R_386_32:
-    case elfcpp::R_386_PC32:
     case elfcpp::R_386_16:
-    case elfcpp::R_386_PC16:
     case elfcpp::R_386_8:
-    case elfcpp::R_386_PC8:
       {
-        bool is_pcrel = (r_type == elfcpp::R_386_PC32
-                        || r_type == elfcpp::R_386_PC16
-                        || r_type == elfcpp::R_386_PC8);
-
-        if (gsym->is_from_dynobj()
-            || (parameters->output_is_shared()
-                && gsym->is_preemptible()))
-         {
-           // (a) This symbol is defined in a dynamic object.  If it is a
-           // function, we make a PLT entry.  Otherwise we need to
-           // either generate a COPY reloc or copy this reloc.
-           // (b) We are building a shared object and this symbol is
-           // preemptible. If it is a function, we make a PLT entry.
-           // Otherwise, we copy the reloc.
-           if (gsym->type() == elfcpp::STT_FUNC)
-             {
-               target->make_plt_entry(symtab, layout, gsym);
-  
-               // If this is not a PC relative reference, then we may
-               // be taking the address of the function.  In that case
-               // we need to set the entry in the dynamic symbol table
-               // to the address of the PLT entry. We will also need to
-               // create a dynamic relocation.
-               if (!is_pcrel)
-                 {
-                   if (gsym->is_from_dynobj())
-                     gsym->set_needs_dynsym_value();
-                    if (parameters->output_is_position_independent())
-                      {
-                        Reloc_section* rel_dyn =
-                          target->rel_dyn_section(layout);
-                        rel_dyn->add_global(gsym, r_type, object, data_shndx, 
-                                            reloc.get_r_offset());
-                      }
-                 }
-             }
-           else if (parameters->output_is_shared())
-             {
-               // We do not make COPY relocs in shared objects.
+        // Make a PLT entry if necessary.
+        if (gsym->needs_plt_entry())
+          {
+            target->make_plt_entry(symtab, layout, gsym);
+            // Since this is not a PC-relative relocation, we may be
+            // taking the address of a function. In that case we need to
+            // set the entry in the dynamic symbol table to the address of
+            // the PLT entry.
+            if (gsym->is_from_dynobj())
+              gsym->set_needs_dynsym_value();
+          }
+        // Make a dynamic relocation if necessary.
+        if (gsym->needs_dynamic_reloc(true, false))
+          {
+            if (target->may_need_copy_reloc(gsym))
+              {
+               target->copy_reloc(&options, symtab, layout, object, data_shndx,
+                                   gsym, reloc);
+              }
+            else if (r_type == elfcpp::R_386_32
+                     && gsym->can_use_relative_reloc(false))
+              {
+                Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+                rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE, data_shndx,
+                                   reloc.get_r_offset());
+              }
+            else
+              {
                 Reloc_section* rel_dyn = target->rel_dyn_section(layout);
                 rel_dyn->add_global(gsym, r_type, object, data_shndx, 
                                     reloc.get_r_offset());
-             }
-           else
-             target->copy_reloc(&options, symtab, layout, object, data_shndx,
-                                gsym, reloc);
-         }
-        else if (!is_pcrel && parameters->output_is_position_independent())
+              }
+          }
+      }
+      break;
+
+    case elfcpp::R_386_PC32:
+    case elfcpp::R_386_PC16:
+    case elfcpp::R_386_PC8:
+      {
+        // Make a PLT entry if necessary.
+        if (gsym->needs_plt_entry())
+          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))
           {
-            // This is not a PC-relative reference, so we need to generate
-            // a dynamic relocation. At this point, we know the symbol
-            // is not preemptible, so we can use the RELATIVE relocation.
-            Reloc_section* rel_dyn = target->rel_dyn_section(layout);
-            if (r_type == elfcpp::R_386_32)
-              rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE, data_shndx,
-                                 reloc.get_r_offset());
+            if (target->may_need_copy_reloc(gsym))
+              {
+               target->copy_reloc(&options, symtab, layout, object, data_shndx,
+                                   gsym, reloc);
+              }
             else
-              rel_dyn->add_global(gsym, r_type, object, data_shndx, 
-                                  reloc.get_r_offset());
+              {
+                Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+                rel_dyn->add_global(gsym, r_type, object, data_shndx, 
+                                    reloc.get_r_offset());
+              }
           }
       }
       break;
@@ -1031,8 +1046,16 @@ Target_i386::Scan::global(const General_options& options,
             if (!gsym->final_value_is_known())
               {
                 Reloc_section* rel_dyn = target->rel_dyn_section(layout);
-                rel_dyn->add_global(gsym, elfcpp::R_386_GLOB_DAT, got,
-                                    gsym->got_offset());
+                if (gsym->is_preemptible())
+                    rel_dyn->add_global(gsym, elfcpp::R_386_GLOB_DAT, got,
+                                        gsym->got_offset());
+                else
+                  {
+                    rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE,
+                                       got, gsym->got_offset());
+                    // Make sure we write the link-time value to the GOT.
+                    gsym->set_needs_value_in_got();
+                  }
               }
           }
       }
@@ -1246,36 +1269,22 @@ Target_i386::do_finalize_sections(Layout* layout)
 
 inline bool
 Target_i386::Relocate::should_apply_static_reloc(const Sized_symbol<32>* gsym,
-                                                 bool is_pcrel,
+                                                 bool is_absolute_ref,
+                                                 bool is_function_call,
                                                  bool is_32bit)
 {
-  // For local symbols, return FALSE if a non-RELATIVE dynamic
-  // relocation was created; return TRUE otherwise.
+  // For local symbols, we will have created a non-RELATIVE dynamic
+  // relocation only if (a) the output is position independent,
+  // (b) the relocation is absolute (not pc- or segment-relative), and
+  // (c) the relocation is not 32 bits wide.
   if (gsym == NULL)
-    return (!parameters->output_is_position_independent() || is_32bit);
-
-  // For global symbols, mimic the logic in Scan::global()
-  // to decide whether a non-RELATIVE dynamic relocation was
-  // created.
-  // FIXME: This is ugly. Try to refactor this logic so it can be
-  // shared by Scan::global() and Relocate::relocate().
-  if (gsym->is_from_dynobj()
-      || (parameters->output_is_shared()
-          && gsym->is_preemptible()))
-    {
-      if (gsym->type() == elfcpp::STT_FUNC)
-       {
-         if (!is_pcrel && parameters->output_is_position_independent())
-            return false;
-       }
-      else
-       return false;
-    }
-  else if (!is_pcrel && parameters->output_is_position_independent())
-    return is_32bit;
+    return !(parameters->output_is_position_independent()
+             && is_absolute_ref
+             && !is_32bit);
 
-  // For all other cases, return TRUE 
-  return true;
+  // For global symbols, we use the same helper routines used in the scan pass.
+  return !(gsym->needs_dynamic_reloc(is_absolute_ref, is_function_call)
+           && !gsym->can_use_relative_reloc(is_function_call));
 }
 
 // Perform a relocation.
@@ -1355,33 +1364,45 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
       break;
 
     case elfcpp::R_386_32:
-      if (should_apply_static_reloc(gsym, false, true))
+      if (should_apply_static_reloc(gsym, true, false, true))
         Relocate_functions<32, false>::rel32(view, object, psymval);
       break;
 
     case elfcpp::R_386_PC32:
-      if (should_apply_static_reloc(gsym, true, true))
-        Relocate_functions<32, false>::pcrel32(view, object, psymval, address);
+      {
+        bool is_function_call = (gsym != NULL
+                                 && gsym->type() == elfcpp::STT_FUNC);
+        if (should_apply_static_reloc(gsym, false, is_function_call, true))
+          Relocate_functions<32, false>::pcrel32(view, object, psymval, address);
+      }
       break;
 
     case elfcpp::R_386_16:
-      if (should_apply_static_reloc(gsym, false, false))
+      if (should_apply_static_reloc(gsym, true, false, false))
         Relocate_functions<32, false>::rel16(view, object, psymval);
       break;
 
     case elfcpp::R_386_PC16:
-      if (should_apply_static_reloc(gsym, true, false))
-        Relocate_functions<32, false>::pcrel16(view, object, psymval, address);
+      {
+        bool is_function_call = (gsym != NULL
+                                 && gsym->type() == elfcpp::STT_FUNC);
+        if (should_apply_static_reloc(gsym, false, is_function_call, false))
+          Relocate_functions<32, false>::pcrel32(view, object, psymval, address);
+      }
       break;
 
     case elfcpp::R_386_8:
-      if (should_apply_static_reloc(gsym, false, false))
+      if (should_apply_static_reloc(gsym, true, false, false))
         Relocate_functions<32, false>::rel8(view, object, psymval);
       break;
 
     case elfcpp::R_386_PC8:
-      if (should_apply_static_reloc(gsym, true, false))
-        Relocate_functions<32, false>::pcrel8(view, object, psymval, address);
+      {
+        bool is_function_call = (gsym != NULL
+                                 && gsym->type() == elfcpp::STT_FUNC);
+        if (should_apply_static_reloc(gsym, false, is_function_call, false))
+          Relocate_functions<32, false>::pcrel32(view, object, psymval, address);
+      }
       break;
 
     case elfcpp::R_386_PLT32:
index ca990976e3476df9142463bf3bf8c247abf8adf7..b1917257f2bdc8ffed4acbd1f606217737fcc6d3 100644 (file)
@@ -684,8 +684,11 @@ Output_data_got<size, big_endian>::Got_entry::write(unsigned char* pov) const
        // If the symbol is resolved locally, we need to write out its
        // value.  Otherwise we just write zero.  The target code is
        // responsible for creating a relocation entry to fill in the
-       // value at runtime.
-       if (gsym->final_value_is_known())
+       // value at runtime. For non-preemptible symbols in a shared
+       // library, the target will need to record whether or not the
+       // value should be written (e.g., it may use a RELATIVE
+       // relocation type).
+       if (gsym->final_value_is_known() || gsym->needs_value_in_got())
          {
            Sized_symbol<size>* sgsym;
            // This cast is a bit ugly.  We don't want to put a
index 3c2a9761a09a42fd266745a49cf2972468c4c4f3..e3face79df711ee3f6d01894bc8b541bb2efe7a7 100644 (file)
@@ -67,6 +67,7 @@ Symbol::init_fields(const char* name, const char* version,
   this->has_plt_offset_ = false;
   this->has_warning_ = false;
   this->is_copied_from_dynobj_ = false;
+  this->needs_value_in_got_ = false;
 }
 
 // Initialize the fields in the base class Symbol for SYM in OBJECT.
index 38320f9d8f2b4774ee76c3fa214950321914af5b..2f43c4b7f92cc2a84f2ce3edb8ec0f47d2b5e65a 100644 (file)
@@ -408,9 +408,75 @@ class Symbol
     return (this->visibility_ != elfcpp::STV_INTERNAL
             && this->visibility_ != elfcpp::STV_HIDDEN
             && this->visibility_ != elfcpp::STV_PROTECTED
+            && parameters->output_is_shared()
            && !parameters->symbolic());
   }
 
+  // Return true if this symbol is a function that needs a PLT entry.
+  // If the symbol is defined in a dynamic object or if it is subject
+  // to pre-emption, we need to make a PLT entry.
+  bool
+  needs_plt_entry() const
+  {
+    return (this->type() == elfcpp::STT_FUNC
+            && (this->is_from_dynobj() || this->is_preemptible()));
+  }
+
+  // Given a direct absolute or pc-relative static relocation against
+  // the global symbol, this function returns whether a dynamic relocation
+  // is needed.
+
+  bool
+  needs_dynamic_reloc(bool is_absolute_ref, bool is_function_call) const
+  {
+    // An absolute reference within a position-independent output file
+    // will need a dynamic relocaion.
+    if (is_absolute_ref && parameters->output_is_position_independent())
+      return true;
+
+    // A function call that can branch to a local PLT entry does not need
+    // a dynamic relocation.
+    if (is_function_call && this->has_plt_offset())
+      return false;
+
+    // A reference to any PLT entry in a non-position-independent executable
+    // does not need a dynamic relocation.
+    if (!parameters->output_is_position_independent()
+        && this->has_plt_offset())
+      return false;
+
+    // A reference to a symbol defined in a dynamic object or to a
+    // symbol that is preemptible will need a dynamic relocation.
+    if (this->is_from_dynobj() || this->is_preemptible())
+      return true;
+
+    // For all other cases, return FALSE.
+    return false;
+  }
+
+  // Given a direct absolute static relocation against
+  // the global symbol, where a dynamic relocation is needed, this
+  // function returns whether a relative dynamic relocation can be used.
+  // The caller must determine separately whether the static relocation
+  // is compatible with a relative relocation.
+
+  bool
+  can_use_relative_reloc(bool is_function_call) const
+  {
+    // A function call that can branch to a local PLT entry can
+    // use a RELATIVE relocation.
+    if (is_function_call && this->has_plt_offset())
+      return true;
+
+    // A reference to a symbol defined in a dynamic object or to a
+    // symbol that is preemptible can not use a RELATIVE relocaiton.
+    if (this->is_from_dynobj() || this->is_preemptible())
+      return false;
+
+    // For all other cases, return TRUE.
+    return true;
+  }
+
   // Return whether there should be a warning for references to this
   // symbol.
   bool
@@ -433,6 +499,19 @@ class Symbol
   set_is_copied_from_dynobj()
   { this->is_copied_from_dynobj_ = true; }
 
+  // Mark this symbol as needing its value written to the GOT even when
+  // the value is subject to dynamic relocation (e.g., when the target
+  // uses a RELATIVE relocation for the GOT entry).
+  void
+  set_needs_value_in_got()
+  { this->needs_value_in_got_ = true; }
+
+  // Return whether this symbol needs its value written to the GOT even
+  // when the value is subject to dynamic relocation.
+  bool
+  needs_value_in_got() const
+  { return this->needs_value_in_got_; }
+
  protected:
   // Instances of this class should always be created at a specific
   // size.
@@ -587,6 +666,9 @@ class Symbol
   // True if we are using a COPY reloc for this symbol, so that the
   // real definition lives in a dynamic object.
   bool is_copied_from_dynobj_ : 1;
+  // True if the static value should be written to the GOT even
+  // when the final value is subject to dynamic relocation.
+  bool needs_value_in_got_ : 1;
 };
 
 // The parts of a symbol which are size specific.  Using a template
index 3be64cfed5b5850874e717ee5cfcac3e86987bfb..5bc74bf29a87dcffc98b532f9c8e092ce94deddd 100644 (file)
@@ -250,6 +250,17 @@ class Target_x86_64 : public Sized_target<64, false>
   Reloc_section*
   rela_dyn_section(Layout*);
 
+  // Return true if the symbol may need a COPY relocation.
+  // References from an executable object to non-function symbols
+  // defined in a dynamic object may need a COPY relocation.
+  bool
+  may_need_copy_reloc(Symbol* gsym)
+  {
+    return (!parameters->output_is_shared()
+            && gsym->is_from_dynobj()
+            && gsym->type() != elfcpp::STT_FUNC);
+  }
+
   // Copy a relocation against a global symbol.
   void
   copy_reloc(const General_options*, Symbol_table*, Layout*,
@@ -734,6 +745,20 @@ Target_x86_64::Scan::local(const General_options&,
       break;
 
     case elfcpp::R_X86_64_64:
+      // If building a shared library (or a position-independent
+      // executable), we need to create a dynamic relocation for
+      // this location. The relocation applied at link time will
+      // apply the link-time value, so we flag the location with
+      // an R_386_RELATIVE relocation so the dynamic loader can
+      // relocate it easily.
+      if (parameters->output_is_position_independent())
+        {
+          Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+          rela_dyn->add_local(object, 0, elfcpp::R_X86_64_RELATIVE,
+                              data_shndx, reloc.get_r_offset(), 0);
+        }
+      break;
+
     case elfcpp::R_X86_64_32:
     case elfcpp::R_X86_64_32S:
     case elfcpp::R_X86_64_16:
@@ -747,16 +772,10 @@ Target_x86_64::Scan::local(const General_options&,
       if (parameters->output_is_position_independent())
         {
           Reloc_section* rela_dyn = target->rela_dyn_section(layout);
-          if (r_type == elfcpp::R_X86_64_64)
-            rela_dyn->add_local(object, 0, elfcpp::R_X86_64_RELATIVE,
-                                data_shndx, reloc.get_r_offset(), 0);
-          else
-            {
-              unsigned int r_sym = elfcpp::elf_r_sym<64>(reloc.get_r_info());
-              rela_dyn->add_local(object, r_sym, r_type, data_shndx,
-                                  reloc.get_r_offset(),
-                                  reloc.get_r_addend());
-            }
+          unsigned int r_sym = elfcpp::elf_r_sym<64>(reloc.get_r_info());
+          rela_dyn->add_local(object, r_sym, r_type, data_shndx,
+                              reloc.get_r_offset(),
+                              reloc.get_r_addend());
         }
       break;
 
@@ -915,81 +934,75 @@ Target_x86_64::Scan::global(const General_options& options,
       break;
 
     case elfcpp::R_X86_64_64:
-    case elfcpp::R_X86_64_PC64:
     case elfcpp::R_X86_64_32:
     case elfcpp::R_X86_64_32S:
-    case elfcpp::R_X86_64_PC32:
     case elfcpp::R_X86_64_16:
-    case elfcpp::R_X86_64_PC16:
     case elfcpp::R_X86_64_8:
-    case elfcpp::R_X86_64_PC8:
       {
-        bool is_pcrel = (r_type == elfcpp::R_X86_64_PC64
-                        || r_type == elfcpp::R_X86_64_PC32
-                        || r_type == elfcpp::R_X86_64_PC16
-                        || r_type == elfcpp::R_X86_64_PC8);
-
-        if (gsym->is_from_dynobj()
-            || (parameters->output_is_shared()
-                && gsym->is_preemptible()))
-         {
-           // (a) This symbol is defined in a dynamic object.  If it is a
-           // function, we make a PLT entry.  Otherwise we need to
-           // either generate a COPY reloc or copy this reloc.
-           // (b) We are building a shared object and this symbol is
-           // preemptible. If it is a function, we make a PLT entry.
-           // Otherwise, we copy the reloc.
-           if (gsym->type() == elfcpp::STT_FUNC)
-             {
-               target->make_plt_entry(symtab, layout, gsym);
-
-               // If this is not a PC relative reference, then we may
-               // be taking the address of the function.  In that case
-               // we need to set the entry in the dynamic symbol table
-               // to the address of the PLT entry. We will also need to
-               // create a dynamic relocation.
-               if (!is_pcrel)
-                 {
-                   if (gsym->is_from_dynobj())
-                     gsym->set_needs_dynsym_value();
-                    if (parameters->output_is_position_independent())
-                      {
-                        Reloc_section* rela_dyn =
-                          target->rela_dyn_section(layout);
-                        rela_dyn->add_global(gsym, r_type, object, data_shndx, 
-                                            reloc.get_r_offset(),
-                                            reloc.get_r_addend());
-                      }
-                 }
-             }
-           else if (parameters->output_is_shared())
-             {
-               // We do not make COPY relocs in shared objects.
+        // Make a PLT entry if necessary.
+        if (gsym->needs_plt_entry())
+          {
+            target->make_plt_entry(symtab, layout, gsym);
+            // Since this is not a PC-relative relocation, we may be
+            // taking the address of a function. In that case we need to
+            // set the entry in the dynamic symbol table to the address of
+            // the PLT entry.
+            if (gsym->is_from_dynobj())
+              gsym->set_needs_dynsym_value();
+          }
+        // Make a dynamic relocation if necessary.
+        if (gsym->needs_dynamic_reloc(true, false))
+          {
+            if (target->may_need_copy_reloc(gsym))
+              {
+                target->copy_reloc(&options, symtab, layout, object, data_shndx,
+                                   gsym, reloc);
+              }
+            else if (r_type == elfcpp::R_X86_64_64
+                     && gsym->can_use_relative_reloc(false))
+              {
+                Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+                rela_dyn->add_local(object, 0, elfcpp::R_X86_64_RELATIVE,
+                                    data_shndx,
+                                    reloc.get_r_offset(), 0);
+              }
+            else
+              {
                 Reloc_section* rela_dyn = target->rela_dyn_section(layout);
                 rela_dyn->add_global(gsym, r_type, object, data_shndx, 
                                      reloc.get_r_offset(),
                                      reloc.get_r_addend());
-             }
-           else
-             target->copy_reloc(&options, symtab, layout, object, data_shndx,
-                                gsym, reloc);
-         }
-        else if (!is_pcrel && parameters->output_is_position_independent())
+              }
+          }
+      }
+      break;
+
+    case elfcpp::R_X86_64_PC64:
+    case elfcpp::R_X86_64_PC32:
+    case elfcpp::R_X86_64_PC16:
+    case elfcpp::R_X86_64_PC8:
+      {
+        // Make a PLT entry if necessary.
+        if (gsym->needs_plt_entry())
+          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(true, is_function_call))
           {
-            // This is not a PC-relative reference, so we need to generate
-            // a dynamic relocation. At this point, we know the symbol
-            // is not preemptible, so we can use the RELATIVE relocation.
-            Reloc_section* rela_dyn = target->rela_dyn_section(layout);
-            if (r_type == elfcpp::R_X86_64_64)
-              rela_dyn->add_local(object, 0, elfcpp::R_X86_64_RELATIVE,
-                                  data_shndx,
-                                  reloc.get_r_offset(), 0);
+            if (target->may_need_copy_reloc(gsym))
+              {
+                target->copy_reloc(&options, symtab, layout, object, data_shndx,
+                                   gsym, reloc);
+              }
             else
-              rela_dyn->add_global(gsym, r_type, object, data_shndx, 
-                                   reloc.get_r_offset(),
-                                   reloc.get_r_addend());
+              {
+                Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+                rela_dyn->add_global(gsym, r_type, object, data_shndx, 
+                                     reloc.get_r_offset(),
+                                     reloc.get_r_addend());
+              }
           }
-       }
+      }
       break;
 
     case elfcpp::R_X86_64_GOT64:
@@ -1007,8 +1020,16 @@ Target_x86_64::Scan::global(const General_options& options,
             if (!gsym->final_value_is_known())
               {
                 Reloc_section* rela_dyn = target->rela_dyn_section(layout);
-                rela_dyn->add_global(gsym, elfcpp::R_X86_64_GLOB_DAT, got,
-                                     gsym->got_offset(), 0);
+                if (gsym->is_preemptible())
+                  rela_dyn->add_global(gsym, elfcpp::R_X86_64_GLOB_DAT, got,
+                                       gsym->got_offset(), 0);
+                else
+                  {
+                    rela_dyn->add_local(object, 0, elfcpp::R_X86_64_RELATIVE,
+                                        got, gsym->got_offset(), 0);
+                    // Make sure we write the link-time value to the GOT.
+                    gsym->set_needs_value_in_got();
+                  }
               }
           }
         // For GOTPLT64, we also need a PLT entry (but only if the