diff options
author | Han Shen <shenhan@google.com> | 2015-01-29 10:00:46 -0800 |
---|---|---|
committer | Han Shen <shenhan@google.com> | 2015-01-29 10:15:42 -0800 |
commit | fa89cc82f5ca51f3135a9f2043b85b6a16c205eb (patch) | |
tree | 5f21ce4f356cb7edbfc5475aae567c381f2cb3ee /gold | |
parent | 716db898b72787d0d342c639c421cdd97f3afe9c (diff) | |
download | fsf-binutils-gdb-fa89cc82f5ca51f3135a9f2043b85b6a16c205eb.zip fsf-binutils-gdb-fa89cc82f5ca51f3135a9f2043b85b6a16c205eb.tar.gz fsf-binutils-gdb-fa89cc82f5ca51f3135a9f2043b85b6a16c205eb.tar.bz2 |
This patch adds IFUNC support for arm gold backend.
This is a feature required in chromeos arm development work.
Tested:
1) Built passed all-gold on x86_64 machine
2) Tested with basic gold aarch64 ifunc unittests -
a) global ifunc, statically/non-statically linked
b) local ifunc, statically/non-statically linked
c) global/local, other shared library routine mixed,
statically/non-statically linked
d) arm/thumb mode ifunc
e) linking chrome browser passed
Diffstat (limited to 'gold')
-rw-r--r-- | gold/ChangeLog | 48 | ||||
-rw-r--r-- | gold/aarch64.cc | 2 | ||||
-rw-r--r-- | gold/arm.cc | 593 | ||||
-rw-r--r-- | gold/output.h | 11 |
4 files changed, 606 insertions, 48 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog index 8803028..601d11b 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,51 @@ +2015-01-22 Han Shen <shenhan@google.com> + + * arm.cc (Target_arm::Target_arm): Add initialization for new members. + (Target_arm::do_plt_address_for_global): New method. + (Target_arm::do_plt_address_for_local): New method. + (Target_arm::rel_irelative_section): New method. + (Target_arm::make_data_plt): Add more parameters for plt ctor. + (Target_arm::do_make_data_plt): Add more parameters for plt ctor. + (Target_arm::Scan::symbol_needs_plt_entry): Add ifunc support. + (Target_arm::Scan::reloc_needs_plt_for_ifunc): Add ifunc support. + (Target_arm::Scan::check_non_pic): Add ifunc support. + (Target_arm::Scan::local): Add ifunc support. + (Target_arm::Scan::global): Add ifunc support. + (Target_arm::make_plt_section): New method. + (Target_arm::make_plt_entry): Change to call to make_plt_section. + (Target_arm::make_local_ifunc_plt_entry): New method. + (Target_arm::got_irelative_): New member. + (Target_arm::rel_irelative_): New member. + (Target_arm::got_section): Add creation for got_irelative_. + (Target_arm::rel_dyn_section): Add creation for rel_irelative_. + (Target_arm::Relocate::relocate): Properly set local ifunc address. + (Target_arm::do_dynsym_value): Properly set global ifunc address. + (Target_arm::scan_reloc_for_stub): Properly set global ifunc address. + (Output_data_plt_arm::IRelative_data): New type. + (Output_data_plt_arm::Output_data_plt_arm): Add more parameters. + (Output_data_plt_arm::add_entry): Add more parameters. + (Output_data_plt_arm::add_relocation): New method. + (Output_data_plt_arm::add_local_ifunc_entry): New method. + (Output_data_plt_arm::rel_irelative): New method. + (Output_data_plt_arm::entry_count): Modified. + (Output_data_plt_arm::address_for_global): New method. + (Output_data_plt_arm::address_for_local): New method. + (Output_data_plt_arm::set_final_data_size): Add irelative_count_. + (Output_data_plt_arm::insert_irelative_data): New method. + (Output_data_plt_arm::irelative_rel_): New member. + (Output_data_plt_arm::got_): New member. + (Output_data_plt_arm::got_irelative_): New member. + (Output_data_plt_arm::irelative_count_): New member. + (Output_data_plt_arm::IRelative_data_vec): New typedef. + (Output_data_plt_arm::irelative_data_vec_): New member. + (Output_data_plt_arm::do_write): Write out irelative entries. + (Output_data_plt_arm_standard::Output_data_plt_arm_standard): Add + more parameters to ctor. + (Output_data_plt_arm_nacl::Output_data_plt_arm_nacl): Add + more parameters to ctor. + * output.h (Output_data_reloc::add_local_relative): New method. + * aarch64.cc (Output_data_plt_aarch64): Fixed typo in comment. + 2015-01-29 Alan Modra <amodra@gmail.com> * powerpc.cc (Target_powerpc::Relocate::relocate): Correct GOT_TLSLD diff --git a/gold/aarch64.cc b/gold/aarch64.cc index 7f5ebb6..b16049e 100644 --- a/gold/aarch64.cc +++ b/gold/aarch64.cc @@ -3082,7 +3082,7 @@ class Output_data_plt_aarch64 : public Output_section_data // The number of PLT entries. unsigned int count_; - // Number of PLT entries with R_X86_64_IRELATIVE relocs. These + // Number of PLT entries with R_AARCH64_IRELATIVE relocs. These // follow the regular PLT entries. unsigned int irelative_count_; diff --git a/gold/arm.cc b/gold/arm.cc index 6312cc9..be2294c 100644 --- a/gold/arm.cc +++ b/gold/arm.cc @@ -2119,8 +2119,8 @@ class Target_arm : public Sized_target<32, big_endian> Target_arm(const Target::Target_info* info = &arm_info) : Sized_target<32, big_endian>(info), - got_(NULL), plt_(NULL), got_plt_(NULL), rel_dyn_(NULL), - copy_relocs_(elfcpp::R_ARM_COPY), + got_(NULL), plt_(NULL), got_plt_(NULL), got_irelative_(NULL), + rel_dyn_(NULL), rel_irelative_(NULL), copy_relocs_(elfcpp::R_ARM_COPY), got_mod_index_offset_(-1U), tls_base_symbol_defined_(false), stub_tables_(), stub_factory_(Stub_factory::get_instance()), should_force_pic_veneer_(false), @@ -2258,6 +2258,18 @@ class Target_arm : public Sized_target<32, big_endian> uint64_t do_dynsym_value(const Symbol*) const; + // Return the plt address for globals. Since we have irelative plt entries, + // address calculation is not as straightforward as plt_address + plt_offset. + uint64_t + do_plt_address_for_global(const Symbol* gsym) const + { return this->plt_section()->address_for_global(gsym); } + + // Return the plt address for locals. Since we have irelative plt entries, + // address calculation is not as straightforward as plt_address + plt_offset. + uint64_t + do_plt_address_for_local(const Relobj* relobj, unsigned int symndx) const + { return this->plt_section()->address_for_local(relobj, symndx); } + // Relocate a section. void relocate_section(const Relocate_info<32, big_endian>*, @@ -2357,6 +2369,10 @@ class Target_arm : public Sized_target<32, big_endian> unsigned int plt_entry_size() const; + // Get the section to use for IRELATIVE relocations, create it if necessary. + Reloc_section* + rel_irelative_section(Layout*); + // Map platform-specific reloc types static unsigned int get_real_reloc_type(unsigned int r_type); @@ -2448,8 +2464,11 @@ class Target_arm : public Sized_target<32, big_endian> protected: // Make the PLT-generator object. Output_data_plt_arm<big_endian>* - make_data_plt(Layout* layout, Output_data_space* got_plt) - { return this->do_make_data_plt(layout, got_plt); } + make_data_plt(Layout* layout, + Arm_output_data_got<big_endian>* got, + Output_data_space* got_plt, + Output_data_space* got_irelative) + { return this->do_make_data_plt(layout, got, got_plt, got_irelative); } // Make an ELF object. Object* @@ -2530,9 +2549,14 @@ class Target_arm : public Sized_target<32, big_endian> do_define_standard_symbols(Symbol_table*, Layout*); virtual Output_data_plt_arm<big_endian>* - do_make_data_plt(Layout* layout, Output_data_space* got_plt) + do_make_data_plt(Layout* layout, + Arm_output_data_got<big_endian>* got, + Output_data_space* got_plt, + Output_data_space* got_irelative) { - return new Output_data_plt_arm_standard<big_endian>(layout, got_plt); + gold_assert(got_plt != NULL && got_irelative != NULL); + return new Output_data_plt_arm_standard<big_endian>( + layout, got, got_plt, got_irelative); } private: @@ -2602,6 +2626,9 @@ class Target_arm : public Sized_target<32, big_endian> if (sym->is_undefined() && !parameters->options().shared()) return false; + if (sym->type() == elfcpp::STT_GNU_IFUNC) + return true; + return (!parameters->doing_static_link() && (sym->type() == elfcpp::STT_FUNC || sym->type() == elfcpp::STT_ARM_TFUNC) @@ -2613,6 +2640,11 @@ class Target_arm : public Sized_target<32, big_endian> inline bool possible_function_pointer_reloc(unsigned int r_type); + // Whether a plt entry is needed for ifunc. + bool + reloc_needs_plt_for_ifunc(Sized_relobj_file<32, big_endian>*, + unsigned int r_type); + // Whether we have issued an error about a non-PIC compilation. bool issued_non_pic_error_; }; @@ -2718,10 +2750,20 @@ class Target_arm : public Sized_target<32, big_endian> return this->got_plt_; } + // Create the PLT section. + void + make_plt_section(Symbol_table* symtab, Layout* layout); + // Create a PLT entry for a global symbol. void make_plt_entry(Symbol_table*, Layout*, Symbol*); + // Create a PLT entry for a local STT_GNU_IFUNC symbol. + void + make_local_ifunc_plt_entry(Symbol_table*, Layout*, + Sized_relobj_file<32, big_endian>* relobj, + unsigned int local_sym_index); + // Define the _TLS_MODULE_BASE_ symbol in the TLS segment. void define_tls_base_symbol(Symbol_table*, Layout*); @@ -2903,8 +2945,12 @@ class Target_arm : public Sized_target<32, big_endian> Output_data_plt_arm<big_endian>* plt_; // The GOT PLT section. Output_data_space* got_plt_; + // The GOT section for IRELATIVE relocations. + Output_data_space* got_irelative_; // 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<elfcpp::SHT_REL, 32, big_endian> copy_relocs_; // Offset of the GOT entry for the TLS module index. @@ -4244,6 +4290,15 @@ Target_arm<big_endian>::got_section(Symbol_table* symtab, Layout* layout) elfcpp::STB_LOCAL, 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(4, "** GOT IRELATIVE PLT"); + layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, + (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE), + this->got_irelative_, + got_order, is_got_relro); + } return this->got_; } @@ -4257,14 +4312,43 @@ Target_arm<big_endian>::rel_dyn_section(Layout* layout) if (this->rel_dyn_ == NULL) { gold_assert(layout != NULL); + // Create both relocation sections in the same place, so as to ensure + // their relative order in the output section. this->rel_dyn_ = new Reloc_section(parameters->options().combreloc()); + this->rel_irelative_ = new Reloc_section(false); layout->add_output_section_data(".rel.dyn", elfcpp::SHT_REL, elfcpp::SHF_ALLOC, this->rel_dyn_, ORDER_DYNAMIC_RELOCS, false); + layout->add_output_section_data(".rel.dyn", elfcpp::SHT_REL, + elfcpp::SHF_ALLOC, this->rel_irelative_, + ORDER_DYNAMIC_RELOCS, false); } return this->rel_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. + +template<bool big_endian> +typename Target_arm<big_endian>::Reloc_section* +Target_arm<big_endian>::rel_irelative_section(Layout* layout) +{ + if (this->rel_irelative_ == NULL) + { + // Delegate the creation to rel_dyn_section so as to ensure their order in + // the output section. + this->rel_dyn_section(layout); + gold_assert(this->rel_irelative_ != NULL + && (this->rel_dyn_->output_section() + == this->rel_irelative_->output_section())); + } + return this->rel_irelative_; +} + + // Insn_template methods. // Return byte size of an instruction template. @@ -7221,24 +7305,80 @@ template<bool big_endian> class Output_data_plt_arm : public Output_section_data { public: + // Unlike aarch64, which records symbol value in "addend" field of relocations + // and could be done at the same time an IRelative reloc is created for the + // symbol, arm puts the symbol value into "GOT" table, which, however, is + // issued later in Output_data_plt_arm::do_write(). So we have a struct here + // to keep necessary symbol information for later use in do_write. We usually + // have only a very limited number of ifuncs, so the extra data required here + // is also limited. + + struct IRelative_data + { + IRelative_data(Sized_symbol<32>* sized_symbol) + : symbol_is_global_(true) + { + u_.global = sized_symbol; + } + + IRelative_data(Sized_relobj_file<32, big_endian>* relobj, + unsigned int index) + : symbol_is_global_(false) + { + u_.local.relobj = relobj; + u_.local.index = index; + } + + union + { + Sized_symbol<32>* global; + + struct + { + Sized_relobj_file<32, big_endian>* relobj; + unsigned int index; + } local; + } u_; + + bool symbol_is_global_; + }; + typedef Output_data_reloc<elfcpp::SHT_REL, true, 32, big_endian> Reloc_section; - Output_data_plt_arm(Layout*, uint64_t addralign, Output_data_space*); + Output_data_plt_arm(Layout* layout, uint64_t addralign, + Arm_output_data_got<big_endian>* got, + Output_data_space* got_plt, + Output_data_space* got_irelative); // Add an entry to the PLT. void - add_entry(Symbol* gsym); + add_entry(Symbol_table* symtab, Layout* layout, Symbol* gsym); + + // Add the relocation for a plt entry. + void + add_relocation(Symbol_table* symtab, Layout* layout, + Symbol* gsym, unsigned int got_offset); + + // Add an entry to the PLT for a local STT_GNU_IFUNC symbol. + unsigned int + add_local_ifunc_entry(Symbol_table* symtab, Layout*, + Sized_relobj_file<32, big_endian>* relobj, + unsigned int local_sym_index); // Return the .rel.plt section data. const Reloc_section* rel_plt() const { return this->rel_; } + // Return the PLT relocation container for IRELATIVE. + Reloc_section* + rel_irelative(Symbol_table*, Layout*); + // 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. unsigned int @@ -7250,6 +7390,14 @@ class Output_data_plt_arm : public Output_section_data get_plt_entry_size() const { return this->do_get_plt_entry_size(); } + // Return the PLT address for globals. + uint32_t + address_for_global(const Symbol*) const; + + // Return the PLT address for locals. + uint32_t + address_for_local(const Relobj*, unsigned int symndx) const; + protected: // Fill in the first PLT entry. void @@ -7298,19 +7446,37 @@ class Output_data_plt_arm : public Output_section_data set_final_data_size() { this->set_data_size(this->first_plt_entry_offset() - + this->count_ * this->get_plt_entry_size()); + + ((this->count_ + this->irelative_count_) + * this->get_plt_entry_size())); } // Write out the PLT data. void do_write(Output_file*); + // Record irelative symbol data. + void insert_irelative_data(const IRelative_data& idata) + { irelative_data_vec_.push_back(idata); } + // The reloc section. Reloc_section* rel_; + // The IRELATIVE relocs, if necessary. These must follow the + // regular PLT relocations. + Reloc_section* irelative_rel_; + // The .got section. + Arm_output_data_got<big_endian>* 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_ARM_IRELATIVE relocs. These + // follow the regular PLT entries. + unsigned int irelative_count_; + // Vector for irelative data. + typedef std::vector<IRelative_data> IRelative_data_vec; + IRelative_data_vec irelative_data_vec_; }; // Create the PLT section. The ordinary .got section is an argument, @@ -7318,10 +7484,14 @@ class Output_data_plt_arm : public Output_section_data // section just for PLT entries. template<bool big_endian> -Output_data_plt_arm<big_endian>::Output_data_plt_arm(Layout* layout, - uint64_t addralign, - Output_data_space* got_plt) - : Output_section_data(addralign), got_plt_(got_plt), count_(0) +Output_data_plt_arm<big_endian>::Output_data_plt_arm( + Layout* layout, uint64_t addralign, + Arm_output_data_got<big_endian>* got, + Output_data_space* got_plt, + Output_data_space* got_irelative) + : Output_section_data(addralign), irelative_rel_(NULL), + got_(got), got_plt_(got_plt), got_irelative_(got_irelative), + count_(0), irelative_count_(0) { this->rel_ = new Reloc_section(false); layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL, @@ -7340,40 +7510,210 @@ Output_data_plt_arm<big_endian>::do_adjust_output_section(Output_section* os) template<bool big_endian> void -Output_data_plt_arm<big_endian>::add_entry(Symbol* gsym) +Output_data_plt_arm<big_endian>::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_) * this->get_plt_entry_size() - + this->first_plt_entry_offset()); + unsigned int* entry_count; + Output_section_data_build* got; + + // We have 2 different types of plt entry here, normal and ifunc. + + // For normal plt, the offset begins with first_plt_entry_offset(20), and the + // 1st entry offset would be 20, the second 32, third 44 ... etc. + + // For ifunc plt, the offset begins with 0. So the first offset would 0, + // second 12, third 24 ... etc. + + // IFunc plt entries *always* come after *normal* plt entries. + + // Notice, when computing the plt address of a certain symbol, "plt_address + + // plt_offset" is no longer correct. Use target->plt_address_for_global() or + // target->plt_address_for_local() instead. + + int begin_offset = 0; + if (gsym->type() == elfcpp::STT_GNU_IFUNC + && gsym->can_use_relative_reloc(false)) + { + entry_count = &this->irelative_count_; + got = this->got_irelative_; + // For irelative plt entries, offset is relative to the end of normal plt + // entries, so it starts from 0. + begin_offset = 0; + // Record symbol information. + this->insert_irelative_data( + IRelative_data(symtab->get_sized_symbol<32>(gsym))); + } + else + { + entry_count = &this->count_; + got = this->got_plt_; + // Note that for normal plt entries, when setting the PLT offset we skip + // the initial reserved PLT entry. + begin_offset = this->first_plt_entry_offset(); + } + + gsym->set_plt_offset(begin_offset + + (*entry_count) * this->get_plt_entry_size()); - ++this->count_; + ++(*entry_count); - section_offset_type got_offset = this->got_plt_->current_data_size(); + section_offset_type 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 + 4); + got->set_current_data_size(got_offset + 4); // Every PLT entry needs a reloc. - gsym->set_needs_dynsym_entry(); - this->rel_->add_global(gsym, elfcpp::R_ARM_JUMP_SLOT, this->got_plt_, - 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 // appear in the relocations. } +// Add an entry to the PLT for a local STT_GNU_IFUNC symbol. Return +// the PLT offset. + +template<bool big_endian> +unsigned int +Output_data_plt_arm<big_endian>::add_local_ifunc_entry( + Symbol_table* symtab, + Layout* layout, + Sized_relobj_file<32, big_endian>* relobj, + unsigned int local_sym_index) +{ + this->insert_irelative_data(IRelative_data(relobj, local_sym_index)); + + // Notice, when computingthe plt entry address, "plt_address + plt_offset" is + // no longer correct. Use target->plt_address_for_local() instead. + unsigned int plt_offset = this->irelative_count_ * this->get_plt_entry_size(); + ++this->irelative_count_; + + 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_irelative_->set_current_data_size(got_offset + 4); + + + // Every PLT entry needs a reloc. + Reloc_section* rel = this->rel_irelative(symtab, layout); + rel->add_symbolless_local_addend(relobj, local_sym_index, + elfcpp::R_ARM_IRELATIVE, + this->got_irelative_, got_offset); + return plt_offset; +} + + +// Add the relocation for a PLT entry. + +template<bool big_endian> +void +Output_data_plt_arm<big_endian>::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)) + { + Reloc_section* rel = this->rel_irelative(symtab, layout); + rel->add_symbolless_global_addend(gsym, elfcpp::R_ARM_IRELATIVE, + this->got_irelative_, got_offset); + } + else + { + gsym->set_needs_dynsym_entry(); + this->rel_->add_global(gsym, elfcpp::R_ARM_JUMP_SLOT, this->got_plt_, + got_offset); + } +} + + +// Create the irelative relocation data. + +template<bool big_endian> +typename Output_data_plt_arm<big_endian>::Reloc_section* +Output_data_plt_arm<big_endian>::rel_irelative(Symbol_table* symtab, + Layout* layout) +{ + if (this->irelative_rel_ == NULL) + { + // Since irelative relocations goes into 'rel.dyn', we delegate the + // creation of irelative_rel_ to where rel_dyn section gets created. + Target_arm<big_endian>* arm_target = + Target_arm<big_endian>::default_target(); + this->irelative_rel_ = arm_target->rel_irelative_section(layout); + + // Make sure we have a place for the TLSDESC relocations, in + // case we see any later on. + // this->rel_tlsdesc(layout); + if (parameters->doing_static_link()) + { + // A statically linked executable will only have a .rel.plt section to + // hold R_ARM_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 for a global symbol. + +template<bool big_endian> +uint32_t +Output_data_plt_arm<big_endian>::address_for_global(const Symbol* gsym) const +{ + uint64_t begin_offset = 0; + if (gsym->type() == elfcpp::STT_GNU_IFUNC + && gsym->can_use_relative_reloc(false)) + { + begin_offset = (this->first_plt_entry_offset() + + this->count_ * this->get_plt_entry_size()); + } + return this->address() + begin_offset + gsym->plt_offset(); +} + + +// Return the PLT address for a local symbol. These are always +// IRELATIVE relocs. + +template<bool big_endian> +uint32_t +Output_data_plt_arm<big_endian>::address_for_local( + const Relobj* object, + unsigned int r_sym) const +{ + return (this->address() + + this->first_plt_entry_offset() + + this->count_ * this->get_plt_entry_size() + + object->local_plt_offset(r_sym)); +} + + template<bool big_endian> class Output_data_plt_arm_standard : public Output_data_plt_arm<big_endian> { public: - Output_data_plt_arm_standard(Layout* layout, Output_data_space* got_plt) - : Output_data_plt_arm<big_endian>(layout, 4, got_plt) + Output_data_plt_arm_standard(Layout* layout, + Arm_output_data_got<big_endian>* got, + Output_data_space* got_plt, + Output_data_space* got_irelative) + : Output_data_plt_arm<big_endian>(layout, 4, got, got_plt, got_irelative) { } protected: @@ -7485,8 +7825,11 @@ Output_data_plt_arm<big_endian>::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(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); unsigned char* pov = oview; @@ -7505,7 +7848,8 @@ Output_data_plt_arm<big_endian>::do_write(Output_file* of) unsigned int plt_offset = this->first_plt_entry_offset(); unsigned int got_offset = 12; - const unsigned int count = this->count_; + const unsigned int count = this->count_ + this->irelative_count_; + gold_assert(this->irelative_count_ == this->irelative_data_vec_.size()); for (unsigned int i = 0; i < count; ++i, @@ -7518,8 +7862,33 @@ Output_data_plt_arm<big_endian>::do_write(Output_file* of) this->fill_plt_entry(pov, got_address, plt_address, got_offset, plt_offset); - // Set the entry in the GOT. - elfcpp::Swap<32, big_endian>::writeval(got_pov, plt_address); + Arm_address value; + if (i < this->count_) + { + // For non-irelative got entries, the value is the beginning of plt. + value = plt_address; + } + else + { + // For irelative got entries, the value is the (global/local) symbol + // address. + const IRelative_data& idata = + this->irelative_data_vec_[i - this->count_]; + if (idata.symbol_is_global_) + { + // Set the entry in the GOT for irelative symbols. The content is + // the address of the ifunc, not the address of plt start. + const Sized_symbol<32>* sized_symbol = idata.u_.global; + gold_assert(sized_symbol->type() == elfcpp::STT_GNU_IFUNC); + value = sized_symbol->value(); + } + else + { + value = idata.u_.local.relobj->local_symbol_value( + idata.u_.local.index, 0); + } + } + elfcpp::Swap<32, big_endian>::writeval(got_pov, value); } gold_assert(static_cast<section_size_type>(pov - oview) == oview_size); @@ -7529,6 +7898,7 @@ Output_data_plt_arm<big_endian>::do_write(Output_file* of) of->write_output_view(got_file_offset, got_size, got_view); } + // Create a PLT entry for a global symbol. template<bool big_endian> @@ -7540,20 +7910,58 @@ Target_arm<big_endian>::make_plt_entry(Symbol_table* symtab, Layout* layout, return; if (this->plt_ == NULL) + this->make_plt_section(symtab, layout); + + this->plt_->add_entry(symtab, layout, gsym); +} + + +// Create the PLT section. +template<bool big_endian> +void +Target_arm<big_endian>::make_plt_section( + Symbol_table* symtab, Layout* layout) +{ + if (this->plt_ == NULL) { - // Create the GOT sections first. + // Create the GOT section first. this->got_section(symtab, layout); - this->plt_ = this->make_data_plt(layout, this->got_plt_); + // GOT for irelatives is create along with got.plt. + gold_assert(this->got_ != NULL + && this->got_plt_ != NULL + && this->got_irelative_ != NULL); + this->plt_ = this->make_data_plt(layout, this->got_, this->got_plt_, + this->got_irelative_); layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS, (elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR), this->plt_, ORDER_PLT, false); } - this->plt_->add_entry(gsym); } + +// Make a PLT entry for a local STT_GNU_IFUNC symbol. + +template<bool big_endian> +void +Target_arm<big_endian>::make_local_ifunc_plt_entry( + Symbol_table* symtab, Layout* layout, + Sized_relobj_file<32, big_endian>* relobj, + unsigned int local_sym_index) +{ + if (relobj->local_has_plt_offset(local_sym_index)) + return; + if (this->plt_ == NULL) + this->make_plt_section(symtab, layout); + 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); +} + + // Return the number of entries in the PLT. template<bool big_endian> @@ -7823,6 +8231,7 @@ Target_arm<big_endian>::Scan::check_non_pic(Relobj* object, case elfcpp::R_ARM_JUMP_SLOT: case elfcpp::R_ARM_ABS32: case elfcpp::R_ARM_ABS32_NOI: + case elfcpp::R_ARM_IRELATIVE: case elfcpp::R_ARM_PC24: // FIXME: The following 3 types are not supported by Android's dynamic // linker. @@ -7853,6 +8262,27 @@ Target_arm<big_endian>::Scan::check_non_pic(Relobj* object, } } + +// Return whether we need to make a PLT entry for a relocation of the +// given type against a STT_GNU_IFUNC symbol. + +template<bool big_endian> +bool +Target_arm<big_endian>::Scan::reloc_needs_plt_for_ifunc( + Sized_relobj_file<32, big_endian>* object, + unsigned int r_type) +{ + int flags = Scan::get_reference_flags(r_type); + if (flags & Symbol::TLS_REF) + { + gold_error(_("%s: unsupported TLS reloc %u for IFUNC symbol"), + object->name().c_str(), r_type); + return false; + } + return flags != 0; +} + + // Scan a relocation for a local symbol. // FIXME: This only handles a subset of relocation types used by Android // on ARM v5te devices. @@ -7874,6 +8304,15 @@ Target_arm<big_endian>::Scan::local(Symbol_table* symtab, return; r_type = get_real_reloc_type(r_type); + + // A local STT_GNU_IFUNC symbol may require a PLT entry. + bool is_ifunc = lsym.get_st_type() == elfcpp::STT_GNU_IFUNC; + if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type)) + { + unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info()); + target->make_local_ifunc_plt_entry(symtab, layout, object, r_sym); + } + switch (r_type) { case elfcpp::R_ARM_NONE: @@ -7898,7 +8337,7 @@ Target_arm<big_endian>::Scan::local(Symbol_table* symtab, // we need to add check_non_pic(object, r_type) here. rel_dyn->add_local_relative(object, r_sym, elfcpp::R_ARM_RELATIVE, output_section, data_shndx, - reloc.get_r_offset()); + reloc.get_r_offset(), is_ifunc); } break; @@ -8265,6 +8704,11 @@ Target_arm<big_endian>::Scan::global(Symbol_table* symtab, && strcmp(gsym->name(), "_GLOBAL_OFFSET_TABLE_") == 0) target->got_section(symtab, layout); + // A STT_GNU_IFUNC symbol may require a PLT entry. + if (gsym->type() == elfcpp::STT_GNU_IFUNC + && this->reloc_needs_plt_for_ifunc(object, r_type)) + target->make_plt_entry(symtab, layout, gsym); + r_type = get_real_reloc_type(r_type); switch (r_type) { @@ -8309,6 +8753,24 @@ Target_arm<big_endian>::Scan::global(Symbol_table* symtab, } else if ((r_type == elfcpp::R_ARM_ABS32 || r_type == elfcpp::R_ARM_ABS32_NOI) + && gsym->type() == elfcpp::STT_GNU_IFUNC + && gsym->can_use_relative_reloc(false) + && !gsym->is_from_dynobj() + && !gsym->is_undefined() + && !gsym->is_preemptible()) + { + // Use an IRELATIVE reloc for a locally defined 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_irelative = + target->rel_irelative_section(layout); + unsigned int r_type = elfcpp::R_ARM_IRELATIVE; + rel_irelative->add_symbolless_global_addend( + gsym, r_type, output_section, object, + data_shndx, reloc.get_r_offset()); + } + else if ((r_type == elfcpp::R_ARM_ABS32 + || r_type == elfcpp::R_ARM_ABS32_NOI) && gsym->can_use_relative_reloc(false)) { Reloc_section* rel_dyn = target->rel_dyn_section(layout); @@ -8442,7 +8904,13 @@ Target_arm<big_endian>::Scan::global(Symbol_table* symtab, Arm_output_data_got<big_endian>* got = target->got_section(symtab, layout); if (gsym->final_value_is_known()) - got->add_global(gsym, GOT_TYPE_STANDARD); + { + // For a STT_GNU_IFUNC symbol we want the PLT address. + if (gsym->type() == elfcpp::STT_GNU_IFUNC) + got->add_global_plt(gsym, GOT_TYPE_STANDARD); + else + got->add_global(gsym, GOT_TYPE_STANDARD); + } else { // If this symbol is not fully resolved, we need to add a @@ -8452,12 +8920,29 @@ Target_arm<big_endian>::Scan::global(Symbol_table* symtab, || gsym->is_undefined() || gsym->is_preemptible() || (gsym->visibility() == elfcpp::STV_PROTECTED - && parameters->options().shared())) + && parameters->options().shared()) + || (gsym->type() == elfcpp::STT_GNU_IFUNC + && parameters->options().output_is_position_independent())) got->add_global_with_rel(gsym, GOT_TYPE_STANDARD, rel_dyn, elfcpp::R_ARM_GLOB_DAT); else { - if (got->add_global(gsym, GOT_TYPE_STANDARD)) + // For a STT_GNU_IFUNC symbol we want to write the PLT + // offset into the GOT, so that function pointer + // comparisons work correctly. + bool is_new; + if (gsym->type() != elfcpp::STT_GNU_IFUNC) + is_new = got->add_global(gsym, GOT_TYPE_STANDARD); + else + { + is_new = got->add_global_plt(gsym, GOT_TYPE_STANDARD); + // Tell the dynamic linker to use the PLT address + // when resolving relocations. + if (gsym->is_from_dynobj() + && !parameters->options().shared()) + gsym->set_needs_dynsym_value(); + } + if (is_new) rel_dyn->add_global_relative( gsym, elfcpp::R_ARM_RELATIVE, got, gsym->got_offset(GOT_TYPE_STANDARD)); @@ -8919,8 +9404,7 @@ Target_arm<big_endian>::Relocate::relocate( if (gsym->use_plt_offset(Scan::get_reference_flags(r_type))) { // This uses a PLT, change the symbol value. - symval.set_output_value(target->plt_section()->address() - + gsym->plt_offset()); + symval.set_output_value(target->plt_address_for_global(gsym)); psymval = &symval; } else if (gsym->is_weak_undefined()) @@ -8958,6 +9442,13 @@ Target_arm<big_endian>::Relocate::relocate( elfcpp::Elf_types<32>::Elf_WXword r_info = rel.get_r_info(); unsigned int r_sym = elfcpp::elf_r_sym<32>(r_info); thumb_bit = object->local_symbol_is_thumb_function(r_sym) ? 1 : 0; + + if (psymval->is_ifunc_symbol() && object->local_has_plt_offset(r_sym)) + { + symval.set_output_value( + target->plt_address_for_local(object, r_sym)); + psymval = &symval; + } } } else @@ -9936,7 +10427,7 @@ uint64_t Target_arm<big_endian>::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); } // Map platform-specific relocs to real relocs @@ -11091,8 +11582,7 @@ Target_arm<big_endian>::scan_reloc_for_stub( if (gsym->use_plt_offset(Scan::get_reference_flags(r_type))) { // This uses a PLT, change the symbol value. - symval.set_output_value(this->plt_section()->address() - + gsym->plt_offset()); + symval.set_output_value(this->plt_address_for_global(gsym)); psymval = &symval; target_is_thumb = false; } @@ -12195,8 +12685,13 @@ class Target_arm_nacl : public Target_arm<big_endian> protected: virtual Output_data_plt_arm<big_endian>* - do_make_data_plt(Layout* layout, Output_data_space* got_plt) - { return new Output_data_plt_arm_nacl<big_endian>(layout, got_plt); } + do_make_data_plt( + Layout* layout, + Arm_output_data_got<big_endian>* got, + Output_data_space* got_plt, + Output_data_space* got_irelative) + { return new Output_data_plt_arm_nacl<big_endian>( + layout, got, got_plt, got_irelative); } private: static const Target::Target_info arm_nacl_info; @@ -12233,8 +12728,12 @@ template<bool big_endian> class Output_data_plt_arm_nacl : public Output_data_plt_arm<big_endian> { public: - Output_data_plt_arm_nacl(Layout* layout, Output_data_space* got_plt) - : Output_data_plt_arm<big_endian>(layout, 16, got_plt) + Output_data_plt_arm_nacl( + Layout* layout, + Arm_output_data_got<big_endian>* got, + Output_data_space* got_plt, + Output_data_space* got_irelative) + : Output_data_plt_arm<big_endian>(layout, 16, got, got_plt, got_irelative) { } protected: diff --git a/gold/output.h b/gold/output.h index 8ef8942..8e0c33a 100644 --- a/gold/output.h +++ b/gold/output.h @@ -1714,6 +1714,17 @@ class Output_data_reloc<elfcpp::SHT_REL, dynamic, size, big_endian> address, true, true, false, false)); } + void + add_local_relative(Sized_relobj<size, big_endian>* relobj, + unsigned int local_sym_index, unsigned int type, + Output_data* od, unsigned int shndx, Address address, + bool use_plt_offset) + { + this->add(od, Output_reloc_type(relobj, local_sym_index, type, shndx, + address, true, true, false, + use_plt_offset)); + } + // Add a local relocation which does not use a symbol for the relocation, // but which gets its addend from a symbol. |