From Cary Coutant: Fix handling of RELATIVE RELA relocs.
authorIan Lance Taylor <iant@google.com>
Fri, 7 Dec 2007 19:32:59 +0000 (19:32 +0000)
committerIan Lance Taylor <iant@google.com>
Fri, 7 Dec 2007 19:32:59 +0000 (19:32 +0000)
gold/i386.cc
gold/output.cc
gold/output.h
gold/symtab.cc
gold/symtab.h
gold/x86_64.cc

index 071940b..a03893c 100644 (file)
@@ -811,8 +811,10 @@ Target_i386::Scan::local(const General_options&,
       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());
+          unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
+          rel_dyn->add_local_relative(object, r_sym, elfcpp::R_386_RELATIVE,
+                                      output_section, data_shndx,
+                                      reloc.get_r_offset());
         }
       break;
 
@@ -860,8 +862,11 @@ Target_i386::Scan::local(const General_options&,
             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,
-                                   got, object->local_got_offset(r_sym));
+                unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
+                rel_dyn->add_local_relative(object, r_sym,
+                                            elfcpp::R_386_RELATIVE,
+                                            got,
+                                            object->local_got_offset(r_sym));
               }
           }
       }
@@ -955,9 +960,12 @@ Target_i386::Scan::local(const General_options&,
                    && 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());
+                    unsigned int r_sym
+                        = elfcpp::elf_r_sym<32>(reloc.get_r_info());
+                    rel_dyn->add_local_relative(object, r_sym,
+                                                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
@@ -1070,9 +1078,9 @@ Target_i386::Scan::global(const General_options& options,
                      && 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,
-                                   output_section, data_shndx,
-                                   reloc.get_r_offset());
+                rel_dyn->add_global_relative(gsym, elfcpp::R_386_RELATIVE,
+                                             output_section, object,
+                                             data_shndx, reloc.get_r_offset());
               }
             else
               {
@@ -1136,12 +1144,8 @@ Target_i386::Scan::global(const General_options& options,
             else
               {
                 if (got->add_global(gsym))
-                  {
-                    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();
-                  }
+                  rel_dyn->add_global_relative(gsym, elfcpp::R_386_RELATIVE,
+                                               got, gsym->got_offset());
               }
           }
       }
@@ -1264,9 +1268,10 @@ Target_i386::Scan::global(const General_options& options,
                    && 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());
+                    rel_dyn->add_global_relative(gsym, elfcpp::R_386_RELATIVE,
+                                                 output_section, object,
+                                                 data_shndx,
+                                                 reloc.get_r_offset());
                  }
                // Create a GOT entry for the tp-relative offset.
                 Output_data_got<32, false>* got
index 7fd901c..922a9e5 100644 (file)
@@ -512,13 +512,14 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
     Symbol* gsym,
     unsigned int type,
     Output_data* od,
-    Address address)
+    Address address,
+    bool is_relative)
   : address_(address), local_sym_index_(GSYM_CODE), type_(type),
-    shndx_(INVALID_CODE)
+    is_relative_(is_relative), shndx_(INVALID_CODE)
 {
   this->u1_.gsym = gsym;
   this->u2_.od = od;
-  if (dynamic)
+  if (dynamic && !is_relative)
     gsym->set_needs_dynsym_entry();
 }
 
@@ -528,14 +529,15 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
     unsigned int type,
     Relobj* relobj,
     unsigned int shndx,
-    Address address)
+    Address address,
+    bool is_relative)
   : address_(address), local_sym_index_(GSYM_CODE), type_(type),
-    shndx_(shndx)
+    is_relative_(is_relative), shndx_(shndx)
 {
   gold_assert(shndx != INVALID_CODE);
   this->u1_.gsym = gsym;
   this->u2_.relobj = relobj;
-  if (dynamic)
+  if (dynamic && !is_relative)
     gsym->set_needs_dynsym_entry();
 }
 
@@ -547,15 +549,16 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
     unsigned int local_sym_index,
     unsigned int type,
     Output_data* od,
-    Address address)
+    Address address,
+    bool is_relative)
   : address_(address), local_sym_index_(local_sym_index), type_(type),
-    shndx_(INVALID_CODE)
+    is_relative_(is_relative), 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)
+  if (dynamic && !is_relative)
     relobj->set_needs_output_dynsym_entry(local_sym_index);
 }
 
@@ -565,16 +568,17 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
     unsigned int local_sym_index,
     unsigned int type,
     unsigned int shndx,
-    Address address)
+    Address address,
+    bool is_relative)
   : address_(address), local_sym_index_(local_sym_index), type_(type),
-    shndx_(shndx)
+    is_relative_(is_relative), 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)
+  if (dynamic && !is_relative)
     relobj->set_needs_output_dynsym_entry(local_sym_index);
 }
 
