]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Experimental implementation of RELR-style relocations. users/ccoutant/experimental-relr
authorCary Coutant <ccoutant@gmail.com>
Wed, 26 Apr 2017 02:21:57 +0000 (19:21 -0700)
committerCary Coutant <ccoutant@gmail.com>
Wed, 26 Apr 2017 03:07:52 +0000 (20:07 -0700)
For x86-64 only. Instead of R_X86_64_RELATIVE relocations, we
write the offsets of the relocation targets to a new section,
.relr.dyn, with section type SHT_RELR.

elfcpp/elfcpp.h
elfcpp/elfcpp_internal.h
gold/layout.cc
gold/layout.h
gold/options.h
gold/output.cc
gold/output.h
gold/reloc-types.h
gold/x86_64.cc

index cce40d41dedb281c2d8f41135250c2cc39162469..d4c0a63b2bf9538507b3cc6a5b26ac98d6c5cde7 100644 (file)
@@ -358,6 +358,7 @@ enum SHT
   SHT_PREINIT_ARRAY = 16,
   SHT_GROUP = 17,
   SHT_SYMTAB_SHNDX = 18,
+  SHT_RELR = 19,               // Experimental
   SHT_LOOS = 0x60000000,
   SHT_HIOS = 0x6fffffff,
   SHT_LOPROC = 0x70000000,
@@ -719,6 +720,11 @@ enum DT
 
   DT_PREINIT_ARRAY = 32,
   DT_PREINIT_ARRAYSZ = 33,
+  DT_SYMTAB_SHNDX = 34,
+  DT_RELRSZ = 35,              // Experimental
+  DT_RELR = 36,                        // Experimental
+  DT_RELRENT = 37,             // Experimental
+
   DT_LOOS = 0x6000000d,
   DT_HIOS = 0x6ffff000,
   DT_LOPROC = 0x70000000,
@@ -1021,6 +1027,7 @@ struct Elf_sizes
   // Sizes of ELF reloc entries.
   static const int rel_size = sizeof(internal::Rel_data<size>);
   static const int rela_size = sizeof(internal::Rela_data<size>);
+  static const int relr_size = sizeof(internal::Relr_data<size>);
   // Size of ELF dynamic entry.
   static const int dyn_size = sizeof(internal::Dyn_data<size>);
   // Size of ELF version structures.
@@ -1666,6 +1673,48 @@ class Rela_write
   internal::Rela_data<size>* p_;
 };
 
+// Accessor class for an ELF Relr relocation.
+
+template<int size, bool big_endian>
+class Relr
+{
+ public:
+  Relr(const unsigned char* p)
+    : p_(reinterpret_cast<const internal::Relr_data<size>*>(p))
+  { }
+
+  template<typename File>
+  Relr(File* file, typename File::Location loc)
+    : p_(reinterpret_cast<const internal::Relr_data<size>*>(
+          file->view(loc.file_offset, loc.data_size).data()))
+  { }
+
+  typename Elf_types<size>::Elf_Addr
+  get_r_offset() const
+  { return Convert<size, big_endian>::convert_host(this->p_->r_offset); }
+
+ private:
+  const internal::Relr_data<size>* p_;
+};
+
+// Writer class for an ELF Relr relocation.
+
+template<int size, bool big_endian>
+class Relr_write
+{
+ public:
+  Relr_write(unsigned char* p)
+    : p_(reinterpret_cast<internal::Relr_data<size>*>(p))
+  { }
+
+  void
+  put_r_offset(typename Elf_types<size>::Elf_Addr v)
+  { this->p_->r_offset = Convert<size, big_endian>::convert_host(v); }
+
+ private:
+  internal::Relr_data<size>* p_;
+};
+
 // MIPS-64 has a non-standard relocation layout.
 
 template<bool big_endian>
