From 67181c72fbaad59e5793cf6bcb8d12d7d211c817 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 8 Jul 2011 22:48:08 +0000 Subject: [PATCH] PR gold/12372 * target.h (Target::plt_address_for_global): New function. (Target::plt_address_for_local): New function. (Target::plt_section_for_global): Remove. (Target::plt_section_for_local): Remove. (Target::do_plt_address_for_global): New virtual function. (Target::do_plt_address_for_local): New virtual function. (Target::do_plt_section_for_global): Remove. (Target::do_plt_section_for_local): Remove. (Target::register_global_plt_entry): Add Symbol_table and Layout parameters. * output.cc (Output_data_got::Got_entry::write): Use plt_address_for_global and plt_address_for_local. * layout.cc (Layout::add_target_dynamic_tags): Use size and address of output section. * i386.cc (class Output_data_plt_i386): Add irelative_rel_, got_irelative_, and irelative_count_ fields. Update declarations. (Output_data_plt_i386::has_irelative_section): New function. (Output_data_plt_i386::entry_count): Add irelative_count_. (Output_data_plt_i386::set_final_data_size): Likewise. (class Target_i386): Add got_irelative_ and rel_irelative_ fields. Update declarations. (Target_i386::Target_i386): Initialize new fields. (Target_i386::do_plt_address_for_global): New function replacing do_plt_section_for_global. (Target_i386::do_plt_address_for_local): New function replacing do_plt_section_for_local. (Target_i386::got_section): Create got_irelative_. (Target_i386::rel_irelative_section): New function. (Output_data_plt_i386::Output_data_plt_i386): Initialize new fields. Don't define __rel_iplt_{start,end}. (Output_data_plt_i386::add_entry): Add symtab and layout parameters. Change all callers. Use different PLT and GOT for IFUNC symbols. (Output_data_plt_i386::add_local_ifunc_entry): Add symtab and layout parameters. Change all callers. Use different PLT and GOT. (Output_data_plt_i386::rel_tls_desc): Fix formatting. (Output_data_plt_i386::rel_irelative): New function. (Output_data_plt_i386::address_for_global): New function. (Output_data_plt_i386::address_for_local): New function. (Output_data_plt_i386::do_write): Write out IRELATIVE area. Use IRELATIVE GOT when changing IFUNC GOT entries. (Target_i386::Scan::global): Use IRELATIVE GOT for IRELATIVE reloc. (Target_i386::do_finalize_sections): Create the __rel_iplt symbols if we didn't create an IRELATIVE GOT. (Target_i386::Relocate::relocate): Use plt_address_for_global and plt_address_for_local. (Target_i386::do_dynsym_value): Use plt_address_for_global. * x86_64.cc (class Output_data_plt_x86_64): Add irelative_rel_, got_irelative_, and irelative_count_ fields. Update declarations. (Output_data_plt_x86_64::Output_data_plt_x86_64) [both versions]: Initialize new fields. Remove symtab parameter. Change all callers. (Output_data_plt_x86_64::get_tlsdesc_plt_offset): Add irelative_count_. (Output_data_plt_x86_64::has_irelative_section): New function. (Output_data_plt_x86_64::entry_count): Add irelative_count_. (class Target_x86_64): Add got_irelative_ and rel_irelative_ fields. Update declarations. (Target_x86_64::Target_x86_64): Initialize new fields. (Target_x86_64::do_plt_address_for_global): New function replacing do_plt_section_for_global. (Target_x86_64::do_plt_address_for_local): New function replacing do_plt_section_for_local. (Target_x86_64::got_section): Create got_irelative_. (Target_x86_64::rela_irelative_section): New function. (Output_data_plt_x86_64::init): Remove symtab parameter. Change all callers. Don't create __rel_iplt_{start,end}. (Output_data_plt_x86_64::add_entry): Add symtab and layout parameters. Change all callers. Use different PLT and GOT for IFUNC symbols. (Output_data_plt_x86_64::add_local_ifunc_entry): Add symtab and layout parameters. Change all callers. Use different PLT and GOT. (Output_data_plt_x86_64::add_relocation): Add symtab and layout parameters. Change all callers. Use different PLT and GOT for IFUNC symbols. (Output_data_plt_x86_64::rela_tlsdesc): Fix formatting. (Output_data_plt_x86_64::rela_irelative): New function. (Output_data_plt_x86_64::address_for_global): New function. (Output_data_plt_x86_64::address_for_local): New function. (Output_data_plt_x86_64::set_final_data_size): Likewise. (Output_data_plt_x86_64::do_write): Write out IRELATIVE area. (Target_x86_64::init_got_plt_for_update): Create got_irelative_. (Target_x86_64::register_global_plt_entry): Add symtab and layout parameters. (Target_x86_64::Scan::global): Use IRELATIVE GOT for IRELATIVE reloc. (Target_x86_64::do_finalize_sections): Create the __rela_iplt symbols if we didn't create an IRELATIVE GOT. (Target_x86_64::Relocate::relocate): Use plt_address_for_global and plt_address_for_local. (Target_x86_64::do_dynsym_value): Use plt_address_for_global. * testsuite/ifuncvar1.c: New test file. * testsuite/ifuncvar2.c: New test file. * testsuite/ifuncvar3.c: New test file. * testsuite/Makefile.am (check_PROGRAMS): Add ifuncvar. (ifuncvar1_pic.o, ifuncvar2_pic.o, ifuncvar.so): New targets. (ifuncvar_SOURCES, ifuncvar_DEPENDENCIES): New variables. (ifuncvar_LDFLAGS, ifuncvar_LDADD): New variables. * testsuite/Makefile.in: Rebuild. --- gold/ChangeLog | 108 ++++++++++++ gold/i386.cc | 280 ++++++++++++++++++++++--------- gold/incremental.cc | 4 +- gold/layout.cc | 14 +- gold/output.cc | 8 +- gold/target.h | 35 ++-- gold/testsuite/Makefile.am | 12 ++ gold/testsuite/Makefile.in | 49 ++++-- gold/testsuite/ifuncvar1.c | 20 +++ gold/testsuite/ifuncvar2.c | 12 ++ gold/testsuite/ifuncvar3.c | 14 ++ gold/x86_64.cc | 336 +++++++++++++++++++++++++++---------- 12 files changed, 690 insertions(+), 202 deletions(-) create mode 100644 gold/testsuite/ifuncvar1.c create mode 100644 gold/testsuite/ifuncvar2.c create mode 100644 gold/testsuite/ifuncvar3.c diff --git a/gold/ChangeLog b/gold/ChangeLog index cadee9050d8..028919856a1 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,111 @@ +2011-07-08 Ian Lance Taylor + + PR gold/12372 + * target.h (Target::plt_address_for_global): New function. + (Target::plt_address_for_local): New function. + (Target::plt_section_for_global): Remove. + (Target::plt_section_for_local): Remove. + (Target::do_plt_address_for_global): New virtual function. + (Target::do_plt_address_for_local): New virtual function. + (Target::do_plt_section_for_global): Remove. + (Target::do_plt_section_for_local): Remove. + (Target::register_global_plt_entry): Add Symbol_table and Layout + parameters. + * output.cc (Output_data_got::Got_entry::write): Use + plt_address_for_global and plt_address_for_local. + * layout.cc (Layout::add_target_dynamic_tags): Use size and + address of output section. + * i386.cc (class Output_data_plt_i386): Add irelative_rel_, + got_irelative_, and irelative_count_ fields. Update + declarations. + (Output_data_plt_i386::has_irelative_section): New function. + (Output_data_plt_i386::entry_count): Add irelative_count_. + (Output_data_plt_i386::set_final_data_size): Likewise. + (class Target_i386): Add got_irelative_ and rel_irelative_ + fields. Update declarations. + (Target_i386::Target_i386): Initialize new fields. + (Target_i386::do_plt_address_for_global): New function replacing + do_plt_section_for_global. + (Target_i386::do_plt_address_for_local): New function replacing + do_plt_section_for_local. + (Target_i386::got_section): Create got_irelative_. + (Target_i386::rel_irelative_section): New function. + (Output_data_plt_i386::Output_data_plt_i386): Initialize new + fields. Don't define __rel_iplt_{start,end}. + (Output_data_plt_i386::add_entry): Add symtab and layout + parameters. Change all callers. Use different PLT and GOT for + IFUNC symbols. + (Output_data_plt_i386::add_local_ifunc_entry): Add symtab and + layout parameters. Change all callers. Use different PLT and + GOT. + (Output_data_plt_i386::rel_tls_desc): Fix formatting. + (Output_data_plt_i386::rel_irelative): New function. + (Output_data_plt_i386::address_for_global): New function. + (Output_data_plt_i386::address_for_local): New function. + (Output_data_plt_i386::do_write): Write out IRELATIVE area. Use + IRELATIVE GOT when changing IFUNC GOT entries. + (Target_i386::Scan::global): Use IRELATIVE GOT for IRELATIVE + reloc. + (Target_i386::do_finalize_sections): Create the __rel_iplt symbols + if we didn't create an IRELATIVE GOT. + (Target_i386::Relocate::relocate): Use plt_address_for_global and + plt_address_for_local. + (Target_i386::do_dynsym_value): Use plt_address_for_global. + * x86_64.cc (class Output_data_plt_x86_64): Add irelative_rel_, + got_irelative_, and irelative_count_ fields. Update + declarations. + (Output_data_plt_x86_64::Output_data_plt_x86_64) [both versions]: + Initialize new fields. Remove symtab parameter. Change all + callers. + (Output_data_plt_x86_64::get_tlsdesc_plt_offset): Add + irelative_count_. + (Output_data_plt_x86_64::has_irelative_section): New function. + (Output_data_plt_x86_64::entry_count): Add irelative_count_. + (class Target_x86_64): Add got_irelative_ and rel_irelative_ + fields. Update declarations. + (Target_x86_64::Target_x86_64): Initialize new fields. + (Target_x86_64::do_plt_address_for_global): New function replacing + do_plt_section_for_global. + (Target_x86_64::do_plt_address_for_local): New function replacing + do_plt_section_for_local. + (Target_x86_64::got_section): Create got_irelative_. + (Target_x86_64::rela_irelative_section): New function. + (Output_data_plt_x86_64::init): Remove symtab parameter. Change + all callers. Don't create __rel_iplt_{start,end}. + (Output_data_plt_x86_64::add_entry): Add symtab and layout + parameters. Change all callers. Use different PLT and GOT for + IFUNC symbols. + (Output_data_plt_x86_64::add_local_ifunc_entry): Add symtab and + layout parameters. Change all callers. Use different PLT and + GOT. + (Output_data_plt_x86_64::add_relocation): Add symtab and layout + parameters. Change all callers. Use different PLT and GOT for + IFUNC symbols. + (Output_data_plt_x86_64::rela_tlsdesc): Fix formatting. + (Output_data_plt_x86_64::rela_irelative): New function. + (Output_data_plt_x86_64::address_for_global): New function. + (Output_data_plt_x86_64::address_for_local): New function. + (Output_data_plt_x86_64::set_final_data_size): Likewise. + (Output_data_plt_x86_64::do_write): Write out IRELATIVE area. + (Target_x86_64::init_got_plt_for_update): Create got_irelative_. + (Target_x86_64::register_global_plt_entry): Add symtab and layout + parameters. + (Target_x86_64::Scan::global): Use IRELATIVE GOT for IRELATIVE + reloc. + (Target_x86_64::do_finalize_sections): Create the __rela_iplt + symbols if we didn't create an IRELATIVE GOT. + (Target_x86_64::Relocate::relocate): Use plt_address_for_global and + plt_address_for_local. + (Target_x86_64::do_dynsym_value): Use plt_address_for_global. + * testsuite/ifuncvar1.c: New test file. + * testsuite/ifuncvar2.c: New test file. + * testsuite/ifuncvar3.c: New test file. + * testsuite/Makefile.am (check_PROGRAMS): Add ifuncvar. + (ifuncvar1_pic.o, ifuncvar2_pic.o, ifuncvar.so): New targets. + (ifuncvar_SOURCES, ifuncvar_DEPENDENCIES): New variables. + (ifuncvar_LDFLAGS, ifuncvar_LDADD): New variables. + * testsuite/Makefile.in: Rebuild. + 2011-07-07 Cary Coutant * testsuite/Makefile.am (two_file_test_1_v1_ndebug.o): New target. diff --git a/gold/i386.cc b/gold/i386.cc index afb9c31f0ae..6c6c4b41489 100644 --- a/gold/i386.cc +++ b/gold/i386.cc @@ -53,15 +53,16 @@ class Output_data_plt_i386 : public Output_section_data public: typedef Output_data_reloc Reloc_section; - Output_data_plt_i386(Symbol_table*, Layout*, Output_data_space*); + Output_data_plt_i386(Layout*, Output_data_space*, Output_data_space*); // Add an entry to the PLT. void - add_entry(Symbol* gsym); + add_entry(Symbol_table*, Layout*, Symbol* gsym); // Add an entry to the PLT for a local STT_GNU_IFUNC symbol. unsigned int - add_local_ifunc_entry(Sized_relobj_file<32, false>* relobj, + add_local_ifunc_entry(Symbol_table*, Layout*, + Sized_relobj_file<32, false>* relobj, unsigned int local_sym_index); // Return the .rel.plt section data. @@ -73,10 +74,19 @@ class Output_data_plt_i386 : public Output_section_data Reloc_section* rel_tls_desc(Layout*); + // Return where the IRELATIVE relocations should go. + Reloc_section* + rel_irelative(Symbol_table*, Layout*); + + // Return whether we created a section for IRELATIVE relocations. + bool + has_irelative_section() const + { return this->irelative_rel_ != NULL; } + // Return the number of PLT entries. unsigned int entry_count() const - { return this->count_; } + { return this->count_ + this->irelative_count_; } // Return the offset of the first non-reserved PLT entry. static unsigned int @@ -88,6 +98,14 @@ class Output_data_plt_i386 : public Output_section_data get_plt_entry_size() { return plt_entry_size; } + // Return the PLT address to use for a global symbol. + uint64_t + address_for_global(const Symbol*); + + // Return the PLT address to use for a local symbol. + uint64_t + address_for_local(const Relobj*, unsigned int symndx); + protected: void do_adjust_output_section(Output_section* os); @@ -122,7 +140,10 @@ class Output_data_plt_i386 : public Output_section_data // Set the final size. void set_final_data_size() - { this->set_data_size((this->count_ + 1) * plt_entry_size); } + { + this->set_data_size((this->count_ + this->irelative_count_ + 1) + * plt_entry_size); + } // Write out the PLT data. void @@ -150,10 +171,18 @@ class Output_data_plt_i386 : public Output_section_data // The TLS_DESC relocations, if necessary. These must follow the // regular PLT relocs. Reloc_section* tls_desc_rel_; + // The IRELATIVE relocations, if necessary. These must follow the + // regular relocatoins and the TLS_DESC relocations. + Reloc_section* irelative_rel_; // The .got.plt section. Output_data_space* got_plt_; + // The part of the .got.plt section used for IRELATIVE relocs. + Output_data_space* got_irelative_; // The number of PLT entries. unsigned int count_; + // Number of PLT entries with R_386_IRELATIVE relocs. These follow + // the regular PLT entries. + unsigned int irelative_count_; // Global STT_GNU_IFUNC symbols. std::vector global_ifuncs_; // Local STT_GNU_IFUNC symbols. @@ -172,9 +201,9 @@ class Target_i386 : public Sized_target<32, false> Target_i386() : Sized_target<32, false>(&i386_info), - got_(NULL), plt_(NULL), got_plt_(NULL), got_tlsdesc_(NULL), - global_offset_table_(NULL), rel_dyn_(NULL), - copy_relocs_(elfcpp::R_386_COPY), dynbss_(NULL), + got_(NULL), plt_(NULL), got_plt_(NULL), got_irelative_(NULL), + got_tlsdesc_(NULL), global_offset_table_(NULL), rel_dyn_(NULL), + rel_irelative_(NULL), copy_relocs_(elfcpp::R_386_COPY), dynbss_(NULL), got_mod_index_offset_(-1U), tls_base_symbol_defined_(false) { } @@ -281,14 +310,14 @@ class Target_i386 : public Sized_target<32, false> return Target::do_is_local_label_name(name); } - // Return the PLT section. - Output_data* - do_plt_section_for_global(const Symbol*) const - { return this->plt_section(); } + // Return the PLT address to use for a global symbol. + uint64_t + do_plt_address_for_global(const Symbol* gsym) const + { return this->plt_section()->address_for_global(gsym); } - Output_data* - do_plt_section_for_local(const Relobj*, unsigned int) const - { return this->plt_section(); } + uint64_t + do_plt_address_for_local(const Relobj* relobj, unsigned int symndx) const + { return this->plt_section()->address_for_local(relobj, symndx); } // We can tell whether we take the address of a function. inline bool @@ -589,6 +618,10 @@ class Target_i386 : public Sized_target<32, false> Reloc_section* rel_tls_desc_section(Layout*) const; + // Get the section to use for IRELATIVE relocations. + Reloc_section* + rel_irelative_section(Layout*); + // Add a potential copy relocation. void copy_reloc(Symbol_table* symtab, Layout* layout, @@ -625,12 +658,16 @@ class Target_i386 : public Sized_target<32, false> Output_data_plt_i386* plt_; // The GOT PLT section. Output_data_space* got_plt_; + // The GOT section for IRELATIVE relocations. + Output_data_space* got_irelative_; // The GOT section for TLSDESC relocations. Output_data_got<32, false>* got_tlsdesc_; // The _GLOBAL_OFFSET_TABLE_ symbol. Symbol* global_offset_table_; // The dynamic reloc section. Reloc_section* rel_dyn_; + // The section to use for IRELATIVE relocs. + Reloc_section* rel_irelative_; // Relocs saved to avoid a COPY reloc. Copy_relocs copy_relocs_; // Space for variables copied with a COPY reloc. @@ -703,6 +740,15 @@ Target_i386::got_section(Symbol_table* symtab, Layout* layout) elfcpp::STV_HIDDEN, 0, false, false); + // If there are any IRELATIVE relocations, they get GOT entries + // in .got.plt after the jump slot relocations. + this->got_irelative_ = new Output_data_space(4, "** GOT IRELATIVE PLT"); + layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS, + (elfcpp::SHF_ALLOC + | elfcpp::SHF_WRITE), + this->got_irelative_, + ORDER_NON_RELRO_FIRST, false); + // If there are any TLSDESC relocations, they get GOT entries in // .got.plt after the jump slot entries. this->got_tlsdesc_ = new Output_data_got<32, false>(); @@ -732,39 +778,45 @@ Target_i386::rel_dyn_section(Layout* layout) return this->rel_dyn_; } +// Get the section to use for IRELATIVE relocs, creating it if +// necessary. These go in .rel.dyn, but only after all other dynamic +// relocations. They need to follow the other dynamic relocations so +// that they can refer to global variables initialized by those +// relocs. + +Target_i386::Reloc_section* +Target_i386::rel_irelative_section(Layout* layout) +{ + if (this->rel_irelative_ == NULL) + { + // Make sure we have already create the dynamic reloc section. + this->rel_dyn_section(layout); + this->rel_irelative_ = new Reloc_section(false); + layout->add_output_section_data(".rel.dyn", elfcpp::SHT_REL, + elfcpp::SHF_ALLOC, this->rel_irelative_, + ORDER_DYNAMIC_RELOCS, false); + gold_assert(this->rel_dyn_->output_section() + == this->rel_irelative_->output_section()); + } + return this->rel_irelative_; +} + // Create the PLT section. The ordinary .got section is an argument, // since we need to refer to the start. We also create our own .got // section just for PLT entries. -Output_data_plt_i386::Output_data_plt_i386(Symbol_table* symtab, - Layout* layout, - Output_data_space* got_plt) - : Output_section_data(16), tls_desc_rel_(NULL), got_plt_(got_plt), count_(0), - global_ifuncs_(), local_ifuncs_() +Output_data_plt_i386::Output_data_plt_i386(Layout* layout, + Output_data_space* got_plt, + Output_data_space* got_irelative) + : Output_section_data(16), tls_desc_rel_(NULL), irelative_rel_(NULL), + got_plt_(got_plt), got_irelative_(got_irelative), count_(0), + irelative_count_(0), global_ifuncs_(), local_ifuncs_() { this->rel_ = new Reloc_section(false); layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL, elfcpp::SHF_ALLOC, this->rel_, ORDER_DYNAMIC_PLT_RELOCS, false); - if (parameters->doing_static_link()) - { - // A statically linked executable will only have a .rel.plt - // section to hold R_386_IRELATIVE relocs for STT_GNU_IFUNC - // symbols. The library will use these symbols to locate the - // IRELATIVE relocs at program startup time. - symtab->define_in_output_data("__rel_iplt_start", NULL, - Symbol_table::PREDEFINED, - this->rel_, 0, 0, elfcpp::STT_NOTYPE, - elfcpp::STB_GLOBAL, elfcpp::STV_HIDDEN, - 0, false, true); - symtab->define_in_output_data("__rel_iplt_end", NULL, - Symbol_table::PREDEFINED, - this->rel_, 0, 0, elfcpp::STT_NOTYPE, - elfcpp::STB_GLOBAL, elfcpp::STV_HIDDEN, - 0, true, true); - } - // Add unwind information if requested. if (parameters->options().ld_generated_unwind_info()) layout->add_eh_frame_for_plt(this, plt_eh_frame_cie, plt_eh_frame_cie_size, @@ -782,29 +834,23 @@ Output_data_plt_i386::do_adjust_output_section(Output_section* os) // Add an entry to the PLT. void -Output_data_plt_i386::add_entry(Symbol* gsym) +Output_data_plt_i386::add_entry(Symbol_table* symtab, Layout* layout, + Symbol* gsym) { gold_assert(!gsym->has_plt_offset()); - // Note that when setting the PLT offset we skip the initial - // reserved PLT entry. - gsym->set_plt_offset((this->count_ + 1) * plt_entry_size); - - ++this->count_; - - section_offset_type got_offset = this->got_plt_->current_data_size(); - - // Every PLT entry needs a GOT entry which points back to the PLT - // entry (this will be changed by the dynamic linker, normally - // lazily when the function is called). - this->got_plt_->set_current_data_size(got_offset + 4); - // Every PLT entry needs a reloc. if (gsym->type() == elfcpp::STT_GNU_IFUNC && gsym->can_use_relative_reloc(false)) { - this->rel_->add_symbolless_global_addend(gsym, elfcpp::R_386_IRELATIVE, - this->got_plt_, got_offset); + gsym->set_plt_offset(this->irelative_count_ * plt_entry_size); + ++this->irelative_count_; + section_offset_type got_offset = + this->got_irelative_->current_data_size(); + this->got_irelative_->set_current_data_size(got_offset + 4); + Reloc_section* rel = this->rel_irelative(symtab, layout); + rel->add_symbolless_global_addend(gsym, elfcpp::R_386_IRELATIVE, + this->got_irelative_, got_offset); struct Global_ifunc gi; gi.sym = gsym; gi.got_offset = got_offset; @@ -812,6 +858,19 @@ Output_data_plt_i386::add_entry(Symbol* gsym) } else { + // When setting the PLT offset we skip the initial reserved PLT + // entry. + gsym->set_plt_offset((this->count_ + 1) * plt_entry_size); + + ++this->count_; + + section_offset_type got_offset = this->got_plt_->current_data_size(); + + // Every PLT entry needs a GOT entry which points back to the + // PLT entry (this will be changed by the dynamic linker, + // normally lazily when the function is called). + this->got_plt_->set_current_data_size(got_offset + 4); + gsym->set_needs_dynsym_entry(); this->rel_->add_global(gsym, elfcpp::R_386_JUMP_SLOT, this->got_plt_, got_offset); @@ -827,22 +886,25 @@ Output_data_plt_i386::add_entry(Symbol* gsym) unsigned int Output_data_plt_i386::add_local_ifunc_entry( + Symbol_table* symtab, + Layout* layout, Sized_relobj_file<32, false>* relobj, unsigned int local_sym_index) { - unsigned int plt_offset = (this->count_ + 1) * plt_entry_size; - ++this->count_; + unsigned int plt_offset = this->irelative_count_ * plt_entry_size; + ++this->irelative_count_; - section_offset_type got_offset = this->got_plt_->current_data_size(); + section_offset_type got_offset = this->got_irelative_->current_data_size(); // Every PLT entry needs a GOT entry which points back to the PLT // entry. - this->got_plt_->set_current_data_size(got_offset + 4); + this->got_irelative_->set_current_data_size(got_offset + 4); // Every PLT entry needs a reloc. - this->rel_->add_symbolless_local_addend(relobj, local_sym_index, - elfcpp::R_386_IRELATIVE, - this->got_plt_, got_offset); + Reloc_section* rel = this->rel_irelative(symtab, layout); + rel->add_symbolless_local_addend(relobj, local_sym_index, + elfcpp::R_386_IRELATIVE, + this->got_irelative_, got_offset); struct Local_ifunc li; li.object = relobj; @@ -865,12 +927,72 @@ Output_data_plt_i386::rel_tls_desc(Layout* layout) layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL, elfcpp::SHF_ALLOC, this->tls_desc_rel_, ORDER_DYNAMIC_PLT_RELOCS, false); - gold_assert(this->tls_desc_rel_->output_section() == - this->rel_->output_section()); + gold_assert(this->tls_desc_rel_->output_section() + == this->rel_->output_section()); } return this->tls_desc_rel_; } +// Return where the IRELATIVE relocations should go in the PLT. These +// follow the JUMP_SLOT and TLS_DESC relocations. + +Output_data_plt_i386::Reloc_section* +Output_data_plt_i386::rel_irelative(Symbol_table* symtab, Layout* layout) +{ + if (this->irelative_rel_ == NULL) + { + // Make sure we have a place for the TLS_DESC relocations, in + // case we see any later on. + this->rel_tls_desc(layout); + this->irelative_rel_ = new Reloc_section(false); + layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL, + elfcpp::SHF_ALLOC, this->irelative_rel_, + ORDER_DYNAMIC_PLT_RELOCS, false); + gold_assert(this->irelative_rel_->output_section() + == this->rel_->output_section()); + + if (parameters->doing_static_link()) + { + // A statically linked executable will only have a .rel.plt + // section to hold R_386_IRELATIVE relocs for STT_GNU_IFUNC + // symbols. The library will use these symbols to locate + // the IRELATIVE relocs at program startup time. + symtab->define_in_output_data("__rel_iplt_start", NULL, + Symbol_table::PREDEFINED, + this->irelative_rel_, 0, 0, + elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL, + elfcpp::STV_HIDDEN, 0, false, true); + symtab->define_in_output_data("__rel_iplt_end", NULL, + Symbol_table::PREDEFINED, + this->irelative_rel_, 0, 0, + elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL, + elfcpp::STV_HIDDEN, 0, true, true); + } + } + return this->irelative_rel_; +} + +// Return the PLT address to use for a global symbol. + +uint64_t +Output_data_plt_i386::address_for_global(const Symbol* gsym) +{ + uint64_t offset = 0; + if (gsym->type() == elfcpp::STT_GNU_IFUNC + && gsym->can_use_relative_reloc(false)) + offset = (this->count_ + 1) * plt_entry_size; + return this->address() + offset; +} + +// Return the PLT address to use for a local symbol. These are always +// IRELATIVE relocs. + +uint64_t +Output_data_plt_i386::address_for_local(const Relobj*, unsigned int) +{ + return this->address() + (this->count_ + 1) * plt_entry_size; +} + // The first entry in the PLT for an executable. const unsigned char Output_data_plt_i386::exec_first_plt_entry[plt_entry_size] = @@ -976,8 +1098,12 @@ Output_data_plt_i386::do_write(Output_file* of) unsigned char* const oview = of->get_output_view(offset, oview_size); const off_t got_file_offset = this->got_plt_->offset(); + gold_assert(parameters->incremental_update() + || (got_file_offset + this->got_plt_->data_size() + == this->got_irelative_->offset())); const section_size_type got_size = - convert_to_section_size_type(this->got_plt_->data_size()); + convert_to_section_size_type(this->got_plt_->data_size() + + this->got_irelative_->data_size()); unsigned char* const got_view = of->get_output_view(got_file_offset, got_size); @@ -1006,7 +1132,7 @@ Output_data_plt_i386::do_write(Output_file* of) unsigned int plt_offset = plt_entry_size; unsigned int plt_rel_offset = 0; unsigned int got_offset = 12; - const unsigned int count = this->count_; + const unsigned int count = this->count_ + this->irelative_count_; for (unsigned int i = 0; i < count; ++i, @@ -1043,6 +1169,7 @@ Output_data_plt_i386::do_write(Output_file* of) // the GOT to point to the actual symbol value, rather than point to // the PLT entry. That will let the dynamic linker call the right // function when resolving IRELATIVE relocations. + unsigned char* got_irelative_view = got_view + this->got_plt_->data_size(); for (std::vector::const_iterator p = this->global_ifuncs_.begin(); p != this->global_ifuncs_.end(); @@ -1050,7 +1177,7 @@ Output_data_plt_i386::do_write(Output_file* of) { const Sized_symbol<32>* ssym = static_cast*>(p->sym); - elfcpp::Swap<32, false>::writeval(got_view + p->got_offset, + elfcpp::Swap<32, false>::writeval(got_irelative_view + p->got_offset, ssym->value()); } @@ -1061,7 +1188,7 @@ Output_data_plt_i386::do_write(Output_file* of) { const Symbol_value<32>* psymval = p->object->local_symbol(p->local_sym_index); - elfcpp::Swap<32, false>::writeval(got_view + p->got_offset, + elfcpp::Swap<32, false>::writeval(got_irelative_view + p->got_offset, psymval->value(p->object, 0)); } @@ -1082,7 +1209,8 @@ Target_i386::make_plt_section(Symbol_table* symtab, Layout* layout) // Create the GOT sections first. this->got_section(symtab, layout); - this->plt_ = new Output_data_plt_i386(symtab, layout, this->got_plt_); + this->plt_ = new Output_data_plt_i386(layout, this->got_plt_, + this->got_irelative_); layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS, (elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR), @@ -1103,7 +1231,7 @@ Target_i386::make_plt_entry(Symbol_table* symtab, Layout* layout, Symbol* gsym) return; if (this->plt_ == NULL) this->make_plt_section(symtab, layout); - this->plt_->add_entry(gsym); + this->plt_->add_entry(symtab, layout, gsym); } // Make a PLT entry for a local STT_GNU_IFUNC symbol. @@ -1117,7 +1245,8 @@ Target_i386::make_local_ifunc_plt_entry(Symbol_table* symtab, Layout* layout, return; if (this->plt_ == NULL) this->make_plt_section(symtab, layout); - unsigned int plt_offset = this->plt_->add_local_ifunc_entry(relobj, + unsigned int plt_offset = this->plt_->add_local_ifunc_entry(symtab, layout, + relobj, local_sym_index); relobj->set_local_plt_offset(local_sym_index, plt_offset); } @@ -1779,7 +1908,7 @@ Target_i386::Scan::global(Symbol_table* symtab, // STT_GNU_IFUNC symbol. This makes a function // address in a PIE executable match the address in a // shared library that it links against. - Reloc_section* rel_dyn = target->rel_dyn_section(layout); + Reloc_section* rel_dyn = target->rel_irelative_section(layout); rel_dyn->add_symbolless_global_addend(gsym, elfcpp::R_386_IRELATIVE, output_section, @@ -2182,7 +2311,8 @@ Target_i386::do_finalize_sections( symtab->get_sized_symbol<32>(sym)->set_symsize(data_size); } - if (parameters->doing_static_link() && this->plt_ == NULL) + if (parameters->doing_static_link() + && (this->plt_ == NULL || !this->plt_->has_irelative_section())) { // If linking statically, make sure that the __rel_iplt symbols // were defined if necessary, even if we didn't create a PLT. @@ -2312,7 +2442,7 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo, else if (gsym != NULL && gsym->use_plt_offset(Scan::get_reference_flags(r_type))) { - symval.set_output_value(target->plt_section()->address() + symval.set_output_value(target->plt_address_for_global(gsym) + gsym->plt_offset()); psymval = &symval; } @@ -2321,7 +2451,7 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo, unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info()); if (object->local_has_plt_offset(r_sym)) { - symval.set_output_value(target->plt_section()->address() + symval.set_output_value(target->plt_address_for_local(object, r_sym) + object->local_plt_offset(r_sym)); psymval = &symval; } @@ -3245,7 +3375,7 @@ uint64_t Target_i386::do_dynsym_value(const Symbol* gsym) const { gold_assert(gsym->is_from_dynobj() && gsym->has_plt_offset()); - return this->plt_section()->address() + gsym->plt_offset(); + return this->plt_address_for_global(gsym) + gsym->plt_offset(); } // Return a string used to fill a code section with nops to take up diff --git a/gold/incremental.cc b/gold/incremental.cc index cb97044a3d8..e3b48a4471e 100644 --- a/gold/incremental.cc +++ b/gold/incremental.cc @@ -1,6 +1,6 @@ // inremental.cc -- incremental linking support for gold -// Copyright 2009, 2010 Free Software Foundation, Inc. +// Copyright 2009, 2010, 2011 Free Software Foundation, Inc. // Written by Mikolaj Zalewski . // This file is part of gold. @@ -674,7 +674,7 @@ Sized_incremental_binary::do_process_got_plt( gold_debug(DEBUG_INCREMENTAL, "PLT entry %d: %s", i, sym->name()); - target->register_global_plt_entry(i, sym); + target->register_global_plt_entry(symtab, layout, i, sym); } } } diff --git a/gold/layout.cc b/gold/layout.cc index 3c3b5b35ab4..84914c9db72 100644 --- a/gold/layout.cc +++ b/gold/layout.cc @@ -4027,7 +4027,8 @@ Layout::create_interp(const Target* target) // some targets have multiple reloc sections in PLT_REL. // If DYN_REL is not NULL, it is used for DT_REL/DT_RELA, -// DT_RELSZ/DT_RELASZ, DT_RELENT/DT_RELAENT. +// DT_RELSZ/DT_RELASZ, DT_RELENT/DT_RELAENT. Again we use the output +// section. // If ADD_DEBUG is true, we add a DT_DEBUG entry when generating an // executable. @@ -4056,13 +4057,16 @@ Layout::add_target_dynamic_tags(bool use_rel, const Output_data* plt_got, if (dyn_rel != NULL && dyn_rel->output_section() != NULL) { odyn->add_section_address(use_rel ? elfcpp::DT_REL : elfcpp::DT_RELA, - dyn_rel); - if (plt_rel != NULL && dynrel_includes_plt) + dyn_rel->output_section()); + if (plt_rel != NULL + && plt_rel->output_section() != NULL + && dynrel_includes_plt) odyn->add_section_size(use_rel ? elfcpp::DT_RELSZ : elfcpp::DT_RELASZ, - dyn_rel, plt_rel); + dyn_rel->output_section(), + plt_rel->output_section()); else odyn->add_section_size(use_rel ? elfcpp::DT_RELSZ : elfcpp::DT_RELASZ, - dyn_rel); + dyn_rel->output_section()); const int size = parameters->target().get_size(); elfcpp::DT rel_tag; int rel_size; diff --git a/gold/output.cc b/gold/output.cc index b9cfafd9cd2..b72bd8d4768 100644 --- a/gold/output.cc +++ b/gold/output.cc @@ -1347,7 +1347,7 @@ Output_data_got::Got_entry::write(unsigned char* pov) const // RELATIVE relocation. Symbol* gsym = this->u_.gsym; if (this->use_plt_offset_ && gsym->has_plt_offset()) - val = (parameters->target().plt_section_for_global(gsym)->address() + val = (parameters->target().plt_address_for_global(gsym) + gsym->plt_offset()); else { @@ -1381,9 +1381,9 @@ Output_data_got::Got_entry::write(unsigned char* pov) const val = symval->value(this->u_.object, 0); else { - const Output_data* plt = - parameters->target().plt_section_for_local(object, lsi); - val = plt->address() + object->local_plt_offset(lsi); + uint64_t plt_address = + parameters->target().plt_address_for_local(object, lsi); + val = plt_address + object->local_plt_offset(lsi); } } break; diff --git a/gold/target.h b/gold/target.h index c2ccc638c94..a378120325c 100644 --- a/gold/target.h +++ b/gold/target.h @@ -246,17 +246,19 @@ class Target reloc_addend(void* arg, unsigned int type, uint64_t addend) const { return this->do_reloc_addend(arg, type, addend); } - // Return the PLT section to use for a global symbol. This is used - // for STT_GNU_IFUNC symbols. - Output_data* - plt_section_for_global(const Symbol* sym) const - { return this->do_plt_section_for_global(sym); } - - // Return the PLT section to use for a local symbol. This is used - // for STT_GNU_IFUNC symbols. - Output_data* - plt_section_for_local(const Relobj* object, unsigned int symndx) const - { return this->do_plt_section_for_local(object, symndx); } + // Return the PLT address to use for a global symbol. This is used + // for STT_GNU_IFUNC symbols. The symbol's plt_offset is relative + // to this PLT address. + uint64_t + plt_address_for_global(const Symbol* sym) const + { return this->do_plt_address_for_global(sym); } + + // Return the PLT address to use for a local symbol. This is used + // for STT_GNU_IFUNC symbols. The symbol's plt_offset is relative + // to this PLT address. + uint64_t + plt_address_for_local(const Relobj* object, unsigned int symndx) const + { return this->do_plt_address_for_local(object, symndx); } // Return whether this target can use relocation types to determine // if a function's address is taken. @@ -502,12 +504,12 @@ class Target // Virtual functions that must be overridden by a target that uses // STT_GNU_IFUNC symbols. - virtual Output_data* - do_plt_section_for_global(const Symbol*) const + virtual uint64_t + do_plt_address_for_global(const Symbol*) const { gold_unreachable(); } - virtual Output_data* - do_plt_section_for_local(const Relobj*, unsigned int) const + virtual uint64_t + do_plt_address_for_local(const Relobj*, unsigned int) const { gold_unreachable(); } // Virtual function which may be overriden by the child class. @@ -870,7 +872,8 @@ class Sized_target : public Target // A target needs to implement this to support incremental linking. virtual void - register_global_plt_entry(unsigned int /* plt_index */, + register_global_plt_entry(Symbol_table*, Layout*, + unsigned int /* plt_index */, Symbol*) { gold_unreachable(); } diff --git a/gold/testsuite/Makefile.am b/gold/testsuite/Makefile.am index a01218aceca..0e9e3b11928 100644 --- a/gold/testsuite/Makefile.am +++ b/gold/testsuite/Makefile.am @@ -1833,6 +1833,18 @@ check_PROGRAMS += ifuncmain7pie ifuncmain7pie: ifuncmain7pie.o gcctestdir/ld $(LINK) -Bgcctestdir/ -pie ifuncmain7pie.o +check_PROGRAMS += ifuncvar +ifuncvar1_pic.o: ifuncvar1.c + $(COMPILE) -c -fpic -o $@ $< +ifuncvar2_pic.o: ifuncvar2.c + $(COMPILE) -c -fpic -o $@ $< +ifuncvar.so: ifuncvar1_pic.o ifuncvar2_pic.o gcctestdir/ld + $(LINK) -Bgcctestdir/ -shared ifuncvar1_pic.o ifuncvar2_pic.o +ifuncvar_SOURCES = ifuncvar3.c +ifuncvar_DEPENDENCIES = gcctestdir/ld ifuncvar.so +ifuncvar_LDFLAGS = -Bgcctestdir/ -Wl,-R,. +ifuncvar_LDADD = ifuncvar.so + endif IFUNC # Test that strong reference to a weak symbol in a DSO remains strong. diff --git a/gold/testsuite/Makefile.in b/gold/testsuite/Makefile.in index 760bf24f7a6..5d8560e04ba 100644 --- a/gold/testsuite/Makefile.in +++ b/gold/testsuite/Makefile.in @@ -442,7 +442,8 @@ check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \ @NATIVE_LINKER_FALSE@ifuncmain7static_DEPENDENCIES = @GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@am__append_52 = ifuncmain7 \ @GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@ ifuncmain7pic \ -@GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@ ifuncmain7pie +@GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@ ifuncmain7pie \ +@GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@ ifuncvar @GCC_FALSE@ifuncmain7_DEPENDENCIES = @IFUNC_FALSE@ifuncmain7_DEPENDENCIES = @NATIVE_LINKER_FALSE@ifuncmain7_DEPENDENCIES = @@ -722,7 +723,8 @@ libgoldtest_a_OBJECTS = $(am_libgoldtest_a_OBJECTS) @GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@am__EXEEXT_36 = \ @GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@ ifuncmain7$(EXEEXT) \ @GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@ ifuncmain7pic$(EXEEXT) \ -@GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@ ifuncmain7pie$(EXEEXT) +@GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@ ifuncmain7pie$(EXEEXT) \ +@GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@ ifuncvar$(EXEEXT) @GCC_TRUE@@NATIVE_LINKER_TRUE@am__EXEEXT_37 = start_lib_test$(EXEEXT) @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@am__EXEEXT_38 = incremental_test_2$(EXEEXT) \ @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ incremental_test_3$(EXEEXT) \ @@ -1053,6 +1055,11 @@ ifuncmain7pie_DEPENDENCIES = libgoldtest.a ../libgold.a \ ifuncmain7static_OBJECTS = $(am_ifuncmain7static_OBJECTS) ifuncmain7static_LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(ifuncmain7static_LDFLAGS) $(LDFLAGS) -o $@ +@GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@am_ifuncvar_OBJECTS = \ +@GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@ ifuncvar3.$(OBJEXT) +ifuncvar_OBJECTS = $(am_ifuncvar_OBJECTS) +ifuncvar_LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(ifuncvar_LDFLAGS) \ + $(LDFLAGS) -o $@ incremental_common_test_1_SOURCES = incremental_common_test_1.c incremental_common_test_1_OBJECTS = \ incremental_common_test_1.$(OBJEXT) @@ -1594,17 +1601,17 @@ SOURCES = $(libgoldtest_a_SOURCES) basic_pic_test.c basic_pie_test.c \ $(ifuncmain5static_SOURCES) ifuncmain5staticpic.c \ ifuncmain6pie.c $(ifuncmain7_SOURCES) ifuncmain7pic.c \ ifuncmain7picstatic.c ifuncmain7pie.c \ - $(ifuncmain7static_SOURCES) incremental_common_test_1.c \ - incremental_copy_test.c incremental_test_2.c \ - incremental_test_3.c incremental_test_4.c incremental_test_5.c \ - incremental_test_6.c $(initpri1_SOURCES) $(initpri2_SOURCES) \ - $(initpri3a_SOURCES) $(initpri3b_SOURCES) $(justsyms_SOURCES) \ - $(large_SOURCES) local_labels_test.c many_sections_r_test.c \ - $(many_sections_test_SOURCES) $(object_unittest_SOURCES) \ - permission_test.c plugin_test_1.c plugin_test_2.c \ - plugin_test_3.c plugin_test_4.c plugin_test_5.c \ - plugin_test_6.c plugin_test_7.c plugin_test_8.c \ - $(protected_1_SOURCES) $(protected_2_SOURCES) \ + $(ifuncmain7static_SOURCES) $(ifuncvar_SOURCES) \ + incremental_common_test_1.c incremental_copy_test.c \ + incremental_test_2.c incremental_test_3.c incremental_test_4.c \ + incremental_test_5.c incremental_test_6.c $(initpri1_SOURCES) \ + $(initpri2_SOURCES) $(initpri3a_SOURCES) $(initpri3b_SOURCES) \ + $(justsyms_SOURCES) $(large_SOURCES) local_labels_test.c \ + many_sections_r_test.c $(many_sections_test_SOURCES) \ + $(object_unittest_SOURCES) permission_test.c plugin_test_1.c \ + plugin_test_2.c plugin_test_3.c plugin_test_4.c \ + plugin_test_5.c plugin_test_6.c plugin_test_7.c \ + plugin_test_8.c $(protected_1_SOURCES) $(protected_2_SOURCES) \ $(relro_script_test_SOURCES) $(relro_strip_test_SOURCES) \ $(relro_test_SOURCES) $(script_test_1_SOURCES) \ $(script_test_2_SOURCES) script_test_3.c \ @@ -2334,6 +2341,10 @@ LDADD = libgoldtest.a ../libgold.a ../../libiberty/libiberty.a $(LIBINTL) \ @GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@ifuncmain7_DEPENDENCIES = gcctestdir/ld @GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@ifuncmain7_LDFLAGS = -Bgcctestdir/ @GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@ifuncmain7_LDADD = +@GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@ifuncvar_SOURCES = ifuncvar3.c +@GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@ifuncvar_DEPENDENCIES = gcctestdir/ld ifuncvar.so +@GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@ifuncvar_LDFLAGS = -Bgcctestdir/ -Wl,-R,. +@GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@ifuncvar_LDADD = ifuncvar.so @DEFAULT_TARGET_I386_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@SPLIT_DEFSYMS = --defsym __morestack=0x100 --defsym __morestack_non_split=0x200 @DEFAULT_TARGET_X86_64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@SPLIT_DEFSYMS = --defsym __morestack=0x100 --defsym __morestack_non_split=0x200 all: $(BUILT_SOURCES) @@ -2708,6 +2719,9 @@ ifuncmain7$(EXEEXT): $(ifuncmain7_OBJECTS) $(ifuncmain7_DEPENDENCIES) ifuncmain7static$(EXEEXT): $(ifuncmain7static_OBJECTS) $(ifuncmain7static_DEPENDENCIES) @rm -f ifuncmain7static$(EXEEXT) $(ifuncmain7static_LINK) $(ifuncmain7static_OBJECTS) $(ifuncmain7static_LDADD) $(LIBS) +ifuncvar$(EXEEXT): $(ifuncvar_OBJECTS) $(ifuncvar_DEPENDENCIES) + @rm -f ifuncvar$(EXEEXT) + $(ifuncvar_LINK) $(ifuncvar_OBJECTS) $(ifuncvar_LDADD) $(LIBS) @DEFAULT_TARGET_X86_64_FALSE@incremental_common_test_1$(EXEEXT): $(incremental_common_test_1_OBJECTS) $(incremental_common_test_1_DEPENDENCIES) @DEFAULT_TARGET_X86_64_FALSE@ @rm -f incremental_common_test_1$(EXEEXT) @DEFAULT_TARGET_X86_64_FALSE@ $(LINK) $(incremental_common_test_1_OBJECTS) $(incremental_common_test_1_LDADD) $(LIBS) @@ -3150,6 +3164,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ifuncmain7pic.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ifuncmain7picstatic.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ifuncmain7pie.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ifuncvar3.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/incremental_common_test_1.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/incremental_copy_test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/incremental_test_2.Po@am__quote@ @@ -3839,6 +3854,8 @@ ifuncmain7pic.log: ifuncmain7pic$(EXEEXT) @p='ifuncmain7pic$(EXEEXT)'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post) ifuncmain7pie.log: ifuncmain7pie$(EXEEXT) @p='ifuncmain7pie$(EXEEXT)'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post) +ifuncvar.log: ifuncvar$(EXEEXT) + @p='ifuncvar$(EXEEXT)'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post) start_lib_test.log: start_lib_test$(EXEEXT) @p='start_lib_test$(EXEEXT)'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post) incremental_test_2.log: incremental_test_2$(EXEEXT) @@ -4787,6 +4804,12 @@ uninstall-am: @GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@ $(LINK) -Bgcctestdir/ ifuncmain7pic.o @GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@ifuncmain7pie: ifuncmain7pie.o gcctestdir/ld @GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@ $(LINK) -Bgcctestdir/ -pie ifuncmain7pie.o +@GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@ifuncvar1_pic.o: ifuncvar1.c +@GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@ $(COMPILE) -c -fpic -o $@ $< +@GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@ifuncvar2_pic.o: ifuncvar2.c +@GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@ $(COMPILE) -c -fpic -o $@ $< +@GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@ifuncvar.so: ifuncvar1_pic.o ifuncvar2_pic.o gcctestdir/ld +@GCC_TRUE@@IFUNC_TRUE@@NATIVE_LINKER_TRUE@ $(LINK) -Bgcctestdir/ -shared ifuncvar1_pic.o ifuncvar2_pic.o @GCC_TRUE@@NATIVE_LINKER_TRUE@strong_ref_weak_def_2.o: strong_ref_weak_def_2.c @GCC_TRUE@@NATIVE_LINKER_TRUE@ $(COMPILE) -o $@ -c -fPIC $< @GCC_TRUE@@NATIVE_LINKER_TRUE@strong_ref_weak_def_2.so: strong_ref_weak_def_2.o gcctestdir/ld diff --git a/gold/testsuite/ifuncvar1.c b/gold/testsuite/ifuncvar1.c new file mode 100644 index 00000000000..75af2a6a930 --- /dev/null +++ b/gold/testsuite/ifuncvar1.c @@ -0,0 +1,20 @@ +/* Test global variable initialized to hidden STT_GNU_IFUNC symbol. */ + +int didit; + +extern void doit (void); + +void +doit (void) +{ + didit = 1; +} + +void (*get_foo (void)) (void) __asm__ ("foo"); +__asm__ (".type foo, %gnu_indirect_function"); +__asm__ (".hidden foo"); + +void (*get_foo (void)) (void) +{ + return &doit; +} diff --git a/gold/testsuite/ifuncvar2.c b/gold/testsuite/ifuncvar2.c new file mode 100644 index 00000000000..f09de0e12a1 --- /dev/null +++ b/gold/testsuite/ifuncvar2.c @@ -0,0 +1,12 @@ +/* Test global variable initialized to hidden STT_GNU_IFUNC symbol. */ + +extern void foo (void); +void (*f) (void) = &foo; + +extern void bar (void); + +void +bar (void) +{ + f (); +} diff --git a/gold/testsuite/ifuncvar3.c b/gold/testsuite/ifuncvar3.c new file mode 100644 index 00000000000..e078b567e2e --- /dev/null +++ b/gold/testsuite/ifuncvar3.c @@ -0,0 +1,14 @@ +/* Test global variable initialized to hidden STT_GNU_IFUNC symbol. */ + +#include + +extern void bar (void); +extern int didit; + +int +main (void) +{ + bar (); + assert (didit == 1); + return 0; +} diff --git a/gold/x86_64.cc b/gold/x86_64.cc index adcfd42d94c..da4efeda2a3 100644 --- a/gold/x86_64.cc +++ b/gold/x86_64.cc @@ -54,22 +54,24 @@ class Output_data_plt_x86_64 : public Output_section_data public: typedef Output_data_reloc Reloc_section; - Output_data_plt_x86_64(Symbol_table* symtab, Layout* layout, - Output_data_got<64, false>* got, - Output_data_space* got_plt) - : Output_section_data(16), tlsdesc_rel_(NULL), got_(got), got_plt_(got_plt), - count_(0), tlsdesc_got_offset_(-1U), free_list_() - { this->init(symtab, layout); } - - Output_data_plt_x86_64(Symbol_table* symtab, Layout* layout, - Output_data_got<64, false>* got, + Output_data_plt_x86_64(Layout* layout, Output_data_got<64, false>* got, Output_data_space* got_plt, + Output_data_space* got_irelative) + : Output_section_data(16), tlsdesc_rel_(NULL), irelative_rel_(NULL), + got_(got), got_plt_(got_plt), got_irelative_(got_irelative), count_(0), + irelative_count_(0), tlsdesc_got_offset_(-1U), free_list_() + { this->init(layout); } + + Output_data_plt_x86_64(Layout* layout, Output_data_got<64, false>* got, + Output_data_space* got_plt, + Output_data_space* got_irelative, unsigned int plt_count) : Output_section_data((plt_count + 1) * plt_entry_size, 16, false), - tlsdesc_rel_(NULL), got_(got), got_plt_(got_plt), - count_(plt_count), tlsdesc_got_offset_(-1U), free_list_() + tlsdesc_rel_(NULL), irelative_rel_(NULL), got_(got), got_plt_(got_plt), + got_irelative_(got_irelative), count_(plt_count), irelative_count_(0), + tlsdesc_got_offset_(-1U), free_list_() { - this->init(symtab, layout); + this->init(layout); // Initialize the free list and reserve the first entry. this->free_list_.init((plt_count + 1) * plt_entry_size, false); @@ -78,20 +80,22 @@ class Output_data_plt_x86_64 : public Output_section_data // Initialize the PLT section. void - init(Symbol_table* symtab, Layout* layout); + init(Layout* layout); // Add an entry to the PLT. void - add_entry(Symbol* gsym); + add_entry(Symbol_table*, Layout*, Symbol* gsym); // Add an entry to the PLT for a local STT_GNU_IFUNC symbol. unsigned int - add_local_ifunc_entry(Sized_relobj_file<64, false>* relobj, + add_local_ifunc_entry(Symbol_table* symtab, Layout*, + Sized_relobj_file<64, false>* relobj, unsigned int local_sym_index); // Add the relocation for a PLT entry. void - add_relocation(Symbol* gsym, unsigned int got_offset); + add_relocation(Symbol_table*, Layout*, Symbol* gsym, + unsigned int got_offset); // Add the reserved TLSDESC_PLT entry to the PLT. void @@ -111,7 +115,7 @@ class Output_data_plt_x86_64 : public Output_section_data // Return the offset of the reserved TLSDESC_PLT entry. unsigned int get_tlsdesc_plt_offset() const - { return (this->count_ + 1) * plt_entry_size; } + { return (this->count_ + this->irelative_count_ + 1) * plt_entry_size; } // Return the .rela.plt section data. Reloc_section* @@ -122,10 +126,20 @@ class Output_data_plt_x86_64 : public Output_section_data Reloc_section* rela_tlsdesc(Layout*); + // Return where the IRELATIVE relocations should go in the PLT + // relocations. + Reloc_section* + rela_irelative(Symbol_table*, Layout*); + + // Return whether we created a section for IRELATIVE relocations. + bool + has_irelative_section() const + { return this->irelative_rel_ != NULL; } + // Return the number of PLT entries. unsigned int entry_count() const - { return this->count_; } + { return this->count_ + this->irelative_count_; } // Return the offset of the first non-reserved PLT entry. static unsigned int @@ -145,6 +159,14 @@ class Output_data_plt_x86_64 : public Output_section_data (plt_index + 2) * plt_entry_size); } + // Return the PLT address to use for a global symbol. + uint64_t + address_for_global(const Symbol*); + + // Return the PLT address to use for a local symbol. + uint64_t + address_for_local(const Relobj*, unsigned int symndx); + protected: void do_adjust_output_section(Output_section* os); @@ -188,12 +210,20 @@ class Output_data_plt_x86_64 : public Output_section_data // The TLSDESC relocs, if necessary. These must follow the regular // PLT relocs. Reloc_section* tlsdesc_rel_; + // The IRELATIVE relocs, if necessary. These must follow the + // regular PLT relocations and the TLSDESC relocations. + Reloc_section* irelative_rel_; // The .got section. Output_data_got<64, false>* got_; // The .got.plt section. Output_data_space* got_plt_; + // The part of the .got.plt section used for IRELATIVE relocs. + Output_data_space* got_irelative_; // The number of PLT entries. unsigned int count_; + // Number of PLT entries with R_X86_64_IRELATIVE relocs. These + // follow the regular PLT entries. + unsigned int irelative_count_; // Offset of the reserved TLSDESC_GOT entry when needed. unsigned int tlsdesc_got_offset_; // List of available regions within the section, for incremental @@ -217,10 +247,10 @@ class Target_x86_64 : public Sized_target<64, false> Target_x86_64() : Sized_target<64, false>(&x86_64_info), - got_(NULL), plt_(NULL), got_plt_(NULL), got_tlsdesc_(NULL), - global_offset_table_(NULL), rela_dyn_(NULL), - copy_relocs_(elfcpp::R_X86_64_COPY), dynbss_(NULL), - got_mod_index_offset_(-1U), tlsdesc_reloc_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), + dynbss_(NULL), got_mod_index_offset_(-1U), tlsdesc_reloc_info_(), tls_base_symbol_defined_(false) { } @@ -332,13 +362,13 @@ class Target_x86_64 : public Sized_target<64, false> do_reloc_addend(void* arg, unsigned int r_type, uint64_t addend) const; // Return the PLT section. - Output_data* - do_plt_section_for_global(const Symbol*) const - { return this->plt_section(); } + uint64_t + do_plt_address_for_global(const Symbol* gsym) const + { return this->plt_section()->address_for_global(gsym); } - Output_data* - do_plt_section_for_local(const Relobj*, unsigned int) const - { return this->plt_section(); } + uint64_t + do_plt_address_for_local(const Relobj* relobj, unsigned int symndx) const + { return this->plt_section()->address_for_local(relobj, symndx); } // This function should be defined in targets that can use relocation // types to determine (implemented in local_reloc_may_be_function_pointer @@ -413,7 +443,8 @@ class Target_x86_64 : public Sized_target<64, false> // Register an existing PLT entry for a global symbol. void - register_global_plt_entry(unsigned int plt_index, Symbol* gsym); + register_global_plt_entry(Symbol_table*, Layout*, unsigned int plt_index, + Symbol* gsym); // Force a COPY relocation for a given symbol. void @@ -683,6 +714,10 @@ class Target_x86_64 : public Sized_target<64, false> Reloc_section* rela_tlsdesc_section(Layout*) const; + // Get the section to use for IRELATIVE relocations. + Reloc_section* + rela_irelative_section(Layout*); + // Add a potential copy relocation. void copy_reloc(Symbol_table* symtab, Layout* layout, @@ -733,12 +768,16 @@ class Target_x86_64 : public Sized_target<64, false> Output_data_plt_x86_64* plt_; // The GOT PLT section. Output_data_space* got_plt_; + // The GOT section for IRELATIVE relocations. + Output_data_space* got_irelative_; // The GOT section for TLSDESC relocations. Output_data_got<64, false>* got_tlsdesc_; // The _GLOBAL_OFFSET_TABLE_ symbol. Symbol* global_offset_table_; // The dynamic reloc section. Reloc_section* rela_dyn_; + // The section to use for IRELATIVE relocs. + Reloc_section* rela_irelative_; // Relocs saved to avoid a COPY reloc. Copy_relocs copy_relocs_; // Space for variables copied with a COPY reloc. @@ -826,8 +865,17 @@ Target_x86_64::got_section(Symbol_table* symtab, Layout* layout) elfcpp::STV_HIDDEN, 0, false, false); + // If there are any IRELATIVE relocations, they get GOT entries + // in .got.plt after the jump slot entries. + this->got_irelative_ = new Output_data_space(8, "** GOT IRELATIVE PLT"); + layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS, + (elfcpp::SHF_ALLOC + | elfcpp::SHF_WRITE), + this->got_irelative_, + ORDER_NON_RELRO_FIRST, false); + // If there are any TLSDESC relocations, they get GOT entries in - // .got.plt after the jump slot entries. + // .got.plt after the jump slot and IRELATIVE entries. this->got_tlsdesc_ = new Output_data_got<64, false>(); layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS, (elfcpp::SHF_ALLOC @@ -855,34 +903,39 @@ Target_x86_64::rela_dyn_section(Layout* layout) return this->rela_dyn_; } +// Get the section to use for IRELATIVE relocs, creating it if +// necessary. These go in .rela.dyn, but only after all other dynamic +// relocations. They need to follow the other dynamic relocations so +// that they can refer to global variables initialized by those +// relocs. + +Target_x86_64::Reloc_section* +Target_x86_64::rela_irelative_section(Layout* layout) +{ + if (this->rela_irelative_ == NULL) + { + // Make sure we have already created the dynamic reloc section. + this->rela_dyn_section(layout); + this->rela_irelative_ = new Reloc_section(false); + layout->add_output_section_data(".rela.dyn", elfcpp::SHT_RELA, + elfcpp::SHF_ALLOC, this->rela_irelative_, + ORDER_DYNAMIC_RELOCS, false); + gold_assert(this->rela_dyn_->output_section() + == this->rela_irelative_->output_section()); + } + return this->rela_irelative_; +} + // Initialize the PLT section. void -Output_data_plt_x86_64::init(Symbol_table* symtab, Layout* layout) +Output_data_plt_x86_64::init(Layout* layout) { this->rel_ = new Reloc_section(false); layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA, elfcpp::SHF_ALLOC, this->rel_, ORDER_DYNAMIC_PLT_RELOCS, false); - if (parameters->doing_static_link()) - { - // A statically linked executable will only have a .rela.plt - // section to hold R_X86_64_IRELATIVE relocs for STT_GNU_IFUNC - // symbols. The library will use these symbols to locate the - // IRELATIVE relocs at program startup time. - symtab->define_in_output_data("__rela_iplt_start", NULL, - Symbol_table::PREDEFINED, - this->rel_, 0, 0, elfcpp::STT_NOTYPE, - elfcpp::STB_GLOBAL, elfcpp::STV_HIDDEN, - 0, false, true); - symtab->define_in_output_data("__rela_iplt_end", NULL, - Symbol_table::PREDEFINED, - this->rel_, 0, 0, elfcpp::STT_NOTYPE, - elfcpp::STB_GLOBAL, elfcpp::STV_HIDDEN, - 0, true, true); - } - // Add unwind information if requested. if (parameters->options().ld_generated_unwind_info()) layout->add_eh_frame_for_plt(this, plt_eh_frame_cie, plt_eh_frame_cie_size, @@ -898,7 +951,8 @@ Output_data_plt_x86_64::do_adjust_output_section(Output_section* os) // Add an entry to the PLT. void -Output_data_plt_x86_64::add_entry(Symbol* gsym) +Output_data_plt_x86_64::add_entry(Symbol_table* symtab, Layout* layout, + Symbol* gsym) { gold_assert(!gsym->has_plt_offset()); @@ -906,25 +960,47 @@ Output_data_plt_x86_64::add_entry(Symbol* gsym) off_t plt_offset; section_offset_type got_offset; + unsigned int* pcount; + unsigned int offset; + unsigned int reserved; + Output_data_space* got; + if (gsym->type() == elfcpp::STT_GNU_IFUNC + && gsym->can_use_relative_reloc(false)) + { + pcount = &this->irelative_count_; + offset = 0; + reserved = 0; + got = this->got_irelative_; + } + else + { + pcount = &this->count_; + offset = 1; + reserved = 3; + got = this->got_plt_; + } + if (!this->is_data_size_valid()) { - // Note that when setting the PLT offset we skip the initial - // reserved PLT entry. - plt_index = this->count_ + 1; + // Note that when setting the PLT offset for a non-IRELATIVE + // entry we skip the initial reserved PLT entry. + plt_index = *pcount + offset; plt_offset = plt_index * plt_entry_size; - ++this->count_; + ++*pcount; - got_offset = (plt_index - 1 + 3) * 8; - gold_assert(got_offset == this->got_plt_->current_data_size()); + got_offset = (plt_index - offset + reserved) * 8; + gold_assert(got_offset == got->current_data_size()); // Every PLT entry needs a GOT entry which points back to the PLT // entry (this will be changed by the dynamic linker, normally // lazily when the function is called). - this->got_plt_->set_current_data_size(got_offset + 8); + got->set_current_data_size(got_offset + 8); } else { + // FIXME: This is probably not correct for IRELATIVE relocs. + // For incremental updates, find an available slot. plt_offset = this->free_list_.allocate(plt_entry_size, plt_entry_size, 0); if (plt_offset == -1) @@ -935,13 +1011,13 @@ Output_data_plt_x86_64::add_entry(Symbol* gsym) // can be calculated from the PLT index, adjusting for the three // reserved entries at the beginning of the GOT. plt_index = plt_offset / plt_entry_size - 1; - got_offset = (plt_index - 1 + 3) * 8; + got_offset = (plt_index - offset + reserved) * 8; } gsym->set_plt_offset(plt_offset); // Every PLT entry needs a reloc. - this->add_relocation(gsym, got_offset); + this->add_relocation(symtab, layout, gsym, got_offset); // Note that we don't need to save the symbol. The contents of the // PLT are independent of which symbols are used. The symbols only @@ -953,22 +1029,25 @@ Output_data_plt_x86_64::add_entry(Symbol* gsym) unsigned int Output_data_plt_x86_64::add_local_ifunc_entry( + Symbol_table* symtab, + Layout* layout, Sized_relobj_file<64, false>* relobj, unsigned int local_sym_index) { - unsigned int plt_offset = (this->count_ + 1) * plt_entry_size; - ++this->count_; + unsigned int plt_offset = this->irelative_count_ * plt_entry_size; + ++this->irelative_count_; - section_offset_type got_offset = this->got_plt_->current_data_size(); + section_offset_type got_offset = this->got_irelative_->current_data_size(); // Every PLT entry needs a GOT entry which points back to the PLT // entry. - this->got_plt_->set_current_data_size(got_offset + 8); + this->got_irelative_->set_current_data_size(got_offset + 8); // Every PLT entry needs a reloc. - this->rel_->add_symbolless_local_addend(relobj, local_sym_index, - elfcpp::R_X86_64_IRELATIVE, - this->got_plt_, got_offset, 0); + Reloc_section* rela = this->rela_irelative(symtab, layout); + rela->add_symbolless_local_addend(relobj, local_sym_index, + elfcpp::R_X86_64_IRELATIVE, + this->got_irelative_, got_offset, 0); return plt_offset; } @@ -976,12 +1055,16 @@ Output_data_plt_x86_64::add_local_ifunc_entry( // Add the relocation for a PLT entry. void -Output_data_plt_x86_64::add_relocation(Symbol* gsym, unsigned int got_offset) +Output_data_plt_x86_64::add_relocation(Symbol_table* symtab, Layout* layout, + Symbol* gsym, unsigned int got_offset) { if (gsym->type() == elfcpp::STT_GNU_IFUNC && gsym->can_use_relative_reloc(false)) - this->rel_->add_symbolless_global_addend(gsym, elfcpp::R_X86_64_IRELATIVE, - this->got_plt_, got_offset, 0); + { + Reloc_section* rela = this->rela_irelative(symtab, layout); + rela->add_symbolless_global_addend(gsym, elfcpp::R_X86_64_IRELATIVE, + this->got_irelative_, got_offset, 0); + } else { gsym->set_needs_dynsym_entry(); @@ -1002,17 +1085,78 @@ Output_data_plt_x86_64::rela_tlsdesc(Layout* layout) layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA, elfcpp::SHF_ALLOC, this->tlsdesc_rel_, ORDER_DYNAMIC_PLT_RELOCS, false); - gold_assert(this->tlsdesc_rel_->output_section() == - this->rel_->output_section()); + gold_assert(this->tlsdesc_rel_->output_section() + == this->rel_->output_section()); } return this->tlsdesc_rel_; } +// Return where the IRELATIVE relocations should go in the PLT. These +// follow the JUMP_SLOT and the TLSDESC relocations. + +Output_data_plt_x86_64::Reloc_section* +Output_data_plt_x86_64::rela_irelative(Symbol_table* symtab, Layout* layout) +{ + if (this->irelative_rel_ == NULL) + { + // Make sure we have a place for the TLSDESC relocations, in + // case we see any later on. + this->rela_tlsdesc(layout); + this->irelative_rel_ = new Reloc_section(false); + layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA, + elfcpp::SHF_ALLOC, this->irelative_rel_, + ORDER_DYNAMIC_PLT_RELOCS, false); + gold_assert(this->irelative_rel_->output_section() + == this->rel_->output_section()); + + if (parameters->doing_static_link()) + { + // A statically linked executable will only have a .rela.plt + // section to hold R_X86_64_IRELATIVE relocs for + // STT_GNU_IFUNC symbols. The library will use these + // symbols to locate the IRELATIVE relocs at program startup + // time. + symtab->define_in_output_data("__rela_iplt_start", NULL, + Symbol_table::PREDEFINED, + this->irelative_rel_, 0, 0, + elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL, + elfcpp::STV_HIDDEN, 0, false, true); + symtab->define_in_output_data("__rela_iplt_end", NULL, + Symbol_table::PREDEFINED, + this->irelative_rel_, 0, 0, + elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL, + elfcpp::STV_HIDDEN, 0, true, true); + } + } + return this->irelative_rel_; +} + +// Return the PLT address to use for a global symbol. + +uint64_t +Output_data_plt_x86_64::address_for_global(const Symbol* gsym) +{ + uint64_t offset = 0; + if (gsym->type() == elfcpp::STT_GNU_IFUNC + && gsym->can_use_relative_reloc(false)) + offset = (this->count_ + 1) * plt_entry_size; + return this->address() + offset; +} + +// Return the PLT address to use for a local symbol. These are always +// IRELATIVE relocs. + +uint64_t +Output_data_plt_x86_64::address_for_local(const Relobj*, unsigned int) +{ + return this->address() + (this->count_ + 1) * plt_entry_size; +} + // Set the final size. void Output_data_plt_x86_64::set_final_data_size() { - unsigned int count = this->count_; + unsigned int count = this->count_ + this->irelative_count_; if (this->has_tlsdesc_entry()) ++count; this->set_data_size((count + 1) * plt_entry_size); @@ -1117,8 +1261,12 @@ Output_data_plt_x86_64::do_write(Output_file* of) unsigned char* const oview = of->get_output_view(offset, oview_size); const off_t got_file_offset = this->got_plt_->offset(); + gold_assert(parameters->incremental_update() + || (got_file_offset + this->got_plt_->data_size() + == this->got_irelative_->offset())); const section_size_type got_size = - convert_to_section_size_type(this->got_plt_->data_size()); + convert_to_section_size_type(this->got_plt_->data_size() + + this->got_irelative_->data_size()); unsigned char* const got_view = of->get_output_view(got_file_offset, got_size); @@ -1150,7 +1298,7 @@ Output_data_plt_x86_64::do_write(Output_file* of) unsigned int plt_offset = plt_entry_size; unsigned int got_offset = 24; - const unsigned int count = this->count_; + const unsigned int count = this->count_ + this->irelative_count_; for (unsigned int plt_index = 0; plt_index < count; ++plt_index, @@ -1208,8 +1356,9 @@ Target_x86_64::make_plt_section(Symbol_table* symtab, Layout* layout) // Create the GOT sections first. this->got_section(symtab, layout); - this->plt_ = new Output_data_plt_x86_64(symtab, layout, this->got_, - this->got_plt_); + this->plt_ = new Output_data_plt_x86_64(layout, this->got_, + this->got_plt_, + this->got_irelative_); layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS, (elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR), @@ -1241,7 +1390,7 @@ Target_x86_64::make_plt_entry(Symbol_table* symtab, Layout* layout, if (this->plt_ == NULL) this->make_plt_section(symtab, layout); - this->plt_->add_entry(gsym); + this->plt_->add_entry(symtab, layout, gsym); } // Make a PLT entry for a local STT_GNU_IFUNC symbol. @@ -1255,7 +1404,8 @@ Target_x86_64::make_local_ifunc_plt_entry(Symbol_table* symtab, Layout* layout, return; if (this->plt_ == NULL) this->make_plt_section(symtab, layout); - unsigned int plt_offset = this->plt_->add_local_ifunc_entry(relobj, + unsigned int plt_offset = this->plt_->add_local_ifunc_entry(symtab, layout, + relobj, local_sym_index); relobj->set_local_plt_offset(local_sym_index, plt_offset); } @@ -1330,9 +1480,17 @@ Target_x86_64::init_got_plt_for_update(Symbol_table* symtab, this->got_tlsdesc_, ORDER_NON_RELRO_FIRST, false); + // If there are any IRELATIVE relocations, they get GOT entries in + // .got.plt after the jump slot and TLSDESC entries. + this->got_irelative_ = new Output_data_space(0, 8, "** GOT IRELATIVE PLT"); + layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS, + elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE, + this->got_irelative_, + ORDER_NON_RELRO_FIRST, false); + // Create the PLT section. - this->plt_ = new Output_data_plt_x86_64(symtab, layout, this->got_, - this->got_plt_, plt_count); + this->plt_ = new Output_data_plt_x86_64(layout, this->got_, this->got_plt_, + this->got_irelative_, plt_count); layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS, elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR, this->plt_, ORDER_PLT, false); @@ -1439,7 +1597,9 @@ Target_x86_64::reserve_global_got_entry(unsigned int got_index, Symbol* gsym, // Register an existing PLT entry for a global symbol. void -Target_x86_64::register_global_plt_entry(unsigned int plt_index, +Target_x86_64::register_global_plt_entry(Symbol_table* symtab, + Layout* layout, + unsigned int plt_index, Symbol* gsym) { gold_assert(this->plt_ != NULL); @@ -1450,7 +1610,7 @@ Target_x86_64::register_global_plt_entry(unsigned int plt_index, gsym->set_plt_offset((plt_index + 1) * this->plt_entry_size()); unsigned int got_offset = (plt_index + 3) * 8; - this->plt_->add_relocation(gsym, got_offset); + this->plt_->add_relocation(symtab, layout, gsym, got_offset); } // Force a COPY relocation for a given symbol. @@ -2196,7 +2356,8 @@ Target_x86_64::Scan::global(Symbol_table* symtab, // STT_GNU_IFUNC symbol. This makes a function // address in a PIE executable match the address in a // shared library that it links against. - Reloc_section* rela_dyn = target->rela_dyn_section(layout); + Reloc_section* rela_dyn = + target->rela_irelative_section(layout); unsigned int r_type = elfcpp::R_X86_64_IRELATIVE; rela_dyn->add_symbolless_global_addend(gsym, r_type, output_section, object, @@ -2600,7 +2761,8 @@ Target_x86_64::do_finalize_sections( symtab->get_sized_symbol<64>(sym)->set_symsize(data_size); } - if (parameters->doing_static_link() && this->plt_ == NULL) + if (parameters->doing_static_link() + && (this->plt_ == NULL || !this->plt_->has_irelative_section())) { // If linking statically, make sure that the __rela_iplt symbols // were defined if necessary, even if we didn't create a PLT. @@ -2680,7 +2842,7 @@ Target_x86_64::Relocate::relocate(const Relocate_info<64, false>* relinfo, if (gsym != NULL && gsym->use_plt_offset(Scan::get_reference_flags(r_type))) { - symval.set_output_value(target->plt_section()->address() + symval.set_output_value(target->plt_address_for_global(gsym) + gsym->plt_offset()); psymval = &symval; } @@ -2689,7 +2851,7 @@ Target_x86_64::Relocate::relocate(const Relocate_info<64, false>* relinfo, unsigned int r_sym = elfcpp::elf_r_sym<64>(rela.get_r_info()); if (object->local_has_plt_offset(r_sym)) { - symval.set_output_value(target->plt_section()->address() + symval.set_output_value(target->plt_address_for_local(object, r_sym) + object->local_plt_offset(r_sym)); psymval = &symval; } @@ -3599,7 +3761,7 @@ uint64_t Target_x86_64::do_dynsym_value(const Symbol* gsym) const { gold_assert(gsym->is_from_dynobj() && gsym->has_plt_offset()); - return this->plt_section()->address() + gsym->plt_offset(); + return this->plt_address_for_global(gsym) + gsym->plt_offset(); } // Return a string used to fill a code section with nops to take up -- 2.30.2