// aarch64.cc -- aarch64 target support for gold.
-// Copyright (C) 2014 Free Software Foundation, Inc.
+// Copyright (C) 2014-2018 Free Software Foundation, Inc.
// Written by Jing Yu <jingyu@google.com> and Han Shen <shenhan@google.com>.
// This file is part of gold.
#include "gold.h"
#include <cstring>
+#include <map>
+#include <set>
#include "elfcpp.h"
#include "dwarf.h"
// The first three .got.plt entries are reserved.
const int32_t AARCH64_GOTPLT_RESERVE_COUNT = 3;
+
namespace
{
template<int size, bool big_endian>
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<bool big_endian>
+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_adr(const Insntype insn)
+ { return (insn & 0x9F000000) == 0x10000000; }
+
+ static bool
+ is_adrp(const Insntype insn)
+ { return (insn & 0x9F000000) == 0x90000000; }
+
+ static bool
+ is_mrs_tpidr_el0(const Insntype insn)
+ { return (insn & 0xFFFFFFE0) == 0xd53bd040; }
+
+ 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); }
+
+ // Encode imm21 into adr. Signed imm21 is in the range of [-1M, 1M).
+ static Insntype
+ aarch64_adr_encode_imm(Insntype adr, int imm21)
+ {
+ gold_assert(is_adr(adr));
+ gold_assert(-(1 << 20) <= imm21 && imm21 < (1 << 20));
+ const int mask19 = (1 << 19) - 1;
+ const int mask2 = 3;
+ adr &= ~((mask19 << 5) | (mask2 << 29));
+ adr |= ((imm21 & mask2) << 29) | (((imm21 >> 2) & mask19) << 5);
+ return adr;
+ }
+
+ // Retrieve encoded adrp 33-bit signed imm value. This value is obtained by
+ // 21-bit signed imm encoded in the insn multiplied by 4k (page size) and
+ // 64-bit sign-extended, resulting in [-4G, 4G) with 12-lsb being 0.
+ static int64_t
+ aarch64_adrp_decode_imm(const Insntype adrp)
+ {
+ const int mask19 = (1 << 19) - 1;
+ const int mask2 = 3;
+ gold_assert(is_adrp(adrp));
+ // 21-bit imm encoded in adrp.
+ uint64_t imm = ((adrp >> 29) & mask2) | (((adrp >> 5) & mask19) << 2);
+ // Retrieve msb of 21-bit-signed imm for sign extension.
+ uint64_t msbt = (imm >> 20) & 1;
+ // Real value is imm multiplied by 4k. Value now has 33-bit information.
+ int64_t value = imm << 12;
+ // Sign extend to 64-bit by repeating msbt 31 (64-33) times and merge it
+ // with value.
+ return ((((uint64_t)(1) << 32) - msbt) << 33) | value;
+ }
+
+ 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 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<bool big_endian>
+const int AArch64_insn_utilities<big_endian>::BYTES_PER_INSN = 4;
+
+
+// Zero register encoding - 31.
+
+template<bool big_endian>
+const unsigned int AArch64_insn_utilities<big_endian>::AARCH64_ZR = 0x1f;
+
+
// Output_data_got_aarch64 class.
template<int size, bool big_endian>
gold_assert(tls_segment != NULL);
AArch64_address aligned_tcb_address =
- align_address(Target_aarch64<size,big_endian>::TCB_SIZE,
+ align_address(Target_aarch64<size, big_endian>::TCB_SIZE,
tls_segment->maximum_alignment());
for (size_t i = 0; i < this->static_relocs_.size(); ++i)
got_offset() const
{ return this->got_offset_; }
- // Relocation type.
- unsigned int
- r_type() const
- { return this->r_type_; }
+ // 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<size, big_endian>*
+ 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<size, big_endian>* relobj;
+ // For a local symbol, the symbol index.
+ unsigned int index;
+ } local;
+ } u_;
+ }; // End of inner class Static_reloc
+
+ std::vector<Static_reloc> static_relocs_;
+}; // End of Output_data_got_aarch64
+
+
+template<int size, bool big_endian>
+class AArch64_input_section;
+
+
+template<int size, bool big_endian>
+class AArch64_output_section;
+
+
+template<int size, bool big_endian>
+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<bool big_endian>
+struct Stub_template
+{
+ const typename AArch64_insn_utilities<big_endian>::Insntype* insns;
+ const int insn_num;
+};
+
+
+// Simple singleton class that creates/initializes/stores all types of stub
+// templates.
+
+template<bool big_endian>
+class Stub_template_repertoire
+{
+public:
+ typedef typename AArch64_insn_utilities<big_endian>::Insntype Insntype;
+
+ // Single static method to get stub template for a given stub type.
+ static const Stub_template<big_endian>*
+ get_stub_template(int type)
+ {
+ static Stub_template_repertoire<big_endian> 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<big_endian>* stub_templates_[ST_NUMBER];
+}; // End of "class Stub_template_repertoire".
+
+
+// Constructor - creates/initilizes all stub templates.
+
+template<bool big_endian>
+Stub_template_repertoire<big_endian>::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 <label> */
+ };
+
+ // ST_E_835769 has the same stub template as ST_E_843419
+ // but we reproduce the array here so that the sizeof
+ // expressions in install_insn_template will work.
+ const static Insntype ST_E_835769_INSNS[] =
+ {
+ 0x00000000, /* Placeholder for erratum insn. */
+ 0x14000000, /* b <label> */
+ };
+
+#define install_insn_template(T) \
+ const static Stub_template<big_endian> template_##T = { \
+ T##_INSNS, sizeof(T##_INSNS) / sizeof(T##_INSNS[0]) }; \
+ this->stub_templates_[T] = &template_##T
+
+ install_insn_template(ST_NONE);
+ install_insn_template(ST_ADRP_BRANCH);
+ install_insn_template(ST_LONG_BRANCH_ABS);
+ install_insn_template(ST_LONG_BRANCH_PCREL);
+ install_insn_template(ST_E_843419);
+ install_insn_template(ST_E_835769);
+
+#undef install_insn_template
+}
+
+
+// Base class for stubs.
+
+template<int size, bool big_endian>
+class Stub_base
+{
+public:
+ typedef typename elfcpp::Elf_types<size>::Elf_Addr AArch64_address;
+ typedef typename AArch64_insn_utilities<big_endian>::Insntype Insntype;
+
+ static const AArch64_address invalid_address =
+ static_cast<AArch64_address>(-1);
+
+ static const section_offset_type invalid_offset =
+ static_cast<section_offset_type>(-1);
+
+ Stub_base(int type)
+ : destination_address_(invalid_address),
+ offset_(invalid_offset),
+ type_(type)
+ {}
+
+ ~Stub_base()
+ {}
+
+ // Get stub type.
+ int
+ type() const
+ { return this->type_; }
+
+ // Get stub template that provides stub insn information.
+ const Stub_template<big_endian>*
+ stub_template() const
+ {
+ return Stub_template_repertoire<big_endian>::
+ get_stub_template(this->type());
+ }
+
+ // Get destination address.
+ AArch64_address
+ destination_address() const
+ {
+ gold_assert(this->destination_address_ != this->invalid_address);
+ return this->destination_address_;
+ }
+
+ // Set destination address.
+ void
+ set_destination_address(AArch64_address address)
+ {
+ gold_assert(address != this->invalid_address);
+ this->destination_address_ = address;
+ }
+
+ // Reset the destination address.
+ void
+ reset_destination_address()
+ { this->destination_address_ = this->invalid_address; }
+
+ // Get offset of code stub. For Reloc_stub, it is the offset from the
+ // beginning of its containing stub table; for Erratum_stub, it is the offset
+ // from the end of reloc_stubs.
+ section_offset_type
+ offset() const
+ {
+ gold_assert(this->offset_ != this->invalid_offset);
+ return this->offset_;
+ }
+
+ // Set stub offset.
+ void
+ set_offset(section_offset_type offset)
+ { this->offset_ = offset; }
+
+ // Return the stub insn.
+ const Insntype*
+ insns() const
+ { return this->stub_template()->insns; }
+
+ // Return num of stub insns.
+ unsigned int
+ insn_num() const
+ { return this->stub_template()->insn_num; }
+
+ // Get size of the stub.
+ int
+ stub_size() const
+ {
+ return this->insn_num() *
+ AArch64_insn_utilities<big_endian>::BYTES_PER_INSN;
+ }
+
+ // Write stub to output file.
+ void
+ write(unsigned char* view, section_size_type view_size)
+ { this->do_write(view, view_size); }
+
+protected:
+ // Abstract method to be implemented by sub-classes.
+ virtual void
+ do_write(unsigned char*, section_size_type) = 0;
+
+private:
+ // The last insn of a stub is a jump to destination insn. This field records
+ // the destination address.
+ AArch64_address destination_address_;
+ // The stub offset. Note this has difference interpretations between an
+ // Reloc_stub and an Erratum_stub. For Reloc_stub this is the offset from the
+ // beginning of the containing stub_table, whereas for Erratum_stub, this is
+ // the offset from the end of reloc_stubs.
+ section_offset_type offset_;
+ // Stub type.
+ const int type_;
+}; // End of "Stub_base".
+
+
+// Erratum stub class. An erratum stub differs from a reloc stub in that for
+// each erratum occurrence, we generate an erratum stub. We never share erratum
+// stubs, whereas for reloc stubs, different branch insns share a single reloc
+// stub as long as the branch targets are the same. (More to the point, reloc
+// stubs can be shared because they're used to reach a specific target, whereas
+// erratum stubs branch back to the original control flow.)
+
+template<int size, bool big_endian>
+class Erratum_stub : public Stub_base<size, big_endian>
+{
+public:
+ typedef AArch64_relobj<size, big_endian> The_aarch64_relobj;
+ typedef typename elfcpp::Elf_types<size>::Elf_Addr AArch64_address;
+ typedef AArch64_insn_utilities<big_endian> Insn_utilities;
+ typedef typename AArch64_insn_utilities<big_endian>::Insntype Insntype;
+
+ static const int STUB_ADDR_ALIGN;
+
+ static const Insntype invalid_insn = static_cast<Insntype>(-1);
+
+ Erratum_stub(The_aarch64_relobj* relobj, int type,
+ unsigned shndx, unsigned int sh_offset)
+ : Stub_base<size, big_endian>(type), relobj_(relobj),
+ shndx_(shndx), sh_offset_(sh_offset),
+ erratum_insn_(invalid_insn),
+ erratum_address_(this->invalid_address)
+ {}
+
+ ~Erratum_stub() {}
+
+ // Return the object that contains the erratum.
+ The_aarch64_relobj*
+ relobj()
+ { return this->relobj_; }
+
+ // Get section index of the erratum.
+ unsigned int
+ shndx() const
+ { return this->shndx_; }
+
+ // Get section offset of the erratum.
+ unsigned int
+ sh_offset() const
+ { return this->sh_offset_; }
+
+ // Get the erratum insn. This is the insn located at erratum_insn_address.
+ Insntype
+ erratum_insn() const
+ {
+ gold_assert(this->erratum_insn_ != this->invalid_insn);
+ return this->erratum_insn_;
+ }
+
+ // Set the insn that the erratum happens to.
+ void
+ set_erratum_insn(Insntype insn)
+ { this->erratum_insn_ = insn; }
+
+ // For 843419, the erratum insn is ld/st xt, [xn, #uimm], which may be a
+ // relocation spot, in this case, the erratum_insn_ recorded at scanning phase
+ // is no longer the one we want to write out to the stub, update erratum_insn_
+ // with relocated version. Also note that in this case xn must not be "PC", so
+ // it is safe to move the erratum insn from the origin place to the stub. For
+ // 835769, the erratum insn is multiply-accumulate insn, which could not be a
+ // relocation spot (assertion added though).
+ void
+ update_erratum_insn(Insntype insn)
+ {
+ gold_assert(this->erratum_insn_ != this->invalid_insn);
+ switch (this->type())
+ {
+ case ST_E_843419:
+ gold_assert(Insn_utilities::aarch64_ldst_uimm(insn));
+ gold_assert(Insn_utilities::aarch64_ldst_uimm(this->erratum_insn()));
+ gold_assert(Insn_utilities::aarch64_rd(insn) ==
+ Insn_utilities::aarch64_rd(this->erratum_insn()));
+ gold_assert(Insn_utilities::aarch64_rn(insn) ==
+ Insn_utilities::aarch64_rn(this->erratum_insn()));
+ // Update plain ld/st insn with relocated insn.
+ this->erratum_insn_ = insn;
+ break;
+ case ST_E_835769:
+ gold_assert(insn == this->erratum_insn());
+ break;
+ default:
+ gold_unreachable();
+ }
+ }
+
+
+ // Return the address where an erratum must be done.
+ AArch64_address
+ erratum_address() const
+ {
+ gold_assert(this->erratum_address_ != this->invalid_address);
+ return this->erratum_address_;
+ }
+
+ // Set the address where an erratum must be done.
+ void
+ set_erratum_address(AArch64_address addr)
+ { this->erratum_address_ = addr; }
+
+ // Later relaxation passes of may alter the recorded erratum and destination
+ // address. Given an up to date output section address of shidx_ in
+ // relobj_ we can derive the erratum_address and destination address.
+ void
+ update_erratum_address(AArch64_address output_section_addr)
+ {
+ const int BPI = AArch64_insn_utilities<big_endian>::BYTES_PER_INSN;
+ AArch64_address updated_addr = output_section_addr + this->sh_offset_;
+ this->set_erratum_address(updated_addr);
+ this->set_destination_address(updated_addr + BPI);
+ }
+
+ // Comparator used to group Erratum_stubs in a set by (obj, shndx,
+ // sh_offset). We do not include 'type' in the calculation, because there is
+ // at most one stub type at (obj, shndx, sh_offset).
+ bool
+ operator<(const Erratum_stub<size, big_endian>& k) const
+ {
+ if (this == &k)
+ return false;
+ // We group stubs by relobj.
+ if (this->relobj_ != k.relobj_)
+ return this->relobj_ < k.relobj_;
+ // Then by section index.
+ if (this->shndx_ != k.shndx_)
+ return this->shndx_ < k.shndx_;
+ // Lastly by section offset.
+ return this->sh_offset_ < k.sh_offset_;
+ }
+
+ void
+ invalidate_erratum_stub()
+ {
+ gold_assert(this->erratum_insn_ != invalid_insn);
+ this->erratum_insn_ = invalid_insn;
+ }
+
+ bool
+ is_invalidated_erratum_stub()
+ { return this->erratum_insn_ == invalid_insn; }
+
+protected:
+ virtual void
+ do_write(unsigned char*, section_size_type);
+
+private:
+ // The object that needs to be fixed.
+ The_aarch64_relobj* relobj_;
+ // The shndx in the object that needs to be fixed.
+ const unsigned int shndx_;
+ // The section offset in the obejct that needs to be fixed.
+ const unsigned int sh_offset_;
+ // The insn to be fixed.
+ Insntype erratum_insn_;
+ // The address of the above insn.
+ AArch64_address erratum_address_;
+}; // End of "Erratum_stub".
+
+
+// Erratum sub class to wrap additional info needed by 843419. In fixing this
+// erratum, we may choose to replace 'adrp' with 'adr', in this case, we need
+// adrp's code position (two or three insns before erratum insn itself).
+
+template<int size, bool big_endian>
+class E843419_stub : public Erratum_stub<size, big_endian>
+{
+public:
+ typedef typename AArch64_insn_utilities<big_endian>::Insntype Insntype;
+
+ E843419_stub(AArch64_relobj<size, big_endian>* relobj,
+ unsigned int shndx, unsigned int sh_offset,
+ unsigned int adrp_sh_offset)
+ : Erratum_stub<size, big_endian>(relobj, ST_E_843419, shndx, sh_offset),
+ adrp_sh_offset_(adrp_sh_offset)
+ {}
+
+ unsigned int
+ adrp_sh_offset() const
+ { return this->adrp_sh_offset_; }
+
+private:
+ // Section offset of "adrp". (We do not need a "adrp_shndx_" field, because we
+ // can obtain it from its parent.)
+ const unsigned int adrp_sh_offset_;
+};
+
+
+template<int size, bool big_endian>
+const int Erratum_stub<size, big_endian>::STUB_ADDR_ALIGN = 4;
+
+// Comparator used in set definition.
+template<int size, bool big_endian>
+struct Erratum_stub_less
+{
+ bool
+ operator()(const Erratum_stub<size, big_endian>* s1,
+ const Erratum_stub<size, big_endian>* s2) const
+ { return *s1 < *s2; }
+};
+
+// Erratum_stub implementation for writing stub to output file.
+
+template<int size, bool big_endian>
+void
+Erratum_stub<size, big_endian>::do_write(unsigned char* view, section_size_type)
+{
+ typedef typename elfcpp::Swap<32, big_endian>::Valtype Insntype;
+ const Insntype* insns = this->insns();
+ uint32_t num_insns = this->insn_num();
+ Insntype* ip = reinterpret_cast<Insntype*>(view);
+ // For current implemented erratum 843419 and 835769, the first insn in the
+ // stub is always a copy of the problematic insn (in 843419, the mem access
+ // insn, in 835769, the mac insn), followed by a jump-back.
+ elfcpp::Swap<32, big_endian>::writeval(ip, this->erratum_insn());
+ for (uint32_t i = 1; i < num_insns; ++i)
+ elfcpp::Swap<32, big_endian>::writeval(ip + i, insns[i]);
+}
+
+
+// Reloc stub class.
+
+template<int size, bool big_endian>
+class Reloc_stub : public Stub_base<size, big_endian>
+{
+ public:
+ typedef Reloc_stub<size, big_endian> This;
+ typedef typename elfcpp::Elf_types<size>::Elf_Addr AArch64_address;
+
+ // Branch range. This is used to calculate the section group size, as well as
+ // determine whether a stub is needed.
+ static const int MAX_BRANCH_OFFSET = ((1 << 25) - 1) << 2;
+ static const int MIN_BRANCH_OFFSET = -((1 << 25) << 2);
+
+ // Constant used to determine if an offset fits in the adrp instruction
+ // encoding.
+ static const int MAX_ADRP_IMM = (1 << 20) - 1;
+ static const int MIN_ADRP_IMM = -(1 << 20);
+
+ static const int BYTES_PER_INSN = 4;
+ static const int STUB_ADDR_ALIGN;
+
+ // Determine whether the offset fits in the jump/branch instruction.
+ static bool
+ aarch64_valid_branch_offset_p(int64_t offset)
+ { return offset >= MIN_BRANCH_OFFSET && offset <= MAX_BRANCH_OFFSET; }
+
+ // Determine whether the offset fits in the adrp immediate field.
+ static bool
+ aarch64_valid_for_adrp_p(AArch64_address location, AArch64_address dest)
+ {
+ typedef AArch64_relocate_functions<size, big_endian> Reloc;
+ int64_t adrp_imm = (Reloc::Page(dest) - Reloc::Page(location)) >> 12;
+ return adrp_imm >= MIN_ADRP_IMM && adrp_imm <= MAX_ADRP_IMM;
+ }
+
+ // Determine the stub type for a certain relocation or ST_NONE, if no stub is
+ // needed.
+ static int
+ stub_type_for_reloc(unsigned int r_type, AArch64_address address,
+ AArch64_address target);
+
+ Reloc_stub(int type)
+ : Stub_base<size, big_endian>(type)
+ { }
+
+ ~Reloc_stub()
+ { }
+
+ // The key class used to index the stub instance in the stub table's stub map.
+ class Key
+ {
+ public:
+ Key(int type, const Symbol* symbol, const Relobj* relobj,
+ unsigned int r_sym, int32_t addend)
+ : type_(type), addend_(addend)
+ {
+ if (symbol != NULL)
+ {
+ this->r_sym_ = Reloc_stub::invalid_index;
+ this->u_.symbol = symbol;
+ }
+ else
+ {
+ gold_assert(relobj != NULL && r_sym != invalid_index);
+ this->r_sym_ = r_sym;
+ this->u_.relobj = relobj;
+ }
+ }
+
+ ~Key()
+ { }
+
+ // Return stub type.
+ int
+ type() const
+ { return this->type_; }
+
+ // Return the local symbol index or invalid_index.
+ unsigned int
+ r_sym() const
+ { return this->r_sym_; }
+
+ // Return the symbol if there is one.
+ const Symbol*
+ symbol() const
+ { return this->r_sym_ == invalid_index ? this->u_.symbol : NULL; }
+
+ // Return the relobj if there is one.
+ const Relobj*
+ relobj() const
+ { return this->r_sym_ != invalid_index ? this->u_.relobj : NULL; }
+
+ // Whether this equals to another key k.
+ bool
+ eq(const Key& k) const
+ {
+ return ((this->type_ == k.type_)
+ && (this->r_sym_ == k.r_sym_)
+ && ((this->r_sym_ != Reloc_stub::invalid_index)
+ ? (this->u_.relobj == k.u_.relobj)
+ : (this->u_.symbol == k.u_.symbol))
+ && (this->addend_ == k.addend_));
+ }
+
+ // Return a hash value.
+ size_t
+ hash_value() const
+ {
+ size_t name_hash_value = gold::string_hash<char>(
+ (this->r_sym_ != Reloc_stub::invalid_index)
+ ? this->u_.relobj->name().c_str()
+ : this->u_.symbol->name());
+ // We only have 4 stub types.
+ size_t stub_type_hash_value = 0x03 & this->type_;
+ return (name_hash_value
+ ^ stub_type_hash_value
+ ^ ((this->r_sym_ & 0x3fff) << 2)
+ ^ ((this->addend_ & 0xffff) << 16));
+ }
+
+ // Functors for STL associative containers.
+ struct hash
+ {
+ size_t
+ operator()(const Key& k) const
+ { return k.hash_value(); }
+ };
+
+ struct equal_to
+ {
+ bool
+ operator()(const Key& k1, const Key& k2) const
+ { return k1.eq(k2); }
+ };
+
+ private:
+ // Stub type.
+ const int type_;
+ // If this is a local symbol, this is the index in the defining object.
+ // Otherwise, it is invalid_index for a global symbol.
+ unsigned int r_sym_;
+ // If r_sym_ is an invalid index, this points to a global symbol.
+ // Otherwise, it points to a relobj. We used the unsized and target
+ // independent Symbol and Relobj classes instead of Sized_symbol<32> and
+ // Arm_relobj, in order to avoid making the stub class a template
+ // as most of the stub machinery is endianness-neutral. However, it
+ // may require a bit of casting done by users of this class.
+ union
+ {
+ const Symbol* symbol;
+ const Relobj* relobj;
+ } u_;
+ // Addend associated with a reloc.
+ int32_t addend_;
+ }; // End of inner class Reloc_stub::Key
+
+ protected:
+ // This may be overridden in the child class.
+ virtual void
+ do_write(unsigned char*, section_size_type);
+
+ private:
+ static const unsigned int invalid_index = static_cast<unsigned int>(-1);
+}; // End of Reloc_stub
+
+template<int size, bool big_endian>
+const int Reloc_stub<size, big_endian>::STUB_ADDR_ALIGN = 4;
+
+// Write data to output file.
+
+template<int size, bool big_endian>
+void
+Reloc_stub<size, big_endian>::
+do_write(unsigned char* view, section_size_type)
+{
+ typedef typename elfcpp::Swap<32, big_endian>::Valtype Insntype;
+ const uint32_t* insns = this->insns();
+ uint32_t num_insns = this->insn_num();
+ Insntype* ip = reinterpret_cast<Insntype*>(view);
+ for (uint32_t i = 0; i < num_insns; ++i)
+ elfcpp::Swap<32, big_endian>::writeval(ip + i, insns[i]);
+}
+
+
+// Determine the stub type for a certain relocation or ST_NONE, if no stub is
+// needed.
+
+template<int size, bool big_endian>
+inline int
+Reloc_stub<size, big_endian>::stub_type_for_reloc(
+ unsigned int r_type, AArch64_address location, AArch64_address dest)
+{
+ int64_t branch_offset = 0;
+ switch(r_type)
+ {
+ case elfcpp::R_AARCH64_CALL26:
+ case elfcpp::R_AARCH64_JUMP26:
+ branch_offset = dest - location;
+ break;
+ default:
+ gold_unreachable();
+ }
+
+ if (aarch64_valid_branch_offset_p(branch_offset))
+ return ST_NONE;
+
+ if (aarch64_valid_for_adrp_p(location, dest))
+ return ST_ADRP_BRANCH;
+
+ // Always use PC-relative addressing in case of -shared or -pie.
+ if (parameters->options().output_is_position_independent())
+ return ST_LONG_BRANCH_PCREL;
+
+ // This saves 2 insns per stub, compared to ST_LONG_BRANCH_PCREL.
+ // But is only applicable to non-shared or non-pie.
+ return ST_LONG_BRANCH_ABS;
+}
+
+// A class to hold stubs for the ARM target. This contains 2 different types of
+// stubs - reloc stubs and erratum stubs.
+
+template<int size, bool big_endian>
+class Stub_table : public Output_data
+{
+ public:
+ typedef Target_aarch64<size, big_endian> The_target_aarch64;
+ typedef typename elfcpp::Elf_types<size>::Elf_Addr AArch64_address;
+ typedef AArch64_relobj<size, big_endian> The_aarch64_relobj;
+ typedef AArch64_input_section<size, big_endian> The_aarch64_input_section;
+ typedef Reloc_stub<size, big_endian> The_reloc_stub;
+ typedef typename The_reloc_stub::Key The_reloc_stub_key;
+ typedef Erratum_stub<size, big_endian> The_erratum_stub;
+ typedef Erratum_stub_less<size, big_endian> The_erratum_stub_less;
+ typedef typename The_reloc_stub_key::hash The_reloc_stub_key_hash;
+ typedef typename The_reloc_stub_key::equal_to The_reloc_stub_key_equal_to;
+ typedef Stub_table<size, big_endian> The_stub_table;
+ typedef Unordered_map<The_reloc_stub_key, The_reloc_stub*,
+ The_reloc_stub_key_hash, The_reloc_stub_key_equal_to>
+ Reloc_stub_map;
+ typedef typename Reloc_stub_map::const_iterator Reloc_stub_map_const_iter;
+ typedef Relocate_info<size, big_endian> The_relocate_info;
+
+ typedef std::set<The_erratum_stub*, The_erratum_stub_less> Erratum_stub_set;
+ typedef typename Erratum_stub_set::iterator Erratum_stub_set_iter;
+
+ Stub_table(The_aarch64_input_section* owner)
+ : Output_data(), owner_(owner), reloc_stubs_size_(0),
+ erratum_stubs_size_(0), prev_data_size_(0)
+ { }
+
+ ~Stub_table()
+ { }
+
+ The_aarch64_input_section*
+ owner() const
+ { return owner_; }
+
+ // Whether this stub table is empty.
+ bool
+ empty() const
+ { return reloc_stubs_.empty() && erratum_stubs_.empty(); }
+
+ // Return the current data size.
+ off_t
+ current_data_size() const
+ { return this->current_data_size_for_child(); }
+
+ // Add a STUB using KEY. The caller is responsible for avoiding addition
+ // if a STUB with the same key has already been added.
+ void
+ add_reloc_stub(The_reloc_stub* stub, const The_reloc_stub_key& key);
+
+ // Add an erratum stub into the erratum stub set. The set is ordered by
+ // (relobj, shndx, sh_offset).
+ void
+ add_erratum_stub(The_erratum_stub* stub);
+
+ // Find if such erratum exists for any given (obj, shndx, sh_offset).
+ The_erratum_stub*
+ find_erratum_stub(The_aarch64_relobj* a64relobj,
+ unsigned int shndx, unsigned int sh_offset);
+
+ // Find all the erratums for a given input section. The return value is a pair
+ // of iterators [begin, end).
+ std::pair<Erratum_stub_set_iter, Erratum_stub_set_iter>
+ find_erratum_stubs_for_input_section(The_aarch64_relobj* a64relobj,
+ unsigned int shndx);
+
+ // Compute the erratum stub address.
+ AArch64_address
+ erratum_stub_address(The_erratum_stub* stub) const
+ {
+ AArch64_address r = align_address(this->address() + this->reloc_stubs_size_,
+ The_erratum_stub::STUB_ADDR_ALIGN);
+ r += stub->offset();
+ return r;
+ }
+
+ // Finalize stubs. No-op here, just for completeness.
+ void
+ finalize_stubs()
+ { }
+
+ // Look up a relocation stub using KEY. Return NULL if there is none.
+ The_reloc_stub*
+ find_reloc_stub(The_reloc_stub_key& key)
+ {
+ Reloc_stub_map_const_iter p = this->reloc_stubs_.find(key);
+ return (p != this->reloc_stubs_.end()) ? p->second : NULL;
+ }
+
+ // Relocate reloc stubs in this stub table. This does not relocate erratum stubs.
+ void
+ relocate_reloc_stubs(const The_relocate_info*,
+ The_target_aarch64*,
+ Output_section*,
+ unsigned char*,
+ AArch64_address,
+ section_size_type);
+
+ // Relocate an erratum stub.
+ void
+ relocate_erratum_stub(The_erratum_stub*, unsigned char*);
+
+ // Update data size at the end of a relaxation pass. Return true if data size
+ // is different from that of the previous relaxation pass.
+ bool
+ update_data_size_changed_p()
+ {
+ // No addralign changed here.
+ off_t s = align_address(this->reloc_stubs_size_,
+ The_erratum_stub::STUB_ADDR_ALIGN)
+ + this->erratum_stubs_size_;
+ bool changed = (s != this->prev_data_size_);
+ this->prev_data_size_ = s;
+ return changed;
+ }
+
+ protected:
+ // Write out section contents.
+ void
+ do_write(Output_file*);
+
+ // Return the required alignment.
+ uint64_t
+ do_addralign() const
+ {
+ return std::max(The_reloc_stub::STUB_ADDR_ALIGN,
+ The_erratum_stub::STUB_ADDR_ALIGN);
+ }
+
+ // Reset address and file offset.
+ void
+ do_reset_address_and_file_offset()
+ { this->set_current_data_size_for_child(this->prev_data_size_); }
+
+ // Set final data size.
+ void
+ set_final_data_size()
+ { this->set_data_size(this->current_data_size()); }
+
+ private:
+ // Relocate one reloc stub.
+ void
+ relocate_reloc_stub(The_reloc_stub*,
+ const The_relocate_info*,
+ The_target_aarch64*,
+ Output_section*,
+ unsigned char*,
+ AArch64_address,
+ section_size_type);
+
+ private:
+ // Owner of this stub table.
+ The_aarch64_input_section* owner_;
+ // The relocation stubs.
+ Reloc_stub_map reloc_stubs_;
+ // The erratum stubs.
+ Erratum_stub_set erratum_stubs_;
+ // Size of reloc stubs.
+ off_t reloc_stubs_size_;
+ // Size of erratum stubs.
+ off_t erratum_stubs_size_;
+ // data size of this in the previous pass.
+ off_t prev_data_size_;
+}; // End of Stub_table
+
+
+// Add an erratum stub into the erratum stub set. The set is ordered by
+// (relobj, shndx, sh_offset).
+
+template<int size, bool big_endian>
+void
+Stub_table<size, big_endian>::add_erratum_stub(The_erratum_stub* stub)
+{
+ std::pair<Erratum_stub_set_iter, bool> ret =
+ this->erratum_stubs_.insert(stub);
+ gold_assert(ret.second);
+ this->erratum_stubs_size_ = align_address(
+ this->erratum_stubs_size_, The_erratum_stub::STUB_ADDR_ALIGN);
+ stub->set_offset(this->erratum_stubs_size_);
+ this->erratum_stubs_size_ += stub->stub_size();
+}
+
+
+// Find if such erratum exists for given (obj, shndx, sh_offset).
+
+template<int size, bool big_endian>
+Erratum_stub<size, big_endian>*
+Stub_table<size, big_endian>::find_erratum_stub(
+ The_aarch64_relobj* a64relobj, unsigned int shndx, unsigned int sh_offset)
+{
+ // A dummy object used as key to search in the set.
+ The_erratum_stub key(a64relobj, ST_NONE,
+ shndx, sh_offset);
+ Erratum_stub_set_iter i = this->erratum_stubs_.find(&key);
+ if (i != this->erratum_stubs_.end())
+ {
+ The_erratum_stub* stub(*i);
+ gold_assert(stub->erratum_insn() != 0);
+ return stub;
+ }
+ return NULL;
+}
+
+
+// Find all the errata for a given input section. The return value is a pair of
+// iterators [begin, end).
+
+template<int size, bool big_endian>
+std::pair<typename Stub_table<size, big_endian>::Erratum_stub_set_iter,
+ typename Stub_table<size, big_endian>::Erratum_stub_set_iter>
+Stub_table<size, big_endian>::find_erratum_stubs_for_input_section(
+ The_aarch64_relobj* a64relobj, unsigned int shndx)
+{
+ typedef std::pair<Erratum_stub_set_iter, Erratum_stub_set_iter> Result_pair;
+ Erratum_stub_set_iter start, end;
+ The_erratum_stub low_key(a64relobj, ST_NONE, shndx, 0);
+ start = this->erratum_stubs_.lower_bound(&low_key);
+ if (start == this->erratum_stubs_.end())
+ return Result_pair(this->erratum_stubs_.end(),
+ this->erratum_stubs_.end());
+ end = start;
+ while (end != this->erratum_stubs_.end() &&
+ (*end)->relobj() == a64relobj && (*end)->shndx() == shndx)
+ ++end;
+ return Result_pair(start, end);
+}
+
+
+// Add a STUB using KEY. The caller is responsible for avoiding addition
+// if a STUB with the same key has already been added.
+
+template<int size, bool big_endian>
+void
+Stub_table<size, big_endian>::add_reloc_stub(
+ The_reloc_stub* stub, const The_reloc_stub_key& key)
+{
+ gold_assert(stub->type() == key.type());
+ this->reloc_stubs_[key] = stub;
+
+ // Assign stub offset early. We can do this because we never remove
+ // reloc stubs and they are in the beginning of the stub table.
+ this->reloc_stubs_size_ = align_address(this->reloc_stubs_size_,
+ The_reloc_stub::STUB_ADDR_ALIGN);
+ stub->set_offset(this->reloc_stubs_size_);
+ this->reloc_stubs_size_ += stub->stub_size();
+}
+
+
+// Relocate an erratum stub.
+
+template<int size, bool big_endian>
+void
+Stub_table<size, big_endian>::
+relocate_erratum_stub(The_erratum_stub* estub,
+ unsigned char* view)
+{
+ // Just for convenience.
+ const int BPI = AArch64_insn_utilities<big_endian>::BYTES_PER_INSN;
+
+ gold_assert(!estub->is_invalidated_erratum_stub());
+ AArch64_address stub_address = this->erratum_stub_address(estub);
+ // The address of "b" in the stub that is to be "relocated".
+ AArch64_address stub_b_insn_address;
+ // Branch offset that is to be filled in "b" insn.
+ int b_offset = 0;
+ switch (estub->type())
+ {
+ case ST_E_843419:
+ case ST_E_835769:
+ // The 1st insn of the erratum could be a relocation spot,
+ // in this case we need to fix it with
+ // "(*i)->erratum_insn()".
+ elfcpp::Swap<32, big_endian>::writeval(
+ view + (stub_address - this->address()),
+ estub->erratum_insn());
+ // For the erratum, the 2nd insn is a b-insn to be patched
+ // (relocated).
+ stub_b_insn_address = stub_address + 1 * BPI;
+ b_offset = estub->destination_address() - stub_b_insn_address;
+ AArch64_relocate_functions<size, big_endian>::construct_b(
+ view + (stub_b_insn_address - this->address()),
+ ((unsigned int)(b_offset)) & 0xfffffff);
+ break;
+ default:
+ gold_unreachable();
+ break;
+ }
+ estub->invalidate_erratum_stub();
+}
+
+
+// Relocate only reloc stubs in this stub table. This does not relocate erratum
+// stubs.
+
+template<int size, bool big_endian>
+void
+Stub_table<size, big_endian>::
+relocate_reloc_stubs(const The_relocate_info* relinfo,
+ The_target_aarch64* target_aarch64,
+ Output_section* output_section,
+ unsigned char* view,
+ AArch64_address address,
+ section_size_type view_size)
+{
+ // "view_size" is the total size of the stub_table.
+ gold_assert(address == this->address() &&
+ view_size == static_cast<section_size_type>(this->data_size()));
+ for(Reloc_stub_map_const_iter p = this->reloc_stubs_.begin();
+ p != this->reloc_stubs_.end(); ++p)
+ relocate_reloc_stub(p->second, relinfo, target_aarch64, output_section,
+ view, address, view_size);
+}
+
+
+// Relocate one reloc stub. This is a helper for
+// Stub_table::relocate_reloc_stubs().
+
+template<int size, bool big_endian>
+void
+Stub_table<size, big_endian>::
+relocate_reloc_stub(The_reloc_stub* stub,
+ const The_relocate_info* relinfo,
+ The_target_aarch64* target_aarch64,
+ Output_section* output_section,
+ unsigned char* view,
+ AArch64_address address,
+ section_size_type view_size)
+{
+ // "offset" is the offset from the beginning of the stub_table.
+ section_size_type offset = stub->offset();
+ section_size_type stub_size = stub->stub_size();
+ // "view_size" is the total size of the stub_table.
+ gold_assert(offset + stub_size <= view_size);
+
+ target_aarch64->relocate_reloc_stub(stub, relinfo, output_section,
+ view + offset, address + offset, view_size);
+}
+
+
+// Write out the stubs to file.
+
+template<int size, bool big_endian>
+void
+Stub_table<size, big_endian>::do_write(Output_file* of)
+{
+ 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);
+
+ // Write relocation stubs.
+ for (typename Reloc_stub_map::const_iterator p = this->reloc_stubs_.begin();
+ p != this->reloc_stubs_.end(); ++p)
+ {
+ The_reloc_stub* stub = p->second;
+ AArch64_address address = this->address() + stub->offset();
+ gold_assert(address ==
+ align_address(address, The_reloc_stub::STUB_ADDR_ALIGN));
+ stub->write(oview + stub->offset(), stub->stub_size());
+ }
+
+ // Write erratum stubs.
+ unsigned int erratum_stub_start_offset =
+ align_address(this->reloc_stubs_size_, The_erratum_stub::STUB_ADDR_ALIGN);
+ for (typename Erratum_stub_set::iterator p = this->erratum_stubs_.begin();
+ p != this->erratum_stubs_.end(); ++p)
+ {
+ The_erratum_stub* stub(*p);
+ stub->write(oview + erratum_stub_start_offset + stub->offset(),
+ stub->stub_size());
+ }
+
+ of->write_output_view(this->offset(), oview_size, oview);
+}
+
+
+// AArch64_relobj class.
+
+template<int size, bool big_endian>
+class AArch64_relobj : public Sized_relobj_file<size, big_endian>
+{
+ public:
+ typedef AArch64_relobj<size, big_endian> This;
+ typedef Target_aarch64<size, big_endian> The_target_aarch64;
+ typedef AArch64_input_section<size, big_endian> The_aarch64_input_section;
+ typedef typename elfcpp::Elf_types<size>::Elf_Addr AArch64_address;
+ typedef Stub_table<size, big_endian> The_stub_table;
+ typedef Erratum_stub<size, big_endian> The_erratum_stub;
+ typedef typename The_stub_table::Erratum_stub_set_iter Erratum_stub_set_iter;
+ typedef std::vector<The_stub_table*> Stub_table_list;
+ static const AArch64_address invalid_address =
+ static_cast<AArch64_address>(-1);
+
+ AArch64_relobj(const std::string& name, Input_file* input_file, off_t offset,
+ const typename elfcpp::Ehdr<size, big_endian>& ehdr)
+ : Sized_relobj_file<size, big_endian>(name, input_file, offset, ehdr),
+ stub_tables_()
+ { }
+
+ ~AArch64_relobj()
+ { }
+
+ // Return the stub table of the SHNDX-th section if there is one.
+ The_stub_table*
+ stub_table(unsigned int shndx) const
+ {
+ gold_assert(shndx < this->stub_tables_.size());
+ return this->stub_tables_[shndx];
+ }
+
+ // Set STUB_TABLE to be the stub_table of the SHNDX-th section.
+ void
+ set_stub_table(unsigned int shndx, The_stub_table* stub_table)
+ {
+ gold_assert(shndx < this->stub_tables_.size());
+ this->stub_tables_[shndx] = stub_table;
+ }
+
+ // Entrance to errata scanning.
+ void
+ scan_errata(unsigned int shndx,
+ const elfcpp::Shdr<size, big_endian>&,
+ Output_section*, const Symbol_table*,
+ The_target_aarch64*);
+
+ // Scan all relocation sections for stub generation.
+ void
+ scan_sections_for_stubs(The_target_aarch64*, const Symbol_table*,
+ const Layout*);
+
+ // Whether a section is a scannable text section.
+ bool
+ text_section_is_scannable(const elfcpp::Shdr<size, big_endian>&, unsigned int,
+ const Output_section*, const Symbol_table*);
+
+ // Convert regular input section with index SHNDX to a relaxed section.
+ void
+ convert_input_section_to_relaxed_section(unsigned shndx)
+ {
+ // The stubs have relocations and we need to process them after writing
+ // out the stubs. So relocation now must follow section write.
+ this->set_section_offset(shndx, -1ULL);
+ this->set_relocs_must_follow_section_writes();
+ }
+
+ // Structure for mapping symbol position.
+ struct Mapping_symbol_position
+ {
+ Mapping_symbol_position(unsigned int shndx, AArch64_address offset):
+ shndx_(shndx), offset_(offset)
+ {}
+
+ // "<" comparator used in ordered_map container.
+ bool
+ operator<(const Mapping_symbol_position& p) const
+ {
+ return (this->shndx_ < p.shndx_
+ || (this->shndx_ == p.shndx_ && this->offset_ < p.offset_));
+ }
+
+ // Section index.
+ unsigned int shndx_;
+
+ // Section offset.
+ AArch64_address offset_;
+ };
+
+ typedef std::map<Mapping_symbol_position, char> Mapping_symbol_info;
+
+ protected:
+ // Post constructor setup.
+ void
+ do_setup()
+ {
+ // Call parent's setup method.
+ Sized_relobj_file<size, big_endian>::do_setup();
+
+ // Initialize look-up tables.
+ this->stub_tables_.resize(this->shnum());
+ }
+
+ virtual void
+ do_relocate_sections(
+ const Symbol_table* symtab, const Layout* layout,
+ const unsigned char* pshdrs, Output_file* of,
+ typename Sized_relobj_file<size, big_endian>::Views* pviews);
+
+ // Count local symbols and (optionally) record mapping info.
+ virtual void
+ do_count_local_symbols(Stringpool_template<char>*,
+ Stringpool_template<char>*);
+
+ private:
+ // Fix all errata in the object, and for each erratum, relocate corresponding
+ // erratum stub.
+ void
+ fix_errata_and_relocate_erratum_stubs(
+ typename Sized_relobj_file<size, big_endian>::Views* pviews);
+
+ // Try to fix erratum 843419 in an optimized way. Return true if patch is
+ // applied.
+ bool
+ try_fix_erratum_843419_optimized(
+ The_erratum_stub*, AArch64_address,
+ typename Sized_relobj_file<size, big_endian>::View_size&);
+
+ // Whether a section needs to be scanned for relocation stubs.
+ bool
+ section_needs_reloc_stub_scanning(const elfcpp::Shdr<size, big_endian>&,
+ const Relobj::Output_sections&,
+ const Symbol_table*, const unsigned char*);
+
+ // List of stub tables.
+ Stub_table_list stub_tables_;
+
+ // Mapping symbol information sorted by (section index, section_offset).
+ Mapping_symbol_info mapping_symbol_info_;
+}; // End of AArch64_relobj
+
+
+// Override to record mapping symbol information.
+template<int size, bool big_endian>
+void
+AArch64_relobj<size, big_endian>::do_count_local_symbols(
+ Stringpool_template<char>* pool, Stringpool_template<char>* dynpool)
+{
+ Sized_relobj_file<size, big_endian>::do_count_local_symbols(pool, dynpool);
+
+ // Only erratum-fixing work needs mapping symbols, so skip this time consuming
+ // processing if not fixing erratum.
+ if (!parameters->options().fix_cortex_a53_843419()
+ && !parameters->options().fix_cortex_a53_835769())
+ return;
+
+ const unsigned int loccount = this->local_symbol_count();
+ if (loccount == 0)
+ return;
+
+ // Read the symbol table section header.
+ const unsigned int symtab_shndx = this->symtab_shndx();
+ elfcpp::Shdr<size, big_endian>
+ symtabshdr(this, this->elf_file()->section_header(symtab_shndx));
+ gold_assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB);
+
+ // Read the local symbols.
+ const int sym_size =elfcpp::Elf_sizes<size>::sym_size;
+ gold_assert(loccount == symtabshdr.get_sh_info());
+ off_t locsize = loccount * sym_size;
+ const unsigned char* psyms = this->get_view(symtabshdr.get_sh_offset(),
+ locsize, true, true);
+
+ // For mapping symbol processing, we need to read the symbol names.
+ unsigned int strtab_shndx = this->adjust_shndx(symtabshdr.get_sh_link());
+ if (strtab_shndx >= this->shnum())
+ {
+ this->error(_("invalid symbol table name index: %u"), strtab_shndx);
+ return;
+ }
+
+ elfcpp::Shdr<size, big_endian>
+ strtabshdr(this, this->elf_file()->section_header(strtab_shndx));
+ if (strtabshdr.get_sh_type() != elfcpp::SHT_STRTAB)
+ {
+ this->error(_("symbol table name section has wrong type: %u"),
+ static_cast<unsigned int>(strtabshdr.get_sh_type()));
+ return;
+ }
+
+ const char* pnames =
+ reinterpret_cast<const char*>(this->get_view(strtabshdr.get_sh_offset(),
+ strtabshdr.get_sh_size(),
+ false, false));
+
+ // Skip the first dummy symbol.
+ psyms += sym_size;
+ typename Sized_relobj_file<size, big_endian>::Local_values*
+ plocal_values = this->local_values();
+ for (unsigned int i = 1; i < loccount; ++i, psyms += sym_size)
+ {
+ elfcpp::Sym<size, big_endian> sym(psyms);
+ Symbol_value<size>& lv((*plocal_values)[i]);
+ AArch64_address input_value = lv.input_value();
+
+ // Check to see if this is a mapping symbol. AArch64 mapping symbols are
+ // defined in "ELF for the ARM 64-bit Architecture", Table 4-4, Mapping
+ // symbols.
+ // Mapping symbols could be one of the following 4 forms -
+ // a) $x
+ // b) $x.<any...>
+ // c) $d
+ // d) $d.<any...>
+ const char* sym_name = pnames + sym.get_st_name();
+ if (sym_name[0] == '$' && (sym_name[1] == 'x' || sym_name[1] == 'd')
+ && (sym_name[2] == '\0' || sym_name[2] == '.'))
+ {
+ bool is_ordinary;
+ unsigned int input_shndx =
+ this->adjust_sym_shndx(i, sym.get_st_shndx(), &is_ordinary);
+ gold_assert(is_ordinary);
+
+ Mapping_symbol_position msp(input_shndx, input_value);
+ // Insert mapping_symbol_info into map whose ordering is defined by
+ // (shndx, offset_within_section).
+ this->mapping_symbol_info_[msp] = sym_name[1];
+ }
+ }
+}
+
+
+// Fix all errata in the object and for each erratum, we relocate the
+// corresponding erratum stub (by calling Stub_table::relocate_erratum_stub).
+
+template<int size, bool big_endian>
+void
+AArch64_relobj<size, big_endian>::fix_errata_and_relocate_erratum_stubs(
+ typename Sized_relobj_file<size, big_endian>::Views* pviews)
+{
+ typedef typename elfcpp::Swap<32,big_endian>::Valtype Insntype;
+ unsigned int shnum = this->shnum();
+ const Relobj::Output_sections& out_sections(this->output_sections());
+ for (unsigned int i = 1; i < shnum; ++i)
+ {
+ The_stub_table* stub_table = this->stub_table(i);
+ if (!stub_table)
+ continue;
+ std::pair<Erratum_stub_set_iter, Erratum_stub_set_iter>
+ ipair(stub_table->find_erratum_stubs_for_input_section(this, i));
+ Erratum_stub_set_iter p = ipair.first, end = ipair.second;
+ typename Sized_relobj_file<size, big_endian>::View_size&
+ pview((*pviews)[i]);
+ AArch64_address view_offset = 0;
+ if (pview.is_input_output_view)
+ {
+ // In this case, write_sections has not added the output offset to
+ // the view's address, so we must do so. Currently this only happens
+ // for a relaxed section.
+ unsigned int index = this->adjust_shndx(i);
+ const Output_relaxed_input_section* poris =
+ out_sections[index]->find_relaxed_input_section(this, index);
+ gold_assert(poris != NULL);
+ view_offset = poris->address() - pview.address;
+ }
+
+ while (p != end)
+ {
+ The_erratum_stub* stub = *p;
+
+ // Double check data before fix.
+ gold_assert(pview.address + view_offset + stub->sh_offset()
+ == stub->erratum_address());
+
+ // Update previously recorded erratum insn with relocated
+ // version.
+ Insntype* ip =
+ reinterpret_cast<Insntype*>(
+ pview.view + view_offset + stub->sh_offset());
+ Insntype insn_to_fix = ip[0];
+ stub->update_erratum_insn(insn_to_fix);
+
+ // First try to see if erratum is 843419 and if it can be fixed
+ // without using branch-to-stub.
+ if (!try_fix_erratum_843419_optimized(stub, view_offset, pview))
+ {
+ // Replace the erratum insn with a branch-to-stub.
+ AArch64_address stub_address =
+ stub_table->erratum_stub_address(stub);
+ unsigned int b_offset = stub_address - stub->erratum_address();
+ AArch64_relocate_functions<size, big_endian>::construct_b(
+ pview.view + view_offset + stub->sh_offset(),
+ b_offset & 0xfffffff);
+ }
+
+ // Erratum fix is done (or skipped), continue to relocate erratum
+ // stub. Note, when erratum fix is skipped (either because we
+ // proactively change the code sequence or the code sequence is
+ // changed by relaxation, etc), we can still safely relocate the
+ // erratum stub, ignoring the fact the erratum could never be
+ // executed.
+ stub_table->relocate_erratum_stub(
+ stub,
+ pview.view + (stub_table->address() - pview.address));
+
+ // Next erratum stub.
+ ++p;
+ }
+ }
+}
+
+
+// This is an optimization for 843419. This erratum requires the sequence begin
+// with 'adrp', when final value calculated by adrp fits in adr, we can just
+// replace 'adrp' with 'adr', so we save 2 jumps per occurrence. (Note, however,
+// in this case, we do not delete the erratum stub (too late to do so), it is
+// merely generated without ever being called.)
+
+template<int size, bool big_endian>
+bool
+AArch64_relobj<size, big_endian>::try_fix_erratum_843419_optimized(
+ The_erratum_stub* stub, AArch64_address view_offset,
+ typename Sized_relobj_file<size, big_endian>::View_size& pview)
+{
+ if (stub->type() != ST_E_843419)
+ return false;
+
+ typedef AArch64_insn_utilities<big_endian> Insn_utilities;
+ typedef typename elfcpp::Swap<32,big_endian>::Valtype Insntype;
+ E843419_stub<size, big_endian>* e843419_stub =
+ reinterpret_cast<E843419_stub<size, big_endian>*>(stub);
+ AArch64_address pc =
+ pview.address + view_offset + e843419_stub->adrp_sh_offset();
+ unsigned int adrp_offset = e843419_stub->adrp_sh_offset ();
+ Insntype* adrp_view =
+ reinterpret_cast<Insntype*>(pview.view + view_offset + adrp_offset);
+ Insntype adrp_insn = adrp_view[0];
+
+ // If the instruction at adrp_sh_offset is "mrs R, tpidr_el0", it may come
+ // from IE -> LE relaxation etc. This is a side-effect of TLS relaxation that
+ // ADRP has been turned into MRS, there is no erratum risk anymore.
+ // Therefore, we return true to avoid doing unnecessary branch-to-stub.
+ if (Insn_utilities::is_mrs_tpidr_el0(adrp_insn))
+ return true;
+
+ // If the instruction at adrp_sh_offset is not ADRP and the instruction before
+ // it is "mrs R, tpidr_el0", it may come from LD -> LE relaxation etc.
+ // Like the above case, there is no erratum risk any more, we can safely
+ // return true.
+ if (!Insn_utilities::is_adrp(adrp_insn) && adrp_offset)
+ {
+ Insntype* prev_view =
+ reinterpret_cast<Insntype*>(
+ pview.view + view_offset + adrp_offset - 4);
+ Insntype prev_insn = prev_view[0];
+
+ if (Insn_utilities::is_mrs_tpidr_el0(prev_insn))
+ return true;
+ }
+
+ /* If we reach here, the first instruction must be ADRP. */
+ gold_assert(Insn_utilities::is_adrp(adrp_insn));
+ // Get adrp 33-bit signed imm value.
+ int64_t adrp_imm = Insn_utilities::
+ aarch64_adrp_decode_imm(adrp_insn);
+ // adrp - final value transferred to target register is calculated as:
+ // PC[11:0] = Zeros(12)
+ // adrp_dest_value = PC + adrp_imm;
+ int64_t adrp_dest_value = (pc & ~((1 << 12) - 1)) + adrp_imm;
+ // adr -final value transferred to target register is calucalted as:
+ // PC + adr_imm
+ // So we have:
+ // PC + adr_imm = adrp_dest_value
+ // ==>
+ // adr_imm = adrp_dest_value - PC
+ int64_t adr_imm = adrp_dest_value - pc;
+ // Check if imm fits in adr (21-bit signed).
+ if (-(1 << 20) <= adr_imm && adr_imm < (1 << 20))
+ {
+ // Convert 'adrp' into 'adr'.
+ Insntype adr_insn = adrp_insn & ((1u << 31) - 1);
+ adr_insn = Insn_utilities::
+ aarch64_adr_encode_imm(adr_insn, adr_imm);
+ elfcpp::Swap<32, big_endian>::writeval(adrp_view, adr_insn);
+ return true;
+ }
+ return false;
+}
+
+
+// Relocate sections.
+
+template<int size, bool big_endian>
+void
+AArch64_relobj<size, big_endian>::do_relocate_sections(
+ const Symbol_table* symtab, const Layout* layout,
+ const unsigned char* pshdrs, Output_file* of,
+ typename Sized_relobj_file<size, big_endian>::Views* 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())
+ return;
+
+ // This part only relocates erratum stubs that belong to input sections of this
+ // object file.
+ if (parameters->options().fix_cortex_a53_843419()
+ || parameters->options().fix_cortex_a53_835769())
+ this->fix_errata_and_relocate_erratum_stubs(pviews);
+
+ Relocate_info<size, big_endian> relinfo;
+ relinfo.symtab = symtab;
+ relinfo.layout = layout;
+ relinfo.object = this;
+
+ // This part relocates all reloc stubs that are contained in stub_tables of
+ // this object file.
+ unsigned int shnum = this->shnum();
+ The_target_aarch64* target = The_target_aarch64::current_target();
+
+ for (unsigned int i = 1; i < shnum; ++i)
+ {
+ The_aarch64_input_section* aarch64_input_section =
+ target->find_aarch64_input_section(this, i);
+ if (aarch64_input_section != NULL
+ && aarch64_input_section->is_stub_table_owner()
+ && !aarch64_input_section->stub_table()->empty())
+ {
+ Output_section* os = this->output_section(i);
+ gold_assert(os != NULL);
+
+ relinfo.reloc_shndx = elfcpp::SHN_UNDEF;
+ relinfo.reloc_shdr = NULL;
+ relinfo.data_shndx = i;
+ relinfo.data_shdr = pshdrs + i * elfcpp::Elf_sizes<size>::shdr_size;
+
+ typename Sized_relobj_file<size, big_endian>::View_size&
+ view_struct = (*pviews)[i];
+ gold_assert(view_struct.view != NULL);
+
+ The_stub_table* stub_table = aarch64_input_section->stub_table();
+ off_t offset = stub_table->address() - view_struct.address;
+ unsigned char* view = view_struct.view + offset;
+ AArch64_address address = stub_table->address();
+ section_size_type view_size = stub_table->data_size();
+ stub_table->relocate_reloc_stubs(&relinfo, target, os, view, address,
+ view_size);
+ }
+ }
+}
+
+
+// Determine if an input section is scannable for stub processing. SHDR is
+// the header of the section and SHNDX is the section index. OS is the output
+// section for the input section and SYMTAB is the global symbol table used to
+// look up ICF information.
+
+template<int size, bool big_endian>
+bool
+AArch64_relobj<size, big_endian>::text_section_is_scannable(
+ const elfcpp::Shdr<size, big_endian>& text_shdr,
+ unsigned int text_shndx,
+ const Output_section* os,
+ const Symbol_table* symtab)
+{
+ // Skip any empty sections, unallocated sections or sections whose
+ // type are not SHT_PROGBITS.
+ if (text_shdr.get_sh_size() == 0
+ || (text_shdr.get_sh_flags() & elfcpp::SHF_ALLOC) == 0
+ || text_shdr.get_sh_type() != elfcpp::SHT_PROGBITS)
+ return false;
+
+ // Skip any discarded or ICF'ed sections.
+ if (os == NULL || symtab->is_section_folded(this, text_shndx))
+ return false;
+
+ // Skip exception frame.
+ if (strcmp(os->name(), ".eh_frame") == 0)
+ return false ;
+
+ gold_assert(!this->is_output_section_offset_invalid(text_shndx) ||
+ os->find_relaxed_input_section(this, text_shndx) != NULL);
+
+ return true;
+}
+
+
+// Determine if we want to scan the SHNDX-th section for relocation stubs.
+// This is a helper for AArch64_relobj::scan_sections_for_stubs().
+
+template<int size, bool big_endian>
+bool
+AArch64_relobj<size, big_endian>::section_needs_reloc_stub_scanning(
+ const elfcpp::Shdr<size, big_endian>& shdr,
+ const Relobj::Output_sections& out_sections,
+ const Symbol_table* symtab,
+ const unsigned char* pshdrs)
+{
+ unsigned int sh_type = shdr.get_sh_type();
+ if (sh_type != elfcpp::SHT_RELA)
+ return false;
+
+ // Ignore empty section.
+ off_t sh_size = shdr.get_sh_size();
+ if (sh_size == 0)
+ return false;
+
+ // Ignore reloc section with unexpected symbol table. The
+ // error will be reported in the final link.
+ if (this->adjust_shndx(shdr.get_sh_link()) != this->symtab_shndx())
+ return false;
+
+ gold_assert(sh_type == elfcpp::SHT_RELA);
+ unsigned int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
+
+ // Ignore reloc section with unexpected entsize or uneven size.
+ // The error will be reported in the final link.
+ if (reloc_size != shdr.get_sh_entsize() || sh_size % reloc_size != 0)
+ return false;
+
+ // Ignore reloc section with bad info. This error will be
+ // reported in the final link.
+ unsigned int text_shndx = this->adjust_shndx(shdr.get_sh_info());
+ if (text_shndx >= this->shnum())
+ return false;
+
+ const unsigned int shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
+ const elfcpp::Shdr<size, big_endian> text_shdr(pshdrs +
+ text_shndx * shdr_size);
+ return this->text_section_is_scannable(text_shdr, text_shndx,
+ out_sections[text_shndx], symtab);
+}
+
+
+// Scan section SHNDX for erratum 843419 and 835769.
+
+template<int size, bool big_endian>
+void
+AArch64_relobj<size, big_endian>::scan_errata(
+ unsigned int shndx, const elfcpp::Shdr<size, big_endian>& shdr,
+ Output_section* os, const Symbol_table* symtab,
+ The_target_aarch64* target)
+{
+ if (shdr.get_sh_size() == 0
+ || (shdr.get_sh_flags() &
+ (elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR)) == 0
+ || shdr.get_sh_type() != elfcpp::SHT_PROGBITS)
+ return;
+
+ if (!os || symtab->is_section_folded(this, shndx)) return;
+
+ AArch64_address output_offset = this->get_output_section_offset(shndx);
+ AArch64_address output_address;
+ if (output_offset != invalid_address)
+ output_address = os->address() + output_offset;
+ else
+ {
+ const Output_relaxed_input_section* poris =
+ os->find_relaxed_input_section(this, shndx);
+ if (!poris) return;
+ output_address = poris->address();
+ }
+
+ // Update the addresses in previously generated erratum stubs. Unlike when
+ // we scan relocations for stubs, if section addresses have changed due to
+ // other relaxations we are unlikely to scan the same erratum instances
+ // again.
+ The_stub_table* stub_table = this->stub_table(shndx);
+ if (stub_table)
+ {
+ std::pair<Erratum_stub_set_iter, Erratum_stub_set_iter>
+ ipair(stub_table->find_erratum_stubs_for_input_section(this, shndx));
+ for (Erratum_stub_set_iter p = ipair.first; p != ipair.second; ++p)
+ (*p)->update_erratum_address(output_address);
+ }
+
+ section_size_type input_view_size = 0;
+ const unsigned char* input_view =
+ this->section_contents(shndx, &input_view_size, false);
+
+ Mapping_symbol_position section_start(shndx, 0);
+ // Find the first mapping symbol record within section shndx.
+ typename Mapping_symbol_info::const_iterator p =
+ this->mapping_symbol_info_.lower_bound(section_start);
+ while (p != this->mapping_symbol_info_.end() &&
+ p->first.shndx_ == shndx)
+ {
+ typename Mapping_symbol_info::const_iterator prev = p;
+ ++p;
+ if (prev->second == 'x')
+ {
+ section_size_type span_start =
+ convert_to_section_size_type(prev->first.offset_);
+ section_size_type span_end;
+ if (p != this->mapping_symbol_info_.end()
+ && p->first.shndx_ == shndx)
+ span_end = convert_to_section_size_type(p->first.offset_);
+ else
+ span_end = convert_to_section_size_type(shdr.get_sh_size());
+
+ // Here we do not share the scanning code of both errata. For 843419,
+ // only the last few insns of each page are examined, which is fast,
+ // whereas, for 835769, every insn pair needs to be checked.
+
+ if (parameters->options().fix_cortex_a53_843419())
+ target->scan_erratum_843419_span(
+ this, shndx, span_start, span_end,
+ const_cast<unsigned char*>(input_view), output_address);
+
+ if (parameters->options().fix_cortex_a53_835769())
+ target->scan_erratum_835769_span(
+ this, shndx, span_start, span_end,
+ const_cast<unsigned char*>(input_view), output_address);
+ }
+ }
+}
+
+
+// Scan relocations for stub generation.
+
+template<int size, bool big_endian>
+void
+AArch64_relobj<size, big_endian>::scan_sections_for_stubs(
+ The_target_aarch64* target,
+ const Symbol_table* symtab,
+ const Layout* layout)
+{
+ unsigned int shnum = this->shnum();
+ const unsigned int shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
+
+ // Read the section headers.
+ const unsigned char* pshdrs = this->get_view(this->elf_file()->shoff(),
+ shnum * shdr_size,
+ true, true);
+
+ // To speed up processing, we set up hash tables for fast lookup of
+ // input offsets to output addresses.
+ this->initialize_input_to_output_maps();
+
+ const Relobj::Output_sections& out_sections(this->output_sections());
+
+ Relocate_info<size, big_endian> relinfo;
+ relinfo.symtab = symtab;
+ relinfo.layout = layout;
+ relinfo.object = this;
+
+ // Do relocation stubs scanning.
+ const unsigned char* p = pshdrs + shdr_size;
+ for (unsigned int i = 1; i < shnum; ++i, p += shdr_size)
+ {
+ const elfcpp::Shdr<size, big_endian> shdr(p);
+ if (parameters->options().fix_cortex_a53_843419()
+ || parameters->options().fix_cortex_a53_835769())
+ scan_errata(i, shdr, out_sections[i], symtab, target);
+ if (this->section_needs_reloc_stub_scanning(shdr, out_sections, symtab,
+ pshdrs))
+ {
+ unsigned int index = this->adjust_shndx(shdr.get_sh_info());
+ AArch64_address output_offset =
+ this->get_output_section_offset(index);
+ AArch64_address output_address;
+ if (output_offset != invalid_address)
+ {
+ output_address = out_sections[index]->address() + output_offset;
+ }
+ else
+ {
+ // Currently this only happens for a relaxed section.
+ const Output_relaxed_input_section* poris =
+ out_sections[index]->find_relaxed_input_section(this, index);
+ gold_assert(poris != NULL);
+ output_address = poris->address();
+ }
+
+ // Get the relocations.
+ const unsigned char* prelocs = this->get_view(shdr.get_sh_offset(),
+ shdr.get_sh_size(),
+ true, false);
+
+ // Get the section contents.
+ section_size_type input_view_size = 0;
+ const unsigned char* input_view =
+ this->section_contents(index, &input_view_size, false);
+
+ relinfo.reloc_shndx = i;
+ relinfo.data_shndx = index;
+ unsigned int sh_type = shdr.get_sh_type();
+ unsigned int reloc_size;
+ gold_assert (sh_type == elfcpp::SHT_RELA);
+ reloc_size = elfcpp::Elf_sizes<size>::rela_size;
+
+ Output_section* os = out_sections[index];
+ target->scan_section_for_stubs(&relinfo, sh_type, prelocs,
+ shdr.get_sh_size() / reloc_size,
+ os,
+ output_offset == invalid_address,
+ input_view, output_address,
+ input_view_size);
+ }
+ }
+}
+
+
+// A class to wrap an ordinary input section containing executable code.
+
+template<int size, bool big_endian>
+class AArch64_input_section : public Output_relaxed_input_section
+{
+ public:
+ typedef Stub_table<size, big_endian> The_stub_table;
+
+ AArch64_input_section(Relobj* relobj, unsigned int shndx)
+ : Output_relaxed_input_section(relobj, shndx, 1),
+ stub_table_(NULL),
+ original_contents_(NULL), original_size_(0),
+ original_addralign_(1)
+ { }
+
+ ~AArch64_input_section()
+ { delete[] this->original_contents_; }
+
+ // Initialize.
+ void
+ init();
+
+ // Set the stub_table.
+ void
+ set_stub_table(The_stub_table* st)
+ { this->stub_table_ = st; }
+
+ // Whether this is a stub table owner.
+ bool
+ is_stub_table_owner() const
+ { return this->stub_table_ != NULL && this->stub_table_->owner() == this; }
+
+ // Return the original size of the section.
+ uint32_t
+ original_size() const
+ { return this->original_size_; }
+
+ // Return the stub table.
+ The_stub_table*
+ stub_table()
+ { return stub_table_; }
+
+ protected:
+ // Write out this input section.
+ void
+ do_write(Output_file*);
+
+ // Return required alignment of this.
+ uint64_t
+ do_addralign() const
+ {
+ if (this->is_stub_table_owner())
+ return std::max(this->stub_table_->addralign(),
+ static_cast<uint64_t>(this->original_addralign_));
+ else
+ return this->original_addralign_;
+ }
+
+ // Finalize data size.
+ void
+ set_final_data_size();
+
+ // Reset address and file offset.
+ void
+ do_reset_address_and_file_offset();
+
+ // Output offset.
+ bool
+ do_output_offset(const Relobj* object, unsigned int shndx,
+ section_offset_type offset,
+ section_offset_type* poutput) const
+ {
+ if ((object == this->relobj())
+ && (shndx == this->shndx())
+ && (offset >= 0)
+ && (offset <=
+ convert_types<section_offset_type, uint32_t>(this->original_size_)))
+ {
+ *poutput = offset;
+ return true;
+ }
+ else
+ return false;
+ }
+
+ private:
+ // Copying is not allowed.
+ AArch64_input_section(const AArch64_input_section&);
+ AArch64_input_section& operator=(const AArch64_input_section&);
+
+ // The relocation stubs.
+ The_stub_table* stub_table_;
+ // Original section contents. We have to make a copy here since the file
+ // containing the original section may not be locked when we need to access
+ // the contents.
+ unsigned char* original_contents_;
+ // Section size of the original input section.
+ uint32_t original_size_;
+ // Address alignment of the original input section.
+ uint32_t original_addralign_;
+}; // End of AArch64_input_section
+
+
+// Finalize data size.
+
+template<int size, bool big_endian>
+void
+AArch64_input_section<size, big_endian>::set_final_data_size()
+{
+ off_t off = convert_types<off_t, uint64_t>(this->original_size_);
+
+ if (this->is_stub_table_owner())
+ {
+ this->stub_table_->finalize_data_size();
+ off = align_address(off, this->stub_table_->addralign());
+ off += this->stub_table_->data_size();
+ }
+ this->set_data_size(off);
+}
+
+
+// Reset address and file offset.
+
+template<int size, bool big_endian>
+void
+AArch64_input_section<size, big_endian>::do_reset_address_and_file_offset()
+{
+ // Size of the original input section contents.
+ off_t off = convert_types<off_t, uint64_t>(this->original_size_);
+
+ // If this is a stub table owner, account for the stub table size.
+ if (this->is_stub_table_owner())
+ {
+ The_stub_table* stub_table = this->stub_table_;
+
+ // Reset the stub table's address and file offset. The
+ // current data size for child will be updated after that.
+ stub_table_->reset_address_and_file_offset();
+ off = align_address(off, stub_table_->addralign());
+ off += stub_table->current_data_size();
+ }
+
+ this->set_current_data_size(off);
+}
+
+
+// Initialize an Arm_input_section.
+
+template<int size, bool big_endian>
+void
+AArch64_input_section<size, big_endian>::init()
+{
+ Relobj* relobj = this->relobj();
+ unsigned int shndx = this->shndx();
+
+ // We have to cache original size, alignment and contents to avoid locking
+ // the original file.
+ this->original_addralign_ =
+ convert_types<uint32_t, uint64_t>(relobj->section_addralign(shndx));
+
+ // This is not efficient but we expect only a small number of relaxed
+ // input sections for stubs.
+ section_size_type section_size;
+ const unsigned char* section_contents =
+ relobj->section_contents(shndx, §ion_size, false);
+ this->original_size_ =
+ convert_types<uint32_t, uint64_t>(relobj->section_size(shndx));
+
+ gold_assert(this->original_contents_ == NULL);
+ this->original_contents_ = new unsigned char[section_size];
+ memcpy(this->original_contents_, section_contents, section_size);
+
+ // We want to make this look like the original input section after
+ // output sections are finalized.
+ Output_section* os = relobj->output_section(shndx);
+ off_t offset = relobj->output_section_offset(shndx);
+ gold_assert(os != NULL && !relobj->is_output_section_offset_invalid(shndx));
+ this->set_address(os->address() + offset);
+ this->set_file_offset(os->offset() + offset);
+ this->set_current_data_size(this->original_size_);
+ this->finalize_data_size();
+}
+
+
+// Write data to output file.
+
+template<int size, bool big_endian>
+void
+AArch64_input_section<size, big_endian>::do_write(Output_file* of)
+{
+ // We have to write out the original section content.
+ gold_assert(this->original_contents_ != NULL);
+ of->write(this->offset(), this->original_contents_,
+ this->original_size_);
+
+ // If this owns a stub table and it is not empty, write it.
+ if (this->is_stub_table_owner() && !this->stub_table_->empty())
+ this->stub_table_->write(of);
+}
+
+
+// Arm output section class. This is defined mainly to add a number of stub
+// generation methods.
+
+template<int size, bool big_endian>
+class AArch64_output_section : public Output_section
+{
+ public:
+ typedef Target_aarch64<size, big_endian> The_target_aarch64;
+ typedef AArch64_relobj<size, big_endian> The_aarch64_relobj;
+ typedef Stub_table<size, big_endian> The_stub_table;
+ typedef AArch64_input_section<size, big_endian> The_aarch64_input_section;
+
+ public:
+ AArch64_output_section(const char* name, elfcpp::Elf_Word type,
+ elfcpp::Elf_Xword flags)
+ : Output_section(name, type, flags)
+ { }
+
+ ~AArch64_output_section() {}
+
+ // Group input sections for stub generation.
+ void
+ group_sections(section_size_type, bool, Target_aarch64<size, big_endian>*,
+ const Task*);
+
+ private:
+ typedef Output_section::Input_section Input_section;
+ typedef Output_section::Input_section_list Input_section_list;
+
+ // Create a stub group.
+ void
+ create_stub_group(Input_section_list::const_iterator,
+ Input_section_list::const_iterator,
+ Input_section_list::const_iterator,
+ The_target_aarch64*,
+ std::vector<Output_relaxed_input_section*>&,
+ const Task*);
+}; // End of AArch64_output_section
+
+
+// Create a stub group for input sections from FIRST to LAST. OWNER points to
+// the input section that will be the owner of the stub table.
+
+template<int size, bool big_endian> void
+AArch64_output_section<size, big_endian>::create_stub_group(
+ Input_section_list::const_iterator first,
+ Input_section_list::const_iterator last,
+ Input_section_list::const_iterator owner,
+ The_target_aarch64* target,
+ std::vector<Output_relaxed_input_section*>& new_relaxed_sections,
+ const Task* task)
+{
+ // Currently we convert ordinary input sections into relaxed sections only
+ // at this point.
+ The_aarch64_input_section* input_section;
+ if (owner->is_relaxed_input_section())
+ gold_unreachable();
+ else
+ {
+ gold_assert(owner->is_input_section());
+ // Create a new relaxed input section. We need to lock the original
+ // file.
+ Task_lock_obj<Object> tl(task, owner->relobj());
+ input_section =
+ target->new_aarch64_input_section(owner->relobj(), owner->shndx());
+ new_relaxed_sections.push_back(input_section);
+ }
+
+ // Create a stub table.
+ The_stub_table* stub_table =
+ target->new_stub_table(input_section);
- // Whether the symbol is global or not.
- bool
- symbol_is_global() const
- { return this->symbol_is_global_; }
+ input_section->set_stub_table(stub_table);
- // For a relocation against a global symbol, the global symbol.
- Symbol*
- symbol() const
+ Input_section_list::const_iterator p = first;
+ // Look for input sections or relaxed input sections in [first ... last].
+ do
{
- gold_assert(this->symbol_is_global_);
- return this->u_.global.symbol;
+ if (p->is_input_section() || p->is_relaxed_input_section())
+ {
+ // The stub table information for input sections live
+ // in their objects.
+ The_aarch64_relobj* aarch64_relobj =
+ static_cast<The_aarch64_relobj*>(p->relobj());
+ aarch64_relobj->set_stub_table(p->shndx(), stub_table);
+ }
}
+ while (p++ != last);
+}
- // For a relocation against a local symbol, the defining object.
- Sized_relobj_file<size, big_endian>*
- relobj() const
+
+// Group input sections for stub generation. GROUP_SIZE is roughly the limit of
+// stub groups. We grow a stub group by adding input section until the size is
+// just below GROUP_SIZE. The last input section will be converted into a stub
+// table owner. If STUB_ALWAYS_AFTER_BRANCH is false, we also add input sectiond
+// after the stub table, effectively doubling the group size.
+//
+// This is similar to the group_sections() function in elf32-arm.c but is
+// implemented differently.
+
+template<int size, bool big_endian>
+void AArch64_output_section<size, big_endian>::group_sections(
+ section_size_type group_size,
+ bool stubs_always_after_branch,
+ Target_aarch64<size, big_endian>* target,
+ const Task* task)
+{
+ typedef enum
+ {
+ NO_GROUP,
+ FINDING_STUB_SECTION,
+ HAS_STUB_SECTION
+ } State;
+
+ std::vector<Output_relaxed_input_section*> new_relaxed_sections;
+
+ State state = NO_GROUP;
+ section_size_type off = 0;
+ section_size_type group_begin_offset = 0;
+ section_size_type group_end_offset = 0;
+ section_size_type stub_table_end_offset = 0;
+ Input_section_list::const_iterator group_begin =
+ this->input_sections().end();
+ Input_section_list::const_iterator stub_table =
+ this->input_sections().end();
+ Input_section_list::const_iterator group_end = this->input_sections().end();
+ for (Input_section_list::const_iterator p = this->input_sections().begin();
+ p != this->input_sections().end();
+ ++p)
{
- gold_assert(!this->symbol_is_global_);
- return this->u_.local.relobj;
+ section_size_type section_begin_offset =
+ align_address(off, p->addralign());
+ section_size_type section_end_offset =
+ section_begin_offset + p->data_size();
+
+ // Check to see if we should group the previously seen sections.
+ switch (state)
+ {
+ case NO_GROUP:
+ break;
+
+ case FINDING_STUB_SECTION:
+ // Adding this section makes the group larger than GROUP_SIZE.
+ if (section_end_offset - group_begin_offset >= group_size)
+ {
+ if (stubs_always_after_branch)
+ {
+ gold_assert(group_end != this->input_sections().end());
+ this->create_stub_group(group_begin, group_end, group_end,
+ target, new_relaxed_sections,
+ task);
+ state = NO_GROUP;
+ }
+ else
+ {
+ // Input sections up to stub_group_size bytes after the stub
+ // table can be handled by it too.
+ state = HAS_STUB_SECTION;
+ stub_table = group_end;
+ stub_table_end_offset = group_end_offset;
+ }
+ }
+ break;
+
+ case HAS_STUB_SECTION:
+ // Adding this section makes the post stub-section group larger
+ // than GROUP_SIZE.
+ gold_unreachable();
+ // NOT SUPPORTED YET. For completeness only.
+ if (section_end_offset - stub_table_end_offset >= group_size)
+ {
+ gold_assert(group_end != this->input_sections().end());
+ this->create_stub_group(group_begin, group_end, stub_table,
+ target, new_relaxed_sections, task);
+ state = NO_GROUP;
+ }
+ break;
+
+ default:
+ gold_unreachable();
+ }
+
+ // If we see an input section and currently there is no group, start
+ // a new one. Skip any empty sections. We look at the data size
+ // instead of calling p->relobj()->section_size() to avoid locking.
+ if ((p->is_input_section() || p->is_relaxed_input_section())
+ && (p->data_size() != 0))
+ {
+ if (state == NO_GROUP)
+ {
+ state = FINDING_STUB_SECTION;
+ group_begin = p;
+ group_begin_offset = section_begin_offset;
+ }
+
+ // Keep track of the last input section seen.
+ group_end = p;
+ group_end_offset = section_end_offset;
+ }
+
+ off = section_end_offset;
}
- // For a relocation against a local symbol, the local symbol index.
- unsigned int
- index() const
+ // Create a stub group for any ungrouped sections.
+ if (state == FINDING_STUB_SECTION || state == HAS_STUB_SECTION)
{
- gold_assert(!this->symbol_is_global_);
- return this->u_.local.index;
+ gold_assert(group_end != this->input_sections().end());
+ this->create_stub_group(group_begin, group_end,
+ (state == FINDING_STUB_SECTION
+ ? group_end
+ : stub_table),
+ target, new_relaxed_sections, task);
}
- 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 object.
- Sized_relobj_file<size, big_endian>* relobj;
- // For a local symbol, the symbol index.
- unsigned int index;
- } local;
- } u_;
- }; // End of inner class Static_reloc
+ if (!new_relaxed_sections.empty())
+ this->convert_input_sections_to_relaxed_sections(new_relaxed_sections);
- std::vector<Static_reloc> static_relocs_;
-}; // End of Output_data_got_aarch64
+ // Update the section offsets
+ for (size_t i = 0; i < new_relaxed_sections.size(); ++i)
+ {
+ The_aarch64_relobj* relobj = static_cast<The_aarch64_relobj*>(
+ new_relaxed_sections[i]->relobj());
+ unsigned int shndx = new_relaxed_sections[i]->shndx();
+ // Tell AArch64_relobj that this input section is converted.
+ relobj->convert_input_section_to_relaxed_section(shndx);
+ }
+} // End of AArch64_output_section::group_sections
AArch64_reloc_property_table* aarch64_reloc_property_table = NULL;
class Target_aarch64 : public Sized_target<size, big_endian>
{
public:
- typedef Target_aarch64<size,big_endian> This;
+ typedef Target_aarch64<size, big_endian> This;
typedef Output_data_reloc<elfcpp::SHT_RELA, true, size, big_endian>
Reloc_section;
+ typedef Relocate_info<size, big_endian> The_relocate_info;
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
+ typedef AArch64_relobj<size, big_endian> The_aarch64_relobj;
+ typedef Reloc_stub<size, big_endian> The_reloc_stub;
+ typedef Erratum_stub<size, big_endian> The_erratum_stub;
+ typedef typename Reloc_stub<size, big_endian>::Key The_reloc_stub_key;
+ typedef Stub_table<size, big_endian> The_stub_table;
+ typedef std::vector<The_stub_table*> Stub_table_list;
+ typedef typename Stub_table_list::iterator Stub_table_iterator;
+ typedef AArch64_input_section<size, big_endian> The_aarch64_input_section;
+ typedef AArch64_output_section<size, big_endian> The_aarch64_output_section;
+ typedef Unordered_map<Section_id,
+ AArch64_input_section<size, big_endian>*,
+ Section_id_hash> AArch64_input_section_map;
+ typedef AArch64_insn_utilities<big_endian> Insn_utilities;
const static int TCB_SIZE = size / 8 * 2;
Target_aarch64(const Target::Target_info* info = &aarch64_info)
got_(NULL), plt_(NULL), got_plt_(NULL), got_irelative_(NULL),
got_tlsdesc_(NULL), global_offset_table_(NULL), rela_dyn_(NULL),
rela_irelative_(NULL), copy_relocs_(elfcpp::R_AARCH64_COPY),
- got_mod_index_offset_(-1U), tlsdesc_reloc_info_(),
- tls_base_symbol_defined_(false)
+ got_mod_index_offset_(-1U),
+ tlsdesc_reloc_info_(), tls_base_symbol_defined_(false),
+ stub_tables_(), stub_group_size_(0), aarch64_input_section_map_()
{ }
// Scan the relocations to determine unreferenced sections for
const unsigned char* plocal_symbols,
Relocatable_relocs*);
+ // Scan the relocs for --emit-relocs.
+ void
+ emit_relocs_scan(Symbol_table* symtab,
+ Layout* layout,
+ Sized_relobj_file<size, 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);
+
// Relocate a section during a relocatable link.
void
relocate_relocs(
size_t reloc_count,
Output_section* output_section,
typename elfcpp::Elf_types<size>::Elf_Off offset_in_output_section,
- const Relocatable_relocs*,
unsigned char* view,
typename elfcpp::Elf_types<size>::Elf_Addr view_address,
section_size_type view_size,
}
// Return the addend to use for a target specific relocation.
- typename elfcpp::Elf_types<size>::Elf_Addr
- do_reloc_addend(void* arg, unsigned int r_type,
- typename elfcpp::Elf_types<size>::Elf_Addr addend) const;
+ uint64_t
+ do_reloc_addend(void* arg, unsigned int r_type, uint64_t addend) const;
// Return the PLT section.
uint64_t
do_plt_address_for_local(const Relobj* relobj, unsigned int symndx) const
{ return this->plt_section()->address_for_local(relobj, symndx); }
+ // This function should be defined in targets that can use relocation
+ // types to determine (implemented in local_reloc_may_be_function_pointer
+ // and global_reloc_may_be_function_pointer)
+ // if a function's pointer is taken. ICF uses this in safe mode to only
+ // fold those functions whose pointer is defintely not taken.
+ bool
+ do_can_check_for_function_pointers() const
+ { return true; }
+
// Return the number of entries in the PLT.
unsigned int
plt_entry_count() const;
unsigned int
plt_entry_size() const;
+ // Create a stub table.
+ The_stub_table*
+ new_stub_table(The_aarch64_input_section*);
+
+ // Create an aarch64 input section.
+ The_aarch64_input_section*
+ new_aarch64_input_section(Relobj*, unsigned int);
+
+ // Find an aarch64 input section instance for a given OBJ and SHNDX.
+ The_aarch64_input_section*
+ find_aarch64_input_section(Relobj*, unsigned int) const;
+
+ // Return the thread control block size.
unsigned int
tcb_size() const { return This::TCB_SIZE; }
+ // Scan a section for stub generation.
+ void
+ scan_section_for_stubs(const Relocate_info<size, big_endian>*, unsigned int,
+ const unsigned char*, size_t, Output_section*,
+ bool, const unsigned char*,
+ Address,
+ section_size_type);
+
+ // Scan a relocation section for stub.
+ template<int sh_type>
+ void
+ scan_reloc_section_for_stubs(
+ const The_relocate_info* relinfo,
+ const unsigned char* prelocs,
+ size_t reloc_count,
+ Output_section* output_section,
+ bool needs_special_offset_handling,
+ const unsigned char* view,
+ Address view_address,
+ section_size_type);
+
+ // Relocate a single reloc stub.
+ void
+ relocate_reloc_stub(The_reloc_stub*, const Relocate_info<size, big_endian>*,
+ Output_section*, unsigned char*, Address,
+ section_size_type);
+
+ // Get the default AArch64 target.
+ static This*
+ current_target()
+ {
+ gold_assert(parameters->target().machine_code() == elfcpp::EM_AARCH64
+ && parameters->target().get_size() == size
+ && parameters->target().is_big_endian() == big_endian);
+ return static_cast<This*>(parameters->sized_target<size, big_endian>());
+ }
+
+
+ // Scan erratum 843419 for a part of a section.
+ void
+ scan_erratum_843419_span(
+ AArch64_relobj<size, big_endian>*,
+ unsigned int,
+ const section_size_type,
+ const section_size_type,
+ unsigned char*,
+ Address);
+
+ // Scan erratum 835769 for a part of a section.
+ void
+ scan_erratum_835769_span(
+ AArch64_relobj<size, big_endian>*,
+ unsigned int,
+ const section_size_type,
+ const section_size_type,
+ unsigned char*,
+ Address);
+
protected:
void
do_select_as_default_target()
layout, got, got_plt, got_irelative);
}
+
+ // do_make_elf_object to override the same function in the base class.
+ Object*
+ do_make_elf_object(const std::string&, Input_file*, off_t,
+ const elfcpp::Ehdr<size, big_endian>&);
+
Output_data_plt_aarch64<size, big_endian>*
make_data_plt(Layout* layout,
Output_data_got_aarch64<size, big_endian>* got,
return this->do_make_data_plt(layout, got, got_plt, got_irelative);
}
+ // We only need to generate stubs, and hence perform relaxation if we are
+ // not doing relocatable linking.
+ virtual bool
+ do_may_relax() const
+ { return !parameters->options().relocatable(); }
+
+ // Relaxation hook. This is where we do stub generation.
+ virtual bool
+ do_relax(int, const Input_objects*, Symbol_table*, Layout*, const Task*);
+
+ void
+ group_sections(Layout* layout,
+ section_size_type group_size,
+ bool stubs_always_after_branch,
+ const Task* task);
+
+ void
+ scan_reloc_for_stub(const The_relocate_info*, unsigned int,
+ const Sized_symbol<size>*, unsigned int,
+ const Symbol_value<size>*,
+ typename elfcpp::Elf_types<size>::Elf_Swxword,
+ Address Elf_Addr);
+
+ // Make an output section.
+ Output_section*
+ do_make_output_section(const char* name, elfcpp::Elf_Word type,
+ elfcpp::Elf_Xword flags)
+ { return new The_aarch64_output_section(name, type, flags); }
+
private:
// The class which scans relocations.
class Scan
void
check_non_pic(Relobj*, unsigned int r_type);
+ bool
+ reloc_needs_plt_for_ifunc(Sized_relobj_file<size, big_endian>*,
+ unsigned int r_type);
+
// Whether we have issued an error about a non-PIC compilation.
bool issued_non_pic_error_;
};
// Do a relocation. Return false if the caller should not issue
// any warnings about this relocation.
inline bool
- relocate(const Relocate_info<size, big_endian>*, Target_aarch64*,
- Output_section*,
- size_t relnum, const elfcpp::Rela<size, big_endian>&,
- unsigned int r_type, const Sized_symbol<size>*,
- const Symbol_value<size>*,
+ relocate(const Relocate_info<size, big_endian>*, unsigned int,
+ Target_aarch64*, Output_section*, size_t, const unsigned char*,
+ const Sized_symbol<size>*, const Symbol_value<size>*,
unsigned char*, typename elfcpp::Elf_types<size>::Elf_Addr,
section_size_type);
private:
- inline typename AArch64_relocate_functions<size,big_endian>::Status
- relocate_tls(const Relocate_info<size,big_endian>*,
+ inline typename AArch64_relocate_functions<size, big_endian>::Status
+ relocate_tls(const Relocate_info<size, big_endian>*,
Target_aarch64<size, big_endian>*,
size_t,
const elfcpp::Rela<size, big_endian>&,
unsigned char*,
typename elfcpp::Elf_types<size>::Elf_Addr);
- inline typename AArch64_relocate_functions<size,big_endian>::Status
+ inline typename AArch64_relocate_functions<size, big_endian>::Status
tls_gd_to_le(
- const Relocate_info<size,big_endian>*,
+ const Relocate_info<size, big_endian>*,
Target_aarch64<size, big_endian>*,
const elfcpp::Rela<size, big_endian>&,
unsigned int,
unsigned char*,
const Symbol_value<size>*);
- inline typename AArch64_relocate_functions<size,big_endian>::Status
+ inline typename AArch64_relocate_functions<size, big_endian>::Status
+ tls_ld_to_le(
+ const Relocate_info<size, big_endian>*,
+ Target_aarch64<size, big_endian>*,
+ const elfcpp::Rela<size, big_endian>&,
+ unsigned int,
+ unsigned char*,
+ const Symbol_value<size>*);
+
+ inline typename AArch64_relocate_functions<size, big_endian>::Status
tls_ie_to_le(
- const Relocate_info<size,big_endian>*,
+ const Relocate_info<size, big_endian>*,
Target_aarch64<size, big_endian>*,
const elfcpp::Rela<size, big_endian>&,
unsigned int,
unsigned char*,
const Symbol_value<size>*);
- inline typename AArch64_relocate_functions<size,big_endian>::Status
+ inline typename AArch64_relocate_functions<size, big_endian>::Status
tls_desc_gd_to_le(
- const Relocate_info<size,big_endian>*,
+ const Relocate_info<size, big_endian>*,
Target_aarch64<size, big_endian>*,
const elfcpp::Rela<size, big_endian>&,
unsigned int,
unsigned char*,
const Symbol_value<size>*);
- inline typename AArch64_relocate_functions<size,big_endian>::Status
+ inline typename AArch64_relocate_functions<size, big_endian>::Status
tls_desc_gd_to_ie(
- const Relocate_info<size,big_endian>*,
+ const Relocate_info<size, big_endian>*,
Target_aarch64<size, big_endian>*,
const elfcpp::Rela<size, big_endian>&,
unsigned int,
}; // End of class Relocate
- // A class which returns the size required for a relocation type,
- // used while scanning relocs during a relocatable link.
- class Relocatable_size_for_reloc
- {
- public:
- unsigned int
- get_size_for_reloc(unsigned int, Relobj*);
- };
-
// Adjust TLS relocation type based on the options and whether this
// is a local symbol.
static tls::Tls_optimization
return this->plt_;
}
+ // Helper method to create erratum stubs for ST_E_843419 and ST_E_835769. For
+ // ST_E_843419, we need an additional field for adrp offset.
+ void create_erratum_stub(
+ AArch64_relobj<size, big_endian>* relobj,
+ unsigned int shndx,
+ section_size_type erratum_insn_offset,
+ Address erratum_address,
+ typename Insn_utilities::Insntype erratum_insn,
+ int erratum_type,
+ unsigned int e843419_adrp_offset=0);
+
+ // Return whether this is a 3-insn erratum sequence.
+ bool is_erratum_843419_sequence(
+ typename elfcpp::Swap<32,big_endian>::Valtype insn1,
+ typename elfcpp::Swap<32,big_endian>::Valtype insn2,
+ typename elfcpp::Swap<32,big_endian>::Valtype insn3);
+
+ // Return whether this is a 835769 sequence.
+ // (Similarly implemented as in elfnn-aarch64.c.)
+ bool is_erratum_835769_sequence(
+ typename elfcpp::Swap<32,big_endian>::Valtype,
+ typename elfcpp::Swap<32,big_endian>::Valtype);
+
// Get the dynamic reloc section, creating it if necessary.
Reloc_section*
rela_dyn_section(Layout*);
unsigned int shndx, Output_section* output_section,
Symbol* sym, const elfcpp::Rela<size, big_endian>& reloc)
{
+ unsigned int r_type = elfcpp::elf_r_type<size>(reloc.get_r_info());
this->copy_relocs_.copy_reloc(symtab, layout,
symtab->get_sized_symbol<size>(sym),
object, shndx, output_section,
- reloc, this->rela_dyn_section(layout));
+ r_type, reloc.get_r_offset(),
+ reloc.get_r_addend(),
+ this->rela_dyn_section(layout));
}
// Information about this specific target which we pass to the
std::vector<Tlsdesc_info> tlsdesc_reloc_info_;
// True if the _TLS_MODULE_BASE_ symbol has been defined.
bool tls_base_symbol_defined_;
+ // List of stub_tables
+ Stub_table_list stub_tables_;
+ // Actual stub group size
+ section_size_type stub_group_size_;
+ AArch64_input_section_map aarch64_input_section_map_;
}; // End of Target_aarch64
false, // has_make_symbol
false, // has_resolve
false, // has_code_fill
- true, // is_default_stack_executable
- false, // can_icf_inline_merge_sections
+ false, // is_default_stack_executable
+ true, // can_icf_inline_merge_sections
'\0', // wrap_char
"/lib/ld.so.1", // program interpreter
0x400000, // default_text_segment_address
- 0x1000, // abi_pagesize (overridable by -z max-page-size)
+ 0x10000, // abi_pagesize (overridable by -z max-page-size)
0x1000, // common_pagesize (overridable by -z common-page-size)
false, // isolate_execinstr
0, // rosegment_gap
0, // large_common_section_flags
NULL, // attributes_section
NULL, // attributes_vendor
- "_start" // entry_symbol_name
+ "_start", // entry_symbol_name
+ 32, // hash_entry_size
+ elfcpp::SHT_PROGBITS, // unwind_section_type
};
template<>
false, // has_make_symbol
false, // has_resolve
false, // has_code_fill
- true, // is_default_stack_executable
+ false, // is_default_stack_executable
false, // can_icf_inline_merge_sections
'\0', // wrap_char
"/lib/ld.so.1", // program interpreter
0x400000, // default_text_segment_address
- 0x1000, // abi_pagesize (overridable by -z max-page-size)
+ 0x10000, // abi_pagesize (overridable by -z max-page-size)
0x1000, // common_pagesize (overridable by -z common-page-size)
false, // isolate_execinstr
0, // rosegment_gap
0, // large_common_section_flags
NULL, // attributes_section
NULL, // attributes_vendor
- "_start" // entry_symbol_name
+ "_start", // entry_symbol_name
+ 32, // hash_entry_size
+ elfcpp::SHT_PROGBITS, // unwind_section_type
};
template<>
false, // has_make_symbol
false, // has_resolve
false, // has_code_fill
- true, // is_default_stack_executable
- false, // can_icf_inline_merge_sections
+ false, // is_default_stack_executable
+ true, // can_icf_inline_merge_sections
'\0', // wrap_char
"/lib/ld.so.1", // program interpreter
0x400000, // default_text_segment_address
- 0x1000, // abi_pagesize (overridable by -z max-page-size)
+ 0x10000, // abi_pagesize (overridable by -z max-page-size)
0x1000, // common_pagesize (overridable by -z common-page-size)
false, // isolate_execinstr
0, // rosegment_gap
0, // large_common_section_flags
NULL, // attributes_section
NULL, // attributes_vendor
- "_start" // entry_symbol_name
+ "_start", // entry_symbol_name
+ 32, // hash_entry_size
+ elfcpp::SHT_PROGBITS, // unwind_section_type
};
template<>
false, // has_make_symbol
false, // has_resolve
false, // has_code_fill
- true, // is_default_stack_executable
+ false, // is_default_stack_executable
false, // can_icf_inline_merge_sections
'\0', // wrap_char
"/lib/ld.so.1", // program interpreter
0x400000, // default_text_segment_address
- 0x1000, // abi_pagesize (overridable by -z max-page-size)
+ 0x10000, // abi_pagesize (overridable by -z max-page-size)
0x1000, // common_pagesize (overridable by -z common-page-size)
false, // isolate_execinstr
0, // rosegment_gap
0, // large_common_section_flags
NULL, // attributes_section
NULL, // attributes_vendor
- "_start" // entry_symbol_name
+ "_start", // entry_symbol_name
+ 32, // hash_entry_size
+ elfcpp::SHT_PROGBITS, // unwind_section_type
};
// Get the GOT section, creating it if necessary.
// that they can refer to global variables initialized by those
// relocs.
-template<int size, bool big_endian>
-typename Target_aarch64<size, big_endian>::Reloc_section*
-Target_aarch64<size, big_endian>::rela_irelative_section(Layout* layout)
-{
- if (this->rela_irelative_ == NULL)
- {
- // Make sure we have already created the dynamic reloc section.
- this->rela_dyn_section(layout);
- this->rela_irelative_ = new Reloc_section(false);
- layout->add_output_section_data(".rela.dyn", elfcpp::SHT_RELA,
- elfcpp::SHF_ALLOC, this->rela_irelative_,
- ORDER_DYNAMIC_RELOCS, false);
- gold_assert(this->rela_dyn_->output_section()
- == this->rela_irelative_->output_section());
+template<int size, bool big_endian>
+typename Target_aarch64<size, big_endian>::Reloc_section*
+Target_aarch64<size, big_endian>::rela_irelative_section(Layout* layout)
+{
+ if (this->rela_irelative_ == NULL)
+ {
+ // Make sure we have already created the dynamic reloc section.
+ this->rela_dyn_section(layout);
+ this->rela_irelative_ = new Reloc_section(false);
+ layout->add_output_section_data(".rela.dyn", elfcpp::SHT_RELA,
+ elfcpp::SHF_ALLOC, this->rela_irelative_,
+ ORDER_DYNAMIC_RELOCS, false);
+ gold_assert(this->rela_dyn_->output_section()
+ == this->rela_irelative_->output_section());
+ }
+ return this->rela_irelative_;
+}
+
+
+// do_make_elf_object to override the same function in the base class. We need
+// to use a target-specific sub-class of Sized_relobj_file<size, big_endian> to
+// store backend specific information. Hence we need to have our own ELF object
+// creation.
+
+template<int size, bool big_endian>
+Object*
+Target_aarch64<size, big_endian>::do_make_elf_object(
+ const std::string& name,
+ Input_file* input_file,
+ off_t offset, const elfcpp::Ehdr<size, big_endian>& ehdr)
+{
+ int et = ehdr.get_e_type();
+ // ET_EXEC files are valid input for --just-symbols/-R,
+ // and we treat them as relocatable objects.
+ if (et == elfcpp::ET_EXEC && input_file->just_symbols())
+ return Sized_target<size, big_endian>::do_make_elf_object(
+ name, input_file, offset, ehdr);
+ else if (et == elfcpp::ET_REL)
+ {
+ AArch64_relobj<size, big_endian>* obj =
+ new AArch64_relobj<size, big_endian>(name, input_file, offset, ehdr);
+ obj->setup();
+ return obj;
+ }
+ else if (et == elfcpp::ET_DYN)
+ {
+ // Keep base implementation.
+ Sized_dynobj<size, big_endian>* obj =
+ new Sized_dynobj<size, big_endian>(name, input_file, offset, ehdr);
+ obj->setup();
+ return obj;
+ }
+ else
+ {
+ gold_error(_("%s: unsupported ELF file type %d"),
+ name.c_str(), et);
+ return NULL;
+ }
+}
+
+
+// Scan a relocation for stub generation.
+
+template<int size, bool big_endian>
+void
+Target_aarch64<size, big_endian>::scan_reloc_for_stub(
+ const Relocate_info<size, big_endian>* relinfo,
+ unsigned int r_type,
+ const Sized_symbol<size>* gsym,
+ unsigned int r_sym,
+ const Symbol_value<size>* psymval,
+ typename elfcpp::Elf_types<size>::Elf_Swxword addend,
+ Address address)
+{
+ const AArch64_relobj<size, big_endian>* aarch64_relobj =
+ static_cast<AArch64_relobj<size, big_endian>*>(relinfo->object);
+
+ Symbol_value<size> symval;
+ if (gsym != NULL)
+ {
+ const AArch64_reloc_property* arp = aarch64_reloc_property_table->
+ get_reloc_property(r_type);
+ if (gsym->use_plt_offset(arp->reference_flags()))
+ {
+ // This uses a PLT, change the symbol value.
+ symval.set_output_value(this->plt_address_for_global(gsym));
+ psymval = &symval;
+ }
+ else if (gsym->is_undefined())
+ {
+ // There is no need to generate a stub symbol if the original symbol
+ // is undefined.
+ gold_debug(DEBUG_TARGET,
+ "stub: not creating a stub for undefined symbol %s in file %s",
+ gsym->name(), aarch64_relobj->name().c_str());
+ return;
+ }
+ }
+
+ // Get the symbol value.
+ typename Symbol_value<size>::Value value = psymval->value(aarch64_relobj, 0);
+
+ // Owing to pipelining, the PC relative branches below actually skip
+ // two instructions when the branch offset is 0.
+ Address destination = static_cast<Address>(-1);
+ switch (r_type)
+ {
+ case elfcpp::R_AARCH64_CALL26:
+ case elfcpp::R_AARCH64_JUMP26:
+ destination = value + addend;
+ break;
+ default:
+ gold_unreachable();
+ }
+
+ int stub_type = The_reloc_stub::
+ stub_type_for_reloc(r_type, address, destination);
+ if (stub_type == ST_NONE)
+ return;
+
+ The_stub_table* stub_table = aarch64_relobj->stub_table(relinfo->data_shndx);
+ gold_assert(stub_table != NULL);
+
+ The_reloc_stub_key key(stub_type, gsym, aarch64_relobj, r_sym, addend);
+ The_reloc_stub* stub = stub_table->find_reloc_stub(key);
+ if (stub == NULL)
+ {
+ stub = new The_reloc_stub(stub_type);
+ stub_table->add_reloc_stub(stub, key);
+ }
+ stub->set_destination_address(destination);
+} // End of Target_aarch64::scan_reloc_for_stub
+
+
+// This function scans a relocation section for stub generation.
+// The template parameter Relocate must be a class type which provides
+// a single function, relocate(), which implements the machine
+// specific part of a relocation.
+
+// BIG_ENDIAN is the endianness of the data. SH_TYPE is the section type:
+// SHT_REL or SHT_RELA.
+
+// PRELOCS points to the relocation data. RELOC_COUNT is the number
+// of relocs. OUTPUT_SECTION is the output section.
+// NEEDS_SPECIAL_OFFSET_HANDLING is true if input offsets need to be
+// mapped to output offsets.
+
+// VIEW is the section data, VIEW_ADDRESS is its memory address, and
+// VIEW_SIZE is the size. These refer to the input section, unless
+// NEEDS_SPECIAL_OFFSET_HANDLING is true, in which case they refer to
+// the output section.
+
+template<int size, bool big_endian>
+template<int sh_type>
+void inline
+Target_aarch64<size, big_endian>::scan_reloc_section_for_stubs(
+ const Relocate_info<size, big_endian>* relinfo,
+ const unsigned char* prelocs,
+ size_t reloc_count,
+ Output_section* /*output_section*/,
+ bool /*needs_special_offset_handling*/,
+ const unsigned char* /*view*/,
+ Address view_address,
+ section_size_type)
+{
+ typedef typename Reloc_types<sh_type,size,big_endian>::Reloc Reltype;
+
+ const int reloc_size =
+ Reloc_types<sh_type,size,big_endian>::reloc_size;
+ AArch64_relobj<size, big_endian>* object =
+ static_cast<AArch64_relobj<size, big_endian>*>(relinfo->object);
+ unsigned int local_count = 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)
+ {
+ Reltype reloc(prelocs);
+ typename elfcpp::Elf_types<size>::Elf_WXword r_info = reloc.get_r_info();
+ unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info);
+ unsigned int r_type = elfcpp::elf_r_type<size>(r_info);
+ if (r_type != elfcpp::R_AARCH64_CALL26
+ && r_type != elfcpp::R_AARCH64_JUMP26)
+ continue;
+
+ section_offset_type offset =
+ convert_to_section_size_type(reloc.get_r_offset());
+
+ // Get the addend.
+ typename elfcpp::Elf_types<size>::Elf_Swxword addend =
+ reloc.get_r_addend();
+
+ const Sized_symbol<size>* sym;
+ Symbol_value<size> symval;
+ const Symbol_value<size> *psymval;
+ bool is_defined_in_discarded_section;
+ unsigned int shndx;
+ const Symbol* gsym = NULL;
+ if (r_sym < local_count)
+ {
+ sym = NULL;
+ psymval = object->local_symbol(r_sym);
+
+ // If the local symbol belongs to a section we are discarding,
+ // and that section is a debug section, try to find the
+ // corresponding kept section and map this symbol to its
+ // counterpart in the kept section. The symbol must not
+ // correspond to a section we are folding.
+ bool is_ordinary;
+ shndx = psymval->input_shndx(&is_ordinary);
+ is_defined_in_discarded_section =
+ (is_ordinary
+ && shndx != elfcpp::SHN_UNDEF
+ && !object->is_section_included(shndx)
+ && !relinfo->symtab->is_section_folded(object, shndx));
+
+ // We need to compute the would-be final value of this local
+ // symbol.
+ if (!is_defined_in_discarded_section)
+ {
+ typedef Sized_relobj_file<size, big_endian> ObjType;
+ if (psymval->is_section_symbol())
+ symval.set_is_section_symbol();
+ typename ObjType::Compute_final_local_value_status status =
+ object->compute_final_local_value(r_sym, psymval, &symval,
+ relinfo->symtab);
+ if (status == ObjType::CFLV_OK)
+ {
+ // Currently we cannot handle a branch to a target in
+ // a merged section. If this is the case, issue an error
+ // and also free the merge symbol value.
+ if (!symval.has_output_value())
+ {
+ const std::string& section_name =
+ object->section_name(shndx);
+ object->error(_("cannot handle branch to local %u "
+ "in a merged section %s"),
+ r_sym, section_name.c_str());
+ }
+ psymval = &symval;
+ }
+ else
+ {
+ // We cannot determine the final value.
+ continue;
+ }
+ }
+ }
+ else
+ {
+ gsym = object->global_symbol(r_sym);
+ gold_assert(gsym != NULL);
+ if (gsym->is_forwarder())
+ gsym = relinfo->symtab->resolve_forwards(gsym);
+
+ sym = static_cast<const Sized_symbol<size>*>(gsym);
+ if (sym->has_symtab_index() && sym->symtab_index() != -1U)
+ symval.set_output_symtab_index(sym->symtab_index());
+ else
+ symval.set_no_output_symtab_entry();
+
+ // We need to compute the would-be final value of this global
+ // symbol.
+ const Symbol_table* symtab = relinfo->symtab;
+ const Sized_symbol<size>* sized_symbol =
+ symtab->get_sized_symbol<size>(gsym);
+ Symbol_table::Compute_final_value_status status;
+ typename elfcpp::Elf_types<size>::Elf_Addr value =
+ symtab->compute_final_value<size>(sized_symbol, &status);
+
+ // Skip this if the symbol has not output section.
+ if (status == Symbol_table::CFVS_NO_OUTPUT_SECTION)
+ continue;
+ symval.set_output_value(value);
+
+ if (gsym->type() == elfcpp::STT_TLS)
+ symval.set_is_tls_symbol();
+ else if (gsym->type() == elfcpp::STT_GNU_IFUNC)
+ symval.set_is_ifunc_symbol();
+ psymval = &symval;
+
+ is_defined_in_discarded_section =
+ (gsym->is_defined_in_discarded_section()
+ && gsym->is_undefined());
+ shndx = 0;
+ }
+
+ Symbol_value<size> symval2;
+ if (is_defined_in_discarded_section)
+ {
+ std::string name = object->section_name(relinfo->data_shndx);
+
+ if (comdat_behavior == CB_UNDETERMINED)
+ comdat_behavior = default_comdat_behavior.get(name.c_str());
+
+ if (comdat_behavior == CB_PRETEND)
+ {
+ bool found;
+ typename elfcpp::Elf_types<size>::Elf_Addr value =
+ object->map_to_kept_section(shndx, name, &found);
+ if (found)
+ symval2.set_output_value(value + psymval->input_value());
+ else
+ symval2.set_output_value(0);
+ }
+ else
+ {
+ 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();
+ psymval = &symval2;
+ }
+
+ this->scan_reloc_for_stub(relinfo, r_type, sym, r_sym, psymval,
+ addend, view_address + offset);
+ } // End of iterating relocs in a section
+} // End of Target_aarch64::scan_reloc_section_for_stubs
+
+
+// Scan an input section for stub generation.
+
+template<int size, bool big_endian>
+void
+Target_aarch64<size, big_endian>::scan_section_for_stubs(
+ const Relocate_info<size, big_endian>* relinfo,
+ unsigned int sh_type,
+ const unsigned char* prelocs,
+ size_t reloc_count,
+ Output_section* output_section,
+ bool needs_special_offset_handling,
+ const unsigned char* view,
+ Address view_address,
+ section_size_type view_size)
+{
+ gold_assert(sh_type == elfcpp::SHT_RELA);
+ this->scan_reloc_section_for_stubs<elfcpp::SHT_RELA>(
+ relinfo,
+ prelocs,
+ reloc_count,
+ output_section,
+ needs_special_offset_handling,
+ view,
+ view_address,
+ view_size);
+}
+
+
+// Relocate a single reloc stub.
+
+template<int size, bool big_endian>
+void Target_aarch64<size, big_endian>::
+relocate_reloc_stub(The_reloc_stub* stub,
+ const The_relocate_info*,
+ Output_section*,
+ unsigned char* view,
+ Address address,
+ section_size_type)
+{
+ typedef AArch64_relocate_functions<size, big_endian> The_reloc_functions;
+ typedef typename The_reloc_functions::Status The_reloc_functions_status;
+ typedef typename elfcpp::Swap<32,big_endian>::Valtype Insntype;
+
+ Insntype* ip = reinterpret_cast<Insntype*>(view);
+ int insn_number = stub->insn_num();
+ const uint32_t* insns = stub->insns();
+ // Check the insns are really those stub insns.
+ for (int i = 0; i < insn_number; ++i)
+ {
+ Insntype insn = elfcpp::Swap<32,big_endian>::readval(ip + i);
+ gold_assert(((uint32_t)insn == insns[i]));
+ }
+
+ Address dest = stub->destination_address();
+
+ switch(stub->type())
+ {
+ case ST_ADRP_BRANCH:
+ {
+ // 1st reloc is ADR_PREL_PG_HI21
+ The_reloc_functions_status status =
+ The_reloc_functions::adrp(view, dest, address);
+ // An error should never arise in the above step. If so, please
+ // check 'aarch64_valid_for_adrp_p'.
+ gold_assert(status == The_reloc_functions::STATUS_OKAY);
+
+ // 2nd reloc is ADD_ABS_LO12_NC
+ const AArch64_reloc_property* arp =
+ aarch64_reloc_property_table->get_reloc_property(
+ elfcpp::R_AARCH64_ADD_ABS_LO12_NC);
+ gold_assert(arp != NULL);
+ status = The_reloc_functions::template
+ rela_general<32>(view + 4, dest, 0, arp);
+ // An error should never arise, it is an "_NC" relocation.
+ gold_assert(status == The_reloc_functions::STATUS_OKAY);
+ }
+ break;
+
+ case ST_LONG_BRANCH_ABS:
+ // 1st reloc is R_AARCH64_PREL64, at offset 8
+ elfcpp::Swap<64,big_endian>::writeval(view + 8, dest);
+ break;
+
+ case ST_LONG_BRANCH_PCREL:
+ {
+ // "PC" calculation is the 2nd insn in the stub.
+ uint64_t offset = dest - (address + 4);
+ // Offset is placed at offset 4 and 5.
+ elfcpp::Swap<64,big_endian>::writeval(view + 16, offset);
+ }
+ break;
+
+ default:
+ gold_unreachable();
}
- return this->rela_irelative_;
}
Output_data_got_aarch64<size, big_endian>* got,
Output_data_space* got_plt,
Output_data_space* got_irelative)
- : Output_section_data(addralign), tlsdesc_rel_(NULL),
+ : Output_section_data(addralign), tlsdesc_rel_(NULL), irelative_rel_(NULL),
got_(got), got_plt_(got_plt), got_irelative_(got_irelative),
count_(0), irelative_count_(0), tlsdesc_got_offset_(-1U)
{ this->init(layout); }
// Add an entry to the PLT.
void
- add_entry(Symbol* gsym);
+ add_entry(Symbol_table*, Layout*, Symbol* gsym);
+
+ // Add an entry to the PLT for a local STT_GNU_IFUNC symbol.
+ unsigned int
+ add_local_ifunc_entry(Symbol_table* symtab, Layout*,
+ Sized_relobj_file<size, big_endian>* relobj,
+ unsigned int local_sym_index);
+
+ // Add the relocation for a PLT entry.
+ void
+ add_relocation(Symbol_table*, Layout*, Symbol* gsym,
+ unsigned int got_offset);
// Add the reserved TLSDESC_PLT entry to the PLT.
void
// The number of PLT entries.
unsigned int count_;
- // Number of PLT entries with R_X86_64_IRELATIVE relocs. These
+ // Number of PLT entries with R_AARCH64_IRELATIVE relocs. These
// follow the regular PLT entries.
unsigned int irelative_count_;
template<int size, bool big_endian>
void
-Output_data_plt_aarch64<size, big_endian>::add_entry(Symbol* gsym)
+Output_data_plt_aarch64<size, big_endian>::add_entry(Symbol_table* symtab,
+ Layout* layout, Symbol* gsym)
{
gold_assert(!gsym->has_plt_offset());
- gsym->set_plt_offset((this->count_) * this->get_plt_entry_size()
- + this->first_plt_entry_offset());
+ unsigned int* pcount;
+ unsigned int plt_reserved;
+ Output_section_data_build* got;
+
+ if (gsym->type() == elfcpp::STT_GNU_IFUNC
+ && gsym->can_use_relative_reloc(false))
+ {
+ pcount = &this->irelative_count_;
+ plt_reserved = 0;
+ got = this->got_irelative_;
+ }
+ else
+ {
+ pcount = &this->count_;
+ plt_reserved = this->first_plt_entry_offset();
+ got = this->got_plt_;
+ }
+
+ gsym->set_plt_offset((*pcount) * this->get_plt_entry_size()
+ + plt_reserved);
- ++this->count_;
+ ++*pcount;
- 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 + size / 8);
+ got->set_current_data_size(got_offset + size / 8);
// Every PLT entry needs a reloc.
- gsym->set_needs_dynsym_entry();
- this->rel_->add_global(gsym, elfcpp::R_AARCH64_JUMP_SLOT,
- this->got_plt_, got_offset, 0);
+ 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<int size, bool big_endian>
+unsigned int
+Output_data_plt_aarch64<size, big_endian>::add_local_ifunc_entry(
+ Symbol_table* symtab,
+ Layout* layout,
+ Sized_relobj_file<size, big_endian>* relobj,
+ unsigned int local_sym_index)
+{
+ 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 + size / 8);
+
+ // Every PLT entry needs a reloc.
+ Reloc_section* rela = this->rela_irelative(symtab, layout);
+ rela->add_symbolless_local_addend(relobj, local_sym_index,
+ elfcpp::R_AARCH64_IRELATIVE,
+ this->got_irelative_, got_offset, 0);
+
+ return plt_offset;
+}
+
+// Add the relocation for a PLT entry.
+
+template<int size, bool big_endian>
+void
+Output_data_plt_aarch64<size, big_endian>::add_relocation(
+ Symbol_table* symtab, Layout* layout, Symbol* gsym, unsigned int got_offset)
+{
+ if (gsym->type() == elfcpp::STT_GNU_IFUNC
+ && gsym->can_use_relative_reloc(false))
+ {
+ Reloc_section* rela = this->rela_irelative(symtab, layout);
+ rela->add_symbolless_global_addend(gsym, elfcpp::R_AARCH64_IRELATIVE,
+ this->got_irelative_, got_offset, 0);
+ }
+ else
+ {
+ gsym->set_needs_dynsym_entry();
+ this->rel_->add_global(gsym, elfcpp::R_AARCH64_JUMP_SLOT, this->got_plt_,
+ got_offset, 0);
+ }
+}
+
// Return where the TLSDESC relocations should go, creating it if
// necessary. These follow the JUMP_SLOT relocations.
0xd503201f, /* nop */
};
+
template<>
const uint32_t
Output_data_plt_aarch64_standard<32, true>::
0xd503201f, /* nop */
};
+
template<>
const uint32_t
Output_data_plt_aarch64_standard<64, false>::
0xd503201f, /* nop */
};
+
template<>
const uint32_t
Output_data_plt_aarch64_standard<64, true>::
0xd503201f, /* nop */
};
+
template<>
const uint32_t
Output_data_plt_aarch64_standard<32, false>::
0xd61f0220, /* br x17. */
};
+
template<>
const uint32_t
Output_data_plt_aarch64_standard<32, true>::
0xd61f0220, /* br x17. */
};
+
template<>
const uint32_t
Output_data_plt_aarch64_standard<64, false>::
0xd61f0220, /* br x17. */
};
+
template<>
const uint32_t
Output_data_plt_aarch64_standard<64, true>::
0xd61f0220, /* br x17. */
};
+
template<int size, bool big_endian>
void
Output_data_plt_aarch64_standard<size, big_endian>::do_fill_first_plt_entry(
| ((gotplt_2nd_ent & 0xfff) << 10)));
}
+
// Subsequent entries in the PLT for an executable.
// FIXME: This only works for 64bit
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);
STATUS_BAD_RELOC, // Relocation cannot be applied.
} Status;
- private:
typedef AArch64_relocate_functions<size, big_endian> This;
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
+ typedef Relocate_info<size, big_endian> The_relocate_info;
+ typedef AArch64_relobj<size, big_endian> The_aarch64_relobj;
+ typedef Reloc_stub<size, big_endian> The_reloc_stub;
+ typedef Stub_table<size, big_endian> The_stub_table;
+ typedef elfcpp::Rela<size, big_endian> The_rela;
+ typedef typename elfcpp::Swap<size, big_endian>::Valtype AArch64_valtype;
// Return the page address of the address.
// Page(address) = address & ~0xFFF
- static inline typename elfcpp::Swap<size, big_endian>::Valtype
+ static inline AArch64_valtype
Page(Address address)
{
return (address & (~static_cast<Address>(0xFFF)));
}
+ private:
// Update instruction (pointed by view) with selected bits (immed).
// val = (val & ~dst_mask) | (immed << doffset)
template<int valsize>
static inline void
update_view(unsigned char* view,
- typename elfcpp::Swap<size, big_endian>::Valtype immed,
+ AArch64_valtype immed,
elfcpp::Elf_Xword doffset,
elfcpp::Elf_Xword dst_mask)
{
static inline void
update_view_two_parts(
unsigned char* view,
- typename elfcpp::Swap<size, big_endian>::Valtype immed1,
- typename elfcpp::Swap<size, big_endian>::Valtype immed2,
+ AArch64_valtype immed1,
+ AArch64_valtype immed2,
elfcpp::Elf_Xword doffset1,
elfcpp::Elf_Xword doffset2,
elfcpp::Elf_Xword dst_mask)
(immed2 << doffset2)));
}
- // Update adr or adrp instruction with [32:12] of X.
+ // Update adr or adrp instruction with immed.
// In adr and adrp: [30:29] immlo [23:5] immhi
static inline void
- update_adr(unsigned char* view,
- typename elfcpp::Swap<size, big_endian>::Valtype x,
- const AArch64_reloc_property* /* reloc_property */)
+ update_adr(unsigned char* view, AArch64_valtype immed)
{
elfcpp::Elf_Xword dst_mask = (0x3 << 29) | (0x7ffff << 5);
- typename elfcpp::Swap<32, big_endian>::Valtype immed =
- (x >> 12) & 0x1fffff;
This::template update_view_two_parts<32>(
view,
immed & 0x3,
// Update movz/movn instruction with bits immed.
// Set instruction to movz if is_movz is true, otherwise set instruction
// to movn.
+
static inline void
update_movnz(unsigned char* view,
- typename elfcpp::Swap<size, big_endian>::Valtype immed,
+ AArch64_valtype immed,
bool is_movz)
{
typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
aarch64_howto[AArch64_reloc_property::INST_MOVW].dst_mask;
// Clear immediate fields and opc code.
- val &= ~(dst_mask | (0x11 << 29));
+ val &= ~(dst_mask | (0x3 << 29));
// Set instruction to movz or movn.
// movz: [30:29] is 10 movn: [30:29] is 00
if (is_movz)
- val |= (0x10 << 29);
+ val |= (0x2 << 29);
elfcpp::Swap<32, big_endian>::writeval(wv,
static_cast<Valtype>(val | (immed << doffset)));
public:
+ // Update selected bits in text.
+
+ template<int valsize>
+ static inline typename This::Status
+ reloc_common(unsigned char* view, Address x,
+ const AArch64_reloc_property* reloc_property)
+ {
+ // Select bits from X.
+ Address immed = reloc_property->select_x_value(x);
+
+ // Update view.
+ const AArch64_reloc_property::Reloc_inst inst =
+ reloc_property->reloc_inst();
+ // If it is a data relocation or instruction has 2 parts of immediate
+ // fields, you should not call pcrela_general.
+ gold_assert(aarch64_howto[inst].doffset2 == -1 &&
+ aarch64_howto[inst].doffset != -1);
+ This::template update_view<valsize>(view, immed,
+ aarch64_howto[inst].doffset,
+ aarch64_howto[inst].dst_mask);
+
+ // Do check overflow or alignment if needed.
+ return (reloc_property->checkup_x_value(x)
+ ? This::STATUS_OKAY
+ : This::STATUS_OVERFLOW);
+ }
+
+ // Construct a B insn. Note, although we group it here with other relocation
+ // operation, there is actually no 'relocation' involved here.
+ static inline void
+ construct_b(unsigned char* view, unsigned int branch_offset)
+ {
+ update_view_two_parts<32>(view, 0x05, (branch_offset >> 2),
+ 26, 0, 0xffffffff);
+ }
+
// Do a simple rela relocation at unaligned addresses.
template<int valsize>
rela_ua(unsigned char* view,
const Sized_relobj_file<size, big_endian>* object,
const Symbol_value<size>* psymval,
- typename elfcpp::Swap<size, big_endian>::Valtype addend,
+ AArch64_valtype addend,
const AArch64_reloc_property* reloc_property)
{
typedef typename elfcpp::Swap_unaligned<valsize, big_endian>::Valtype
pcrela_ua(unsigned char* view,
const Sized_relobj_file<size, big_endian>* object,
const Symbol_value<size>* psymval,
- typename elfcpp::Swap<size, big_endian>::Valtype addend,
+ AArch64_valtype addend,
Address address,
const AArch64_reloc_property* reloc_property)
{
unsigned char* view,
const Sized_relobj_file<size, big_endian>* object,
const Symbol_value<size>* psymval,
- typename elfcpp::Swap<size, big_endian>::Valtype addend,
+ AArch64_valtype addend,
const AArch64_reloc_property* reloc_property)
{
- typedef typename elfcpp::Swap<valsize, big_endian>::Valtype
- Valtype;
+ typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
Valtype* wv = reinterpret_cast<Valtype*>(view);
Address x = psymval->value(object, addend);
- elfcpp::Swap<valsize, big_endian>::writeval(wv,
- static_cast<Valtype>(x));
+ elfcpp::Swap<valsize, big_endian>::writeval(wv,static_cast<Valtype>(x));
return (reloc_property->checkup_x_value(x)
? This::STATUS_OKAY
: This::STATUS_OVERFLOW);
rela_general(unsigned char* view,
const Sized_relobj_file<size, big_endian>* object,
const Symbol_value<size>* psymval,
- typename elfcpp::Swap<size, big_endian>::Valtype addend,
+ AArch64_valtype addend,
const AArch64_reloc_property* reloc_property)
{
// Calculate relocation.
- Address x = psymval->value(object, addend);
-
- // Select bits from X.
- Address immed = reloc_property->select_x_value(x);
-
- // Update view.
- const AArch64_reloc_property::Reloc_inst inst =
- reloc_property->reloc_inst();
- // If it is a data relocation or instruction has 2 parts of immediate
- // fields, you should not call rela_general.
- gold_assert(aarch64_howto[inst].doffset2 == -1 &&
- aarch64_howto[inst].doffset != -1);
- This::template update_view<valsize>(view, immed,
- aarch64_howto[inst].doffset,
- aarch64_howto[inst].dst_mask);
-
- // Do check overflow or alignment if needed.
- return (reloc_property->checkup_x_value(x)
- ? This::STATUS_OKAY
- : This::STATUS_OVERFLOW);
+ Address x = psymval->value(object, addend);
+ return This::template reloc_common<valsize>(view, x, reloc_property);
}
// Do relocate. Update selected bits in text.
static inline typename This::Status
rela_general(
unsigned char* view,
- typename elfcpp::Swap<size, big_endian>::Valtype s,
- typename elfcpp::Swap<size, big_endian>::Valtype addend,
+ AArch64_valtype s,
+ AArch64_valtype addend,
const AArch64_reloc_property* reloc_property)
{
// Calculate relocation.
Address x = s + addend;
-
- // Select bits from X.
- Address immed = reloc_property->select_x_value(x);
-
- // Update view.
- const AArch64_reloc_property::Reloc_inst inst =
- reloc_property->reloc_inst();
- // If it is a data relocation or instruction has 2 parts of immediate
- // fields, you should not call rela_general.
- gold_assert(aarch64_howto[inst].doffset2 == -1 &&
- aarch64_howto[inst].doffset != -1);
- This::template update_view<valsize>(view, immed,
- aarch64_howto[inst].doffset,
- aarch64_howto[inst].dst_mask);
-
- // Do check overflow or alignment if needed.
- return (reloc_property->checkup_x_value(x)
- ? This::STATUS_OKAY
- : This::STATUS_OVERFLOW);
+ return This::template reloc_common<valsize>(view, x, reloc_property);
}
// Do address relative relocate. Update selected bits in text.
unsigned char* view,
const Sized_relobj_file<size, big_endian>* object,
const Symbol_value<size>* psymval,
- typename elfcpp::Swap<size, big_endian>::Valtype addend,
+ AArch64_valtype addend,
Address address,
const AArch64_reloc_property* reloc_property)
{
// Calculate relocation.
Address x = psymval->value(object, addend) - address;
+ return This::template reloc_common<valsize>(view, x, reloc_property);
+ }
+
+
+ // Calculate (S + A) - address, update adr instruction.
+
+ static inline typename This::Status
+ adr(unsigned char* view,
+ const Sized_relobj_file<size, big_endian>* object,
+ const Symbol_value<size>* psymval,
+ Address addend,
+ Address address,
+ const AArch64_reloc_property* /* reloc_property */)
+ {
+ AArch64_valtype x = psymval->value(object, addend) - address;
+ // Pick bits [20:0] of X.
+ AArch64_valtype immed = x & 0x1fffff;
+ update_adr(view, immed);
+ // Check -2^20 <= X < 2^20
+ return (size == 64 && Bits<21>::has_overflow((x))
+ ? This::STATUS_OVERFLOW
+ : This::STATUS_OKAY);
+ }
+
+ // Calculate PG(S+A) - PG(address), update adrp instruction.
+ // R_AARCH64_ADR_PREL_PG_HI21
+
+ static inline typename This::Status
+ adrp(
+ unsigned char* view,
+ Address sa,
+ Address address)
+ {
+ AArch64_valtype x = This::Page(sa) - This::Page(address);
+ // Pick [32:12] of X.
+ AArch64_valtype immed = (x >> 12) & 0x1fffff;
+ update_adr(view, immed);
+ // Check -2^32 <= X < 2^32
+ return (size == 64 && Bits<33>::has_overflow((x))
+ ? This::STATUS_OVERFLOW
+ : This::STATUS_OKAY);
+ }
+
+ // Calculate PG(S+A) - PG(address), update adrp instruction.
+ // R_AARCH64_ADR_PREL_PG_HI21
+
+ static inline typename This::Status
+ adrp(unsigned char* view,
+ const Sized_relobj_file<size, big_endian>* object,
+ const Symbol_value<size>* psymval,
+ Address addend,
+ Address address,
+ const AArch64_reloc_property* reloc_property)
+ {
+ Address sa = psymval->value(object, addend);
+ AArch64_valtype x = This::Page(sa) - This::Page(address);
+ // Pick [32:12] of X.
+ AArch64_valtype immed = (x >> 12) & 0x1fffff;
+ update_adr(view, immed);
+ return (reloc_property->checkup_x_value(x)
+ ? This::STATUS_OKAY
+ : This::STATUS_OVERFLOW);
+ }
+ // Update mov[n/z] instruction. Check overflow if needed.
+ // If X >=0, set the instruction to movz and its immediate value to the
+ // selected bits S.
+ // If X < 0, set the instruction to movn and its immediate value to
+ // NOT (selected bits of).
+
+ static inline typename This::Status
+ movnz(unsigned char* view,
+ AArch64_valtype x,
+ const AArch64_reloc_property* reloc_property)
+ {
// Select bits from X.
- Address immed = reloc_property->select_x_value(x);
+ Address immed;
+ bool is_movz;
+ typedef typename elfcpp::Elf_types<size>::Elf_Swxword SignedW;
+ if (static_cast<SignedW>(x) >= 0)
+ {
+ immed = reloc_property->select_x_value(x);
+ is_movz = true;
+ }
+ else
+ {
+ immed = reloc_property->select_x_value(~x);;
+ is_movz = false;
+ }
- // Update view.
- const AArch64_reloc_property::Reloc_inst inst =
- reloc_property->reloc_inst();
- // If it is a data relocation or instruction has 2 parts of immediate
- // fields, you should not call pcrela_general.
- gold_assert(aarch64_howto[inst].doffset2 == -1 &&
- aarch64_howto[inst].doffset != -1);
- This::template update_view<valsize>(view, immed,
- aarch64_howto[inst].doffset,
- aarch64_howto[inst].dst_mask);
+ // Update movnz instruction.
+ update_movnz(view, immed, is_movz);
+
+ // Do check overflow or alignment if needed.
+ return (reloc_property->checkup_x_value(x)
+ ? This::STATUS_OKAY
+ : This::STATUS_OVERFLOW);
+ }
+
+ static inline bool
+ maybe_apply_stub(unsigned int,
+ const The_relocate_info*,
+ const The_rela&,
+ unsigned char*,
+ Address,
+ const Sized_symbol<size>*,
+ const Symbol_value<size>*,
+ const Sized_relobj_file<size, big_endian>*,
+ section_size_type);
+
+}; // End of AArch64_relocate_functions
+
+
+// For a certain relocation type (usually jump/branch), test to see if the
+// destination needs a stub to fulfil. If so, re-route the destination of the
+// original instruction to the stub, note, at this time, the stub has already
+// been generated.
+
+template<int size, bool big_endian>
+bool
+AArch64_relocate_functions<size, big_endian>::
+maybe_apply_stub(unsigned int r_type,
+ const The_relocate_info* relinfo,
+ const The_rela& rela,
+ unsigned char* view,
+ Address address,
+ const Sized_symbol<size>* gsym,
+ const Symbol_value<size>* psymval,
+ const Sized_relobj_file<size, big_endian>* object,
+ section_size_type current_group_size)
+{
+ if (parameters->options().relocatable())
+ return false;
+
+ typename elfcpp::Elf_types<size>::Elf_Swxword addend = rela.get_r_addend();
+ Address branch_target = psymval->value(object, 0) + addend;
+ int stub_type =
+ The_reloc_stub::stub_type_for_reloc(r_type, address, branch_target);
+ if (stub_type == ST_NONE)
+ return false;
+
+ const The_aarch64_relobj* aarch64_relobj =
+ static_cast<const The_aarch64_relobj*>(object);
+ const AArch64_reloc_property* arp =
+ aarch64_reloc_property_table->get_reloc_property(r_type);
+ gold_assert(arp != NULL);
+
+ // We don't create stubs for undefined symbols, but do for weak.
+ if (gsym
+ && !gsym->use_plt_offset(arp->reference_flags())
+ && gsym->is_undefined())
+ {
+ gold_debug(DEBUG_TARGET,
+ "stub: looking for a stub for undefined symbol %s in file %s",
+ gsym->name(), aarch64_relobj->name().c_str());
+ return false;
+ }
+
+ The_stub_table* stub_table = aarch64_relobj->stub_table(relinfo->data_shndx);
+ gold_assert(stub_table != NULL);
+
+ unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
+ typename The_reloc_stub::Key stub_key(stub_type, gsym, object, r_sym, addend);
+ The_reloc_stub* stub = stub_table->find_reloc_stub(stub_key);
+ gold_assert(stub != NULL);
+
+ Address new_branch_target = stub_table->address() + stub->offset();
+ typename elfcpp::Swap<size, big_endian>::Valtype branch_offset =
+ new_branch_target - address;
+ typename This::Status status = This::template
+ rela_general<32>(view, branch_offset, 0, arp);
+ if (status != This::STATUS_OKAY)
+ gold_error(_("Stub is too far away, try a smaller value "
+ "for '--stub-group-size'. The current value is 0x%lx."),
+ static_cast<unsigned long>(current_group_size));
+ return true;
+}
+
+
+// Group input sections for stub generation.
+//
+// We group input sections in an output section so that the total size,
+// including any padding space due to alignment is smaller than GROUP_SIZE
+// unless the only input section in group is bigger than GROUP_SIZE already.
+// Then an ARM stub table is created to follow the last input section
+// in group. For each group an ARM stub table is created an is placed
+// after the last group. If STUB_ALWAYS_AFTER_BRANCH is false, we further
+// extend the group after the stub table.
+
+template<int size, bool big_endian>
+void
+Target_aarch64<size, big_endian>::group_sections(
+ Layout* layout,
+ section_size_type group_size,
+ bool stubs_always_after_branch,
+ const Task* task)
+{
+ // Group input sections and insert stub table
+ Layout::Section_list section_list;
+ layout->get_executable_sections(§ion_list);
+ for (Layout::Section_list::const_iterator p = section_list.begin();
+ p != section_list.end();
+ ++p)
+ {
+ AArch64_output_section<size, big_endian>* output_section =
+ static_cast<AArch64_output_section<size, big_endian>*>(*p);
+ output_section->group_sections(group_size, stubs_always_after_branch,
+ this, task);
+ }
+}
+
+
+// Find the AArch64_input_section object corresponding to the SHNDX-th input
+// section of RELOBJ.
+
+template<int size, bool big_endian>
+AArch64_input_section<size, big_endian>*
+Target_aarch64<size, big_endian>::find_aarch64_input_section(
+ Relobj* relobj, unsigned int shndx) const
+{
+ Section_id sid(relobj, shndx);
+ typename AArch64_input_section_map::const_iterator p =
+ this->aarch64_input_section_map_.find(sid);
+ return (p != this->aarch64_input_section_map_.end()) ? p->second : NULL;
+}
+
+
+// Make a new AArch64_input_section object.
+
+template<int size, bool big_endian>
+AArch64_input_section<size, big_endian>*
+Target_aarch64<size, big_endian>::new_aarch64_input_section(
+ Relobj* relobj, unsigned int shndx)
+{
+ Section_id sid(relobj, shndx);
+
+ AArch64_input_section<size, big_endian>* input_section =
+ new AArch64_input_section<size, big_endian>(relobj, shndx);
+ input_section->init();
+
+ // Register new AArch64_input_section in map for look-up.
+ std::pair<typename AArch64_input_section_map::iterator,bool> ins =
+ this->aarch64_input_section_map_.insert(
+ std::make_pair(sid, input_section));
+
+ // Make sure that it we have not created another AArch64_input_section
+ // for this input section already.
+ gold_assert(ins.second);
+
+ return input_section;
+}
+
+
+// Relaxation hook. This is where we do stub generation.
- // Do check overflow or alignment if needed.
- return (reloc_property->checkup_x_value(x)
- ? This::STATUS_OKAY
- : This::STATUS_OVERFLOW);
- }
+template<int size, bool big_endian>
+bool
+Target_aarch64<size, big_endian>::do_relax(
+ int pass,
+ const Input_objects* input_objects,
+ Symbol_table* symtab,
+ Layout* layout ,
+ const Task* task)
+{
+ gold_assert(!parameters->options().relocatable());
+ if (pass == 1)
+ {
+ // We don't handle negative stub_group_size right now.
+ this->stub_group_size_ = abs(parameters->options().stub_group_size());
+ if (this->stub_group_size_ == 1)
+ {
+ // Leave room for 4096 4-byte stub entries. If we exceed that, then we
+ // will fail to link. The user will have to relink with an explicit
+ // group size option.
+ this->stub_group_size_ = The_reloc_stub::MAX_BRANCH_OFFSET -
+ 4096 * 4;
+ }
+ group_sections(layout, this->stub_group_size_, true, task);
+ }
+ else
+ {
+ // If this is not the first pass, addresses and file offsets have
+ // been reset at this point, set them here.
+ for (Stub_table_iterator sp = this->stub_tables_.begin();
+ sp != this->stub_tables_.end(); ++sp)
+ {
+ The_stub_table* stt = *sp;
+ The_aarch64_input_section* owner = stt->owner();
+ off_t off = align_address(owner->original_size(),
+ stt->addralign());
+ stt->set_address_and_file_offset(owner->address() + off,
+ owner->offset() + off);
+ }
+ }
- // Calculate PG(S+A) - PG(address), update adrp instruction.
- // R_AARCH64_ADR_PREL_PG_HI21
+ // Scan relocs for relocation stubs
+ for (Input_objects::Relobj_iterator op = input_objects->relobj_begin();
+ op != input_objects->relobj_end();
+ ++op)
+ {
+ The_aarch64_relobj* aarch64_relobj =
+ static_cast<The_aarch64_relobj*>(*op);
+ // Lock the object so we can read from it. This is only called
+ // single-threaded from Layout::finalize, so it is OK to lock.
+ Task_lock_obj<Object> tl(task, aarch64_relobj);
+ aarch64_relobj->scan_sections_for_stubs(this, symtab, layout);
+ }
- static inline typename This::Status
- adrp(
- unsigned char* view,
- Address sa,
- Address address)
- {
- typename elfcpp::Swap<size, big_endian>::Valtype x =
- This::Page(sa) - This::Page(address);
- update_adr(view, x, NULL);
- return (size == 64 && Bits<32>::has_overflow(x)
- ? This::STATUS_OVERFLOW
- : This::STATUS_OKAY);
- }
+ bool any_stub_table_changed = false;
+ for (Stub_table_iterator siter = this->stub_tables_.begin();
+ siter != this->stub_tables_.end() && !any_stub_table_changed; ++siter)
+ {
+ The_stub_table* stub_table = *siter;
+ if (stub_table->update_data_size_changed_p())
+ {
+ The_aarch64_input_section* owner = stub_table->owner();
+ uint64_t address = owner->address();
+ off_t offset = owner->offset();
+ owner->reset_address_and_file_offset();
+ owner->set_address_and_file_offset(address, offset);
- // Calculate PG(S+A) - PG(address), update adrp instruction.
- // R_AARCH64_ADR_PREL_PG_HI21
+ any_stub_table_changed = true;
+ }
+ }
- static inline typename This::Status
- adrp(unsigned char* view,
- const Sized_relobj_file<size, big_endian>* object,
- const Symbol_value<size>* psymval,
- Address addend,
- Address address,
- const AArch64_reloc_property* reloc_property)
- {
- Address sa = psymval->value(object, addend);
- typename elfcpp::Swap<size, big_endian>::Valtype x =
- This::Page(sa) - This::Page(address);
- update_adr(view, x, reloc_property);
- return (reloc_property->checkup_x_value(x)
- ? This::STATUS_OKAY
- : This::STATUS_OVERFLOW);
- }
+ // Do not continue relaxation.
+ bool continue_relaxation = any_stub_table_changed;
+ if (!continue_relaxation)
+ for (Stub_table_iterator sp = this->stub_tables_.begin();
+ (sp != this->stub_tables_.end());
+ ++sp)
+ (*sp)->finalize_stubs();
- // Update mov[n/z] instruction. Check overflow if needed.
- // If X >=0, set the instruction to movz and its immediate value to the
- // selected bits S.
- // If X < 0, set the instruction to movn and its immediate value to
- // NOT (selected bits of).
+ return continue_relaxation;
+}
- static inline typename This::Status
- movnz(unsigned char* view,
- typename elfcpp::Swap<size, big_endian>::Valtype x,
- const AArch64_reloc_property* reloc_property)
- {
- // Select bits from X.
- Address immed = reloc_property->select_x_value(x);
- bool is_movz = true;
- if (static_cast<int64_t>(x) < 0)
- {
- immed = ~immed;
- is_movz = false;
- }
- // Update movnz instruction.
- update_movnz(view, immed, is_movz);
+// Make a new Stub_table.
- // Do check overflow or alignment if needed.
- return (reloc_property->checkup_x_value(x)
- ? This::STATUS_OKAY
- : This::STATUS_OVERFLOW);
- }
+template<int size, bool big_endian>
+Stub_table<size, big_endian>*
+Target_aarch64<size, big_endian>::new_stub_table(
+ AArch64_input_section<size, big_endian>* owner)
+{
+ Stub_table<size, big_endian>* stub_table =
+ new Stub_table<size, big_endian>(owner);
+ stub_table->set_address(align_address(
+ owner->address() + owner->data_size(), 8));
+ stub_table->set_file_offset(owner->offset() + owner->data_size());
+ stub_table->finalize_data_size();
-}; // End of AArch64_relocate_functions
+ this->stub_tables_.push_back(stub_table);
+
+ return stub_table;
+}
template<int size, bool big_endian>
-typename elfcpp::Elf_types<size>::Elf_Addr
+uint64_t
Target_aarch64<size, big_endian>::do_reloc_addend(
- void* arg, unsigned int r_type,
- typename elfcpp::Elf_types<size>::Elf_Addr) const
+ void* arg, unsigned int r_type, uint64_t) const
{
gold_assert(r_type == elfcpp::R_AARCH64_TLSDESC);
uintptr_t intarg = reinterpret_cast<uintptr_t>(arg);
Output_segment* tls_segment = layout->tls_segment();
if (tls_segment != NULL)
{
- bool is_exec = parameters->options().output_is_executable();
+ // _TLS_MODULE_BASE_ always points to the beginning of tls segment.
symtab->define_in_output_segment("_TLS_MODULE_BASE_", NULL,
Symbol_table::PREDEFINED,
tls_segment, 0, 0,
elfcpp::STT_TLS,
elfcpp::STB_LOCAL,
elfcpp::STV_HIDDEN, 0,
- (is_exec
- ? Symbol::SEGMENT_END
- : Symbol::SEGMENT_START),
+ Symbol::SEGMENT_START,
true);
}
this->tls_base_symbol_defined_ = true;
return tls::TLSOPT_TO_LE;
return tls::TLSOPT_TO_IE;
+ case elfcpp::R_AARCH64_TLSLD_ADR_PAGE21:
+ case elfcpp::R_AARCH64_TLSLD_ADD_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLD_MOVW_DTPREL_G1:
+ case elfcpp::R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC:
+ case elfcpp::R_AARCH64_TLSLD_ADD_DTPREL_HI12:
+ case elfcpp::R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC:
+ // These are Local-Dynamic, which refer to local symbols in the
+ // dynamic TLS block. Since we know that we generating an
+ // executable, we can switch to Local-Exec.
+ return tls::TLSOPT_TO_LE;
+
case elfcpp::R_AARCH64_TLSIE_MOVW_GOTTPREL_G1:
case elfcpp::R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC:
case elfcpp::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_HI12:
case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_LO12:
case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLE_LDST8_TPREL_LO12:
+ case elfcpp::R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLE_LDST16_TPREL_LO12:
+ case elfcpp::R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLE_LDST32_TPREL_LO12:
+ case elfcpp::R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLE_LDST64_TPREL_LO12:
+ case elfcpp::R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
// When we already have Local-Exec, there is nothing further we
// can do.
return tls::TLSOPT_NONE;
{
switch (r_type)
{
- case elfcpp::R_AARCH64_ABS64:
- //TODO
+ case elfcpp::R_AARCH64_ADR_PREL_PG_HI21:
+ case elfcpp::R_AARCH64_ADR_PREL_PG_HI21_NC:
+ case elfcpp::R_AARCH64_ADD_ABS_LO12_NC:
+ case elfcpp::R_AARCH64_ADR_GOT_PAGE:
+ case elfcpp::R_AARCH64_LD64_GOT_LO12_NC:
{
return true;
}
unsigned int r_type,
const elfcpp::Sym<size, big_endian>&)
{
- // When building a shared library, do not fold any local symbols as it is
- // not possible to distinguish pointer taken versus a call by looking at
- // the relocation types.
+ // When building a shared library, do not fold any local symbols.
return (parameters->options().shared()
|| possible_function_pointer_reloc(r_type));
}
return;
}
+// Return whether we need to make a PLT entry for a relocation of the
+// given type against a STT_GNU_IFUNC symbol.
+
+template<int size, bool big_endian>
+bool
+Target_aarch64<size, big_endian>::Scan::reloc_needs_plt_for_ifunc(
+ Sized_relobj_file<size, big_endian>* object,
+ unsigned int r_type)
+{
+ const AArch64_reloc_property* arp =
+ aarch64_reloc_property_table->get_reloc_property(r_type);
+ gold_assert(arp != NULL);
+
+ int flags = arp->reference_flags();
+ if (flags & Symbol::TLS_REF)
+ {
+ gold_error(_("%s: unsupported TLS reloc %s for IFUNC symbol"),
+ object->name().c_str(), arp->name().c_str());
+ return false;
+ }
+ return flags != 0;
+}
+
// Scan a relocation for a local symbol.
template<int size, bool big_endian>
Output_section* output_section,
const elfcpp::Rela<size, big_endian>& rela,
unsigned int r_type,
- const elfcpp::Sym<size, big_endian>& /* lsym */,
+ const elfcpp::Sym<size, big_endian>& lsym,
bool is_discarded)
{
if (is_discarded)
typedef Output_data_reloc<elfcpp::SHT_RELA, true, size, big_endian>
Reloc_section;
- Output_data_got_aarch64<size, big_endian>* got =
- target->got_section(symtab, layout);
unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
+ // 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))
+ target->make_local_ifunc_plt_entry(symtab, layout, object, r_sym);
+
switch (r_type)
{
+ case elfcpp::R_AARCH64_NONE:
+ break;
+
case elfcpp::R_AARCH64_ABS32:
case elfcpp::R_AARCH64_ABS16:
if (parameters->options().output_is_position_independent())
data_shndx,
rela.get_r_offset(),
rela.get_r_addend(),
- false /* is ifunc */);
+ is_ifunc);
}
break;
case elfcpp::R_AARCH64_PREL16:
break;
+ case elfcpp::R_AARCH64_ADR_GOT_PAGE:
+ case elfcpp::R_AARCH64_LD64_GOT_LO12_NC:
+ case elfcpp::R_AARCH64_LD64_GOTPAGE_LO15:
+ // The above relocations are used to access GOT entries.
+ {
+ Output_data_got_aarch64<size, big_endian>* got =
+ target->got_section(symtab, layout);
+ bool is_new = false;
+ // This symbol requires a GOT entry.
+ if (is_ifunc)
+ is_new = got->add_local_plt(object, r_sym, GOT_TYPE_STANDARD);
+ else
+ is_new = got->add_local(object, r_sym, GOT_TYPE_STANDARD);
+ if (is_new && parameters->options().output_is_position_independent())
+ target->rela_dyn_section(layout)->
+ add_local_relative(object,
+ r_sym,
+ elfcpp::R_AARCH64_RELATIVE,
+ got,
+ object->local_got_offset(r_sym,
+ GOT_TYPE_STANDARD),
+ 0,
+ false);
+ }
+ break;
+
+ case elfcpp::R_AARCH64_MOVW_UABS_G0: // 263
+ case elfcpp::R_AARCH64_MOVW_UABS_G0_NC: // 264
+ case elfcpp::R_AARCH64_MOVW_UABS_G1: // 265
+ case elfcpp::R_AARCH64_MOVW_UABS_G1_NC: // 266
+ case elfcpp::R_AARCH64_MOVW_UABS_G2: // 267
+ case elfcpp::R_AARCH64_MOVW_UABS_G2_NC: // 268
+ case elfcpp::R_AARCH64_MOVW_UABS_G3: // 269
+ case elfcpp::R_AARCH64_MOVW_SABS_G0: // 270
+ case elfcpp::R_AARCH64_MOVW_SABS_G1: // 271
+ case elfcpp::R_AARCH64_MOVW_SABS_G2: // 272
+ if (parameters->options().output_is_position_independent())
+ {
+ gold_error(_("%s: unsupported reloc %u in pos independent link."),
+ object->name().c_str(), r_type);
+ }
+ break;
+
case elfcpp::R_AARCH64_LD_PREL_LO19: // 273
case elfcpp::R_AARCH64_ADR_PREL_LO21: // 274
case elfcpp::R_AARCH64_ADR_PREL_PG_HI21: // 275
// Create a GOT entry for the tp-relative offset.
if (!parameters->doing_static_link())
{
+ Output_data_got_aarch64<size, big_endian>* got =
+ target->got_section(symtab, layout);
got->add_local_with_rel(object, r_sym, GOT_TYPE_TLS_OFFSET,
target->rela_dyn_section(layout),
elfcpp::R_AARCH64_TLS_TPREL64);
else if (!object->local_has_got_offset(r_sym,
GOT_TYPE_TLS_OFFSET))
{
+ Output_data_got_aarch64<size, big_endian>* got =
+ target->got_section(symtab, layout);
got->add_local(object, r_sym, GOT_TYPE_TLS_OFFSET);
unsigned int got_offset =
object->local_got_offset(r_sym, GOT_TYPE_TLS_OFFSET);
}
gold_assert(tlsopt == tls::TLSOPT_NONE);
+ Output_data_got_aarch64<size, big_endian>* got =
+ target->got_section(symtab, layout);
got->add_local_pair_with_rel(object,r_sym, data_shndx,
GOT_TYPE_TLS_PAIR,
target->rela_dyn_section(layout),
}
break;
+ case elfcpp::R_AARCH64_TLSLE_MOVW_TPREL_G2:
+ case elfcpp::R_AARCH64_TLSLE_MOVW_TPREL_G1:
+ case elfcpp::R_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
+ case elfcpp::R_AARCH64_TLSLE_MOVW_TPREL_G0:
+ case elfcpp::R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_HI12:
case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_LO12:
case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLE_LDST8_TPREL_LO12:
+ case elfcpp::R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLE_LDST16_TPREL_LO12:
+ case elfcpp::R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLE_LDST32_TPREL_LO12:
+ case elfcpp::R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLE_LDST64_TPREL_LO12:
+ case elfcpp::R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
{
layout->set_has_static_tls();
bool output_is_shared = parameters->options().shared();
}
break;
+ case elfcpp::R_AARCH64_TLSLD_ADR_PAGE21:
+ case elfcpp::R_AARCH64_TLSLD_ADD_LO12_NC:
+ {
+ tls::Tls_optimization tlsopt = Target_aarch64<size, big_endian>::
+ optimize_tls_reloc(!parameters->options().shared(), r_type);
+ if (tlsopt == tls::TLSOPT_NONE)
+ {
+ // Create a GOT entry for the module index.
+ target->got_mod_index_entry(symtab, layout, object);
+ }
+ else if (tlsopt != tls::TLSOPT_TO_LE)
+ unsupported_reloc_local(object, r_type);
+ }
+ break;
+
+ case elfcpp::R_AARCH64_TLSLD_MOVW_DTPREL_G1:
+ case elfcpp::R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC:
+ case elfcpp::R_AARCH64_TLSLD_ADD_DTPREL_HI12:
+ case elfcpp::R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC:
+ break;
+
case elfcpp::R_AARCH64_TLSDESC_ADR_PAGE21:
case elfcpp::R_AARCH64_TLSDESC_LD64_LO12:
case elfcpp::R_AARCH64_TLSDESC_ADD_LO12:
unsigned int r_type,
Symbol* gsym)
{
+ // 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);
+
typedef Output_data_reloc<elfcpp::SHT_RELA, true, size, big_endian>
Reloc_section;
const AArch64_reloc_property* arp =
switch (r_type)
{
+ case elfcpp::R_AARCH64_NONE:
+ break;
+
case elfcpp::R_AARCH64_ABS16:
case elfcpp::R_AARCH64_ABS32:
case elfcpp::R_AARCH64_ABS64:
target->copy_reloc(symtab, layout, object,
data_shndx, output_section, gsym, rela);
}
+ else if (r_type == elfcpp::R_AARCH64_ABS64
+ && 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* rela_dyn =
+ target->rela_irelative_section(layout);
+ unsigned int r_type = elfcpp::R_AARCH64_IRELATIVE;
+ rela_dyn->add_symbolless_global_addend(gsym, r_type,
+ output_section, object,
+ data_shndx,
+ rela.get_r_offset(),
+ rela.get_r_addend());
+ }
else if (r_type == elfcpp::R_AARCH64_ABS64
&& gsym->can_use_relative_reloc(false))
{
}
break;
+ case elfcpp::R_AARCH64_MOVW_UABS_G0: // 263
+ case elfcpp::R_AARCH64_MOVW_UABS_G0_NC: // 264
+ case elfcpp::R_AARCH64_MOVW_UABS_G1: // 265
+ case elfcpp::R_AARCH64_MOVW_UABS_G1_NC: // 266
+ case elfcpp::R_AARCH64_MOVW_UABS_G2: // 267
+ case elfcpp::R_AARCH64_MOVW_UABS_G2_NC: // 268
+ case elfcpp::R_AARCH64_MOVW_UABS_G3: // 269
+ case elfcpp::R_AARCH64_MOVW_SABS_G0: // 270
+ case elfcpp::R_AARCH64_MOVW_SABS_G1: // 271
+ case elfcpp::R_AARCH64_MOVW_SABS_G2: // 272
+ if (parameters->options().output_is_position_independent())
+ {
+ gold_error(_("%s: unsupported reloc %u in pos independent link."),
+ object->name().c_str(), r_type);
+ }
+ break;
+
case elfcpp::R_AARCH64_LD_PREL_LO19: // 273
case elfcpp::R_AARCH64_ADR_PREL_LO21: // 274
case elfcpp::R_AARCH64_ADR_PREL_PG_HI21: // 275
case elfcpp::R_AARCH64_ADR_GOT_PAGE:
case elfcpp::R_AARCH64_LD64_GOT_LO12_NC:
+ case elfcpp::R_AARCH64_LD64_GOTPAGE_LO15:
{
- // This pair of relocations is used to access a specific GOT entry.
+ // The above relocations are used to access GOT entries.
// Note a GOT entry is an *address* to a symbol.
// The symbol requires a GOT entry
Output_data_got_aarch64<size, big_endian>* got =
target->got_section(symtab, layout);
if (gsym->final_value_is_known())
{
- got->add_global(gsym, GOT_TYPE_STANDARD);
+ // For a STT_GNU_IFUNC symbol we want the PLT address.
+ if (gsym->type() == elfcpp::STT_GNU_IFUNC)
+ got->add_global_plt(gsym, GOT_TYPE_STANDARD);
+ else
+ got->add_global(gsym, GOT_TYPE_STANDARD);
}
else
{
+ // If this symbol is not fully resolved, we need to add a dynamic
+ // relocation for it.
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+
+ // Use a GLOB_DAT rather than a RELATIVE reloc if:
+ //
+ // 1) The symbol may be defined in some other module.
+ // 2) We are building a shared library and this is a protected
+ // symbol; using GLOB_DAT means that the dynamic linker can use
+ // the address of the PLT in the main executable when appropriate
+ // so that function address comparisons work.
+ // 3) This is a STT_GNU_IFUNC symbol in position dependent code,
+ // again so that function address comparisons work.
if (gsym->is_from_dynobj()
|| 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,
rela_dyn, elfcpp::R_AARCH64_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)
{
rela_dyn->add_global_relative(
gsym, elfcpp::R_AARCH64_RELATIVE,
}
break;
+ case elfcpp::R_AARCH64_TLSLD_ADR_PAGE21:
+ case elfcpp::R_AARCH64_TLSLD_ADD_LO12_NC: // Local dynamic
+ {
+ tls::Tls_optimization tlsopt = Target_aarch64<size, big_endian>::
+ optimize_tls_reloc(!parameters->options().shared(), r_type);
+ if (tlsopt == tls::TLSOPT_NONE)
+ {
+ // Create a GOT entry for the module index.
+ target->got_mod_index_entry(symtab, layout, object);
+ }
+ else if (tlsopt != tls::TLSOPT_TO_LE)
+ unsupported_reloc_local(object, r_type);
+ }
+ break;
+
+ case elfcpp::R_AARCH64_TLSLD_MOVW_DTPREL_G1:
+ case elfcpp::R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC:
+ case elfcpp::R_AARCH64_TLSLD_ADD_DTPREL_HI12:
+ case elfcpp::R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC: // Other local dynamic
+ break;
+
case elfcpp::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
case elfcpp::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: // Initial executable
{
- tls::Tls_optimization tlsopt =Target_aarch64<size, big_endian>::
+ tls::Tls_optimization tlsopt = Target_aarch64<size, big_endian>::
optimize_tls_reloc(gsym->final_value_is_known(), r_type);
if (tlsopt == tls::TLSOPT_TO_LE)
break;
}
break;
+ case elfcpp::R_AARCH64_TLSLE_MOVW_TPREL_G2:
+ case elfcpp::R_AARCH64_TLSLE_MOVW_TPREL_G1:
+ case elfcpp::R_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
+ case elfcpp::R_AARCH64_TLSLE_MOVW_TPREL_G0:
+ case elfcpp::R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_HI12:
case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_LO12:
- case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: // Local executable
+ case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLE_LDST8_TPREL_LO12:
+ case elfcpp::R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLE_LDST16_TPREL_LO12:
+ case elfcpp::R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLE_LDST32_TPREL_LO12:
+ case elfcpp::R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLE_LDST64_TPREL_LO12:
+ case elfcpp::R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC: // Local executable
layout->set_has_static_tls();
if (parameters->options().shared())
gold_error(_("%s: unsupported TLSLE reloc type %u in shared objects."),
if (this->plt_ == NULL)
this->make_plt_section(symtab, layout);
- this->plt_->add_entry(gsym);
+ this->plt_->add_entry(symtab, layout, gsym);
+}
+
+// Make a PLT entry for a local STT_GNU_IFUNC symbol.
+
+template<int size, bool big_endian>
+void
+Target_aarch64<size, big_endian>::make_local_ifunc_plt_entry(
+ Symbol_table* symtab, Layout* layout,
+ Sized_relobj_file<size, 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);
}
template<int size, bool big_endian>
size_t local_symbol_count,
const unsigned char* plocal_symbols)
{
+ typedef Target_aarch64<size, big_endian> Aarch64;
+ typedef gold::Default_classify_reloc<elfcpp::SHT_RELA, size, big_endian>
+ Classify_reloc;
+
if (sh_type == elfcpp::SHT_REL)
{
return;
}
- gold::gc_process_relocs<
- size, big_endian,
- Target_aarch64<size, big_endian>,
- elfcpp::SHT_RELA,
- typename Target_aarch64<size, big_endian>::Scan,
- typename Target_aarch64<size, big_endian>::Relocatable_size_for_reloc>(
+ gold::gc_process_relocs<size, big_endian, Aarch64, Scan, Classify_reloc>(
symtab,
layout,
this,
size_t local_symbol_count,
const unsigned char* plocal_symbols)
{
+ typedef Target_aarch64<size, big_endian> Aarch64;
+ typedef gold::Default_classify_reloc<elfcpp::SHT_RELA, size, big_endian>
+ Classify_reloc;
+
if (sh_type == elfcpp::SHT_REL)
{
gold_error(_("%s: unsupported REL reloc section"),
object->name().c_str());
return;
}
- gold::scan_relocs<size, big_endian, Target_aarch64, elfcpp::SHT_RELA, Scan>(
+
+ gold::scan_relocs<size, big_endian, Aarch64, Scan, Classify_reloc>(
symtab,
layout,
this,
// pointers across shared library boundaries, as described in the
// processor specific ABI supplement.
-template<int size,bool big_endian>
+template<int size, bool big_endian>
uint64_t
-Target_aarch64<size,big_endian>::do_dynsym_value(const Symbol* gsym) const
+Target_aarch64<size, big_endian>::do_dynsym_value(const Symbol* gsym) const
{
gold_assert(gsym->is_from_dynobj() && gsym->has_plt_offset());
return this->plt_address_for_global(gsym);
}
+
// Finalize the sections.
template<int size, bool big_endian>
}
// Set the size of the _GLOBAL_OFFSET_TABLE_ symbol to the size of
- // the .got.plt section.
+ // the .got section.
Symbol* sym = this->global_offset_table_;
if (sym != NULL)
{
- uint64_t data_size = this->got_plt_->current_data_size();
+ uint64_t data_size = this->got_->current_data_size();
symtab->get_sized_symbol<size>(sym)->set_symsize(data_size);
// If the .got section is more than 0x8000 bytes, we add
inline bool
Target_aarch64<size, big_endian>::Relocate::relocate(
const Relocate_info<size, big_endian>* relinfo,
+ unsigned int,
Target_aarch64<size, big_endian>* target,
Output_section* ,
size_t relnum,
- const elfcpp::Rela<size, big_endian>& rela,
- unsigned int r_type,
+ const unsigned char* preloc,
const Sized_symbol<size>* gsym,
const Symbol_value<size>* psymval,
unsigned char* view,
typedef AArch64_relocate_functions<size, big_endian> Reloc;
+ const elfcpp::Rela<size, big_endian> rela(preloc);
+ unsigned int r_type = elfcpp::elf_r_type<size>(rela.get_r_info());
const AArch64_reloc_property* reloc_property =
aarch64_reloc_property_table->get_reloc_property(r_type);
break;
case elfcpp::R_AARCH64_ABS64:
+ if (!parameters->options().apply_dynamic_relocs()
+ && parameters->options().output_is_position_independent()
+ && gsym != NULL
+ && gsym->needs_dynamic_reloc(reloc_property->reference_flags())
+ && !gsym->can_use_relative_reloc(false))
+ // We have generated an absolute dynamic relocation, so do not
+ // apply the relocation statically. (Works around bugs in older
+ // Android dynamic linkers.)
+ break;
reloc_status = Reloc::template rela_ua<64>(
view, object, psymval, addend, reloc_property);
break;
case elfcpp::R_AARCH64_ABS32:
+ if (!parameters->options().apply_dynamic_relocs()
+ && parameters->options().output_is_position_independent()
+ && gsym != NULL
+ && gsym->needs_dynamic_reloc(reloc_property->reference_flags()))
+ // We have generated an absolute dynamic relocation, so do not
+ // apply the relocation statically. (Works around bugs in older
+ // Android dynamic linkers.)
+ break;
reloc_status = Reloc::template rela_ua<32>(
view, object, psymval, addend, reloc_property);
break;
case elfcpp::R_AARCH64_ABS16:
+ if (!parameters->options().apply_dynamic_relocs()
+ && parameters->options().output_is_position_independent()
+ && gsym != NULL
+ && gsym->needs_dynamic_reloc(reloc_property->reference_flags()))
+ // We have generated an absolute dynamic relocation, so do not
+ // apply the relocation statically. (Works around bugs in older
+ // Android dynamic linkers.)
+ break;
reloc_status = Reloc::template rela_ua<16>(
view, object, psymval, addend, reloc_property);
break;
case elfcpp::R_AARCH64_PREL64:
reloc_status = Reloc::template pcrela_ua<64>(
view, object, psymval, addend, address, reloc_property);
+ break;
case elfcpp::R_AARCH64_PREL32:
reloc_status = Reloc::template pcrela_ua<32>(
view, object, psymval, addend, address, reloc_property);
+ break;
case elfcpp::R_AARCH64_PREL16:
reloc_status = Reloc::template pcrela_ua<16>(
view, object, psymval, addend, address, reloc_property);
+ break;
+
+ case elfcpp::R_AARCH64_MOVW_UABS_G0:
+ case elfcpp::R_AARCH64_MOVW_UABS_G0_NC:
+ case elfcpp::R_AARCH64_MOVW_UABS_G1:
+ case elfcpp::R_AARCH64_MOVW_UABS_G1_NC:
+ case elfcpp::R_AARCH64_MOVW_UABS_G2:
+ case elfcpp::R_AARCH64_MOVW_UABS_G2_NC:
+ case elfcpp::R_AARCH64_MOVW_UABS_G3:
+ reloc_status = Reloc::template rela_general<32>(
+ view, object, psymval, addend, reloc_property);
+ break;
+ case elfcpp::R_AARCH64_MOVW_SABS_G0:
+ case elfcpp::R_AARCH64_MOVW_SABS_G1:
+ case elfcpp::R_AARCH64_MOVW_SABS_G2:
+ reloc_status = Reloc::movnz(view, psymval->value(object, addend),
+ reloc_property);
+ break;
+
+ case elfcpp::R_AARCH64_LD_PREL_LO19:
+ reloc_status = Reloc::template pcrela_general<32>(
+ view, object, psymval, addend, address, reloc_property);
+ break;
+
+ case elfcpp::R_AARCH64_ADR_PREL_LO21:
+ reloc_status = Reloc::adr(view, object, psymval, addend,
+ address, reloc_property);
+ break;
case elfcpp::R_AARCH64_ADR_PREL_PG_HI21_NC:
case elfcpp::R_AARCH64_ADR_PREL_PG_HI21:
// Return false to stop further processing this reloc.
return false;
}
- // Continue.
+ // Fall through.
+ case elfcpp::R_AARCH64_JUMP26:
+ if (Reloc::maybe_apply_stub(r_type, relinfo, rela, view, address,
+ gsym, psymval, object,
+ target->stub_group_size_))
+ break;
+ // Fall through.
case elfcpp::R_AARCH64_TSTBR14:
case elfcpp::R_AARCH64_CONDBR19:
- case elfcpp::R_AARCH64_JUMP26:
reloc_status = Reloc::template pcrela_general<32>(
view, object, psymval, addend, address, reloc_property);
break;
view, value, addend, reloc_property);
break;
+ case elfcpp::R_AARCH64_LD64_GOTPAGE_LO15:
+ {
+ gold_assert(have_got_offset);
+ value = target->got_->address() + got_base + got_offset + addend -
+ Reloc::Page(target->got_->address() + got_base);
+ if ((value & 7) != 0)
+ reloc_status = Reloc::STATUS_OVERFLOW;
+ else
+ reloc_status = Reloc::template reloc_common<32>(
+ view, value, reloc_property);
+ break;
+ }
+
case elfcpp::R_AARCH64_TLSGD_ADR_PAGE21:
case elfcpp::R_AARCH64_TLSGD_ADD_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLD_ADR_PAGE21:
+ case elfcpp::R_AARCH64_TLSLD_ADD_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLD_MOVW_DTPREL_G1:
+ case elfcpp::R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC:
+ case elfcpp::R_AARCH64_TLSLD_ADD_DTPREL_HI12:
+ case elfcpp::R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC:
case elfcpp::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
case elfcpp::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLE_MOVW_TPREL_G2:
+ case elfcpp::R_AARCH64_TLSLE_MOVW_TPREL_G1:
+ case elfcpp::R_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
+ case elfcpp::R_AARCH64_TLSLE_MOVW_TPREL_G0:
+ case elfcpp::R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_HI12:
case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_LO12:
case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLE_LDST8_TPREL_LO12:
+ case elfcpp::R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLE_LDST16_TPREL_LO12:
+ case elfcpp::R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLE_LDST32_TPREL_LO12:
+ case elfcpp::R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLE_LDST64_TPREL_LO12:
+ case elfcpp::R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
case elfcpp::R_AARCH64_TLSDESC_ADR_PAGE21:
case elfcpp::R_AARCH64_TLSDESC_LD64_LO12:
case elfcpp::R_AARCH64_TLSDESC_ADD_LO12:
template<int size, bool big_endian>
inline
-typename AArch64_relocate_functions<size,big_endian>::Status
+typename AArch64_relocate_functions<size, big_endian>::Status
Target_aarch64<size, big_endian>::Relocate::relocate_tls(
- const Relocate_info<size,big_endian>* relinfo,
+ const Relocate_info<size, big_endian>* relinfo,
Target_aarch64<size, big_endian>* target,
size_t relnum,
const elfcpp::Rela<size, big_endian>& rela,
unsigned char* view,
typename elfcpp::Elf_types<size>::Elf_Addr address)
{
- typedef AArch64_relocate_functions<size,big_endian> aarch64_reloc_funcs;
+ typedef AArch64_relocate_functions<size, big_endian> aarch64_reloc_funcs;
typedef typename elfcpp::Elf_types<size>::Elf_Addr AArch64_address;
Output_segment* tls_segment = relinfo->layout->tls_segment();
tls::Tls_optimization tlsopt = Target_aarch64<size, big_endian>::
optimize_tls_reloc(is_final, r_type);
- Sized_relobj_file<size,big_endian>* object = relinfo->object;
+ Sized_relobj_file<size, big_endian>* object = relinfo->object;
int tls_got_offset_type;
switch (r_type)
{
return aarch64_reloc_funcs::adrp(
view, got_entry_address + addend, address);
- break;
+ break;
+
+ case elfcpp::R_AARCH64_TLSGD_ADD_LO12_NC:
+ return aarch64_reloc_funcs::template rela_general<32>(
+ view, got_entry_address, addend, reloc_property);
+ break;
+
+ default:
+ gold_unreachable();
+ }
+ }
+ gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
+ _("unsupported gd_to_ie relaxation on %u"),
+ r_type);
+ }
+ break;
+
+ case elfcpp::R_AARCH64_TLSLD_ADR_PAGE21:
+ case elfcpp::R_AARCH64_TLSLD_ADD_LO12_NC: // Local-dynamic
+ {
+ if (tlsopt == tls::TLSOPT_TO_LE)
+ {
+ if (tls_segment == NULL)
+ {
+ gold_assert(parameters->errors()->error_count() > 0
+ || issue_undefined_symbol_error(gsym));
+ return aarch64_reloc_funcs::STATUS_BAD_RELOC;
+ }
+ return this->tls_ld_to_le(relinfo, target, rela, r_type, view,
+ psymval);
+ }
+
+ gold_assert(tlsopt == tls::TLSOPT_NONE);
+ // Relocate the field with the offset of the GOT entry for
+ // the module index.
+ typename elfcpp::Elf_types<size>::Elf_Addr got_entry_address;
+ got_entry_address = (target->got_mod_index_entry(NULL, NULL, NULL) +
+ target->got_->address());
+
+ switch (r_type)
+ {
+ case elfcpp::R_AARCH64_TLSLD_ADR_PAGE21:
+ return aarch64_reloc_funcs::adrp(
+ view, got_entry_address + addend, address);
+ break;
+
+ case elfcpp::R_AARCH64_TLSLD_ADD_LO12_NC:
+ return aarch64_reloc_funcs::template rela_general<32>(
+ view, got_entry_address, addend, reloc_property);
+ break;
- case elfcpp::R_AARCH64_TLSGD_ADD_LO12_NC:
- return aarch64_reloc_funcs::template rela_general<32>(
- view, got_entry_address, addend, reloc_property);
- break;
+ default:
+ gold_unreachable();
+ }
+ }
+ break;
- default:
- gold_assert(false);
+ case elfcpp::R_AARCH64_TLSLD_MOVW_DTPREL_G1:
+ case elfcpp::R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC:
+ case elfcpp::R_AARCH64_TLSLD_ADD_DTPREL_HI12:
+ case elfcpp::R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC: // Other local-dynamic
+ {
+ AArch64_address value = psymval->value(object, 0);
+ if (tlsopt == tls::TLSOPT_TO_LE)
+ {
+ if (tls_segment == NULL)
+ {
+ gold_assert(parameters->errors()->error_count() > 0
+ || issue_undefined_symbol_error(gsym));
+ return aarch64_reloc_funcs::STATUS_BAD_RELOC;
}
}
- gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
- _("unsupported gd_to_ie relaxation on %u"),
- r_type);
+ switch (r_type)
+ {
+ case elfcpp::R_AARCH64_TLSLD_MOVW_DTPREL_G1:
+ return aarch64_reloc_funcs::movnz(view, value + addend,
+ reloc_property);
+ break;
+
+ case elfcpp::R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC:
+ case elfcpp::R_AARCH64_TLSLD_ADD_DTPREL_HI12:
+ case elfcpp::R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC:
+ return aarch64_reloc_funcs::template rela_general<32>(
+ view, value, addend, reloc_property);
+ break;
+
+ default:
+ gold_unreachable();
+ }
+ // We should never reach here.
}
break;
return aarch64_reloc_funcs::template rela_general<32>(
view, got_entry_address, addend, reloc_property);
default:
- gold_assert(false);
+ gold_unreachable();
}
}
// We shall never reach here.
break;
+ case elfcpp::R_AARCH64_TLSLE_MOVW_TPREL_G2:
+ case elfcpp::R_AARCH64_TLSLE_MOVW_TPREL_G1:
+ case elfcpp::R_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
+ case elfcpp::R_AARCH64_TLSLE_MOVW_TPREL_G0:
+ case elfcpp::R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_HI12:
case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_LO12:
case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLE_LDST8_TPREL_LO12:
+ case elfcpp::R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLE_LDST16_TPREL_LO12:
+ case elfcpp::R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLE_LDST32_TPREL_LO12:
+ case elfcpp::R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLE_LDST64_TPREL_LO12:
+ case elfcpp::R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
{
gold_assert(tls_segment != NULL);
AArch64_address value = psymval->value(object, 0);
AArch64_address aligned_tcb_size =
align_address(target->tcb_size(),
tls_segment->maximum_alignment());
- return aarch64_reloc_funcs::template
- rela_general<32>(view,
- value + aligned_tcb_size,
- addend,
- reloc_property);
+ value += aligned_tcb_size;
+ switch (r_type)
+ {
+ case elfcpp::R_AARCH64_TLSLE_MOVW_TPREL_G2:
+ case elfcpp::R_AARCH64_TLSLE_MOVW_TPREL_G1:
+ case elfcpp::R_AARCH64_TLSLE_MOVW_TPREL_G0:
+ return aarch64_reloc_funcs::movnz(view, value + addend,
+ reloc_property);
+ default:
+ return aarch64_reloc_funcs::template
+ rela_general<32>(view,
+ value,
+ addend,
+ reloc_property);
+ }
}
else
gold_error(_("%s: unsupported reloc %u "
tls_got_offset_type = (tlsopt == tls::TLSOPT_TO_IE
? GOT_TYPE_TLS_OFFSET
: GOT_TYPE_TLS_DESC);
- unsigned int got_tlsdesc_offset = 0;
+ int got_tlsdesc_offset = 0;
if (r_type != elfcpp::R_AARCH64_TLSDESC_CALL
&& tlsopt == tls::TLSOPT_NONE)
{
// We created GOT entries in the .got.tlsdesc portion of the
// .got.plt section, but the offset stored in the symbol is the
// offset within .got.tlsdesc.
- got_tlsdesc_offset = (target->got_->data_size()
- + target->got_plt_section()->data_size());
+ got_tlsdesc_offset = (target->got_tlsdesc_->address()
+ - target->got_->address());
}
typename elfcpp::Elf_types<size>::Elf_Addr got_entry_address;
if (gsym != NULL)
}
if (tlsopt == tls::TLSOPT_TO_IE)
{
- if (tls_segment == NULL)
- {
- gold_assert(parameters->errors()->error_count() > 0
- || issue_undefined_symbol_error(gsym));
- return aarch64_reloc_funcs::STATUS_BAD_RELOC;
- }
return tls_desc_gd_to_ie(relinfo, target, rela, r_type,
view, psymval, got_entry_address,
address);
template<int size, bool big_endian>
inline
-typename AArch64_relocate_functions<size,big_endian>::Status
+typename AArch64_relocate_functions<size, big_endian>::Status
Target_aarch64<size, big_endian>::Relocate::tls_gd_to_le(
- const Relocate_info<size,big_endian>* relinfo,
+ const Relocate_info<size, big_endian>* relinfo,
Target_aarch64<size, big_endian>* target,
const elfcpp::Rela<size, big_endian>& rela,
unsigned int r_type,
unsigned char* view,
const Symbol_value<size>* psymval)
{
- typedef AArch64_relocate_functions<size,big_endian> aarch64_reloc_funcs;
+ typedef AArch64_relocate_functions<size, big_endian> aarch64_reloc_funcs;
typedef typename elfcpp::Swap<32, big_endian>::Valtype Insntype;
typedef typename elfcpp::Elf_types<size>::Elf_Addr AArch64_address;
template<int size, bool big_endian>
inline
-typename AArch64_relocate_functions<size,big_endian>::Status
+typename AArch64_relocate_functions<size, big_endian>::Status
+Target_aarch64<size, big_endian>::Relocate::tls_ld_to_le(
+ const Relocate_info<size, big_endian>* relinfo,
+ Target_aarch64<size, big_endian>* target,
+ const elfcpp::Rela<size, big_endian>& rela,
+ unsigned int r_type,
+ unsigned char* view,
+ const Symbol_value<size>* psymval)
+{
+ typedef AArch64_relocate_functions<size, big_endian> aarch64_reloc_funcs;
+ typedef typename elfcpp::Swap<32, big_endian>::Valtype Insntype;
+ typedef typename elfcpp::Elf_types<size>::Elf_Addr AArch64_address;
+
+ Insntype* ip = reinterpret_cast<Insntype*>(view);
+ Insntype insn1 = elfcpp::Swap<32, big_endian>::readval(ip);
+ Insntype insn2 = elfcpp::Swap<32, big_endian>::readval(ip + 1);
+ Insntype insn3 = elfcpp::Swap<32, big_endian>::readval(ip + 2);
+
+ if (r_type == elfcpp::R_AARCH64_TLSLD_ADD_LO12_NC)
+ {
+ // This is the 2nd relocs, optimization should already have been
+ // done.
+ gold_assert((insn1 & 0xfff00000) == 0x91400000);
+ return aarch64_reloc_funcs::STATUS_OKAY;
+ }
+
+ // The original sequence is -
+ // 90000000 adrp x0, 0 <main>
+ // 91000000 add x0, x0, #0x0
+ // 94000000 bl 0 <__tls_get_addr>
+ // optimized to sequence -
+ // d53bd040 mrs x0, tpidr_el0
+ // 91400000 add x0, x0, #0x0, lsl #12
+ // 91000000 add x0, x0, #0x0
+
+ // Unlike tls_ie_to_le, we change the 3 insns in one function call when we
+ // encounter the first relocation "R_AARCH64_TLSLD_ADR_PAGE21". Because we
+ // have to change "bl tls_get_addr", which does not have a corresponding tls
+ // relocation type. So before proceeding, we need to make sure compiler
+ // does not change the sequence.
+ if(!(insn1 == 0x90000000 // adrp x0,0
+ && insn2 == 0x91000000 // add x0, x0, #0x0
+ && insn3 == 0x94000000)) // bl 0
+ {
+ // Ideally we should give up gd_to_le relaxation and do gd access.
+ // However the gd_to_le relaxation decision has been made early
+ // in the scan stage, where we did not allocate a GOT entry for
+ // this symbol. Therefore we have to exit and report an error now.
+ gold_error(_("unexpected reloc insn sequence while relaxing "
+ "tls gd to le for reloc %u."), r_type);
+ return aarch64_reloc_funcs::STATUS_BAD_RELOC;
+ }
+
+ // Write new insns.
+ insn1 = 0xd53bd040; // mrs x0, tpidr_el0
+ insn2 = 0x91400000; // add x0, x0, #0x0, lsl #12
+ insn3 = 0x91000000; // add x0, x0, #0x0
+ elfcpp::Swap<32, big_endian>::writeval(ip, insn1);
+ elfcpp::Swap<32, big_endian>::writeval(ip + 1, insn2);
+ elfcpp::Swap<32, big_endian>::writeval(ip + 2, insn3);
+
+ // Calculate tprel value.
+ Output_segment* tls_segment = relinfo->layout->tls_segment();
+ gold_assert(tls_segment != NULL);
+ AArch64_address value = psymval->value(relinfo->object, 0);
+ const elfcpp::Elf_Xword addend = rela.get_r_addend();
+ AArch64_address aligned_tcb_size =
+ align_address(target->tcb_size(), tls_segment->maximum_alignment());
+ AArch64_address x = value + aligned_tcb_size;
+
+ // After new insns are written, apply TLSLE relocs.
+ const AArch64_reloc_property* rp1 =
+ aarch64_reloc_property_table->get_reloc_property(
+ elfcpp::R_AARCH64_TLSLE_ADD_TPREL_HI12);
+ const AArch64_reloc_property* rp2 =
+ aarch64_reloc_property_table->get_reloc_property(
+ elfcpp::R_AARCH64_TLSLE_ADD_TPREL_LO12);
+ gold_assert(rp1 != NULL && rp2 != NULL);
+
+ typename aarch64_reloc_funcs::Status s1 =
+ aarch64_reloc_funcs::template rela_general<32>(view + 4,
+ x,
+ addend,
+ rp1);
+ if (s1 != aarch64_reloc_funcs::STATUS_OKAY)
+ return s1;
+
+ typename aarch64_reloc_funcs::Status s2 =
+ aarch64_reloc_funcs::template rela_general<32>(view + 8,
+ x,
+ addend,
+ rp2);
+
+ this->skip_call_tls_get_addr_ = true;
+ return s2;
+
+} // End of tls_ld_to_le
+
+template<int size, bool big_endian>
+inline
+typename AArch64_relocate_functions<size, big_endian>::Status
Target_aarch64<size, big_endian>::Relocate::tls_ie_to_le(
- const Relocate_info<size,big_endian>* relinfo,
+ const Relocate_info<size, big_endian>* relinfo,
Target_aarch64<size, big_endian>* target,
const elfcpp::Rela<size, big_endian>& rela,
unsigned int r_type,
{
typedef typename elfcpp::Elf_types<size>::Elf_Addr AArch64_address;
typedef typename elfcpp::Swap<32, big_endian>::Valtype Insntype;
- typedef AArch64_relocate_functions<size,big_endian> aarch64_reloc_funcs;
+ typedef AArch64_relocate_functions<size, big_endian> aarch64_reloc_funcs;
AArch64_address value = psymval->value(relinfo->object, 0);
Output_segment* tls_segment = relinfo->layout->tls_segment();
newinsn = (0xf2800000 | regno) | ((x & 0xffff) << 5);
}
else
- gold_assert(false);
+ gold_unreachable();
elfcpp::Swap<32, big_endian>::writeval(ip, newinsn);
return aarch64_reloc_funcs::STATUS_OKAY;
template<int size, bool big_endian>
inline
-typename AArch64_relocate_functions<size,big_endian>::Status
+typename AArch64_relocate_functions<size, big_endian>::Status
Target_aarch64<size, big_endian>::Relocate::tls_desc_gd_to_le(
- const Relocate_info<size,big_endian>* relinfo,
+ const Relocate_info<size, big_endian>* relinfo,
Target_aarch64<size, big_endian>* target,
const elfcpp::Rela<size, big_endian>& rela,
unsigned int r_type,
{
typedef typename elfcpp::Elf_types<size>::Elf_Addr AArch64_address;
typedef typename elfcpp::Swap<32, big_endian>::Valtype Insntype;
- typedef AArch64_relocate_functions<size,big_endian> aarch64_reloc_funcs;
+ typedef AArch64_relocate_functions<size, big_endian> aarch64_reloc_funcs;
// TLSDESC-GD sequence is like:
// adrp x0, :tlsdesc:v1
template<int size, bool big_endian>
inline
-typename AArch64_relocate_functions<size,big_endian>::Status
+typename AArch64_relocate_functions<size, big_endian>::Status
Target_aarch64<size, big_endian>::Relocate::tls_desc_gd_to_ie(
- const Relocate_info<size,big_endian>* /* relinfo */,
+ const Relocate_info<size, big_endian>* /* relinfo */,
Target_aarch64<size, big_endian>* /* target */,
const elfcpp::Rela<size, big_endian>& rela,
unsigned int r_type,
typename elfcpp::Elf_types<size>::Elf_Addr address)
{
typedef typename elfcpp::Swap<32, big_endian>::Valtype Insntype;
- typedef AArch64_relocate_functions<size,big_endian> aarch64_reloc_funcs;
+ typedef AArch64_relocate_functions<size, big_endian> aarch64_reloc_funcs;
// TLSDESC-GD sequence is like:
// adrp x0, :tlsdesc:v1
case elfcpp::R_AARCH64_TLSDESC_LD64_LO12:
{
+ // Set ldr target register to be x0.
+ Insntype insn = elfcpp::Swap<32, big_endian>::readval(ip);
+ insn &= 0xffffffe0;
+ elfcpp::Swap<32, big_endian>::writeval(ip, insn);
+ // Do relocation.
const AArch64_reloc_property* reloc_property =
aarch64_reloc_property_table->get_reloc_property(
elfcpp::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC);
section_size_type view_size,
const Reloc_symbol_changes* reloc_symbol_changes)
{
- gold_assert(sh_type == elfcpp::SHT_RELA);
+ typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
+ typedef Target_aarch64<size, big_endian> Aarch64;
typedef typename Target_aarch64<size, big_endian>::Relocate AArch64_relocate;
- gold::relocate_section<size, big_endian, Target_aarch64, elfcpp::SHT_RELA,
- AArch64_relocate, gold::Default_comdat_behavior>(
+ typedef gold::Default_classify_reloc<elfcpp::SHT_RELA, size, big_endian>
+ Classify_reloc;
+
+ gold_assert(sh_type == elfcpp::SHT_RELA);
+
+ // See if we are relocating a relaxed input section. If so, the view
+ // covers the whole output section and we need to adjust accordingly.
+ if (needs_special_offset_handling)
+ {
+ const Output_relaxed_input_section* poris =
+ output_section->find_relaxed_input_section(relinfo->object,
+ relinfo->data_shndx);
+ if (poris != NULL)
+ {
+ Address section_address = poris->address();
+ section_size_type section_size = poris->data_size();
+
+ gold_assert((section_address >= address)
+ && ((section_address + section_size)
+ <= (address + view_size)));
+
+ off_t offset = section_address - address;
+ view += offset;
+ address += offset;
+ view_size = section_size;
+ }
+ }
+
+ gold::relocate_section<size, big_endian, Aarch64, AArch64_relocate,
+ gold::Default_comdat_behavior, Classify_reloc>(
relinfo,
this,
prelocs,
reloc_symbol_changes);
}
-// Return the size of a relocation while scanning during a relocatable
-// link.
-
-template<int size, bool big_endian>
-unsigned int
-Target_aarch64<size, big_endian>::Relocatable_size_for_reloc::
-get_size_for_reloc(
- unsigned int ,
- Relobj* )
-{
- // We will never support SHT_REL relocations.
- gold_unreachable();
- return 0;
-}
-
// Scan the relocs during a relocatable link.
template<int size, bool big_endian>
const unsigned char* plocal_symbols,
Relocatable_relocs* rr)
{
- gold_assert(sh_type == elfcpp::SHT_RELA);
+ typedef gold::Default_classify_reloc<elfcpp::SHT_RELA, size, big_endian>
+ Classify_reloc;
+ typedef gold::Default_scan_relocatable_relocs<Classify_reloc>
+ Scan_relocatable_relocs;
- typedef gold::Default_scan_relocatable_relocs<elfcpp::SHT_RELA,
- Relocatable_size_for_reloc> Scan_relocatable_relocs;
+ gold_assert(sh_type == elfcpp::SHT_RELA);
- gold::scan_relocatable_relocs<size, big_endian, elfcpp::SHT_RELA,
- Scan_relocatable_relocs>(
+ gold::scan_relocatable_relocs<size, big_endian, Scan_relocatable_relocs>(
symtab,
layout,
object,
rr);
}
+// Scan the relocs for --emit-relocs.
+
+template<int size, bool big_endian>
+void
+Target_aarch64<size, big_endian>::emit_relocs_scan(
+ Symbol_table* symtab,
+ Layout* layout,
+ Sized_relobj_file<size, 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<elfcpp::SHT_RELA, size, big_endian>
+ Classify_reloc;
+ typedef gold::Default_emit_relocs_strategy<Classify_reloc>
+ Emit_relocs_strategy;
+
+ gold_assert(sh_type == elfcpp::SHT_RELA);
+
+ gold::scan_relocatable_relocs<size, 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);
+}
+
// Relocate a section during a relocatable link.
template<int size, bool big_endian>
size_t reloc_count,
Output_section* output_section,
typename elfcpp::Elf_types<size>::Elf_Off offset_in_output_section,
- const Relocatable_relocs* rr,
unsigned char* view,
typename elfcpp::Elf_types<size>::Elf_Addr view_address,
section_size_type view_size,
unsigned char* reloc_view,
section_size_type reloc_view_size)
{
+ typedef gold::Default_classify_reloc<elfcpp::SHT_RELA, size, big_endian>
+ Classify_reloc;
+
gold_assert(sh_type == elfcpp::SHT_RELA);
- gold::relocate_relocs<size, big_endian, elfcpp::SHT_RELA>(
+ gold::relocate_relocs<size, big_endian, Classify_reloc>(
relinfo,
prelocs,
reloc_count,
output_section,
offset_in_output_section,
- rr,
view,
view_address,
view_size,
reloc_view_size);
}
+
+// Return whether this is a 3-insn erratum sequence.
+
+template<int size, bool big_endian>
+bool
+Target_aarch64<size, big_endian>::is_erratum_843419_sequence(
+ typename elfcpp::Swap<32,big_endian>::Valtype insn1,
+ typename elfcpp::Swap<32,big_endian>::Valtype insn2,
+ typename elfcpp::Swap<32,big_endian>::Valtype insn3)
+{
+ unsigned rt1, rt2;
+ bool load, pair;
+
+ // The 2nd insn is a single register load or store; or register pair
+ // store.
+ if (Insn_utilities::aarch64_mem_op_p(insn2, &rt1, &rt2, &pair, &load)
+ && (!pair || (pair && !load)))
+ {
+ // The 3rd insn is a load or store instruction from the "Load/store
+ // register (unsigned immediate)" encoding class, using Rn as the
+ // base address register.
+ if (Insn_utilities::aarch64_ldst_uimm(insn3)
+ && (Insn_utilities::aarch64_rn(insn3)
+ == Insn_utilities::aarch64_rd(insn1)))
+ return true;
+ }
+ return false;
+}
+
+
+// Return whether this is a 835769 sequence.
+// (Similarly implemented as in elfnn-aarch64.c.)
+
+template<int size, bool big_endian>
+bool
+Target_aarch64<size, big_endian>::is_erratum_835769_sequence(
+ typename elfcpp::Swap<32,big_endian>::Valtype insn1,
+ typename elfcpp::Swap<32,big_endian>::Valtype insn2)
+{
+ uint32_t rt;
+ uint32_t rt2 = 0;
+ uint32_t rn;
+ uint32_t rm;
+ uint32_t ra;
+ bool pair;
+ bool load;
+
+ if (Insn_utilities::aarch64_mlxl(insn2)
+ && Insn_utilities::aarch64_mem_op_p (insn1, &rt, &rt2, &pair, &load))
+ {
+ /* Any SIMD memory op is independent of the subsequent MLA
+ by definition of the erratum. */
+ if (Insn_utilities::aarch64_bit(insn1, 26))
+ return true;
+
+ /* If not SIMD, check for integer memory ops and MLA relationship. */
+ rn = Insn_utilities::aarch64_rn(insn2);
+ ra = Insn_utilities::aarch64_ra(insn2);
+ rm = Insn_utilities::aarch64_rm(insn2);
+
+ /* If this is a load and there's a true(RAW) dependency, we are safe
+ and this is not an erratum sequence. */
+ if (load &&
+ (rt == rn || rt == rm || rt == ra
+ || (pair && (rt2 == rn || rt2 == rm || rt2 == ra))))
+ return false;
+
+ /* We conservatively put out stubs for all other cases (including
+ writebacks). */
+ return true;
+ }
+
+ return false;
+}
+
+
+// Helper method to create erratum stub for ST_E_843419 and ST_E_835769.
+
+template<int size, bool big_endian>
+void
+Target_aarch64<size, big_endian>::create_erratum_stub(
+ AArch64_relobj<size, big_endian>* relobj,
+ unsigned int shndx,
+ section_size_type erratum_insn_offset,
+ Address erratum_address,
+ typename Insn_utilities::Insntype erratum_insn,
+ int erratum_type,
+ unsigned int e843419_adrp_offset)
+{
+ gold_assert(erratum_type == ST_E_843419 || erratum_type == ST_E_835769);
+ The_stub_table* stub_table = relobj->stub_table(shndx);
+ gold_assert(stub_table != NULL);
+ if (stub_table->find_erratum_stub(relobj,
+ shndx,
+ erratum_insn_offset) == NULL)
+ {
+ const int BPI = AArch64_insn_utilities<big_endian>::BYTES_PER_INSN;
+ The_erratum_stub* stub;
+ if (erratum_type == ST_E_835769)
+ stub = new The_erratum_stub(relobj, erratum_type, shndx,
+ erratum_insn_offset);
+ else if (erratum_type == ST_E_843419)
+ stub = new E843419_stub<size, big_endian>(
+ relobj, shndx, erratum_insn_offset, e843419_adrp_offset);
+ else
+ gold_unreachable();
+ stub->set_erratum_insn(erratum_insn);
+ stub->set_erratum_address(erratum_address);
+ // For erratum ST_E_843419 and ST_E_835769, the destination address is
+ // always the next insn after erratum insn.
+ stub->set_destination_address(erratum_address + BPI);
+ stub_table->add_erratum_stub(stub);
+ }
+}
+
+
+// Scan erratum for section SHNDX range [output_address + span_start,
+// output_address + span_end). Note here we do not share the code with
+// scan_erratum_843419_span function, because for 843419 we optimize by only
+// scanning the last few insns of a page, whereas for 835769, we need to scan
+// every insn.
+
+template<int size, bool big_endian>
+void
+Target_aarch64<size, big_endian>::scan_erratum_835769_span(
+ AArch64_relobj<size, big_endian>* relobj,
+ unsigned int shndx,
+ const section_size_type span_start,
+ const section_size_type span_end,
+ unsigned char* input_view,
+ Address output_address)
+{
+ typedef typename Insn_utilities::Insntype Insntype;
+
+ const int BPI = AArch64_insn_utilities<big_endian>::BYTES_PER_INSN;
+
+ // Adjust output_address and view to the start of span.
+ output_address += span_start;
+ input_view += span_start;
+
+ section_size_type span_length = span_end - span_start;
+ section_size_type offset = 0;
+ for (offset = 0; offset + BPI < span_length; offset += BPI)
+ {
+ Insntype* ip = reinterpret_cast<Insntype*>(input_view + offset);
+ Insntype insn1 = ip[0];
+ Insntype insn2 = ip[1];
+ if (is_erratum_835769_sequence(insn1, insn2))
+ {
+ Insntype erratum_insn = insn2;
+ // "span_start + offset" is the offset for insn1. So for insn2, it is
+ // "span_start + offset + BPI".
+ section_size_type erratum_insn_offset = span_start + offset + BPI;
+ Address erratum_address = output_address + offset + BPI;
+ gold_info(_("Erratum 835769 found and fixed at \"%s\", "
+ "section %d, offset 0x%08x."),
+ relobj->name().c_str(), shndx,
+ (unsigned int)(span_start + offset));
+
+ this->create_erratum_stub(relobj, shndx,
+ erratum_insn_offset, erratum_address,
+ erratum_insn, ST_E_835769);
+ offset += BPI; // Skip mac insn.
+ }
+ }
+} // End of "Target_aarch64::scan_erratum_835769_span".
+
+
+// Scan erratum for section SHNDX range
+// [output_address + span_start, output_address + span_end).
+
+template<int size, bool big_endian>
+void
+Target_aarch64<size, big_endian>::scan_erratum_843419_span(
+ AArch64_relobj<size, big_endian>* relobj,
+ unsigned int shndx,
+ const section_size_type span_start,
+ const section_size_type span_end,
+ unsigned char* input_view,
+ Address output_address)
+{
+ typedef typename Insn_utilities::Insntype Insntype;
+
+ // Adjust output_address and view to the start of span.
+ output_address += span_start;
+ input_view += span_start;
+
+ if ((output_address & 0x03) != 0)
+ return;
+
+ section_size_type offset = 0;
+ section_size_type span_length = span_end - span_start;
+ // The first instruction must be ending at 0xFF8 or 0xFFC.
+ unsigned int page_offset = output_address & 0xFFF;
+ // Make sure starting position, that is "output_address+offset",
+ // starts at page position 0xff8 or 0xffc.
+ if (page_offset < 0xff8)
+ offset = 0xff8 - page_offset;
+ while (offset + 3 * Insn_utilities::BYTES_PER_INSN <= span_length)
+ {
+ Insntype* ip = reinterpret_cast<Insntype*>(input_view + offset);
+ Insntype insn1 = ip[0];
+ if (Insn_utilities::is_adrp(insn1))
+ {
+ Insntype insn2 = ip[1];
+ Insntype insn3 = ip[2];
+ Insntype erratum_insn;
+ unsigned insn_offset;
+ bool do_report = false;
+ if (is_erratum_843419_sequence(insn1, insn2, insn3))
+ {
+ do_report = true;
+ erratum_insn = insn3;
+ insn_offset = 2 * Insn_utilities::BYTES_PER_INSN;
+ }
+ else if (offset + 4 * Insn_utilities::BYTES_PER_INSN <= span_length)
+ {
+ // Optionally we can have an insn between ins2 and ins3
+ Insntype insn_opt = ip[2];
+ // And insn_opt must not be a branch.
+ if (!Insn_utilities::aarch64_b(insn_opt)
+ && !Insn_utilities::aarch64_bl(insn_opt)
+ && !Insn_utilities::aarch64_blr(insn_opt)
+ && !Insn_utilities::aarch64_br(insn_opt))
+ {
+ // And insn_opt must not write to dest reg in insn1. However
+ // we do a conservative scan, which means we may fix/report
+ // more than necessary, but it doesn't hurt.
+
+ Insntype insn4 = ip[3];
+ if (is_erratum_843419_sequence(insn1, insn2, insn4))
+ {
+ do_report = true;
+ erratum_insn = insn4;
+ insn_offset = 3 * Insn_utilities::BYTES_PER_INSN;
+ }
+ }
+ }
+ if (do_report)
+ {
+ unsigned int erratum_insn_offset =
+ span_start + offset + insn_offset;
+ Address erratum_address =
+ output_address + offset + insn_offset;
+ create_erratum_stub(relobj, shndx,
+ erratum_insn_offset, erratum_address,
+ erratum_insn, ST_E_843419,
+ span_start + offset);
+ }
+ }
+
+ // Advance to next candidate instruction. We only consider instruction
+ // sequences starting at a page offset of 0xff8 or 0xffc.
+ page_offset = (output_address + offset) & 0xfff;
+ if (page_offset == 0xff8)
+ offset += 4;
+ else // (page_offset == 0xffc), we move to next page's 0xff8.
+ offset += 0xffc;
+ }
+} // End of "Target_aarch64::scan_erratum_843419_span".
+
+
// The selector for aarch64 object files.
template<int size, bool big_endian>