// aarch64.cc -- aarch64 target support for gold. // Copyright (C) 2014-2015 Free Software Foundation, Inc. // Written by Jing Yu and Han Shen . // This file is part of gold. // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, // MA 02110-1301, USA. #include "gold.h" #include #include #include #include "elfcpp.h" #include "dwarf.h" #include "parameters.h" #include "reloc.h" #include "aarch64.h" #include "object.h" #include "symtab.h" #include "layout.h" #include "output.h" #include "copy-relocs.h" #include "target.h" #include "target-reloc.h" #include "target-select.h" #include "tls.h" #include "freebsd.h" #include "nacl.h" #include "gc.h" #include "icf.h" #include "aarch64-reloc-property.h" // The first three .got.plt entries are reserved. const int32_t AARCH64_GOTPLT_RESERVE_COUNT = 3; namespace { using namespace gold; template class Output_data_plt_aarch64; template class Output_data_plt_aarch64_standard; template class Target_aarch64; template class AArch64_relocate_functions; // Utility class dealing with insns. This is ported from macros in // bfd/elfnn-aarch64.cc, but wrapped inside a class as static members. This // class is used in erratum sequence scanning. template class AArch64_insn_utilities { public: typedef typename elfcpp::Swap<32, big_endian>::Valtype Insntype; static const int BYTES_PER_INSN; // Zero register encoding - 31. static const unsigned int AARCH64_ZR; static unsigned int aarch64_bit(Insntype insn, int pos) { return ((1 << pos) & insn) >> pos; } static unsigned int aarch64_bits(Insntype insn, int pos, int l) { return (insn >> pos) & ((1 << l) - 1); } // Get the encoding field "op31" of 3-source data processing insns. "op31" is // the name defined in armv8 insn manual C3.5.9. static unsigned int aarch64_op31(Insntype insn) { return aarch64_bits(insn, 21, 3); } // Get the encoding field "ra" of 3-source data processing insns. "ra" is the // third source register. See armv8 insn manual C3.5.9. static unsigned int aarch64_ra(Insntype insn) { return aarch64_bits(insn, 10, 5); } static bool is_adrp(const Insntype insn) { return (insn & 0x9F000000) == 0x90000000; } static unsigned int aarch64_rm(const Insntype insn) { return aarch64_bits(insn, 16, 5); } static unsigned int aarch64_rn(const Insntype insn) { return aarch64_bits(insn, 5, 5); } static unsigned int aarch64_rd(const Insntype insn) { return aarch64_bits(insn, 0, 5); } static unsigned int aarch64_rt(const Insntype insn) { return aarch64_bits(insn, 0, 5); } static unsigned int aarch64_rt2(const Insntype insn) { return aarch64_bits(insn, 10, 5); } static bool aarch64_b(const Insntype insn) { return (insn & 0xFC000000) == 0x14000000; } static bool aarch64_bl(const Insntype insn) { return (insn & 0xFC000000) == 0x94000000; } static bool aarch64_blr(const Insntype insn) { return (insn & 0xFFFFFC1F) == 0xD63F0000; } static bool aarch64_br(const Insntype insn) { return (insn & 0xFFFFFC1F) == 0xD61F0000; } // All ld/st ops. See C4-182 of the ARM ARM. The encoding space for // LD_PCREL, LDST_RO, LDST_UI and LDST_UIMM cover prefetch ops. static bool aarch64_ld(Insntype insn) { return aarch64_bit(insn, 22) == 1; } static bool aarch64_ldst(Insntype insn) { return (insn & 0x0a000000) == 0x08000000; } static bool aarch64_ldst_ex(Insntype insn) { return (insn & 0x3f000000) == 0x08000000; } static bool aarch64_ldst_pcrel(Insntype insn) { return (insn & 0x3b000000) == 0x18000000; } static bool aarch64_ldst_nap(Insntype insn) { return (insn & 0x3b800000) == 0x28000000; } static bool aarch64_ldstp_pi(Insntype insn) { return (insn & 0x3b800000) == 0x28800000; } static bool aarch64_ldstp_o(Insntype insn) { return (insn & 0x3b800000) == 0x29000000; } static bool aarch64_ldstp_pre(Insntype insn) { return (insn & 0x3b800000) == 0x29800000; } static bool aarch64_ldst_ui(Insntype insn) { return (insn & 0x3b200c00) == 0x38000000; } static bool aarch64_ldst_piimm(Insntype insn) { return (insn & 0x3b200c00) == 0x38000400; } static bool aarch64_ldst_u(Insntype insn) { return (insn & 0x3b200c00) == 0x38000800; } static bool aarch64_ldst_preimm(Insntype insn) { return (insn & 0x3b200c00) == 0x38000c00; } static bool aarch64_ldst_ro(Insntype insn) { return (insn & 0x3b200c00) == 0x38200800; } static bool aarch64_ldst_uimm(Insntype insn) { return (insn & 0x3b000000) == 0x39000000; } static bool aarch64_ldst_simd_m(Insntype insn) { return (insn & 0xbfbf0000) == 0x0c000000; } static bool aarch64_ldst_simd_m_pi(Insntype insn) { return (insn & 0xbfa00000) == 0x0c800000; } static bool aarch64_ldst_simd_s(Insntype insn) { return (insn & 0xbf9f0000) == 0x0d000000; } static bool aarch64_ldst_simd_s_pi(Insntype insn) { return (insn & 0xbf800000) == 0x0d800000; } // Classify an INSN if it is indeed a load/store. Return true if INSN is a // LD/ST instruction otherwise return false. For scalar LD/ST instructions // PAIR is FALSE, RT is returned and RT2 is set equal to RT. For LD/ST pair // instructions PAIR is TRUE, RT and RT2 are returned. static bool aarch64_mem_op_p(Insntype insn, unsigned int *rt, unsigned int *rt2, bool *pair, bool *load) { uint32_t opcode; unsigned int r; uint32_t opc = 0; uint32_t v = 0; uint32_t opc_v = 0; /* Bail out quickly if INSN doesn't fall into the the load-store encoding space. */ if (!aarch64_ldst (insn)) return false; *pair = false; *load = false; if (aarch64_ldst_ex (insn)) { *rt = aarch64_rt (insn); *rt2 = *rt; if (aarch64_bit (insn, 21) == 1) { *pair = true; *rt2 = aarch64_rt2 (insn); } *load = aarch64_ld (insn); return true; } else if (aarch64_ldst_nap (insn) || aarch64_ldstp_pi (insn) || aarch64_ldstp_o (insn) || aarch64_ldstp_pre (insn)) { *pair = true; *rt = aarch64_rt (insn); *rt2 = aarch64_rt2 (insn); *load = aarch64_ld (insn); return true; } else if (aarch64_ldst_pcrel (insn) || aarch64_ldst_ui (insn) || aarch64_ldst_piimm (insn) || aarch64_ldst_u (insn) || aarch64_ldst_preimm (insn) || aarch64_ldst_ro (insn) || aarch64_ldst_uimm (insn)) { *rt = aarch64_rt (insn); *rt2 = *rt; if (aarch64_ldst_pcrel (insn)) *load = true; opc = aarch64_bits (insn, 22, 2); v = aarch64_bit (insn, 26); opc_v = opc | (v << 2); *load = (opc_v == 1 || opc_v == 2 || opc_v == 3 || opc_v == 5 || opc_v == 7); return true; } else if (aarch64_ldst_simd_m (insn) || aarch64_ldst_simd_m_pi (insn)) { *rt = aarch64_rt (insn); *load = aarch64_bit (insn, 22); opcode = (insn >> 12) & 0xf; switch (opcode) { case 0: case 2: *rt2 = *rt + 3; break; case 4: case 6: *rt2 = *rt + 2; break; case 7: *rt2 = *rt; break; case 8: case 10: *rt2 = *rt + 1; break; default: return false; } return true; } else if (aarch64_ldst_simd_s (insn) || aarch64_ldst_simd_s_pi (insn)) { *rt = aarch64_rt (insn); r = (insn >> 21) & 1; *load = aarch64_bit (insn, 22); opcode = (insn >> 13) & 0x7; switch (opcode) { case 0: case 2: case 4: *rt2 = *rt + r; break; case 1: case 3: case 5: *rt2 = *rt + (r == 0 ? 2 : 3); break; case 6: *rt2 = *rt + r; break; case 7: *rt2 = *rt + (r == 0 ? 2 : 3); break; default: return false; } return true; } return false; } // End of "aarch64_mem_op_p". // Return true if INSN is mac insn. static bool aarch64_mac(Insntype insn) { return (insn & 0xff000000) == 0x9b000000; } // Return true if INSN is multiply-accumulate. // (This is similar to implementaton in elfnn-aarch64.c.) static bool aarch64_mlxl(Insntype insn) { uint32_t op31 = aarch64_op31(insn); if (aarch64_mac(insn) && (op31 == 0 || op31 == 1 || op31 == 5) /* Exclude MUL instructions which are encoded as a multiple-accumulate with RA = XZR. */ && aarch64_ra(insn) != AARCH64_ZR) { return true; } return false; } }; // End of "AArch64_insn_utilities". // Insn length in byte. template const int AArch64_insn_utilities::BYTES_PER_INSN = 4; // Zero register encoding - 31. template const unsigned int AArch64_insn_utilities::AARCH64_ZR = 0x1f; // Output_data_got_aarch64 class. template class Output_data_got_aarch64 : public Output_data_got { public: typedef typename elfcpp::Elf_types::Elf_Addr Valtype; Output_data_got_aarch64(Symbol_table* symtab, Layout* layout) : Output_data_got(), symbol_table_(symtab), layout_(layout) { } // Add a static entry for the GOT entry at OFFSET. GSYM is a global // symbol and R_TYPE is the code of a dynamic relocation that needs to be // applied in a static link. void add_static_reloc(unsigned int got_offset, unsigned int r_type, Symbol* gsym) { this->static_relocs_.push_back(Static_reloc(got_offset, r_type, gsym)); } // Add a static reloc for the GOT entry at OFFSET. RELOBJ is an object // defining a local symbol with INDEX. R_TYPE is the code of a dynamic // relocation that needs to be applied in a static link. void add_static_reloc(unsigned int got_offset, unsigned int r_type, Sized_relobj_file* relobj, unsigned int index) { this->static_relocs_.push_back(Static_reloc(got_offset, r_type, relobj, index)); } protected: // Write out the GOT table. void do_write(Output_file* of) { // The first entry in the GOT is the address of the .dynamic section. gold_assert(this->data_size() >= size / 8); Output_section* dynamic = this->layout_->dynamic_section(); Valtype dynamic_addr = dynamic == NULL ? 0 : dynamic->address(); this->replace_constant(0, dynamic_addr); Output_data_got::do_write(of); // Handling static relocs if (this->static_relocs_.empty()) return; typedef typename elfcpp::Elf_types::Elf_Addr AArch64_address; gold_assert(parameters->doing_static_link()); const off_t offset = this->offset(); const section_size_type oview_size = convert_to_section_size_type(this->data_size()); unsigned char* const oview = of->get_output_view(offset, oview_size); Output_segment* tls_segment = this->layout_->tls_segment(); gold_assert(tls_segment != NULL); AArch64_address aligned_tcb_address = align_address(Target_aarch64::TCB_SIZE, tls_segment->maximum_alignment()); for (size_t i = 0; i < this->static_relocs_.size(); ++i) { Static_reloc& reloc(this->static_relocs_[i]); AArch64_address value; if (!reloc.symbol_is_global()) { Sized_relobj_file* object = reloc.relobj(); const Symbol_value* psymval = reloc.relobj()->local_symbol(reloc.index()); // We are doing static linking. Issue an error and skip this // relocation if the symbol is undefined or in a discarded_section. bool is_ordinary; unsigned int shndx = psymval->input_shndx(&is_ordinary); if ((shndx == elfcpp::SHN_UNDEF) || (is_ordinary && shndx != elfcpp::SHN_UNDEF && !object->is_section_included(shndx) && !this->symbol_table_->is_section_folded(object, shndx))) { gold_error(_("undefined or discarded local symbol %u from " " object %s in GOT"), reloc.index(), reloc.relobj()->name().c_str()); continue; } value = psymval->value(object, 0); } else { const Symbol* gsym = reloc.symbol(); gold_assert(gsym != NULL); if (gsym->is_forwarder()) gsym = this->symbol_table_->resolve_forwards(gsym); // We are doing static linking. Issue an error and skip this // relocation if the symbol is undefined or in a discarded_section // unless it is a weakly_undefined symbol. if ((gsym->is_defined_in_discarded_section() || gsym->is_undefined()) && !gsym->is_weak_undefined()) { gold_error(_("undefined or discarded symbol %s in GOT"), gsym->name()); continue; } if (!gsym->is_weak_undefined()) { const Sized_symbol* sym = static_cast*>(gsym); value = sym->value(); } else value = 0; } unsigned got_offset = reloc.got_offset(); gold_assert(got_offset < oview_size); typedef typename elfcpp::Swap::Valtype Valtype; Valtype* wv = reinterpret_cast(oview + got_offset); Valtype x; switch (reloc.r_type()) { case elfcpp::R_AARCH64_TLS_DTPREL64: x = value; break; case elfcpp::R_AARCH64_TLS_TPREL64: x = value + aligned_tcb_address; break; default: gold_unreachable(); } elfcpp::Swap::writeval(wv, x); } of->write_output_view(offset, oview_size, oview); } private: // Symbol table of the output object. Symbol_table* symbol_table_; // A pointer to the Layout class, so that we can find the .dynamic // section when we write out the GOT section. Layout* layout_; // This class represent dynamic relocations that need to be applied by // gold because we are using TLS relocations in a static link. class Static_reloc { public: Static_reloc(unsigned int got_offset, unsigned int r_type, Symbol* gsym) : got_offset_(got_offset), r_type_(r_type), symbol_is_global_(true) { this->u_.global.symbol = gsym; } Static_reloc(unsigned int got_offset, unsigned int r_type, Sized_relobj_file* relobj, unsigned int index) : got_offset_(got_offset), r_type_(r_type), symbol_is_global_(false) { this->u_.local.relobj = relobj; this->u_.local.index = index; } // Return the GOT offset. unsigned int got_offset() const { return this->got_offset_; } // Relocation type. unsigned int r_type() const { return this->r_type_; } // Whether the symbol is global or not. bool symbol_is_global() const { return this->symbol_is_global_; } // For a relocation against a global symbol, the global symbol. Symbol* symbol() const { gold_assert(this->symbol_is_global_); return this->u_.global.symbol; } // For a relocation against a local symbol, the defining object. Sized_relobj_file* relobj() const { gold_assert(!this->symbol_is_global_); return this->u_.local.relobj; } // For a relocation against a local symbol, the local symbol index. unsigned int index() const { gold_assert(!this->symbol_is_global_); return this->u_.local.index; } private: // GOT offset of the entry to which this relocation is applied. unsigned int got_offset_; // Type of relocation. unsigned int r_type_; // Whether this relocation is against a global symbol. bool symbol_is_global_; // A global or local symbol. union { struct { // For a global symbol, the symbol itself. Symbol* symbol; } global; struct { // For a local symbol, the object defining the symbol. Sized_relobj_file* relobj; // For a local symbol, the symbol index. unsigned int index; } local; } u_; }; // End of inner class Static_reloc std::vector static_relocs_; }; // End of Output_data_got_aarch64 template class AArch64_input_section; template class AArch64_output_section; template class AArch64_relobj; // Stub type enum constants. enum { ST_NONE = 0, // Using adrp/add pair, 4 insns (including alignment) without mem access, // the fastest stub. This has a limited jump distance, which is tested by // aarch64_valid_for_adrp_p. ST_ADRP_BRANCH = 1, // Using ldr-absolute-address/br-register, 4 insns with 1 mem access, // unlimited in jump distance. ST_LONG_BRANCH_ABS = 2, // Using ldr/calculate-pcrel/jump, 8 insns (including alignment) with 1 // mem access, slowest one. Only used in position independent executables. ST_LONG_BRANCH_PCREL = 3, // Stub for erratum 843419 handling. ST_E_843419 = 4, // Stub for erratum 835769 handling. ST_E_835769 = 5, // Number of total stub types. ST_NUMBER = 6 }; // Struct that wraps insns for a particular stub. All stub templates are // created/initialized as constants by Stub_template_repertoire. template struct Stub_template { const typename AArch64_insn_utilities::Insntype* insns; const int insn_num; }; // Simple singleton class that creates/initializes/stores all types of stub // templates. template class Stub_template_repertoire { public: typedef typename AArch64_insn_utilities::Insntype Insntype; // Single static method to get stub template for a given stub type. static const Stub_template* get_stub_template(int type) { static Stub_template_repertoire singleton; return singleton.stub_templates_[type]; } private: // Constructor - creates/initializes all stub templates. Stub_template_repertoire(); ~Stub_template_repertoire() { } // Disallowing copy ctor and copy assignment operator. Stub_template_repertoire(Stub_template_repertoire&); Stub_template_repertoire& operator=(Stub_template_repertoire&); // Data that stores all insn templates. const Stub_template* stub_templates_[ST_NUMBER]; }; // End of "class Stub_template_repertoire". // Constructor - creates/initilizes all stub templates. template Stub_template_repertoire::Stub_template_repertoire() { // Insn array definitions. const static Insntype ST_NONE_INSNS[] = {}; const static Insntype ST_ADRP_BRANCH_INSNS[] = { 0x90000010, /* adrp ip0, X */ /* ADR_PREL_PG_HI21(X) */ 0x91000210, /* add ip0, ip0, :lo12:X */ /* ADD_ABS_LO12_NC(X) */ 0xd61f0200, /* br ip0 */ 0x00000000, /* alignment padding */ }; const static Insntype ST_LONG_BRANCH_ABS_INSNS[] = { 0x58000050, /* ldr ip0, 0x8 */ 0xd61f0200, /* br ip0 */ 0x00000000, /* address field */ 0x00000000, /* address fields */ }; const static Insntype ST_LONG_BRANCH_PCREL_INSNS[] = { 0x58000090, /* ldr ip0, 0x10 */ 0x10000011, /* adr ip1, #0 */ 0x8b110210, /* add ip0, ip0, ip1 */ 0xd61f0200, /* br ip0 */ 0x00000000, /* address field */ 0x00000000, /* address field */ 0x00000000, /* alignment padding */ 0x00000000, /* alignment padding */ }; const static Insntype ST_E_843419_INSNS[] = { 0x00000000, /* Placeholder for erratum insn. */ 0x14000000, /* b