index 14adfdef3441e0d030d363e733054e83a39885fc..a1cfcf5ce6f8370d00849b01464b0b38d3817fe0 100644 (file)
@@ -180,6 +180,12 @@ struct Rela_data
   typename Elf_types<size>::Elf_Swxword r_addend;
 };
 
+template<int size>
+struct Relr_data
+{
+  typename Elf_types<size>::Elf_Addr r_offset;
+};
+
 // MIPS-64 has a non-standard layout for relocations.
 
 struct Mips64_rel_data
index 07a359074e0a0fcd729423b6fdfc201eeb6b2b0d..504028a968e1007e1a2178dc89506d808c54c595 100644 (file)
@@ -4710,7 +4710,8 @@ void
 Layout::add_target_dynamic_tags(bool use_rel, const Output_data* plt_got,
                                const Output_data* plt_rel,
                                const Output_data_reloc_generic* dyn_rel,
-                               bool add_debug, bool dynrel_includes_plt)
+                               bool add_debug, bool dynrel_includes_plt,
+                               const Output_data_reloc_generic* dyn_relr)
 {
   Output_data_dynamic* odyn = this->dynamic_data_;
   if (odyn == NULL)
@@ -4783,6 +4784,14 @@ Layout::add_target_dynamic_tags(bool use_rel, const Output_data* plt_got,
        }
     }
 
+  if (dyn_relr != NULL && dyn_relr->output_section() != NULL)
+    {
+      const int size = parameters->target().get_size();
+      odyn->add_section_address(elfcpp::DT_RELR, dyn_relr->output_section());
+      odyn->add_section_size(elfcpp::DT_RELRSZ, dyn_relr->output_section());
+      odyn->add_constant(elfcpp::DT_RELRENT, size / 8);
+    }
+
   if (add_debug && !parameters->options().shared())
     {
       // The value of the DT_DEBUG tag is filled in by the dynamic
index 5f58c2c67f69905ae5be98ab302577f44612bef0..5852898cc3f4103601e57c4593298e6be9508470 100644 (file)
@@ -900,7 +900,8 @@ class Layout
   add_target_dynamic_tags(bool use_rel, const Output_data* plt_got,
                          const Output_data* plt_rel,
                          const Output_data_reloc_generic* dyn_rel,
-                         bool add_debug, bool dynrel_includes_plt);
+                         bool add_debug, bool dynrel_includes_plt,
+                         const Output_data_reloc_generic* dyn_relr = NULL);
 
   // Add a target-specific dynamic tag with constant value.
   void
index a8b1d46aa1093e742f385eb62ce76d9666bc1024..b08acad34a16709997a56fc67f00edc0e96bb203 100644 (file)
@@ -834,6 +834,10 @@ class General_options
                 N_("Exclude libraries from automatic export"),
                 N_(("lib,lib ...")));
 
+  DEFINE_bool(experimental_use_relr, options::TWO_DASHES, '\0', false,
+             N_("(x86-64 only) Generate RELR dynamic relocations"),
+             N_("Do not generate RELR dynamic relocations"));
+
   DEFINE_bool(export_dynamic, options::TWO_DASHES, 'E', false,
              N_("Export all dynamic symbols"),
              N_("Do not export all dynamic symbols"));
index 5b1e601d4912f2df8e6ab9a680c507110360e909..8c27ad4977967c2054fc612aa6890d836312218c 100644 (file)
@@ -1224,6 +1224,17 @@ Output_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian>::write(
   orel.put_r_addend(addend);
 }
 
+// Write out a Relr relocation.
+
+template<bool dynamic, int size, bool big_endian>
+void
+Output_reloc<elfcpp::SHT_RELR, dynamic, size, big_endian>::write(
+    unsigned char* pov) const
+{
+  elfcpp::Relr_write<size, big_endian> orel(pov);
+  orel.put_r_offset(this->rel_.get_address());
+}
+
 // Output_data_reloc_base methods.
 
 // Adjust the output section.