@@ -587,7 +591,7 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
     Output_data* od,
     Address address)
   : address_(address), local_sym_index_(SECTION_CODE), type_(type),
-    shndx_(INVALID_CODE)
+    is_relative_(false), shndx_(INVALID_CODE)
 {
   this->u1_.os = os;
   this->u2_.od = od;
@@ -603,7 +607,7 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
     unsigned int shndx,
     Address address)
   : address_(address), local_sym_index_(SECTION_CODE), type_(type),
-    shndx_(shndx)
+    is_relative_(false), shndx_(shndx)
 {
   gold_assert(shndx != INVALID_CODE);
   this->u1_.os = os;
@@ -685,8 +689,8 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::write_rel(
   else if (this->u2_.od != NULL)
     address += this->u2_.od->address();
   wr->put_r_offset(address);
-  wr->put_r_info(elfcpp::elf_r_info<size>(this->get_symbol_index(),
-                                         this->type_));
+  unsigned int sym_index = this->is_relative_ ? 0 : this->get_symbol_index();
+  wr->put_r_info(elfcpp::elf_r_info<size>(sym_index, this->type_));
 }
 
 // Write out a Rel relocation.
@@ -700,6 +704,24 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::write(
   this->write_rel(&orel);
 }
 
+// Get the value of the symbol referred to by a Rel relocation.
+
+template<bool dynamic, int size, bool big_endian>
+typename elfcpp::Elf_types<size>::Elf_Addr
+Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::symbol_value() const
+{
+  if (this->local_sym_index_ == GSYM_CODE)
+    {
+      const Sized_symbol<size>* sym;
+      sym = static_cast<const Sized_symbol<size>*>(this->u1_.gsym);
+      return sym->value();
+    }
+  gold_assert(this->local_sym_index_ != SECTION_CODE
+              && this->local_sym_index_ != INVALID_CODE);
+  const Sized_relobj<size, big_endian>* relobj = this->u1_.relobj;
+  return relobj->local_symbol_value(this->local_sym_index_);
+}
+
 // Write out a Rela relocation.
 
 template<bool dynamic, int size, bool big_endian>
@@ -709,7 +731,10 @@ Output_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian>::write(
 {
   elfcpp::Rela_write<size, big_endian> orel(pov);
   this->rel_.write_rel(&orel);
-  orel.put_r_addend(this->addend_);
+  Addend addend = this->addend_;
+  if (rel_.is_relative())
+    addend += rel_.symbol_value();
+  orel.put_r_addend(addend);
 }
 
 // Output_data_reloc_base methods.
@@ -775,24 +800,16 @@ Output_data_got<size, big_endian>::Got_entry::write(unsigned char* pov) const
     {
     case GSYM_CODE:
       {
+       // If the symbol is resolved locally, we need to write out the
+       // link-time value, which will be relocated dynamically by a
+       // RELATIVE relocation.
        Symbol* gsym = this->u_.gsym;
-
-       // 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. 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
-           // virtual method in Symbol, because we want Symbol to be
-           // as small as possible.
-           sgsym = static_cast<Sized_symbol<size>*>(gsym);
-           val = sgsym->value();
-         }
+       Sized_symbol<size>* sgsym;
+       // This cast is a bit ugly.  We don't want to put a
+       // virtual method in Symbol, because we want Symbol to be
+       // as small as possible.
+       sgsym = static_cast<Sized_symbol<size>*>(gsym);
+       val = sgsym->value();
       }
       break;
 
index 6c80d0a..172be49 100644 (file)
@@ -703,20 +703,20 @@ 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, bool is_relative);
 
   Output_reloc(Symbol* gsym, unsigned int type, Relobj* relobj,
-              unsigned int shndx, Address address);
+              unsigned int shndx, Address address, bool is_relative);
 
   // 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);
+              Output_data* od, Address address, bool is_relative);
 
   Output_reloc(Sized_relobj<size, big_endian>* relobj,
               unsigned int local_sym_index, unsigned int type,
-              unsigned int shndx, Address address);
+              unsigned int shndx, Address address, bool is_relative);
 
   // A reloc against the STT_SECTION symbol of an output section.
 
@@ -726,6 +726,16 @@ class Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>
   Output_reloc(Output_section* os, unsigned int type, Relobj* relobj,
               unsigned int shndx, Address address);
 
+  // Return TRUE if this is a RELATIVE relocation.
+  bool
+  is_relative() const
+  { return this->is_relative_; }
+
+  // Get the value of the symbol referred to by a Rel relocation.
+
+  Address
+  symbol_value() const;
+
   // Write the reloc entry to an output view.
   void
   write(unsigned char* pov) const;
@@ -780,7 +790,9 @@ class Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>
   // for a global symbol, or INVALID_CODE for an uninitialized value.
   unsigned int local_sym_index_;
   // The reloc type--a processor specific code.
