// 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"
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>
class AArch64_output_section;
-// Reloc stub class.
-
template<int size, bool big_endian>
-class Reloc_stub
+class AArch64_relobj;
+
+
+// Stub type enum constants.
+
+enum
{
- public:
- typedef Reloc_stub<size, big_endian> This;
- typedef typename elfcpp::Elf_types<size>::Elf_Addr AArch64_address;
+ ST_NONE = 0,
- // Do not change the value of the enums, they are used to index into
- // stub_insns array.
- typedef 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 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-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,
- // 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_type;
+ // Stub for erratum 835769 handling.
+ ST_E_835769 = 5,
- // 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);
+ // Number of total stub types.
+ ST_NUMBER = 6
+};
- // 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 = 4;
+// Struct that wraps insns for a particular stub. All stub templates are
+// created/initialized as constants by Stub_template_repertoire.
- // 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; }
+template<bool big_endian>
+struct Stub_template
+{
+ const typename AArch64_insn_utilities<big_endian>::Insntype* insns;
+ const int insn_num;
+};
- // Determine whether the offset fits in the adrp immediate field.
- static bool
- aarch64_valid_for_adrp_p(AArch64_address location, AArch64_address dest)
+
+// 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)
{
- 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;
+ static Stub_template_repertoire<big_endian> singleton;
+ return singleton.stub_templates_[type];
}
- // Determine the stub type for a certain relocation or ST_NONE, if no stub is
- // needed.
- static Stub_type
- stub_type_for_reloc(unsigned int r_type, AArch64_address address,
- AArch64_address target);
-
- Reloc_stub(Stub_type stub_type)
- : stub_type_(stub_type), offset_(invalid_offset),
- destination_address_(invalid_address)
+private:
+ // Constructor - creates/initializes all stub templates.
+ Stub_template_repertoire();
+ ~Stub_template_repertoire()
{ }
- ~Reloc_stub()
- { }
+ // Disallowing copy ctor and copy assignment operator.
+ Stub_template_repertoire(Stub_template_repertoire&);
+ Stub_template_repertoire& operator=(Stub_template_repertoire&);
- // Return offset of code stub from beginning of its containing stub table.
- section_offset_type
- offset() const
+ // 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
{
- gold_assert(this->offset_ != invalid_offset);
- return this->offset_;
+ return Stub_template_repertoire<big_endian>::
+ get_stub_template(this->type());
}
- // Set offset of code stub from beginning of its containing stub table.
- void
- set_offset(section_offset_type offset)
- { this->offset_ = offset; }
-
- // Return destination address.
+ // Get destination address.
AArch64_address
destination_address() const
{
reset_destination_address()
{ this->destination_address_ = this->invalid_address; }
- // Return the stub type.
- Stub_type
- stub_type() const
- { return stub_type_; }
+ // 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_;
+ }
- // Return the stub size.
- uint32_t
- stub_size() const
- { return this->stub_insn_number() * BYTES_PER_INSN; }
+ // Set stub offset.
+ void
+ set_offset(section_offset_type offset)
+ { this->offset_ = offset; }
- // Return the instruction number of this stub instance.
- int
- stub_insn_number() const
- { return stub_insns_[this->stub_type_][0]; }
+ // Return the stub insn.
+ const Insntype*
+ insns() const
+ { return this->stub_template()->insns; }
- // Note the first "insn" is the number of total insns in this array.
- const uint32_t*
- stub_insns() const
- { return stub_insns_[this->stub_type_]; }
+ // 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(Stub_type stub_type, const Symbol* symbol, const Relobj* relobj,
+ Key(int type, const Symbol* symbol, const Relobj* relobj,
unsigned int r_sym, int32_t addend)
- : stub_type_(stub_type), addend_(addend)
+ : type_(type), addend_(addend)
{
if (symbol != NULL)
{
{ }
// Return stub type.
- Stub_type
- stub_type() const
- { return this->stub_type_; }
+ int
+ type() const
+ { return this->type_; }
// Return the local symbol index or invalid_index.
unsigned int
bool
eq(const Key& k) const
{
- return ((this->stub_type_ == k.stub_type_)
+ 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_.relobj->name().c_str()
: this->u_.symbol->name());
// We only have 4 stub types.
- size_t stub_type_hash_value = 0x03 & this->stub_type_;
+ size_t stub_type_hash_value = 0x03 & this->type_;
return (name_hash_value
^ stub_type_hash_value
^ ((this->r_sym_ & 0x3fff) << 2)
{ return k1.eq(k2); }
};
- private:
+ private:
// Stub type.
- const Stub_type 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_;
do_write(unsigned char*, section_size_type);
private:
- static const section_offset_type invalid_offset =
- static_cast<section_offset_type>(-1);
static const unsigned int invalid_index = static_cast<unsigned int>(-1);
- static const AArch64_address invalid_address =
- static_cast<AArch64_address>(-1);
-
- static const uint32_t stub_insns_[][10];
-
- const Stub_type stub_type_;
- section_offset_type offset_;
- AArch64_address destination_address_;
}; // 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.
do_write(unsigned char* view, section_size_type)
{
typedef typename elfcpp::Swap<32, big_endian>::Valtype Insntype;
- const uint32_t* insns = this->stub_insns();
- uint32_t num_insns = this->stub_insn_number();
+ const uint32_t* insns = this->insns();
+ uint32_t num_insns = this->insn_num();
Insntype* ip = reinterpret_cast<Insntype*>(view);
- for (uint32_t i = 1; i <= num_insns; ++i)
- elfcpp::Swap<32, big_endian>::writeval(ip + i - 1, insns[i]);
+ for (uint32_t i = 0; i < num_insns; ++i)
+ elfcpp::Swap<32, big_endian>::writeval(ip + i, insns[i]);
}
-// Stubs instructions definition.
-
-template<int size, bool big_endian>
-const uint32_t
-Reloc_stub<size, big_endian>::stub_insns_[][10] =
- {
- // The first element of each group is the num of the insns.
-
- // ST_NONE
- {0, 0},
-
- // ST_ADRP_BRANCH
- {
- 4,
- 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 */
- },
-
- // ST_LONG_BRANCH_ABS
- {
- 4,
- 0x58000050, /* ldr ip0, 0x8 */
- 0xd61f0200, /* br ip0 */
- 0x00000000, /* address field */
- 0x00000000, /* address fields */
- },
-
- // ST_LONG_BRANCH_PCREL
- {
- 8,
- 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 */
- }
- };
-
-
// Determine the stub type for a certain relocation or ST_NONE, if no stub is
// needed.
template<int size, bool big_endian>
-inline
-typename Reloc_stub<size, big_endian>::Stub_type
+inline int
Reloc_stub<size, big_endian>::stub_type_for_reloc(
unsigned int r_type, AArch64_address location, AArch64_address dest)
{
branch_offset = dest - location;
break;
default:
- gold_assert(false);
+ gold_unreachable();
}
if (aarch64_valid_branch_offset_p(branch_offset))
if (aarch64_valid_for_adrp_p(location, dest))
return ST_ADRP_BRANCH;
- if (parameters->options().output_is_position_independent()
- && parameters->options().output_is_executable())
+ // 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.
+// 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 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), prev_data_size_(0)
+ : Output_data(), owner_(owner), reloc_stubs_size_(0),
+ erratum_stubs_size_(0), prev_data_size_(0)
{ }
~Stub_table()
// Whether this stub table is empty.
bool
empty() const
- { return reloc_stubs_.empty(); }
+ { return reloc_stubs_.empty() && erratum_stubs_.empty(); }
// Return the current data size.
off_t
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()
return (p != this->reloc_stubs_.end()) ? p->second : NULL;
}
- // Relocate stubs in this stub table.
+ // Relocate reloc stubs in this stub table. This does not relocate erratum stubs.
void
- relocate_stubs(const The_relocate_info*,
- The_target_aarch64*,
- Output_section*,
- unsigned char*,
- AArch64_address,
- section_size_type);
+ 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.
update_data_size_changed_p()
{
// No addralign changed here.
- off_t s = this->reloc_stubs_size_;
+ 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;
// Return the required alignment.
uint64_t
do_addralign() const
- { return The_reloc_stub::STUB_ADDR_ALIGN; }
+ {
+ return std::max(The_reloc_stub::STUB_ADDR_ALIGN,
+ The_erratum_stub::STUB_ADDR_ALIGN);
+ }
// Reset address and file offset.
void
{ this->set_data_size(this->current_data_size()); }
private:
- // Relocate one stub.
+ // Relocate one reloc stub.
void
- relocate_stub(The_reloc_stub*,
- const The_relocate_info*,
- The_target_aarch64*,
- Output_section*,
- unsigned char*,
- AArch64_address,
- section_size_type);
+ 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.
Stub_table<size, big_endian>::add_reloc_stub(
The_reloc_stub* stub, const The_reloc_stub_key& key)
{
- gold_assert(stub->stub_type() == key.stub_type());
+ gold_assert(stub->type() == key.type());
this->reloc_stubs_[key] = stub;
// Assign stub offset early. We can do this because we never remove
}
-// Relocate all stubs in this stub table.
+// 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_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)
+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_stub(p->second, relinfo, target_aarch64, output_section,
- view, address, view_size);
+ relocate_reloc_stub(p->second, relinfo, target_aarch64, output_section,
+ view, address, view_size);
}
-// Relocate one stub. This is a helper for Stub_table::relocate_stubs().
+// 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_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)
+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();
// "view_size" is the total size of the stub_table.
gold_assert(offset + stub_size <= view_size);
- target_aarch64->relocate_stub(stub, relinfo, output_section,
- view + offset, address + offset, view_size);
+ target_aarch64->relocate_reloc_stub(stub, relinfo, output_section,
+ view + offset, address + offset, view_size);
}
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);
}
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);
this->stub_tables_[shndx] = stub_table;
}
- // Scan all relocation sections for stub generation.
+ // 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*);
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_relocs_must_follow_section_writes();
- }
+ // 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.
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>&,
// 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>
const unsigned char* pshdrs, Output_file* of,
typename Sized_relobj_file<size, big_endian>::Views* pviews)
{
- // Call parent to relocate sections.
- Sized_relobj_file<size, big_endian>::do_relocate_sections(symtab, layout,
- pshdrs, of, pviews);
+ // Relocate the section data.
+ this->relocate_section_range(symtab, layout, pshdrs, of, pviews,
+ 1, this->shnum() - 1);
// We do not generate stubs if doing a relocatable link.
if (parameters->options().relocatable())
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;
- // Relocate stub tables.
+ // 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();
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_stubs(&relinfo, target, os, view, address,
- view_size);
+ stub_table->relocate_reloc_stubs(&relinfo, target, os, view, address,
+ view_size);
}
}
}
}
+// 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>
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))
{
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 typename The_reloc_stub::Stub_type The_reloc_stub_type;
+ 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 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)
rela_irelative_(NULL), copy_relocs_(elfcpp::R_AARCH64_COPY),
got_mod_index_offset_(-1U),
tlsdesc_reloc_info_(), tls_base_symbol_defined_(false),
- stub_tables_(), aarch64_input_section_map_()
+ 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;
Address view_address,
section_size_type);
- // Relocate a single stub.
+ // Relocate a single reloc stub.
void
- relocate_stub(The_reloc_stub*, const Relocate_info<size, big_endian>*,
- Output_section*, unsigned char*, Address,
- section_size_type);
+ 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*
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()
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);
unsigned char*,
const Symbol_value<size>*);
+ 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>*,
}; // 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
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
};
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
};
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
};
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
};
// Get the GOT section, creating it if necessary.
if (gsym->use_plt_offset(arp->reference_flags()))
{
// This uses a PLT, change the symbol value.
- symval.set_output_value(this->plt_section()->address()
- + gsym->plt_offset());
+ symval.set_output_value(this->plt_address_for_global(gsym));
psymval = &symval;
}
else if (gsym->is_undefined())
- // There is no need to generate a stub symbol is undefined.
- return;
+ {
+ // 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.
destination = value + addend;
break;
default:
- gold_assert(false);
+ gold_unreachable();
}
- typename The_reloc_stub::Stub_type stub_type = The_reloc_stub::
+ int stub_type = The_reloc_stub::
stub_type_for_reloc(r_type, address, destination);
- if (stub_type == The_reloc_stub::ST_NONE)
- return ;
+ if (stub_type == ST_NONE)
+ return;
The_stub_table* stub_table = aarch64_relobj->stub_table(relinfo->data_shndx);
gold_assert(stub_table != NULL);
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);
psymval = &symval2;
}
- // If symbol is a section symbol, we don't know the actual type of
- // destination. Give up.
- if (psymval->is_section_symbol())
- continue;
-
this->scan_reloc_for_stub(relinfo, r_type, sym, r_sym, psymval,
addend, view_address + offset);
} // End of iterating relocs in a section
}
-// Relocate a single stub.
+// Relocate a single reloc stub.
template<int size, bool big_endian>
void Target_aarch64<size, big_endian>::
-relocate_stub(The_reloc_stub* stub,
- const The_relocate_info*,
- Output_section*,
- unsigned char* view,
- Address address,
- section_size_type)
+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->stub_insn_number();
- const uint32_t* insns = stub->stub_insns();
+ 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+1]));
+ gold_assert(((uint32_t)insn == insns[i]));
}
Address dest = stub->destination_address();
- switch(stub->stub_type())
+ switch(stub->type())
{
- case The_reloc_stub::ST_ADRP_BRANCH:
+ case ST_ADRP_BRANCH:
{
// 1st reloc is ADR_PREL_PG_HI21
The_reloc_functions_status status =
}
break;
- case The_reloc_stub::ST_LONG_BRANCH_ABS:
+ 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 The_reloc_stub::ST_LONG_BRANCH_PCREL:
+ case ST_LONG_BRANCH_PCREL:
{
// "PC" calculation is the 2nd insn in the stub.
uint64_t offset = dest - (address + 4);
break;
default:
- gold_assert(false);
+ gold_unreachable();
}
}
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.
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);
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 typename The_reloc_stub::Stub_type The_reloc_stub_type;
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)));
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));
- return (reloc_property->checkup_x_value(x)
- ? This::STATUS_OKAY
- : This::STATUS_OVERFLOW);
- }
-
- // Do relocate. Update selected bits in text.
- // new_val = (val & ~dst_mask) | (immed << doffset)
-
- template<int valsize>
- static inline typename This::Status
- 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,
- 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.
+ Address x = psymval->value(object, addend);
+ elfcpp::Swap<valsize, big_endian>::writeval(wv,static_cast<Valtype>(x));
return (reloc_property->checkup_x_value(x)
? This::STATUS_OKAY
: This::STATUS_OVERFLOW);
}
+ // Do relocate. Update selected bits in text.
+ // new_val = (val & ~dst_mask) | (immed << doffset)
+
+ template<int valsize>
+ static inline typename This::Status
+ rela_general(unsigned char* view,
+ const Sized_relobj_file<size, big_endian>* object,
+ const Symbol_value<size>* psymval,
+ AArch64_valtype addend,
+ const AArch64_reloc_property* reloc_property)
+ {
+ // Calculate relocation.
+ Address x = psymval->value(object, addend);
+ return This::template reloc_common<valsize>(view, x, reloc_property);
+ }
+
// Do relocate. Update selected bits in text.
// new val = (val & ~dst_mask) | (immed << doffset)
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);
+ }
- // 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);
+ // Calculate (S + A) - address, update adr instruction.
- // Do check overflow or alignment if needed.
- return (reloc_property->checkup_x_value(x)
- ? This::STATUS_OKAY
- : This::STATUS_OVERFLOW);
+ 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.
Address sa,
Address address)
{
- typename elfcpp::Swap<size, big_endian>::Valtype x =
- This::Page(sa) - This::Page(address);
- update_adr(view, x, NULL);
+ 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
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);
+ 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);
static inline typename This::Status
movnz(unsigned char* view,
- typename elfcpp::Swap<size, big_endian>::Valtype x,
+ AArch64_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)
+ 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 = ~immed;
+ immed = reloc_property->select_x_value(~x);;
is_movz = false;
}
Address,
const Sized_symbol<size>*,
const Symbol_value<size>*,
- const Sized_relobj_file<size, big_endian>*);
+ const Sized_relobj_file<size, big_endian>*,
+ section_size_type);
}; // End of AArch64_relocate_functions
Address address,
const Sized_symbol<size>* gsym,
const Symbol_value<size>* psymval,
- const Sized_relobj_file<size, big_endian>* object)
+ 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;
- The_reloc_stub_type stub_type = The_reloc_stub::
- stub_type_for_reloc(r_type, address, branch_target);
- if (stub_type == The_reloc_stub::ST_NONE)
+ 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);
Address new_branch_target = stub_table->address() + stub->offset();
typename elfcpp::Swap<size, big_endian>::Valtype branch_offset =
new_branch_target - address;
- const AArch64_reloc_property* arp =
- aarch64_reloc_property_table->get_reloc_property(r_type);
- gold_assert(arp != NULL);
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'. For example, 0x2000000."));
+ "for '--stub-group-size'. The current value is 0x%lx."),
+ static_cast<unsigned long>(current_group_size));
return true;
}
gold_assert(!parameters->options().relocatable());
if (pass == 1)
{
- section_size_type stub_group_size =
- parameters->options().stub_group_size();
- if (stub_group_size == 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.
- stub_group_size = The_reloc_stub::MAX_BRANCH_OFFSET - 4096 * 4;
+ this->stub_group_size_ = The_reloc_stub::MAX_BRANCH_OFFSET -
+ 4096 * 4;
}
- group_sections(layout, stub_group_size, true, task);
+ group_sections(layout, this->stub_group_size_, true, task);
}
else
{
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:
{
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:
}
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
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,
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;
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:
reloc_status = Reloc::adrp(view, object, psymval, addend, address,
// Return false to stop further processing this reloc.
return false;
}
- // Fallthrough
+ // Fall through.
case elfcpp::R_AARCH64_JUMP26:
if (Reloc::maybe_apply_stub(r_type, relinfo, rela, view, address,
- gsym, psymval, object))
+ gsym, psymval, object,
+ target->stub_group_size_))
break;
- // Fallthrough
+ // Fall through.
case elfcpp::R_AARCH64_TSTBR14:
case elfcpp::R_AARCH64_CONDBR19:
reloc_status = Reloc::template pcrela_general<32>(
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:
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;
+ 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;
+
+ default:
+ gold_unreachable();
+ }
+ }
+ 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
+ {
+ 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;
+ }
+ }
+ 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_assert(false);
- }
+ default:
+ gold_unreachable();
}
- gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
- _("unsupported gd_to_ie relaxation on %u"),
- r_type);
+ // 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:
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);
} // End of tls_gd_to_le
+template<int size, bool big_endian>
+inline
+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
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;
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,
}
+// 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>