@@ -1237,6 +1248,8 @@ Output_data_reloc_base<sh_type, dynamic, size, big_endian>
     os->set_entsize(elfcpp::Elf_sizes<size>::rel_size);
   else if (sh_type == elfcpp::SHT_RELA)
     os->set_entsize(elfcpp::Elf_sizes<size>::rela_size);
+  else if (sh_type == elfcpp::SHT_RELR)
+    os->set_entsize(elfcpp::Elf_sizes<size>::relr_size);
   else
     gold_unreachable();
 
@@ -5526,6 +5539,26 @@ template
 class Output_data_reloc<elfcpp::SHT_RELA, true, 64, true>;
 #endif
 
+#ifdef HAVE_TARGET_32_LITTLE
+template
+class Output_data_reloc<elfcpp::SHT_RELR, true, 32, false>;
+#endif
+
+#ifdef HAVE_TARGET_32_BIG
+template
+class Output_data_reloc<elfcpp::SHT_RELR, true, 32, true>;
+#endif
+
+#ifdef HAVE_TARGET_64_LITTLE
+template
+class Output_data_reloc<elfcpp::SHT_RELR, true, 64, false>;
+#endif
+
+#ifdef HAVE_TARGET_64_BIG
+template
+class Output_data_reloc<elfcpp::SHT_RELR, true, 64, true>;
+#endif
+
 #ifdef HAVE_TARGET_32_LITTLE
 template
 class Output_relocatable_relocs<elfcpp::SHT_REL, 32, false>;
index 157cef272d8ec314d1b4e8c06eb41f264f25e989..f33a7f838a0672f6415973e6553ca8d9602bcdc9 100644 (file)
@@ -1500,6 +1500,104 @@ class Output_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian>
   Addend addend_;
 };
 
+// The SHT_RELR version of Output_reloc<>.  This is a relative reloc,
+// and holds nothing but an offset.  Rather than duplicate all the fields
+// of the SHT_REL version except for the symbol and relocation type, we
+// simply use an SHT_REL as a proxy.
+
+template<bool dynamic, int size, bool big_endian>
+class Output_reloc<elfcpp::SHT_RELR, dynamic, size, big_endian>
+{
+ public:
+  typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
+
+  // An uninitialized entry.
+  Output_reloc()
+    : rel_()
+  { }
+
+  // A reloc against a global symbol.
+
+  Output_reloc(Symbol* gsym, Output_data* od, Address address)
+    : rel_(gsym, 0, od, address, true, true, false)
+  { }
+
+  Output_reloc(Symbol* gsym, Sized_relobj<size, big_endian>* relobj,
+              unsigned int shndx, Address address)
+    : rel_(gsym, 0, relobj, shndx, address, true, true, false)
+  { }
+
+  // A reloc against a local symbol.
+
+  Output_reloc(Sized_relobj<size, big_endian>* relobj,
+              unsigned int local_sym_index, Output_data* od, Address address,
+              bool is_section_symbol)
+    : rel_(relobj, local_sym_index, 0, od, address, true,
+          true, is_section_symbol, false)
+  { }
+
+  Output_reloc(Sized_relobj<size, big_endian>* relobj,
+              unsigned int local_sym_index, unsigned int shndx,
+              Address address, bool is_section_symbol)
+    : rel_(relobj, local_sym_index, 0, shndx, address, true,
+          true, is_section_symbol, false)
+  { }
+
+  // A reloc against the STT_SECTION symbol of an output section.
+
+  Output_reloc(Output_section* os, Output_data* od, Address address)
+    : rel_(os, 0, od, address, true)
+  { }
+
+  Output_reloc(Output_section* os, Sized_relobj<size, big_endian>* relobj,
+              unsigned int shndx, Address address)
+    : rel_(os, 0, relobj, shndx, address, true)
+  { }
+
+  // A relative relocation with no symbol.
+
+  Output_reloc(Output_data* od, Address address)
+    : rel_(0, od, address, true)
+  { }
+
+  Output_reloc(Sized_relobj<size, big_endian>* relobj,
+              unsigned int shndx, Address address)
+    : rel_(0, relobj, shndx, address, true)
+  { }
+
+  // Return whether this is a RELATIVE relocation.
+  bool
+  is_relative() const
+  { return true; }
+
+  // Return whether this is a relocation which should not use
+  // a symbol, but which obtains its addend from a symbol.
+  bool
+  is_symbolless() const
+  { return true; }
+
+  // If this relocation is against an input section, return the
+  // relocatable object containing the input section.
+  Sized_relobj<size, big_endian>*
+  get_relobj() const
+  { return this->rel_.get_relobj(); }
+
+  // Write the reloc entry to an output view.
+  void
+  write(unsigned char* pov) const;
+
+  // Return whether this reloc should be sorted before the argument
+  // when sorting dynamic relocs.
+  bool
+  sort_before(const Output_reloc<elfcpp::SHT_RELR, dynamic, size, big_endian>&
+             r2) const
+  { return this->rel_.compare(r2.rel_) < 0; }
+
+ private:
+  // The basic reloc.
+  Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian> rel_;
+};
+
 // Output_data_reloc_generic is a non-template base class for
 // Output_data_reloc_base.  This gives the generic code a way to hold
 // a pointer to a reloc section.
