X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gold%2Farm.cc;h=a5a01bcd60ecc3e70148a4f0269864d99a9e630d;hb=7a554b2725e6c336ff98dc671848a29d55f4c16f;hp=92735ea84a2cb401945bd44322d337b1d1580711;hpb=2e702c99c59f581594820852c607394c25bc9bd2;p=binutils-gdb.git diff --git a/gold/arm.cc b/gold/arm.cc index 92735ea84a2..a5a01bcd60e 100644 --- a/gold/arm.cc +++ b/gold/arm.cc @@ -1,6 +1,6 @@ // arm.cc -- arm target support for gold. -// Copyright 2009, 2010, 2011, 2012 Free Software Foundation, Inc. +// Copyright (C) 2009-2021 Free Software Foundation, Inc. // Written by Doug Kwan based on the i386 code // by Ian Lance Taylor . // This file also contains borrowed and adapted code from @@ -62,7 +62,10 @@ template class Output_data_plt_arm; template -class Output_data_plt_arm_standard; +class Output_data_plt_arm_short; + +template +class Output_data_plt_arm_long; template class Stub_table; @@ -126,7 +129,7 @@ const size_t ARM_TCB_SIZE = 8; // Target::do_select_as_default_target() hook so that we do not spend time // building the table if we are not linking ARM objects. // -// An alternative is to to process the information in arm-reloc.def in +// An alternative is to process the information in arm-reloc.def in // compilation time and generate a representation of it in PODs only. That // way we can avoid initialization when the linker starts. @@ -594,7 +597,7 @@ class Reloc_stub : public Stub // Name of key. This is mainly for debugging. std::string - name() const; + name() const ATTRIBUTE_UNUSED; private: // Stub type. @@ -2037,9 +2040,9 @@ class Arm_output_data_got : public Output_data_got<32, big_endian> // bits. The default handling of relocatable relocation cannot process these // relocations. So we have to extend the default code. -template +template class Arm_scan_relocatable_relocs : - public Default_scan_relocatable_relocs + public Default_scan_relocatable_relocs { public: // Return the strategy to use for a local symbol which is a section @@ -2047,7 +2050,7 @@ class Arm_scan_relocatable_relocs : inline Relocatable_relocs::Reloc_strategy local_section_strategy(unsigned int r_type, Relobj*) { - if (sh_type == elfcpp::SHT_RELA) + if (Classify_reloc::sh_type == elfcpp::SHT_RELA) return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_RELA; else { @@ -2119,13 +2122,16 @@ 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), dynbss_(NULL), + 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), arm_input_section_map_(), attributes_section_data_(NULL), - fix_cortex_a8_(false), cortex_a8_relocs_info_() + fix_cortex_a8_(false), cortex_a8_relocs_info_(), + target1_reloc_(elfcpp::R_ARM_ABS32), + // This can be any reloc type but usually is R_ARM_GOT_PREL. + target2_reloc_(elfcpp::R_ARM_GOT_PREL) { } // Whether we force PCI branch veneers. @@ -2258,6 +2264,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>*, @@ -2286,20 +2304,35 @@ class Target_arm : public Sized_target<32, big_endian> const unsigned char* plocal_symbols, Relocatable_relocs*); - // Relocate a section during a relocatable link. + // Scan the relocs for --emit-relocs. + void + emit_relocs_scan(Symbol_table* symtab, + Layout* layout, + Sized_relobj_file<32, big_endian>* object, + unsigned int data_shndx, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + Output_section* output_section, + bool needs_special_offset_handling, + size_t local_symbol_count, + const unsigned char* plocal_syms, + Relocatable_relocs* rr); + + // Emit relocations for a section. void - relocate_for_relocatable(const Relocate_info<32, big_endian>*, - unsigned int sh_type, - const unsigned char* prelocs, - size_t reloc_count, - Output_section* output_section, - off_t offset_in_output_section, - const Relocatable_relocs*, - unsigned char* view, - Arm_address view_address, - section_size_type view_size, - unsigned char* reloc_view, - section_size_type reloc_view_size); + relocate_relocs(const Relocate_info<32, big_endian>*, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + Output_section* output_section, + typename elfcpp::Elf_types<32>::Elf_Off + offset_in_output_section, + unsigned char* view, + Arm_address view_address, + section_size_type view_size, + unsigned char* reloc_view, + section_size_type reloc_view_size); // Perform target-specific processing in a relocatable link. This is // only used if we use the relocation strategy RELOC_SPECIAL. @@ -2309,7 +2342,8 @@ class Target_arm : public Sized_target<32, big_endian> const unsigned char* preloc_in, size_t relnum, Output_section* output_section, - off_t offset_in_output_section, + typename elfcpp::Elf_types<32>::Elf_Off + offset_in_output_section, unsigned char* view, typename elfcpp::Elf_types<32>::Elf_Addr view_address, @@ -2355,9 +2389,13 @@ 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); + unsigned int + get_real_reloc_type(unsigned int r_type) const; // // Methods to support stub-generations. @@ -2446,8 +2484,11 @@ class Target_arm : public Sized_target<32, big_endian> protected: // Make the PLT-generator object. Output_data_plt_arm* - 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* 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* @@ -2476,7 +2517,7 @@ class Target_arm : public Sized_target<32, big_endian> { return new Arm_output_section(name, type, flags); } void - do_adjust_elf_header(unsigned char* view, int len) const; + do_adjust_elf_header(unsigned char* view, int len); // We only need to generate stubs, and hence perform relaxation if we are // not doing relocatable linking. @@ -2505,6 +2546,30 @@ class Target_arm : public Sized_target<32, big_endian> // as the default. gold_assert(arm_reloc_property_table == NULL); arm_reloc_property_table = new Arm_reloc_property_table(); + if (parameters->options().user_set_target1_rel()) + { + // FIXME: This is not strictly compatible with ld, which allows both + // --target1-abs and --target-rel to be given. + if (parameters->options().user_set_target1_abs()) + gold_error(_("Cannot use both --target1-abs and --target1-rel.")); + else + this->target1_reloc_ = elfcpp::R_ARM_REL32; + } + // We don't need to handle --target1-abs because target1_reloc_ is set + // to elfcpp::R_ARM_ABS32 in the member initializer list. + + if (parameters->options().user_set_target2()) + { + const char* target2 = parameters->options().target2(); + if (strcmp(target2, "rel") == 0) + this->target2_reloc_ = elfcpp::R_ARM_REL32; + else if (strcmp(target2, "abs") == 0) + this->target2_reloc_ = elfcpp::R_ARM_ABS32; + else if (strcmp(target2, "got-rel") == 0) + this->target2_reloc_ = elfcpp::R_ARM_GOT_PREL; + else + gold_unreachable(); + } } // Virtual function which is set to return true by a target if @@ -2528,9 +2593,18 @@ class Target_arm : public Sized_target<32, big_endian> do_define_standard_symbols(Symbol_table*, Layout*); virtual Output_data_plt_arm* - do_make_data_plt(Layout* layout, Output_data_space* got_plt) + do_make_data_plt(Layout* layout, + Arm_output_data_got* got, + Output_data_space* got_plt, + Output_data_space* got_irelative) { - return new Output_data_plt_arm_standard(layout, got_plt); + gold_assert(got_plt != NULL && got_irelative != NULL); + if (parameters->options().long_plt()) + return new Output_data_plt_arm_long( + layout, got, got_plt, got_irelative); + else + return new Output_data_plt_arm_short( + layout, got, got_plt, got_irelative); } private: @@ -2551,7 +2625,8 @@ class Target_arm : public Sized_target<32, big_endian> unsigned int data_shndx, Output_section* output_section, const elfcpp::Rel<32, big_endian>& reloc, unsigned int r_type, - const elfcpp::Sym<32, big_endian>& lsym); + const elfcpp::Sym<32, big_endian>& lsym, + bool is_discarded); inline void global(Symbol_table* symtab, Layout* layout, Target_arm* target, @@ -2599,6 +2674,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) @@ -2610,6 +2688,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_; }; @@ -2634,13 +2717,10 @@ class Target_arm : public Sized_target<32, big_endian> // Do a relocation. Return false if the caller should not issue // any warnings about this relocation. inline bool - relocate(const Relocate_info<32, big_endian>*, Target_arm*, - Output_section*, size_t relnum, - const elfcpp::Rel<32, big_endian>&, - unsigned int r_type, const Sized_symbol<32>*, - const Symbol_value<32>*, - unsigned char*, Arm_address, - section_size_type); + relocate(const Relocate_info<32, big_endian>*, unsigned int, + Target_arm*, Output_section*, size_t, const unsigned char*, + const Sized_symbol<32>*, const Symbol_value<32>*, + unsigned char*, Arm_address, section_size_type); // Return whether we want to pass flag NON_PIC_REF for this // reloc. This means the relocation type accesses a symbol not via @@ -2689,12 +2769,23 @@ class Target_arm : public Sized_target<32, big_endian> }; - // A class which returns the size required for a relocation type, - // used while scanning relocs during a relocatable link. - class Relocatable_size_for_reloc + // A class for inquiring about properties of a relocation, + // used while scanning relocs during a relocatable link and + // garbage collection. + class Classify_reloc : + public gold::Default_classify_reloc { public: - unsigned int + typedef typename Reloc_types::Reloc + Reltype; + + // Return the explicit addend of the relocation (return 0 for SHT_REL). + static typename elfcpp::Elf_types<32>::Elf_Swxword + get_r_addend(const Reltype*) + { return 0; } + + // Return the size of the addend of the relocation (only used for SHT_REL). + static unsigned int get_size_for_reloc(unsigned int, Relobj*); }; @@ -2715,10 +2806,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*); @@ -2761,9 +2862,11 @@ class Target_arm : public Sized_target<32, big_endian> unsigned int shndx, Output_section* output_section, Symbol* sym, const elfcpp::Rel<32, big_endian>& reloc) { + unsigned int r_type = elfcpp::elf_r_type<32>(reloc.get_r_info()); this->copy_relocs_.copy_reloc(symtab, layout, symtab->get_sized_symbol<32>(sym), - object, shndx, output_section, reloc, + object, shndx, output_section, + r_type, reloc.get_r_offset(), 0, this->rel_dyn_section(layout)); } @@ -2795,6 +2898,18 @@ class Target_arm : public Sized_target<32, big_endian> static std::string tag_cpu_name_value(unsigned int); + // Query attributes object to see if integer divide instructions may be + // present in an object. + static bool + attributes_accept_div(int arch, int profile, + const Object_attribute* div_attr); + + // Query attributes object to see if integer divide instructions are + // forbidden to be in the object. This is not the inverse of + // attributes_accept_div. + static bool + attributes_forbid_div(const Object_attribute* div_attr); + // Merge object attributes from input object and those in the output. void merge_object_attributes(const char*, const Attributes_section_data*); @@ -2888,12 +3003,14 @@ class Target_arm : public Sized_target<32, big_endian> Output_data_plt_arm* 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 copy_relocs_; - // Space for variables copied with a COPY reloc. - Output_data_space* dynbss_; // Offset of the GOT entry for the TLS module index. unsigned int got_mod_index_offset_; // True if the _TLS_MODULE_BASE_ symbol has been defined. @@ -2912,6 +3029,11 @@ class Target_arm : public Sized_target<32, big_endian> bool fix_cortex_a8_; // Map addresses to relocs for Cortex-A8 erratum. Cortex_a8_relocs_info cortex_a8_relocs_info_; + // What R_ARM_TARGET1 maps to. It can be R_ARM_REL32 or R_ARM_ABS32. + unsigned int target1_reloc_; + // What R_ARM_TARGET2 maps to. It should be one of R_ARM_REL32, R_ARM_ABS32 + // and R_ARM_GOT_PREL. + unsigned int target2_reloc_; }; template @@ -2937,7 +3059,10 @@ const Target::Target_info Target_arm::arm_info = 0, // small_common_section_flags 0, // large_common_section_flags ".ARM.attributes", // attributes_section - "aeabi" // attributes_vendor + "aeabi", // attributes_vendor + "_start", // entry_symbol_name + 32, // hash_entry_size + elfcpp::SHT_PROGBITS, // unwind_section_type }; // Arm relocate functions class @@ -3253,7 +3378,6 @@ class Arm_relocate_functions : public Relocate_functions<32, big_endian> const Symbol_value<32>* psymval) { typedef typename elfcpp::Swap_unaligned<16, big_endian>::Valtype Valtype; - typedef typename elfcpp::Swap<32, big_endian>::Valtype Reltype; Valtype val = elfcpp::Swap_unaligned<16, big_endian>::readval(view); int32_t addend = Bits<16>::sign_extend32(val); Arm_address x = psymval->value(object, addend); @@ -3301,7 +3425,7 @@ class Arm_relocate_functions : public Relocate_functions<32, big_endian> const Symbol_value<32>* psymval, Arm_address address, Arm_address thumb_bit); - // R_ARM_THM_JUMP6: S + A – P + // R_ARM_THM_JUMP6: S + A - P static inline typename This::Status thm_jump6(unsigned char* view, const Sized_relobj_file<32, big_endian>* object, @@ -3312,7 +3436,7 @@ class Arm_relocate_functions : public Relocate_functions<32, big_endian> typedef typename elfcpp::Swap<16, big_endian>::Valtype Reltype; Valtype* wv = reinterpret_cast(view); Valtype val = elfcpp::Swap<16, big_endian>::readval(wv); - // bit[9]:bit[7:3]:’0’ (mask: 0x02f8) + // bit[9]:bit[7:3]:'0' (mask: 0x02f8) Reltype addend = (((val & 0x0200) >> 3) | ((val & 0x00f8) >> 2)); Reltype x = (psymval->value(object, addend) - address); val = (val & 0xfd07) | ((x & 0x0040) << 3) | ((val & 0x003e) << 2); @@ -3323,7 +3447,7 @@ class Arm_relocate_functions : public Relocate_functions<32, big_endian> : This::STATUS_OKAY); } - // R_ARM_THM_JUMP8: S + A – P + // R_ARM_THM_JUMP8: S + A - P static inline typename This::Status thm_jump8(unsigned char* view, const Sized_relobj_file<32, big_endian>* object, @@ -3343,7 +3467,7 @@ class Arm_relocate_functions : public Relocate_functions<32, big_endian> : This::STATUS_OKAY); } - // R_ARM_THM_JUMP11: S + A – P + // R_ARM_THM_JUMP11: S + A - P static inline typename This::Status thm_jump11(unsigned char* view, const Sized_relobj_file<32, big_endian>* object, @@ -4231,6 +4355,15 @@ Target_arm::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_; } @@ -4244,14 +4377,43 @@ Target_arm::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 +typename Target_arm::Reloc_section* +Target_arm::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. @@ -4355,30 +4517,49 @@ Stub::do_fixed_endian_write(unsigned char* view, section_size_type view_size) { const Stub_template* stub_template = this->stub_template(); const Insn_template* insns = stub_template->insns(); + const bool enable_be8 = parameters->options().be8(); - // FIXME: We do not handle BE8 encoding yet. unsigned char* pov = view; for (size_t i = 0; i < stub_template->insn_count(); i++) { switch (insns[i].type()) { case Insn_template::THUMB16_TYPE: - elfcpp::Swap<16, big_endian>::writeval(pov, insns[i].data() & 0xffff); + if (enable_be8) + elfcpp::Swap<16, false>::writeval(pov, insns[i].data() & 0xffff); + else + elfcpp::Swap<16, big_endian>::writeval(pov, + insns[i].data() & 0xffff); break; case Insn_template::THUMB16_SPECIAL_TYPE: - elfcpp::Swap<16, big_endian>::writeval( - pov, - this->thumb16_special(i)); + if (enable_be8) + elfcpp::Swap<16, false>::writeval(pov, this->thumb16_special(i)); + else + elfcpp::Swap<16, big_endian>::writeval(pov, + this->thumb16_special(i)); break; case Insn_template::THUMB32_TYPE: { uint32_t hi = (insns[i].data() >> 16) & 0xffff; uint32_t lo = insns[i].data() & 0xffff; - elfcpp::Swap<16, big_endian>::writeval(pov, hi); - elfcpp::Swap<16, big_endian>::writeval(pov + 2, lo); + if (enable_be8) + { + elfcpp::Swap<16, false>::writeval(pov, hi); + elfcpp::Swap<16, false>::writeval(pov + 2, lo); + } + else + { + elfcpp::Swap<16, big_endian>::writeval(pov, hi); + elfcpp::Swap<16, big_endian>::writeval(pov + 2, lo); + } } break; case Insn_template::ARM_TYPE: + if (enable_be8) + elfcpp::Swap<32, false>::writeval(pov, insns[i].data()); + else + elfcpp::Swap<32, big_endian>::writeval(pov, insns[i].data()); + break; case Insn_template::DATA_TYPE: elfcpp::Swap<32, big_endian>::writeval(pov, insns[i].data()); break; @@ -4445,7 +4626,7 @@ Reloc_stub::stub_type_for_reloc( // This is a bit ugly but we want to avoid using a templated class for // big and little endianities. bool may_use_blx; - bool should_force_pic_veneer; + bool should_force_pic_veneer = parameters->options().pic_veneer(); bool thumb2; bool thumb_only; if (parameters->target().is_big_endian()) @@ -4453,7 +4634,7 @@ Reloc_stub::stub_type_for_reloc( const Target_arm* big_endian_target = Target_arm::default_target(); may_use_blx = big_endian_target->may_use_v5t_interworking(); - should_force_pic_veneer = big_endian_target->should_force_pic_veneer(); + should_force_pic_veneer |= big_endian_target->should_force_pic_veneer(); thumb2 = big_endian_target->using_thumb2(); thumb_only = big_endian_target->using_thumb_only(); } @@ -4462,7 +4643,8 @@ Reloc_stub::stub_type_for_reloc( const Target_arm* little_endian_target = Target_arm::default_target(); may_use_blx = little_endian_target->may_use_v5t_interworking(); - should_force_pic_veneer = little_endian_target->should_force_pic_veneer(); + should_force_pic_veneer |= + little_endian_target->should_force_pic_veneer(); thumb2 = little_endian_target->using_thumb2(); thumb_only = little_endian_target->using_thumb_only(); } @@ -5209,8 +5391,6 @@ Arm_exidx_cantunwind::do_fixed_endian_write(Output_file* of) const section_size_type oview_size = 8; unsigned char* const oview = of->get_output_view(offset, oview_size); - typedef typename elfcpp::Swap_unaligned<32, big_endian>::Valtype Valtype; - Output_section* os = this->relobj_->output_section(this->shndx_); gold_assert(os != NULL); @@ -5644,10 +5824,6 @@ Arm_output_section::group_sections( Target_arm* target, const Task* task) { - // We only care about sections containing code. - if ((this->flags() & elfcpp::SHF_EXECINSTR) == 0) - return; - // States for grouping. typedef enum { @@ -6164,16 +6340,9 @@ Arm_relobj::scan_section_for_cortex_a8_erratum( this->mapping_symbols_info_.lower_bound(section_start); // There are no mapping symbols for this section. Treat it as a data-only - // section. Issue a warning if section is marked as containing - // instructions. + // section. if (p == this->mapping_symbols_info_.end() || p->first.first != shndx) - { - if ((this->section_flags(shndx) & elfcpp::SHF_EXECINSTR) != 0) - gold_warning(_("cannot scan executable section %u of %s for Cortex-A8 " - "erratum because it has no mapping symbols."), - shndx, this->name().c_str()); - return; - } + return; Arm_address output_address = this->simple_input_section_output_address(shndx, os); @@ -6438,9 +6607,9 @@ Arm_relobj::do_relocate_sections( Output_file* of, typename Sized_relobj_file<32, big_endian>::Views* pviews) { - // Call parent to relocate sections. - Sized_relobj_file<32, big_endian>::do_relocate_sections(symtab, layout, - pshdrs, of, pviews); + // Relocate the section data. + this->relocate_section_range(symtab, layout, pshdrs, of, pviews, + 1, this->shnum() - 1); // We do not generate stubs if doing a relocatable link. if (parameters->options().relocatable()) @@ -6522,6 +6691,80 @@ Arm_relobj::do_relocate_sections( section_address, section_size); } + // BE8 swapping + if (parameters->options().be8()) + { + section_size_type span_start, span_end; + elfcpp::Shdr<32, big_endian> + shdr(pshdrs + i * elfcpp::Elf_sizes<32>::shdr_size); + Mapping_symbol_position section_start(i, 0); + typename Mapping_symbols_info::const_iterator p = + this->mapping_symbols_info_.lower_bound(section_start); + unsigned char* view = (*pviews)[i].view; + Arm_address view_address = (*pviews)[i].address; + section_size_type view_size = (*pviews)[i].view_size; + while (p != this->mapping_symbols_info_.end() + && p->first.first == i) + { + typename Mapping_symbols_info::const_iterator next = + this->mapping_symbols_info_.upper_bound(p->first); + + // Only swap arm or thumb code. + if ((p->second == 'a') || (p->second == 't')) + { + Output_section* os = this->output_section(i); + gold_assert(os != NULL); + Arm_address section_address = + this->simple_input_section_output_address(i, os); + span_start = convert_to_section_size_type(p->first.second); + if (next != this->mapping_symbols_info_.end() + && next->first.first == i) + span_end = + convert_to_section_size_type(next->first.second); + else + span_end = + convert_to_section_size_type(shdr.get_sh_size()); + unsigned char* section_view = + view + (section_address - view_address); + uint64_t section_size = this->section_size(i); + + gold_assert(section_address >= view_address + && ((section_address + section_size) + <= (view_address + view_size))); + + // Set Output view for swapping + unsigned char *oview = section_view + span_start; + unsigned int index = 0; + if (p->second == 'a') + { + while (index + 3 < (span_end - span_start)) + { + typedef typename elfcpp::Swap<32, big_endian> + ::Valtype Valtype; + Valtype* wv = + reinterpret_cast(oview+index); + uint32_t val = elfcpp::Swap<32, false>::readval(wv); + elfcpp::Swap<32, true>::writeval(wv, val); + index += 4; + } + } + else if (p->second == 't') + { + while (index + 1 < (span_end - span_start)) + { + typedef typename elfcpp::Swap<16, big_endian> + ::Valtype Valtype; + Valtype* wv = + reinterpret_cast(oview+index); + uint16_t val = elfcpp::Swap<16, false>::readval(wv); + elfcpp::Swap<16, true>::writeval(wv, val); + index += 2; + } + } + } + p = next; + } + } } } @@ -6676,7 +6919,7 @@ void Arm_relobj::do_read_symbols(Read_symbols_data* sd) { // Call parent class to read symbol information. - Sized_relobj_file<32, big_endian>::do_read_symbols(sd); + this->base_read_symbols(sd); // If this input file is a binary file, it has no processor // specific flags and attributes section. @@ -6967,7 +7210,7 @@ void Arm_dynobj::do_read_symbols(Read_symbols_data* sd) { // Call parent class to read symbol information. - Sized_dynobj<32, big_endian>::do_read_symbols(sd); + this->base_read_symbols(sd); // Read processor-specific flags in ELF file header. const unsigned char* pehdr = this->get_view(elfcpp::file_header_offset, @@ -7214,24 +7457,80 @@ template 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 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* 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 @@ -7243,6 +7542,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 @@ -7291,19 +7598,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* 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_vec; + IRelative_data_vec irelative_data_vec_; }; // Create the PLT section. The ordinary .got section is an argument, @@ -7311,10 +7636,14 @@ class Output_data_plt_arm : public Output_section_data // section just for PLT entries. template -Output_data_plt_arm::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::Output_data_plt_arm( + Layout* layout, uint64_t addralign, + Arm_output_data_got* 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, @@ -7333,40 +7662,210 @@ Output_data_plt_arm::do_adjust_output_section(Output_section* os) template void -Output_data_plt_arm::add_entry(Symbol* gsym) +Output_data_plt_arm::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 +unsigned int +Output_data_plt_arm::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 +void +Output_data_plt_arm::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 +typename Output_data_plt_arm::Reloc_section* +Output_data_plt_arm::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* arm_target = + Target_arm::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 +uint32_t +Output_data_plt_arm::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 +uint32_t +Output_data_plt_arm::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 class Output_data_plt_arm_standard : public Output_data_plt_arm { public: - Output_data_plt_arm_standard(Layout* layout, Output_data_space* got_plt) - : Output_data_plt_arm(layout, 4, got_plt) + Output_data_plt_arm_standard(Layout* layout, + Arm_output_data_got* got, + Output_data_space* got_plt, + Output_data_space* got_irelative) + : Output_data_plt_arm(layout, 4, got, got_plt, got_irelative) { } protected: @@ -7375,29 +7874,14 @@ class Output_data_plt_arm_standard : public Output_data_plt_arm do_first_plt_entry_offset() const { return sizeof(first_plt_entry); } - // Return the size of a PLT entry. - virtual unsigned int - do_get_plt_entry_size() const - { return sizeof(plt_entry); } - virtual void do_fill_first_plt_entry(unsigned char* pov, Arm_address got_address, Arm_address plt_address); - virtual void - do_fill_plt_entry(unsigned char* pov, - Arm_address got_address, - Arm_address plt_address, - unsigned int got_offset, - unsigned int plt_offset); - private: // Template for the first PLT entry. static const uint32_t first_plt_entry[5]; - - // Template for subsequent PLT entries. - static const uint32_t plt_entry[3]; }; // ARM PLTs. @@ -7425,18 +7909,59 @@ Output_data_plt_arm_standard::do_fill_first_plt_entry( { // Write first PLT entry. All but the last word are constants. const size_t num_first_plt_words = (sizeof(first_plt_entry) - / sizeof(plt_entry[0])); + / sizeof(first_plt_entry[0])); for (size_t i = 0; i < num_first_plt_words - 1; i++) - elfcpp::Swap<32, big_endian>::writeval(pov + i * 4, first_plt_entry[i]); + { + if (parameters->options().be8()) + { + elfcpp::Swap<32, false>::writeval(pov + i * 4, + first_plt_entry[i]); + } + else + { + elfcpp::Swap<32, big_endian>::writeval(pov + i * 4, + first_plt_entry[i]); + } + } // Last word in first PLT entry is &GOT[0] - . elfcpp::Swap<32, big_endian>::writeval(pov + 16, got_address - (plt_address + 16)); } // Subsequent entries in the PLT. +// This class generates short (12-byte) entries, for displacements up to 2^28. template -const uint32_t Output_data_plt_arm_standard::plt_entry[3] = +class Output_data_plt_arm_short : public Output_data_plt_arm_standard +{ + public: + Output_data_plt_arm_short(Layout* layout, + Arm_output_data_got* got, + Output_data_space* got_plt, + Output_data_space* got_irelative) + : Output_data_plt_arm_standard(layout, got, got_plt, got_irelative) + { } + + protected: + // Return the size of a PLT entry. + virtual unsigned int + do_get_plt_entry_size() const + { return sizeof(plt_entry); } + + virtual void + do_fill_plt_entry(unsigned char* pov, + Arm_address got_address, + Arm_address plt_address, + unsigned int got_offset, + unsigned int plt_offset); + + private: + // Template for subsequent PLT entries. + static const uint32_t plt_entry[3]; +}; + +template +const uint32_t Output_data_plt_arm_short::plt_entry[3] = { 0xe28fc600, // add ip, pc, #0xNN00000 0xe28cca00, // add ip, ip, #0xNN000 @@ -7445,7 +7970,7 @@ const uint32_t Output_data_plt_arm_standard::plt_entry[3] = template void -Output_data_plt_arm_standard::do_fill_plt_entry( +Output_data_plt_arm_short::do_fill_plt_entry( unsigned char* pov, Arm_address got_address, Arm_address plt_address, @@ -7454,14 +7979,98 @@ Output_data_plt_arm_standard::do_fill_plt_entry( { int32_t offset = ((got_address + got_offset) - (plt_address + plt_offset + 8)); + if (offset < 0 || offset > 0x0fffffff) + gold_error(_("PLT offset too large, try linking with --long-plt")); - gold_assert(offset >= 0 && offset < 0x0fffffff); uint32_t plt_insn0 = plt_entry[0] | ((offset >> 20) & 0xff); - elfcpp::Swap<32, big_endian>::writeval(pov, plt_insn0); uint32_t plt_insn1 = plt_entry[1] | ((offset >> 12) & 0xff); - elfcpp::Swap<32, big_endian>::writeval(pov + 4, plt_insn1); uint32_t plt_insn2 = plt_entry[2] | (offset & 0xfff); - elfcpp::Swap<32, big_endian>::writeval(pov + 8, plt_insn2); + + if (parameters->options().be8()) + { + elfcpp::Swap<32, false>::writeval(pov, plt_insn0); + elfcpp::Swap<32, false>::writeval(pov + 4, plt_insn1); + elfcpp::Swap<32, false>::writeval(pov + 8, plt_insn2); + } + else + { + elfcpp::Swap<32, big_endian>::writeval(pov, plt_insn0); + elfcpp::Swap<32, big_endian>::writeval(pov + 4, plt_insn1); + elfcpp::Swap<32, big_endian>::writeval(pov + 8, plt_insn2); + } +} + +// This class generates long (16-byte) entries, for arbitrary displacements. + +template +class Output_data_plt_arm_long : public Output_data_plt_arm_standard +{ + public: + Output_data_plt_arm_long(Layout* layout, + Arm_output_data_got* got, + Output_data_space* got_plt, + Output_data_space* got_irelative) + : Output_data_plt_arm_standard(layout, got, got_plt, got_irelative) + { } + + protected: + // Return the size of a PLT entry. + virtual unsigned int + do_get_plt_entry_size() const + { return sizeof(plt_entry); } + + virtual void + do_fill_plt_entry(unsigned char* pov, + Arm_address got_address, + Arm_address plt_address, + unsigned int got_offset, + unsigned int plt_offset); + + private: + // Template for subsequent PLT entries. + static const uint32_t plt_entry[4]; +}; + +template +const uint32_t Output_data_plt_arm_long::plt_entry[4] = +{ + 0xe28fc200, // add ip, pc, #0xN0000000 + 0xe28cc600, // add ip, ip, #0xNN00000 + 0xe28cca00, // add ip, ip, #0xNN000 + 0xe5bcf000, // ldr pc, [ip, #0xNNN]! +}; + +template +void +Output_data_plt_arm_long::do_fill_plt_entry( + unsigned char* pov, + Arm_address got_address, + Arm_address plt_address, + unsigned int got_offset, + unsigned int plt_offset) +{ + int32_t offset = ((got_address + got_offset) + - (plt_address + plt_offset + 8)); + + uint32_t plt_insn0 = plt_entry[0] | (offset >> 28); + uint32_t plt_insn1 = plt_entry[1] | ((offset >> 20) & 0xff); + uint32_t plt_insn2 = plt_entry[2] | ((offset >> 12) & 0xff); + uint32_t plt_insn3 = plt_entry[3] | (offset & 0xfff); + + if (parameters->options().be8()) + { + elfcpp::Swap<32, false>::writeval(pov, plt_insn0); + elfcpp::Swap<32, false>::writeval(pov + 4, plt_insn1); + elfcpp::Swap<32, false>::writeval(pov + 8, plt_insn2); + elfcpp::Swap<32, false>::writeval(pov + 12, plt_insn3); + } + else + { + elfcpp::Swap<32, big_endian>::writeval(pov, plt_insn0); + elfcpp::Swap<32, big_endian>::writeval(pov + 4, plt_insn1); + elfcpp::Swap<32, big_endian>::writeval(pov + 8, plt_insn2); + elfcpp::Swap<32, big_endian>::writeval(pov + 12, plt_insn3); + } } // Write out the PLT. This uses the hand-coded instructions above, @@ -7478,8 +8087,11 @@ Output_data_plt_arm::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; @@ -7498,7 +8110,8 @@ Output_data_plt_arm::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, @@ -7511,8 +8124,33 @@ Output_data_plt_arm::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(pov - oview) == oview_size); @@ -7522,6 +8160,7 @@ Output_data_plt_arm::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 @@ -7532,21 +8171,66 @@ Target_arm::make_plt_entry(Symbol_table* symtab, Layout* layout, if (gsym->has_plt_offset()) return; + if (this->plt_ == NULL) + this->make_plt_section(symtab, layout); + + this->plt_->add_entry(symtab, layout, gsym); +} + + +// Create the PLT section. +template +void +Target_arm::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); + symtab->define_in_output_data("$a", NULL, + Symbol_table::PREDEFINED, + this->plt_, + 0, 0, elfcpp::STT_NOTYPE, + elfcpp::STB_LOCAL, + elfcpp::STV_DEFAULT, 0, + false, false); } - this->plt_->add_entry(gsym); } + +// Make a PLT entry for a local STT_GNU_IFUNC symbol. + +template +void +Target_arm::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 @@ -7816,6 +8500,7 @@ Target_arm::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. @@ -7846,6 +8531,27 @@ Target_arm::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 +Target_arm::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. @@ -7860,9 +8566,22 @@ Target_arm::Scan::local(Symbol_table* symtab, Output_section* output_section, const elfcpp::Rel<32, big_endian>& reloc, unsigned int r_type, - const elfcpp::Sym<32, big_endian>& lsym) + const elfcpp::Sym<32, big_endian>& lsym, + bool is_discarded) { - r_type = get_real_reloc_type(r_type); + if (is_discarded) + return; + + r_type = target->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: @@ -7887,7 +8606,7 @@ Target_arm::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; @@ -8071,7 +8790,7 @@ Target_arm::Scan::local(Symbol_table* symtab, got->add_local_pair_with_rel(object, r_sym, shndx, GOT_TYPE_TLS_PAIR, target->rel_dyn_section(layout), - elfcpp::R_ARM_TLS_DTPMOD32, 0); + elfcpp::R_ARM_TLS_DTPMOD32); else got->add_tls_gd32_with_static_reloc(GOT_TYPE_TLS_PAIR, object, r_sym); @@ -8254,7 +8973,12 @@ Target_arm::Scan::global(Symbol_table* symtab, && strcmp(gsym->name(), "_GLOBAL_OFFSET_TABLE_") == 0) target->got_section(symtab, layout); - r_type = get_real_reloc_type(r_type); + // 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 = target->get_real_reloc_type(r_type); switch (r_type) { case elfcpp::R_ARM_NONE: @@ -8290,11 +9014,30 @@ Target_arm::Scan::global(Symbol_table* symtab, // Make a dynamic relocation if necessary. if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type))) { - if (gsym->may_need_copy_reloc()) + if (!parameters->options().output_is_position_independent() + && gsym->may_need_copy_reloc()) { target->copy_reloc(symtab, layout, object, data_shndx, output_section, gsym, reloc); } + 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)) @@ -8371,7 +9114,8 @@ Target_arm::Scan::global(Symbol_table* symtab, // Make a dynamic relocation if necessary. if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type))) { - if (target->may_need_copy_reloc(gsym)) + if (parameters->options().output_is_executable() + && target->may_need_copy_reloc(gsym)) { target->copy_reloc(symtab, layout, object, data_shndx, output_section, gsym, reloc); @@ -8429,7 +9173,13 @@ Target_arm::Scan::global(Symbol_table* symtab, Arm_output_data_got* 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 @@ -8439,12 +9189,29 @@ Target_arm::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)); @@ -8587,8 +9354,7 @@ Target_arm::gc_process_relocs( typedef Target_arm Arm; typedef typename Target_arm::Scan Scan; - gold::gc_process_relocs<32, big_endian, Arm, elfcpp::SHT_REL, Scan, - typename Target_arm::Relocatable_size_for_reloc>( + gold::gc_process_relocs<32, big_endian, Arm, Scan, Classify_reloc>( symtab, layout, this, @@ -8618,7 +9384,6 @@ Target_arm::scan_relocs(Symbol_table* symtab, size_t local_symbol_count, const unsigned char* plocal_symbols) { - typedef typename Target_arm::Scan Scan; if (sh_type == elfcpp::SHT_RELA) { gold_error(_("%s: unsupported RELA reloc section"), @@ -8626,7 +9391,7 @@ Target_arm::scan_relocs(Symbol_table* symtab, return; } - gold::scan_relocs<32, big_endian, Target_arm, elfcpp::SHT_REL, Scan>( + gold::scan_relocs<32, big_endian, Target_arm, Scan, Classify_reloc>( symtab, layout, this, @@ -8821,20 +9586,25 @@ template inline bool Target_arm::Relocate::relocate( const Relocate_info<32, big_endian>* relinfo, + unsigned int, Target_arm* target, Output_section* output_section, size_t relnum, - const elfcpp::Rel<32, big_endian>& rel, - unsigned int r_type, + const unsigned char* preloc, const Sized_symbol<32>* gsym, const Symbol_value<32>* psymval, unsigned char* view, Arm_address address, section_size_type view_size) { + if (view == NULL) + return true; + typedef Arm_relocate_functions Arm_relocate_functions; - r_type = get_real_reloc_type(r_type); + const elfcpp::Rel<32, big_endian> rel(preloc); + unsigned int r_type = elfcpp::elf_r_type<32>(rel.get_r_info()); + r_type = target->get_real_reloc_type(r_type); const Arm_reloc_property* reloc_property = arm_reloc_property_table->get_implemented_static_reloc_property(r_type); if (reloc_property == NULL) @@ -8903,8 +9673,7 @@ Target_arm::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()) @@ -8942,6 +9711,13 @@ Target_arm::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 @@ -9515,8 +10291,8 @@ Target_arm::relocate_section( } } - gold::relocate_section<32, big_endian, Target_arm, elfcpp::SHT_REL, - Arm_relocate>( + gold::relocate_section<32, big_endian, Target_arm, Arm_relocate, + gold::Default_comdat_behavior, Classify_reloc>( relinfo, this, prelocs, @@ -9534,11 +10310,13 @@ Target_arm::relocate_section( template unsigned int -Target_arm::Relocatable_size_for_reloc::get_size_for_reloc( +Target_arm::Classify_reloc::get_size_for_reloc( unsigned int r_type, Relobj* object) { - r_type = get_real_reloc_type(r_type); + Target_arm* arm_target = + Target_arm::default_target(); + r_type = arm_target->get_real_reloc_type(r_type); const Arm_reloc_property* arp = arm_reloc_property_table->get_implemented_static_reloc_property(r_type); if (arp != NULL) @@ -9571,13 +10349,12 @@ Target_arm::scan_relocatable_relocs( const unsigned char* plocal_symbols, Relocatable_relocs* rr) { - gold_assert(sh_type == elfcpp::SHT_REL); + typedef Arm_scan_relocatable_relocs + Scan_relocatable_relocs; - typedef Arm_scan_relocatable_relocs Scan_relocatable_relocs; + gold_assert(sh_type == elfcpp::SHT_REL); - gold::scan_relocatable_relocs<32, big_endian, elfcpp::SHT_REL, - Scan_relocatable_relocs>( + gold::scan_relocatable_relocs<32, big_endian, Scan_relocatable_relocs>( symtab, layout, object, @@ -9591,18 +10368,55 @@ Target_arm::scan_relocatable_relocs( rr); } -// Relocate a section during a relocatable link. +// Scan the relocs for --emit-relocs. template void -Target_arm::relocate_for_relocatable( +Target_arm::emit_relocs_scan(Symbol_table* symtab, + Layout* layout, + Sized_relobj_file<32, big_endian>* object, + unsigned int data_shndx, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + Output_section* output_section, + bool needs_special_offset_handling, + size_t local_symbol_count, + const unsigned char* plocal_syms, + Relocatable_relocs* rr) +{ + typedef gold::Default_classify_reloc + Classify_reloc; + typedef gold::Default_emit_relocs_strategy + Emit_relocs_strategy; + + gold_assert(sh_type == elfcpp::SHT_REL); + + gold::scan_relocatable_relocs<32, big_endian, Emit_relocs_strategy>( + symtab, + layout, + object, + data_shndx, + prelocs, + reloc_count, + output_section, + needs_special_offset_handling, + local_symbol_count, + plocal_syms, + rr); +} + +// Emit relocations for a section. + +template +void +Target_arm::relocate_relocs( const Relocate_info<32, big_endian>* relinfo, unsigned int sh_type, const unsigned char* prelocs, size_t reloc_count, Output_section* output_section, - off_t offset_in_output_section, - const Relocatable_relocs* rr, + typename elfcpp::Elf_types<32>::Elf_Off offset_in_output_section, unsigned char* view, Arm_address view_address, section_size_type view_size, @@ -9611,13 +10425,12 @@ Target_arm::relocate_for_relocatable( { gold_assert(sh_type == elfcpp::SHT_REL); - gold::relocate_for_relocatable<32, big_endian, elfcpp::SHT_REL>( + gold::relocate_relocs<32, big_endian, Classify_reloc>( relinfo, prelocs, reloc_count, output_section, offset_in_output_section, - rr, view, view_address, view_size, @@ -9636,7 +10449,7 @@ Target_arm::relocate_special_relocatable( const unsigned char* preloc_in, size_t relnum, Output_section* output_section, - off_t offset_in_output_section, + typename elfcpp::Elf_types<32>::Elf_Off offset_in_output_section, unsigned char* view, elfcpp::Elf_types<32>::Elf_Addr view_address, section_size_type, @@ -9920,24 +10733,22 @@ uint64_t Target_arm::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 // template unsigned int -Target_arm::get_real_reloc_type(unsigned int r_type) +Target_arm::get_real_reloc_type(unsigned int r_type) const { switch (r_type) { case elfcpp::R_ARM_TARGET1: - // This is either R_ARM_ABS32 or R_ARM_REL32; - return elfcpp::R_ARM_ABS32; + return this->target1_reloc_; case elfcpp::R_ARM_TARGET2: - // This can be any reloc type but usually is R_ARM_GOT_PREL - return elfcpp::R_ARM_GOT_PREL; + return this->target2_reloc_; default: return r_type; @@ -10014,25 +10825,49 @@ template void Target_arm::do_adjust_elf_header( unsigned char* view, - int len) const + int len) { gold_assert(len == elfcpp::Elf_sizes<32>::ehdr_size); elfcpp::Ehdr<32, big_endian> ehdr(view); + elfcpp::Elf_Word flags = this->processor_specific_flags(); unsigned char e_ident[elfcpp::EI_NIDENT]; memcpy(e_ident, ehdr.get_e_ident(), elfcpp::EI_NIDENT); - if (elfcpp::arm_eabi_version(this->processor_specific_flags()) + if (elfcpp::arm_eabi_version(flags) == elfcpp::EF_ARM_EABI_UNKNOWN) e_ident[elfcpp::EI_OSABI] = elfcpp::ELFOSABI_ARM; else e_ident[elfcpp::EI_OSABI] = 0; e_ident[elfcpp::EI_ABIVERSION] = 0; - // FIXME: Do EF_ARM_BE8 adjustment. + // Do EF_ARM_BE8 adjustment. + if (parameters->options().be8() && !big_endian) + gold_error("BE8 images only valid in big-endian mode."); + if (parameters->options().be8()) + { + flags |= elfcpp::EF_ARM_BE8; + this->set_processor_specific_flags(flags); + } + // If we're working in EABI_VER5, set the hard/soft float ABI flags + // as appropriate. + if (elfcpp::arm_eabi_version(flags) == elfcpp::EF_ARM_EABI_VER5) + { + elfcpp::Elf_Half type = ehdr.get_e_type(); + if (type == elfcpp::ET_EXEC || type == elfcpp::ET_DYN) + { + Object_attribute* attr = this->get_aeabi_object_attribute(elfcpp::Tag_ABI_VFP_args); + if (attr->int_value() == elfcpp::AEABI_VFP_args_vfp) + flags |= elfcpp::EF_ARM_ABI_FLOAT_HARD; + else + flags |= elfcpp::EF_ARM_ABI_FLOAT_SOFT; + this->set_processor_specific_flags(flags); + } + } elfcpp::Ehdr_write<32, big_endian> oehdr(view); oehdr.put_e_ident(e_ident); + oehdr.put_e_flags(this->processor_specific_flags()); } // do_make_elf_object to override the same function in the base class. @@ -10229,6 +11064,24 @@ Target_arm::tag_cpu_arch_combine( T(V7E_M), // V6S_M. T(V7E_M) // V7E_M. }; + static const int v8[] = + { + T(V8), // PRE_V4. + T(V8), // V4. + T(V8), // V4T. + T(V8), // V5T. + T(V8), // V5TE. + T(V8), // V5TEJ. + T(V8), // V6. + T(V8), // V6KZ. + T(V8), // V6T2. + T(V8), // V6K. + T(V8), // V7. + T(V8), // V6_M. + T(V8), // V6S_M. + T(V8), // V7E_M. + T(V8) // V8. + }; static const int v4t_plus_v6_m[] = { -1, // PRE_V4. @@ -10245,6 +11098,7 @@ Target_arm::tag_cpu_arch_combine( T(V6_M), // V6_M. T(V6S_M), // V6S_M. T(V7E_M), // V7E_M. + T(V8), // V8. T(V4T_PLUS_V6_M) // V4T plus V6_M. }; static const int* comb[] = @@ -10255,6 +11109,7 @@ Target_arm::tag_cpu_arch_combine( v6_m, v6s_m, v7e_m, + v8, // Pseudo-architecture. v4t_plus_v6_m }; @@ -10352,7 +11207,8 @@ Target_arm::tag_cpu_name_value(unsigned int value) "ARM v7", "ARM v6-M", "ARM v6S-M", - "ARM v7E-M" + "ARM v7E-M", + "ARM v8" }; const size_t name_table_size = sizeof(name_table) / sizeof(name_table[0]); @@ -10366,6 +11222,49 @@ Target_arm::tag_cpu_name_value(unsigned int value) } } +// Query attributes object to see if integer divide instructions may be +// present in an object. + +template +bool +Target_arm::attributes_accept_div(int arch, int profile, + const Object_attribute* div_attr) +{ + switch (div_attr->int_value()) + { + case 0: + // Integer divide allowed if instruction contained in + // architecture. + if (arch == elfcpp::TAG_CPU_ARCH_V7 && (profile == 'R' || profile == 'M')) + return true; + else if (arch >= elfcpp::TAG_CPU_ARCH_V7E_M) + return true; + else + return false; + + case 1: + // Integer divide explicitly prohibited. + return false; + + default: + // Unrecognised case - treat as allowing divide everywhere. + case 2: + // Integer divide allowed in ARM state. + return true; + } +} + +// Query attributes object to see if integer divide instructions are +// forbidden to be in the object. This is not the inverse of +// attributes_accept_div. + +template +bool +Target_arm::attributes_forbid_div(const Object_attribute* div_attr) +{ + return div_attr->int_value() == 1; +} + // Merge object attributes from input file called NAME with those of the // output. The input object attributes are in the object pointed by PASD. @@ -10418,10 +11317,18 @@ Target_arm::merge_object_attributes( != out_attr[elfcpp::Tag_ABI_VFP_args].int_value()) { // Ignore mismatches if the object doesn't use floating point. */ - if (out_attr[elfcpp::Tag_ABI_FP_number_model].int_value() == 0) + if (out_attr[elfcpp::Tag_ABI_FP_number_model].int_value() + == elfcpp::AEABI_FP_number_model_none + || (in_attr[elfcpp::Tag_ABI_FP_number_model].int_value() + != elfcpp::AEABI_FP_number_model_none + && out_attr[elfcpp::Tag_ABI_VFP_args].int_value() + == elfcpp::AEABI_VFP_args_compatible)) out_attr[elfcpp::Tag_ABI_VFP_args].set_int_value( in_attr[elfcpp::Tag_ABI_VFP_args].int_value()); - else if (in_attr[elfcpp::Tag_ABI_FP_number_model].int_value() != 0 + else if (in_attr[elfcpp::Tag_ABI_FP_number_model].int_value() + != elfcpp::AEABI_FP_number_model_none + && in_attr[elfcpp::Tag_ABI_VFP_args].int_value() + != elfcpp::AEABI_VFP_args_compatible && parameters->options().warn_mismatch()) gold_error(_("%s uses VFP register arguments, output does not"), name); @@ -10735,27 +11642,33 @@ Target_arm::merge_object_attributes( break; case elfcpp::Tag_DIV_use: - // This tag is set to zero if we can use UDIV and SDIV in Thumb - // mode on a v7-M or v7-R CPU; to one if we can not use UDIV or - // SDIV at all; and to two if we can use UDIV or SDIV on a v7-A - // CPU. We will merge as follows: If the input attribute's value - // is one then the output attribute's value remains unchanged. If - // the input attribute's value is zero or two then if the output - // attribute's value is one the output value is set to the input - // value, otherwise the output value must be the same as the - // inputs. */ - if (in_attr[i].int_value() != 1 && out_attr[i].int_value() != 1) - { - if (in_attr[i].int_value() != out_attr[i].int_value()) - { - gold_error(_("DIV usage mismatch between %s and output"), - name); - } - } - - if (in_attr[i].int_value() != 1) - out_attr[i].set_int_value(in_attr[i].int_value()); - + { + // A value of zero on input means that the divide + // instruction may be used if available in the base + // architecture as specified via Tag_CPU_arch and + // Tag_CPU_arch_profile. A value of 1 means that the user + // did not want divide instructions. A value of 2 + // explicitly means that divide instructions were allowed + // in ARM and Thumb state. + int arch = this-> + get_aeabi_object_attribute(elfcpp::Tag_CPU_arch)-> + int_value(); + int profile = this-> + get_aeabi_object_attribute(elfcpp::Tag_CPU_arch_profile)-> + int_value(); + if (in_attr[i].int_value() == out_attr[i].int_value()) + { + // Do nothing. + } + else if (attributes_forbid_div(&in_attr[i]) + && !attributes_accept_div(arch, profile, &out_attr[i])) + out_attr[i].set_int_value(1); + else if (attributes_forbid_div(&out_attr[i]) + && attributes_accept_div(arch, profile, &in_attr[i])) + out_attr[i].set_int_value(in_attr[i].int_value()); + else if (in_attr[i].int_value() == 2) + out_attr[i].set_int_value(in_attr[i].int_value()); + } break; case elfcpp::Tag_MPextension_use_legacy: @@ -10767,7 +11680,7 @@ Target_arm::merge_object_attributes( if (in_attr[elfcpp::Tag_MPextension_use].int_value() != in_attr[i].int_value()) { - gold_error(_("%s has has both the current and legacy " + gold_error(_("%s has both the current and legacy " "Tag_MPextension_use attributes"), name); } @@ -10990,8 +11903,6 @@ Target_arm::scan_reloc_for_stub( elfcpp::Elf_types<32>::Elf_Swxword addend, Arm_address address) { - typedef typename Target_arm::Relocate Relocate; - const Arm_relobj* arm_relobj = Arm_relobj::as_arm_relobj(relinfo->object); @@ -11004,8 +11915,7 @@ Target_arm::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; } @@ -11150,6 +12060,7 @@ Target_arm::scan_reloc_section_for_stubs( Arm_relobj::as_arm_relobj(relinfo->object); unsigned int local_count = arm_object->local_symbol_count(); + gold::Default_comdat_behavior default_comdat_behavior; Comdat_behavior comdat_behavior = CB_UNDETERMINED; for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size) @@ -11229,6 +12140,7 @@ Target_arm::scan_reloc_section_for_stubs( const Symbol_value<32> *psymval; bool is_defined_in_discarded_section; unsigned int shndx; + const Symbol* gsym = NULL; if (r_sym < local_count) { sym = NULL; @@ -11252,6 +12164,8 @@ Target_arm::scan_reloc_section_for_stubs( if (!is_defined_in_discarded_section) { typedef Sized_relobj_file<32, big_endian> ObjType; + if (psymval->is_section_symbol()) + symval.set_is_section_symbol(); typename ObjType::Compute_final_local_value_status status = arm_object->compute_final_local_value(r_sym, psymval, &symval, relinfo->symtab); @@ -11279,7 +12193,6 @@ Target_arm::scan_reloc_section_for_stubs( } else { - const Symbol* gsym; gsym = arm_object->global_symbol(r_sym); gold_assert(gsym != NULL); if (gsym->is_forwarder()) @@ -11320,11 +12233,11 @@ Target_arm::scan_reloc_section_for_stubs( Symbol_value<32> symval2; if (is_defined_in_discarded_section) { + std::string name = arm_object->section_name(relinfo->data_shndx); + if (comdat_behavior == CB_UNDETERMINED) - { - std::string name = arm_object->section_name(relinfo->data_shndx); - comdat_behavior = get_comdat_behavior(name.c_str()); - } + comdat_behavior = default_comdat_behavior.get(name.c_str()); + if (comdat_behavior == CB_PRETEND) { // FIXME: This case does not work for global symbols. @@ -11334,7 +12247,7 @@ Target_arm::scan_reloc_section_for_stubs( // script. bool found; typename elfcpp::Elf_types<32>::Elf_Addr value = - arm_object->map_to_kept_section(shndx, &found); + arm_object->map_to_kept_section(shndx, name, &found); if (found) symval2.set_output_value(value + psymval->input_value()); else @@ -11342,10 +12255,8 @@ Target_arm::scan_reloc_section_for_stubs( } else { - if (comdat_behavior == CB_WARNING) - gold_warning_at_location(relinfo, i, offset, - _("relocation refers to discarded " - "section")); + if (comdat_behavior == CB_ERROR) + issue_discarded_error(relinfo, i, offset, r_sym, gsym); symval2.set_output_value(0); } symval2.set_no_output_symtab_entry(); @@ -11423,7 +12334,7 @@ Target_arm::group_sections( { // Group input sections and insert stub table Layout::Section_list section_list; - layout->get_allocated_sections(§ion_list); + layout->get_executable_sections(§ion_list); for (Layout::Section_list::const_iterator p = section_list.begin(); p != section_list.end(); ++p) @@ -11678,10 +12589,9 @@ Target_arm::relocate_stub( elfcpp::Rel_write<32, big_endian> reloc_write(reloc_buffer); reloc_write.put_r_offset(reloc_offset); reloc_write.put_r_info(elfcpp::elf_r_info<32>(0, r_type)); - elfcpp::Rel<32, big_endian> rel(reloc_buffer); - relocate.relocate(relinfo, this, output_section, - this->fake_relnum_for_stubs, rel, r_type, + relocate.relocate(relinfo, elfcpp::SHT_REL, this, output_section, + this->fake_relnum_for_stubs, reloc_buffer, NULL, &symval, view + reloc_offset, address + reloc_offset, reloc_size); } @@ -11883,7 +12793,7 @@ Target_arm::scan_span_for_cortex_a8_erratum( Arm_address target = (pc_for_insn + offset) | (is_blx ? 0 : 1); - // Add a new stub if destination address in in the same page. + // Add a new stub if destination address is in the same page. if (((address + i) & ~0xfffU) == (target & ~0xfffU)) { Cortex_a8_stub* stub = @@ -11929,7 +12839,7 @@ Target_arm::apply_cortex_a8_workaround( // branch to the stub. We use the THUMB-2 encoding here. upper_insn = 0xf000U; lower_insn = 0xb800U; - // Fall through + // Fall through. case arm_stub_a8_veneer_b: case arm_stub_a8_veneer_bl: case arm_stub_a8_veneer_blx: @@ -11985,7 +12895,7 @@ Target_arm::fix_exidx_coverage( const Task* task) { // We need to look at all the input sections in output in ascending - // order of of output address. We do that by building a sorted list + // order of output address. We do that by building a sorted list // of output sections by addresses. Then we looks at the output sections // in order. The input sections in an output section are already sorted // by addresses within the output section. @@ -12107,8 +13017,13 @@ class Target_arm_nacl : public Target_arm protected: virtual Output_data_plt_arm* - do_make_data_plt(Layout* layout, Output_data_space* got_plt) - { return new Output_data_plt_arm_nacl(layout, got_plt); } + do_make_data_plt( + Layout* layout, + Arm_output_data_got* got, + Output_data_space* got_plt, + Output_data_space* got_irelative) + { return new Output_data_plt_arm_nacl( + layout, got, got_plt, got_irelative); } private: static const Target::Target_info arm_nacl_info; @@ -12137,15 +13052,22 @@ const Target::Target_info Target_arm_nacl::arm_nacl_info = 0, // small_common_section_flags 0, // large_common_section_flags ".ARM.attributes", // attributes_section - "aeabi" // attributes_vendor + "aeabi", // attributes_vendor + "_start", // entry_symbol_name + 32, // hash_entry_size + elfcpp::SHT_PROGBITS, // unwind_section_type }; template class Output_data_plt_arm_nacl : public Output_data_plt_arm { public: - Output_data_plt_arm_nacl(Layout* layout, Output_data_space* got_plt) - : Output_data_plt_arm(layout, 16, got_plt) + Output_data_plt_arm_nacl( + Layout* layout, + Arm_output_data_got* got, + Output_data_space* got_plt, + Output_data_space* got_irelative) + : Output_data_plt_arm(layout, 16, got, got_plt, got_irelative) { } protected: @@ -12199,7 +13121,7 @@ const uint32_t Output_data_plt_arm_nacl::first_plt_entry[16] = 0xe08cc00f, // add ip, ip, pc 0xe52dc008, // str ip, [sp, #-8]! // Second bundle: - 0xe7dfcf1f, // bfc ip, #30, #2 + 0xe3ccc103, // bic ip, ip, #0xc0000000 0xe59cc000, // ldr ip, [ip] 0xe3ccc13f, // bic ip, ip, #0xc000000f 0xe12fff1c, // bx ip @@ -12210,7 +13132,7 @@ const uint32_t Output_data_plt_arm_nacl::first_plt_entry[16] = // .Lplt_tail: 0xe50dc004, // str ip, [sp, #-4] // Fourth bundle: - 0xe7dfcf1f, // bfc ip, #30, #2 + 0xe3ccc103, // bic ip, ip, #0xc0000000 0xe59cc000, // ldr ip, [ip] 0xe3ccc13f, // bic ip, ip, #0xc000000f 0xe12fff1c, // bx ip