From 8c13bcd2209cb776aa2cf7e5016b7884b0aecbf6 Mon Sep 17 00:00:00 2001 From: Cary Coutant Date: Tue, 25 Apr 2017 19:21:57 -0700 Subject: Experimental implementation of RELR-style relocations. 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 | 49 +++++++++++ elfcpp/elfcpp_internal.h | 6 ++ gold/layout.cc | 11 ++- gold/layout.h | 3 +- gold/options.h | 4 + gold/output.cc | 33 +++++++ gold/output.h | 219 +++++++++++++++++++++++++++++++++++++++++++++++ gold/reloc-types.h | 21 +++++ gold/x86_64.cc | 135 ++++++++++++++++++++++------- 9 files changed, 446 insertions(+), 35 deletions(-) diff --git a/elfcpp/elfcpp.h b/elfcpp/elfcpp.h index cce40d41ded..d4c0a63b2bf 100644 --- a/elfcpp/elfcpp.h +++ b/elfcpp/elfcpp.h @@ -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); static const int rela_size = sizeof(internal::Rela_data); + static const int relr_size = sizeof(internal::Relr_data); // Size of ELF dynamic entry. static const int dyn_size = sizeof(internal::Dyn_data); // Size of ELF version structures. @@ -1666,6 +1673,48 @@ class Rela_write internal::Rela_data* p_; }; +// Accessor class for an ELF Relr relocation. + +template +class Relr +{ + public: + Relr(const unsigned char* p) + : p_(reinterpret_cast*>(p)) + { } + + template + Relr(File* file, typename File::Location loc) + : p_(reinterpret_cast*>( + file->view(loc.file_offset, loc.data_size).data())) + { } + + typename Elf_types::Elf_Addr + get_r_offset() const + { return Convert::convert_host(this->p_->r_offset); } + + private: + const internal::Relr_data* p_; +}; + +// Writer class for an ELF Relr relocation. + +template +class Relr_write +{ + public: + Relr_write(unsigned char* p) + : p_(reinterpret_cast*>(p)) + { } + + void + put_r_offset(typename Elf_types::Elf_Addr v) + { this->p_->r_offset = Convert::convert_host(v); } + + private: + internal::Relr_data* p_; +}; + // MIPS-64 has a non-standard relocation layout. template diff --git a/elfcpp/elfcpp_internal.h b/elfcpp/elfcpp_internal.h index 14adfdef344..a1cfcf5ce6f 100644 --- a/elfcpp/elfcpp_internal.h +++ b/elfcpp/elfcpp_internal.h @@ -180,6 +180,12 @@ struct Rela_data typename Elf_types::Elf_Swxword r_addend; }; +template +struct Relr_data +{ + typename Elf_types::Elf_Addr r_offset; +}; + // MIPS-64 has a non-standard layout for relocations. struct Mips64_rel_data diff --git a/gold/layout.cc b/gold/layout.cc index 07a359074e0..504028a968e 100644 --- a/gold/layout.cc +++ b/gold/layout.cc @@ -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 diff --git a/gold/layout.h b/gold/layout.h index 5f58c2c67f6..5852898cc3f 100644 --- a/gold/layout.h +++ b/gold/layout.h @@ -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 diff --git a/gold/options.h b/gold/options.h index a8b1d46aa10..b08acad34a1 100644 --- a/gold/options.h +++ b/gold/options.h @@ -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")); diff --git a/gold/output.cc b/gold/output.cc index 5b1e601d491..8c27ad49779 100644 --- a/gold/output.cc +++ b/gold/output.cc @@ -1224,6 +1224,17 @@ Output_reloc::write( orel.put_r_addend(addend); } +// Write out a Relr relocation. + +template +void +Output_reloc::write( + unsigned char* pov) const +{ + elfcpp::Relr_write 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 os->set_entsize(elfcpp::Elf_sizes::rel_size); else if (sh_type == elfcpp::SHT_RELA) os->set_entsize(elfcpp::Elf_sizes::rela_size); + else if (sh_type == elfcpp::SHT_RELR) + os->set_entsize(elfcpp::Elf_sizes::relr_size); else gold_unreachable(); @@ -5526,6 +5539,26 @@ template class Output_data_reloc; #endif +#ifdef HAVE_TARGET_32_LITTLE +template +class Output_data_reloc; +#endif + +#ifdef HAVE_TARGET_32_BIG +template +class Output_data_reloc; +#endif + +#ifdef HAVE_TARGET_64_LITTLE +template +class Output_data_reloc; +#endif + +#ifdef HAVE_TARGET_64_BIG +template +class Output_data_reloc; +#endif + #ifdef HAVE_TARGET_32_LITTLE template class Output_relocatable_relocs; diff --git a/gold/output.h b/gold/output.h index 157cef272d8..f33a7f838a0 100644 --- a/gold/output.h +++ b/gold/output.h @@ -1500,6 +1500,104 @@ class Output_reloc 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 +class Output_reloc +{ + public: + typedef typename elfcpp::Elf_types::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* 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* 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* 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* 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* 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* + 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& + r2) const + { return this->rel_.compare(r2.rel_) < 0; } + + private: + // The basic reloc. + Output_reloc 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 } }; +// The SHT_RELR version of Output_data_reloc. + +template +class Output_data_reloc + : public Output_data_reloc_base +{ + private: + typedef Output_data_reloc_base 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(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* 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* 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* 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* 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* 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. diff --git a/gold/reloc-types.h b/gold/reloc-types.h index eea7dd6b2df..f25cd5b8580 100644 --- a/gold/reloc-types.h +++ b/gold/reloc-types.h @@ -79,6 +79,27 @@ struct Reloc_types { p->put_r_addend(val); } }; +template +struct Reloc_types +{ + typedef typename elfcpp::Relr Reloc; + typedef typename elfcpp::Relr_write Reloc_write; + static const int reloc_size = elfcpp::Elf_sizes::relr_size; + + static inline typename elfcpp::Elf_types::Elf_Swxword + get_reloc_addend(const Reloc*) + { gold_unreachable(); } + + static inline typename elfcpp::Elf_types::Elf_Swxword + get_reloc_addend_noerror(const Reloc*) + { return 0; } + + static inline void + set_reloc_addend(Reloc_write*, + typename elfcpp::Elf_types::Elf_Swxword) + { gold_unreachable(); } +}; + }; // End namespace gold. #endif // !defined(GOLD_RELOC_TYPE_SH) diff --git a/gold/x86_64.cc b/gold/x86_64.cc index 7f1742dd5f6..53c9d198a8e 100644 --- a/gold/x86_64.cc +++ b/gold/x86_64.cc @@ -584,13 +584,15 @@ class Target_x86_64 : public Sized_target // uses only Elf64_Rela relocation entries with explicit addends." typedef Output_data_reloc Reloc_section; + typedef Output_data_reloc Relr_section; + Target_x86_64(const Target::Target_info* info = &x86_64_info) : Sized_target(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 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 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 copy_relocs_; // Offset of the GOT entry for the TLS module index. @@ -1431,6 +1439,23 @@ Target_x86_64::rela_irelative_section(Layout* layout) return this->rela_irelative_; } +// Get the RELR dynamic reloc section, creating it if necessary. + +template +typename Target_x86_64::Relr_section* +Target_x86_64::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::Scan::local(Symbol_table* symtab, if (parameters->options().output_is_position_independent()) { unsigned int r_sym = elfcpp::elf_r_sym(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::Scan::local(Symbol_table* symtab, if (size == 32 && r_type == elfcpp::R_X86_64_32) { unsigned int r_sym = elfcpp::elf_r_sym(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::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::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::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::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(); -- cgit v1.2.1