+// Telling how to update the immediate field of an instruction.
+struct AArch64_howto
+{
+ // The immediate field mask.
+ elfcpp::Elf_Xword dst_mask;
+
+ // The offset to apply relocation immediate
+ int doffset;
+
+ // The second part offset, if the immediate field has two parts.
+ // -1 if the immediate field has only one part.
+ int doffset2;
+};
+
+static const AArch64_howto aarch64_howto[AArch64_reloc_property::INST_NUM] =
+{
+ {0, -1, -1}, // DATA
+ {0x1fffe0, 5, -1}, // MOVW [20:5]-imm16
+ {0xffffe0, 5, -1}, // LD [23:5]-imm19
+ {0x60ffffe0, 29, 5}, // ADR [30:29]-immlo [23:5]-immhi
+ {0x60ffffe0, 29, 5}, // ADRP [30:29]-immlo [23:5]-immhi
+ {0x3ffc00, 10, -1}, // ADD [21:10]-imm12
+ {0x3ffc00, 10, -1}, // LDST [21:10]-imm12
+ {0x7ffe0, 5, -1}, // TBZNZ [18:5]-imm14
+ {0xffffe0, 5, -1}, // CONDB [23:5]-imm19
+ {0x3ffffff, 0, -1}, // B [25:0]-imm26
+ {0x3ffffff, 0, -1}, // CALL [25:0]-imm26
+};
+
+// AArch64 relocate function class
+
+template<int size, bool big_endian>
+class AArch64_relocate_functions
+{
+ public:
+ typedef enum
+ {
+ STATUS_OKAY, // No error during relocation.
+ STATUS_OVERFLOW, // Relocation overflow.
+ STATUS_BAD_RELOC, // Relocation cannot be applied.
+ } Status;
+
+ private:
+ typedef AArch64_relocate_functions<size, big_endian> This;
+ typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
+
+ // Return the page address of the address.
+ // Page(address) = address & ~0xFFF
+
+ static inline typename elfcpp::Swap<size, big_endian>::Valtype
+ Page(Address address)
+ {
+ return (address & (~static_cast<Address>(0xFFF)));
+ }
+
+ // Update instruction (pointed by view) with selected bits (immed).
+ // val = (val & ~dst_mask) | (immed << doffset)
+
+ template<int valsize>
+ static inline void
+ update_view(unsigned char* view,
+ typename elfcpp::Swap<size, big_endian>::Valtype immed,
+ elfcpp::Elf_Xword doffset,
+ elfcpp::Elf_Xword dst_mask)
+ {
+ typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
+ Valtype* wv = reinterpret_cast<Valtype*>(view);
+ Valtype val = elfcpp::Swap<valsize, big_endian>::readval(wv);
+
+ // Clear immediate fields.
+ val &= ~dst_mask;
+ elfcpp::Swap<valsize, big_endian>::writeval(wv,
+ static_cast<Valtype>(val | (immed << doffset)));
+ }
+
+ // Update two parts of an instruction (pointed by view) with selected
+ // bits (immed1 and immed2).
+ // val = (val & ~dst_mask) | (immed1 << doffset1) | (immed2 << doffset2)
+
+ template<int valsize>
+ 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,
+ elfcpp::Elf_Xword doffset1,
+ elfcpp::Elf_Xword doffset2,
+ elfcpp::Elf_Xword dst_mask)
+ {
+ typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
+ Valtype* wv = reinterpret_cast<Valtype*>(view);
+ Valtype val = elfcpp::Swap<valsize, big_endian>::readval(wv);
+ val &= ~dst_mask;
+ elfcpp::Swap<valsize, big_endian>::writeval(wv,
+ static_cast<Valtype>(val | (immed1 << doffset1) |
+ (immed2 << doffset2)));
+ }
+
+ // Update adr or adrp instruction with [32:12] of X.
+ // 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 */)
+ {
+ 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,
+ (immed & 0x1ffffc) >> 2,
+ 29,
+ 5,
+ dst_mask);
+ }
+
+ public:
+
+ // Do a simple rela relocation at unaligned addresses.
+
+ template<int valsize>
+ static inline typename This::Status
+ 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,
+ const AArch64_reloc_property* reloc_property)
+ {
+ typedef typename elfcpp::Swap_unaligned<valsize, big_endian>::Valtype
+ Valtype;
+ typename elfcpp::Elf_types<size>::Elf_Addr x =
+ psymval->value(object, addend);
+ elfcpp::Swap_unaligned<valsize, big_endian>::writeval(view,
+ static_cast<Valtype>(x));
+ return (reloc_property->checkup_x_value(x)
+ ? This::STATUS_OKAY
+ : This::STATUS_OVERFLOW);
+ }
+
+ // Do a simple pc-relative relocation at unaligned addresses.
+
+ template<int valsize>
+ static inline typename This::Status
+ 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,
+ Address address,
+ const AArch64_reloc_property* reloc_property)
+ {
+ typedef typename elfcpp::Swap_unaligned<valsize, big_endian>::Valtype
+ Valtype;
+ Address x = psymval->value(object, addend) - address;
+ elfcpp::Swap_unaligned<valsize, big_endian>::writeval(view,
+ static_cast<Valtype>(x));
+ return (reloc_property->checkup_x_value(x)
+ ? This::STATUS_OKAY
+ : This::STATUS_OVERFLOW);
+ }
+
+ // Do a simple rela relocation at aligned addresses.
+
+ template<int valsize>
+ static inline typename This::Status
+ rela(
+ 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)
+ {
+ 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.
+ 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,
+ typename elfcpp::Swap<size, big_endian>::Valtype s,
+ typename elfcpp::Swap<size, big_endian>::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);
+ }
+
+ // Do address relative relocate. Update selected bits in text.
+ // new val = (val & ~dst_mask) | (immed << doffset)
+
+ template<int valsize>
+ static inline typename This::Status
+ pcrela_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,
+ Address address,
+ const AArch64_reloc_property* reloc_property)
+ {
+ // Calculate relocation.
+ Address x = psymval->value(object, addend) - address;
+
+ // 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);
+ }
+
+ // Calculate PG(S+A) - PG(address), update adrp instruction.
+ // R_AARCH64_ADR_PREL_PG_HI21
+
+ static inline typename This::Status
+ adrp(
+ unsigned char* view,
+ Address sa,
+ Address address)
+ {
+ typename elfcpp::Swap<size, big_endian>::Valtype x =
+ This::Page(sa) - This::Page(address);
+ update_adr(view, x, NULL);
+ return (size == 64 && Bits<32>::has_overflow(x)
+ ? This::STATUS_OVERFLOW
+ : This::STATUS_OKAY);
+ }
+
+ // Calculate PG(S+A) - PG(address), update adrp instruction.
+ // R_AARCH64_ADR_PREL_PG_HI21
+
+ static inline typename This::Status
+ adrp(unsigned char* view,
+ const Sized_relobj_file<size, big_endian>* object,
+ const Symbol_value<size>* psymval,
+ Address addend,
+ Address address,
+ const AArch64_reloc_property* reloc_property)
+ {
+ Address sa = psymval->value(object, addend);
+ typename elfcpp::Swap<size, big_endian>::Valtype x =
+ This::Page(sa) - This::Page(address);
+ update_adr(view, x, reloc_property);
+ return (reloc_property->checkup_x_value(x)
+ ? This::STATUS_OKAY
+ : This::STATUS_OVERFLOW);
+ }
+
+};
+