@@ -2344,6 +2442,127 @@ class Output_data_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian>
   }
 };
 
+// The SHT_RELR version of Output_data_reloc.
+
+template<bool dynamic, int size, bool big_endian>
+class Output_data_reloc<elfcpp::SHT_RELR, dynamic, size, big_endian>
+  : public Output_data_reloc_base<elfcpp::SHT_RELR, dynamic, size, big_endian>
+{
+ private:
+  typedef Output_data_reloc_base<elfcpp::SHT_RELR, dynamic, size,
+                                big_endian> Base;
+
+ public:
+  typedef typename Base::Output_reloc_type Output_reloc_type;
+  typedef typename Output_reloc_type::Address Address;
+
+  Output_data_reloc()
+    : Output_data_reloc_base<elfcpp::SHT_RELR, dynamic, size, big_endian>(false)
+  { }
+
+  void
+  add_global_generic(Symbol*, unsigned int, Output_data*, uint64_t, uint64_t)
+  {
+    gold_unreachable();
+  }
+
+  void
+  add_global_generic(Symbol*, unsigned int, Output_data*, Relobj*,
+                    unsigned int, uint64_t, uint64_t)
+  {
+    gold_unreachable();
+  }
+
+  // Add a RELATIVE reloc against a global symbol.  The final relocation
+  // will not reference the symbol.
+
+  void
+  add_global_relative(Symbol* gsym, Output_data* od, Address address)
+  {
+    this->add(od, Output_reloc_type(gsym, od, address));
+  }
+
+  void
+  add_global_relative(Symbol* gsym, Output_data* od,
+                     Sized_relobj<size, big_endian>* relobj,
+                     unsigned int shndx, Address address)
+  {
+    this->add(od, Output_reloc_type(gsym, relobj, shndx, address));
+  }
+
+  void
+  add_local_generic(Relobj*, unsigned int, unsigned int, Output_data*, uint64_t,
+                   uint64_t)
+  {
+    gold_unreachable();
+  }
+
+  void
+  add_local_generic(Relobj*, unsigned int, unsigned int, Output_data*,
+                   unsigned int, uint64_t, uint64_t)
+  {
+    gold_unreachable();
+  }
+
+  // Add a RELATIVE reloc against a local symbol.
+
+  void
+  add_local_relative(Sized_relobj<size, big_endian>* relobj,
+                    unsigned int local_sym_index, Output_data* od,
+                    Address address)
+  {
+    this->add(od, Output_reloc_type(relobj, local_sym_index, od, address,
+                                   false));
+  }
+
+  void
+  add_local_relative(Sized_relobj<size, big_endian>* relobj,
+                    unsigned int local_sym_index, Output_data* od,
+                    unsigned int shndx, Address address)
+  {
+    this->add(od, Output_reloc_type(relobj, local_sym_index, shndx, address,
+                                   false));
+  }
+
+  void
+  add_output_section_generic(Output_section*, unsigned int, Output_data*,
+                            uint64_t, uint64_t)
+  {
+    gold_unreachable();
+  }
+
+  void
+  add_output_section_generic(Output_section*, unsigned int, Output_data*,
+                            Relobj*, unsigned int, uint64_t, uint64_t)
+  {
+    gold_unreachable();
+  }
+
+  // Add a RELATIVE reloc against an output section symbol.
+
+  void
+  add_output_section_relative(Output_section* os, Output_data* od,
+                             Address address)
+  { this->add(od, Output_reloc_type(os, od, address)); }
+
+  void
+  add_output_section_relative(Output_section* os, Output_data* od,
+                             Sized_relobj<size, big_endian>* relobj,
+                             unsigned int shndx, Address address)
+  { this->add(od, Output_reloc_type(os, relobj, shndx, address)); }
+
+  // Add a relative relocation
+
+  void
+  add_relative(Output_data* od, Address address)
+  { this->add(od, Output_reloc_type(od, address)); }
+
+  void
+  add_relative(Output_data* od, Sized_relobj<size, big_endian>* relobj,
+              unsigned int shndx, Address address)
+  { this->add(od, Output_reloc_type(relobj, shndx, address)); }
+};
+
 // Output_relocatable_relocs represents a relocation section in a
 // relocatable link.  The actual data is written out in the target
 // hook relocate_relocs.  This just saves space for it.
