/* AArch64-specific support for NN-bit ELF. Copyright (C) 2009-2014 Free Software Foundation, Inc. Contributed by ARM Ltd. This file is part of BFD, the Binary File Descriptor library. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING3. If not, see . */ /* Notes on implementation: Thread Local Store (TLS) Overview: The implementation currently supports both traditional TLS and TLS descriptors, but only general dynamic (GD). For traditional TLS the assembler will present us with code fragments of the form: adrp x0, :tlsgd:foo R_AARCH64_TLSGD_ADR_PAGE21(foo) add x0, :tlsgd_lo12:foo R_AARCH64_TLSGD_ADD_LO12_NC(foo) bl __tls_get_addr nop For TLS descriptors the assembler will present us with code fragments of the form: adrp x0, :tlsdesc:foo R_AARCH64_TLSDESC_ADR_PAGE21(foo) ldr x1, [x0, #:tlsdesc_lo12:foo] R_AARCH64_TLSDESC_LD64_LO12(foo) add x0, x0, #:tlsdesc_lo12:foo R_AARCH64_TLSDESC_ADD_LO12(foo) .tlsdesccall foo blr x1 R_AARCH64_TLSDESC_CALL(foo) The relocations R_AARCH64_TLSGD_{ADR_PREL21,ADD_LO12_NC} against foo indicate that foo is thread local and should be accessed via the traditional TLS mechanims. The relocations R_AARCH64_TLSDESC_{ADR_PAGE21,LD64_LO12_NC,ADD_LO12_NC} against foo indicate that 'foo' is thread local and should be accessed via a TLS descriptor mechanism. The precise instruction sequence is only relevant from the perspective of linker relaxation which is currently not implemented. The static linker must detect that 'foo' is a TLS object and allocate a double GOT entry. The GOT entry must be created for both global and local TLS symbols. Note that this is different to none TLS local objects which do not need a GOT entry. In the traditional TLS mechanism, the double GOT entry is used to provide the tls_index structure, containing module and offset entries. The static linker places the relocation R_AARCH64_TLS_DTPMOD on the module entry. The loader will subsequently fixup this relocation with the module identity. For global traditional TLS symbols the static linker places an R_AARCH64_TLS_DTPREL relocation on the offset entry. The loader will subsequently fixup the offset. For local TLS symbols the static linker fixes up offset. In the TLS descriptor mechanism the double GOT entry is used to provide the descriptor. The static linker places the relocation R_AARCH64_TLSDESC on the first GOT slot. The loader will subsequently fix this up. Implementation: The handling of TLS symbols is implemented across a number of different backend functions. The following is a top level view of what processing is performed where. The TLS implementation maintains state information for each TLS symbol. The state information for local and global symbols is kept in different places. Global symbols use generic BFD structures while local symbols use backend specific structures that are allocated and maintained entirely by the backend. The flow: elfNN_aarch64_check_relocs() This function is invoked for each relocation. The TLS relocations R_AARCH64_TLSGD_{ADR_PREL21,ADD_LO12_NC} and R_AARCH64_TLSDESC_{ADR_PAGE21,LD64_LO12_NC,ADD_LO12_NC} are spotted. One time creation of local symbol data structures are created when the first local symbol is seen. The reference count for a symbol is incremented. The GOT type for each symbol is marked as general dynamic. elfNN_aarch64_allocate_dynrelocs () For each global with positive reference count we allocate a double GOT slot. For a traditional TLS symbol we allocate space for two relocation entries on the GOT, for a TLS descriptor symbol we allocate space for one relocation on the slot. Record the GOT offset for this symbol. elfNN_aarch64_size_dynamic_sections () Iterate all input BFDS, look for in the local symbol data structure constructed earlier for local TLS symbols and allocate them double GOT slots along with space for a single GOT relocation. Update the local symbol structure to record the GOT offset allocated. elfNN_aarch64_relocate_section () Calls elfNN_aarch64_final_link_relocate () Emit the relevant TLS relocations against the GOT for each TLS symbol. For local TLS symbols emit the GOT offset directly. The GOT relocations are emitted once the first time a TLS symbol is encountered. The implementation uses the LSB of the GOT offset to flag that the relevant GOT relocations for a symbol have been emitted. All of the TLS code that uses the GOT offset needs to take care to mask out this flag bit before using the offset. elfNN_aarch64_final_link_relocate () Fixup the R_AARCH64_TLSGD_{ADR_PREL21, ADD_LO12_NC} relocations. */ #include "sysdep.h" #include "bfd.h" #include "libiberty.h" #include "libbfd.h" #include "bfd_stdint.h" #include "elf-bfd.h" #include "bfdlink.h" #include "objalloc.h" #include "elf/aarch64.h" #include "elfxx-aarch64.h" #define ARCH_SIZE NN #if ARCH_SIZE == 64 #define AARCH64_R(NAME) R_AARCH64_ ## NAME #define AARCH64_R_STR(NAME) "R_AARCH64_" #NAME #define HOWTO64(...) HOWTO (__VA_ARGS__) #define HOWTO32(...) EMPTY_HOWTO (0) #define LOG_FILE_ALIGN 3 #endif #if ARCH_SIZE == 32 #define AARCH64_R(NAME) R_AARCH64_P32_ ## NAME #define AARCH64_R_STR(NAME) "R_AARCH64_P32_" #NAME #define HOWTO64(...) EMPTY_HOWTO (0) #define HOWTO32(...) HOWTO (__VA_ARGS__) #define LOG_FILE_ALIGN 2 #endif #define IS_AARCH64_TLS_RELOC(R_TYPE) \ ((R_TYPE) == BFD_RELOC_AARCH64_TLSGD_ADR_PAGE21 \ || (R_TYPE) == BFD_RELOC_AARCH64_TLSGD_ADD_LO12_NC \ || (R_TYPE) == BFD_RELOC_AARCH64_TLSIE_MOVW_GOTTPREL_G1 \ || (R_TYPE) == BFD_RELOC_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC \ || (R_TYPE) == BFD_RELOC_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 \ || (R_TYPE) == BFD_RELOC_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC \ || (R_TYPE) == BFD_RELOC_AARCH64_TLSIE_LD32_GOTTPREL_LO12_NC \ || (R_TYPE) == BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_PREL19 \ || (R_TYPE) == BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12 \ || (R_TYPE) == BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_HI12 \ || (R_TYPE) == BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12_NC \ || (R_TYPE) == BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G2 \ || (R_TYPE) == BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1 \ || (R_TYPE) == BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1_NC \ || (R_TYPE) == BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0 \ || (R_TYPE) == BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0_NC \ || (R_TYPE) == BFD_RELOC_AARCH64_TLS_DTPMOD \ || (R_TYPE) == BFD_RELOC_AARCH64_TLS_DTPREL \ || (R_TYPE) == BFD_RELOC_AARCH64_TLS_TPREL \ || IS_AARCH64_TLSDESC_RELOC ((R_TYPE))) #define IS_AARCH64_TLSDESC_RELOC(R_TYPE) \ ((R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_LD_PREL19 \ || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_ADR_PREL21 \ || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_ADR_PAGE21 \ || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_ADD_LO12_NC \ || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_LD64_LO12_NC \ || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_LD32_LO12_NC \ || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_OFF_G1 \ || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_OFF_G0_NC \ || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_LDR \ || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_ADD \ || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_CALL \ || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC) #define ELIMINATE_COPY_RELOCS 0 /* Return size of a relocation entry. HTAB is the bfd's elf_aarch64_link_hash_entry. */ #define RELOC_SIZE(HTAB) (sizeof (ElfNN_External_Rela)) /* GOT Entry size - 8 bytes in ELF64 and 4 bytes in ELF32. */ #define GOT_ENTRY_SIZE (ARCH_SIZE / 8) #define PLT_ENTRY_SIZE (32) #define PLT_SMALL_ENTRY_SIZE (16) #define PLT_TLSDESC_ENTRY_SIZE (32) /* Encoding of the nop instruction */ #define INSN_NOP 0xd503201f #define aarch64_compute_jump_table_size(htab) \ (((htab)->root.srelplt == NULL) ? 0 \ : (htab)->root.srelplt->reloc_count * GOT_ENTRY_SIZE) /* The first entry in a procedure linkage table looks like this if the distance between the PLTGOT and the PLT is < 4GB use these PLT entries. Note that the dynamic linker gets &PLTGOT[2] in x16 and needs to work out PLTGOT[1] by using an address of [x16,#-GOT_ENTRY_SIZE]. */ static const bfd_byte elfNN_aarch64_small_plt0_entry[PLT_ENTRY_SIZE] = { 0xf0, 0x7b, 0xbf, 0xa9, /* stp x16, x30, [sp, #-16]! */ 0x10, 0x00, 0x00, 0x90, /* adrp x16, (GOT+16) */ #if ARCH_SIZE == 64 0x11, 0x0A, 0x40, 0xf9, /* ldr x17, [x16, #PLT_GOT+0x10] */ 0x10, 0x42, 0x00, 0x91, /* add x16, x16,#PLT_GOT+0x10 */ #else 0x11, 0x0A, 0x40, 0xb9, /* ldr w17, [x16, #PLT_GOT+0x8] */ 0x10, 0x22, 0x00, 0x11, /* add w16, w16,#PLT_GOT+0x8 */ #endif 0x20, 0x02, 0x1f, 0xd6, /* br x17 */ 0x1f, 0x20, 0x03, 0xd5, /* nop */ 0x1f, 0x20, 0x03, 0xd5, /* nop */ 0x1f, 0x20, 0x03, 0xd5, /* nop */ }; /* Per function entry in a procedure linkage table looks like this if the distance between the PLTGOT and the PLT is < 4GB use these PLT entries. */ static const bfd_byte elfNN_aarch64_small_plt_entry[PLT_SMALL_ENTRY_SIZE] = { 0x10, 0x00, 0x00, 0x90, /* adrp x16, PLTGOT + n * 8 */ #if ARCH_SIZE == 64 0x11, 0x02, 0x40, 0xf9, /* ldr x17, [x16, PLTGOT + n * 8] */ 0x10, 0x02, 0x00, 0x91, /* add x16, x16, :lo12:PLTGOT + n * 8 */ #else 0x11, 0x02, 0x40, 0xb9, /* ldr w17, [x16, PLTGOT + n * 4] */ 0x10, 0x02, 0x00, 0x11, /* add w16, w16, :lo12:PLTGOT + n * 4 */ #endif 0x20, 0x02, 0x1f, 0xd6, /* br x17. */ }; static const bfd_byte elfNN_aarch64_tlsdesc_small_plt_entry[PLT_TLSDESC_ENTRY_SIZE] = { 0xe2, 0x0f, 0xbf, 0xa9, /* stp x2, x3, [sp, #-16]! */ 0x02, 0x00, 0x00, 0x90, /* adrp x2, 0 */ 0x03, 0x00, 0x00, 0x90, /* adrp x3, 0 */ #if ARCH_SIZE == 64 0x42, 0x00, 0x40, 0xf9, /* ldr x2, [x2, #0] */ 0x63, 0x00, 0x00, 0x91, /* add x3, x3, 0 */ #else 0x42, 0x00, 0x40, 0xb9, /* ldr w2, [x2, #0] */ 0x63, 0x00, 0x00, 0x11, /* add w3, w3, 0 */ #endif 0x40, 0x00, 0x1f, 0xd6, /* br x2 */ 0x1f, 0x20, 0x03, 0xd5, /* nop */ 0x1f, 0x20, 0x03, 0xd5, /* nop */ }; #define elf_info_to_howto elfNN_aarch64_info_to_howto #define elf_info_to_howto_rel elfNN_aarch64_info_to_howto #define AARCH64_ELF_ABI_VERSION 0 /* In case we're on a 32-bit machine, construct a 64-bit "-1" value. */ #define ALL_ONES (~ (bfd_vma) 0) /* Indexed by the bfd interal reloc enumerators. Therefore, the table needs to be synced with BFD_RELOC_AARCH64_* in reloc.c. */ static reloc_howto_type elfNN_aarch64_howto_table[] = { EMPTY_HOWTO (0), /* Basic data relocations. */ #if ARCH_SIZE == 64 HOWTO (R_AARCH64_NULL, /* type */ 0, /* rightshift */ 0, /* size (0 = byte, 1 = short, 2 = long) */ 0, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_AARCH64_NULL", /* name */ FALSE, /* partial_inplace */ 0, /* src_mask */ 0, /* dst_mask */ FALSE), /* pcrel_offset */ #else HOWTO (R_AARCH64_NONE, /* type */ 0, /* rightshift */ 0, /* size (0 = byte, 1 = short, 2 = long) */ 0, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_AARCH64_NONE", /* name */ FALSE, /* partial_inplace */ 0, /* src_mask */ 0, /* dst_mask */ FALSE), /* pcrel_offset */ #endif /* .xword: (S+A) */ HOWTO64 (AARCH64_R (ABS64), /* type */ 0, /* rightshift */ 4, /* size (4 = long long) */ 64, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_unsigned, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (ABS64), /* name */ FALSE, /* partial_inplace */ ALL_ONES, /* src_mask */ ALL_ONES, /* dst_mask */ FALSE), /* pcrel_offset */ /* .word: (S+A) */ HOWTO (AARCH64_R (ABS32), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_unsigned, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (ABS32), /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* .half: (S+A) */ HOWTO (AARCH64_R (ABS16), /* type */ 0, /* rightshift */ 1, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_unsigned, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (ABS16), /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* .xword: (S+A-P) */ HOWTO64 (AARCH64_R (PREL64), /* type */ 0, /* rightshift */ 4, /* size (4 = long long) */ 64, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (PREL64), /* name */ FALSE, /* partial_inplace */ ALL_ONES, /* src_mask */ ALL_ONES, /* dst_mask */ TRUE), /* pcrel_offset */ /* .word: (S+A-P) */ HOWTO (AARCH64_R (PREL32), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (PREL32), /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ /* .half: (S+A-P) */ HOWTO (AARCH64_R (PREL16), /* type */ 0, /* rightshift */ 1, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (PREL16), /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ TRUE), /* pcrel_offset */ /* Group relocations to create a 16, 32, 48 or 64 bit unsigned data or abs address inline. */ /* MOVZ: ((S+A) >> 0) & 0xffff */ HOWTO (AARCH64_R (MOVW_UABS_G0), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_unsigned, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (MOVW_UABS_G0), /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* MOVK: ((S+A) >> 0) & 0xffff [no overflow check] */ HOWTO (AARCH64_R (MOVW_UABS_G0_NC), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (MOVW_UABS_G0_NC), /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* MOVZ: ((S+A) >> 16) & 0xffff */ HOWTO (AARCH64_R (MOVW_UABS_G1), /* type */ 16, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_unsigned, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (MOVW_UABS_G1), /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* MOVK: ((S+A) >> 16) & 0xffff [no overflow check] */ HOWTO64 (AARCH64_R (MOVW_UABS_G1_NC), /* type */ 16, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (MOVW_UABS_G1_NC), /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* MOVZ: ((S+A) >> 32) & 0xffff */ HOWTO64 (AARCH64_R (MOVW_UABS_G2), /* type */ 32, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_unsigned, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (MOVW_UABS_G2), /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* MOVK: ((S+A) >> 32) & 0xffff [no overflow check] */ HOWTO64 (AARCH64_R (MOVW_UABS_G2_NC), /* type */ 32, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (MOVW_UABS_G2_NC), /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* MOVZ: ((S+A) >> 48) & 0xffff */ HOWTO64 (AARCH64_R (MOVW_UABS_G3), /* type */ 48, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_unsigned, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (MOVW_UABS_G3), /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* Group relocations to create high part of a 16, 32, 48 or 64 bit signed data or abs address inline. Will change instruction to MOVN or MOVZ depending on sign of calculated value. */ /* MOV[ZN]: ((S+A) >> 0) & 0xffff */ HOWTO (AARCH64_R (MOVW_SABS_G0), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (MOVW_SABS_G0), /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* MOV[ZN]: ((S+A) >> 16) & 0xffff */ HOWTO64 (AARCH64_R (MOVW_SABS_G1), /* type */ 16, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (MOVW_SABS_G1), /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* MOV[ZN]: ((S+A) >> 32) & 0xffff */ HOWTO64 (AARCH64_R (MOVW_SABS_G2), /* type */ 32, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (MOVW_SABS_G2), /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* Relocations to generate 19, 21 and 33 bit PC-relative load/store addresses: PG(x) is (x & ~0xfff). */ /* LD-lit: ((S+A-P) >> 2) & 0x7ffff */ HOWTO (AARCH64_R (LD_PREL_LO19), /* type */ 2, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 19, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (LD_PREL_LO19), /* name */ FALSE, /* partial_inplace */ 0x7ffff, /* src_mask */ 0x7ffff, /* dst_mask */ TRUE), /* pcrel_offset */ /* ADR: (S+A-P) & 0x1fffff */ HOWTO (AARCH64_R (ADR_PREL_LO21), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 21, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (ADR_PREL_LO21), /* name */ FALSE, /* partial_inplace */ 0x1fffff, /* src_mask */ 0x1fffff, /* dst_mask */ TRUE), /* pcrel_offset */ /* ADRP: ((PG(S+A)-PG(P)) >> 12) & 0x1fffff */ HOWTO (AARCH64_R (ADR_PREL_PG_HI21), /* type */ 12, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 21, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (ADR_PREL_PG_HI21), /* name */ FALSE, /* partial_inplace */ 0x1fffff, /* src_mask */ 0x1fffff, /* dst_mask */ TRUE), /* pcrel_offset */ /* ADRP: ((PG(S+A)-PG(P)) >> 12) & 0x1fffff [no overflow check] */ HOWTO64 (AARCH64_R (ADR_PREL_PG_HI21_NC), /* type */ 12, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 21, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (ADR_PREL_PG_HI21_NC), /* name */ FALSE, /* partial_inplace */ 0x1fffff, /* src_mask */ 0x1fffff, /* dst_mask */ TRUE), /* pcrel_offset */ /* ADD: (S+A) & 0xfff [no overflow check] */ HOWTO (AARCH64_R (ADD_ABS_LO12_NC), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 10, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (ADD_ABS_LO12_NC), /* name */ FALSE, /* partial_inplace */ 0x3ffc00, /* src_mask */ 0x3ffc00, /* dst_mask */ FALSE), /* pcrel_offset */ /* LD/ST8: (S+A) & 0xfff */ HOWTO (AARCH64_R (LDST8_ABS_LO12_NC), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (LDST8_ABS_LO12_NC), /* name */ FALSE, /* partial_inplace */ 0xfff, /* src_mask */ 0xfff, /* dst_mask */ FALSE), /* pcrel_offset */ /* Relocations for control-flow instructions. */ /* TBZ/NZ: ((S+A-P) >> 2) & 0x3fff */ HOWTO (AARCH64_R (TSTBR14), /* type */ 2, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 14, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TSTBR14), /* name */ FALSE, /* partial_inplace */ 0x3fff, /* src_mask */ 0x3fff, /* dst_mask */ TRUE), /* pcrel_offset */ /* B.cond: ((S+A-P) >> 2) & 0x7ffff */ HOWTO (AARCH64_R (CONDBR19), /* type */ 2, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 19, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (CONDBR19), /* name */ FALSE, /* partial_inplace */ 0x7ffff, /* src_mask */ 0x7ffff, /* dst_mask */ TRUE), /* pcrel_offset */ /* B: ((S+A-P) >> 2) & 0x3ffffff */ HOWTO (AARCH64_R (JUMP26), /* type */ 2, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 26, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (JUMP26), /* name */ FALSE, /* partial_inplace */ 0x3ffffff, /* src_mask */ 0x3ffffff, /* dst_mask */ TRUE), /* pcrel_offset */ /* BL: ((S+A-P) >> 2) & 0x3ffffff */ HOWTO (AARCH64_R (CALL26), /* type */ 2, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 26, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (CALL26), /* name */ FALSE, /* partial_inplace */ 0x3ffffff, /* src_mask */ 0x3ffffff, /* dst_mask */ TRUE), /* pcrel_offset */ /* LD/ST16: (S+A) & 0xffe */ HOWTO (AARCH64_R (LDST16_ABS_LO12_NC), /* type */ 1, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (LDST16_ABS_LO12_NC), /* name */ FALSE, /* partial_inplace */ 0xffe, /* src_mask */ 0xffe, /* dst_mask */ FALSE), /* pcrel_offset */ /* LD/ST32: (S+A) & 0xffc */ HOWTO (AARCH64_R (LDST32_ABS_LO12_NC), /* type */ 2, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (LDST32_ABS_LO12_NC), /* name */ FALSE, /* partial_inplace */ 0xffc, /* src_mask */ 0xffc, /* dst_mask */ FALSE), /* pcrel_offset */ /* LD/ST64: (S+A) & 0xff8 */ HOWTO (AARCH64_R (LDST64_ABS_LO12_NC), /* type */ 3, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (LDST64_ABS_LO12_NC), /* name */ FALSE, /* partial_inplace */ 0xff8, /* src_mask */ 0xff8, /* dst_mask */ FALSE), /* pcrel_offset */ /* LD/ST128: (S+A) & 0xff0 */ HOWTO (AARCH64_R (LDST128_ABS_LO12_NC), /* type */ 4, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (LDST128_ABS_LO12_NC), /* name */ FALSE, /* partial_inplace */ 0xff0, /* src_mask */ 0xff0, /* dst_mask */ FALSE), /* pcrel_offset */ /* Set a load-literal immediate field to bits 0x1FFFFC of G(S)-P */ HOWTO (AARCH64_R (GOT_LD_PREL19), /* type */ 2, /* rightshift */ 2, /* size (0 = byte,1 = short,2 = long) */ 19, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (GOT_LD_PREL19), /* name */ FALSE, /* partial_inplace */ 0xffffe0, /* src_mask */ 0xffffe0, /* dst_mask */ TRUE), /* pcrel_offset */ /* Get to the page for the GOT entry for the symbol (G(S) - P) using an ADRP instruction. */ HOWTO (AARCH64_R (ADR_GOT_PAGE), /* type */ 12, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 21, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (ADR_GOT_PAGE), /* name */ FALSE, /* partial_inplace */ 0x1fffff, /* src_mask */ 0x1fffff, /* dst_mask */ TRUE), /* pcrel_offset */ /* LD64: GOT offset G(S) & 0xff8 */ HOWTO64 (AARCH64_R (LD64_GOT_LO12_NC), /* type */ 3, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (LD64_GOT_LO12_NC), /* name */ FALSE, /* partial_inplace */ 0xff8, /* src_mask */ 0xff8, /* dst_mask */ FALSE), /* pcrel_offset */ /* LD32: GOT offset G(S) & 0xffc */ HOWTO32 (AARCH64_R (LD32_GOT_LO12_NC), /* type */ 2, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (LD32_GOT_LO12_NC), /* name */ FALSE, /* partial_inplace */ 0xffc, /* src_mask */ 0xffc, /* dst_mask */ FALSE), /* pcrel_offset */ /* Get to the page for the GOT entry for the symbol (G(S) - P) using an ADRP instruction. */ HOWTO (AARCH64_R (TLSGD_ADR_PAGE21), /* type */ 12, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 21, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSGD_ADR_PAGE21), /* name */ FALSE, /* partial_inplace */ 0x1fffff, /* src_mask */ 0x1fffff, /* dst_mask */ TRUE), /* pcrel_offset */ /* ADD: GOT offset G(S) & 0xff8 [no overflow check] */ HOWTO (AARCH64_R (TLSGD_ADD_LO12_NC), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSGD_ADD_LO12_NC), /* name */ FALSE, /* partial_inplace */ 0xfff, /* src_mask */ 0xfff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO64 (AARCH64_R (TLSIE_MOVW_GOTTPREL_G1), /* type */ 16, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSIE_MOVW_GOTTPREL_G1), /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO64 (AARCH64_R (TLSIE_MOVW_GOTTPREL_G0_NC), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSIE_MOVW_GOTTPREL_G0_NC), /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (AARCH64_R (TLSIE_ADR_GOTTPREL_PAGE21), /* type */ 12, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 21, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSIE_ADR_GOTTPREL_PAGE21), /* name */ FALSE, /* partial_inplace */ 0x1fffff, /* src_mask */ 0x1fffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO64 (AARCH64_R (TLSIE_LD64_GOTTPREL_LO12_NC), /* type */ 3, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSIE_LD64_GOTTPREL_LO12_NC), /* name */ FALSE, /* partial_inplace */ 0xff8, /* src_mask */ 0xff8, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO32 (AARCH64_R (TLSIE_LD32_GOTTPREL_LO12_NC), /* type */ 2, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSIE_LD32_GOTTPREL_LO12_NC), /* name */ FALSE, /* partial_inplace */ 0xffc, /* src_mask */ 0xffc, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (AARCH64_R (TLSIE_LD_GOTTPREL_PREL19), /* type */ 2, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 21, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSIE_LD_GOTTPREL_PREL19), /* name */ FALSE, /* partial_inplace */ 0x1ffffc, /* src_mask */ 0x1ffffc, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO64 (AARCH64_R (TLSLE_MOVW_TPREL_G2), /* type */ 32, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSLE_MOVW_TPREL_G2), /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (AARCH64_R (TLSLE_MOVW_TPREL_G1), /* type */ 16, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSLE_MOVW_TPREL_G1), /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO64 (AARCH64_R (TLSLE_MOVW_TPREL_G1_NC), /* type */ 16, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSLE_MOVW_TPREL_G1_NC), /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (AARCH64_R (TLSLE_MOVW_TPREL_G0), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSLE_MOVW_TPREL_G0), /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (AARCH64_R (TLSLE_MOVW_TPREL_G0_NC), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSLE_MOVW_TPREL_G0_NC), /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (AARCH64_R (TLSLE_ADD_TPREL_HI12), /* type */ 12, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSLE_ADD_TPREL_HI12), /* name */ FALSE, /* partial_inplace */ 0xfff, /* src_mask */ 0xfff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (AARCH64_R (TLSLE_ADD_TPREL_LO12), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSLE_ADD_TPREL_LO12), /* name */ FALSE, /* partial_inplace */ 0xfff, /* src_mask */ 0xfff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (AARCH64_R (TLSLE_ADD_TPREL_LO12_NC), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSLE_ADD_TPREL_LO12_NC), /* name */ FALSE, /* partial_inplace */ 0xfff, /* src_mask */ 0xfff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (AARCH64_R (TLSDESC_LD_PREL19), /* type */ 2, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 21, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSDESC_LD_PREL19), /* name */ FALSE, /* partial_inplace */ 0x1ffffc, /* src_mask */ 0x1ffffc, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (AARCH64_R (TLSDESC_ADR_PREL21), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 21, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSDESC_ADR_PREL21), /* name */ FALSE, /* partial_inplace */ 0x1fffff, /* src_mask */ 0x1fffff, /* dst_mask */ TRUE), /* pcrel_offset */ /* Get to the page for the GOT entry for the symbol (G(S) - P) using an ADRP instruction. */ HOWTO (AARCH64_R (TLSDESC_ADR_PAGE21), /* type */ 12, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 21, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSDESC_ADR_PAGE21), /* name */ FALSE, /* partial_inplace */ 0x1fffff, /* src_mask */ 0x1fffff, /* dst_mask */ TRUE), /* pcrel_offset */ /* LD64: GOT offset G(S) & 0xff8. */ HOWTO64 (AARCH64_R (TLSDESC_LD64_LO12_NC), /* type */ 3, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSDESC_LD64_LO12_NC), /* name */ FALSE, /* partial_inplace */ 0xff8, /* src_mask */ 0xff8, /* dst_mask */ FALSE), /* pcrel_offset */ /* LD32: GOT offset G(S) & 0xffc. */ HOWTO32 (AARCH64_R (TLSDESC_LD32_LO12_NC), /* type */ 2, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSDESC_LD32_LO12_NC), /* name */ FALSE, /* partial_inplace */ 0xffc, /* src_mask */ 0xffc, /* dst_mask */ FALSE), /* pcrel_offset */ /* ADD: GOT offset G(S) & 0xfff. */ HOWTO (AARCH64_R (TLSDESC_ADD_LO12_NC), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSDESC_ADD_LO12_NC), /* name */ FALSE, /* partial_inplace */ 0xfff, /* src_mask */ 0xfff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO64 (AARCH64_R (TLSDESC_OFF_G1), /* type */ 16, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSDESC_OFF_G1), /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO64 (AARCH64_R (TLSDESC_OFF_G0_NC), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSDESC_OFF_G0_NC), /* name */ FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO64 (AARCH64_R (TLSDESC_LDR), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSDESC_LDR), /* name */ FALSE, /* partial_inplace */ 0x0, /* src_mask */ 0x0, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO64 (AARCH64_R (TLSDESC_ADD), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSDESC_ADD), /* name */ FALSE, /* partial_inplace */ 0x0, /* src_mask */ 0x0, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (AARCH64_R (TLSDESC_CALL), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSDESC_CALL), /* name */ FALSE, /* partial_inplace */ 0x0, /* src_mask */ 0x0, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (AARCH64_R (COPY), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 64, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (COPY), /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (AARCH64_R (GLOB_DAT), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 64, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (GLOB_DAT), /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (AARCH64_R (JUMP_SLOT), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 64, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (JUMP_SLOT), /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (AARCH64_R (RELATIVE), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 64, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (RELATIVE), /* name */ TRUE, /* partial_inplace */ ALL_ONES, /* src_mask */ ALL_ONES, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (AARCH64_R (TLS_DTPMOD), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 64, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ #if ARCH_SIZE == 64 AARCH64_R_STR (TLS_DTPMOD64), /* name */ #else AARCH64_R_STR (TLS_DTPMOD), /* name */ #endif FALSE, /* partial_inplace */ 0, /* src_mask */ ALL_ONES, /* dst_mask */ FALSE), /* pc_reloffset */ HOWTO (AARCH64_R (TLS_DTPREL), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 64, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ #if ARCH_SIZE == 64 AARCH64_R_STR (TLS_DTPREL64), /* name */ #else AARCH64_R_STR (TLS_DTPREL), /* name */ #endif FALSE, /* partial_inplace */ 0, /* src_mask */ ALL_ONES, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (AARCH64_R (TLS_TPREL), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 64, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ #if ARCH_SIZE == 64 AARCH64_R_STR (TLS_TPREL64), /* name */ #else AARCH64_R_STR (TLS_TPREL), /* name */ #endif FALSE, /* partial_inplace */ 0, /* src_mask */ ALL_ONES, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (AARCH64_R (TLSDESC), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 64, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (TLSDESC), /* name */ FALSE, /* partial_inplace */ 0, /* src_mask */ ALL_ONES, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (AARCH64_R (IRELATIVE), /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 64, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ AARCH64_R_STR (IRELATIVE), /* name */ FALSE, /* partial_inplace */ 0, /* src_mask */ ALL_ONES, /* dst_mask */ FALSE), /* pcrel_offset */ EMPTY_HOWTO (0), }; static reloc_howto_type elfNN_aarch64_howto_none = HOWTO (R_AARCH64_NONE, /* type */ 0, /* rightshift */ 0, /* size (0 = byte, 1 = short, 2 = long) */ 0, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_AARCH64_NONE", /* name */ FALSE, /* partial_inplace */ 0, /* src_mask */ 0, /* dst_mask */ FALSE); /* pcrel_offset */ /* Given HOWTO, return the bfd internal relocation enumerator. */ static bfd_reloc_code_real_type elfNN_aarch64_bfd_reloc_from_howto (reloc_howto_type *howto) { const int size = (int) ARRAY_SIZE (elfNN_aarch64_howto_table); const ptrdiff_t offset = howto - elfNN_aarch64_howto_table; if (offset > 0 && offset < size - 1) return BFD_RELOC_AARCH64_RELOC_START + offset; if (howto == &elfNN_aarch64_howto_none) return BFD_RELOC_AARCH64_NONE; return BFD_RELOC_AARCH64_RELOC_START; } /* Given R_TYPE, return the bfd internal relocation enumerator. */ static bfd_reloc_code_real_type elfNN_aarch64_bfd_reloc_from_type (unsigned int r_type) { static bfd_boolean initialized_p = FALSE; /* Indexed by R_TYPE, values are offsets in the howto_table. */ static unsigned int offsets[R_AARCH64_end]; if (initialized_p == FALSE) { unsigned int i; for (i = 1; i < ARRAY_SIZE (elfNN_aarch64_howto_table) - 1; ++i) if (elfNN_aarch64_howto_table[i].type != 0) offsets[elfNN_aarch64_howto_table[i].type] = i; initialized_p = TRUE; } if (r_type == R_AARCH64_NONE || r_type == R_AARCH64_NULL) return BFD_RELOC_AARCH64_NONE; return BFD_RELOC_AARCH64_RELOC_START + offsets[r_type]; } struct elf_aarch64_reloc_map { bfd_reloc_code_real_type from; bfd_reloc_code_real_type to; }; /* Map bfd generic reloc to AArch64-specific reloc. */ static const struct elf_aarch64_reloc_map elf_aarch64_reloc_map[] = { {BFD_RELOC_NONE, BFD_RELOC_AARCH64_NONE}, /* Basic data relocations. */ {BFD_RELOC_CTOR, BFD_RELOC_AARCH64_NN}, {BFD_RELOC_64, BFD_RELOC_AARCH64_64}, {BFD_RELOC_32, BFD_RELOC_AARCH64_32}, {BFD_RELOC_16, BFD_RELOC_AARCH64_16}, {BFD_RELOC_64_PCREL, BFD_RELOC_AARCH64_64_PCREL}, {BFD_RELOC_32_PCREL, BFD_RELOC_AARCH64_32_PCREL}, {BFD_RELOC_16_PCREL, BFD_RELOC_AARCH64_16_PCREL}, }; /* Given the bfd internal relocation enumerator in CODE, return the corresponding howto entry. */ static reloc_howto_type * elfNN_aarch64_howto_from_bfd_reloc (bfd_reloc_code_real_type code) { unsigned int i; /* Convert bfd generic reloc to AArch64-specific reloc. */ if (code < BFD_RELOC_AARCH64_RELOC_START || code > BFD_RELOC_AARCH64_RELOC_END) for (i = 0; i < ARRAY_SIZE (elf_aarch64_reloc_map); i++) if (elf_aarch64_reloc_map[i].from == code) { code = elf_aarch64_reloc_map[i].to; break; } if (code > BFD_RELOC_AARCH64_RELOC_START && code < BFD_RELOC_AARCH64_RELOC_END) if (elfNN_aarch64_howto_table[code - BFD_RELOC_AARCH64_RELOC_START].type) return &elfNN_aarch64_howto_table[code - BFD_RELOC_AARCH64_RELOC_START]; if (code == BFD_RELOC_AARCH64_NONE) return &elfNN_aarch64_howto_none; return NULL; } static reloc_howto_type * elfNN_aarch64_howto_from_type (unsigned int r_type) { bfd_reloc_code_real_type val; reloc_howto_type *howto; #if ARCH_SIZE == 32 if (r_type > 256) { bfd_set_error (bfd_error_bad_value); return NULL; } #endif if (r_type == R_AARCH64_NONE) return &elfNN_aarch64_howto_none; val = elfNN_aarch64_bfd_reloc_from_type (r_type); howto = elfNN_aarch64_howto_from_bfd_reloc (val); if (howto != NULL) return howto; bfd_set_error (bfd_error_bad_value); return NULL; } static void elfNN_aarch64_info_to_howto (bfd *abfd ATTRIBUTE_UNUSED, arelent *bfd_reloc, Elf_Internal_Rela *elf_reloc) { unsigned int r_type; r_type = ELFNN_R_TYPE (elf_reloc->r_info); bfd_reloc->howto = elfNN_aarch64_howto_from_type (r_type); } static reloc_howto_type * elfNN_aarch64_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, bfd_reloc_code_real_type code) { reloc_howto_type *howto = elfNN_aarch64_howto_from_bfd_reloc (code); if (howto != NULL) return howto; bfd_set_error (bfd_error_bad_value); return NULL; } static reloc_howto_type * elfNN_aarch64_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, const char *r_name) { unsigned int i; for (i = 1; i < ARRAY_SIZE (elfNN_aarch64_howto_table) - 1; ++i) if (elfNN_aarch64_howto_table[i].name != NULL && strcasecmp (elfNN_aarch64_howto_table[i].name, r_name) == 0) return &elfNN_aarch64_howto_table[i]; return NULL; } #define TARGET_LITTLE_SYM aarch64_elfNN_le_vec #define TARGET_LITTLE_NAME "elfNN-littleaarch64" #define TARGET_BIG_SYM aarch64_elfNN_be_vec #define TARGET_BIG_NAME "elfNN-bigaarch64" /* The linker script knows the section names for placement. The entry_names are used to do simple name mangling on the stubs. Given a function name, and its type, the stub can be found. The name can be changed. The only requirement is the %s be present. */ #define STUB_ENTRY_NAME "__%s_veneer" /* The name of the dynamic interpreter. This is put in the .interp section. */ #define ELF_DYNAMIC_INTERPRETER "/lib/ld.so.1" #define AARCH64_MAX_FWD_BRANCH_OFFSET \ (((1 << 25) - 1) << 2) #define AARCH64_MAX_BWD_BRANCH_OFFSET \ (-((1 << 25) << 2)) #define AARCH64_MAX_ADRP_IMM ((1 << 20) - 1) #define AARCH64_MIN_ADRP_IMM (-(1 << 20)) static int aarch64_valid_for_adrp_p (bfd_vma value, bfd_vma place) { bfd_signed_vma offset = (bfd_signed_vma) (PG (value) - PG (place)) >> 12; return offset <= AARCH64_MAX_ADRP_IMM && offset >= AARCH64_MIN_ADRP_IMM; } static int aarch64_valid_branch_p (bfd_vma value, bfd_vma place) { bfd_signed_vma offset = (bfd_signed_vma) (value - place); return (offset <= AARCH64_MAX_FWD_BRANCH_OFFSET && offset >= AARCH64_MAX_BWD_BRANCH_OFFSET); } static const uint32_t aarch64_adrp_branch_stub [] = { 0x90000010, /* adrp ip0, X */ /* R_AARCH64_ADR_HI21_PCREL(X) */ 0x91000210, /* add ip0, ip0, :lo12:X */ /* R_AARCH64_ADD_ABS_LO12_NC(X) */ 0xd61f0200, /* br ip0 */ }; static const uint32_t aarch64_long_branch_stub[] = { #if ARCH_SIZE == 64 0x58000090, /* ldr ip0, 1f */ #else 0x18000090, /* ldr wip0, 1f */ #endif 0x10000011, /* adr ip1, #0 */ 0x8b110210, /* add ip0, ip0, ip1 */ 0xd61f0200, /* br ip0 */ 0x00000000, /* 1: .xword or .word R_AARCH64_PRELNN(X) + 12 */ 0x00000000, }; static const uint32_t aarch64_erratum_835769_stub[] = { 0x00000000, /* Placeholder for multiply accumulate. */ 0x14000000, /* b