-  unsigned int type_;
+  unsigned int type_ : 31;
+  // True if the relocation is a RELATIVE relocation.
+  bool is_relative_ : 1;
   // If the reloc address is an input section in an object, the
   // section index.  This is INVALID_CODE if the reloc address is
   // specified in some other way.
@@ -805,31 +817,31 @@ class Output_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian>
   // A reloc against a global symbol.
 
   Output_reloc(Symbol* gsym, unsigned int type, Output_data* od,
-              Address address, Addend addend)
-    : rel_(gsym, type, od, address), addend_(addend)
+              Address address, Addend addend, bool is_relative)
+    : rel_(gsym, type, od, address, is_relative), addend_(addend)
   { }
 
   Output_reloc(Symbol* gsym, unsigned int type, Relobj* relobj,
-              unsigned int shndx, Address address, Addend addend)
-    : rel_(gsym, type, relobj, shndx, address), addend_(addend)
+              unsigned int shndx, Address address, Addend addend,
+              bool is_relative)
+    : rel_(gsym, type, relobj, shndx, address, is_relative), addend_(addend)
   { }
 
   // 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,
-              Addend addend)
-    : rel_(relobj, local_sym_index, type, od, address), addend_(addend)
+              unsigned int local_sym_index, unsigned int type,
+              Output_data* od, Address address,
+              Addend addend, bool is_relative)
+    : rel_(relobj, local_sym_index, type, od, address, is_relative),
+      addend_(addend)
   { }
 
   Output_reloc(Sized_relobj<size, big_endian>* relobj,
-              unsigned int local_sym_index,
-              unsigned int type,
-              unsigned int shndx,
-              Address address,
-              Addend addend)
-    : rel_(relobj, local_sym_index, type, shndx, address),
+              unsigned int local_sym_index, unsigned int type,
+              unsigned int shndx, Address address,
+              Addend addend, bool is_relative)
+    : rel_(relobj, local_sym_index, type, shndx, address, is_relative),
       addend_(addend)
   { }
 
@@ -928,12 +940,27 @@ class Output_data_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>
 
   void
   add_global(Symbol* gsym, unsigned int type, Output_data* od, Address address)
-  { this->add(od, Output_reloc_type(gsym, type, od, address)); }
+  { this->add(od, Output_reloc_type(gsym, type, od, address, false)); }
 
   void
   add_global(Symbol* gsym, unsigned int type, Output_data* od, Relobj* relobj,
             unsigned int shndx, Address address)
-  { this->add(od, Output_reloc_type(gsym, type, relobj, shndx, address)); }
+  { this->add(od, Output_reloc_type(gsym, type, relobj, shndx, address,
+                                    false)); }
+
+  // Add a RELATIVE reloc against a global symbol.  The final relocation
+  // will not reference the symbol.
+
+  void
+  add_global_relative(Symbol* gsym, unsigned int type, Output_data* od,
+                      Address address)
+  { this->add(od, Output_reloc_type(gsym, type, od, address, true)); }
+
+  void
+  add_global_relative(Symbol* gsym, unsigned int type, Output_data* od,
+                      Relobj* relobj, unsigned int shndx, Address address)
+  { this->add(od, Output_reloc_type(gsym, type, relobj, shndx, address,
+                                    true)); }
 
   // Add a reloc against a local symbol.
 
@@ -942,15 +969,30 @@ class Output_data_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>
            unsigned int local_sym_index, unsigned int type,
            Output_data* od, Address address)
   { this->add(od, Output_reloc_type(relobj, local_sym_index, type, od,
-                                    address)); }
+                                    address, false)); }
 
   void
   add_local(Sized_relobj<size, big_endian>* relobj,
            unsigned int local_sym_index, unsigned int type,
            Output_data* od, unsigned int shndx, Address address)
   { this->add(od, Output_reloc_type(relobj, local_sym_index, type, shndx,
-                                   address)); }
+                                   address, false)); }
+
+  // Add a RELATIVE reloc against a local symbol.
 
+  void
+  add_local_relative(Sized_relobj<size, big_endian>* relobj,
+                    unsigned int local_sym_index, unsigned int type,
+                    Output_data* od, Address address)
+  { this->add(od, Output_reloc_type(relobj, local_sym_index, type, od,
+                                    address, true)); }
+
+  void
+  add_local_relative(Sized_relobj<size, big_endian>* relobj,
+                    unsigned int local_sym_index, unsigned int type,
+                    Output_data* od, unsigned int shndx, Address address)
+  { this->add(od, Output_reloc_type(relobj, local_sym_index, type, shndx,
+                                   address, true)); }
 
   // A reloc against the STT_SECTION symbol of an output section.
   // OS is the Output_section that the relocation refers to; OD is