index eea7dd6b2df5c7760cd186eff475737c8844da15..f25cd5b858033e08c4305bf125f7a2d66f71696e 100644 (file)
@@ -79,6 +79,27 @@ struct Reloc_types<elfcpp::SHT_RELA, size, big_endian>
   { p->put_r_addend(val); }
 };
 
+template<int size, bool big_endian>
+struct Reloc_types<elfcpp::SHT_RELR, size, big_endian>
+{
+  typedef typename elfcpp::Relr<size, big_endian> Reloc;
+  typedef typename elfcpp::Relr_write<size, big_endian> Reloc_write;
+  static const int reloc_size = elfcpp::Elf_sizes<size>::relr_size;
+
+  static inline typename elfcpp::Elf_types<size>::Elf_Swxword
+  get_reloc_addend(const Reloc*)
+  { gold_unreachable(); }
+
+  static inline typename elfcpp::Elf_types<size>::Elf_Swxword
+  get_reloc_addend_noerror(const Reloc*)
+  { return 0; }
+
+  static inline void
+  set_reloc_addend(Reloc_write*,
+                  typename elfcpp::Elf_types<size>::Elf_Swxword)
+  { gold_unreachable(); }
+};
+
 }; // End namespace gold.
 
 #endif // !defined(GOLD_RELOC_TYPE_SH)
index 7f1742dd5f69faf23e62d4ce29194fa75413c67d..53c9d198a8e138aa7774e6fecaf6f6ad1828e14b 100644 (file)
@@ -584,13 +584,15 @@ class Target_x86_64 : public Sized_target<size, false>
   // uses only Elf64_Rela relocation entries with explicit addends."
   typedef Output_data_reloc<elfcpp::SHT_RELA, true, size, false> Reloc_section;
 
+  typedef Output_data_reloc<elfcpp::SHT_RELR, true, size, false> Relr_section;
+
   Target_x86_64(const Target::Target_info* info = &x86_64_info)
     : Sized_target<size, false>(info),
       got_(NULL), plt_(NULL), got_plt_(NULL), got_irelative_(NULL),
       got_tlsdesc_(NULL), global_offset_table_(NULL), rela_dyn_(NULL),
