diff options
author | Khem Raj <raj.khem@gmail.com> | 2016-01-14 03:58:10 -0800 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2016-01-22 23:42:55 +0000 |
commit | 9db094039ed7bce34006cc21bb65cd800fbfee7d (patch) | |
tree | 096091d2854c771706d619f3918dba898ec52fd1 /meta/recipes-devtools/binutils | |
parent | 2a18273fc74c6493e3d34499a8774e237772f109 (diff) | |
download | openembedded-core-9db094039ed7bce34006cc21bb65cd800fbfee7d.tar.gz openembedded-core-9db094039ed7bce34006cc21bb65cd800fbfee7d.tar.bz2 openembedded-core-9db094039ed7bce34006cc21bb65cd800fbfee7d.zip |
binutils: Fix gold linking errors due to unresolved R_ARM_MOVW_ABS_NC
This issue has been seen in multiple times e.g.
http://patchwork.openembedded.org/patch/103083/
https://www.mail-archive.com/openembedded-core@lists.openembedded.org/msg72513.html
Signed-off-by: Khem Raj <raj.khem@gmail.com>
Diffstat (limited to 'meta/recipes-devtools/binutils')
-rw-r--r-- | meta/recipes-devtools/binutils/binutils-2.25.1.inc | 1 | ||||
-rw-r--r-- | meta/recipes-devtools/binutils/binutils/0016-This-patch-adds-IFUNC-support-for-arm-gold-backend.patch | 1004 |
2 files changed, 1005 insertions, 0 deletions
diff --git a/meta/recipes-devtools/binutils/binutils-2.25.1.inc b/meta/recipes-devtools/binutils/binutils-2.25.1.inc index bf29da16f5..dc23c4d9d9 100644 --- a/meta/recipes-devtools/binutils/binutils-2.25.1.inc +++ b/meta/recipes-devtools/binutils/binutils-2.25.1.inc @@ -35,6 +35,7 @@ SRC_URI = "\ file://0013-Fix-an-internal-error-in-do_print_to_mapfile-seen-wi.patch \ file://0014-gold-arm-Skip-pic-check-for-R_ARM_REL32.patch \ file://0015-Fix-dynamic-list-so-that-symbols-not-in-the-list-are.patch \ + file://0016-This-patch-adds-IFUNC-support-for-arm-gold-backend.patch \ file://binutils-octeon3.patch \ file://add-thunderx-support-for-gas.patch \ " diff --git a/meta/recipes-devtools/binutils/binutils/0016-This-patch-adds-IFUNC-support-for-arm-gold-backend.patch b/meta/recipes-devtools/binutils/binutils/0016-This-patch-adds-IFUNC-support-for-arm-gold-backend.patch new file mode 100644 index 0000000000..dcac308bcc --- /dev/null +++ b/meta/recipes-devtools/binutils/binutils/0016-This-patch-adds-IFUNC-support-for-arm-gold-backend.patch @@ -0,0 +1,1004 @@ +From b780c9e06cabe6d8e301aaf46f33f116f3224021 Mon Sep 17 00:00:00 2001 +From: Han Shen <shenhan@google.com> +Date: Thu, 29 Jan 2015 10:00:46 -0800 +Subject: [PATCH] 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 +--- +Upstream-Status: Backport +Signed-off-by: Khem Raj <raj.khem@gmail.com> + + elfcpp/arm.h | 5 +- + gold/aarch64.cc | 2 +- + gold/arm.cc | 593 +++++++++++++++++++++++++++++++++++++++++++++++++++----- + gold/output.h | 11 ++ + 4 files changed, 561 insertions(+), 50 deletions(-) + +diff --git a/elfcpp/arm.h b/elfcpp/arm.h +index 8c6b6bf..1c13dc9 100644 +--- a/elfcpp/arm.h ++++ b/elfcpp/arm.h +@@ -192,11 +192,12 @@ enum + R_ARM_PRIVATE_14 = 126, + R_ARM_PRIVATE_15 = 127, + R_ARM_ME_TOO = 128, // Obsolete +- R_ARM_THM_TLS_DESCSEQ16 = 129,// Static Thumb16 ++ R_ARM_THM_TLS_DESCSEQ16 = 129,// Static Thumb16 + R_ARM_THM_TLS_DESCSEQ32 = 130,// Static Thumb32 + // 131 - 139 Unallocated + // 140 - 159 Dynamic Reserved for future allocation +- // 160 - 255 Unallocated ++ R_ARM_IRELATIVE = 160, // Dynamic ++ // 161 - 255 Unallocated + }; + + // e_flags values used for ARM. We only support flags defined in AAELF. +diff --git a/gold/aarch64.cc b/gold/aarch64.cc +index afb9024..7fbbdbd 100644 +--- a/gold/aarch64.cc ++++ b/gold/aarch64.cc +@@ -1226,7 +1226,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 6c472bb..8719cc9 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 +@@ -11083,8 +11574,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; + } +@@ -12187,8 +12677,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; +@@ -12225,8 +12720,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 ba0cdaa..599c2b7 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. + +-- +2.7.0 + |