@@ -991,14 +1033,32 @@ class Output_data_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian>
   void
   add_global(Symbol* gsym, unsigned int type, Output_data* od,
             Address address, Addend addend)
-  { this->add(od, Output_reloc_type(gsym, type, od, address, addend)); }
+  { this->add(od, Output_reloc_type(gsym, type, od, address, addend,
+                                    false)); }
 
   void
   add_global(Symbol* gsym, unsigned int type, Output_data* od, Relobj* relobj,
             unsigned int shndx, Address address,
             Addend addend)
   { this->add(od, Output_reloc_type(gsym, type, relobj, shndx, address,
-                                    addend)); }
+                                    addend, false)); }
+
+  // Add a RELATIVE reloc against a global symbol.  The final output
+  // relocation will not reference the symbol, but we must keep the symbol
+  // information long enough to set the addend of the relocation correctly
+  // when it is written.
+
+  void
+  add_global_relative(Symbol* gsym, unsigned int type, Output_data* od,
+                     Address address, Addend addend)
+  { this->add(od, Output_reloc_type(gsym, type, od, address, addend, true)); }
+
+  void
+  add_global_relative(Symbol* gsym, unsigned int type, Output_data* od,
+                      Relobj* relobj, unsigned int shndx, Address address,
+                     Addend addend)
+  { this->add(od, Output_reloc_type(gsym, type, relobj, shndx, address,
+                                    addend, true)); }
 
   // Add a reloc against a local symbol.
 
@@ -1008,7 +1068,7 @@ class Output_data_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian>
            Output_data* od, Address address, Addend addend)
   {
     this->add(od, Output_reloc_type(relobj, local_sym_index, type, od, address,
-                                   addend));
+                                   addend, false));
   }
 
   void
@@ -1018,7 +1078,28 @@ class Output_data_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian>
            Addend addend)
   {
     this->add(od, Output_reloc_type(relobj, local_sym_index, type, shndx,
-                                    address, addend));
+                                    address, addend, false));
+  }
+
+  // Add a RELATIVE reloc against a local symbol.
+
+  void
+  add_local_relative(Sized_relobj<size, big_endian>* relobj,
+                    unsigned int local_sym_index, unsigned int type,
+                    Output_data* od, Address address, Addend addend)
+  {
+    this->add(od, Output_reloc_type(relobj, local_sym_index, type, od, address,
+                                   addend, true));
+  }
+
+  void
+  add_local_relative(Sized_relobj<size, big_endian>* relobj,
+                    unsigned int local_sym_index, unsigned int type,
+                    Output_data* od, unsigned int shndx, Address address,
+                    Addend addend)
+  {
+    this->add(od, Output_reloc_type(relobj, local_sym_index, type, shndx,
+                                    address, addend, true));
   }
 
   // A reloc against the STT_SECTION symbol of an output section.
index a18d3ae..9187f32 100644 (file)
@@ -70,7 +70,6 @@ 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;
 }
 
 // Return the demangled version of the symbol's name, but only
index 6d2577b..a4f1106 100644 (file)
@@ -541,19 +541,6 @@ 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.
@@ -730,9 +717,6 @@ 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 cb68730..bdc28d4 100644 (file)
@@ -765,10 +765,13 @@ Target_x86_64::Scan::local(const General_options&,
       // relocate it easily.
       if (parameters->output_is_position_independent())
         {
+          unsigned int r_sym = elfcpp::elf_r_sym<64>(reloc.get_r_info());
           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);
+          rela_dyn->add_local_relative(object, r_sym,
+                                       elfcpp::R_X86_64_RELATIVE,
+                                       output_section, data_shndx,
+                                       reloc.get_r_offset(),
+                                       reloc.get_r_addend());
         }
       break;
 
@@ -831,8 +834,10 @@ Target_x86_64::Scan::local(const General_options&,
                 Reloc_section* rela_dyn = target->rela_dyn_section(layout);
                // 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);
+                  rela_dyn->add_local_relative(object, r_sym,
+                                               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);
@@ -1012,9 +1017,10 @@ Target_x86_64::Scan::global(const General_options& options,
                      && 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,
-                                    output_section, data_shndx,
-                                    reloc.get_r_offset(), 0);
+                rela_dyn->add_global_relative(gsym, elfcpp::R_X86_64_RELATIVE,
+                                              output_section, object,
+                                              data_shndx, reloc.get_r_offset(),
+                                              reloc.get_r_addend());
               }
             else
               {
@@ -1076,12 +1082,9 @@ Target_x86_64::Scan::global(const General_options& options,
             else
               {
                 if (got->add_global(gsym))
-                  {
-                    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();
-                  }
+                  rela_dyn->add_global_relative(gsym,
+                                                elfcpp::R_X86_64_RELATIVE,
+                                                got, gsym->got_offset(), 0);
               }
           }
         // For GOTPLT64, we also need a PLT entry (but only if the