-      rela_irelative_(NULL), copy_relocs_(elfcpp::R_X86_64_COPY),
-      got_mod_index_offset_(-1U), tlsdesc_reloc_info_(),
-      tls_base_symbol_defined_(false)
+      rela_irelative_(NULL), relr_dyn_(NULL),
+      copy_relocs_(elfcpp::R_X86_64_COPY), got_mod_index_offset_(-1U),
+      tlsdesc_reloc_info_(), tls_base_symbol_defined_(false)
   { }
 
   // Hook for a new output section.
@@ -1172,6 +1174,10 @@ class Target_x86_64 : public Sized_target<size, false>
   Reloc_section*
   rela_irelative_section(Layout*);
 
+  // Get the RELR dynamic reloc section, creating it if necessary.
+  Relr_section*
+  relr_dyn_section(Layout*);
+
   // Add a potential copy relocation.
   void
   copy_reloc(Symbol_table* symtab, Layout* layout,
@@ -1235,6 +1241,8 @@ class Target_x86_64 : public Sized_target<size, false>
   Reloc_section* rela_dyn_;
   // The section to use for IRELATIVE relocs.
   Reloc_section* rela_irelative_;
+  // The RELR dynamic reloc section.
+  Relr_section* relr_dyn_;
   // Relocs saved to avoid a COPY reloc.
   Copy_relocs<elfcpp::SHT_RELA, size, false> copy_relocs_;
   // Offset of the GOT entry for the TLS module index.
@@ -1431,6 +1439,23 @@ Target_x86_64<size>::rela_irelative_section(Layout* layout)
   return this->rela_irelative_;
 }
 
+// Get the RELR dynamic reloc section, creating it if necessary.
+
+template<int size>
+typename Target_x86_64<size>::Relr_section*
+Target_x86_64<size>::relr_dyn_section(Layout* layout)
+{
+  if (this->relr_dyn_ == NULL)
+    {
+      gold_assert(layout != NULL);
+      this->relr_dyn_ = new Relr_section();
+      layout->add_output_section_data(".relr.dyn", elfcpp::SHT_RELR,
+                                     elfcpp::SHF_ALLOC, this->relr_dyn_,
+                                     ORDER_DYNAMIC_RELOCS, false);
+    }
+  return this->relr_dyn_;
+}
+
 // Write the first three reserved words of the .got.plt section.
 // The remainder of the section is written while writing the PLT
 // in Output_data_plt_i386::do_write.
@@ -2966,14 +2991,25 @@ Target_x86_64<size>::Scan::local(Symbol_table* symtab,
       if (parameters->options().output_is_position_independent())
        {
          unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
-         Reloc_section* rela_dyn = target->rela_dyn_section(layout);
-         rela_dyn->add_local_relative(object, r_sym,
-                                      (size == 32
-                                       ? elfcpp::R_X86_64_RELATIVE64
-                                       : elfcpp::R_X86_64_RELATIVE),
-                                      output_section, data_shndx,
-                                      reloc.get_r_offset(),
-                                      reloc.get_r_addend(), is_ifunc);
+         if (size == 64
+             && !is_ifunc
+             && parameters->options().experimental_use_relr())
+           {
+             Relr_section* relr_dyn = target->relr_dyn_section(layout);
+             relr_dyn->add_local_relative(object, r_sym, output_section,
+                                          data_shndx, reloc.get_r_offset());
+           }
+         else
+           {
+             Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+             rela_dyn->add_local_relative(object, r_sym,
+                                          (size == 32
+                                           ? elfcpp::R_X86_64_RELATIVE64
+                                           : elfcpp::R_X86_64_RELATIVE),
+                                          output_section, data_shndx,
+                                          reloc.get_r_offset(),
+                                          reloc.get_r_addend(), is_ifunc);
+           }
        }
       break;
 
@@ -2991,12 +3027,22 @@ Target_x86_64<size>::Scan::local(Symbol_table* symtab,
          if (size == 32 && r_type == elfcpp::R_X86_64_32)
            {
              unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
-             Reloc_section* rela_dyn = target->rela_dyn_section(layout);
-             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(), is_ifunc);
+             if (!is_ifunc && parameters->options().experimental_use_relr())
+               {
+                 Relr_section* relr_dyn = target->relr_dyn_section(layout);
+                 relr_dyn->add_local_relative(object, r_sym, output_section,
+                                              data_shndx,
+                                              reloc.get_r_offset());
+               }
+             else
+               {
+                 Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+                 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(), is_ifunc);
+               }
              break;
            }
 
