| /* PowerPC64-specific support for 64-bit ELF. |
| Copyright (C) 1999-2024 Free Software Foundation, Inc. |
| Written by Linus Nordberg, Swox AB <info@swox.com>, |
| based on elf32-ppc.c by Ian Lance Taylor. |
| Largely rewritten by Alan Modra. |
| |
| 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; if not, write to the Free Software Foundation, Inc., |
| 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ |
| |
| |
| /* The 64-bit PowerPC ELF ABI may be found at |
| http://www.linuxbase.org/spec/ELF/ppc64/PPC-elf64abi.txt, and |
| http://www.linuxbase.org/spec/ELF/ppc64/spec/book1.html */ |
| |
| /* The assembler should generate a full set of section symbols even |
| when they appear unused. The linux kernel build tool recordmcount |
| needs them. */ |
| #define TARGET_KEEP_UNUSED_SECTION_SYMBOLS true |
| |
| #include "sysdep.h" |
| #include <stdarg.h> |
| #include "bfd.h" |
| #include "bfdlink.h" |
| #include "libbfd.h" |
| #include "elf-bfd.h" |
| #include "elf/ppc64.h" |
| #include "elf64-ppc.h" |
| #include "dwarf2.h" |
| |
| /* All users of this file have bfd_octets_per_byte (abfd, sec) == 1. */ |
| #define OCTETS_PER_BYTE(ABFD, SEC) 1 |
| |
| static bfd_reloc_status_type ppc64_elf_ha_reloc |
| (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); |
| static bfd_reloc_status_type ppc64_elf_branch_reloc |
| (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); |
| static bfd_reloc_status_type ppc64_elf_brtaken_reloc |
| (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); |
| static bfd_reloc_status_type ppc64_elf_sectoff_reloc |
| (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); |
| static bfd_reloc_status_type ppc64_elf_sectoff_ha_reloc |
| (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); |
| static bfd_reloc_status_type ppc64_elf_toc_reloc |
| (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); |
| static bfd_reloc_status_type ppc64_elf_toc_ha_reloc |
| (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); |
| static bfd_reloc_status_type ppc64_elf_toc64_reloc |
| (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); |
| static bfd_reloc_status_type ppc64_elf_prefix_reloc |
| (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); |
| static bfd_reloc_status_type ppc64_elf_unhandled_reloc |
| (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); |
| static bfd_vma opd_entry_value |
| (asection *, bfd_vma, asection **, bfd_vma *, bool); |
| |
| #define TARGET_LITTLE_SYM powerpc_elf64_le_vec |
| #define TARGET_LITTLE_NAME "elf64-powerpcle" |
| #define TARGET_BIG_SYM powerpc_elf64_vec |
| #define TARGET_BIG_NAME "elf64-powerpc" |
| #define ELF_ARCH bfd_arch_powerpc |
| #define ELF_TARGET_ID PPC64_ELF_DATA |
| #define ELF_MACHINE_CODE EM_PPC64 |
| #define ELF_MAXPAGESIZE 0x10000 |
| #define ELF_COMMONPAGESIZE 0x1000 |
| #define elf_info_to_howto ppc64_elf_info_to_howto |
| |
| #define elf_backend_want_got_sym 0 |
| #define elf_backend_want_plt_sym 0 |
| #define elf_backend_plt_alignment 3 |
| #define elf_backend_plt_not_loaded 1 |
| #define elf_backend_got_header_size 8 |
| #define elf_backend_want_dynrelro 1 |
| #define elf_backend_can_gc_sections 1 |
| #define elf_backend_can_refcount 1 |
| #define elf_backend_rela_normal 1 |
| #define elf_backend_dtrel_excludes_plt 1 |
| #define elf_backend_default_execstack 0 |
| |
| #define bfd_elf64_mkobject ppc64_elf_mkobject |
| #define bfd_elf64_bfd_free_cached_info ppc64_elf_free_cached_info |
| #define bfd_elf64_bfd_reloc_type_lookup ppc64_elf_reloc_type_lookup |
| #define bfd_elf64_bfd_reloc_name_lookup ppc64_elf_reloc_name_lookup |
| #define bfd_elf64_bfd_merge_private_bfd_data ppc64_elf_merge_private_bfd_data |
| #define bfd_elf64_bfd_print_private_bfd_data ppc64_elf_print_private_bfd_data |
| #define bfd_elf64_new_section_hook ppc64_elf_new_section_hook |
| #define bfd_elf64_bfd_link_hash_table_create ppc64_elf_link_hash_table_create |
| #define bfd_elf64_get_synthetic_symtab ppc64_elf_get_synthetic_symtab |
| #define bfd_elf64_bfd_link_just_syms ppc64_elf_link_just_syms |
| #define bfd_elf64_bfd_gc_sections ppc64_elf_gc_sections |
| |
| #define elf_backend_object_p ppc64_elf_object_p |
| #define elf_backend_grok_prstatus ppc64_elf_grok_prstatus |
| #define elf_backend_grok_psinfo ppc64_elf_grok_psinfo |
| #define elf_backend_write_core_note ppc64_elf_write_core_note |
| #define elf_backend_create_dynamic_sections _bfd_elf_create_dynamic_sections |
| #define elf_backend_copy_indirect_symbol ppc64_elf_copy_indirect_symbol |
| #define elf_backend_add_symbol_hook ppc64_elf_add_symbol_hook |
| #define elf_backend_check_directives ppc64_elf_before_check_relocs |
| #define elf_backend_notice_as_needed ppc64_elf_notice_as_needed |
| #define elf_backend_archive_symbol_lookup ppc64_elf_archive_symbol_lookup |
| #define elf_backend_check_relocs ppc64_elf_check_relocs |
| #define elf_backend_relocs_compatible _bfd_elf_relocs_compatible |
| #define elf_backend_gc_keep ppc64_elf_gc_keep |
| #define elf_backend_gc_mark_dynamic_ref ppc64_elf_gc_mark_dynamic_ref |
| #define elf_backend_gc_mark_hook ppc64_elf_gc_mark_hook |
| #define elf_backend_adjust_dynamic_symbol ppc64_elf_adjust_dynamic_symbol |
| #define elf_backend_hide_symbol ppc64_elf_hide_symbol |
| #define elf_backend_maybe_function_sym ppc64_elf_maybe_function_sym |
| #define elf_backend_early_size_sections ppc64_elf_edit |
| #define elf_backend_late_size_sections ppc64_elf_late_size_sections |
| #define elf_backend_hash_symbol ppc64_elf_hash_symbol |
| #define elf_backend_init_index_section _bfd_elf_init_2_index_sections |
| #define elf_backend_action_discarded ppc64_elf_action_discarded |
| #define elf_backend_relocate_section ppc64_elf_relocate_section |
| #define elf_backend_finish_dynamic_symbol ppc64_elf_finish_dynamic_symbol |
| #define elf_backend_reloc_type_class ppc64_elf_reloc_type_class |
| #define elf_backend_finish_dynamic_sections ppc64_elf_finish_dynamic_sections |
| #define elf_backend_link_output_symbol_hook ppc64_elf_output_symbol_hook |
| #define elf_backend_special_sections ppc64_elf_special_sections |
| #define elf_backend_section_flags ppc64_elf_section_flags |
| #define elf_backend_merge_symbol_attribute ppc64_elf_merge_symbol_attribute |
| #define elf_backend_merge_symbol ppc64_elf_merge_symbol |
| #define elf_backend_get_reloc_section bfd_get_section_by_name |
| |
| /* The name of the dynamic interpreter. This is put in the .interp |
| section. */ |
| #define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so.1" |
| |
| /* The size in bytes of an entry in the procedure linkage table. */ |
| #define PLT_ENTRY_SIZE(htab) (htab->opd_abi ? 24 : 8) |
| #define LOCAL_PLT_ENTRY_SIZE(htab) (htab->opd_abi ? 16 : 8) |
| |
| /* The initial size of the plt reserved for the dynamic linker. */ |
| #define PLT_INITIAL_ENTRY_SIZE(htab) (htab->opd_abi ? 24 : 16) |
| |
| /* Offsets to some stack save slots. */ |
| #define STK_LR 16 |
| #define STK_TOC(htab) (htab->opd_abi ? 40 : 24) |
| /* This one is dodgy. ELFv2 does not have a linker word, so use the |
| CR save slot. Used only by optimised __tls_get_addr call stub, |
| relying on __tls_get_addr_opt not saving CR.. */ |
| #define STK_LINKER(htab) (htab->opd_abi ? 32 : 8) |
| |
| /* TOC base pointers offset from start of TOC. */ |
| #define TOC_BASE_OFF 0x8000 |
| /* TOC base alignment. */ |
| #define TOC_BASE_ALIGN 256 |
| |
| /* Offset of tp and dtp pointers from start of TLS block. */ |
| #define TP_OFFSET 0x7000 |
| #define DTP_OFFSET 0x8000 |
| |
| /* .plt call stub instructions. The normal stub is like this, but |
| sometimes the .plt entry crosses a 64k boundary and we need to |
| insert an addi to adjust r11. */ |
| #define STD_R2_0R1 0xf8410000 /* std %r2,0+40(%r1) */ |
| #define ADDIS_R11_R2 0x3d620000 /* addis %r11,%r2,xxx@ha */ |
| #define LD_R12_0R11 0xe98b0000 /* ld %r12,xxx+0@l(%r11) */ |
| #define MTCTR_R12 0x7d8903a6 /* mtctr %r12 */ |
| #define LD_R2_0R11 0xe84b0000 /* ld %r2,xxx+8@l(%r11) */ |
| #define LD_R11_0R11 0xe96b0000 /* ld %r11,xxx+16@l(%r11) */ |
| #define BCTR 0x4e800420 /* bctr */ |
| |
| #define ADDI_R11_R11 0x396b0000 /* addi %r11,%r11,off@l */ |
| #define ADDI_R12_R11 0x398b0000 /* addi %r12,%r11,off@l */ |
| #define ADDI_R12_R12 0x398c0000 /* addi %r12,%r12,off@l */ |
| #define ADDIS_R2_R2 0x3c420000 /* addis %r2,%r2,off@ha */ |
| #define ADDI_R2_R2 0x38420000 /* addi %r2,%r2,off@l */ |
| |
| #define XOR_R2_R12_R12 0x7d826278 /* xor %r2,%r12,%r12 */ |
| #define ADD_R11_R11_R2 0x7d6b1214 /* add %r11,%r11,%r2 */ |
| #define XOR_R11_R12_R12 0x7d8b6278 /* xor %r11,%r12,%r12 */ |
| #define ADD_R2_R2_R11 0x7c425a14 /* add %r2,%r2,%r11 */ |
| #define CMPLDI_R2_0 0x28220000 /* cmpldi %r2,0 */ |
| #define BNECTR 0x4ca20420 /* bnectr+ */ |
| #define BNECTR_P4 0x4ce20420 /* bnectr+ */ |
| |
| #define LD_R12_0R2 0xe9820000 /* ld %r12,xxx+0(%r2) */ |
| #define LD_R11_0R2 0xe9620000 /* ld %r11,xxx+0(%r2) */ |
| #define LD_R2_0R2 0xe8420000 /* ld %r2,xxx+0(%r2) */ |
| |
| #define LD_R2_0R1 0xe8410000 /* ld %r2,0(%r1) */ |
| #define LD_R2_0R12 0xe84c0000 /* ld %r2,0(%r12) */ |
| #define ADD_R2_R2_R12 0x7c426214 /* add %r2,%r2,%r12 */ |
| |
| #define LI_R11_0 0x39600000 /* li %r11,0 */ |
| #define LIS_R2 0x3c400000 /* lis %r2,xxx@ha */ |
| #define LIS_R11 0x3d600000 /* lis %r11,xxx@ha */ |
| #define LIS_R12 0x3d800000 /* lis %r12,xxx@ha */ |
| #define ADDIS_R2_R12 0x3c4c0000 /* addis %r2,%r12,xxx@ha */ |
| #define ADDIS_R12_R2 0x3d820000 /* addis %r12,%r2,xxx@ha */ |
| #define ADDIS_R12_R11 0x3d8b0000 /* addis %r12,%r11,xxx@ha */ |
| #define ADDIS_R12_R12 0x3d8c0000 /* addis %r12,%r12,xxx@ha */ |
| #define ORIS_R12_R12_0 0x658c0000 /* oris %r12,%r12,xxx@hi */ |
| #define ORI_R11_R11_0 0x616b0000 /* ori %r11,%r11,xxx@l */ |
| #define ORI_R12_R12_0 0x618c0000 /* ori %r12,%r12,xxx@l */ |
| #define LD_R12_0R12 0xe98c0000 /* ld %r12,xxx@l(%r12) */ |
| #define SLDI_R11_R11_34 0x796b1746 /* sldi %r11,%r11,34 */ |
| #define SLDI_R12_R12_32 0x799c07c6 /* sldi %r12,%r12,32 */ |
| #define LDX_R12_R11_R12 0x7d8b602a /* ldx %r12,%r11,%r12 */ |
| #define ADD_R12_R11_R12 0x7d8b6214 /* add %r12,%r11,%r12 */ |
| #define PADDI_R12_PC 0x0610000039800000ULL |
| #define PLD_R12_PC 0x04100000e5800000ULL |
| #define PNOP 0x0700000000000000ULL |
| |
| /* __glink_PLTresolve stub instructions. We enter with the index in |
| R0 for ELFv1, and the address of a glink branch in R12 for ELFv2. */ |
| #define GLINK_PLTRESOLVE_SIZE(htab) \ |
| (8u + (htab->opd_abi ? 11 * 4 : htab->has_plt_localentry0 ? 14 * 4 : 13 * 4)) |
| /* 0: */ |
| /* .quad plt0-1f */ |
| /* __glink: */ |
| #define MFLR_R12 0x7d8802a6 /* mflr %12 */ |
| #define BCL_20_31 0x429f0005 /* bcl 20,31,1f */ |
| /* 1: */ |
| #define MFLR_R11 0x7d6802a6 /* mflr %11 */ |
| /* ld %2,(0b-1b)(%11) */ |
| #define MTLR_R12 0x7d8803a6 /* mtlr %12 */ |
| #define ADD_R11_R2_R11 0x7d625a14 /* add %11,%2,%11 */ |
| /* ld %12,0(%11) */ |
| /* ld %2,8(%11) */ |
| /* mtctr %12 */ |
| /* ld %11,16(%11) */ |
| /* bctr */ |
| |
| #define MFLR_R0 0x7c0802a6 /* mflr %r0 */ |
| #define MTLR_R0 0x7c0803a6 /* mtlr %r0 */ |
| #define SUB_R12_R12_R11 0x7d8b6050 /* subf %r12,%r11,%r12 */ |
| #define ADDI_R0_R12 0x380c0000 /* addi %r0,%r12,0 */ |
| #define SRDI_R0_R0_2 0x7800f082 /* rldicl %r0,%r0,62,2 */ |
| #define LD_R0_0R11 0xe80b0000 /* ld %r0,0(%r11) */ |
| #define ADD_R11_R0_R11 0x7d605a14 /* add %r11,%r0,%r11 */ |
| |
| /* Pad with this. */ |
| #define NOP 0x60000000 |
| |
| /* Some other nops. */ |
| #define CROR_151515 0x4def7b82 |
| #define CROR_313131 0x4ffffb82 |
| |
| /* .glink entries for the first 32k functions are two instructions. */ |
| #define LI_R0_0 0x38000000 /* li %r0,0 */ |
| #define B_DOT 0x48000000 /* b . */ |
| |
| /* After that, we need two instructions to load the index, followed by |
| a branch. */ |
| #define LIS_R0_0 0x3c000000 /* lis %r0,0 */ |
| #define ORI_R0_R0_0 0x60000000 /* ori %r0,%r0,0 */ |
| |
| /* Instructions used by the save and restore reg functions. */ |
| #define STD_R0_0R1 0xf8010000 /* std %r0,0(%r1) */ |
| #define STD_R0_0R12 0xf80c0000 /* std %r0,0(%r12) */ |
| #define LD_R0_0R1 0xe8010000 /* ld %r0,0(%r1) */ |
| #define LD_R0_0R12 0xe80c0000 /* ld %r0,0(%r12) */ |
| #define STFD_FR0_0R1 0xd8010000 /* stfd %fr0,0(%r1) */ |
| #define LFD_FR0_0R1 0xc8010000 /* lfd %fr0,0(%r1) */ |
| #define LI_R12_0 0x39800000 /* li %r12,0 */ |
| #define STVX_VR0_R12_R0 0x7c0c01ce /* stvx %v0,%r12,%r0 */ |
| #define LVX_VR0_R12_R0 0x7c0c00ce /* lvx %v0,%r12,%r0 */ |
| #define MTLR_R0 0x7c0803a6 /* mtlr %r0 */ |
| #define BLR 0x4e800020 /* blr */ |
| |
| /* Since .opd is an array of descriptors and each entry will end up |
| with identical R_PPC64_RELATIVE relocs, there is really no need to |
| propagate .opd relocs; The dynamic linker should be taught to |
| relocate .opd without reloc entries. */ |
| #ifndef NO_OPD_RELOCS |
| #define NO_OPD_RELOCS 0 |
| #endif |
| |
| #ifndef ARRAY_SIZE |
| #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0])) |
| #endif |
| |
| static inline int |
| abiversion (bfd *abfd) |
| { |
| return elf_elfheader (abfd)->e_flags & EF_PPC64_ABI; |
| } |
| |
| static inline void |
| set_abiversion (bfd *abfd, int ver) |
| { |
| elf_elfheader (abfd)->e_flags &= ~EF_PPC64_ABI; |
| elf_elfheader (abfd)->e_flags |= ver & EF_PPC64_ABI; |
| } |
| |
| #define is_ppc64_elf(bfd) \ |
| (bfd_get_flavour (bfd) == bfd_target_elf_flavour \ |
| && elf_object_id (bfd) == PPC64_ELF_DATA) |
| |
| /* Relocation HOWTO's. */ |
| /* Like other ELF RELA targets that don't apply multiple |
| field-altering relocations to the same localation, src_mask is |
| always zero and pcrel_offset is the same as pc_relative. |
| PowerPC can always use a zero bitpos, even when the field is not at |
| the LSB. For example, a REL24 could use rightshift=2, bisize=24 |
| and bitpos=2 which matches the ABI description, or as we do here, |
| rightshift=0, bitsize=26 and bitpos=0. */ |
| #define HOW(type, size, bitsize, mask, rightshift, pc_relative, \ |
| complain, special_func) \ |
| HOWTO (type, rightshift, size, bitsize, pc_relative, 0, \ |
| complain_overflow_ ## complain, special_func, \ |
| #type, false, 0, mask, pc_relative) |
| |
| static reloc_howto_type *ppc64_elf_howto_table[(int) R_PPC64_max]; |
| |
| static reloc_howto_type ppc64_elf_howto_raw[] = |
| { |
| /* This reloc does nothing. */ |
| HOW (R_PPC64_NONE, 0, 0, 0, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| /* A standard 32 bit relocation. */ |
| HOW (R_PPC64_ADDR32, 4, 32, 0xffffffff, 0, false, bitfield, |
| bfd_elf_generic_reloc), |
| |
| /* An absolute 26 bit branch; the lower two bits must be zero. |
| FIXME: we don't check that, we just clear them. */ |
| HOW (R_PPC64_ADDR24, 4, 26, 0x03fffffc, 0, false, bitfield, |
| bfd_elf_generic_reloc), |
| |
| /* A standard 16 bit relocation. */ |
| HOW (R_PPC64_ADDR16, 2, 16, 0xffff, 0, false, bitfield, |
| bfd_elf_generic_reloc), |
| |
| /* A 16 bit relocation without overflow. */ |
| HOW (R_PPC64_ADDR16_LO, 2, 16, 0xffff, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| /* Bits 16-31 of an address. */ |
| HOW (R_PPC64_ADDR16_HI, 2, 16, 0xffff, 16, false, signed, |
| bfd_elf_generic_reloc), |
| |
| /* Bits 16-31 of an address, plus 1 if the contents of the low 16 |
| bits, treated as a signed number, is negative. */ |
| HOW (R_PPC64_ADDR16_HA, 2, 16, 0xffff, 16, false, signed, |
| ppc64_elf_ha_reloc), |
| |
| /* An absolute 16 bit branch; the lower two bits must be zero. |
| FIXME: we don't check that, we just clear them. */ |
| HOW (R_PPC64_ADDR14, 4, 16, 0x0000fffc, 0, false, signed, |
| ppc64_elf_branch_reloc), |
| |
| /* An absolute 16 bit branch, for which bit 10 should be set to |
| indicate that the branch is expected to be taken. The lower two |
| bits must be zero. */ |
| HOW (R_PPC64_ADDR14_BRTAKEN, 4, 16, 0x0000fffc, 0, false, signed, |
| ppc64_elf_brtaken_reloc), |
| |
| /* An absolute 16 bit branch, for which bit 10 should be set to |
| indicate that the branch is not expected to be taken. The lower |
| two bits must be zero. */ |
| HOW (R_PPC64_ADDR14_BRNTAKEN, 4, 16, 0x0000fffc, 0, false, signed, |
| ppc64_elf_brtaken_reloc), |
| |
| /* A relative 26 bit branch; the lower two bits must be zero. */ |
| HOW (R_PPC64_REL24, 4, 26, 0x03fffffc, 0, true, signed, |
| ppc64_elf_branch_reloc), |
| |
| /* A variant of R_PPC64_REL24, used when r2 is not the toc pointer. */ |
| HOW (R_PPC64_REL24_NOTOC, 4, 26, 0x03fffffc, 0, true, signed, |
| ppc64_elf_branch_reloc), |
| |
| /* Another variant, when p10 insns can't be used on stubs. */ |
| HOW (R_PPC64_REL24_P9NOTOC, 4, 26, 0x03fffffc, 0, true, signed, |
| ppc64_elf_branch_reloc), |
| |
| /* A relative 16 bit branch; the lower two bits must be zero. */ |
| HOW (R_PPC64_REL14, 4, 16, 0x0000fffc, 0, true, signed, |
| ppc64_elf_branch_reloc), |
| |
| /* A relative 16 bit branch. Bit 10 should be set to indicate that |
| the branch is expected to be taken. The lower two bits must be |
| zero. */ |
| HOW (R_PPC64_REL14_BRTAKEN, 4, 16, 0x0000fffc, 0, true, signed, |
| ppc64_elf_brtaken_reloc), |
| |
| /* A relative 16 bit branch. Bit 10 should be set to indicate that |
| the branch is not expected to be taken. The lower two bits must |
| be zero. */ |
| HOW (R_PPC64_REL14_BRNTAKEN, 4, 16, 0x0000fffc, 0, true, signed, |
| ppc64_elf_brtaken_reloc), |
| |
| /* Like R_PPC64_ADDR16, but referring to the GOT table entry for the |
| symbol. */ |
| HOW (R_PPC64_GOT16, 2, 16, 0xffff, 0, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like R_PPC64_ADDR16_LO, but referring to the GOT table entry for |
| the symbol. */ |
| HOW (R_PPC64_GOT16_LO, 2, 16, 0xffff, 0, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like R_PPC64_ADDR16_HI, but referring to the GOT table entry for |
| the symbol. */ |
| HOW (R_PPC64_GOT16_HI, 2, 16, 0xffff, 16, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like R_PPC64_ADDR16_HA, but referring to the GOT table entry for |
| the symbol. */ |
| HOW (R_PPC64_GOT16_HA, 2, 16, 0xffff, 16, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* This is used only by the dynamic linker. The symbol should exist |
| both in the object being run and in some shared library. The |
| dynamic linker copies the data addressed by the symbol from the |
| shared library into the object, because the object being |
| run has to have the data at some particular address. */ |
| HOW (R_PPC64_COPY, 0, 0, 0, 0, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like R_PPC64_ADDR64, but used when setting global offset table |
| entries. */ |
| HOW (R_PPC64_GLOB_DAT, 8, 64, 0xffffffffffffffffULL, 0, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Created by the link editor. Marks a procedure linkage table |
| entry for a symbol. */ |
| HOW (R_PPC64_JMP_SLOT, 0, 0, 0, 0, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Used only by the dynamic linker. When the object is run, this |
| doubleword64 is set to the load address of the object, plus the |
| addend. */ |
| HOW (R_PPC64_RELATIVE, 8, 64, 0xffffffffffffffffULL, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| /* Like R_PPC64_ADDR32, but may be unaligned. */ |
| HOW (R_PPC64_UADDR32, 4, 32, 0xffffffff, 0, false, bitfield, |
| bfd_elf_generic_reloc), |
| |
| /* Like R_PPC64_ADDR16, but may be unaligned. */ |
| HOW (R_PPC64_UADDR16, 2, 16, 0xffff, 0, false, bitfield, |
| bfd_elf_generic_reloc), |
| |
| /* 32-bit PC relative. */ |
| HOW (R_PPC64_REL32, 4, 32, 0xffffffff, 0, true, signed, |
| bfd_elf_generic_reloc), |
| |
| /* 32-bit relocation to the symbol's procedure linkage table. */ |
| HOW (R_PPC64_PLT32, 4, 32, 0xffffffff, 0, false, bitfield, |
| ppc64_elf_unhandled_reloc), |
| |
| /* 32-bit PC relative relocation to the symbol's procedure linkage table. |
| FIXME: R_PPC64_PLTREL32 not supported. */ |
| HOW (R_PPC64_PLTREL32, 4, 32, 0xffffffff, 0, true, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like R_PPC64_ADDR16_LO, but referring to the PLT table entry for |
| the symbol. */ |
| HOW (R_PPC64_PLT16_LO, 2, 16, 0xffff, 0, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like R_PPC64_ADDR16_HI, but referring to the PLT table entry for |
| the symbol. */ |
| HOW (R_PPC64_PLT16_HI, 2, 16, 0xffff, 16, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like R_PPC64_ADDR16_HA, but referring to the PLT table entry for |
| the symbol. */ |
| HOW (R_PPC64_PLT16_HA, 2, 16, 0xffff, 16, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* 16-bit section relative relocation. */ |
| HOW (R_PPC64_SECTOFF, 2, 16, 0xffff, 0, false, signed, |
| ppc64_elf_sectoff_reloc), |
| |
| /* Like R_PPC64_SECTOFF, but no overflow warning. */ |
| HOW (R_PPC64_SECTOFF_LO, 2, 16, 0xffff, 0, false, dont, |
| ppc64_elf_sectoff_reloc), |
| |
| /* 16-bit upper half section relative relocation. */ |
| HOW (R_PPC64_SECTOFF_HI, 2, 16, 0xffff, 16, false, signed, |
| ppc64_elf_sectoff_reloc), |
| |
| /* 16-bit upper half adjusted section relative relocation. */ |
| HOW (R_PPC64_SECTOFF_HA, 2, 16, 0xffff, 16, false, signed, |
| ppc64_elf_sectoff_ha_reloc), |
| |
| /* Like R_PPC64_REL24 without touching the two least significant bits. */ |
| HOW (R_PPC64_REL30, 4, 30, 0xfffffffc, 2, true, dont, |
| bfd_elf_generic_reloc), |
| |
| /* Relocs in the 64-bit PowerPC ELF ABI, not in the 32-bit ABI. */ |
| |
| /* A standard 64-bit relocation. */ |
| HOW (R_PPC64_ADDR64, 8, 64, 0xffffffffffffffffULL, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| /* The bits 32-47 of an address. */ |
| HOW (R_PPC64_ADDR16_HIGHER, 2, 16, 0xffff, 32, false, dont, |
| bfd_elf_generic_reloc), |
| |
| /* The bits 32-47 of an address, plus 1 if the contents of the low |
| 16 bits, treated as a signed number, is negative. */ |
| HOW (R_PPC64_ADDR16_HIGHERA, 2, 16, 0xffff, 32, false, dont, |
| ppc64_elf_ha_reloc), |
| |
| /* The bits 48-63 of an address. */ |
| HOW (R_PPC64_ADDR16_HIGHEST, 2, 16, 0xffff, 48, false, dont, |
| bfd_elf_generic_reloc), |
| |
| /* The bits 48-63 of an address, plus 1 if the contents of the low |
| 16 bits, treated as a signed number, is negative. */ |
| HOW (R_PPC64_ADDR16_HIGHESTA, 2, 16, 0xffff, 48, false, dont, |
| ppc64_elf_ha_reloc), |
| |
| /* Like ADDR64, but may be unaligned. */ |
| HOW (R_PPC64_UADDR64, 8, 64, 0xffffffffffffffffULL, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| /* 64-bit relative relocation. */ |
| HOW (R_PPC64_REL64, 8, 64, 0xffffffffffffffffULL, 0, true, dont, |
| bfd_elf_generic_reloc), |
| |
| /* 64-bit relocation to the symbol's procedure linkage table. */ |
| HOW (R_PPC64_PLT64, 8, 64, 0xffffffffffffffffULL, 0, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* 64-bit PC relative relocation to the symbol's procedure linkage |
| table. */ |
| /* FIXME: R_PPC64_PLTREL64 not supported. */ |
| HOW (R_PPC64_PLTREL64, 8, 64, 0xffffffffffffffffULL, 0, true, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* 16 bit TOC-relative relocation. */ |
| /* R_PPC64_TOC16 47 half16* S + A - .TOC. */ |
| HOW (R_PPC64_TOC16, 2, 16, 0xffff, 0, false, signed, |
| ppc64_elf_toc_reloc), |
| |
| /* 16 bit TOC-relative relocation without overflow. */ |
| /* R_PPC64_TOC16_LO 48 half16 #lo (S + A - .TOC.) */ |
| HOW (R_PPC64_TOC16_LO, 2, 16, 0xffff, 0, false, dont, |
| ppc64_elf_toc_reloc), |
| |
| /* 16 bit TOC-relative relocation, high 16 bits. */ |
| /* R_PPC64_TOC16_HI 49 half16 #hi (S + A - .TOC.) */ |
| HOW (R_PPC64_TOC16_HI, 2, 16, 0xffff, 16, false, signed, |
| ppc64_elf_toc_reloc), |
| |
| /* 16 bit TOC-relative relocation, high 16 bits, plus 1 if the |
| contents of the low 16 bits, treated as a signed number, is |
| negative. */ |
| /* R_PPC64_TOC16_HA 50 half16 #ha (S + A - .TOC.) */ |
| HOW (R_PPC64_TOC16_HA, 2, 16, 0xffff, 16, false, signed, |
| ppc64_elf_toc_ha_reloc), |
| |
| /* 64-bit relocation; insert value of TOC base (.TOC.). */ |
| /* R_PPC64_TOC 51 doubleword64 .TOC. */ |
| HOW (R_PPC64_TOC, 8, 64, 0xffffffffffffffffULL, 0, false, dont, |
| ppc64_elf_toc64_reloc), |
| |
| /* Like R_PPC64_GOT16, but also informs the link editor that the |
| value to relocate may (!) refer to a PLT entry which the link |
| editor (a) may replace with the symbol value. If the link editor |
| is unable to fully resolve the symbol, it may (b) create a PLT |
| entry and store the address to the new PLT entry in the GOT. |
| This permits lazy resolution of function symbols at run time. |
| The link editor may also skip all of this and just (c) emit a |
| R_PPC64_GLOB_DAT to tie the symbol to the GOT entry. */ |
| /* FIXME: R_PPC64_PLTGOT16 not implemented. */ |
| HOW (R_PPC64_PLTGOT16, 2, 16, 0xffff, 0, false,signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like R_PPC64_PLTGOT16, but without overflow. */ |
| /* FIXME: R_PPC64_PLTGOT16_LO not implemented. */ |
| HOW (R_PPC64_PLTGOT16_LO, 2, 16, 0xffff, 0, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like R_PPC64_PLT_GOT16, but using bits 16-31 of the address. */ |
| /* FIXME: R_PPC64_PLTGOT16_HI not implemented. */ |
| HOW (R_PPC64_PLTGOT16_HI, 2, 16, 0xffff, 16, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like R_PPC64_PLT_GOT16, but using bits 16-31 of the address, plus |
| 1 if the contents of the low 16 bits, treated as a signed number, |
| is negative. */ |
| /* FIXME: R_PPC64_PLTGOT16_HA not implemented. */ |
| HOW (R_PPC64_PLTGOT16_HA, 2, 16, 0xffff, 16, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like R_PPC64_ADDR16, but for instructions with a DS field. */ |
| HOW (R_PPC64_ADDR16_DS, 2, 16, 0xfffc, 0, false, signed, |
| bfd_elf_generic_reloc), |
| |
| /* Like R_PPC64_ADDR16_LO, but for instructions with a DS field. */ |
| HOW (R_PPC64_ADDR16_LO_DS, 2, 16, 0xfffc, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| /* Like R_PPC64_GOT16, but for instructions with a DS field. */ |
| HOW (R_PPC64_GOT16_DS, 2, 16, 0xfffc, 0, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like R_PPC64_GOT16_LO, but for instructions with a DS field. */ |
| HOW (R_PPC64_GOT16_LO_DS, 2, 16, 0xfffc, 0, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like R_PPC64_PLT16_LO, but for instructions with a DS field. */ |
| HOW (R_PPC64_PLT16_LO_DS, 2, 16, 0xfffc, 0, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like R_PPC64_SECTOFF, but for instructions with a DS field. */ |
| HOW (R_PPC64_SECTOFF_DS, 2, 16, 0xfffc, 0, false, signed, |
| ppc64_elf_sectoff_reloc), |
| |
| /* Like R_PPC64_SECTOFF_LO, but for instructions with a DS field. */ |
| HOW (R_PPC64_SECTOFF_LO_DS, 2, 16, 0xfffc, 0, false, dont, |
| ppc64_elf_sectoff_reloc), |
| |
| /* Like R_PPC64_TOC16, but for instructions with a DS field. */ |
| HOW (R_PPC64_TOC16_DS, 2, 16, 0xfffc, 0, false, signed, |
| ppc64_elf_toc_reloc), |
| |
| /* Like R_PPC64_TOC16_LO, but for instructions with a DS field. */ |
| HOW (R_PPC64_TOC16_LO_DS, 2, 16, 0xfffc, 0, false, dont, |
| ppc64_elf_toc_reloc), |
| |
| /* Like R_PPC64_PLTGOT16, but for instructions with a DS field. */ |
| /* FIXME: R_PPC64_PLTGOT16_DS not implemented. */ |
| HOW (R_PPC64_PLTGOT16_DS, 2, 16, 0xfffc, 0, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like R_PPC64_PLTGOT16_LO, but for instructions with a DS field. */ |
| /* FIXME: R_PPC64_PLTGOT16_LO not implemented. */ |
| HOW (R_PPC64_PLTGOT16_LO_DS, 2, 16, 0xfffc, 0, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Marker relocs for TLS. */ |
| HOW (R_PPC64_TLS, 4, 32, 0, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| HOW (R_PPC64_TLSGD, 4, 32, 0, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| HOW (R_PPC64_TLSLD, 4, 32, 0, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| /* Marker reloc for optimizing r2 save in prologue rather than on |
| each plt call stub. */ |
| HOW (R_PPC64_TOCSAVE, 4, 32, 0, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| /* Marker relocs on inline plt call instructions. */ |
| HOW (R_PPC64_PLTSEQ, 4, 32, 0, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| HOW (R_PPC64_PLTCALL, 4, 32, 0, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| /* Computes the load module index of the load module that contains the |
| definition of its TLS sym. */ |
| HOW (R_PPC64_DTPMOD64, 8, 64, 0xffffffffffffffffULL, 0, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Computes a dtv-relative displacement, the difference between the value |
| of sym+add and the base address of the thread-local storage block that |
| contains the definition of sym, minus 0x8000. */ |
| HOW (R_PPC64_DTPREL64, 8, 64, 0xffffffffffffffffULL, 0, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* A 16 bit dtprel reloc. */ |
| HOW (R_PPC64_DTPREL16, 2, 16, 0xffff, 0, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like DTPREL16, but no overflow. */ |
| HOW (R_PPC64_DTPREL16_LO, 2, 16, 0xffff, 0, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like DTPREL16_LO, but next higher group of 16 bits. */ |
| HOW (R_PPC64_DTPREL16_HI, 2, 16, 0xffff, 16, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like DTPREL16_HI, but adjust for low 16 bits. */ |
| HOW (R_PPC64_DTPREL16_HA, 2, 16, 0xffff, 16, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like DTPREL16_HI, but next higher group of 16 bits. */ |
| HOW (R_PPC64_DTPREL16_HIGHER, 2, 16, 0xffff, 32, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like DTPREL16_HIGHER, but adjust for low 16 bits. */ |
| HOW (R_PPC64_DTPREL16_HIGHERA, 2, 16, 0xffff, 32, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like DTPREL16_HIGHER, but next higher group of 16 bits. */ |
| HOW (R_PPC64_DTPREL16_HIGHEST, 2, 16, 0xffff, 48, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like DTPREL16_HIGHEST, but adjust for low 16 bits. */ |
| HOW (R_PPC64_DTPREL16_HIGHESTA, 2, 16, 0xffff, 48, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like DTPREL16, but for insns with a DS field. */ |
| HOW (R_PPC64_DTPREL16_DS, 2, 16, 0xfffc, 0, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like DTPREL16_DS, but no overflow. */ |
| HOW (R_PPC64_DTPREL16_LO_DS, 2, 16, 0xfffc, 0, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Computes a tp-relative displacement, the difference between the value of |
| sym+add and the value of the thread pointer (r13). */ |
| HOW (R_PPC64_TPREL64, 8, 64, 0xffffffffffffffffULL, 0, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* A 16 bit tprel reloc. */ |
| HOW (R_PPC64_TPREL16, 2, 16, 0xffff, 0, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like TPREL16, but no overflow. */ |
| HOW (R_PPC64_TPREL16_LO, 2, 16, 0xffff, 0, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like TPREL16_LO, but next higher group of 16 bits. */ |
| HOW (R_PPC64_TPREL16_HI, 2, 16, 0xffff, 16, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like TPREL16_HI, but adjust for low 16 bits. */ |
| HOW (R_PPC64_TPREL16_HA, 2, 16, 0xffff, 16, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like TPREL16_HI, but next higher group of 16 bits. */ |
| HOW (R_PPC64_TPREL16_HIGHER, 2, 16, 0xffff, 32, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like TPREL16_HIGHER, but adjust for low 16 bits. */ |
| HOW (R_PPC64_TPREL16_HIGHERA, 2, 16, 0xffff, 32, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like TPREL16_HIGHER, but next higher group of 16 bits. */ |
| HOW (R_PPC64_TPREL16_HIGHEST, 2, 16, 0xffff, 48, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like TPREL16_HIGHEST, but adjust for low 16 bits. */ |
| HOW (R_PPC64_TPREL16_HIGHESTA, 2, 16, 0xffff, 48, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like TPREL16, but for insns with a DS field. */ |
| HOW (R_PPC64_TPREL16_DS, 2, 16, 0xfffc, 0, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like TPREL16_DS, but no overflow. */ |
| HOW (R_PPC64_TPREL16_LO_DS, 2, 16, 0xfffc, 0, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Allocates two contiguous entries in the GOT to hold a tls_index structure, |
| with values (sym+add)@dtpmod and (sym+add)@dtprel, and computes the offset |
| to the first entry relative to the TOC base (r2). */ |
| HOW (R_PPC64_GOT_TLSGD16, 2, 16, 0xffff, 0, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like GOT_TLSGD16, but no overflow. */ |
| HOW (R_PPC64_GOT_TLSGD16_LO, 2, 16, 0xffff, 0, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like GOT_TLSGD16_LO, but next higher group of 16 bits. */ |
| HOW (R_PPC64_GOT_TLSGD16_HI, 2, 16, 0xffff, 16, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like GOT_TLSGD16_HI, but adjust for low 16 bits. */ |
| HOW (R_PPC64_GOT_TLSGD16_HA, 2, 16, 0xffff, 16, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Allocates two contiguous entries in the GOT to hold a tls_index structure, |
| with values (sym+add)@dtpmod and zero, and computes the offset to the |
| first entry relative to the TOC base (r2). */ |
| HOW (R_PPC64_GOT_TLSLD16, 2, 16, 0xffff, 0, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like GOT_TLSLD16, but no overflow. */ |
| HOW (R_PPC64_GOT_TLSLD16_LO, 2, 16, 0xffff, 0, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like GOT_TLSLD16_LO, but next higher group of 16 bits. */ |
| HOW (R_PPC64_GOT_TLSLD16_HI, 2, 16, 0xffff, 16, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like GOT_TLSLD16_HI, but adjust for low 16 bits. */ |
| HOW (R_PPC64_GOT_TLSLD16_HA, 2, 16, 0xffff, 16, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Allocates an entry in the GOT with value (sym+add)@dtprel, and computes |
| the offset to the entry relative to the TOC base (r2). */ |
| HOW (R_PPC64_GOT_DTPREL16_DS, 2, 16, 0xfffc, 0, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like GOT_DTPREL16_DS, but no overflow. */ |
| HOW (R_PPC64_GOT_DTPREL16_LO_DS, 2, 16, 0xfffc, 0, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like GOT_DTPREL16_LO_DS, but next higher group of 16 bits. */ |
| HOW (R_PPC64_GOT_DTPREL16_HI, 2, 16, 0xffff, 16, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like GOT_DTPREL16_HI, but adjust for low 16 bits. */ |
| HOW (R_PPC64_GOT_DTPREL16_HA, 2, 16, 0xffff, 16, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Allocates an entry in the GOT with value (sym+add)@tprel, and computes the |
| offset to the entry relative to the TOC base (r2). */ |
| HOW (R_PPC64_GOT_TPREL16_DS, 2, 16, 0xfffc, 0, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like GOT_TPREL16_DS, but no overflow. */ |
| HOW (R_PPC64_GOT_TPREL16_LO_DS, 2, 16, 0xfffc, 0, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like GOT_TPREL16_LO_DS, but next higher group of 16 bits. */ |
| HOW (R_PPC64_GOT_TPREL16_HI, 2, 16, 0xffff, 16, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like GOT_TPREL16_HI, but adjust for low 16 bits. */ |
| HOW (R_PPC64_GOT_TPREL16_HA, 2, 16, 0xffff, 16, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| HOW (R_PPC64_JMP_IREL, 0, 0, 0, 0, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| HOW (R_PPC64_IRELATIVE, 8, 64, 0xffffffffffffffffULL, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| /* A 16 bit relative relocation. */ |
| HOW (R_PPC64_REL16, 2, 16, 0xffff, 0, true, signed, |
| bfd_elf_generic_reloc), |
| |
| /* A 16 bit relative relocation without overflow. */ |
| HOW (R_PPC64_REL16_LO, 2, 16, 0xffff, 0, true, dont, |
| bfd_elf_generic_reloc), |
| |
| /* The high order 16 bits of a relative address. */ |
| HOW (R_PPC64_REL16_HI, 2, 16, 0xffff, 16, true, signed, |
| bfd_elf_generic_reloc), |
| |
| /* The high order 16 bits of a relative address, plus 1 if the contents of |
| the low 16 bits, treated as a signed number, is negative. */ |
| HOW (R_PPC64_REL16_HA, 2, 16, 0xffff, 16, true, signed, |
| ppc64_elf_ha_reloc), |
| |
| HOW (R_PPC64_REL16_HIGH, 2, 16, 0xffff, 16, true, dont, |
| bfd_elf_generic_reloc), |
| |
| HOW (R_PPC64_REL16_HIGHA, 2, 16, 0xffff, 16, true, dont, |
| ppc64_elf_ha_reloc), |
| |
| HOW (R_PPC64_REL16_HIGHER, 2, 16, 0xffff, 32, true, dont, |
| bfd_elf_generic_reloc), |
| |
| HOW (R_PPC64_REL16_HIGHERA, 2, 16, 0xffff, 32, true, dont, |
| ppc64_elf_ha_reloc), |
| |
| HOW (R_PPC64_REL16_HIGHEST, 2, 16, 0xffff, 48, true, dont, |
| bfd_elf_generic_reloc), |
| |
| HOW (R_PPC64_REL16_HIGHESTA, 2, 16, 0xffff, 48, true, dont, |
| ppc64_elf_ha_reloc), |
| |
| /* Like R_PPC64_REL16_HA but for split field in addpcis. */ |
| HOW (R_PPC64_REL16DX_HA, 4, 16, 0x1fffc1, 16, true, signed, |
| ppc64_elf_ha_reloc), |
| |
| /* A split-field reloc for addpcis, non-relative (gas internal use only). */ |
| HOW (R_PPC64_16DX_HA, 4, 16, 0x1fffc1, 16, false, signed, |
| ppc64_elf_ha_reloc), |
| |
| /* Like R_PPC64_ADDR16_HI, but no overflow. */ |
| HOW (R_PPC64_ADDR16_HIGH, 2, 16, 0xffff, 16, false, dont, |
| bfd_elf_generic_reloc), |
| |
| /* Like R_PPC64_ADDR16_HA, but no overflow. */ |
| HOW (R_PPC64_ADDR16_HIGHA, 2, 16, 0xffff, 16, false, dont, |
| ppc64_elf_ha_reloc), |
| |
| /* Like R_PPC64_DTPREL16_HI, but no overflow. */ |
| HOW (R_PPC64_DTPREL16_HIGH, 2, 16, 0xffff, 16, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like R_PPC64_DTPREL16_HA, but no overflow. */ |
| HOW (R_PPC64_DTPREL16_HIGHA, 2, 16, 0xffff, 16, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like R_PPC64_TPREL16_HI, but no overflow. */ |
| HOW (R_PPC64_TPREL16_HIGH, 2, 16, 0xffff, 16, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Like R_PPC64_TPREL16_HA, but no overflow. */ |
| HOW (R_PPC64_TPREL16_HIGHA, 2, 16, 0xffff, 16, false, dont, |
| ppc64_elf_unhandled_reloc), |
| |
| /* Marker reloc on ELFv2 large-model function entry. */ |
| HOW (R_PPC64_ENTRY, 4, 32, 0, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| /* Like ADDR64, but use local entry point of function. */ |
| HOW (R_PPC64_ADDR64_LOCAL, 8, 64, 0xffffffffffffffffULL, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| HOW (R_PPC64_PLTSEQ_NOTOC, 4, 32, 0, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| HOW (R_PPC64_PLTCALL_NOTOC, 4, 32, 0, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| HOW (R_PPC64_PCREL_OPT, 4, 32, 0, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| HOW (R_PPC64_D34, 8, 34, 0x3ffff0000ffffULL, 0, false, signed, |
| ppc64_elf_prefix_reloc), |
| |
| HOW (R_PPC64_D34_LO, 8, 34, 0x3ffff0000ffffULL, 0, false, dont, |
| ppc64_elf_prefix_reloc), |
| |
| HOW (R_PPC64_D34_HI30, 8, 34, 0x3ffff0000ffffULL, 34, false, dont, |
| ppc64_elf_prefix_reloc), |
| |
| HOW (R_PPC64_D34_HA30, 8, 34, 0x3ffff0000ffffULL, 34, false, dont, |
| ppc64_elf_prefix_reloc), |
| |
| HOW (R_PPC64_PCREL34, 8, 34, 0x3ffff0000ffffULL, 0, true, signed, |
| ppc64_elf_prefix_reloc), |
| |
| HOW (R_PPC64_GOT_PCREL34, 8, 34, 0x3ffff0000ffffULL, 0, true, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| HOW (R_PPC64_PLT_PCREL34, 8, 34, 0x3ffff0000ffffULL, 0, true, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| HOW (R_PPC64_PLT_PCREL34_NOTOC, 8, 34, 0x3ffff0000ffffULL, 0, true, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| HOW (R_PPC64_TPREL34, 8, 34, 0x3ffff0000ffffULL, 0, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| HOW (R_PPC64_DTPREL34, 8, 34, 0x3ffff0000ffffULL, 0, false, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| HOW (R_PPC64_GOT_TLSGD_PCREL34, 8, 34, 0x3ffff0000ffffULL, 0, true, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| HOW (R_PPC64_GOT_TLSLD_PCREL34, 8, 34, 0x3ffff0000ffffULL, 0, true, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| HOW (R_PPC64_GOT_TPREL_PCREL34, 8, 34, 0x3ffff0000ffffULL, 0, true, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| HOW (R_PPC64_GOT_DTPREL_PCREL34, 8, 34, 0x3ffff0000ffffULL, 0, true, signed, |
| ppc64_elf_unhandled_reloc), |
| |
| HOW (R_PPC64_ADDR16_HIGHER34, 2, 16, 0xffff, 34, false, dont, |
| bfd_elf_generic_reloc), |
| |
| HOW (R_PPC64_ADDR16_HIGHERA34, 2, 16, 0xffff, 34, false, dont, |
| ppc64_elf_ha_reloc), |
| |
| HOW (R_PPC64_ADDR16_HIGHEST34, 2, 16, 0xffff, 50, false, dont, |
| bfd_elf_generic_reloc), |
| |
| HOW (R_PPC64_ADDR16_HIGHESTA34, 2, 16, 0xffff, 50, false, dont, |
| ppc64_elf_ha_reloc), |
| |
| HOW (R_PPC64_REL16_HIGHER34, 2, 16, 0xffff, 34, true, dont, |
| bfd_elf_generic_reloc), |
| |
| HOW (R_PPC64_REL16_HIGHERA34, 2, 16, 0xffff, 34, true, dont, |
| ppc64_elf_ha_reloc), |
| |
| HOW (R_PPC64_REL16_HIGHEST34, 2, 16, 0xffff, 50, true, dont, |
| bfd_elf_generic_reloc), |
| |
| HOW (R_PPC64_REL16_HIGHESTA34, 2, 16, 0xffff, 50, true, dont, |
| ppc64_elf_ha_reloc), |
| |
| HOW (R_PPC64_D28, 8, 28, 0xfff0000ffffULL, 0, false, signed, |
| ppc64_elf_prefix_reloc), |
| |
| HOW (R_PPC64_PCREL28, 8, 28, 0xfff0000ffffULL, 0, true, signed, |
| ppc64_elf_prefix_reloc), |
| |
| /* GNU extension to record C++ vtable hierarchy. */ |
| HOW (R_PPC64_GNU_VTINHERIT, 0, 0, 0, 0, false, dont, |
| NULL), |
| |
| /* GNU extension to record C++ vtable member usage. */ |
| HOW (R_PPC64_GNU_VTENTRY, 0, 0, 0, 0, false, dont, |
| NULL), |
| }; |
| |
| |
| /* Initialize the ppc64_elf_howto_table, so that linear accesses can |
| be done. */ |
| |
| static void |
| ppc_howto_init (void) |
| { |
| unsigned int i, type; |
| |
| for (i = 0; i < ARRAY_SIZE (ppc64_elf_howto_raw); i++) |
| { |
| type = ppc64_elf_howto_raw[i].type; |
| BFD_ASSERT (type < ARRAY_SIZE (ppc64_elf_howto_table)); |
| ppc64_elf_howto_table[type] = &ppc64_elf_howto_raw[i]; |
| } |
| } |
| |
| static reloc_howto_type * |
| ppc64_elf_reloc_type_lookup (bfd *abfd, bfd_reloc_code_real_type code) |
| { |
| enum elf_ppc64_reloc_type r = R_PPC64_NONE; |
| |
| if (!ppc64_elf_howto_table[R_PPC64_ADDR32]) |
| /* Initialize howto table if needed. */ |
| ppc_howto_init (); |
| |
| switch (code) |
| { |
| default: |
| /* xgettext:c-format */ |
| _bfd_error_handler (_("%pB: unsupported relocation type %#x"), abfd, |
| (int) code); |
| bfd_set_error (bfd_error_bad_value); |
| return NULL; |
| |
| case BFD_RELOC_NONE: r = R_PPC64_NONE; |
| break; |
| case BFD_RELOC_32: r = R_PPC64_ADDR32; |
| break; |
| case BFD_RELOC_PPC_BA26: r = R_PPC64_ADDR24; |
| break; |
| case BFD_RELOC_16: r = R_PPC64_ADDR16; |
| break; |
| case BFD_RELOC_LO16: r = R_PPC64_ADDR16_LO; |
| break; |
| case BFD_RELOC_HI16: r = R_PPC64_ADDR16_HI; |
| break; |
| case BFD_RELOC_PPC64_ADDR16_HIGH: r = R_PPC64_ADDR16_HIGH; |
| break; |
| case BFD_RELOC_HI16_S: r = R_PPC64_ADDR16_HA; |
| break; |
| case BFD_RELOC_PPC64_ADDR16_HIGHA: r = R_PPC64_ADDR16_HIGHA; |
| break; |
| case BFD_RELOC_PPC_BA16: r = R_PPC64_ADDR14; |
| break; |
| case BFD_RELOC_PPC_BA16_BRTAKEN: r = R_PPC64_ADDR14_BRTAKEN; |
| break; |
| case BFD_RELOC_PPC_BA16_BRNTAKEN: r = R_PPC64_ADDR14_BRNTAKEN; |
| break; |
| case BFD_RELOC_PPC_B26: r = R_PPC64_REL24; |
| break; |
| case BFD_RELOC_PPC64_REL24_NOTOC: r = R_PPC64_REL24_NOTOC; |
| break; |
| case BFD_RELOC_PPC64_REL24_P9NOTOC: r = R_PPC64_REL24_P9NOTOC; |
| break; |
| case BFD_RELOC_PPC_B16: r = R_PPC64_REL14; |
| break; |
| case BFD_RELOC_PPC_B16_BRTAKEN: r = R_PPC64_REL14_BRTAKEN; |
| break; |
| case BFD_RELOC_PPC_B16_BRNTAKEN: r = R_PPC64_REL14_BRNTAKEN; |
| break; |
| case BFD_RELOC_16_GOTOFF: r = R_PPC64_GOT16; |
| break; |
| case BFD_RELOC_LO16_GOTOFF: r = R_PPC64_GOT16_LO; |
| break; |
| case BFD_RELOC_HI16_GOTOFF: r = R_PPC64_GOT16_HI; |
| break; |
| case BFD_RELOC_HI16_S_GOTOFF: r = R_PPC64_GOT16_HA; |
| break; |
| case BFD_RELOC_PPC_COPY: r = R_PPC64_COPY; |
| break; |
| case BFD_RELOC_PPC_GLOB_DAT: r = R_PPC64_GLOB_DAT; |
| break; |
| case BFD_RELOC_32_PCREL: r = R_PPC64_REL32; |
| break; |
| case BFD_RELOC_32_PLTOFF: r = R_PPC64_PLT32; |
| break; |
| case BFD_RELOC_32_PLT_PCREL: r = R_PPC64_PLTREL32; |
| break; |
| case BFD_RELOC_LO16_PLTOFF: r = R_PPC64_PLT16_LO; |
| break; |
| case BFD_RELOC_HI16_PLTOFF: r = R_PPC64_PLT16_HI; |
| break; |
| case BFD_RELOC_HI16_S_PLTOFF: r = R_PPC64_PLT16_HA; |
| break; |
| case BFD_RELOC_16_BASEREL: r = R_PPC64_SECTOFF; |
| break; |
| case BFD_RELOC_LO16_BASEREL: r = R_PPC64_SECTOFF_LO; |
| break; |
| case BFD_RELOC_HI16_BASEREL: r = R_PPC64_SECTOFF_HI; |
| break; |
| case BFD_RELOC_HI16_S_BASEREL: r = R_PPC64_SECTOFF_HA; |
| break; |
| case BFD_RELOC_CTOR: r = R_PPC64_ADDR64; |
| break; |
| case BFD_RELOC_64: r = R_PPC64_ADDR64; |
| break; |
| case BFD_RELOC_PPC64_HIGHER: r = R_PPC64_ADDR16_HIGHER; |
| break; |
| case BFD_RELOC_PPC64_HIGHER_S: r = R_PPC64_ADDR16_HIGHERA; |
| break; |
| case BFD_RELOC_PPC64_HIGHEST: r = R_PPC64_ADDR16_HIGHEST; |
| break; |
| case BFD_RELOC_PPC64_HIGHEST_S: r = R_PPC64_ADDR16_HIGHESTA; |
| break; |
| case BFD_RELOC_64_PCREL: r = R_PPC64_REL64; |
| break; |
| case BFD_RELOC_64_PLTOFF: r = R_PPC64_PLT64; |
| break; |
| case BFD_RELOC_64_PLT_PCREL: r = R_PPC64_PLTREL64; |
| break; |
| case BFD_RELOC_PPC_TOC16: r = R_PPC64_TOC16; |
| break; |
| case BFD_RELOC_PPC64_TOC16_LO: r = R_PPC64_TOC16_LO; |
| break; |
| case BFD_RELOC_PPC64_TOC16_HI: r = R_PPC64_TOC16_HI; |
| break; |
| case BFD_RELOC_PPC64_TOC16_HA: r = R_PPC64_TOC16_HA; |
| break; |
| case BFD_RELOC_PPC64_TOC: r = R_PPC64_TOC; |
| break; |
| case BFD_RELOC_PPC64_PLTGOT16: r = R_PPC64_PLTGOT16; |
| break; |
| case BFD_RELOC_PPC64_PLTGOT16_LO: r = R_PPC64_PLTGOT16_LO; |
| break; |
| case BFD_RELOC_PPC64_PLTGOT16_HI: r = R_PPC64_PLTGOT16_HI; |
| break; |
| case BFD_RELOC_PPC64_PLTGOT16_HA: r = R_PPC64_PLTGOT16_HA; |
| break; |
| case BFD_RELOC_PPC64_ADDR16_DS: r = R_PPC64_ADDR16_DS; |
| break; |
| case BFD_RELOC_PPC64_ADDR16_LO_DS: r = R_PPC64_ADDR16_LO_DS; |
| break; |
| case BFD_RELOC_PPC64_GOT16_DS: r = R_PPC64_GOT16_DS; |
| break; |
| case BFD_RELOC_PPC64_GOT16_LO_DS: r = R_PPC64_GOT16_LO_DS; |
| break; |
| case BFD_RELOC_PPC64_PLT16_LO_DS: r = R_PPC64_PLT16_LO_DS; |
| break; |
| case BFD_RELOC_PPC64_SECTOFF_DS: r = R_PPC64_SECTOFF_DS; |
| break; |
| case BFD_RELOC_PPC64_SECTOFF_LO_DS: r = R_PPC64_SECTOFF_LO_DS; |
| break; |
| case BFD_RELOC_PPC64_TOC16_DS: r = R_PPC64_TOC16_DS; |
| break; |
| case BFD_RELOC_PPC64_TOC16_LO_DS: r = R_PPC64_TOC16_LO_DS; |
| break; |
| case BFD_RELOC_PPC64_PLTGOT16_DS: r = R_PPC64_PLTGOT16_DS; |
| break; |
| case BFD_RELOC_PPC64_PLTGOT16_LO_DS: r = R_PPC64_PLTGOT16_LO_DS; |
| break; |
| case BFD_RELOC_PPC64_TLS_PCREL: |
| case BFD_RELOC_PPC_TLS: r = R_PPC64_TLS; |
| break; |
| case BFD_RELOC_PPC_TLSGD: r = R_PPC64_TLSGD; |
| break; |
| case BFD_RELOC_PPC_TLSLD: r = R_PPC64_TLSLD; |
| break; |
| case BFD_RELOC_PPC_DTPMOD: r = R_PPC64_DTPMOD64; |
| break; |
| case BFD_RELOC_PPC_TPREL16: r = R_PPC64_TPREL16; |
| break; |
| case BFD_RELOC_PPC_TPREL16_LO: r = R_PPC64_TPREL16_LO; |
| break; |
| case BFD_RELOC_PPC_TPREL16_HI: r = R_PPC64_TPREL16_HI; |
| break; |
| case BFD_RELOC_PPC64_TPREL16_HIGH: r = R_PPC64_TPREL16_HIGH; |
| break; |
| case BFD_RELOC_PPC_TPREL16_HA: r = R_PPC64_TPREL16_HA; |
| break; |
| case BFD_RELOC_PPC64_TPREL16_HIGHA: r = R_PPC64_TPREL16_HIGHA; |
| break; |
| case BFD_RELOC_PPC_TPREL: r = R_PPC64_TPREL64; |
| break; |
| case BFD_RELOC_PPC_DTPREL16: r = R_PPC64_DTPREL16; |
| break; |
| case BFD_RELOC_PPC_DTPREL16_LO: r = R_PPC64_DTPREL16_LO; |
| break; |
| case BFD_RELOC_PPC_DTPREL16_HI: r = R_PPC64_DTPREL16_HI; |
| break; |
| case BFD_RELOC_PPC64_DTPREL16_HIGH: r = R_PPC64_DTPREL16_HIGH; |
| break; |
| case BFD_RELOC_PPC_DTPREL16_HA: r = R_PPC64_DTPREL16_HA; |
| break; |
| case BFD_RELOC_PPC64_DTPREL16_HIGHA: r = R_PPC64_DTPREL16_HIGHA; |
| break; |
| case BFD_RELOC_PPC_DTPREL: r = R_PPC64_DTPREL64; |
| break; |
| case BFD_RELOC_PPC_GOT_TLSGD16: r = R_PPC64_GOT_TLSGD16; |
| break; |
| case BFD_RELOC_PPC_GOT_TLSGD16_LO: r = R_PPC64_GOT_TLSGD16_LO; |
| break; |
| case BFD_RELOC_PPC_GOT_TLSGD16_HI: r = R_PPC64_GOT_TLSGD16_HI; |
| break; |
| case BFD_RELOC_PPC_GOT_TLSGD16_HA: r = R_PPC64_GOT_TLSGD16_HA; |
| break; |
| case BFD_RELOC_PPC_GOT_TLSLD16: r = R_PPC64_GOT_TLSLD16; |
| break; |
| case BFD_RELOC_PPC_GOT_TLSLD16_LO: r = R_PPC64_GOT_TLSLD16_LO; |
| break; |
| case BFD_RELOC_PPC_GOT_TLSLD16_HI: r = R_PPC64_GOT_TLSLD16_HI; |
| break; |
| case BFD_RELOC_PPC_GOT_TLSLD16_HA: r = R_PPC64_GOT_TLSLD16_HA; |
| break; |
| case BFD_RELOC_PPC_GOT_TPREL16: r = R_PPC64_GOT_TPREL16_DS; |
| break; |
| case BFD_RELOC_PPC_GOT_TPREL16_LO: r = R_PPC64_GOT_TPREL16_LO_DS; |
| break; |
| case BFD_RELOC_PPC_GOT_TPREL16_HI: r = R_PPC64_GOT_TPREL16_HI; |
| break; |
| case BFD_RELOC_PPC_GOT_TPREL16_HA: r = R_PPC64_GOT_TPREL16_HA; |
| break; |
| case BFD_RELOC_PPC_GOT_DTPREL16: r = R_PPC64_GOT_DTPREL16_DS; |
| break; |
| case BFD_RELOC_PPC_GOT_DTPREL16_LO: r = R_PPC64_GOT_DTPREL16_LO_DS; |
| break; |
| case BFD_RELOC_PPC_GOT_DTPREL16_HI: r = R_PPC64_GOT_DTPREL16_HI; |
| break; |
| case BFD_RELOC_PPC_GOT_DTPREL16_HA: r = R_PPC64_GOT_DTPREL16_HA; |
| break; |
| case BFD_RELOC_PPC64_TPREL16_DS: r = R_PPC64_TPREL16_DS; |
| break; |
| case BFD_RELOC_PPC64_TPREL16_LO_DS: r = R_PPC64_TPREL16_LO_DS; |
| break; |
| case BFD_RELOC_PPC64_TPREL16_HIGHER: r = R_PPC64_TPREL16_HIGHER; |
| break; |
| case BFD_RELOC_PPC64_TPREL16_HIGHERA: r = R_PPC64_TPREL16_HIGHERA; |
| break; |
| case BFD_RELOC_PPC64_TPREL16_HIGHEST: r = R_PPC64_TPREL16_HIGHEST; |
| break; |
| case BFD_RELOC_PPC64_TPREL16_HIGHESTA: r = R_PPC64_TPREL16_HIGHESTA; |
| break; |
| case BFD_RELOC_PPC64_DTPREL16_DS: r = R_PPC64_DTPREL16_DS; |
| break; |
| case BFD_RELOC_PPC64_DTPREL16_LO_DS: r = R_PPC64_DTPREL16_LO_DS; |
| break; |
| case BFD_RELOC_PPC64_DTPREL16_HIGHER: r = R_PPC64_DTPREL16_HIGHER; |
| break; |
| case BFD_RELOC_PPC64_DTPREL16_HIGHERA: r = R_PPC64_DTPREL16_HIGHERA; |
| break; |
| case BFD_RELOC_PPC64_DTPREL16_HIGHEST: r = R_PPC64_DTPREL16_HIGHEST; |
| break; |
| case BFD_RELOC_PPC64_DTPREL16_HIGHESTA: r = R_PPC64_DTPREL16_HIGHESTA; |
| break; |
| case BFD_RELOC_16_PCREL: r = R_PPC64_REL16; |
| break; |
| case BFD_RELOC_LO16_PCREL: r = R_PPC64_REL16_LO; |
| break; |
| case BFD_RELOC_HI16_PCREL: r = R_PPC64_REL16_HI; |
| break; |
| case BFD_RELOC_HI16_S_PCREL: r = R_PPC64_REL16_HA; |
| break; |
| case BFD_RELOC_PPC64_REL16_HIGH: r = R_PPC64_REL16_HIGH; |
| break; |
| case BFD_RELOC_PPC64_REL16_HIGHA: r = R_PPC64_REL16_HIGHA; |
| break; |
| case BFD_RELOC_PPC64_REL16_HIGHER: r = R_PPC64_REL16_HIGHER; |
| break; |
| case BFD_RELOC_PPC64_REL16_HIGHERA: r = R_PPC64_REL16_HIGHERA; |
| break; |
| case BFD_RELOC_PPC64_REL16_HIGHEST: r = R_PPC64_REL16_HIGHEST; |
| break; |
| case BFD_RELOC_PPC64_REL16_HIGHESTA: r = R_PPC64_REL16_HIGHESTA; |
| break; |
| case BFD_RELOC_PPC_16DX_HA: r = R_PPC64_16DX_HA; |
| break; |
| case BFD_RELOC_PPC_REL16DX_HA: r = R_PPC64_REL16DX_HA; |
| break; |
| case BFD_RELOC_PPC64_ENTRY: r = R_PPC64_ENTRY; |
| break; |
| case BFD_RELOC_PPC64_ADDR64_LOCAL: r = R_PPC64_ADDR64_LOCAL; |
| break; |
| case BFD_RELOC_PPC64_D34: r = R_PPC64_D34; |
| break; |
| case BFD_RELOC_PPC64_D34_LO: r = R_PPC64_D34_LO; |
| break; |
| case BFD_RELOC_PPC64_D34_HI30: r = R_PPC64_D34_HI30; |
| break; |
| case BFD_RELOC_PPC64_D34_HA30: r = R_PPC64_D34_HA30; |
| break; |
| case BFD_RELOC_PPC64_PCREL34: r = R_PPC64_PCREL34; |
| break; |
| case BFD_RELOC_PPC64_GOT_PCREL34: r = R_PPC64_GOT_PCREL34; |
| break; |
| case BFD_RELOC_PPC64_PLT_PCREL34: r = R_PPC64_PLT_PCREL34; |
| break; |
| case BFD_RELOC_PPC64_TPREL34: r = R_PPC64_TPREL34; |
| break; |
| case BFD_RELOC_PPC64_DTPREL34: r = R_PPC64_DTPREL34; |
| break; |
| case BFD_RELOC_PPC64_GOT_TLSGD_PCREL34: r = R_PPC64_GOT_TLSGD_PCREL34; |
| break; |
| case BFD_RELOC_PPC64_GOT_TLSLD_PCREL34: r = R_PPC64_GOT_TLSLD_PCREL34; |
| break; |
| case BFD_RELOC_PPC64_GOT_TPREL_PCREL34: r = R_PPC64_GOT_TPREL_PCREL34; |
| break; |
| case BFD_RELOC_PPC64_GOT_DTPREL_PCREL34: r = R_PPC64_GOT_DTPREL_PCREL34; |
| break; |
| case BFD_RELOC_PPC64_ADDR16_HIGHER34: r = R_PPC64_ADDR16_HIGHER34; |
| break; |
| case BFD_RELOC_PPC64_ADDR16_HIGHERA34: r = R_PPC64_ADDR16_HIGHERA34; |
| break; |
| case BFD_RELOC_PPC64_ADDR16_HIGHEST34: r = R_PPC64_ADDR16_HIGHEST34; |
| break; |
| case BFD_RELOC_PPC64_ADDR16_HIGHESTA34: r = R_PPC64_ADDR16_HIGHESTA34; |
| break; |
| case BFD_RELOC_PPC64_REL16_HIGHER34: r = R_PPC64_REL16_HIGHER34; |
| break; |
| case BFD_RELOC_PPC64_REL16_HIGHERA34: r = R_PPC64_REL16_HIGHERA34; |
| break; |
| case BFD_RELOC_PPC64_REL16_HIGHEST34: r = R_PPC64_REL16_HIGHEST34; |
| break; |
| case BFD_RELOC_PPC64_REL16_HIGHESTA34: r = R_PPC64_REL16_HIGHESTA34; |
| break; |
| case BFD_RELOC_PPC64_D28: r = R_PPC64_D28; |
| break; |
| case BFD_RELOC_PPC64_PCREL28: r = R_PPC64_PCREL28; |
| break; |
| case BFD_RELOC_VTABLE_INHERIT: r = R_PPC64_GNU_VTINHERIT; |
| break; |
| case BFD_RELOC_VTABLE_ENTRY: r = R_PPC64_GNU_VTENTRY; |
| break; |
| } |
| |
| return ppc64_elf_howto_table[r]; |
| }; |
| |
| static reloc_howto_type * |
| ppc64_elf_reloc_name_lookup (bfd *abfd, const char *r_name) |
| { |
| unsigned int i; |
| static char *compat_map[][2] = { |
| { "R_PPC64_GOT_TLSGD34", "R_PPC64_GOT_TLSGD_PCREL34" }, |
| { "R_PPC64_GOT_TLSLD34", "R_PPC64_GOT_TLSLD_PCREL34" }, |
| { "R_PPC64_GOT_TPREL34", "R_PPC64_GOT_TPREL_PCREL34" }, |
| { "R_PPC64_GOT_DTPREL34", "R_PPC64_GOT_DTPREL_PCREL34" } |
| }; |
| |
| for (i = 0; i < ARRAY_SIZE (ppc64_elf_howto_raw); i++) |
| if (ppc64_elf_howto_raw[i].name != NULL |
| && strcasecmp (ppc64_elf_howto_raw[i].name, r_name) == 0) |
| return &ppc64_elf_howto_raw[i]; |
| |
| /* Handle old names of relocations in case they were used by |
| .reloc directives. |
| FIXME: Remove this soon. Mapping the reloc names is very likely |
| completely unnecessary. */ |
| for (i = 0; i < ARRAY_SIZE (compat_map); i++) |
| if (strcasecmp (compat_map[i][0], r_name) == 0) |
| { |
| _bfd_error_handler (_("warning: %s should be used rather than %s"), |
| compat_map[i][1], compat_map[i][0]); |
| return ppc64_elf_reloc_name_lookup (abfd, compat_map[i][1]); |
| } |
| |
| return NULL; |
| } |
| |
| /* Set the howto pointer for a PowerPC ELF reloc. */ |
| |
| static bool |
| ppc64_elf_info_to_howto (bfd *abfd, arelent *cache_ptr, |
| Elf_Internal_Rela *dst) |
| { |
| unsigned int type; |
| |
| /* Initialize howto table if needed. */ |
| if (!ppc64_elf_howto_table[R_PPC64_ADDR32]) |
| ppc_howto_init (); |
| |
| type = ELF64_R_TYPE (dst->r_info); |
| if (type >= ARRAY_SIZE (ppc64_elf_howto_table)) |
| { |
| /* xgettext:c-format */ |
| _bfd_error_handler (_("%pB: unsupported relocation type %#x"), |
| abfd, type); |
| bfd_set_error (bfd_error_bad_value); |
| return false; |
| } |
| cache_ptr->howto = ppc64_elf_howto_table[type]; |
| if (cache_ptr->howto == NULL || cache_ptr->howto->name == NULL) |
| { |
| /* xgettext:c-format */ |
| _bfd_error_handler (_("%pB: unsupported relocation type %#x"), |
| abfd, type); |
| bfd_set_error (bfd_error_bad_value); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* Handle the R_PPC64_ADDR16_HA and similar relocs. */ |
| |
| static bfd_reloc_status_type |
| ppc64_elf_ha_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol, |
| void *data, asection *input_section, |
| bfd *output_bfd, char **error_message) |
| { |
| enum elf_ppc64_reloc_type r_type; |
| long insn; |
| bfd_size_type octets; |
| bfd_vma value; |
| |
| /* If this is a relocatable link (output_bfd test tells us), just |
| call the generic function. Any adjustment will be done at final |
| link time. */ |
| if (output_bfd != NULL) |
| return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, |
| input_section, output_bfd, error_message); |
| |
| /* Adjust the addend for sign extension of the low 16 (or 34) bits. |
| We won't actually be using the low bits, so trashing them |
| doesn't matter. */ |
| r_type = reloc_entry->howto->type; |
| if (r_type == R_PPC64_ADDR16_HIGHERA34 |
| || r_type == R_PPC64_ADDR16_HIGHESTA34 |
| || r_type == R_PPC64_REL16_HIGHERA34 |
| || r_type == R_PPC64_REL16_HIGHESTA34) |
| reloc_entry->addend += 1ULL << 33; |
| else |
| reloc_entry->addend += 1U << 15; |
| if (r_type != R_PPC64_REL16DX_HA) |
| return bfd_reloc_continue; |
| |
| value = 0; |
| if (!bfd_is_com_section (symbol->section)) |
| value = symbol->value; |
| value += (reloc_entry->addend |
| + symbol->section->output_offset |
| + symbol->section->output_section->vma); |
| value -= (reloc_entry->address |
| + input_section->output_offset |
| + input_section->output_section->vma); |
| value = (bfd_signed_vma) value >> 16; |
| |
| octets = reloc_entry->address * OCTETS_PER_BYTE (abfd, input_section); |
| if (!bfd_reloc_offset_in_range (reloc_entry->howto, abfd, |
| input_section, octets)) |
| return bfd_reloc_outofrange; |
| |
| insn = bfd_get_32 (abfd, (bfd_byte *) data + octets); |
| insn &= ~0x1fffc1; |
| insn |= (value & 0xffc1) | ((value & 0x3e) << 15); |
| bfd_put_32 (abfd, insn, (bfd_byte *) data + octets); |
| if (value + 0x8000 > 0xffff) |
| return bfd_reloc_overflow; |
| return bfd_reloc_ok; |
| } |
| |
| static bfd_reloc_status_type |
| ppc64_elf_branch_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol, |
| void *data, asection *input_section, |
| bfd *output_bfd, char **error_message) |
| { |
| if (output_bfd != NULL) |
| return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, |
| input_section, output_bfd, error_message); |
| |
| if (symbol->section->owner == NULL |
| || !is_ppc64_elf (symbol->section->owner)) |
| return bfd_reloc_continue; |
| |
| if (strcmp (symbol->section->name, ".opd") == 0 |
| && (symbol->section->owner->flags & DYNAMIC) == 0) |
| { |
| bfd_vma dest = opd_entry_value (symbol->section, |
| symbol->value + reloc_entry->addend, |
| NULL, NULL, false); |
| if (dest != (bfd_vma) -1) |
| reloc_entry->addend = dest - (symbol->value |
| + symbol->section->output_section->vma |
| + symbol->section->output_offset); |
| } |
| else |
| { |
| elf_symbol_type *elfsym = (elf_symbol_type *) symbol; |
| |
| if (symbol->section->owner != abfd |
| && abiversion (symbol->section->owner) >= 2) |
| { |
| unsigned int i; |
| |
| for (i = 0; i < symbol->section->owner->symcount; ++i) |
| { |
| asymbol *symdef = symbol->section->owner->outsymbols[i]; |
| |
| if (strcmp (symdef->name, symbol->name) == 0) |
| { |
| elfsym = (elf_symbol_type *) symdef; |
| break; |
| } |
| } |
| } |
| reloc_entry->addend |
| += PPC64_LOCAL_ENTRY_OFFSET (elfsym->internal_elf_sym.st_other); |
| } |
| return bfd_reloc_continue; |
| } |
| |
| static bfd_reloc_status_type |
| ppc64_elf_brtaken_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol, |
| void *data, asection *input_section, |
| bfd *output_bfd, char **error_message) |
| { |
| long insn; |
| enum elf_ppc64_reloc_type r_type; |
| bfd_size_type octets; |
| /* Assume 'at' branch hints. */ |
| bool is_isa_v2 = true; |
| |
| /* If this is a relocatable link (output_bfd test tells us), just |
| call the generic function. Any adjustment will be done at final |
| link time. */ |
| if (output_bfd != NULL) |
| return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, |
| input_section, output_bfd, error_message); |
| |
| octets = reloc_entry->address * OCTETS_PER_BYTE (abfd, input_section); |
| if (!bfd_reloc_offset_in_range (reloc_entry->howto, abfd, |
| input_section, octets)) |
| return bfd_reloc_outofrange; |
| |
| insn = bfd_get_32 (abfd, (bfd_byte *) data + octets); |
| insn &= ~(0x01 << 21); |
| r_type = reloc_entry->howto->type; |
| if (r_type == R_PPC64_ADDR14_BRTAKEN |
| || r_type == R_PPC64_REL14_BRTAKEN) |
| insn |= 0x01 << 21; /* 'y' or 't' bit, lowest bit of BO field. */ |
| |
| if (is_isa_v2) |
| { |
| /* Set 'a' bit. This is 0b00010 in BO field for branch |
| on CR(BI) insns (BO == 001at or 011at), and 0b01000 |
| for branch on CTR insns (BO == 1a00t or 1a01t). */ |
| if ((insn & (0x14 << 21)) == (0x04 << 21)) |
| insn |= 0x02 << 21; |
| else if ((insn & (0x14 << 21)) == (0x10 << 21)) |
| insn |= 0x08 << 21; |
| else |
| goto out; |
| } |
| else |
| { |
| bfd_vma target = 0; |
| bfd_vma from; |
| |
| if (!bfd_is_com_section (symbol->section)) |
| target = symbol->value; |
| target += symbol->section->output_section->vma; |
| target += symbol->section->output_offset; |
| target += reloc_entry->addend; |
| |
| from = (reloc_entry->address |
| + input_section->output_offset |
| + input_section->output_section->vma); |
| |
| /* Invert 'y' bit if not the default. */ |
| if ((bfd_signed_vma) (target - from) < 0) |
| insn ^= 0x01 << 21; |
| } |
| bfd_put_32 (abfd, insn, (bfd_byte *) data + octets); |
| out: |
| return ppc64_elf_branch_reloc (abfd, reloc_entry, symbol, data, |
| input_section, output_bfd, error_message); |
| } |
| |
| static bfd_reloc_status_type |
| ppc64_elf_sectoff_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol, |
| void *data, asection *input_section, |
| bfd *output_bfd, char **error_message) |
| { |
| /* If this is a relocatable link (output_bfd test tells us), just |
| call the generic function. Any adjustment will be done at final |
| link time. */ |
| if (output_bfd != NULL) |
| return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, |
| input_section, output_bfd, error_message); |
| |
| /* Subtract the symbol section base address. */ |
| reloc_entry->addend -= symbol->section->output_section->vma; |
| return bfd_reloc_continue; |
| } |
| |
| static bfd_reloc_status_type |
| ppc64_elf_sectoff_ha_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol, |
| void *data, asection *input_section, |
| bfd *output_bfd, char **error_message) |
| { |
| /* If this is a relocatable link (output_bfd test tells us), just |
| call the generic function. Any adjustment will be done at final |
| link time. */ |
| if (output_bfd != NULL) |
| return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, |
| input_section, output_bfd, error_message); |
| |
| /* Subtract the symbol section base address. */ |
| reloc_entry->addend -= symbol->section->output_section->vma; |
| |
| /* Adjust the addend for sign extension of the low 16 bits. */ |
| reloc_entry->addend += 0x8000; |
| return bfd_reloc_continue; |
| } |
| |
| static bfd_reloc_status_type |
| ppc64_elf_toc_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol, |
| void *data, asection *input_section, |
| bfd *output_bfd, char **error_message) |
| { |
| bfd_vma TOCstart; |
| |
| /* If this is a relocatable link (output_bfd test tells us), just |
| call the generic function. Any adjustment will be done at final |
| link time. */ |
| if (output_bfd != NULL) |
| return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, |
| input_section, output_bfd, error_message); |
| |
| TOCstart = _bfd_get_gp_value (input_section->output_section->owner); |
| if (TOCstart == 0) |
| TOCstart = ppc64_elf_set_toc (NULL, input_section->output_section->owner); |
| |
| /* Subtract the TOC base address. */ |
| reloc_entry->addend -= TOCstart + TOC_BASE_OFF; |
| return bfd_reloc_continue; |
| } |
| |
| static bfd_reloc_status_type |
| ppc64_elf_toc_ha_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol, |
| void *data, asection *input_section, |
| bfd *output_bfd, char **error_message) |
| { |
| bfd_vma TOCstart; |
| |
| /* If this is a relocatable link (output_bfd test tells us), just |
| call the generic function. Any adjustment will be done at final |
| link time. */ |
| if (output_bfd != NULL) |
| return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, |
| input_section, output_bfd, error_message); |
| |
| TOCstart = _bfd_get_gp_value (input_section->output_section->owner); |
| if (TOCstart == 0) |
| TOCstart = ppc64_elf_set_toc (NULL, input_section->output_section->owner); |
| |
| /* Subtract the TOC base address. */ |
| reloc_entry->addend -= TOCstart + TOC_BASE_OFF; |
| |
| /* Adjust the addend for sign extension of the low 16 bits. */ |
| reloc_entry->addend += 0x8000; |
| return bfd_reloc_continue; |
| } |
| |
| static bfd_reloc_status_type |
| ppc64_elf_toc64_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol, |
| void *data, asection *input_section, |
| bfd *output_bfd, char **error_message) |
| { |
| bfd_vma TOCstart; |
| bfd_size_type octets; |
| |
| /* If this is a relocatable link (output_bfd test tells us), just |
| call the generic function. Any adjustment will be done at final |
| link time. */ |
| if (output_bfd != NULL) |
| return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, |
| input_section, output_bfd, error_message); |
| |
| octets = reloc_entry->address * OCTETS_PER_BYTE (abfd, input_section); |
| if (!bfd_reloc_offset_in_range (reloc_entry->howto, abfd, |
| input_section, octets)) |
| return bfd_reloc_outofrange; |
| |
| TOCstart = _bfd_get_gp_value (input_section->output_section->owner); |
| if (TOCstart == 0) |
| TOCstart = ppc64_elf_set_toc (NULL, input_section->output_section->owner); |
| |
| bfd_put_64 (abfd, TOCstart + TOC_BASE_OFF, (bfd_byte *) data + octets); |
| return bfd_reloc_ok; |
| } |
| |
| static bfd_reloc_status_type |
| ppc64_elf_prefix_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol, |
| void *data, asection *input_section, |
| bfd *output_bfd, char **error_message) |
| { |
| uint64_t insn; |
| bfd_vma targ; |
| bfd_size_type octets; |
| |
| if (output_bfd != NULL) |
| return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, |
| input_section, output_bfd, error_message); |
| |
| octets = reloc_entry->address * OCTETS_PER_BYTE (abfd, input_section); |
| if (!bfd_reloc_offset_in_range (reloc_entry->howto, abfd, |
| input_section, octets)) |
| return bfd_reloc_outofrange; |
| |
| insn = bfd_get_32 (abfd, (bfd_byte *) data + octets); |
| insn <<= 32; |
| insn |= bfd_get_32 (abfd, (bfd_byte *) data + octets + 4); |
| |
| targ = (symbol->section->output_section->vma |
| + symbol->section->output_offset |
| + reloc_entry->addend); |
| if (!bfd_is_com_section (symbol->section)) |
| targ += symbol->value; |
| if (reloc_entry->howto->type == R_PPC64_D34_HA30) |
| targ += 1ULL << 33; |
| if (reloc_entry->howto->pc_relative) |
| { |
| bfd_vma from = (reloc_entry->address |
| + input_section->output_offset |
| + input_section->output_section->vma); |
| targ -=from; |
| } |
| targ >>= reloc_entry->howto->rightshift; |
| insn &= ~reloc_entry->howto->dst_mask; |
| insn |= ((targ << 16) | (targ & 0xffff)) & reloc_entry->howto->dst_mask; |
| bfd_put_32 (abfd, insn >> 32, (bfd_byte *) data + octets); |
| bfd_put_32 (abfd, insn, (bfd_byte *) data + octets + 4); |
| if (reloc_entry->howto->complain_on_overflow == complain_overflow_signed |
| && (targ + (1ULL << (reloc_entry->howto->bitsize - 1)) |
| >= 1ULL << reloc_entry->howto->bitsize)) |
| return bfd_reloc_overflow; |
| return bfd_reloc_ok; |
| } |
| |
| static bfd_reloc_status_type |
| ppc64_elf_unhandled_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol, |
| void *data, asection *input_section, |
| bfd *output_bfd, char **error_message) |
| { |
| /* If this is a relocatable link (output_bfd test tells us), just |
| call the generic function. Any adjustment will be done at final |
| link time. */ |
| if (output_bfd != NULL) |
| return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, |
| input_section, output_bfd, error_message); |
| |
| if (error_message != NULL) |
| *error_message = bfd_asprintf (_("generic linker can't handle %s"), |
| reloc_entry->howto->name); |
| return bfd_reloc_dangerous; |
| } |
| |
| /* Track GOT entries needed for a given symbol. We might need more |
| than one got entry per symbol. */ |
| struct got_entry |
| { |
| struct got_entry *next; |
| |
| /* The symbol addend that we'll be placing in the GOT. */ |
| bfd_vma addend; |
| |
| /* Unlike other ELF targets, we use separate GOT entries for the same |
| symbol referenced from different input files. This is to support |
| automatic multiple TOC/GOT sections, where the TOC base can vary |
| from one input file to another. After partitioning into TOC groups |
| we merge entries within the group. |
| |
| Point to the BFD owning this GOT entry. */ |
| bfd *owner; |
| |
| /* Zero for non-tls entries, or TLS_TLS and one of TLS_GD, TLS_LD, |
| TLS_TPREL or TLS_DTPREL for tls entries. */ |
| unsigned char tls_type; |
| |
| /* Non-zero if got.ent points to real entry. */ |
| unsigned char is_indirect; |
| |
| /* Reference count until size_dynamic_sections, GOT offset thereafter. */ |
| union |
| { |
| bfd_signed_vma refcount; |
| bfd_vma offset; |
| struct got_entry *ent; |
| } got; |
| }; |
| |
| /* The same for PLT. */ |
| struct plt_entry |
| { |
| struct plt_entry *next; |
| |
| bfd_vma addend; |
| |
| union |
| { |
| bfd_signed_vma refcount; |
| bfd_vma offset; |
| } plt; |
| }; |
| |
| struct ppc64_elf_obj_tdata |
| { |
| struct elf_obj_tdata elf; |
| |
| /* Shortcuts to dynamic linker sections. */ |
| asection *got; |
| asection *relgot; |
| |
| /* Used during garbage collection. We attach global symbols defined |
| on removed .opd entries to this section so that the sym is removed. */ |
| asection *deleted_section; |
| |
| /* TLS local dynamic got entry handling. Support for multiple GOT |
| sections means we potentially need one of these for each input bfd. */ |
| struct got_entry tlsld_got; |
| |
| /* Nonzero if this bfd has small toc/got relocs, ie. that expect |
| the reloc to be in the range -32768 to 32767. */ |
| unsigned int has_small_toc_reloc : 1; |
| |
| /* Set if toc/got ha relocs detected not using r2, or lo reloc |
| instruction not one we handle. */ |
| unsigned int unexpected_toc_insn : 1; |
| |
| /* Set if PLT/GOT/TOC relocs that can be optimised are present in |
| this file. */ |
| unsigned int has_optrel : 1; |
| }; |
| |
| #define ppc64_elf_tdata(bfd) \ |
| ((struct ppc64_elf_obj_tdata *) (bfd)->tdata.any) |
| |
| #define ppc64_tlsld_got(bfd) \ |
| (&ppc64_elf_tdata (bfd)->tlsld_got) |
| |
| /* Override the generic function because we store some extras. */ |
| |
| static bool |
| ppc64_elf_mkobject (bfd *abfd) |
| { |
| return bfd_elf_allocate_object (abfd, sizeof (struct ppc64_elf_obj_tdata), |
| PPC64_ELF_DATA); |
| } |
| |
| /* Fix bad default arch selected for a 64 bit input bfd when the |
| default is 32 bit. Also select arch based on apuinfo. */ |
| |
| static bool |
| ppc64_elf_object_p (bfd *abfd) |
| { |
| if (!abfd->arch_info->the_default) |
| return true; |
| |
| if (abfd->arch_info->bits_per_word == 32) |
| { |
| Elf_Internal_Ehdr *i_ehdr = elf_elfheader (abfd); |
| |
| if (i_ehdr->e_ident[EI_CLASS] == ELFCLASS64) |
| { |
| /* Relies on arch after 32 bit default being 64 bit default. */ |
| abfd->arch_info = abfd->arch_info->next; |
| BFD_ASSERT (abfd->arch_info->bits_per_word == 64); |
| } |
| } |
| return _bfd_elf_ppc_set_arch (abfd); |
| } |
| |
| /* Support for core dump NOTE sections. */ |
| |
| static bool |
| ppc64_elf_grok_prstatus (bfd *abfd, Elf_Internal_Note *note) |
| { |
| size_t offset, size; |
| |
| if (note->descsz != 504) |
| return false; |
| |
| /* pr_cursig */ |
| elf_tdata (abfd)->core->signal = bfd_get_16 (abfd, note->descdata + 12); |
| |
| /* pr_pid */ |
| elf_tdata (abfd)->core->lwpid = bfd_get_32 (abfd, note->descdata + 32); |
| |
| /* pr_reg */ |
| offset = 112; |
| size = 384; |
| |
| /* Make a ".reg/999" section. */ |
| return _bfd_elfcore_make_pseudosection (abfd, ".reg", |
| size, note->descpos + offset); |
| } |
| |
| static bool |
| ppc64_elf_grok_psinfo (bfd *abfd, Elf_Internal_Note *note) |
| { |
| if (note->descsz != 136) |
| return false; |
| |
| elf_tdata (abfd)->core->pid |
| = bfd_get_32 (abfd, note->descdata + 24); |
| elf_tdata (abfd)->core->program |
| = _bfd_elfcore_strndup (abfd, note->descdata + 40, 16); |
| elf_tdata (abfd)->core->command |
| = _bfd_elfcore_strndup (abfd, note->descdata + 56, 80); |
| |
| return true; |
| } |
| |
| static char * |
| ppc64_elf_write_core_note (bfd *abfd, char *buf, int *bufsiz, int note_type, |
| ...) |
| { |
| switch (note_type) |
| { |
| default: |
| return NULL; |
| |
| case NT_PRPSINFO: |
| { |
| char data[136] ATTRIBUTE_NONSTRING; |
| va_list ap; |
| |
| va_start (ap, note_type); |
| memset (data, 0, sizeof (data)); |
| strncpy (data + 40, va_arg (ap, const char *), 16); |
| #if GCC_VERSION == 8000 || GCC_VERSION == 8001 |
| DIAGNOSTIC_PUSH; |
| /* GCC 8.0 and 8.1 warn about 80 equals destination size with |
| -Wstringop-truncation: |
| https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85643 |
| */ |
| DIAGNOSTIC_IGNORE_STRINGOP_TRUNCATION; |
| #endif |
| strncpy (data + 56, va_arg (ap, const char *), 80); |
| #if GCC_VERSION == 8000 || GCC_VERSION == 8001 |
| DIAGNOSTIC_POP; |
| #endif |
| va_end (ap); |
| return elfcore_write_note (abfd, buf, bufsiz, |
| "CORE", note_type, data, sizeof (data)); |
| } |
| |
| case NT_PRSTATUS: |
| { |
| char data[504]; |
| va_list ap; |
| long pid; |
| int cursig; |
| const void *greg; |
| |
| va_start (ap, note_type); |
| memset (data, 0, 112); |
| pid = va_arg (ap, long); |
| bfd_put_32 (abfd, pid, data + 32); |
| cursig = va_arg (ap, int); |
| bfd_put_16 (abfd, cursig, data + 12); |
| greg = va_arg (ap, const void *); |
| memcpy (data + 112, greg, 384); |
| memset (data + 496, 0, 8); |
| va_end (ap); |
| return elfcore_write_note (abfd, buf, bufsiz, |
| "CORE", note_type, data, sizeof (data)); |
| } |
| } |
| } |
| |
| /* Add extra PPC sections. */ |
| |
| static const struct bfd_elf_special_section ppc64_elf_special_sections[] = |
| { |
| { STRING_COMMA_LEN (".plt"), 0, SHT_NOBITS, 0 }, |
| { STRING_COMMA_LEN (".sbss"), -2, SHT_NOBITS, SHF_ALLOC + SHF_WRITE }, |
| { STRING_COMMA_LEN (".sdata"), -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE }, |
| { STRING_COMMA_LEN (".toc"), 0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE }, |
| { STRING_COMMA_LEN (".toc1"), 0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE }, |
| { STRING_COMMA_LEN (".tocbss"), 0, SHT_NOBITS, SHF_ALLOC + SHF_WRITE }, |
| { NULL, 0, 0, 0, 0 } |
| }; |
| |
| enum _ppc64_sec_type { |
| sec_normal = 0, |
| sec_opd = 1, |
| sec_toc = 2, |
| sec_stub = 3 |
| }; |
| |
| struct _ppc64_elf_section_data |
| { |
| struct bfd_elf_section_data elf; |
| |
| union |
| { |
| /* An array with one entry for each opd function descriptor, |
| and some spares since opd entries may be either 16 or 24 bytes. */ |
| #define OPD_NDX(OFF) ((OFF) >> 4) |
| struct _opd_sec_data |
| { |
| /* Points to the function code section for local opd entries. */ |
| asection **func_sec; |
| |
| /* After editing .opd, adjust references to opd local syms. */ |
| long *adjust; |
| |
| union |
| { |
| /* A copy of relocs before they are modified for --emit-relocs. */ |
| Elf_Internal_Rela *relocs; |
| |
| /* Section contents. */ |
| bfd_byte *contents; |
| } u; |
| } opd; |
| |
| /* An array for toc sections, indexed by offset/8. */ |
| struct _toc_sec_data |
| { |
| /* Specifies the relocation symbol index used at a given toc offset. */ |
| unsigned *symndx; |
| |
| /* And the relocation addend. */ |
| bfd_vma *add; |
| } toc; |
| |
| /* Stub debugging. */ |
| struct ppc_stub_hash_entry *last_ent; |
| } u; |
| |
| enum _ppc64_sec_type sec_type:2; |
| |
| /* Flag set when small branches are detected. Used to |
| select suitable defaults for the stub group size. */ |
| unsigned int has_14bit_branch:1; |
| |
| /* Flag set when PLTCALL relocs are detected. */ |
| unsigned int has_pltcall:1; |
| |
| /* Flag set when section has PLT/GOT/TOC relocations that can be |
| optimised. */ |
| unsigned int has_optrel:1; |
| }; |
| |
| #define ppc64_elf_section_data(sec) \ |
| ((struct _ppc64_elf_section_data *) elf_section_data (sec)) |
| |
| static bool |
| ppc64_elf_new_section_hook (bfd *abfd, asection *sec) |
| { |
| if (!sec->used_by_bfd) |
| { |
| struct _ppc64_elf_section_data *sdata; |
| size_t amt = sizeof (*sdata); |
| |
| sdata = bfd_zalloc (abfd, amt); |
| if (sdata == NULL) |
| return false; |
| sec->used_by_bfd = sdata; |
| } |
| |
| return _bfd_elf_new_section_hook (abfd, sec); |
| } |
| |
| static bool |
| ppc64_elf_section_flags (const Elf_Internal_Shdr *hdr) |
| { |
| const char *name = hdr->bfd_section->name; |
| |
| if (startswith (name, ".sbss") |
| || startswith (name, ".sdata")) |
| hdr->bfd_section->flags |= SEC_SMALL_DATA; |
| |
| return true; |
| } |
| |
| static struct _opd_sec_data * |
| get_opd_info (asection * sec) |
| { |
| if (sec != NULL |
| && ppc64_elf_section_data (sec) != NULL |
| && ppc64_elf_section_data (sec)->sec_type == sec_opd) |
| return &ppc64_elf_section_data (sec)->u.opd; |
| return NULL; |
| } |
| |
| /* Parameters for the qsort hook. */ |
| static bool synthetic_relocatable; |
| static const asection *synthetic_opd; |
| |
| /* qsort comparison function for ppc64_elf_get_synthetic_symtab. */ |
| |
| static int |
| compare_symbols (const void *ap, const void *bp) |
| { |
| const asymbol *a = *(const asymbol **) ap; |
| const asymbol *b = *(const asymbol **) bp; |
| |
| /* Section symbols first. */ |
| if ((a->flags & BSF_SECTION_SYM) && !(b->flags & BSF_SECTION_SYM)) |
| return -1; |
| if (!(a->flags & BSF_SECTION_SYM) && (b->flags & BSF_SECTION_SYM)) |
| return 1; |
| |
| /* then .opd symbols. */ |
| if (synthetic_opd != NULL) |
| { |
| if (strcmp (a->section->name, ".opd") == 0 |
| && strcmp (b->section->name, ".opd") != 0) |
| return -1; |
| if (strcmp (a->section->name, ".opd") != 0 |
| && strcmp (b->section->name, ".opd") == 0) |
| return 1; |
| } |
| |
| /* then other code symbols. */ |
| if (((a->section->flags & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL)) |
| == (SEC_CODE | SEC_ALLOC)) |
| && ((b->section->flags & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL)) |
| != (SEC_CODE | SEC_ALLOC))) |
| return -1; |
| |
| if (((a->section->flags & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL)) |
| != (SEC_CODE | SEC_ALLOC)) |
| && ((b->section->flags & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL)) |
| == (SEC_CODE | SEC_ALLOC))) |
| return 1; |
| |
| if (synthetic_relocatable) |
| { |
| if (a->section->id < b->section->id) |
| return -1; |
| |
| if (a->section->id > b->section->id) |
| return 1; |
| } |
| |
| if (a->value + a->section->vma < b->value + b->section->vma) |
| return -1; |
| |
| if (a->value + a->section->vma > b->value + b->section->vma) |
| return 1; |
| |
| /* For syms with the same value, prefer strong dynamic global function |
| syms over other syms. */ |
| if ((a->flags & BSF_GLOBAL) != 0 && (b->flags & BSF_GLOBAL) == 0) |
| return -1; |
| |
| if ((a->flags & BSF_GLOBAL) == 0 && (b->flags & BSF_GLOBAL) != 0) |
| return 1; |
| |
| if ((a->flags & BSF_FUNCTION) != 0 && (b->flags & BSF_FUNCTION) == 0) |
| return -1; |
| |
| if ((a->flags & BSF_FUNCTION) == 0 && (b->flags & BSF_FUNCTION) != 0) |
| return 1; |
| |
| if ((a->flags & BSF_WEAK) == 0 && (b->flags & BSF_WEAK) != 0) |
| return -1; |
| |
| if ((a->flags & BSF_WEAK) != 0 && (b->flags & BSF_WEAK) == 0) |
| return 1; |
| |
| if ((a->flags & BSF_DYNAMIC) != 0 && (b->flags & BSF_DYNAMIC) == 0) |
| return -1; |
| |
| if ((a->flags & BSF_DYNAMIC) == 0 && (b->flags & BSF_DYNAMIC) != 0) |
| return 1; |
| |
| /* Finally, sort on where the symbol is in memory. The symbols will |
| be in at most two malloc'd blocks, one for static syms, one for |
| dynamic syms, and we distinguish the two blocks above by testing |
| BSF_DYNAMIC. Since we are sorting the symbol pointers which were |
| originally in the same order as the symbols (and we're not |
| sorting the symbols themselves), this ensures a stable sort. */ |
| if (a < b) |
| return -1; |
| if (a > b) |
| return 1; |
| return 0; |
| } |
| |
| /* Search SYMS for a symbol of the given VALUE. */ |
| |
| static asymbol * |
| sym_exists_at (asymbol **syms, size_t lo, size_t hi, unsigned int id, |
| bfd_vma value) |
| { |
| size_t mid; |
| |
| if (id == (unsigned) -1) |
| { |
| while (lo < hi) |
| { |
| mid = (lo + hi) >> 1; |
| if (syms[mid]->value + syms[mid]->section->vma < value) |
| lo = mid + 1; |
| else if (syms[mid]->value + syms[mid]->section->vma > value) |
| hi = mid; |
| else |
| return syms[mid]; |
| } |
| } |
| else |
| { |
| while (lo < hi) |
| { |
| mid = (lo + hi) >> 1; |
| if (syms[mid]->section->id < id) |
| lo = mid + 1; |
| else if (syms[mid]->section->id > id) |
| hi = mid; |
| else if (syms[mid]->value < value) |
| lo = mid + 1; |
| else if (syms[mid]->value > value) |
| hi = mid; |
| else |
| return syms[mid]; |
| } |
| } |
| return NULL; |
| } |
| |
| static bool |
| section_covers_vma (bfd *abfd ATTRIBUTE_UNUSED, asection *section, void *ptr) |
| { |
| bfd_vma vma = *(bfd_vma *) ptr; |
| return ((section->flags & SEC_ALLOC) != 0 |
| && section->vma <= vma |
| && vma < section->vma + section->size); |
| } |
| |
| /* Create synthetic symbols, effectively restoring "dot-symbol" function |
| entry syms. Also generate @plt symbols for the glink branch table. |
| Returns count of synthetic symbols in RET or -1 on error. */ |
| |
| static long |
| ppc64_elf_get_synthetic_symtab (bfd *abfd, |
| long static_count, asymbol **static_syms, |
| long dyn_count, asymbol **dyn_syms, |
| asymbol **ret) |
| { |
| asymbol *s; |
| size_t i, j, count; |
| char *names; |
| size_t symcount, codesecsym, codesecsymend, secsymend, opdsymend; |
| asection *opd = NULL; |
| bool relocatable = (abfd->flags & (EXEC_P | DYNAMIC)) == 0; |
| asymbol **syms; |
| int abi = abiversion (abfd); |
| |
| *ret = NULL; |
| |
| if (abi < 2) |
| { |
| opd = bfd_get_section_by_name (abfd, ".opd"); |
| if (opd == NULL && abi == 1) |
| return 0; |
| } |
| |
| syms = NULL; |
| codesecsym = 0; |
| codesecsymend = 0; |
| secsymend = 0; |
| opdsymend = 0; |
| symcount = 0; |
| if (opd != NULL) |
| { |
| symcount = static_count; |
| if (!relocatable) |
| symcount += dyn_count; |
| if (symcount == 0) |
| return 0; |
| |
| syms = bfd_malloc ((symcount + 1) * sizeof (*syms)); |
| if (syms == NULL) |
| return -1; |
| |
| if (!relocatable && static_count != 0 && dyn_count != 0) |
| { |
| /* Use both symbol tables. */ |
| memcpy (syms, static_syms, static_count * sizeof (*syms)); |
| memcpy (syms + static_count, dyn_syms, |
| (dyn_count + 1) * sizeof (*syms)); |
| } |
| else if (!relocatable && static_count == 0) |
| memcpy (syms, dyn_syms, (symcount + 1) * sizeof (*syms)); |
| else |
| memcpy (syms, static_syms, (symcount + 1) * sizeof (*syms)); |
| |
| /* Trim uninteresting symbols. Interesting symbols are section, |
| function, and notype symbols. */ |
| for (i = 0, j = 0; i < symcount; ++i) |
| if ((syms[i]->flags & (BSF_FILE | BSF_OBJECT | BSF_THREAD_LOCAL |
| | BSF_RELC | BSF_SRELC)) == 0) |
| syms[j++] = syms[i]; |
| symcount = j; |
| |
| synthetic_relocatable = relocatable; |
| synthetic_opd = opd; |
| qsort (syms, symcount, sizeof (*syms), compare_symbols); |
| |
| if (!relocatable && symcount > 1) |
| { |
| /* Trim duplicate syms, since we may have merged the normal |
| and dynamic symbols. Actually, we only care about syms |
| that have different values, so trim any with the same |
| value. Don't consider ifunc and ifunc resolver symbols |
| duplicates however, because GDB wants to know whether a |
| text symbol is an ifunc resolver. */ |
| for (i = 1, j = 1; i < symcount; ++i) |
| { |
| const asymbol *s0 = syms[i - 1]; |
| const asymbol *s1 = syms[i]; |
| |
| if ((s0->value + s0->section->vma |
| != s1->value + s1->section->vma) |
| || ((s0->flags & BSF_GNU_INDIRECT_FUNCTION) |
| != (s1->flags & BSF_GNU_INDIRECT_FUNCTION))) |
| syms[j++] = syms[i]; |
| } |
| symcount = j; |
| } |
| |
| i = 0; |
| /* Note that here and in compare_symbols we can't compare opd and |
| sym->section directly. With separate debug info files, the |
| symbols will be extracted from the debug file while abfd passed |
| to this function is the real binary. */ |
| if ((syms[i]->flags & BSF_SECTION_SYM) != 0 |
| && strcmp (syms[i]->section->name, ".opd") == 0) |
| ++i; |
| codesecsym = i; |
| |
| for (; i < symcount; ++i) |
| if (((syms[i]->section->flags & (SEC_CODE | SEC_ALLOC |
| | SEC_THREAD_LOCAL)) |
| != (SEC_CODE | SEC_ALLOC)) |
| || (syms[i]->flags & BSF_SECTION_SYM) == 0) |
| break; |
| codesecsymend = i; |
| |
| for (; i < symcount; ++i) |
| if ((syms[i]->flags & BSF_SECTION_SYM) == 0) |
| break; |
| secsymend = i; |
| |
| for (; i < symcount; ++i) |
| if (strcmp (syms[i]->section->name, ".opd") != 0) |
| break; |
| opdsymend = i; |
| |
| for (; i < symcount; ++i) |
| if (((syms[i]->section->flags |
| & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL))) |
| != (SEC_CODE | SEC_ALLOC)) |
| break; |
| symcount = i; |
| } |
| count = 0; |
| |
| if (relocatable) |
| { |
| bool (*slurp_relocs) (bfd *, asection *, asymbol **, bool); |
| arelent *r; |
| size_t size; |
| size_t relcount; |
| |
| if (opdsymend == secsymend) |
| goto done; |
| |
| slurp_relocs = get_elf_backend_data (abfd)->s->slurp_reloc_table; |
| relcount = (opd->flags & SEC_RELOC) ? opd->reloc_count : 0; |
| if (relcount == 0) |
| goto done; |
| |
| if (!(*slurp_relocs) (abfd, opd, static_syms, false)) |
| { |
| count = -1; |
| goto done; |
| } |
| |
| size = 0; |
| for (i = secsymend, r = opd->relocation; i < opdsymend; ++i) |
| { |
| asymbol *sym; |
| |
| while (r < opd->relocation + relcount |
| && r->address < syms[i]->value + opd->vma) |
| ++r; |
| |
| if (r == opd->relocation + relcount) |
| break; |
| |
| if (r->address != syms[i]->value + opd->vma) |
| continue; |
| |
| if (r->howto->type != R_PPC64_ADDR64) |
| continue; |
| |
| sym = *r->sym_ptr_ptr; |
| if (!sym_exists_at (syms, opdsymend, symcount, |
| sym->section->id, sym->value + r->addend)) |
| { |
| ++count; |
| size += sizeof (asymbol); |
| size += strlen (syms[i]->name) + 2; |
| } |
| } |
| |
| if (size == 0) |
| goto done; |
| s = *ret = bfd_malloc (size); |
| if (s == NULL) |
| { |
| count = -1; |
| goto done; |
| } |
| |
| names = (char *) (s + count); |
| |
| for (i = secsymend, r = opd->relocation; i < opdsymend; ++i) |
| { |
| asymbol *sym; |
| |
| while (r < opd->relocation + relcount |
| && r->address < syms[i]->value + opd->vma) |
| ++r; |
| |
| if (r == opd->relocation + relcount) |
| break; |
| |
| if (r->address != syms[i]->value + opd->vma) |
| continue; |
| |
| if (r->howto->type != R_PPC64_ADDR64) |
| continue; |
| |
| sym = *r->sym_ptr_ptr; |
| if (!sym_exists_at (syms, opdsymend, symcount, |
| sym->section->id, sym->value + r->addend)) |
| { |
| size_t len; |
| |
| *s = *syms[i]; |
| s->flags |= BSF_SYNTHETIC; |
| s->section = sym->section; |
| s->value = sym->value + r->addend; |
| s->name = names; |
| *names++ = '.'; |
| len = strlen (syms[i]->name); |
| memcpy (names, syms[i]->name, len + 1); |
| names += len + 1; |
| /* Have udata.p point back to the original symbol this |
| synthetic symbol was derived from. */ |
| s->udata.p = syms[i]; |
| s++; |
| } |
| } |
| } |
| else |
| { |
| bool (*slurp_relocs) (bfd *, asection *, asymbol **, bool); |
| bfd_byte *contents = NULL; |
| size_t size; |
| size_t plt_count = 0; |
| bfd_vma glink_vma = 0, resolv_vma = 0; |
| asection *dynamic, *glink = NULL, *relplt = NULL; |
| arelent *p; |
| |
| if (opd != NULL |
| && ((opd->flags & SEC_HAS_CONTENTS) == 0 |
| || !bfd_malloc_and_get_section (abfd, opd, &contents))) |
| { |
| free_contents_and_exit_err: |
| count = -1; |
| free_contents_and_exit: |
| free (contents); |
| goto done; |
| } |
| |
| size = 0; |
| for (i = secsymend; i < opdsymend; ++i) |
| { |
| bfd_vma ent; |
| |
| /* Ignore bogus symbols. */ |
| if (syms[i]->value > opd->size - 8) |
| continue; |
| |
| ent = bfd_get_64 (abfd, contents + syms[i]->value); |
| if (!sym_exists_at (syms, opdsymend, symcount, -1, ent)) |
| { |
| ++count; |
| size += sizeof (asymbol); |
| size += strlen (syms[i]->name) + 2; |
| } |
| } |
| |
| /* Get start of .glink stubs from DT_PPC64_GLINK. */ |
| if (dyn_count != 0 |
| && (dynamic = bfd_get_section_by_name (abfd, ".dynamic")) != NULL) |
| { |
| bfd_byte *dynbuf, *extdyn, *extdynend; |
| size_t extdynsize; |
| void (*swap_dyn_in) (bfd *, const void *, Elf_Internal_Dyn *); |
| |
| if ((dynamic->flags & SEC_HAS_CONTENTS) == 0 |
| || !bfd_malloc_and_get_section (abfd, dynamic, &dynbuf)) |
| goto free_contents_and_exit_err; |
| |
| extdynsize = get_elf_backend_data (abfd)->s->sizeof_dyn; |
| swap_dyn_in = get_elf_backend_data (abfd)->s->swap_dyn_in; |
| |
| for (extdyn = dynbuf, extdynend = dynbuf + dynamic->size; |
| (size_t) (extdynend - extdyn) >= extdynsize; |
| extdyn += extdynsize) |
| { |
| Elf_Internal_Dyn dyn; |
| (*swap_dyn_in) (abfd, extdyn, &dyn); |
| |
| if (dyn.d_tag == DT_NULL) |
| break; |
| |
| if (dyn.d_tag == DT_PPC64_GLINK) |
| { |
| /* The first glink stub starts at DT_PPC64_GLINK plus 32. |
| See comment in ppc64_elf_finish_dynamic_sections. */ |
| glink_vma = dyn.d_un.d_val + 8 * 4; |
| /* The .glink section usually does not survive the final |
| link; search for the section (usually .text) where the |
| glink stubs now reside. */ |
| glink = bfd_sections_find_if (abfd, section_covers_vma, |
| &glink_vma); |
| break; |
| } |
| } |
| |
| free (dynbuf); |
| } |
| |
| if (glink != NULL) |
| { |
| /* Determine __glink trampoline by reading the relative branch |
| from the first glink stub. */ |
| bfd_byte buf[4]; |
| unsigned int off = 0; |
| |
| while (bfd_get_section_contents (abfd, glink, buf, |
| glink_vma + off - glink->vma, 4)) |
| { |
| unsigned int insn = bfd_get_32 (abfd, buf); |
| insn ^= B_DOT; |
| if ((insn & ~0x3fffffc) == 0) |
| { |
| resolv_vma |
| = glink_vma + off + (insn ^ 0x2000000) - 0x2000000; |
| break; |
| } |
| off += 4; |
| if (off > 4) |
| break; |
| } |
| |
| if (resolv_vma) |
| size += sizeof (asymbol) + sizeof ("__glink_PLTresolve"); |
| |
| relplt = bfd_get_section_by_name (abfd, ".rela.plt"); |
| if (relplt != NULL) |
| { |
| slurp_relocs = get_elf_backend_data (abfd)->s->slurp_reloc_table; |
| if (!(*slurp_relocs) (abfd, relplt, dyn_syms, true)) |
| goto free_contents_and_exit_err; |
| |
| plt_count = NUM_SHDR_ENTRIES (&elf_section_data (relplt)->this_hdr); |
| size += plt_count * sizeof (asymbol); |
| |
| p = relplt->relocation; |
| for (i = 0; i < plt_count; i++, p++) |
| { |
| size += strlen ((*p->sym_ptr_ptr)->name) + sizeof ("@plt"); |
| if (p->addend != 0) |
| size += sizeof ("+0x") - 1 + 16; |
| } |
| } |
| } |
| |
| if (size == 0) |
| goto free_contents_and_exit; |
| s = *ret = bfd_malloc (size); |
| if (s == NULL) |
| goto free_contents_and_exit_err; |
| |
| names = (char *) (s + count + plt_count + (resolv_vma != 0)); |
| |
| for (i = secsymend; i < opdsymend; ++i) |
| { |
| bfd_vma ent; |
| |
| if (syms[i]->value > opd->size - 8) |
| continue; |
| |
| ent = bfd_get_64 (abfd, contents + syms[i]->value); |
| if (!sym_exists_at (syms, opdsymend, symcount, -1, ent)) |
| { |
| size_t lo, hi; |
| size_t len; |
| asection *sec = abfd->sections; |
| |
| *s = *syms[i]; |
| lo = codesecsym; |
| hi = codesecsymend; |
| while (lo < hi) |
| { |
| size_t mid = (lo + hi) >> 1; |
| if (syms[mid]->section->vma < ent) |
| lo = mid + 1; |
| else if (syms[mid]->section->vma > ent) |
| hi = mid; |
| else |
| { |
| sec = syms[mid]->section; |
| break; |
| } |
| } |
| |
| if (lo >= hi && lo > codesecsym) |
| sec = syms[lo - 1]->section; |
| |
| for (; sec != NULL; sec = sec->next) |
| { |
| if (sec->vma > ent) |
| break; |
| /* SEC_LOAD may not be set if SEC is from a separate debug |
| info file. */ |
| if ((sec->flags & SEC_ALLOC) == 0) |
| break; |
| if ((sec->flags & SEC_CODE) != 0) |
| s->section = sec; |
| } |
| s->flags |= BSF_SYNTHETIC; |
| s->value = ent - s->section->vma; |
| s->name = names; |
| *names++ = '.'; |
| len = strlen (syms[i]->name); |
| memcpy (names, syms[i]->name, len + 1); |
| names += len + 1; |
| /* Have udata.p point back to the original symbol this |
| synthetic symbol was derived from. */ |
| s->udata.p = syms[i]; |
| s++; |
| } |
| } |
| free (contents); |
| |
| if (glink != NULL && relplt != NULL) |
| { |
| if (resolv_vma) |
| { |
| /* Add a symbol for the main glink trampoline. */ |
| memset (s, 0, sizeof *s); |
| s->the_bfd = abfd; |
| s->flags = BSF_GLOBAL | BSF_SYNTHETIC; |
| s->section = glink; |
| s->value = resolv_vma - glink->vma; |
| s->name = names; |
| memcpy (names, "__glink_PLTresolve", |
| sizeof ("__glink_PLTresolve")); |
| names += sizeof ("__glink_PLTresolve"); |
| s++; |
| count++; |
| } |
| |
| /* FIXME: It would be very much nicer to put sym@plt on the |
| stub rather than on the glink branch table entry. The |
| objdump disassembler would then use a sensible symbol |
| name on plt calls. The difficulty in doing so is |
| a) finding the stubs, and, |
| b) matching stubs against plt entries, and, |
| c) there can be multiple stubs for a given plt entry. |
| |
| Solving (a) could be done by code scanning, but older |
| ppc64 binaries used different stubs to current code. |
| (b) is the tricky one since you need to known the toc |
| pointer for at least one function that uses a pic stub to |
| be able to calculate the plt address referenced. |
| (c) means gdb would need to set multiple breakpoints (or |
| find the glink branch itself) when setting breakpoints |
| for pending shared library loads. */ |
| p = relplt->relocation; |
| for (i = 0; i < plt_count; i++, p++) |
| { |
| size_t len; |
| |
| *s = **p->sym_ptr_ptr; |
| /* Undefined syms won't have BSF_LOCAL or BSF_GLOBAL set. Since |
| we are defining a symbol, ensure one of them is set. */ |
| if ((s->flags & BSF_LOCAL) == 0) |
| s->flags |= BSF_GLOBAL; |
| s->flags |= BSF_SYNTHETIC; |
| s->section = glink; |
| s->value = glink_vma - glink->vma; |
| s->name = names; |
| s->udata.p = NULL; |
| len = strlen ((*p->sym_ptr_ptr)->name); |
| memcpy (names, (*p->sym_ptr_ptr)->name, len); |
| names += len; |
| if (p->addend != 0) |
| { |
| memcpy (names, "+0x", sizeof ("+0x") - 1); |
| names += sizeof ("+0x") - 1; |
| bfd_sprintf_vma (abfd, names, p->addend); |
| names += strlen (names); |
| } |
| memcpy (names, "@plt", sizeof ("@plt")); |
| names += sizeof ("@plt"); |
| s++; |
| if (abi < 2) |
| { |
| glink_vma += 8; |
| if (i >= 0x8000) |
| glink_vma += 4; |
| } |
| else |
| glink_vma += 4; |
| } |
| count += plt_count; |
| } |
| } |
| |
| done: |
| free (syms); |
| return count; |
| } |
| |
| /* The following functions are specific to the ELF linker, while |
| functions above are used generally. Those named ppc64_elf_* are |
| called by the main ELF linker code. They appear in this file more |
| or less in the order in which they are called. eg. |
| ppc64_elf_check_relocs is called early in the link process, |
| ppc64_elf_finish_dynamic_sections is one of the last functions |
| called. |
| |
| PowerPC64-ELF uses a similar scheme to PowerPC64-XCOFF in that |
| functions have both a function code symbol and a function descriptor |
| symbol. A call to foo in a relocatable object file looks like: |
| |
| . .text |
| . x: |
| . bl .foo |
| . nop |
| |
| The function definition in another object file might be: |
| |
| . .section .opd |
| . foo: .quad .foo |
| . .quad .TOC.@tocbase |
| . .quad 0 |
| . |
| . .text |
| . .foo: blr |
| |
| When the linker resolves the call during a static link, the branch |
| unsurprisingly just goes to .foo and the .opd information is unused. |
| If the function definition is in a shared library, things are a little |
| different: The call goes via a plt call stub, the opd information gets |
| copied to the plt, and the linker patches the nop. |
| |
| . x: |
| . bl .foo_stub |
| . ld 2,40(1) |
| . |
| . |
| . .foo_stub: |
| . std 2,40(1) # in practice, the call stub |
| . addis 11,2,Lfoo@toc@ha # is slightly optimized, but |
| . addi 11,11,Lfoo@toc@l # this is the general idea |
| . ld 12,0(11) |
| . ld 2,8(11) |
| . mtctr 12 |
| . ld 11,16(11) |
| . bctr |
| . |
| . .section .plt |
| . Lfoo: reloc (R_PPC64_JMP_SLOT, foo) |
| |
| The "reloc ()" notation is supposed to indicate that the linker emits |
| an R_PPC64_JMP_SLOT reloc against foo. The dynamic linker does the opd |
| copying. |
| |
| What are the difficulties here? Well, firstly, the relocations |
| examined by the linker in check_relocs are against the function code |
| sym .foo, while the dynamic relocation in the plt is emitted against |
| the function descriptor symbol, foo. Somewhere along the line, we need |
| to carefully copy dynamic link information from one symbol to the other. |
| Secondly, the generic part of the elf linker will make .foo a dynamic |
| symbol as is normal for most other backends. We need foo dynamic |
| instead, at least for an application final link. However, when |
| creating a shared library containing foo, we need to have both symbols |
| dynamic so that references to .foo are satisfied during the early |
| stages of linking. Otherwise the linker might decide to pull in a |
| definition from some other object, eg. a static library. |
| |
| Update: As of August 2004, we support a new convention. Function |
| calls may use the function descriptor symbol, ie. "bl foo". This |
| behaves exactly as "bl .foo". */ |
| |
| /* Of those relocs that might be copied as dynamic relocs, this |
| function selects those that must be copied when linking a shared |
| library or PIE, even when the symbol is local. */ |
| |
| static int |
| must_be_dyn_reloc (struct bfd_link_info *info, |
| enum elf_ppc64_reloc_type r_type) |
| { |
| switch (r_type) |
| { |
| default: |
| /* Only relative relocs can be resolved when the object load |
| address isn't fixed. DTPREL64 is excluded because the |
| dynamic linker needs to differentiate global dynamic from |
| local dynamic __tls_index pairs when PPC64_OPT_TLS is set. */ |
| return 1; |
| |
| case R_PPC64_REL32: |
| case R_PPC64_REL64: |
| case R_PPC64_REL30: |
| case R_PPC64_TOC16: |
| case R_PPC64_TOC16_DS: |
| case R_PPC64_TOC16_LO: |
| case R_PPC64_TOC16_HI: |
| case R_PPC64_TOC16_HA: |
| case R_PPC64_TOC16_LO_DS: |
| return 0; |
| |
| case R_PPC64_TPREL16: |
| case R_PPC64_TPREL16_LO: |
| case R_PPC64_TPREL16_HI: |
| case R_PPC64_TPREL16_HA: |
| case R_PPC64_TPREL16_DS: |
| case R_PPC64_TPREL16_LO_DS: |
| case R_PPC64_TPREL16_HIGH: |
| case R_PPC64_TPREL16_HIGHA: |
| case R_PPC64_TPREL16_HIGHER: |
| case R_PPC64_TPREL16_HIGHERA: |
| case R_PPC64_TPREL16_HIGHEST: |
| case R_PPC64_TPREL16_HIGHESTA: |
| case R_PPC64_TPREL64: |
| case R_PPC64_TPREL34: |
| /* These relocations are relative but in a shared library the |
| linker doesn't know the thread pointer base. */ |
| return bfd_link_dll (info); |
| } |
| } |
| |
| /* If ELIMINATE_COPY_RELOCS is non-zero, the linker will try to avoid |
| copying dynamic variables from a shared lib into an app's .dynbss |
| section, and instead use a dynamic relocation to point into the |
| shared lib. With code that gcc generates it is vital that this be |
| enabled; In the PowerPC64 ELFv1 ABI the address of a function is |
| actually the address of a function descriptor which resides in the |
| .opd section. gcc uses the descriptor directly rather than going |
| via the GOT as some other ABIs do, which means that initialized |
| function pointers reference the descriptor. Thus, a function |
| pointer initialized to the address of a function in a shared |
| library will either require a .dynbss copy and a copy reloc, or a |
| dynamic reloc. Using a .dynbss copy redefines the function |
| descriptor symbol to point to the copy. This presents a problem as |
| a PLT entry for that function is also initialized from the function |
| descriptor symbol and the copy may not be initialized first. */ |
| #define ELIMINATE_COPY_RELOCS 1 |
| |
| /* Section name for stubs is the associated section name plus this |
| string. */ |
| #define STUB_SUFFIX ".stub" |
| |
| /* Linker stubs. |
| ppc_stub_long_branch: |
| Used when a 14 bit branch (or even a 24 bit branch) can't reach its |
| destination, but a 24 bit branch in a stub section will reach. |
| . b dest |
| |
| ppc_stub_plt_branch: |
| Similar to the above, but a 24 bit branch in the stub section won't |
| reach its destination. |
| . addis %r12,%r2,xxx@toc@ha |
| . ld %r12,xxx@toc@l(%r12) |
| . mtctr %r12 |
| . bctr |
| |
| ppc_stub_plt_call: |
| Used to call a function in a shared library. If it so happens that |
| the plt entry referenced crosses a 64k boundary, then an extra |
| "addi %r11,%r11,xxx@toc@l" will be inserted before the "mtctr". |
| An r2save variant starts with "std %r2,40(%r1)". |
| . addis %r11,%r2,xxx@toc@ha |
| . ld %r12,xxx+0@toc@l(%r11) |
| . mtctr %r12 |
| . ld %r2,xxx+8@toc@l(%r11) |
| . ld %r11,xxx+16@toc@l(%r11) |
| . bctr |
| |
| ppc_stub_long_branch and ppc_stub_plt_branch may also have additional |
| code to adjust the value and save r2 to support multiple toc sections. |
| A ppc_stub_long_branch with an r2 offset looks like: |
| . std %r2,40(%r1) |
| . addis %r2,%r2,off@ha |
| . addi %r2,%r2,off@l |
| . b dest |
| |
| A ppc_stub_plt_branch with an r2 offset looks like: |
| . std %r2,40(%r1) |
| . addis %r12,%r2,xxx@toc@ha |
| . ld %r12,xxx@toc@l(%r12) |
| . addis %r2,%r2,off@ha |
| . addi %r2,%r2,off@l |
| . mtctr %r12 |
| . bctr |
| |
| All of the above stubs are shown as their ELFv1 variants. ELFv2 |
| variants exist too, simpler for plt calls since a new toc pointer |
| and static chain are not loaded by the stub. In addition, ELFv2 |
| has some more complex stubs to handle calls marked with NOTOC |
| relocs from functions where r2 is not a valid toc pointer. |
| ppc_stub_long_branch_p9notoc: |
| . mflr %r12 |
| . bcl 20,31,1f |
| . 1: |
| . mflr %r11 |
| . mtlr %r12 |
| . addis %r12,%r11,dest-1b@ha |
| . addi %r12,%r12,dest-1b@l |
| . b dest |
| |
| ppc_stub_plt_branch_p9notoc: |
| . mflr %r12 |
| . bcl 20,31,1f |
| . 1: |
| . mflr %r11 |
| . mtlr %r12 |
| . lis %r12,xxx-1b@highest |
| . ori %r12,%r12,xxx-1b@higher |
| . sldi %r12,%r12,32 |
| . oris %r12,%r12,xxx-1b@high |
| . ori %r12,%r12,xxx-1b@l |
| . add %r12,%r11,%r12 |
| . mtctr %r12 |
| . bctr |
| |
| ppc_stub_plt_call_p9notoc: |
| . mflr %r12 |
| . bcl 20,31,1f |
| . 1: |
| . mflr %r11 |
| . mtlr %r12 |
| . lis %r12,xxx-1b@highest |
| . ori %r12,%r12,xxx-1b@higher |
| . sldi %r12,%r12,32 |
| . oris %r12,%r12,xxx-1b@high |
| . ori %r12,%r12,xxx-1b@l |
| . ldx %r12,%r11,%r12 |
| . mtctr %r12 |
| . bctr |
| |
| There are also ELFv1 power10 variants of these stubs. |
| ppc_stub_long_branch_notoc: |
| . pla %r12,dest@pcrel |
| . b dest |
| ppc_stub_plt_branch_notoc: |
| . lis %r11,(dest-1f)@highesta34 |
| . ori %r11,%r11,(dest-1f)@highera34 |
| . sldi %r11,%r11,34 |
| . 1: pla %r12,dest@pcrel |
| . add %r12,%r11,%r12 |
| . mtctr %r12 |
| . bctr |
| ppc_stub_plt_call_notoc: |
| . lis %r11,(xxx-1f)@highesta34 |
| . ori %r11,%r11,(xxx-1f)@highera34 |
| . sldi %r11,%r11,34 |
|