blob: 4ebacbd99457bcc6ea1d239159bd9d8b065115dc [file] [log] [blame]
/* PowerPC64-specific support for 64-bit ELF.
Copyright (C) 1999-2021 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_RELROPAGESIZE ELF_MAXPAGESIZE
#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_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_always_size_sections ppc64_elf_edit
#define elf_backend_size_dynamic_sections ppc64_elf_size_dynamic_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;
}
/* 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, 3, 0, 0, 0, false, dont,
bfd_elf_generic_reloc),
/* A standard 32 bit relocation. */
HOW (R_PPC64_ADDR32, 2, 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, 2, 26, 0x03fffffc, 0, false, bitfield,
bfd_elf_generic_reloc),
/* A standard 16 bit relocation. */
HOW (R_PPC64_ADDR16, 1, 16, 0xffff, 0, false, bitfield,
bfd_elf_generic_reloc),
/* A 16 bit relocation without overflow. */
HOW (R_PPC64_ADDR16_LO, 1, 16, 0xffff, 0, false, dont,
bfd_elf_generic_reloc),
/* Bits 16-31 of an address. */
HOW (R_PPC64_ADDR16_HI, 1, 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, 1, 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, 2, 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, 2, 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, 2, 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, 2, 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, 2, 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, 2, 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, 2, 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, 2, 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, 1, 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, 1, 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, 1, 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, 1, 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, 4, 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, 4, 64, 0xffffffffffffffffULL, 0, false, dont,
bfd_elf_generic_reloc),
/* Like R_PPC64_ADDR32, but may be unaligned. */
HOW (R_PPC64_UADDR32, 2, 32, 0xffffffff, 0, false, bitfield,
bfd_elf_generic_reloc),
/* Like R_PPC64_ADDR16, but may be unaligned. */
HOW (R_PPC64_UADDR16, 1, 16, 0xffff, 0, false, bitfield,
bfd_elf_generic_reloc),
/* 32-bit PC relative. */
HOW (R_PPC64_REL32, 2, 32, 0xffffffff, 0, true, signed,
bfd_elf_generic_reloc),
/* 32-bit relocation to the symbol's procedure linkage table. */
HOW (R_PPC64_PLT32, 2, 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, 2, 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, 1, 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, 1, 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, 1, 16, 0xffff, 16, false, signed,
ppc64_elf_unhandled_reloc),
/* 16-bit section relative relocation. */
HOW (R_PPC64_SECTOFF, 1, 16, 0xffff, 0, false, signed,
ppc64_elf_sectoff_reloc),
/* Like R_PPC64_SECTOFF, but no overflow warning. */
HOW (R_PPC64_SECTOFF_LO, 1, 16, 0xffff, 0, false, dont,
ppc64_elf_sectoff_reloc),
/* 16-bit upper half section relative relocation. */
HOW (R_PPC64_SECTOFF_HI, 1, 16, 0xffff, 16, false, signed,
ppc64_elf_sectoff_reloc),
/* 16-bit upper half adjusted section relative relocation. */
HOW (R_PPC64_SECTOFF_HA, 1, 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, 2, 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, 4, 64, 0xffffffffffffffffULL, 0, false, dont,
bfd_elf_generic_reloc),
/* The bits 32-47 of an address. */
HOW (R_PPC64_ADDR16_HIGHER, 1, 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, 1, 16, 0xffff, 32, false, dont,
ppc64_elf_ha_reloc),
/* The bits 48-63 of an address. */
HOW (R_PPC64_ADDR16_HIGHEST, 1, 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, 1, 16, 0xffff, 48, false, dont,
ppc64_elf_ha_reloc),
/* Like ADDR64, but may be unaligned. */
HOW (R_PPC64_UADDR64, 4, 64, 0xffffffffffffffffULL, 0, false, dont,
bfd_elf_generic_reloc),
/* 64-bit relative relocation. */
HOW (R_PPC64_REL64, 4, 64, 0xffffffffffffffffULL, 0, true, dont,
bfd_elf_generic_reloc),
/* 64-bit relocation to the symbol's procedure linkage table. */
HOW (R_PPC64_PLT64, 4, 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, 4, 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, 1, 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, 1, 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, 1, 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, 1, 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, 4, 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, 1, 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, 1, 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, 1, 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, 1, 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, 1, 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, 1, 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, 1, 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, 1, 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, 1, 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, 1, 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, 1, 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, 1, 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, 1, 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, 1, 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, 1, 16, 0xfffc, 0, false, dont,
ppc64_elf_unhandled_reloc),
/* Marker relocs for TLS. */
HOW (R_PPC64_TLS, 2, 32, 0, 0, false, dont,
bfd_elf_generic_reloc),
HOW (R_PPC64_TLSGD, 2, 32, 0, 0, false, dont,
bfd_elf_generic_reloc),
HOW (R_PPC64_TLSLD, 2, 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, 2, 32, 0, 0, false, dont,
bfd_elf_generic_reloc),
/* Marker relocs on inline plt call instructions. */
HOW (R_PPC64_PLTSEQ, 2, 32, 0, 0, false, dont,
bfd_elf_generic_reloc),
HOW (R_PPC64_PLTCALL, 2, 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, 4, 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, 4, 64, 0xffffffffffffffffULL, 0, false, dont,
ppc64_elf_unhandled_reloc),
/* A 16 bit dtprel reloc. */
HOW (R_PPC64_DTPREL16, 1, 16, 0xffff, 0, false, signed,
ppc64_elf_unhandled_reloc),
/* Like DTPREL16, but no overflow. */
HOW (R_PPC64_DTPREL16_LO, 1, 16, 0xffff, 0, false, dont,
ppc64_elf_unhandled_reloc),
/* Like DTPREL16_LO, but next higher group of 16 bits. */
HOW (R_PPC64_DTPREL16_HI, 1, 16, 0xffff, 16, false, signed,
ppc64_elf_unhandled_reloc),
/* Like DTPREL16_HI, but adjust for low 16 bits. */
HOW (R_PPC64_DTPREL16_HA, 1, 16, 0xffff, 16, false, signed,
ppc64_elf_unhandled_reloc),
/* Like DTPREL16_HI, but next higher group of 16 bits. */
HOW (R_PPC64_DTPREL16_HIGHER, 1, 16, 0xffff, 32, false, dont,
ppc64_elf_unhandled_reloc),
/* Like DTPREL16_HIGHER, but adjust for low 16 bits. */
HOW (R_PPC64_DTPREL16_HIGHERA, 1, 16, 0xffff, 32, false, dont,
ppc64_elf_unhandled_reloc),
/* Like DTPREL16_HIGHER, but next higher group of 16 bits. */
HOW (R_PPC64_DTPREL16_HIGHEST, 1, 16, 0xffff, 48, false, dont,
ppc64_elf_unhandled_reloc),
/* Like DTPREL16_HIGHEST, but adjust for low 16 bits. */
HOW (R_PPC64_DTPREL16_HIGHESTA, 1, 16, 0xffff, 48, false, dont,
ppc64_elf_unhandled_reloc),
/* Like DTPREL16, but for insns with a DS field. */
HOW (R_PPC64_DTPREL16_DS, 1, 16, 0xfffc, 0, false, signed,
ppc64_elf_unhandled_reloc),
/* Like DTPREL16_DS, but no overflow. */
HOW (R_PPC64_DTPREL16_LO_DS, 1, 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, 4, 64, 0xffffffffffffffffULL, 0, false, dont,
ppc64_elf_unhandled_reloc),
/* A 16 bit tprel reloc. */
HOW (R_PPC64_TPREL16, 1, 16, 0xffff, 0, false, signed,
ppc64_elf_unhandled_reloc),
/* Like TPREL16, but no overflow. */
HOW (R_PPC64_TPREL16_LO, 1, 16, 0xffff, 0, false, dont,
ppc64_elf_unhandled_reloc),
/* Like TPREL16_LO, but next higher group of 16 bits. */
HOW (R_PPC64_TPREL16_HI, 1, 16, 0xffff, 16, false, signed,
ppc64_elf_unhandled_reloc),
/* Like TPREL16_HI, but adjust for low 16 bits. */
HOW (R_PPC64_TPREL16_HA, 1, 16, 0xffff, 16, false, signed,
ppc64_elf_unhandled_reloc),
/* Like TPREL16_HI, but next higher group of 16 bits. */
HOW (R_PPC64_TPREL16_HIGHER, 1, 16, 0xffff, 32, false, dont,
ppc64_elf_unhandled_reloc),
/* Like TPREL16_HIGHER, but adjust for low 16 bits. */
HOW (R_PPC64_TPREL16_HIGHERA, 1, 16, 0xffff, 32, false, dont,
ppc64_elf_unhandled_reloc),
/* Like TPREL16_HIGHER, but next higher group of 16 bits. */
HOW (R_PPC64_TPREL16_HIGHEST, 1, 16, 0xffff, 48, false, dont,
ppc64_elf_unhandled_reloc),
/* Like TPREL16_HIGHEST, but adjust for low 16 bits. */
HOW (R_PPC64_TPREL16_HIGHESTA, 1, 16, 0xffff, 48, false, dont,
ppc64_elf_unhandled_reloc),
/* Like TPREL16, but for insns with a DS field. */
HOW (R_PPC64_TPREL16_DS, 1, 16, 0xfffc, 0, false, signed,
ppc64_elf_unhandled_reloc),
/* Like TPREL16_DS, but no overflow. */
HOW (R_PPC64_TPREL16_LO_DS, 1, 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, 1, 16, 0xffff, 0, false, signed,
ppc64_elf_unhandled_reloc),
/* Like GOT_TLSGD16, but no overflow. */
HOW (R_PPC64_GOT_TLSGD16_LO, 1, 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, 1, 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, 1, 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, 1, 16, 0xffff, 0, false, signed,
ppc64_elf_unhandled_reloc),
/* Like GOT_TLSLD16, but no overflow. */
HOW (R_PPC64_GOT_TLSLD16_LO, 1, 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, 1, 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, 1, 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, 1, 16, 0xfffc, 0, false, signed,
ppc64_elf_unhandled_reloc),
/* Like GOT_DTPREL16_DS, but no overflow. */
HOW (R_PPC64_GOT_DTPREL16_LO_DS, 1, 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, 1, 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, 1, 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, 1, 16, 0xfffc, 0, false, signed,
ppc64_elf_unhandled_reloc),
/* Like GOT_TPREL16_DS, but no overflow. */
HOW (R_PPC64_GOT_TPREL16_LO_DS, 1, 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, 1, 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, 1, 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, 4, 64, 0xffffffffffffffffULL, 0, false, dont,
bfd_elf_generic_reloc),
/* A 16 bit relative relocation. */
HOW (R_PPC64_REL16, 1, 16, 0xffff, 0, true, signed,
bfd_elf_generic_reloc),
/* A 16 bit relative relocation without overflow. */
HOW (R_PPC64_REL16_LO, 1, 16, 0xffff, 0, true, dont,
bfd_elf_generic_reloc),
/* The high order 16 bits of a relative address. */
HOW (R_PPC64_REL16_HI, 1, 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, 1, 16, 0xffff, 16, true, signed,
ppc64_elf_ha_reloc),
HOW (R_PPC64_REL16_HIGH, 1, 16, 0xffff, 16, true, dont,
bfd_elf_generic_reloc),
HOW (R_PPC64_REL16_HIGHA, 1, 16, 0xffff, 16, true, dont,
ppc64_elf_ha_reloc),
HOW (R_PPC64_REL16_HIGHER, 1, 16, 0xffff, 32, true, dont,
bfd_elf_generic_reloc),
HOW (R_PPC64_REL16_HIGHERA, 1, 16, 0xffff, 32, true, dont,
ppc64_elf_ha_reloc),
HOW (R_PPC64_REL16_HIGHEST, 1, 16, 0xffff, 48, true, dont,
bfd_elf_generic_reloc),
HOW (R_PPC64_REL16_HIGHESTA, 1, 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, 2, 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, 2, 16, 0x1fffc1, 16, false, signed,
ppc64_elf_ha_reloc),
/* Like R_PPC64_ADDR16_HI, but no overflow. */
HOW (R_PPC64_ADDR16_HIGH, 1, 16, 0xffff, 16, false, dont,
bfd_elf_generic_reloc),
/* Like R_PPC64_ADDR16_HA, but no overflow. */
HOW (R_PPC64_ADDR16_HIGHA, 1, 16, 0xffff, 16, false, dont,
ppc64_elf_ha_reloc),
/* Like R_PPC64_DTPREL16_HI, but no overflow. */
HOW (R_PPC64_DTPREL16_HIGH, 1, 16, 0xffff, 16, false, dont,
ppc64_elf_unhandled_reloc),
/* Like R_PPC64_DTPREL16_HA, but no overflow. */
HOW (R_PPC64_DTPREL16_HIGHA, 1, 16, 0xffff, 16, false, dont,
ppc64_elf_unhandled_reloc),
/* Like R_PPC64_TPREL16_HI, but no overflow. */
HOW (R_PPC64_TPREL16_HIGH, 1, 16, 0xffff, 16, false, dont,
ppc64_elf_unhandled_reloc),
/* Like R_PPC64_TPREL16_HA, but no overflow. */
HOW (R_PPC64_TPREL16_HIGHA, 1, 16, 0xffff, 16, false, dont,
ppc64_elf_unhandled_reloc),
/* Marker reloc on ELFv2 large-model function entry. */
HOW (R_PPC64_ENTRY, 2, 32, 0, 0, false, dont,
bfd_elf_generic_reloc),
/* Like ADDR64, but use local entry point of function. */
HOW (R_PPC64_ADDR64_LOCAL, 4, 64, 0xffffffffffffffffULL, 0, false, dont,
bfd_elf_generic_reloc),
HOW (R_PPC64_PLTSEQ_NOTOC, 2, 32, 0, 0, false, dont,
bfd_elf_generic_reloc),
HOW (R_PPC64_PLTCALL_NOTOC, 2, 32, 0, 0, false, dont,
bfd_elf_generic_reloc),
HOW (R_PPC64_PCREL_OPT, 2, 32, 0, 0, false, dont,
bfd_elf_generic_reloc),
HOW (R_PPC64_D34, 4, 34, 0x3ffff0000ffffULL, 0, false, signed,
ppc64_elf_prefix_reloc),
HOW (R_PPC64_D34_LO, 4, 34, 0x3ffff0000ffffULL, 0, false, dont,
ppc64_elf_prefix_reloc),
HOW (R_PPC64_D34_HI30, 4, 34, 0x3ffff0000ffffULL, 34, false, dont,
ppc64_elf_prefix_reloc),
HOW (R_PPC64_D34_HA30, 4, 34, 0x3ffff0000ffffULL, 34, false, dont,
ppc64_elf_prefix_reloc),
HOW (R_PPC64_PCREL34, 4, 34, 0x3ffff0000ffffULL, 0, true, signed,
ppc64_elf_prefix_reloc),
HOW (R_PPC64_GOT_PCREL34, 4, 34, 0x3ffff0000ffffULL, 0, true, signed,
ppc64_elf_unhandled_reloc),
HOW (R_PPC64_PLT_PCREL34, 4, 34, 0x3ffff0000ffffULL, 0, true, signed,
ppc64_elf_unhandled_reloc),
HOW (R_PPC64_PLT_PCREL34_NOTOC, 4, 34, 0x3ffff0000ffffULL, 0, true, signed,
ppc64_elf_unhandled_reloc),
HOW (R_PPC64_TPREL34, 4, 34, 0x3ffff0000ffffULL, 0, false, signed,
ppc64_elf_unhandled_reloc),
HOW (R_PPC64_DTPREL34, 4, 34, 0x3ffff0000ffffULL, 0, false, signed,
ppc64_elf_unhandled_reloc),
HOW (R_PPC64_GOT_TLSGD_PCREL34, 4, 34, 0x3ffff0000ffffULL, 0, true, signed,
ppc64_elf_unhandled_reloc),
HOW (R_PPC64_GOT_TLSLD_PCREL34, 4, 34, 0x3ffff0000ffffULL, 0, true, signed,
ppc64_elf_unhandled_reloc),
HOW (R_PPC64_GOT_TPREL_PCREL34, 4, 34, 0x3ffff0000ffffULL, 0, true, signed,
ppc64_elf_unhandled_reloc),
HOW (R_PPC64_GOT_DTPREL_PCREL34, 4, 34, 0x3ffff0000ffffULL, 0, true, signed,
ppc64_elf_unhandled_reloc),
HOW (R_PPC64_ADDR16_HIGHER34, 1, 16, 0xffff, 34, false, dont,
bfd_elf_generic_reloc),
HOW (R_PPC64_ADDR16_HIGHERA34, 1, 16, 0xffff, 34, false, dont,
ppc64_elf_ha_reloc),
HOW (R_PPC64_ADDR16_HIGHEST34, 1, 16, 0xffff, 50, false, dont,
bfd_elf_generic_reloc),
HOW (R_PPC64_ADDR16_HIGHESTA34, 1, 16, 0xffff, 50, false, dont,
ppc64_elf_ha_reloc),
HOW (R_PPC64_REL16_HIGHER34, 1, 16, 0xffff, 34, true, dont,
bfd_elf_generic_reloc),
HOW (R_PPC64_REL16_HIGHERA34, 1, 16, 0xffff, 34, true, dont,
ppc64_elf_ha_reloc),
HOW (R_PPC64_REL16_HIGHEST34, 1, 16, 0xffff, 50, true, dont,
bfd_elf_generic_reloc),
HOW (R_PPC64_REL16_HIGHESTA34, 1, 16, 0xffff, 50, true, dont,
ppc64_elf_ha_reloc),
HOW (R_PPC64_D28, 4, 28, 0xfff0000ffffULL, 0, false, signed,
ppc64_elf_prefix_reloc),
HOW (R_PPC64_PCREL28, 4, 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_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 (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
&& symbol->section->owner != NULL
&& 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)
{
static char *message;
free (message);
if (asprintf (&message, _("generic linker can't handle %s"),
reloc_entry->howto->name) < 0)
message = NULL;
*error_message = message;
}
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;
union
{
/* A copy of relocs before they are modified for --emit-relocs. */
Elf_Internal_Rela *relocs;
/* Section contents. */
bfd_byte *contents;
} opd;
/* 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)
#define is_ppc64_elf(bfd) \
(bfd_get_flavour (bfd) == bfd_target_elf_flavour \
&& elf_object_id (bfd) == PPC64_ELF_DATA)
/* 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
};
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;
} 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;
} 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 && !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 (!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;
extdyn = dynbuf;
extdynend = extdyn + dynamic->size;
for (; extdyn < extdynend; 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 = relplt->size / sizeof (Elf64_External_Rela);
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".
ppc_stub_plt_call_r2save 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. These
come in two flavours, the ones shown below, and _both variants that
start with "std %r2,24(%r1)" to save r2 in the unlikely event that
one call is from a function where r2 is used as the toc pointer but
needs a toc adjusting stub for small-model multi-toc, and another
call is from a function where r2 is not valid.
ppc_stub_long_branch_notoc:
. 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_notoc:
. 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_notoc:
. 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
. 1: pla %r12,xxx@pcrel
. ldx %r12,%r11,%r12
. mtctr %r12
. bctr
In cases where the high instructions would add zero, they are
omitted and following instructions modified in some cases.
For example, a power10 ppc_stub_plt_call_notoc might simplify down
to
. pld %r12,xxx@pcrel
. mtctr %r12