@@ -3099,15 +3145,7 @@ Target_x86_64<size>::Scan::local(Symbol_table* symtab,
              {
                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)
-                 {
-                   unsigned int got_offset =
-                     object->local_got_offset(r_sym, GOT_TYPE_STANDARD);
-                   rela_dyn->add_local_relative(object, r_sym,
-                                                elfcpp::R_X86_64_RELATIVE,
-                                                got, got_offset, 0, is_ifunc);
-                 }
-               else
+               if (r_type == elfcpp::R_X86_64_GOT32)
                  {
                    this->check_non_pic(object, r_type, NULL);
 
@@ -3116,6 +3154,24 @@ Target_x86_64<size>::Scan::local(Symbol_table* symtab,
                        object, r_sym, r_type, got,
                        object->local_got_offset(r_sym, GOT_TYPE_STANDARD), 0);
                  }
+               else if (size == 64
+                        && !is_ifunc
+                        && parameters->options().experimental_use_relr())
+                 {
+                   Relr_section* relr_dyn = target->relr_dyn_section(layout);
+                   unsigned int got_offset =
+                     object->local_got_offset(r_sym, GOT_TYPE_STANDARD);
+                   relr_dyn->add_local_relative(object, r_sym, got,
+                                                got_offset);
+                 }
+               else
+                 {
+                   unsigned int got_offset =
+                     object->local_got_offset(r_sym, GOT_TYPE_STANDARD);
+                   rela_dyn->add_local_relative(object, r_sym,
+                                                elfcpp::R_X86_64_RELATIVE,
+                                                got, got_offset, 0, is_ifunc);
+                 }
              }
          }
        // For GOTPLT64, we'd normally want a PLT section, but since
@@ -3478,12 +3534,24 @@ Target_x86_64<size>::Scan::global(Symbol_table* symtab,
                      || (size == 32 && r_type == elfcpp::R_X86_64_32))
                     && gsym->can_use_relative_reloc(false))
              {
-               Reloc_section* rela_dyn = target->rela_dyn_section(layout);
-               rela_dyn->add_global_relative(gsym, elfcpp::R_X86_64_RELATIVE,
-                                             output_section, object,
-                                             data_shndx,
-                                             reloc.get_r_offset(),
-                                             reloc.get_r_addend(), false);
+               if (parameters->options().experimental_use_relr())
+                 {
+                   Relr_section* relr_dyn = target->relr_dyn_section(layout);
+                   relr_dyn->add_global_relative(gsym,
+                                                 output_section, object,
+                                                 data_shndx,
+                                                 reloc.get_r_offset());
+                 }
+               else
+                 {
+                   Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+                   rela_dyn->add_global_relative(gsym,
+                                                 elfcpp::R_X86_64_RELATIVE,
+                                                 output_section, object,
+                                                 data_shndx,
+                                                 reloc.get_r_offset(),
+                                                 reloc.get_r_addend(), false);
+                 }
              }
            else
              {
@@ -3886,7 +3954,8 @@ Target_x86_64<size>::do_finalize_sections(
                                  ? NULL
                                  : this->plt_->rela_plt());
   layout->add_target_dynamic_tags(false, this->got_plt_, rel_plt,
-                                 this->rela_dyn_, true, false);
+                                 this->rela_dyn_, true, false,
+                                 this->relr_dyn_);
 
   // Fill in some more dynamic tags.
   Output_data_dynamic* const odyn = layout->dynamic_data();