blob: 34a6b3fd837cc82dd27fe5da3ce76a8852f6aad3 [file] [log] [blame]
/* PowerPC-specific support for 32-bit ELF
Copyright (C) 1994-2021 Free Software Foundation, Inc.
Written by Ian Lance Taylor, Cygnus Support.
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. */
/* Don't generate unused section symbols. */
#define TARGET_KEEP_UNUSED_SECTION_SYMBOLS false
#include "sysdep.h"
#include <stdarg.h>
#include "bfd.h"
#include "bfdlink.h"
#include "libbfd.h"
#include "elf-bfd.h"
#include "elf/ppc.h"
#include "elf32-ppc.h"
#include "elf-vxworks.h"
#include "dwarf2.h"
#include "opcode/ppc.h"
/* All users of this file have bfd_octets_per_byte (abfd, sec) == 1. */
#define OCTETS_PER_BYTE(ABFD, SEC) 1
typedef enum split16_format_type
{
split16a_type = 0,
split16d_type
}
split16_format_type;
/* RELA relocations are used here. */
static bfd_reloc_status_type ppc_elf_addr16_ha_reloc
(bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
static bfd_reloc_status_type ppc_elf_unhandled_reloc
(bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
/* Branch prediction bit for branch taken relocs. */
#define BRANCH_PREDICT_BIT 0x200000
/* Mask to set RA in memory instructions. */
#define RA_REGISTER_MASK 0x001f0000
/* Value to shift register by to insert RA. */
#define RA_REGISTER_SHIFT 16
/* The name of the dynamic interpreter. This is put in the .interp
section. */
#define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so.1"
/* For old-style PLT. */
/* The number of single-slot PLT entries (the rest use two slots). */
#define PLT_NUM_SINGLE_ENTRIES 8192
/* For new-style .glink and .plt. */
#define GLINK_PLTRESOLVE 16*4
#define GLINK_ENTRY_SIZE(htab, h) \
((4*4 \
+ (h != NULL \
&& h == htab->tls_get_addr \
&& !htab->params->no_tls_get_addr_opt ? 8*4 : 0) \
+ (1u << htab->params->plt_stub_align) - 1) \
& -(1u << htab->params->plt_stub_align))
/* VxWorks uses its own plt layout, filled in by the static linker. */
/* The standard VxWorks PLT entry. */
#define VXWORKS_PLT_ENTRY_SIZE 32
static const bfd_vma ppc_elf_vxworks_plt_entry
[VXWORKS_PLT_ENTRY_SIZE / 4] =
{
0x3d800000, /* lis r12,0 */
0x818c0000, /* lwz r12,0(r12) */
0x7d8903a6, /* mtctr r12 */
0x4e800420, /* bctr */
0x39600000, /* li r11,0 */
0x48000000, /* b 14 <.PLT0resolve+0x4> */
0x60000000, /* nop */
0x60000000, /* nop */
};
static const bfd_vma ppc_elf_vxworks_pic_plt_entry
[VXWORKS_PLT_ENTRY_SIZE / 4] =
{
0x3d9e0000, /* addis r12,r30,0 */
0x818c0000, /* lwz r12,0(r12) */
0x7d8903a6, /* mtctr r12 */
0x4e800420, /* bctr */
0x39600000, /* li r11,0 */
0x48000000, /* b 14 <.PLT0resolve+0x4> 14: R_PPC_REL24 .PLTresolve */
0x60000000, /* nop */
0x60000000, /* nop */
};
/* The initial VxWorks PLT entry. */
#define VXWORKS_PLT_INITIAL_ENTRY_SIZE 32
static const bfd_vma ppc_elf_vxworks_plt0_entry
[VXWORKS_PLT_INITIAL_ENTRY_SIZE / 4] =
{
0x3d800000, /* lis r12,0 */
0x398c0000, /* addi r12,r12,0 */
0x800c0008, /* lwz r0,8(r12) */
0x7c0903a6, /* mtctr r0 */
0x818c0004, /* lwz r12,4(r12) */
0x4e800420, /* bctr */
0x60000000, /* nop */
0x60000000, /* nop */
};
static const bfd_vma ppc_elf_vxworks_pic_plt0_entry
[VXWORKS_PLT_INITIAL_ENTRY_SIZE / 4] =
{
0x819e0008, /* lwz r12,8(r30) */
0x7d8903a6, /* mtctr r12 */
0x819e0004, /* lwz r12,4(r30) */
0x4e800420, /* bctr */
0x60000000, /* nop */
0x60000000, /* nop */
0x60000000, /* nop */
0x60000000, /* nop */
};
/* For executables, we have some additional relocations in
.rela.plt.unloaded, for the kernel loader. */
/* The number of non-JMP_SLOT relocations per PLT0 slot. */
#define VXWORKS_PLT_NON_JMP_SLOT_RELOCS 3
/* The number of relocations in the PLTResolve slot. */
#define VXWORKS_PLTRESOLVE_RELOCS 2
/* The number of relocations in the PLTResolve slot when creating
a shared library. */
#define VXWORKS_PLTRESOLVE_RELOCS_SHLIB 0
/* Some instructions. */
#define ADDIS_11_11 0x3d6b0000
#define ADDIS_11_30 0x3d7e0000
#define ADDIS_12_12 0x3d8c0000
#define ADDI_11_11 0x396b0000
#define ADD_0_11_11 0x7c0b5a14
#define ADD_3_12_2 0x7c6c1214
#define ADD_11_0_11 0x7d605a14
#define B 0x48000000
#define BA 0x48000002
#define BCL_20_31 0x429f0005
#define BCTR 0x4e800420
#define BEQLR 0x4d820020
#define CMPWI_11_0 0x2c0b0000
#define LIS_11 0x3d600000
#define LIS_12 0x3d800000
#define LWZU_0_12 0x840c0000
#define LWZ_0_12 0x800c0000
#define LWZ_11_3 0x81630000
#define LWZ_11_11 0x816b0000
#define LWZ_11_30 0x817e0000
#define LWZ_12_3 0x81830000
#define LWZ_12_12 0x818c0000
#define MR_0_3 0x7c601b78
#define MR_3_0 0x7c030378
#define MFLR_0 0x7c0802a6
#define MFLR_12 0x7d8802a6
#define MTCTR_0 0x7c0903a6
#define MTCTR_11 0x7d6903a6
#define MTLR_0 0x7c0803a6
#define NOP 0x60000000
#define SUB_11_11_12 0x7d6c5850
/* Offset of tp and dtp pointers from start of TLS block. */
#define TP_OFFSET 0x7000
#define DTP_OFFSET 0x8000
/* The value of a defined global symbol. */
#define SYM_VAL(SYM) \
((SYM)->root.u.def.section->output_section->vma \
+ (SYM)->root.u.def.section->output_offset \
+ (SYM)->root.u.def.value)
/* 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 *ppc_elf_howto_table[R_PPC_max];
static reloc_howto_type ppc_elf_howto_raw[] = {
/* This reloc does nothing. */
HOW (R_PPC_NONE, 3, 0, 0, 0, false, dont,
bfd_elf_generic_reloc),
/* A standard 32 bit relocation. */
HOW (R_PPC_ADDR32, 2, 32, 0xffffffff, 0, false, dont,
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_PPC_ADDR24, 2, 26, 0x3fffffc, 0, false, signed,
bfd_elf_generic_reloc),
/* A standard 16 bit relocation. */
HOW (R_PPC_ADDR16, 1, 16, 0xffff, 0, false, bitfield,
bfd_elf_generic_reloc),
/* A 16 bit relocation without overflow. */
HOW (R_PPC_ADDR16_LO, 1, 16, 0xffff, 0, false, dont,
bfd_elf_generic_reloc),
/* The high order 16 bits of an address. */
HOW (R_PPC_ADDR16_HI, 1, 16, 0xffff, 16, false, dont,
bfd_elf_generic_reloc),
/* The high order 16 bits of an address, plus 1 if the contents of
the low 16 bits, treated as a signed number, is negative. */
HOW (R_PPC_ADDR16_HA, 1, 16, 0xffff, 16, false, dont,
ppc_elf_addr16_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_PPC_ADDR14, 2, 16, 0xfffc, 0, false, signed,
bfd_elf_generic_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_PPC_ADDR14_BRTAKEN, 2, 16, 0xfffc, 0, false, signed,
bfd_elf_generic_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_PPC_ADDR14_BRNTAKEN, 2, 16, 0xfffc, 0, false, signed,
bfd_elf_generic_reloc),
/* A relative 26 bit branch; the lower two bits must be zero. */
HOW (R_PPC_REL24, 2, 26, 0x3fffffc, 0, true, signed,
bfd_elf_generic_reloc),
/* A relative 16 bit branch; the lower two bits must be zero. */
HOW (R_PPC_REL14, 2, 16, 0xfffc, 0, true, signed,
bfd_elf_generic_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_PPC_REL14_BRTAKEN, 2, 16, 0xfffc, 0, true, signed,
bfd_elf_generic_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_PPC_REL14_BRNTAKEN, 2, 16, 0xfffc, 0, true, signed,
bfd_elf_generic_reloc),
/* Like R_PPC_ADDR16, but referring to the GOT table entry for the
symbol. */
HOW (R_PPC_GOT16, 1, 16, 0xffff, 0, false, signed,
ppc_elf_unhandled_reloc),
/* Like R_PPC_ADDR16_LO, but referring to the GOT table entry for
the symbol. */
HOW (R_PPC_GOT16_LO, 1, 16, 0xffff, 0, false, dont,
ppc_elf_unhandled_reloc),
/* Like R_PPC_ADDR16_HI, but referring to the GOT table entry for
the symbol. */
HOW (R_PPC_GOT16_HI, 1, 16, 0xffff, 16, false, dont,
ppc_elf_unhandled_reloc),
/* Like R_PPC_ADDR16_HA, but referring to the GOT table entry for
the symbol. */
HOW (R_PPC_GOT16_HA, 1, 16, 0xffff, 16, false, dont,
ppc_elf_unhandled_reloc),
/* Like R_PPC_REL24, but referring to the procedure linkage table
entry for the symbol. */
HOW (R_PPC_PLTREL24, 2, 26, 0x3fffffc, 0, true, signed,
ppc_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_PPC_COPY, 2, 32, 0, 0, false, dont,
ppc_elf_unhandled_reloc),
/* Like R_PPC_ADDR32, but used when setting global offset table
entries. */
HOW (R_PPC_GLOB_DAT, 2, 32, 0xffffffff, 0, false, dont,
ppc_elf_unhandled_reloc),
/* Marks a procedure linkage table entry for a symbol. */
HOW (R_PPC_JMP_SLOT, 2, 32, 0, 0, false, dont,
ppc_elf_unhandled_reloc),
/* Used only by the dynamic linker. When the object is run, this
longword is set to the load address of the object, plus the
addend. */
HOW (R_PPC_RELATIVE, 2, 32, 0xffffffff, 0, false, dont,
bfd_elf_generic_reloc),
/* Like R_PPC_REL24, but uses the value of the symbol within the
object rather than the final value. Normally used for
_GLOBAL_OFFSET_TABLE_. */
HOW (R_PPC_LOCAL24PC, 2, 26, 0x3fffffc, 0, true, signed,
bfd_elf_generic_reloc),
/* Like R_PPC_ADDR32, but may be unaligned. */
HOW (R_PPC_UADDR32, 2, 32, 0xffffffff, 0, false, dont,
bfd_elf_generic_reloc),
/* Like R_PPC_ADDR16, but may be unaligned. */
HOW (R_PPC_UADDR16, 1, 16, 0xffff, 0, false, bitfield,
bfd_elf_generic_reloc),
/* 32-bit PC relative */
HOW (R_PPC_REL32, 2, 32, 0xffffffff, 0, true, dont,
bfd_elf_generic_reloc),
/* 32-bit relocation to the symbol's procedure linkage table.
FIXME: not supported. */
HOW (R_PPC_PLT32, 2, 32, 0, 0, false, dont,
ppc_elf_unhandled_reloc),
/* 32-bit PC relative relocation to the symbol's procedure linkage table.
FIXME: not supported. */
HOW (R_PPC_PLTREL32, 2, 32, 0, 0, true, dont,
ppc_elf_unhandled_reloc),
/* Like R_PPC_ADDR16_LO, but referring to the PLT table entry for
the symbol. */
HOW (R_PPC_PLT16_LO, 1, 16, 0xffff, 0, false, dont,
ppc_elf_unhandled_reloc),
/* Like R_PPC_ADDR16_HI, but referring to the PLT table entry for
the symbol. */
HOW (R_PPC_PLT16_HI, 1, 16, 0xffff, 16, false, dont,
ppc_elf_unhandled_reloc),
/* Like R_PPC_ADDR16_HA, but referring to the PLT table entry for
the symbol. */
HOW (R_PPC_PLT16_HA, 1, 16, 0xffff, 16, false, dont,
ppc_elf_unhandled_reloc),
/* A sign-extended 16 bit value relative to _SDA_BASE_, for use with
small data items. */
HOW (R_PPC_SDAREL16, 1, 16, 0xffff, 0, false, signed,
ppc_elf_unhandled_reloc),
/* 16-bit section relative relocation. */
HOW (R_PPC_SECTOFF, 1, 16, 0xffff, 0, false, signed,
ppc_elf_unhandled_reloc),
/* 16-bit lower half section relative relocation. */
HOW (R_PPC_SECTOFF_LO, 1, 16, 0xffff, 0, false, dont,
ppc_elf_unhandled_reloc),
/* 16-bit upper half section relative relocation. */
HOW (R_PPC_SECTOFF_HI, 1, 16, 0xffff, 16, false, dont,
ppc_elf_unhandled_reloc),
/* 16-bit upper half adjusted section relative relocation. */
HOW (R_PPC_SECTOFF_HA, 1, 16, 0xffff, 16, false, dont,
ppc_elf_unhandled_reloc),
/* Marker relocs for TLS. */
HOW (R_PPC_TLS, 2, 32, 0, 0, false, dont,
bfd_elf_generic_reloc),
HOW (R_PPC_TLSGD, 2, 32, 0, 0, false, dont,
bfd_elf_generic_reloc),
HOW (R_PPC_TLSLD, 2, 32, 0, 0, false, dont,
bfd_elf_generic_reloc),
/* Marker relocs on inline plt call instructions. */
HOW (R_PPC_PLTSEQ, 2, 32, 0, 0, false, dont,
bfd_elf_generic_reloc),
HOW (R_PPC_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_PPC_DTPMOD32, 2, 32, 0xffffffff, 0, false, dont,
ppc_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_PPC_DTPREL32, 2, 32, 0xffffffff, 0, false, dont,
ppc_elf_unhandled_reloc),
/* A 16 bit dtprel reloc. */
HOW (R_PPC_DTPREL16, 1, 16, 0xffff, 0, false, signed,
ppc_elf_unhandled_reloc),
/* Like DTPREL16, but no overflow. */
HOW (R_PPC_DTPREL16_LO, 1, 16, 0xffff, 0, false, dont,
ppc_elf_unhandled_reloc),
/* Like DTPREL16_LO, but next higher group of 16 bits. */
HOW (R_PPC_DTPREL16_HI, 1, 16, 0xffff, 16, false, dont,
ppc_elf_unhandled_reloc),
/* Like DTPREL16_HI, but adjust for low 16 bits. */
HOW (R_PPC_DTPREL16_HA, 1, 16, 0xffff, 16, false, dont,
ppc_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_PPC_TPREL32, 2, 32, 0xffffffff, 0, false, dont,
ppc_elf_unhandled_reloc),
/* A 16 bit tprel reloc. */
HOW (R_PPC_TPREL16, 1, 16, 0xffff, 0, false, signed,
ppc_elf_unhandled_reloc),
/* Like TPREL16, but no overflow. */
HOW (R_PPC_TPREL16_LO, 1, 16, 0xffff, 0, false, dont,
ppc_elf_unhandled_reloc),
/* Like TPREL16_LO, but next higher group of 16 bits. */
HOW (R_PPC_TPREL16_HI, 1, 16, 0xffff, 16, false, dont,
ppc_elf_unhandled_reloc),
/* Like TPREL16_HI, but adjust for low 16 bits. */
HOW (R_PPC_TPREL16_HA, 1, 16, 0xffff, 16, false, dont,
ppc_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. */
HOW (R_PPC_GOT_TLSGD16, 1, 16, 0xffff, 0, false, signed,
ppc_elf_unhandled_reloc),
/* Like GOT_TLSGD16, but no overflow. */
HOW (R_PPC_GOT_TLSGD16_LO, 1, 16, 0xffff, 0, false, dont,
ppc_elf_unhandled_reloc),
/* Like GOT_TLSGD16_LO, but next higher group of 16 bits. */
HOW (R_PPC_GOT_TLSGD16_HI, 1, 16, 0xffff, 16, false, dont,
ppc_elf_unhandled_reloc),
/* Like GOT_TLSGD16_HI, but adjust for low 16 bits. */
HOW (R_PPC_GOT_TLSGD16_HA, 1, 16, 0xffff, 16, false, dont,
ppc_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. */
HOW (R_PPC_GOT_TLSLD16, 1, 16, 0xffff, 0, false, signed,
ppc_elf_unhandled_reloc),
/* Like GOT_TLSLD16, but no overflow. */
HOW (R_PPC_GOT_TLSLD16_LO, 1, 16, 0xffff, 0, false, dont,
ppc_elf_unhandled_reloc),
/* Like GOT_TLSLD16_LO, but next higher group of 16 bits. */
HOW (R_PPC_GOT_TLSLD16_HI, 1, 16, 0xffff, 16, false, dont,
ppc_elf_unhandled_reloc),
/* Like GOT_TLSLD16_HI, but adjust for low 16 bits. */
HOW (R_PPC_GOT_TLSLD16_HA, 1, 16, 0xffff, 16, false, dont,
ppc_elf_unhandled_reloc),
/* Allocates an entry in the GOT with value (sym+add)@dtprel, and computes
the offset to the entry. */
HOW (R_PPC_GOT_DTPREL16, 1, 16, 0xffff, 0, false, signed,
ppc_elf_unhandled_reloc),
/* Like GOT_DTPREL16, but no overflow. */
HOW (R_PPC_GOT_DTPREL16_LO, 1, 16, 0xffff, 0, false, dont,
ppc_elf_unhandled_reloc),
/* Like GOT_DTPREL16_LO, but next higher group of 16 bits. */
HOW (R_PPC_GOT_DTPREL16_HI, 1, 16, 0xffff, 16, false, dont,
ppc_elf_unhandled_reloc),
/* Like GOT_DTPREL16_HI, but adjust for low 16 bits. */
HOW (R_PPC_GOT_DTPREL16_HA, 1, 16, 0xffff, 16, false, dont,
ppc_elf_unhandled_reloc),
/* Allocates an entry in the GOT with value (sym+add)@tprel, and computes the
offset to the entry. */
HOW (R_PPC_GOT_TPREL16, 1, 16, 0xffff, 0, false, signed,
ppc_elf_unhandled_reloc),
/* Like GOT_TPREL16, but no overflow. */
HOW (R_PPC_GOT_TPREL16_LO, 1, 16, 0xffff, 0, false, dont,
ppc_elf_unhandled_reloc),
/* Like GOT_TPREL16_LO, but next higher group of 16 bits. */
HOW (R_PPC_GOT_TPREL16_HI, 1, 16, 0xffff, 16, false, dont,
ppc_elf_unhandled_reloc),
/* Like GOT_TPREL16_HI, but adjust for low 16 bits. */
HOW (R_PPC_GOT_TPREL16_HA, 1, 16, 0xffff, 16, false, dont,
ppc_elf_unhandled_reloc),
/* The remaining relocs are from the Embedded ELF ABI, and are not
in the SVR4 ELF ABI. */
/* 32 bit value resulting from the addend minus the symbol. */
HOW (R_PPC_EMB_NADDR32, 2, 32, 0xffffffff, 0, false, dont,
ppc_elf_unhandled_reloc),
/* 16 bit value resulting from the addend minus the symbol. */
HOW (R_PPC_EMB_NADDR16, 1, 16, 0xffff, 0, false, signed,
ppc_elf_unhandled_reloc),
/* 16 bit value resulting from the addend minus the symbol. */
HOW (R_PPC_EMB_NADDR16_LO, 1, 16, 0xffff, 0, false, dont,
ppc_elf_unhandled_reloc),
/* The high order 16 bits of the addend minus the symbol. */
HOW (R_PPC_EMB_NADDR16_HI, 1, 16, 0xffff, 16, false, dont,
ppc_elf_unhandled_reloc),
/* The high order 16 bits of the result of the addend minus the address,
plus 1 if the contents of the low 16 bits, treated as a signed number,
is negative. */
HOW (R_PPC_EMB_NADDR16_HA, 1, 16, 0xffff, 16, false, dont,
ppc_elf_unhandled_reloc),
/* 16 bit value resulting from allocating a 4 byte word to hold an
address in the .sdata section, and returning the offset from
_SDA_BASE_ for that relocation. */
HOW (R_PPC_EMB_SDAI16, 1, 16, 0xffff, 0, false, signed,
ppc_elf_unhandled_reloc),
/* 16 bit value resulting from allocating a 4 byte word to hold an
address in the .sdata2 section, and returning the offset from
_SDA2_BASE_ for that relocation. */
HOW (R_PPC_EMB_SDA2I16, 1, 16, 0xffff, 0, false, signed,
ppc_elf_unhandled_reloc),
/* A sign-extended 16 bit value relative to _SDA2_BASE_, for use with
small data items. */
HOW (R_PPC_EMB_SDA2REL, 1, 16, 0xffff, 0, false, signed,
ppc_elf_unhandled_reloc),
/* Relocate against either _SDA_BASE_ or _SDA2_BASE_, filling in the 16 bit
signed offset from the appropriate base, and filling in the register
field with the appropriate register (0, 2, or 13). */
HOW (R_PPC_EMB_SDA21, 2, 16, 0xffff, 0, false, signed,
ppc_elf_unhandled_reloc),
/* Relocation not handled: R_PPC_EMB_MRKREF */
/* Relocation not handled: R_PPC_EMB_RELSEC16 */
/* Relocation not handled: R_PPC_EMB_RELST_LO */
/* Relocation not handled: R_PPC_EMB_RELST_HI */
/* Relocation not handled: R_PPC_EMB_RELST_HA */
/* Relocation not handled: R_PPC_EMB_BIT_FLD */
/* PC relative relocation against either _SDA_BASE_ or _SDA2_BASE_, filling
in the 16 bit signed offset from the appropriate base, and filling in the
register field with the appropriate register (0, 2, or 13). */
HOW (R_PPC_EMB_RELSDA, 1, 16, 0xffff, 0, false, signed,
ppc_elf_unhandled_reloc),
/* A relative 8 bit branch. */
HOW (R_PPC_VLE_REL8, 1, 8, 0xff, 1, true, signed,
bfd_elf_generic_reloc),
/* A relative 15 bit branch. */
HOW (R_PPC_VLE_REL15, 2, 16, 0xfffe, 0, true, signed,
bfd_elf_generic_reloc),
/* A relative 24 bit branch. */
HOW (R_PPC_VLE_REL24, 2, 25, 0x1fffffe, 0, true, signed,
bfd_elf_generic_reloc),
/* The 16 LSBS in split16a format. */
HOW (R_PPC_VLE_LO16A, 2, 16, 0x1f07ff, 0, false, dont,
ppc_elf_unhandled_reloc),
/* The 16 LSBS in split16d format. */
HOW (R_PPC_VLE_LO16D, 2, 16, 0x3e007ff, 0, false, dont,
ppc_elf_unhandled_reloc),
/* Bits 16-31 split16a format. */
HOW (R_PPC_VLE_HI16A, 2, 16, 0x1f07ff, 16, false, dont,
ppc_elf_unhandled_reloc),
/* Bits 16-31 split16d format. */
HOW (R_PPC_VLE_HI16D, 2, 16, 0x3e007ff, 16, false, dont,
ppc_elf_unhandled_reloc),
/* Bits 16-31 (High Adjusted) in split16a format. */
HOW (R_PPC_VLE_HA16A, 2, 16, 0x1f07ff, 16, false, dont,
ppc_elf_unhandled_reloc),
/* Bits 16-31 (High Adjusted) in split16d format. */
HOW (R_PPC_VLE_HA16D, 2, 16, 0x3e007ff, 16, false, dont,
ppc_elf_unhandled_reloc),
/* This reloc is like R_PPC_EMB_SDA21 but only applies to e_add16i
instructions. If the register base is 0 then the linker changes
the e_add16i to an e_li instruction. */
HOW (R_PPC_VLE_SDA21, 2, 16, 0xffff, 0, false, signed,
ppc_elf_unhandled_reloc),
/* Like R_PPC_VLE_SDA21 but ignore overflow. */
HOW (R_PPC_VLE_SDA21_LO, 2, 16, 0xffff, 0, false, dont,
ppc_elf_unhandled_reloc),
/* The 16 LSBS relative to _SDA_BASE_ in split16a format. */
HOW (R_PPC_VLE_SDAREL_LO16A, 2, 16, 0x1f07ff, 0, false, dont,
ppc_elf_unhandled_reloc),
/* The 16 LSBS relative to _SDA_BASE_ in split16d format. */
HOW (R_PPC_VLE_SDAREL_LO16D, 2, 16, 0x3e007ff, 0, false, dont,
ppc_elf_unhandled_reloc),
/* Bits 16-31 relative to _SDA_BASE_ in split16a format. */
HOW (R_PPC_VLE_SDAREL_HI16A, 2, 16, 0x1f07ff, 16, false, dont,
ppc_elf_unhandled_reloc),
/* Bits 16-31 relative to _SDA_BASE_ in split16d format. */
HOW (R_PPC_VLE_SDAREL_HI16D, 2, 16, 0x3e007ff, 16, false, dont,
ppc_elf_unhandled_reloc),
/* Bits 16-31 (HA) relative to _SDA_BASE split16a format. */
HOW (R_PPC_VLE_SDAREL_HA16A, 2, 16, 0x1f07ff, 16, false, dont,
ppc_elf_unhandled_reloc),
/* Bits 16-31 (HA) relative to _SDA_BASE split16d format. */
HOW (R_PPC_VLE_SDAREL_HA16D, 2, 16, 0x3e007ff, 16, false, dont,
ppc_elf_unhandled_reloc),
/* e_li split20 format. */
HOW (R_PPC_VLE_ADDR20, 2, 20, 0x1f7fff, 0, false, dont,
ppc_elf_unhandled_reloc),
HOW (R_PPC_IRELATIVE, 2, 32, 0xffffffff, 0, false, dont,
ppc_elf_unhandled_reloc),
/* A 16 bit relative relocation. */
HOW (R_PPC_REL16, 1, 16, 0xffff, 0, true, signed,
bfd_elf_generic_reloc),
/* A 16 bit relative relocation without overflow. */
HOW (R_PPC_REL16_LO, 1, 16, 0xffff, 0, true, dont,
bfd_elf_generic_reloc),
/* The high order 16 bits of a relative address. */
HOW (R_PPC_REL16_HI, 1, 16, 0xffff, 16, true, dont,
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_PPC_REL16_HA, 1, 16, 0xffff, 16, true, dont,
ppc_elf_addr16_ha_reloc),
/* Like R_PPC_REL16_HA but for split field in addpcis. */
HOW (R_PPC_REL16DX_HA, 2, 16, 0x1fffc1, 16, true, signed,
ppc_elf_addr16_ha_reloc),
/* A split-field reloc for addpcis, non-relative (gas internal use only). */
HOW (R_PPC_16DX_HA, 2, 16, 0x1fffc1, 16, false, signed,
ppc_elf_addr16_ha_reloc),
/* GNU extension to record C++ vtable hierarchy. */
HOW (R_PPC_GNU_VTINHERIT, 0, 0, 0, 0, false, dont,
NULL),
/* GNU extension to record C++ vtable member usage. */
HOW (R_PPC_GNU_VTENTRY, 0, 0, 0, 0, false, dont,
NULL),
/* Phony reloc to handle AIX style TOC entries. */
HOW (R_PPC_TOC16, 1, 16, 0xffff, 0, false, signed,
ppc_elf_unhandled_reloc),
};
/* Initialize the ppc_elf_howto_table, so that linear accesses can be done. */
static void
ppc_elf_howto_init (void)
{
unsigned int i, type;
for (i = 0;
i < sizeof (ppc_elf_howto_raw) / sizeof (ppc_elf_howto_raw[0]);
i++)
{
type = ppc_elf_howto_raw[i].type;
if (type >= (sizeof (ppc_elf_howto_table)
/ sizeof (ppc_elf_howto_table[0])))
abort ();
ppc_elf_howto_table[type] = &ppc_elf_howto_raw[i];
}
}
static reloc_howto_type *
ppc_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
bfd_reloc_code_real_type code)
{
enum elf_ppc_reloc_type r;
/* Initialize howto table if not already done. */
if (!ppc_elf_howto_table[R_PPC_ADDR32])
ppc_elf_howto_init ();
switch (code)
{
default:
return NULL;
case BFD_RELOC_NONE: r = R_PPC_NONE; break;
case BFD_RELOC_32: r = R_PPC_ADDR32; break;
case BFD_RELOC_PPC_BA26: r = R_PPC_ADDR24; break;
case BFD_RELOC_PPC64_ADDR16_DS:
case BFD_RELOC_16: r = R_PPC_ADDR16; break;
case BFD_RELOC_PPC64_ADDR16_LO_DS:
case BFD_RELOC_LO16: r = R_PPC_ADDR16_LO; break;
case BFD_RELOC_HI16: r = R_PPC_ADDR16_HI; break;
case BFD_RELOC_HI16_S: r = R_PPC_ADDR16_HA; break;
case BFD_RELOC_PPC_BA16: r = R_PPC_ADDR14; break;
case BFD_RELOC_PPC_BA16_BRTAKEN: r = R_PPC_ADDR14_BRTAKEN; break;
case BFD_RELOC_PPC_BA16_BRNTAKEN: r = R_PPC_ADDR14_BRNTAKEN; break;
case BFD_RELOC_PPC_B26: r = R_PPC_REL24; break;
case BFD_RELOC_PPC_B16: r = R_PPC_REL14; break;
case BFD_RELOC_PPC_B16_BRTAKEN: r = R_PPC_REL14_BRTAKEN; break;
case BFD_RELOC_PPC_B16_BRNTAKEN: r = R_PPC_REL14_BRNTAKEN; break;
case BFD_RELOC_PPC64_GOT16_DS:
case BFD_RELOC_16_GOTOFF: r = R_PPC_GOT16; break;
case BFD_RELOC_PPC64_GOT16_LO_DS:
case BFD_RELOC_LO16_GOTOFF: r = R_PPC_GOT16_LO; break;
case BFD_RELOC_HI16_GOTOFF: r = R_PPC_GOT16_HI; break;
case BFD_RELOC_HI16_S_GOTOFF: r = R_PPC_GOT16_HA; break;
case BFD_RELOC_24_PLT_PCREL: r = R_PPC_PLTREL24; break;
case BFD_RELOC_PPC_COPY: r = R_PPC_COPY; break;
case BFD_RELOC_PPC_GLOB_DAT: r = R_PPC_GLOB_DAT; break;
case BFD_RELOC_PPC_LOCAL24PC: r = R_PPC_LOCAL24PC; break;
case BFD_RELOC_32_PCREL: r = R_PPC_REL32; break;
case BFD_RELOC_32_PLTOFF: r = R_PPC_PLT32; break;
case BFD_RELOC_32_PLT_PCREL: r = R_PPC_PLTREL32; break;
case BFD_RELOC_PPC64_PLT16_LO_DS:
case BFD_RELOC_LO16_PLTOFF: r = R_PPC_PLT16_LO; break;
case BFD_RELOC_HI16_PLTOFF: r = R_PPC_PLT16_HI; break;
case BFD_RELOC_HI16_S_PLTOFF: r = R_PPC_PLT16_HA; break;
case BFD_RELOC_GPREL16: r = R_PPC_SDAREL16; break;
case BFD_RELOC_PPC64_SECTOFF_DS:
case BFD_RELOC_16_BASEREL: r = R_PPC_SECTOFF; break;
case BFD_RELOC_PPC64_SECTOFF_LO_DS:
case BFD_RELOC_LO16_BASEREL: r = R_PPC_SECTOFF_LO; break;
case BFD_RELOC_HI16_BASEREL: r = R_PPC_SECTOFF_HI; break;
case BFD_RELOC_HI16_S_BASEREL: r = R_PPC_SECTOFF_HA; break;
case BFD_RELOC_CTOR: r = R_PPC_ADDR32; break;
case BFD_RELOC_PPC64_TOC16_DS:
case BFD_RELOC_PPC_TOC16: r = R_PPC_TOC16; break;
case BFD_RELOC_PPC_TLS: r = R_PPC_TLS; break;
case BFD_RELOC_PPC_TLSGD: r = R_PPC_TLSGD; break;
case BFD_RELOC_PPC_TLSLD: r = R_PPC_TLSLD; break;
case BFD_RELOC_PPC_DTPMOD: r = R_PPC_DTPMOD32; break;
case BFD_RELOC_PPC64_TPREL16_DS:
case BFD_RELOC_PPC_TPREL16: r = R_PPC_TPREL16; break;
case BFD_RELOC_PPC64_TPREL16_LO_DS:
case BFD_RELOC_PPC_TPREL16_LO: r = R_PPC_TPREL16_LO; break;
case BFD_RELOC_PPC_TPREL16_HI: r = R_PPC_TPREL16_HI; break;
case BFD_RELOC_PPC_TPREL16_HA: r = R_PPC_TPREL16_HA; break;
case BFD_RELOC_PPC_TPREL: r = R_PPC_TPREL32; break;
case BFD_RELOC_PPC64_DTPREL16_DS:
case BFD_RELOC_PPC_DTPREL16: r = R_PPC_DTPREL16; break;
case BFD_RELOC_PPC64_DTPREL16_LO_DS:
case BFD_RELOC_PPC_DTPREL16_LO: r = R_PPC_DTPREL16_LO; break;
case BFD_RELOC_PPC_DTPREL16_HI: r = R_PPC_DTPREL16_HI; break;
case BFD_RELOC_PPC_DTPREL16_HA: r = R_PPC_DTPREL16_HA; break;
case BFD_RELOC_PPC_DTPREL: r = R_PPC_DTPREL32; break;
case BFD_RELOC_PPC_GOT_TLSGD16: r = R_PPC_GOT_TLSGD16; break;
case BFD_RELOC_PPC_GOT_TLSGD16_LO: r = R_PPC_GOT_TLSGD16_LO; break;
case BFD_RELOC_PPC_GOT_TLSGD16_HI: r = R_PPC_GOT_TLSGD16_HI; break;
case BFD_RELOC_PPC_GOT_TLSGD16_HA: r = R_PPC_GOT_TLSGD16_HA; break;
case BFD_RELOC_PPC_GOT_TLSLD16: r = R_PPC_GOT_TLSLD16; break;
case BFD_RELOC_PPC_GOT_TLSLD16_LO: r = R_PPC_GOT_TLSLD16_LO; break;
case BFD_RELOC_PPC_GOT_TLSLD16_HI: r = R_PPC_GOT_TLSLD16_HI; break;
case BFD_RELOC_PPC_GOT_TLSLD16_HA: r = R_PPC_GOT_TLSLD16_HA; break;
case BFD_RELOC_PPC_GOT_TPREL16: r = R_PPC_GOT_TPREL16; break;
case BFD_RELOC_PPC_GOT_TPREL16_LO: r = R_PPC_GOT_TPREL16_LO; break;
case BFD_RELOC_PPC_GOT_TPREL16_HI: r = R_PPC_GOT_TPREL16_HI; break;
case BFD_RELOC_PPC_GOT_TPREL16_HA: r = R_PPC_GOT_TPREL16_HA; break;
case BFD_RELOC_PPC_GOT_DTPREL16: r = R_PPC_GOT_DTPREL16; break;
case BFD_RELOC_PPC_GOT_DTPREL16_LO: r = R_PPC_GOT_DTPREL16_LO; break;
case BFD_RELOC_PPC_GOT_DTPREL16_HI: r = R_PPC_GOT_DTPREL16_HI; break;
case BFD_RELOC_PPC_GOT_DTPREL16_HA: r = R_PPC_GOT_DTPREL16_HA; break;
case BFD_RELOC_PPC_EMB_NADDR32: r = R_PPC_EMB_NADDR32; break;
case BFD_RELOC_PPC_EMB_NADDR16: r = R_PPC_EMB_NADDR16; break;
case BFD_RELOC_PPC_EMB_NADDR16_LO: r = R_PPC_EMB_NADDR16_LO; break;
case BFD_RELOC_PPC_EMB_NADDR16_HI: r = R_PPC_EMB_NADDR16_HI; break;
case BFD_RELOC_PPC_EMB_NADDR16_HA: r = R_PPC_EMB_NADDR16_HA; break;
case BFD_RELOC_PPC_EMB_SDAI16: r = R_PPC_EMB_SDAI16; break;
case BFD_RELOC_PPC_EMB_SDA2I16: r = R_PPC_EMB_SDA2I16; break;
case BFD_RELOC_PPC_EMB_SDA2REL: r = R_PPC_EMB_SDA2REL; break;
case BFD_RELOC_PPC_EMB_SDA21: r = R_PPC_EMB_SDA21; break;
case BFD_RELOC_PPC_EMB_MRKREF: r = R_PPC_EMB_MRKREF; break;
case BFD_RELOC_PPC_EMB_RELSEC16: r = R_PPC_EMB_RELSEC16; break;
case BFD_RELOC_PPC_EMB_RELST_LO: r = R_PPC_EMB_RELST_LO; break;
case BFD_RELOC_PPC_EMB_RELST_HI: r = R_PPC_EMB_RELST_HI; break;
case BFD_RELOC_PPC_EMB_RELST_HA: r = R_PPC_EMB_RELST_HA; break;
case BFD_RELOC_PPC_EMB_BIT_FLD: r = R_PPC_EMB_BIT_FLD; break;
case BFD_RELOC_PPC_EMB_RELSDA: r = R_PPC_EMB_RELSDA; break;
case BFD_RELOC_PPC_VLE_REL8: r = R_PPC_VLE_REL8; break;
case BFD_RELOC_PPC_VLE_REL15: r = R_PPC_VLE_REL15; break;
case BFD_RELOC_PPC_VLE_REL24: r = R_PPC_VLE_REL24; break;
case BFD_RELOC_PPC_VLE_LO16A: r = R_PPC_VLE_LO16A; break;
case BFD_RELOC_PPC_VLE_LO16D: r = R_PPC_VLE_LO16D; break;
case BFD_RELOC_PPC_VLE_HI16A: r = R_PPC_VLE_HI16A; break;
case BFD_RELOC_PPC_VLE_HI16D: r = R_PPC_VLE_HI16D; break;
case BFD_RELOC_PPC_VLE_HA16A: r = R_PPC_VLE_HA16A; break;
case BFD_RELOC_PPC_VLE_HA16D: r = R_PPC_VLE_HA16D; break;
case BFD_RELOC_PPC_VLE_SDA21: r = R_PPC_VLE_SDA21; break;
case BFD_RELOC_PPC_VLE_SDA21_LO: r = R_PPC_VLE_SDA21_LO; break;
case BFD_RELOC_PPC_VLE_SDAREL_LO16A:
r = R_PPC_VLE_SDAREL_LO16A;
break;
case BFD_RELOC_PPC_VLE_SDAREL_LO16D:
r = R_PPC_VLE_SDAREL_LO16D;
break;
case BFD_RELOC_PPC_VLE_SDAREL_HI16A:
r = R_PPC_VLE_SDAREL_HI16A;
break;
case BFD_RELOC_PPC_VLE_SDAREL_HI16D:
r = R_PPC_VLE_SDAREL_HI16D;
break;
case BFD_RELOC_PPC_VLE_SDAREL_HA16A:
r = R_PPC_VLE_SDAREL_HA16A;
break;
case BFD_RELOC_PPC_VLE_SDAREL_HA16D:
r = R_PPC_VLE_SDAREL_HA16D;
break;
case BFD_RELOC_16_PCREL: r = R_PPC_REL16; break;
case BFD_RELOC_LO16_PCREL: r = R_PPC_REL16_LO; break;
case BFD_RELOC_HI16_PCREL: r = R_PPC_REL16_HI; break;
case BFD_RELOC_HI16_S_PCREL: r = R_PPC_REL16_HA; break;
case BFD_RELOC_PPC_16DX_HA: r = R_PPC_16DX_HA; break;
case BFD_RELOC_PPC_REL16DX_HA: r = R_PPC_REL16DX_HA; break;
case BFD_RELOC_VTABLE_INHERIT: r = R_PPC_GNU_VTINHERIT; break;
case BFD_RELOC_VTABLE_ENTRY: r = R_PPC_GNU_VTENTRY; break;
}
return ppc_elf_howto_table[r];
};
static reloc_howto_type *
ppc_elf_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
const char *r_name)
{
unsigned int i;
for (i = 0;
i < sizeof (ppc_elf_howto_raw) / sizeof (ppc_elf_howto_raw[0]);
i++)
if (ppc_elf_howto_raw[i].name != NULL
&& strcasecmp (ppc_elf_howto_raw[i].name, r_name) == 0)
return &ppc_elf_howto_raw[i];
return NULL;
}
/* Set the howto pointer for a PowerPC ELF reloc. */
static bool
ppc_elf_info_to_howto (bfd *abfd,
arelent *cache_ptr,
Elf_Internal_Rela *dst)
{
unsigned int r_type;
/* Initialize howto table if not already done. */
if (!ppc_elf_howto_table[R_PPC_ADDR32])
ppc_elf_howto_init ();
r_type = ELF32_R_TYPE (dst->r_info);
if (r_type >= R_PPC_max)
{
/* xgettext:c-format */
_bfd_error_handler (_("%pB: unsupported relocation type %#x"),
abfd, r_type);
bfd_set_error (bfd_error_bad_value);
return false;
}
cache_ptr->howto = ppc_elf_howto_table[r_type];
/* Just because the above assert didn't trigger doesn't mean that
ELF32_R_TYPE (dst->r_info) is necessarily a valid relocation. */
if (cache_ptr->howto == NULL)
{
/* xgettext:c-format */
_bfd_error_handler (_("%pB: unsupported relocation type %#x"),
abfd, r_type);
bfd_set_error (bfd_error_bad_value);
return false;
}
return true;
}
/* Handle the R_PPC_ADDR16_HA and R_PPC_REL16_HA relocs. */
static bfd_reloc_status_type
ppc_elf_addr16_ha_reloc (bfd *abfd,
arelent *reloc_entry,
asymbol *symbol,
void *data,
asection *input_section,
bfd *output_bfd,
char **error_message ATTRIBUTE_UNUSED)
{
enum elf_ppc_reloc_type r_type;
long insn;
bfd_size_type octets;
bfd_vma value;
if (output_bfd != NULL)
{
reloc_entry->address += input_section->output_offset;
return bfd_reloc_ok;
}
reloc_entry->addend += 0x8000;
r_type = reloc_entry->howto->type;
if (r_type != R_PPC_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 >>= 16;
octets = reloc_entry->address * OCTETS_PER_BYTE (abfd, input_section);
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);
return bfd_reloc_ok;
}
static bfd_reloc_status_type
ppc_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;
}
/* Sections created by the linker. */
typedef struct elf_linker_section
{
/* Pointer to the bfd section. */
asection *section;
/* Section name. */
const char *name;
/* Associated bss section name. */
const char *bss_name;
/* Associated symbol name. */
const char *sym_name;
/* Associated symbol. */
struct elf_link_hash_entry *sym;
} elf_linker_section_t;
/* Linked list of allocated pointer entries. This hangs off of the
symbol lists, and provides allows us to return different pointers,
based on different addend's. */
typedef struct elf_linker_section_pointers
{
/* next allocated pointer for this symbol */
struct elf_linker_section_pointers *next;
/* offset of pointer from beginning of section */
bfd_vma offset;
/* addend used */
bfd_vma addend;
/* which linker section this is */
elf_linker_section_t *lsect;
} elf_linker_section_pointers_t;
struct ppc_elf_obj_tdata
{
struct elf_obj_tdata elf;
/* A mapping from local symbols to offsets into the various linker
sections added. This is index by the symbol index. */
elf_linker_section_pointers_t **linker_section_pointers;
/* Flags used to auto-detect plt type. */
unsigned int makes_plt_call : 1;
unsigned int has_rel16 : 1;
};
#define ppc_elf_tdata(bfd) \
((struct ppc_elf_obj_tdata *) (bfd)->tdata.any)
#define elf_local_ptr_offsets(bfd) \
(ppc_elf_tdata (bfd)->linker_section_pointers)
#define is_ppc_elf(bfd) \
(bfd_get_flavour (bfd) == bfd_target_elf_flavour \
&& elf_object_id (bfd) == PPC32_ELF_DATA)
/* Override the generic function because we store some extras. */
static bool
ppc_elf_mkobject (bfd *abfd)
{
return bfd_elf_allocate_object (abfd, sizeof (struct ppc_elf_obj_tdata),
PPC32_ELF_DATA);
}
/* When defaulting arch/mach, decode apuinfo to find a better match. */
bool
_bfd_elf_ppc_set_arch (bfd *abfd)
{
unsigned long mach = 0;
asection *s;
unsigned char *contents;
if (abfd->arch_info->bits_per_word == 32
&& bfd_big_endian (abfd))
{
for (s = abfd->sections; s != NULL; s = s->next)
if ((elf_section_data (s)->this_hdr.sh_flags & SHF_PPC_VLE) != 0)
break;
if (s != NULL)
mach = bfd_mach_ppc_vle;
}
if (mach == 0)
{
s = bfd_get_section_by_name (abfd, APUINFO_SECTION_NAME);
if (s != NULL
&& s->size >= 24
&& bfd_malloc_and_get_section (abfd, s, &contents))
{
unsigned int apuinfo_size = bfd_get_32 (abfd, contents + 4);
unsigned int i;
for (i = 20; i < apuinfo_size + 20 && i + 4 <= s->size; i += 4)
{
unsigned int val = bfd_get_32 (abfd, contents + i);
switch (val >> 16)
{
case PPC_APUINFO_PMR:
case PPC_APUINFO_RFMCI:
if (mach == 0)
mach = bfd_mach_ppc_titan;
break;
case PPC_APUINFO_ISEL:
case PPC_APUINFO_CACHELCK:
if (mach == bfd_mach_ppc_titan)
mach = bfd_mach_ppc_e500mc;
break;
case PPC_APUINFO_SPE:
case PPC_APUINFO_EFS:
case PPC_APUINFO_BRLOCK:
if (mach != bfd_mach_ppc_vle)
mach = bfd_mach_ppc_e500;
break;
case PPC_APUINFO_VLE:
mach = bfd_mach_ppc_vle;
break;
default:
mach = -1ul;
}
}
free (contents);
}
}
if (mach != 0 && mach != -1ul)
{
const bfd_arch_info_type *arch;
for (arch = abfd->arch_info->next; arch; arch = arch->next)
if (arch->mach == mach)
{
abfd->arch_info = arch;
break;
}
}
return true;
}
/* Fix bad default arch selected for a 32 bit input bfd when the
default is 64 bit. Also select arch based on apuinfo. */
static bool
ppc_elf_object_p (bfd *abfd)
{
if (!abfd->arch_info->the_default)
return true;
if (abfd->arch_info->bits_per_word == 64)
{
Elf_Internal_Ehdr *i_ehdr = elf_elfheader (abfd);
if (i_ehdr->e_ident[EI_CLASS] == ELFCLASS32)
{
/* Relies on arch after 64 bit default being 32 bit default. */
abfd->arch_info = abfd->arch_info->next;
BFD_ASSERT (abfd->arch_info->bits_per_word == 32);
}
}
return _bfd_elf_ppc_set_arch (abfd);
}
/* Function to set whether a module needs the -mrelocatable bit set. */
static bool
ppc_elf_set_private_flags (bfd *abfd, flagword flags)
{
BFD_ASSERT (!elf_flags_init (abfd)
|| elf_elfheader (abfd)->e_flags == flags);
elf_elfheader (abfd)->e_flags = flags;
elf_flags_init (abfd) = true;
return true;
}
/* Support for core dump NOTE sections. */
static bool
ppc_elf_grok_prstatus (bfd *abfd, Elf_Internal_Note *note)
{
int offset;
unsigned int size;
switch (note->descsz)
{
default:
return false;
case 268: /* Linux/PPC. */
/* 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 + 24);
/* pr_reg */
offset = 72;
size = 192;
break;
}
/* Make a ".reg/999" section. */
return _bfd_elfcore_make_pseudosection (abfd, ".reg",
size, note->descpos + offset);
}
static bool
ppc_elf_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
{
switch (note->descsz)
{
default:
return false;
case 128: /* Linux/PPC elf_prpsinfo. */
elf_tdata (abfd)->core->pid
= bfd_get_32 (abfd, note->descdata + 16);
elf_tdata (abfd)->core->program
= _bfd_elfcore_strndup (abfd, note->descdata + 32, 16);
elf_tdata (abfd)->core->command
= _bfd_elfcore_strndup (abfd, note->descdata + 48, 80);
}
/* Note that for some reason, a spurious space is tacked
onto the end of the args in some (at least one anyway)
implementations, so strip it off if it exists. */
{
char *command = elf_tdata (abfd)->core->command;
int n = strlen (command);
if (0 < n && command[n - 1] == ' ')
command[n - 1] = '\0';
}
return true;
}
static char *
ppc_elf_write_core_note (bfd *abfd, char *buf, int *bufsiz, int note_type, ...)
{
switch (note_type)
{
default:
return NULL;
case NT_PRPSINFO:
{
char data[128] ATTRIBUTE_NONSTRING;
va_list ap;
va_start (ap, note_type);
memset (data, 0, sizeof (data));
strncpy (data + 32, 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 + 48, 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[268];
va_list ap;
long pid;
int cursig;
const void *greg;
va_start (ap, note_type);
memset (data, 0, 72);
pid = va_arg (ap, long);
bfd_put_32 (abfd, pid, data + 24);
cursig = va_arg (ap, int);
bfd_put_16 (abfd, cursig, data + 12);
greg = va_arg (ap, const void *);
memcpy (data + 72, greg, 192);
memset (data + 264, 0, 4);
va_end (ap);
return elfcore_write_note (abfd, buf, bufsiz,
"CORE", note_type, data, sizeof (data));
}
}
}
static flagword
ppc_elf_lookup_section_flags (char *flag_name)
{
if (!strcmp (flag_name, "SHF_PPC_VLE"))
return SHF_PPC_VLE;
return 0;
}
/* Return address for Ith PLT stub in section PLT, for relocation REL
or (bfd_vma) -1 if it should not be included. */
static bfd_vma
ppc_elf_plt_sym_val (bfd_vma i ATTRIBUTE_UNUSED,
const asection *plt ATTRIBUTE_UNUSED,
const arelent *rel)
{
return rel->address;
}
/* Handle a PowerPC specific section when reading an object file. This
is called when bfd_section_from_shdr finds a section with an unknown
type. */
static bool
ppc_elf_section_from_shdr (bfd *abfd,
Elf_Internal_Shdr *hdr,
const char *name,
int shindex)
{
asection *newsect;
flagword flags;
if (! _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex))
return false;
newsect = hdr->bfd_section;
flags = 0;
if (hdr->sh_flags & SHF_EXCLUDE)
flags |= SEC_EXCLUDE;
if (hdr->sh_type == SHT_ORDERED)
flags |= SEC_SORT_ENTRIES;
if (startswith (name, ".PPC.EMB"))
name += 8;
if (startswith (name, ".sbss")
|| startswith (name, ".sdata"))
flags |= SEC_SMALL_DATA;
return (flags == 0
|| bfd_set_section_flags (newsect, newsect->flags | flags));
}
/* Set up any other section flags and such that may be necessary. */
static bool
ppc_elf_fake_sections (bfd *abfd ATTRIBUTE_UNUSED,
Elf_Internal_Shdr *shdr,
asection *asect)
{
if ((asect->flags & SEC_SORT_ENTRIES) != 0)
shdr->sh_type = SHT_ORDERED;
return true;
}
/* If we have .sbss2 or .PPC.EMB.sbss0 output sections, we
need to bump up the number of section headers. */
static int
ppc_elf_additional_program_headers (bfd *abfd,
struct bfd_link_info *info ATTRIBUTE_UNUSED)
{
asection *s;
int ret = 0;
s = bfd_get_section_by_name (abfd, ".sbss2");
if (s != NULL && (s->flags & SEC_ALLOC) != 0)
++ret;
s = bfd_get_section_by_name (abfd, ".PPC.EMB.sbss0");
if (s != NULL && (s->flags & SEC_ALLOC) != 0)
++ret;
return ret;
}
/* Modify the segment map for VLE executables. */
bool
ppc_elf_modify_segment_map (bfd *abfd,
struct bfd_link_info *info ATTRIBUTE_UNUSED)
{
struct elf_segment_map *m;
/* At this point in the link, output sections have already been sorted by
LMA and assigned to segments. All that is left to do is to ensure
there is no mixing of VLE & non-VLE sections in a text segment.
If we find that case, we split the segment.
We maintain the original output section order. */
for (m = elf_seg_map (abfd); m != NULL; m = m->next)
{
struct elf_segment_map *n;
size_t amt;
unsigned int j, k;
unsigned int p_flags;
if (m->p_type != PT_LOAD || m->count == 0)
continue;
for (p_flags = PF_R, j = 0; j != m->count; ++j)
{
if ((m->sections[j]->flags & SEC_READONLY) == 0)
p_flags |= PF_W;
if ((m->sections[j]->flags & SEC_CODE) != 0)
{
p_flags |= PF_X;
if ((elf_section_flags (m->sections[j]) & SHF_PPC_VLE) != 0)
p_flags |= PF_PPC_VLE;
break;
}
}
if (j != m->count)
while (++j != m->count)
{
unsigned int p_flags1 = PF_R;
if ((m->sections[j]->flags & SEC_READONLY) == 0)
p_flags1 |= PF_W;
if ((m->sections[j]->flags & SEC_CODE) != 0)
{
p_flags1 |= PF_X;
if ((elf_section_flags (m->sections[j]) & SHF_PPC_VLE) != 0)
p_flags1 |= PF_PPC_VLE;
if (((p_flags1 ^ p_flags) & PF_PPC_VLE) != 0)
break;
}
p_flags |= p_flags1;
}
/* If we're splitting a segment which originally contained rw
sections then those sections might now only be in one of the
two parts. So always set p_flags if splitting, even if we
are being called for objcopy with p_flags_valid set. */
if (j != m->count || !m->p_flags_valid)
{
m->p_flags_valid = 1;
m->p_flags = p_flags;
}
if (j == m->count)
continue;
/* Sections 0..j-1 stay in this (current) segment,
the remainder are put in a new segment.
The scan resumes with the new segment. */
amt = sizeof (struct elf_segment_map);
amt += (m->count - j - 1) * sizeof (asection *);
n = (struct elf_segment_map *) bfd_zalloc (abfd, amt);
if (n == NULL)
return false;
n->p_type = PT_LOAD;
n->count = m->count - j;
for (k = 0; k < n->count; ++k)
n->sections[k] = m->sections[j + k];
m->count = j;
m->p_size_valid = 0;
n->next = m->next;
m->next = n;
}
return true;
}
/* Add extra PPC sections -- Note, for now, make .sbss2 and
.PPC.EMB.sbss0 a normal section, and not a bss section so
that the linker doesn't crater when trying to make more than
2 sections. */
static const struct bfd_elf_special_section ppc_elf_special_sections[] =
{
{ STRING_COMMA_LEN (".plt"), 0, SHT_NOBITS, SHF_ALLOC + SHF_EXECINSTR },
{ STRING_COMMA_LEN (".sbss"), -2, SHT_NOBITS, SHF_ALLOC + SHF_WRITE },
{ STRING_COMMA_LEN (".sbss2"), -2, SHT_PROGBITS, SHF_ALLOC },
{ STRING_COMMA_LEN (".sdata"), -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
{ STRING_COMMA_LEN (".sdata2"), -2, SHT_PROGBITS, SHF_ALLOC },
{ STRING_COMMA_LEN (".tags"), 0, SHT_ORDERED, SHF_ALLOC },
{ STRING_COMMA_LEN (APUINFO_SECTION_NAME), 0, SHT_NOTE, 0 },
{ STRING_COMMA_LEN (".PPC.EMB.sbss0"), 0, SHT_PROGBITS, SHF_ALLOC },
{ STRING_COMMA_LEN (".PPC.EMB.sdata0"), 0, SHT_PROGBITS, SHF_ALLOC },
{ NULL, 0, 0, 0, 0 }
};
/* This is what we want for new plt/got. */
static const struct bfd_elf_special_section ppc_alt_plt =
{ STRING_COMMA_LEN (".plt"), 0, SHT_PROGBITS, SHF_ALLOC };
static const struct bfd_elf_special_section *
ppc_elf_get_sec_type_attr (bfd *abfd, asection *sec)
{
const struct bfd_elf_special_section *ssect;
/* See if this is one of the special sections. */
if (sec->name == NULL)
return NULL;
ssect = _bfd_elf_get_special_section (sec->name, ppc_elf_special_sections,
sec->use_rela_p);
if (ssect != NULL)
{
if (ssect == ppc_elf_special_sections && (sec->flags & SEC_LOAD) != 0)
ssect = &ppc_alt_plt;
return ssect;
}
return _bfd_elf_get_sec_type_attr (abfd, sec);
}
/* Very simple linked list structure for recording apuinfo values. */
typedef struct apuinfo_list
{
struct apuinfo_list *next;
unsigned long value;
}
apuinfo_list;
static apuinfo_list *head;
static bool apuinfo_set;
static void
apuinfo_list_init (void)
{
head = NULL;
apuinfo_set = false;
}
static void
apuinfo_list_add (unsigned long value)
{
apuinfo_list *entry = head;
while (entry != NULL)
{
if (entry->value == value)
return;
entry = entry->next;
}
entry = bfd_malloc (sizeof (* entry));
if (entry == NULL)
return;
entry->value = value;
entry->next = head;
head = entry;
}
static unsigned
apuinfo_list_length (void)
{
apuinfo_list *entry;
unsigned long count;
for (entry = head, count = 0;
entry;
entry = entry->next)
++ count;
return count;
}
static inline unsigned long
apuinfo_list_element (unsigned long number)
{
apuinfo_list * entry;
for (entry = head;
entry && number --;
entry = entry->next)
;
return entry ? entry->value : 0;
}
static void
apuinfo_list_finish (void)
{
apuinfo_list *entry;
for (entry = head; entry;)
{
apuinfo_list *next = entry->next;
free (entry);
entry = next;
}
head = NULL;
}
/* Scan the input BFDs and create a linked list of
the APUinfo values that will need to be emitted. */
static void
ppc_elf_begin_write_processing (bfd *abfd, struct bfd_link_info *link_info)
{
bfd *ibfd;
asection *asec;
char *buffer = NULL;
bfd_size_type largest_input_size = 0;
unsigned i;
unsigned long length;
const char *error_message = NULL;
if (link_info == NULL)
return;
apuinfo_list_init ();
/* Read in the input sections contents. */
for (ibfd = link_info->input_bfds; ibfd; ibfd = ibfd->link.next)
{
unsigned long datum;
asec = bfd_get_section_by_name (ibfd, APUINFO_SECTION_NAME);
if (asec == NULL)
continue;
/* xgettext:c-format */
error_message = _("corrupt %s section in %pB");
length = asec->size;
if (length < 20)
goto fail;
apuinfo_set = true;
if (largest_input_size < asec->size)
{
free (buffer);
largest_input_size = asec->size;
buffer = bfd_malloc (largest_input_size);
if (!buffer)
return;
}
if (bfd_seek (ibfd, asec->filepos, SEEK_SET) != 0
|| (bfd_bread (buffer, length, ibfd) != length))
{
/* xgettext:c-format */
error_message = _("unable to read in %s section from %pB");
goto fail;
}
/* Verify the contents of the header. Note - we have to
extract the values this way in order to allow for a
host whose endian-ness is different from the target. */
datum = bfd_get_32 (ibfd, buffer);
if (datum != sizeof APUINFO_LABEL)
goto fail;
datum = bfd_get_32 (ibfd, buffer + 8);
if (datum != 0x2)
goto fail;
if (strcmp (buffer + 12, APUINFO_LABEL) != 0)
goto fail;
/* Get the number of bytes used for apuinfo entries. */
datum = bfd_get_32 (ibfd, buffer + 4);
if (datum + 20 != length)
goto fail;
/* Scan the apuinfo section, building a list of apuinfo numbers. */
for (i = 0; i < datum; i += 4)
apuinfo_list_add (bfd_get_32 (ibfd, buffer + 20 + i));
}
error_message = NULL;
if (apuinfo_set)
{
/* Compute the size of the output section. */
unsigned num_entries = apuinfo_list_length ();
/* Set the output section size, if it exists. */
asec = bfd_get_section_by_name (abfd, APUINFO_SECTION_NAME);
if (asec && !bfd_set_section_size (asec, 20 + num_entries * 4))
{
ibfd = abfd;
/* xgettext:c-format */
error_message = _("warning: unable to set size of %s section in %pB");
}
}
fail:
free (buffer);
if (error_message)
_bfd_error_handler (error_message, APUINFO_SECTION_NAME, ibfd);
}
/* Prevent the output section from accumulating the input sections'
contents. We have already stored this in our linked list structure. */
static bool
ppc_elf_write_section (bfd *abfd ATTRIBUTE_UNUSED,
struct bfd_link_info *link_info ATTRIBUTE_UNUSED,
asection *asec,
bfd_byte *contents ATTRIBUTE_UNUSED)
{
return apuinfo_set && strcmp (asec->name, APUINFO_SECTION_NAME) == 0;
}
/* Finally we can generate the output section. */
static void
ppc_final_write_processing (bfd *abfd)
{
bfd_byte *buffer;
asection *asec;
unsigned i;
unsigned num_entries;
bfd_size_type length;
asec = bfd_get_section_by_name (abfd, APUINFO_SECTION_NAME);
if (asec == NULL)
return;
if (!apuinfo_set)
return;
length = asec->size;
if (length < 20)
return;
buffer = bfd_malloc (length);
if (buffer == NULL)
{
_bfd_error_handler
(_("failed to allocate space for new APUinfo section"));
return;
}
/* Create the apuinfo header. */
num_entries = apuinfo_list_length ();
bfd_put_32 (abfd, sizeof APUINFO_LABEL, buffer);
bfd_put_32 (abfd, num_entries * 4, buffer + 4);
bfd_put_32 (abfd, 0x2, buffer + 8);
strcpy ((char *) buffer + 12, APUINFO_LABEL);
length = 20;
for (i = 0; i < num_entries; i++)
{
bfd_put_32 (abfd, apuinfo_list_element (i), buffer + length);
length += 4;
}
if (length != asec->size)
_bfd_error_handler (_("failed to compute new APUinfo section"));
if (! bfd_set_section_contents (abfd, asec, buffer, (file_ptr) 0, length))
_bfd_error_handler (_("failed to install new APUinfo section"));
free (buffer);
apuinfo_list_finish ();
}
static bool
ppc_elf_final_write_processing (bfd *abfd)
{
ppc_final_write_processing (abfd);
return _bfd_elf_final_write_processing (abfd);
}
static bool
is_nonpic_glink_stub (bfd *abfd, asection *glink, bfd_vma off)
{
bfd_byte buf[4 * 4];
if (!bfd_get_section_contents (abfd, glink, buf, off, sizeof buf))
return false;
return ((bfd_get_32 (abfd, buf + 0) & 0xffff0000) == LIS_11
&& (bfd_get_32 (abfd, buf + 4) & 0xffff0000) == LWZ_11_11
&& bfd_get_32 (abfd, buf + 8) == MTCTR_11
&& bfd_get_32 (abfd, buf + 12) == BCTR);
}
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);
}
static long
ppc_elf_get_synthetic_symtab (bfd *abfd, long symcount, asymbol **syms,
long dynsymcount, asymbol **dynsyms,
asymbol **ret)
{
bool (*slurp_relocs) (bfd *, asection *, asymbol **, bool);
asection *plt, *relplt, *dynamic, *glink;
bfd_vma glink_vma = 0;
bfd_vma resolv_vma = 0;
bfd_vma stub_off;
asymbol *s;
arelent *p;
size_t count, i, stub_delta;
size_t size;
char *names;
bfd_byte buf[4];
*ret = NULL;
if ((abfd->flags & (DYNAMIC | EXEC_P)) == 0)
return 0;
if (dynsymcount <= 0)
return 0;
relplt = bfd_get_section_by_name (abfd, ".rela.plt");
if (relplt == NULL)
return 0;
plt = bfd_get_section_by_name (abfd, ".plt");
if (plt == NULL)
return 0;
/* Call common code to handle old-style executable PLTs. */
if (elf_section_flags (plt) & SHF_EXECINSTR)
return _bfd_elf_get_synthetic_symtab (abfd, symcount, syms,
dynsymcount, dynsyms, ret);
/* If this object was prelinked, the prelinker stored the address
of .glink at got[1]. If it wasn't prelinked, got[1] will be zero. */
dynamic = bfd_get_section_by_name (abfd, ".dynamic");
if (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))
return -1;
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_PPC_GOT)
{
unsigned int g_o_t = dyn.d_un.d_val;
asection *got = bfd_get_section_by_name (abfd, ".got");
if (got != NULL
&& bfd_get_section_contents (abfd, got, buf,
g_o_t - got->vma + 4, 4))
glink_vma = bfd_get_32 (abfd, buf);
break;
}
}
free (dynbuf);
}
/* Otherwise we read the first plt entry. */
if (glink_vma == 0)
{
if (bfd_get_section_contents (abfd, plt, buf, 0, 4))
glink_vma = bfd_get_32 (abfd, buf);
}
if (glink_vma == 0)
return 0;
/* 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);
if (glink == NULL)
return 0;
/* Determine glink PLT resolver by reading the relative branch
from the first glink stub. */
if (bfd_get_section_contents (abfd, glink, buf,
glink_vma - glink->vma, 4))
{
unsigned int insn = bfd_get_32 (abfd, buf);
/* The first glink stub may either branch to the resolver ... */
insn ^= B;
if ((insn & ~0x3fffffc) == 0)
resolv_vma = glink_vma + (insn ^ 0x2000000) - 0x2000000;
/* ... or fall through a bunch of NOPs. */
else if ((insn ^ B ^ NOP) == 0)
for (i = 4;
bfd_get_section_contents (abfd, glink, buf,
glink_vma - glink->vma + i, 4);
i += 4)
if (bfd_get_32 (abfd, buf) != NOP)
{
resolv_vma = glink_vma + i;
break;
}
}
count = relplt->size / sizeof (Elf32_External_Rela);
/* If the stubs are those for -shared/-pie then we might have
multiple stubs for each plt entry. If that is the case then
there is no way to associate stubs with their plt entries short
of figuring out the GOT pointer value used in the stub.
The offsets tested here need to cover all possible values of
GLINK_ENTRY_SIZE for other than __tls_get_addr_opt. */
stub_off = glink_vma - glink->vma;
for (stub_delta = 16; stub_delta <= 32; stub_delta += 8)
if (is_nonpic_glink_stub (abfd, glink, stub_off - stub_delta))
break;
if (stub_delta > 32)
return 0;
slurp_relocs = get_elf_backend_data (abfd)->s->slurp_reloc_table;
if (! (*slurp_relocs) (abfd, relplt, dynsyms, true))
return -1;
size = count * sizeof (asymbol);
p = relplt->relocation;
for (i = 0; i < count; i++, p++)
{
size += strlen ((*p->sym_ptr_ptr)->name) + sizeof ("@plt");
if (p->addend != 0)
size += sizeof ("+0x") - 1 + 8;
}
size += sizeof (asymbol) + sizeof ("__glink");
if (resolv_vma)
size += sizeof (asymbol) + sizeof ("__glink_PLTresolve");
s = *ret = bfd_malloc (size);
if (s == NULL)
return -1;
stub_off = glink_vma - glink->vma;
names = (char *) (s + count + 1 + (resolv_vma != 0));
p = relplt->relocation + count - 1;
for (i = 0; i < count; i++)
{
size_t len;
stub_off -= stub_delta;
if (strcmp ((*p->sym_ptr_ptr)->name, "__tls_get_addr_opt") == 0)
stub_off -= 32;
*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 = stub_off;
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;
--p;
}
/* Add a symbol at the start of the glink branch table. */
memset (s, 0, sizeof *s);
s->the_bfd = abfd;
s->flags = BSF_GLOBAL | BSF_SYNTHETIC;
s->section = glink;
s->value = glink_vma - glink->vma;
s->name = names;
memcpy (names, "__glink", sizeof ("__glink"));
names += sizeof ("__glink");
s++;
count++;
if (resolv_vma)
{
/* Add a symbol for the glink PLT resolver. */
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++;
}
return count;
}
/* The following functions are specific to the ELF linker, while
functions above are used generally. They appear in this file more
or less in the order in which they are called. eg.
ppc_elf_check_relocs is called early in the link process,
ppc_elf_finish_dynamic_sections is one of the last functions
called. */
/* Track PLT entries needed for a given symbol. We might need more
than one glink entry per symbol when generating a pic binary. */
struct plt_entry
{
struct plt_entry *next;
/* -fPIC uses multiple GOT sections, one per file, called ".got2".
This field stores the offset into .got2 used to initialise the
GOT pointer reg. It will always be at least 32768. (Current
gcc always uses an offset of 32768, but ld -r will pack .got2
sections together resulting in larger offsets). */
bfd_vma addend;
/* The .got2 section. */
asection *sec;
/* PLT refcount or offset. */
union
{
bfd_signed_vma refcount;
bfd_vma offset;
} plt;
/* .glink stub offset. */
bfd_vma glink_offset;
};
/* 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_ppc_reloc_type r_type)
{
switch (r_type)
{
default:
/* Only relative relocs can be resolved when the object load
address isn't fixed. DTPREL32 is excluded because the
dynamic linker needs to differentiate global dynamic from
local dynamic __tls_index pairs when PPC_OPT_TLS is set. */
return 1;
case R_PPC_REL24:
case R_PPC_REL14:
case R_PPC_REL14_BRTAKEN:
case R_PPC_REL14_BRNTAKEN:
case R_PPC_REL32:
return 0;
case R_PPC_TPREL32:
case R_PPC_TPREL16:
case R_PPC_TPREL16_LO:
case R_PPC_TPREL16_HI:
case R_PPC_TPREL16_HA:
/* 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. */
#define ELIMINATE_COPY_RELOCS 1
/* Used to track dynamic relocations for local symbols. */
struct ppc_dyn_relocs
{
struct ppc_dyn_relocs *next;
/* The input section of the reloc. */
asection *sec;
/* Total number of relocs copied for the input section. */
unsigned int count : 31;
/* Whether this entry is for STT_GNU_IFUNC symbols. */
unsigned int ifunc : 1;
};
/* PPC ELF linker hash entry. */
struct ppc_elf_link_hash_entry
{
struct elf_link_hash_entry elf;
/* If this symbol is used in the linker created sections, the processor
specific backend uses this field to map the field into the offset
from the beginning of the section. */
elf_linker_section_pointers_t *linker_section_pointer;
/* Contexts in which symbol is used in the GOT.
Bits are or'd into the mask as the corresponding relocs are
encountered during check_relocs, with TLS_TLS being set when any
of the other TLS bits are set. tls_optimize clears bits when
optimizing to indicate the corresponding GOT entry type is not
needed. If set, TLS_TLS is never cleared. tls_optimize may also
set TLS_GDIE when a GD reloc turns into an IE one.
These flags are also kept for local symbols. */
#define TLS_TLS 1 /* Any TLS reloc. */
#define TLS_GD 2 /* GD reloc. */
#define TLS_LD 4 /* LD reloc. */
#define TLS_TPREL 8 /* TPREL reloc, => IE. */
#define TLS_DTPREL 16 /* DTPREL reloc, => LD. */
#define TLS_MARK 32 /* __tls_get_addr call marked. */
#define TLS_GDIE 64 /* GOT TPREL reloc resulting from GD->IE. */
unsigned char tls_mask;
/* The above field is also used to mark function symbols. In which
case TLS_TLS will be 0. */
#define PLT_IFUNC 2 /* STT_GNU_IFUNC. */
#define PLT_KEEP 4 /* inline plt call requires plt entry. */
#define NON_GOT 256 /* local symbol plt, not stored. */
/* Nonzero if we have seen a small data relocation referring to this
symbol. */
unsigned char has_sda_refs : 1;
/* Flag use of given relocations. */
unsigned char has_addr16_ha : 1;
unsigned char has_addr16_lo : 1;
};
#define ppc_elf_hash_entry(ent) ((struct ppc_elf_link_hash_entry *) (ent))
/* PPC ELF linker hash table. */
struct ppc_elf_link_hash_table
{
struct elf_link_hash_table elf;
/* Various options passed from the linker. */
struct ppc_elf_params *params;
/* Short-cuts to get to dynamic linker sections. */
asection *glink;
asection *dynsbss;
asection *relsbss;
elf_linker_section_t sdata[2];
asection *sbss;
asection *glink_eh_frame;
asection *pltlocal;
asection *relpltlocal;
/* The (unloaded but important) .rela.plt.unloaded on VxWorks. */
asection *srelplt2;
/* Shortcut to __tls_get_addr. */
struct elf_link_hash_entry *tls_get_addr;
/* The bfd that forced an old-style PLT. */
bfd *old_bfd;
/* TLS local dynamic got entry handling. */
union {
bfd_signed_vma refcount;
bfd_vma offset;
} tlsld_got;
/* Offset of branch table to PltResolve function in glink. */
bfd_vma glink_pltresolve;
/* Size of reserved GOT entries. */
unsigned int got_header_size;
/* Non-zero if allocating the header left a gap. */
unsigned int got_gap;
/* The type of PLT we have chosen to use. */
enum ppc_elf_plt_type plt_type;
/* Whether there exist local gnu indirect function resolvers,
referenced by dynamic relocations. */
unsigned int local_ifunc_resolver:1;
unsigned int maybe_local_ifunc_resolver:1;
/* Set if tls optimization is enabled. */
unsigned int do_tls_opt:1;
/* Set if inline plt calls should be converted to direct calls. */
unsigned int can_convert_all_inline_plt:1;
/* The size of PLT entries. */
int plt_entry_size;
/* The distance between adjacent PLT slots. */
int plt_slot_size;
/* The size of the first PLT entry. */
int plt_initial_entry_size;
};
/* Rename some of the generic section flags to better document how they
are used for ppc32. The flags are only valid for ppc32 elf objects. */
/* Nonzero if this section has TLS related relocations. */
#define has_tls_reloc sec_flg0
/* Nonzero if this section has a call to __tls_get_addr lacking marker
relocs. */
#define nomark_tls_get_addr sec_flg1
/* Flag set when PLTCALL relocs are detected. */
#define has_pltcall sec_flg2
/* Get the PPC ELF linker hash table from a link_info structure. */
#define ppc_elf_hash_table(p) \
((is_elf_hash_table ((p)->hash) \
&& elf_hash_table_id (elf_hash_table (p)) == PPC32_ELF_DATA) \
? (struct ppc_elf_link_hash_table *) (p)->hash : NULL)
/* Create an entry in a PPC ELF linker hash table. */
static struct bfd_hash_entry *
ppc_elf_link_hash_newfunc (struct bfd_hash_entry *entry,
struct bfd_hash_table *table,
const char *string)
{
/* Allocate the structure if it has not already been allocated by a
subclass. */
if (entry == NULL)
{
entry = bfd_hash_allocate (table,
sizeof (struct ppc_elf_link_hash_entry));
if (entry == NULL)
return entry;
}
/* Call the allocation method of the superclass. */
entry = _bfd_elf_link_hash_newfunc (entry, table, string);
if (entry != NULL)
{
ppc_elf_hash_entry (entry)->linker_section_pointer = NULL;
ppc_elf_hash_entry (entry)->tls_mask = 0;
ppc_elf_hash_entry (entry)->has_sda_refs = 0;
}
return entry;
}
/* Create a PPC ELF linker hash table. */
static struct bfd_link_hash_table *
ppc_elf_link_hash_table_create (bfd *abfd)
{
struct ppc_elf_link_hash_table *ret;
static struct ppc_elf_params default_params
= { PLT_OLD, 0, 0, 1, 0, 0, 12, 0, 0, 0 };
ret = bfd_zmalloc (sizeof (struct ppc_elf_link_hash_table));
if (ret == NULL)
return NULL;
if (!_bfd_elf_link_hash_table_init (&ret->elf, abfd,
ppc_elf_link_hash_newfunc,
sizeof (struct ppc_elf_link_hash_entry),
PPC32_ELF_DATA))
{
free (ret);
return NULL;
}
ret->elf.init_plt_refcount.refcount = 0;
ret->elf.init_plt_refcount.glist = NULL;
ret->elf.init_plt_offset.offset = 0;
ret->elf.init_plt_offset.glist = NULL;
ret->params = &default_params;
ret->sdata[0].name = ".sdata";
ret->sdata[0].sym_name = "_SDA_BASE_";
ret->sdata[0].bss_name = ".sbss";
ret->sdata[1].name = ".sdata2";
ret->sdata[1].sym_name = "_SDA2_BASE_";
ret->sdata[1].bss_name = ".sbss2";
ret->plt_entry_size = 12;
ret->plt_slot_size = 8;
ret->plt_initial_entry_size = 72;
return &ret->elf.root;
}
/* Hook linker params into hash table. */
void
ppc_elf_link_params (struct bfd_link_info *info, struct ppc_elf_params *params)
{
struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info);
if (htab)
htab->params = params;
params->pagesize_p2 = bfd_log2 (params->pagesize);
}
/* Create .got and the related sections. */
static bool
ppc_elf_create_got (bfd *abfd, struct bfd_link_info *info)
{
struct ppc_elf_link_hash_table *htab;
if (!_bfd_elf_create_got_section (abfd, info))
return false;
htab = ppc_elf_hash_table (info);
if (htab->elf.target_os != is_vxworks)
{
/* The powerpc .got has a blrl instruction in it. Mark it
executable. */
flagword flags = (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS
| SEC_IN_MEMORY | SEC_LINKER_CREATED);
if (!bfd_set_section_flags (htab->elf.sgot, flags))
return false;
}
return true;
}
/* Create a special linker section, used for R_PPC_EMB_SDAI16 and
R_PPC_EMB_SDA2I16 pointers. These sections become part of .sdata
and .sdata2. Create _SDA_BASE_ and _SDA2_BASE too. */
static bool
ppc_elf_create_linker_section (bfd *abfd,
struct bfd_link_info *info,
flagword flags,
elf_linker_section_t *lsect)
{
asection *s;
flags |= (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
| SEC_LINKER_CREATED);
s = bfd_make_section_anyway_with_flags (abfd, lsect->name, flags);
if (s == NULL)
return false;
lsect->section = s;
/* Define the sym on the first section of this name. */
s = bfd_get_section_by_name (abfd, lsect->name);
lsect->sym = _bfd_elf_define_linkage_sym (abfd, info, s, lsect->sym_name);
if (lsect->sym == NULL)
return false;
lsect->sym->root.u.def.value = 0x8000;
return true;
}
static bool
ppc_elf_create_glink (bfd *abfd, struct bfd_link_info *info)
{
struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info);
asection *s;
flagword flags;
int p2align;
flags = (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_READONLY | SEC_HAS_CONTENTS
| SEC_IN_MEMORY | SEC_LINKER_CREATED);
s = bfd_make_section_anyway_with_flags (abfd, ".glink", flags);
htab->glink = s;
p2align = htab->params->ppc476_workaround ? 6 : 4;
if (p2align < htab->params->plt_stub_align)
p2align = htab->params->plt_stub_align;
if (s == NULL
|| !bfd_set_section_alignment (s, p2align))
return false;
if (!info->no_ld_generated_unwind_info)
{
flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS
| SEC_IN_MEMORY | SEC_LINKER_CREATED);
s = bfd_make_section_anyway_with_flags (abfd, ".eh_frame", flags);
htab->glink_eh_frame = s;
if (s == NULL
|| !bfd_set_section_alignment (s, 2))
return false;
}
flags = SEC_ALLOC | SEC_LINKER_CREATED;
s = bfd_make_section_anyway_with_flags (abfd, ".iplt", flags);
htab->elf.iplt = s;
if (s == NULL
|| !bfd_set_section_alignment (s, 4))
return false;
flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS
| SEC_IN_MEMORY | SEC_LINKER_CREATED);
s = bfd_make_section_anyway_with_flags (abfd, ".rela.iplt", flags);
htab->elf.irelplt = s;
if (s == NULL
|| ! bfd_set_section_alignment (s, 2))
return false;
/* Local plt entries. */
flags = (SEC_ALLOC | SEC_LOAD
| SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
htab->pltlocal = bfd_make_section_anyway_with_flags (abfd, ".branch_lt",
flags);
if (htab->pltlocal == NULL
|| !bfd_set_section_alignment (htab->pltlocal, 2))
return false;
if (bfd_link_pic (info))
{
flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
| SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
htab->relpltlocal
= bfd_make_section_anyway_with_flags (abfd, ".rela.branch_lt", flags);
if (htab->relpltlocal == NULL
|| !bfd_set_section_alignment (htab->relpltlocal, 2))
return false;
}
if (!ppc_elf_create_linker_section (abfd, info, 0,
&htab->sdata[0]))
return false;
if (!ppc_elf_create_linker_section (abfd, info, SEC_READONLY,
&htab->sdata[1]))
return false;
return true;
}
/* We have to create .dynsbss and .rela.sbss here so that they get mapped
to output sections (just like _bfd_elf_create_dynamic_sections has
to create .dynbss and .rela.bss). */
static bool
ppc_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
{
struct ppc_elf_link_hash_table *htab;
asection *s;
flagword flags;
htab = ppc_elf_hash_table (info);
if (htab->elf.sgot == NULL
&& !ppc_elf_create_got (abfd, info))
return false;
if (!_bfd_elf_create_dynamic_sections (abfd, info))
return false;
if (htab->glink == NULL
&& !ppc_elf_create_glink (abfd, info))
return false;
s = bfd_make_section_anyway_with_flags (abfd, ".dynsbss",
SEC_ALLOC | SEC_LINKER_CREATED);
htab->dynsbss = s;
if (s == NULL)
return false;
if (! bfd_link_pic (info))
{
flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS
| SEC_IN_MEMORY | SEC_LINKER_CREATED);
s = bfd_make_section_anyway_with_flags (abfd, ".rela.sbss", flags);
htab->relsbss = s;
if (s == NULL
|| !bfd_set_section_alignment (s, 2))
return false;
}
if (htab->elf.target_os == is_vxworks
&& !elf_vxworks_create_dynamic_sections (abfd, info, &htab->srelplt2))
return false;
s = htab->elf.splt;
flags = SEC_ALLOC | SEC_CODE | SEC_LINKER_CREATED;
if (htab->plt_type == PLT_VXWORKS)
/* The VxWorks PLT is a loaded section with contents. */
flags |= SEC_HAS_CONTENTS | SEC_LOAD | SEC_READONLY;
return bfd_set_section_flags (s, flags);
}
/* Copy the extra info we tack onto an elf_link_hash_entry. */
static void
ppc_elf_copy_indirect_symbol (struct bfd_link_info *info,
struct elf_link_hash_entry *dir,
struct elf_link_hash_entry *ind)
{
struct ppc_elf_link_hash_entry *edir, *eind;
edir = (struct ppc_elf_link_hash_entry *) dir;
eind = (struct ppc_elf_link_hash_entry *) ind;
edir->tls_mask |= eind->tls_mask;
edir->has_sda_refs |= eind->has_sda_refs;
if (edir->elf.versioned != versioned_hidden)
edir->elf.ref_dynamic |= eind->elf.ref_dynamic;
edir->elf.ref_regular |= eind->elf.ref_regular;
edir->elf.ref_regular_nonweak |= eind->elf.ref_regular_nonweak;
edir->elf.non_got_ref |= eind->elf.non_got_ref;
edir->elf.needs_plt |= eind->elf.needs_plt;
edir->elf.pointer_equality_needed |= eind->elf.pointer_equality_needed;
/* If we were called to copy over info for a weak sym, that's all. */
if (eind->elf.root.type != bfd_link_hash_indirect)
return;
if (ind->dyn_relocs != NULL)
{
if (dir->dyn_relocs != NULL)
{
struct elf_dyn_relocs **pp;
struct elf_dyn_relocs *p;
/* Add reloc counts against the indirect sym to the direct sym
list. Merge any entries against the same section. */
for (pp = &ind->dyn_relocs; (p = *pp) != NULL; )
{
struct elf_dyn_relocs *q;
for (q = dir->dyn_relocs; q != NULL; q = q->next)
if (q->sec == p->sec)
{
q->pc_count += p->pc_count;
q->count += p->count;
*pp = p->next;
break;
}
if (q == NULL)
pp = &p->next;
}
*pp = dir->dyn_relocs;
}
dir->dyn_relocs = ind->dyn_relocs;
ind->dyn_relocs = NULL;
}
/* Copy over the GOT refcount entries that we may have already seen to
the symbol which just became indirect. */
edir->elf.got.refcount += eind->elf.got.refcount;
eind->elf.got.refcount = 0;
/* And plt entries. */
if (eind->elf.plt.plist != NULL)
{
if (edir->elf.plt.plist != NULL)
{
struct plt_entry **entp;
struct plt_entry *ent;
for (entp = &eind->elf.plt.plist; (ent = *entp) != NULL; )
{
struct plt_entry *dent;
for (dent = edir->elf.plt.plist; dent != NULL; dent = dent->next)
if (dent->sec == ent->sec && dent->addend == ent->addend)
{
dent->plt.refcount += ent->plt.refcount;
*entp = ent->next;
break;
}
if (dent == NULL)
entp = &ent->next;
}
*entp = edir->elf.plt.plist;
}
edir->elf.plt.plist = eind->elf.plt.plist;
eind->elf.plt.plist = NULL;
}
if (eind->elf.dynindx != -1)
{
if (edir->elf.dynindx != -1)
_bfd_elf_strtab_delref (elf_hash_table (info)->dynstr,
edir->elf.dynstr_index);
edir->elf.dynindx = eind->elf.dynindx;
edir->elf.dynstr_index = eind->elf.dynstr_index;
eind->elf.dynindx = -1;
eind->elf.dynstr_index = 0;
}
}
/* Hook called by the linker routine which adds symbols from an object
file. We use it to put .comm items in .sbss, and not .bss. */
static bool
ppc_elf_add_symbol_hook (bfd *abfd,
struct bfd_link_info *info,
Elf_Internal_Sym *sym,
const char **namep ATTRIBUTE_UNUSED,
flagword *flagsp ATTRIBUTE_UNUSED,
asection **secp,
bfd_vma *valp)
{
if (sym->st_shndx == SHN_COMMON
&& !bfd_link_relocatable (info)
&& is_ppc_elf (info->output_bfd)
&& sym->st_size <= elf_gp_size (abfd))
{
/* Common symbols less than or equal to -G nn bytes are automatically
put into .sbss. */
struct ppc_elf_link_hash_table *htab;
htab = ppc_elf_hash_table (info);
if (htab->sbss == NULL)
{
flagword flags = SEC_IS_COMMON | SEC_SMALL_DATA | SEC_LINKER_CREATED;
if (!htab->elf.dynobj)
htab->elf.dynobj = abfd;
htab->sbss = bfd_make_section_anyway_with_flags (htab->elf.dynobj,
".sbss",
flags);
if (htab->sbss == NULL)
return false;
}
*secp = htab->sbss;
*valp = sym->st_size;
}
return true;
}
/* Find a linker generated pointer with a given addend and type. */
static elf_linker_section_pointers_t *
elf_find_pointer_linker_section
(elf_linker_section_pointers_t *linker_pointers,
bfd_vma addend,
elf_linker_section_t *lsect)
{
for ( ; linker_pointers != NULL; linker_pointers = linker_pointers->next)
if (lsect == linker_pointers->lsect && addend == linker_pointers->addend)
return linker_pointers;
return NULL;
}
/* Allocate a pointer to live in a linker created section. */
static bool
elf_allocate_pointer_linker_section (bfd *abfd,
elf_linker_section_t *lsect,
struct elf_link_hash_entry *h,
const Elf_Internal_Rela *rel)
{
elf_linker_section_pointers_t **ptr_linker_section_ptr = NULL;
elf_linker_section_pointers_t *linker_section_ptr;
unsigned long r_symndx = ELF32_R_SYM (rel->r_info);
bfd_size_type amt;
BFD_ASSERT (lsect != NULL);
/* Is this a global symbol? */
if (h != NULL)
{
struct ppc_elf_link_hash_entry *eh;
/* Has this symbol already been allocated? If so, our work is done. */
eh = (struct ppc_elf_link_hash_entry *) h;
if (elf_find_pointer_linker_section (eh->linker_section_pointer,
rel->r_addend,
lsect))
return true;
ptr_linker_section_ptr = &eh->linker_section_pointer;
}
else
{
BFD_ASSERT (is_ppc_elf (abfd));
/* Allocation of a pointer to a local symbol. */
elf_linker_section_pointers_t **ptr = elf_local_ptr_offsets (abfd);
/* Allocate a table to hold the local symbols if first time. */
if (!ptr)
{
unsigned int num_symbols = elf_symtab_hdr (abfd).sh_info;
amt = num_symbols;
amt *= sizeof (elf_linker_section_pointers_t *);
ptr = bfd_zalloc (abfd, amt);
if (!ptr)
return false;
elf_local_ptr_offsets (abfd) = ptr;
}
/* Has this symbol already been allocated? If so, our work is done. */
if (elf_find_pointer_linker_section (ptr[r_symndx],
rel->r_addend,
lsect))