#include "bfd.h"
#include "sysdep.h"
#include "bfdlink.h"
#include "libbfd.h"
#include "elf-bfd.h"
#include "elf/ppc.h"
#include "elf32-ppc.h"
#include "elf-vxworks.h"
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 **);
#define BRANCH_PREDICT_BIT 0x200000
#define RA_REGISTER_MASK 0x001f0000
#define RA_REGISTER_SHIFT 16
#define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so.1"
#define PLT_NUM_SINGLE_ENTRIES 8192
#define GLINK_PLTRESOLVE 16*4
#define GLINK_ENTRY_SIZE 4*4
#define VXWORKS_PLT_ENTRY_SIZE 32
static const bfd_vma ppc_elf_vxworks_plt_entry
[VXWORKS_PLT_ENTRY_SIZE / 4] =
{
0x3d800000,
0x818c0000,
0x7d8903a6,
0x4e800420,
0x39600000,
0x48000000,
0x60000000,
0x60000000,
};
static const bfd_vma ppc_elf_vxworks_pic_plt_entry
[VXWORKS_PLT_ENTRY_SIZE / 4] =
{
0x3d9e0000,
0x818c0000,
0x7d8903a6,
0x4e800420,
0x39600000,
0x48000000,
0x60000000,
0x60000000,
};
#define VXWORKS_PLT_INITIAL_ENTRY_SIZE 32
static const bfd_vma ppc_elf_vxworks_plt0_entry
[VXWORKS_PLT_INITIAL_ENTRY_SIZE / 4] =
{
0x3d800000,
0x398c0000,
0x800c0008,
0x7c0903a6,
0x818c0004,
0x4e800420,
0x60000000,
0x60000000,
};
static const bfd_vma ppc_elf_vxworks_pic_plt0_entry
[VXWORKS_PLT_INITIAL_ENTRY_SIZE / 4] =
{
0x819e0008,
0x7d8903a6,
0x819e0004,
0x4e800420,
0x60000000,
0x60000000,
0x60000000,
0x60000000,
};
#define VXWORKS_PLT_NON_JMP_SLOT_RELOCS 3
#define VXWORKS_PLTRESOLVE_RELOCS 2
#define VXWORKS_PLTRESOLVE_RELOCS_SHLIB 0
#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_11_0_11 0x7d605a14
#define B 0x48000000
#define BCL_20_31 0x429f0005
#define BCTR 0x4e800420
#define LIS_11 0x3d600000
#define LIS_12 0x3d800000
#define LWZU_0_12 0x840c0000
#define LWZ_0_12 0x800c0000
#define LWZ_11_11 0x816b0000
#define LWZ_11_30 0x817e0000
#define LWZ_12_12 0x818c0000
#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
#define TP_OFFSET 0x7000
#define DTP_OFFSET 0x8000
static reloc_howto_type *ppc_elf_howto_table[R_PPC_max];
static reloc_howto_type ppc_elf_howto_raw[] = {
HOWTO (R_PPC_NONE,
0,
2,
32,
FALSE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_PPC_NONE",
FALSE,
0,
0,
FALSE),
HOWTO (R_PPC_ADDR32,
0,
2,
32,
FALSE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_PPC_ADDR32",
FALSE,
0,
0xffffffff,
FALSE),
HOWTO (R_PPC_ADDR24,
0,
2,
26,
FALSE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_PPC_ADDR24",
FALSE,
0,
0x3fffffc,
FALSE),
HOWTO (R_PPC_ADDR16,
0,
1,
16,
FALSE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_PPC_ADDR16",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_ADDR16_LO,
0,
1,
16,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_PPC_ADDR16_LO",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_ADDR16_HI,
16,
1,
16,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_PPC_ADDR16_HI",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_ADDR16_HA,
16,
1,
16,
FALSE,
0,
complain_overflow_dont,
ppc_elf_addr16_ha_reloc,
"R_PPC_ADDR16_HA",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_ADDR14,
0,
2,
16,
FALSE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_PPC_ADDR14",
FALSE,
0,
0xfffc,
FALSE),
HOWTO (R_PPC_ADDR14_BRTAKEN,
0,
2,
16,
FALSE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_PPC_ADDR14_BRTAKEN",
FALSE,
0,
0xfffc,
FALSE),
HOWTO (R_PPC_ADDR14_BRNTAKEN,
0,
2,
16,
FALSE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_PPC_ADDR14_BRNTAKEN",
FALSE,
0,
0xfffc,
FALSE),
HOWTO (R_PPC_REL24,
0,
2,
26,
TRUE,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_PPC_REL24",
FALSE,
0,
0x3fffffc,
TRUE),
HOWTO (R_PPC_REL14,
0,
2,
16,
TRUE,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_PPC_REL14",
FALSE,
0,
0xfffc,
TRUE),
HOWTO (R_PPC_REL14_BRTAKEN,
0,
2,
16,
TRUE,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_PPC_REL14_BRTAKEN",
FALSE,
0,
0xfffc,
TRUE),
HOWTO (R_PPC_REL14_BRNTAKEN,
0,
2,
16,
TRUE,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_PPC_REL14_BRNTAKEN",
FALSE,
0,
0xfffc,
TRUE),
HOWTO (R_PPC_GOT16,
0,
1,
16,
FALSE,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_PPC_GOT16",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_GOT16_LO,
0,
1,
16,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_PPC_GOT16_LO",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_GOT16_HI,
16,
1,
16,
FALSE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_PPC_GOT16_HI",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_GOT16_HA,
16,
1,
16,
FALSE,
0,
complain_overflow_bitfield,
ppc_elf_addr16_ha_reloc,
"R_PPC_GOT16_HA",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_PLTREL24,
0,
2,
26,
TRUE,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_PPC_PLTREL24",
FALSE,
0,
0x3fffffc,
TRUE),
HOWTO (R_PPC_COPY,
0,
2,
32,
FALSE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_PPC_COPY",
FALSE,
0,
0,
FALSE),
HOWTO (R_PPC_GLOB_DAT,
0,
2,
32,
FALSE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_PPC_GLOB_DAT",
FALSE,
0,
0xffffffff,
FALSE),
HOWTO (R_PPC_JMP_SLOT,
0,
2,
32,
FALSE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_PPC_JMP_SLOT",
FALSE,
0,
0,
FALSE),
HOWTO (R_PPC_RELATIVE,
0,
2,
32,
FALSE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_PPC_RELATIVE",
FALSE,
0,
0xffffffff,
FALSE),
HOWTO (R_PPC_LOCAL24PC,
0,
2,
26,
TRUE,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_PPC_LOCAL24PC",
FALSE,
0,
0x3fffffc,
TRUE),
HOWTO (R_PPC_UADDR32,
0,
2,
32,
FALSE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_PPC_UADDR32",
FALSE,
0,
0xffffffff,
FALSE),
HOWTO (R_PPC_UADDR16,
0,
1,
16,
FALSE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_PPC_UADDR16",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_REL32,
0,
2,
32,
TRUE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_PPC_REL32",
FALSE,
0,
0xffffffff,
TRUE),
HOWTO (R_PPC_PLT32,
0,
2,
32,
FALSE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_PPC_PLT32",
FALSE,
0,
0,
FALSE),
HOWTO (R_PPC_PLTREL32,
0,
2,
32,
TRUE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_PPC_PLTREL32",
FALSE,
0,
0,
TRUE),
HOWTO (R_PPC_PLT16_LO,
0,
1,
16,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_PPC_PLT16_LO",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_PLT16_HI,
16,
1,
16,
FALSE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_PPC_PLT16_HI",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_PLT16_HA,
16,
1,
16,
FALSE,
0,
complain_overflow_bitfield,
ppc_elf_addr16_ha_reloc,
"R_PPC_PLT16_HA",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_SDAREL16,
0,
1,
16,
FALSE,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_PPC_SDAREL16",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_SECTOFF,
0,
1,
16,
FALSE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_PPC_SECTOFF",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_SECTOFF_LO,
0,
1,
16,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_PPC_SECTOFF_LO",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_SECTOFF_HI,
16,
1,
16,
FALSE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_PPC_SECTOFF_HI",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_SECTOFF_HA,
16,
1,
16,
FALSE,
0,
complain_overflow_bitfield,
ppc_elf_addr16_ha_reloc,
"R_PPC_SECTOFF_HA",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_TLS,
0,
2,
32,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_PPC_TLS",
FALSE,
0,
0,
FALSE),
HOWTO (R_PPC_DTPMOD32,
0,
2,
32,
FALSE,
0,
complain_overflow_dont,
ppc_elf_unhandled_reloc,
"R_PPC_DTPMOD32",
FALSE,
0,
0xffffffff,
FALSE),
HOWTO (R_PPC_DTPREL32,
0,
2,
32,
FALSE,
0,
complain_overflow_dont,
ppc_elf_unhandled_reloc,
"R_PPC_DTPREL32",
FALSE,
0,
0xffffffff,
FALSE),
HOWTO (R_PPC_DTPREL16,
0,
1,
16,
FALSE,
0,
complain_overflow_signed,
ppc_elf_unhandled_reloc,
"R_PPC_DTPREL16",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_DTPREL16_LO,
0,
1,
16,
FALSE,
0,
complain_overflow_dont,
ppc_elf_unhandled_reloc,
"R_PPC_DTPREL16_LO",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_DTPREL16_HI,
16,
1,
16,
FALSE,
0,
complain_overflow_dont,
ppc_elf_unhandled_reloc,
"R_PPC_DTPREL16_HI",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_DTPREL16_HA,
16,
1,
16,
FALSE,
0,
complain_overflow_dont,
ppc_elf_unhandled_reloc,
"R_PPC_DTPREL16_HA",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_TPREL32,
0,
2,
32,
FALSE,
0,
complain_overflow_dont,
ppc_elf_unhandled_reloc,
"R_PPC_TPREL32",
FALSE,
0,
0xffffffff,
FALSE),
HOWTO (R_PPC_TPREL16,
0,
1,
16,
FALSE,
0,
complain_overflow_signed,
ppc_elf_unhandled_reloc,
"R_PPC_TPREL16",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_TPREL16_LO,
0,
1,
16,
FALSE,
0,
complain_overflow_dont,
ppc_elf_unhandled_reloc,
"R_PPC_TPREL16_LO",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_TPREL16_HI,
16,
1,
16,
FALSE,
0,
complain_overflow_dont,
ppc_elf_unhandled_reloc,
"R_PPC_TPREL16_HI",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_TPREL16_HA,
16,
1,
16,
FALSE,
0,
complain_overflow_dont,
ppc_elf_unhandled_reloc,
"R_PPC_TPREL16_HA",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_GOT_TLSGD16,
0,
1,
16,
FALSE,
0,
complain_overflow_signed,
ppc_elf_unhandled_reloc,
"R_PPC_GOT_TLSGD16",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_GOT_TLSGD16_LO,
0,
1,
16,
FALSE,
0,
complain_overflow_dont,
ppc_elf_unhandled_reloc,
"R_PPC_GOT_TLSGD16_LO",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_GOT_TLSGD16_HI,
16,
1,
16,
FALSE,
0,
complain_overflow_dont,
ppc_elf_unhandled_reloc,
"R_PPC_GOT_TLSGD16_HI",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_GOT_TLSGD16_HA,
16,
1,
16,
FALSE,
0,
complain_overflow_dont,
ppc_elf_unhandled_reloc,
"R_PPC_GOT_TLSGD16_HA",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_GOT_TLSLD16,
0,
1,
16,
FALSE,
0,
complain_overflow_signed,
ppc_elf_unhandled_reloc,
"R_PPC_GOT_TLSLD16",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_GOT_TLSLD16_LO,
0,
1,
16,
FALSE,
0,
complain_overflow_dont,
ppc_elf_unhandled_reloc,
"R_PPC_GOT_TLSLD16_LO",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_GOT_TLSLD16_HI,
16,
1,
16,
FALSE,
0,
complain_overflow_dont,
ppc_elf_unhandled_reloc,
"R_PPC_GOT_TLSLD16_HI",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_GOT_TLSLD16_HA,
16,
1,
16,
FALSE,
0,
complain_overflow_dont,
ppc_elf_unhandled_reloc,
"R_PPC_GOT_TLSLD16_HA",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_GOT_DTPREL16,
0,
1,
16,
FALSE,
0,
complain_overflow_signed,
ppc_elf_unhandled_reloc,
"R_PPC_GOT_DTPREL16",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_GOT_DTPREL16_LO,
0,
1,
16,
FALSE,
0,
complain_overflow_dont,
ppc_elf_unhandled_reloc,
"R_PPC_GOT_DTPREL16_LO",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_GOT_DTPREL16_HI,
16,
1,
16,
FALSE,
0,
complain_overflow_dont,
ppc_elf_unhandled_reloc,
"R_PPC_GOT_DTPREL16_HI",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_GOT_DTPREL16_HA,
16,
1,
16,
FALSE,
0,
complain_overflow_dont,
ppc_elf_unhandled_reloc,
"R_PPC_GOT_DTPREL16_HA",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_GOT_TPREL16,
0,
1,
16,
FALSE,
0,
complain_overflow_signed,
ppc_elf_unhandled_reloc,
"R_PPC_GOT_TPREL16",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_GOT_TPREL16_LO,
0,
1,
16,
FALSE,
0,
complain_overflow_dont,
ppc_elf_unhandled_reloc,
"R_PPC_GOT_TPREL16_LO",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_GOT_TPREL16_HI,
16,
1,
16,
FALSE,
0,
complain_overflow_dont,
ppc_elf_unhandled_reloc,
"R_PPC_GOT_TPREL16_HI",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_GOT_TPREL16_HA,
16,
1,
16,
FALSE,
0,
complain_overflow_dont,
ppc_elf_unhandled_reloc,
"R_PPC_GOT_TPREL16_HA",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_EMB_NADDR32,
0,
2,
32,
FALSE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_PPC_EMB_NADDR32",
FALSE,
0,
0xffffffff,
FALSE),
HOWTO (R_PPC_EMB_NADDR16,
0,
1,
16,
FALSE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_PPC_EMB_NADDR16",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_EMB_NADDR16_LO,
0,
1,
16,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_PPC_EMB_ADDR16_LO",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_EMB_NADDR16_HI,
16,
1,
16,
FALSE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_PPC_EMB_NADDR16_HI",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_EMB_NADDR16_HA,
16,
1,
16,
FALSE,
0,
complain_overflow_dont,
ppc_elf_addr16_ha_reloc,
"R_PPC_EMB_NADDR16_HA",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_EMB_SDAI16,
0,
1,
16,
FALSE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_PPC_EMB_SDAI16",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_EMB_SDA2I16,
0,
1,
16,
FALSE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_PPC_EMB_SDA2I16",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_EMB_SDA2REL,
0,
1,
16,
FALSE,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_PPC_EMB_SDA2REL",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_EMB_SDA21,
0,
2,
16,
FALSE,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_PPC_EMB_SDA21",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_EMB_RELSDA,
0,
1,
16,
TRUE,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_PPC_EMB_RELSDA",
FALSE,
0,
0xffff,
FALSE),
HOWTO (R_PPC_REL16,
0,
1,
16,
TRUE,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_PPC_REL16",
FALSE,
0,
0xffff,
TRUE),
HOWTO (R_PPC_REL16_LO,
0,
1,
16,
TRUE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_PPC_REL16_LO",
FALSE,
0,
0xffff,
TRUE),
HOWTO (R_PPC_REL16_HI,
16,
1,
16,
TRUE,
0,
complain_overflow_dont,
bfd_elf_generic_reloc,
"R_PPC_REL16_HI",
FALSE,
0,
0xffff,
TRUE),
HOWTO (R_PPC_REL16_HA,
16,
1,
16,
TRUE,
0,
complain_overflow_dont,
ppc_elf_addr16_ha_reloc,
"R_PPC_REL16_HA",
FALSE,
0,
0xffff,
TRUE),
HOWTO (R_PPC_GNU_VTINHERIT,
0,
0,
0,
FALSE,
0,
complain_overflow_dont,
NULL,
"R_PPC_GNU_VTINHERIT",
FALSE,
0,
0,
FALSE),
HOWTO (R_PPC_GNU_VTENTRY,
0,
0,
0,
FALSE,
0,
complain_overflow_dont,
NULL,
"R_PPC_GNU_VTENTRY",
FALSE,
0,
0,
FALSE),
HOWTO (R_PPC_TOC16,
0,
1,
16,
FALSE,
0,
complain_overflow_signed,
bfd_elf_generic_reloc,
"R_PPC_TOC16",
FALSE,
0,
0xffff,
FALSE),
};
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;
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_16: r = R_PPC_ADDR16; break;
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_16_GOTOFF: r = R_PPC_GOT16; break;
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_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_16_BASEREL: r = R_PPC_SECTOFF; break;
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_PPC_TOC16: r = R_PPC_TOC16; break;
case BFD_RELOC_PPC_TLS: r = R_PPC_TLS; break;
case BFD_RELOC_PPC_DTPMOD: r = R_PPC_DTPMOD32; break;
case BFD_RELOC_PPC_TPREL16: r = R_PPC_TPREL16; break;
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_PPC_DTPREL16: r = R_PPC_DTPREL16; break;
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_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_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 void
ppc_elf_info_to_howto (bfd *abfd ATTRIBUTE_UNUSED,
arelent *cache_ptr,
Elf_Internal_Rela *dst)
{
if (!ppc_elf_howto_table[R_PPC_ADDR32])
ppc_elf_howto_init ();
BFD_ASSERT (ELF32_R_TYPE (dst->r_info) < (unsigned int) R_PPC_max);
cache_ptr->howto = ppc_elf_howto_table[ELF32_R_TYPE (dst->r_info)];
}
static bfd_reloc_status_type
ppc_elf_addr16_ha_reloc (bfd *abfd ATTRIBUTE_UNUSED,
arelent *reloc_entry,
asymbol *symbol,
void *data ATTRIBUTE_UNUSED,
asection *input_section,
bfd *output_bfd,
char **error_message ATTRIBUTE_UNUSED)
{
bfd_vma relocation;
if (output_bfd != NULL)
{
reloc_entry->address += input_section->output_offset;
return bfd_reloc_ok;
}
if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
return bfd_reloc_outofrange;
if (bfd_is_com_section (symbol->section))
relocation = 0;
else
relocation = symbol->value;
relocation += symbol->section->output_section->vma;
relocation += symbol->section->output_offset;
relocation += reloc_entry->addend;
if (reloc_entry->howto->pc_relative)
relocation -= reloc_entry->address;
reloc_entry->addend += (relocation & 0x8000) << 1;
return bfd_reloc_continue;
}
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 (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 buf[60];
sprintf (buf, _("generic linker can't handle %s"),
reloc_entry->howto->name);
*error_message = buf;
}
return bfd_reloc_dangerous;
}
typedef struct elf_linker_section
{
asection *section;
const char *name;
const char *bss_name;
const char *sym_name;
struct elf_link_hash_entry *sym;
} elf_linker_section_t;
typedef struct elf_linker_section_pointers
{
struct elf_linker_section_pointers *next;
bfd_vma offset;
bfd_vma addend;
elf_linker_section_t *lsect;
} elf_linker_section_pointers_t;
struct ppc_elf_obj_tdata
{
struct elf_obj_tdata elf;
elf_linker_section_pointers_t **linker_section_pointers;
};
#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)
static bfd_boolean
ppc_elf_mkobject (bfd *abfd)
{
bfd_size_type amt = sizeof (struct ppc_elf_obj_tdata);
abfd->tdata.any = bfd_zalloc (abfd, amt);
if (abfd->tdata.any == NULL)
return FALSE;
return TRUE;
}
static bfd_boolean
ppc_elf_object_p (bfd *abfd)
{
if (abfd->arch_info->the_default && abfd->arch_info->bits_per_word == 64)
{
Elf_Internal_Ehdr *i_ehdr = elf_elfheader (abfd);
if (i_ehdr->e_ident[EI_CLASS] == ELFCLASS32)
{
abfd->arch_info = abfd->arch_info->next;
BFD_ASSERT (abfd->arch_info->bits_per_word == 32);
}
}
return TRUE;
}
static bfd_boolean
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;
}
static bfd_boolean
ppc_elf_grok_prstatus (bfd *abfd, Elf_Internal_Note *note)
{
int offset;
unsigned int size;
switch (note->descsz)
{
default:
return FALSE;
case 268:
elf_tdata (abfd)->core_signal = bfd_get_16 (abfd, note->descdata + 12);
elf_tdata (abfd)->core_pid = bfd_get_32 (abfd, note->descdata + 24);
offset = 72;
size = 192;
break;
}
return _bfd_elfcore_make_pseudosection (abfd, ".reg",
size, note->descpos + offset);
}
static bfd_boolean
ppc_elf_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
{
switch (note->descsz)
{
default:
return FALSE;
case 128:
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);
}
{
char *command = elf_tdata (abfd)->core_command;
int n = strlen (command);
if (0 < n && command[n - 1] == ' ')
command[n - 1] = '\0';
}
return TRUE;
}
static bfd_vma
ppc_elf_plt_sym_val (bfd_vma i ATTRIBUTE_UNUSED,
const asection *plt ATTRIBUTE_UNUSED,
const arelent *rel)
{
return rel->address;
}
static bfd_boolean
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 = bfd_get_section_flags (abfd, newsect);
if (hdr->sh_flags & SHF_EXCLUDE)
flags |= SEC_EXCLUDE;
if (hdr->sh_type == SHT_ORDERED)
flags |= SEC_SORT_ENTRIES;
bfd_set_section_flags (abfd, newsect, flags);
return TRUE;
}
static bfd_boolean
ppc_elf_fake_sections (bfd *abfd ATTRIBUTE_UNUSED,
Elf_Internal_Shdr *shdr,
asection *asect)
{
if ((asect->flags & SEC_EXCLUDE) != 0)
shdr->sh_flags |= SHF_EXCLUDE;
if ((asect->flags & SEC_SORT_ENTRIES) != 0)
shdr->sh_type = SHT_ORDERED;
return TRUE;
}
static int
ppc_elf_additional_program_headers (bfd *abfd)
{
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;
}
static const struct bfd_elf_special_section ppc_elf_special_sections[] =
{
{ ".plt", 4, 0, SHT_NOBITS, SHF_ALLOC + SHF_EXECINSTR },
{ ".sbss", 5, -2, SHT_NOBITS, SHF_ALLOC + SHF_WRITE },
{ ".sbss2", 6, -2, SHT_PROGBITS, SHF_ALLOC },
{ ".sdata", 6, -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
{ ".sdata2", 7, -2, SHT_PROGBITS, SHF_ALLOC },
{ ".tags", 5, 0, SHT_ORDERED, SHF_ALLOC },
{ ".PPC.EMB.apuinfo", 16, 0, SHT_NOTE, 0 },
{ ".PPC.EMB.sbss0", 14, 0, SHT_PROGBITS, SHF_ALLOC },
{ ".PPC.EMB.sdata0", 15, 0, SHT_PROGBITS, SHF_ALLOC },
{ NULL, 0, 0, 0, 0 }
};
static struct bfd_elf_special_section ppc_alt_plt =
{ ".plt", 4, 0, SHT_PROGBITS, SHF_ALLOC };
static const struct bfd_elf_special_section *
ppc_elf_get_sec_type_attr (bfd *abfd ATTRIBUTE_UNUSED, asection *sec)
{
const struct bfd_elf_special_section *ssect;
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);
}
typedef struct apuinfo_list
{
struct apuinfo_list *next;
unsigned long value;
}
apuinfo_list;
static apuinfo_list *head;
static void
apuinfo_list_init (void)
{
head = NULL;
}
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;
}
#define APUINFO_SECTION_NAME ".PPC.EMB.apuinfo"
#define APUINFO_LABEL "APUinfo"
static void
ppc_elf_begin_write_processing (bfd *abfd, struct bfd_link_info *link_info)
{
bfd *ibfd;
asection *asec;
char *buffer;
unsigned num_input_sections;
bfd_size_type output_section_size;
unsigned i;
unsigned num_entries;
unsigned long offset;
unsigned long length;
const char *error_message = NULL;
if (link_info == NULL)
return;
num_input_sections = 0;
output_section_size = 0;
for (ibfd = link_info->input_bfds; ibfd; ibfd = ibfd->link_next)
{
asec = bfd_get_section_by_name (ibfd, APUINFO_SECTION_NAME);
if (asec)
{
++ num_input_sections;
output_section_size += asec->size;
}
}
if (num_input_sections < 1)
return;
asec = bfd_get_section_by_name (abfd, APUINFO_SECTION_NAME);
if (asec == NULL)
return;
buffer = bfd_malloc (output_section_size);
if (buffer == NULL)
return;
offset = 0;
apuinfo_list_init ();
for (ibfd = link_info->input_bfds; ibfd; ibfd = ibfd->link_next)
{
unsigned long datum;
char *ptr;
asec = bfd_get_section_by_name (ibfd, APUINFO_SECTION_NAME);
if (asec == NULL)
continue;
length = asec->size;
if (length < 24)
{
error_message = _("corrupt or empty %s section in %B");
goto fail;
}
if (bfd_seek (ibfd, asec->filepos, SEEK_SET) != 0
|| (bfd_bread (buffer + offset, length, ibfd) != length))
{
error_message = _("unable to read in %s section from %B");
goto fail;
}
ptr = buffer + offset;
error_message = _("corrupt %s section in %B");
datum = bfd_get_32 (ibfd, ptr);
if (datum != sizeof APUINFO_LABEL)
goto fail;
datum = bfd_get_32 (ibfd, ptr + 8);
if (datum != 0x2)
goto fail;
if (strcmp (ptr + 12, APUINFO_LABEL) != 0)
goto fail;
datum = bfd_get_32 (ibfd, ptr + 4);
if (datum + 20 != length)
goto fail;
if (offset + length > output_section_size)
goto fail;
for (i = 0; i < datum; i += 4)
apuinfo_list_add (bfd_get_32 (ibfd, ptr + 20 + i));
offset += length;
}
error_message = NULL;
num_entries = apuinfo_list_length ();
output_section_size = 20 + num_entries * 4;
asec = bfd_get_section_by_name (abfd, APUINFO_SECTION_NAME);
if (! bfd_set_section_size (abfd, asec, output_section_size))
ibfd = abfd,
error_message = _("warning: unable to set size of %s section in %B");
fail:
free (buffer);
if (error_message)
(*_bfd_error_handler) (error_message, ibfd, APUINFO_SECTION_NAME);
}
static bfd_boolean
ppc_elf_write_section (bfd *abfd ATTRIBUTE_UNUSED,
asection *asec,
bfd_byte *contents ATTRIBUTE_UNUSED)
{
return (apuinfo_list_length ()
&& strcmp (asec->name, APUINFO_SECTION_NAME) == 0);
}
static void
ppc_elf_final_write_processing (bfd *abfd, bfd_boolean linker ATTRIBUTE_UNUSED)
{
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_list_length () == 0)
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;
}
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 ();
}
struct ppc_elf_dyn_relocs
{
struct ppc_elf_dyn_relocs *next;
asection *sec;
bfd_size_type count;
bfd_size_type pc_count;
};
struct plt_entry
{
struct plt_entry *next;
bfd_vma addend;
asection *sec;
union
{
bfd_signed_vma refcount;
bfd_vma offset;
} plt;
bfd_vma glink_offset;
};
#define MUST_BE_DYN_RELOC(RTYPE) \
((RTYPE) != R_PPC_REL24 \
&& (RTYPE) != R_PPC_REL14 \
&& (RTYPE) != R_PPC_REL14_BRTAKEN \
&& (RTYPE) != R_PPC_REL14_BRNTAKEN \
&& (RTYPE) != R_PPC_REL32)
#define ELIMINATE_COPY_RELOCS 1
struct ppc_elf_link_hash_entry
{
struct elf_link_hash_entry elf;
elf_linker_section_pointers_t *linker_section_pointer;
struct ppc_elf_dyn_relocs *dyn_relocs;
#define TLS_GD 1
#define TLS_LD 2
#define TLS_TPREL 4
#define TLS_DTPREL 8
#define TLS_TLS 16
#define TLS_TPRELGD 32
char tls_mask;
unsigned char has_sda_refs;
};
#define ppc_elf_hash_entry(ent) ((struct ppc_elf_link_hash_entry *) (ent))
struct ppc_elf_link_hash_table
{
struct elf_link_hash_table elf;
asection *got;
asection *relgot;
asection *glink;
asection *plt;
asection *relplt;
asection *dynbss;
asection *relbss;
asection *dynsbss;
asection *relsbss;
elf_linker_section_t sdata[2];
asection *sbss;
struct elf_link_hash_entry *tls_get_addr;
union {
bfd_signed_vma refcount;
bfd_vma offset;
} tlsld_got;
bfd_vma glink_pltresolve;
unsigned int got_header_size;
unsigned int got_gap;
unsigned int new_plt:1;
unsigned int old_plt:1;
struct sym_sec_cache sym_sec;
asection *srelplt2;
asection *sgotplt;
struct elf_link_hash_entry *hplt;
int is_vxworks;
int plt_entry_size;
int plt_slot_size;
int plt_initial_entry_size;
};
#define ppc_elf_hash_table(p) \
((struct ppc_elf_link_hash_table *) (p)->hash)
static struct bfd_hash_entry *
ppc_elf_link_hash_newfunc (struct bfd_hash_entry *entry,
struct bfd_hash_table *table,
const char *string)
{
if (entry == NULL)
{
entry = bfd_hash_allocate (table,
sizeof (struct ppc_elf_link_hash_entry));
if (entry == NULL)
return entry;
}
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)->dyn_relocs = NULL;
ppc_elf_hash_entry (entry)->tls_mask = 0;
}
return entry;
}
static struct bfd_link_hash_table *
ppc_elf_link_hash_table_create (bfd *abfd)
{
struct ppc_elf_link_hash_table *ret;
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))
{
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->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;
ret->is_vxworks = 0;
return &ret->elf.root;
}
static bfd_boolean
ppc_elf_create_got (bfd *abfd, struct bfd_link_info *info)
{
struct ppc_elf_link_hash_table *htab;
asection *s;
flagword flags;
if (!_bfd_elf_create_got_section (abfd, info))
return FALSE;
htab = ppc_elf_hash_table (info);
htab->got = s = bfd_get_section_by_name (abfd, ".got");
if (s == NULL)
abort ();
if (htab->is_vxworks)
{
htab->sgotplt = bfd_get_section_by_name (abfd, ".got.plt");
if (!htab->sgotplt)
abort ();
}
else
{
flags = (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS
| SEC_IN_MEMORY | SEC_LINKER_CREATED);
if (!bfd_set_section_flags (abfd, s, flags))
return FALSE;
}
flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
| SEC_LINKER_CREATED | SEC_READONLY);
htab->relgot = bfd_make_section_with_flags (abfd, ".rela.got", flags);
if (!htab->relgot
|| ! bfd_set_section_alignment (abfd, htab->relgot, 2))
return FALSE;
return TRUE;
}
static bfd_boolean
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->got == NULL
&& !ppc_elf_create_got (abfd, info))
return FALSE;
if (!_bfd_elf_create_dynamic_sections (abfd, info))
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, ".glink", flags | SEC_CODE);
htab->glink = s;
if (s == NULL
|| !bfd_set_section_alignment (abfd, s, 4))
return FALSE;
htab->dynbss = bfd_get_section_by_name (abfd, ".dynbss");
s = bfd_make_section_with_flags (abfd, ".dynsbss",
SEC_ALLOC | SEC_LINKER_CREATED);
htab->dynsbss = s;
if (s == NULL)
return FALSE;
if (! info->shared)
{
htab->relbss = bfd_get_section_by_name (abfd, ".rela.bss");
s = bfd_make_section_with_flags (abfd, ".rela.sbss", flags);
htab->relsbss = s;
if (s == NULL
|| ! bfd_set_section_alignment (abfd, s, 2))
return FALSE;
}
if (htab->is_vxworks && !info->shared)
{
s = bfd_make_section (abfd, ".rela.plt.unloaded");
flags = (SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_READONLY
| SEC_LINKER_CREATED);
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, flags)
|| ! bfd_set_section_alignment (abfd, s,
get_elf_backend_data (abfd)->s->log_file_align))
return FALSE;
htab->srelplt2 = s;
}
htab->relplt = bfd_get_section_by_name (abfd, ".rela.plt");
htab->plt = s = bfd_get_section_by_name (abfd, ".plt");
if (s == NULL)
abort ();
flags = SEC_ALLOC | SEC_CODE | SEC_LINKER_CREATED;
return bfd_set_section_flags (abfd, s, flags);
}
static void
ppc_elf_copy_indirect_symbol (const struct elf_backend_data *bed ATTRIBUTE_UNUSED,
struct elf_link_hash_entry *dir,
struct elf_link_hash_entry *ind)
{
struct ppc_elf_link_hash_entry *edir, *eind;
bfd_signed_vma tmp;
edir = (struct ppc_elf_link_hash_entry *) dir;
eind = (struct ppc_elf_link_hash_entry *) ind;
if (eind->dyn_relocs != NULL)
{
if (edir->dyn_relocs != NULL)
{
struct ppc_elf_dyn_relocs **pp;
struct ppc_elf_dyn_relocs *p;
if (ind->root.type == bfd_link_hash_indirect)
abort ();
for (pp = &eind->dyn_relocs; (p = *pp) != NULL; )
{
struct ppc_elf_dyn_relocs *q;
for (q = edir->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 = edir->dyn_relocs;
}
edir->dyn_relocs = eind->dyn_relocs;
eind->dyn_relocs = NULL;
}
edir->tls_mask |= eind->tls_mask;
edir->has_sda_refs |= eind->has_sda_refs;
if (!(ELIMINATE_COPY_RELOCS
&& eind->elf.root.type != bfd_link_hash_indirect
&& edir->elf.dynamic_adjusted))
edir->elf.non_got_ref |= eind->elf.non_got_ref;
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.needs_plt |= eind->elf.needs_plt;
if (eind->elf.root.type != bfd_link_hash_indirect)
return;
tmp = edir->elf.got.refcount;
if (tmp < 1)
{
edir->elf.got.refcount = eind->elf.got.refcount;
eind->elf.got.refcount = tmp;
}
else
BFD_ASSERT (eind->elf.got.refcount < 1);
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 (edir->elf.dynindx == -1)
{
edir->elf.dynindx = eind->elf.dynindx;
edir->elf.dynstr_index = eind->elf.dynstr_index;
eind->elf.dynindx = -1;
eind->elf.dynstr_index = 0;
}
else
BFD_ASSERT (eind->elf.dynindx == -1);
}
static bfd_boolean
is_ppc_elf_target (const struct bfd_target *targ)
{
extern const bfd_target bfd_elf32_powerpc_vec;
extern const bfd_target bfd_elf32_powerpcle_vec;
return targ == &bfd_elf32_powerpc_vec || targ == &bfd_elf32_powerpcle_vec;
}
static bfd_boolean
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
&& !info->relocatable
&& sym->st_size <= elf_gp_size (abfd)
&& is_ppc_elf_target (info->hash->creator))
{
struct ppc_elf_link_hash_table *htab;
htab = ppc_elf_hash_table (info);
if (htab->sbss == NULL)
{
flagword flags = SEC_IS_COMMON | 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;
}
static bfd_boolean
create_sdata_sym (struct ppc_elf_link_hash_table *htab,
elf_linker_section_t *lsect)
{
lsect->sym = elf_link_hash_lookup (&htab->elf, lsect->sym_name,
TRUE, FALSE, TRUE);
if (lsect->sym == NULL)
return FALSE;
if (lsect->sym->root.type == bfd_link_hash_new)
lsect->sym->non_elf = 0;
lsect->sym->ref_regular = 1;
return TRUE;
}
static bfd_boolean
ppc_elf_create_linker_section (bfd *abfd,
struct bfd_link_info *info,
flagword flags,
elf_linker_section_t *lsect)
{
struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info);
asection *s;
flags |= (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
| SEC_LINKER_CREATED);
if (!htab->elf.dynobj)
htab->elf.dynobj = abfd;
s = bfd_make_section_anyway_with_flags (htab->elf.dynobj,
lsect->name,
flags);
if (s == NULL
|| !bfd_set_section_alignment (htab->elf.dynobj, s, 2))
return FALSE;
lsect->section = s;
return create_sdata_sym (htab, lsect);
}
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;
}
static bfd_boolean
elf_create_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);
if (h != NULL)
{
struct ppc_elf_link_hash_entry *eh;
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
{
elf_linker_section_pointers_t **ptr = elf_local_ptr_offsets (abfd);
if (!ptr)
{
unsigned int num_symbols = elf_tdata (abfd)->symtab_hdr.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;
}
if (elf_find_pointer_linker_section (ptr[r_symndx],
rel->r_addend,
lsect))
return TRUE;
ptr_linker_section_ptr = &ptr[r_symndx];
}
BFD_ASSERT (ptr_linker_section_ptr != NULL);
amt = sizeof (elf_linker_section_pointers_t);
linker_section_ptr = bfd_alloc (abfd, amt);
if (!linker_section_ptr)
return FALSE;
linker_section_ptr->next = *ptr_linker_section_ptr;
linker_section_ptr->addend = rel->r_addend;
linker_section_ptr->lsect = lsect;
*ptr_linker_section_ptr = linker_section_ptr;
linker_section_ptr->offset = lsect->section->size;
lsect->section->size += 4;
#ifdef DEBUG
fprintf (stderr,
"Create pointer in linker section %s, offset = %ld, section size = %ld\n",
lsect->name, (long) linker_section_ptr->offset,
(long) lsect->section->size);
#endif
return TRUE;
}
static bfd_boolean
update_local_sym_info (bfd *abfd,
Elf_Internal_Shdr *symtab_hdr,
unsigned long r_symndx,
int tls_type)
{
bfd_signed_vma *local_got_refcounts = elf_local_got_refcounts (abfd);
char *local_got_tls_masks;
if (local_got_refcounts == NULL)
{
bfd_size_type size = symtab_hdr->sh_info;
size *= sizeof (*local_got_refcounts) + sizeof (*local_got_tls_masks);
local_got_refcounts = bfd_zalloc (abfd, size);
if (local_got_refcounts == NULL)
return FALSE;
elf_local_got_refcounts (abfd) = local_got_refcounts;
}
local_got_refcounts[r_symndx] += 1;
local_got_tls_masks = (char *) (local_got_refcounts + symtab_hdr->sh_info);
local_got_tls_masks[r_symndx] |= tls_type;
return TRUE;
}
static bfd_boolean
update_plt_info (bfd *abfd, struct elf_link_hash_entry *h,
asection *sec, bfd_vma addend)
{
struct plt_entry *ent;
if (addend < 32768)
sec = NULL;
for (ent = h->plt.plist; ent != NULL; ent = ent->next)
if (ent->sec == sec && ent->addend == addend)
break;
if (ent == NULL)
{
bfd_size_type amt = sizeof (*ent);
ent = bfd_alloc (abfd, amt);
if (ent == NULL)
return FALSE;
ent->next = h->plt.plist;
ent->sec = sec;
ent->addend = addend;
ent->plt.refcount = 0;
h->plt.plist = ent;
}
ent->plt.refcount += 1;
return TRUE;
}
static struct plt_entry *
find_plt_ent (struct elf_link_hash_entry *h, asection *sec, bfd_vma addend)
{
struct plt_entry *ent;
if (addend < 32768)
sec = NULL;
for (ent = h->plt.plist; ent != NULL; ent = ent->next)
if (ent->sec == sec && ent->addend == addend)
break;
return ent;
}
static void
bad_shared_reloc (bfd *abfd, enum elf_ppc_reloc_type r_type)
{
(*_bfd_error_handler)
(_("%B: relocation %s cannot be used when making a shared object"),
abfd,
ppc_elf_howto_table[r_type]->name);
bfd_set_error (bfd_error_bad_value);
}
static bfd_boolean
ppc_elf_check_relocs (bfd *abfd,
struct bfd_link_info *info,
asection *sec,
const Elf_Internal_Rela *relocs)
{
struct ppc_elf_link_hash_table *htab;
Elf_Internal_Shdr *symtab_hdr;
struct elf_link_hash_entry **sym_hashes;
const Elf_Internal_Rela *rel;
const Elf_Internal_Rela *rel_end;
asection *got2, *sreloc;
if (info->relocatable)
return TRUE;
if ((sec->flags & SEC_ALLOC) == 0)
return TRUE;
#ifdef DEBUG
_bfd_error_handler ("ppc_elf_check_relocs called for section %A in %B",
sec, abfd);
#endif
if (!ppc_elf_howto_table[R_PPC_ADDR32])
ppc_elf_howto_init ();
htab = ppc_elf_hash_table (info);
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
sym_hashes = elf_sym_hashes (abfd);
got2 = bfd_get_section_by_name (abfd, ".got2");
sreloc = NULL;
rel_end = relocs + sec->reloc_count;
for (rel = relocs; rel < rel_end; rel++)
{
unsigned long r_symndx;
enum elf_ppc_reloc_type r_type;
struct elf_link_hash_entry *h;
int tls_type = 0;
r_symndx = ELF32_R_SYM (rel->r_info);
if (r_symndx < symtab_hdr->sh_info)
h = NULL;
else
{
h = sym_hashes[r_symndx - symtab_hdr->sh_info];
while (h->root.type == bfd_link_hash_indirect
|| h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
}
if (h != NULL
&& htab->got == NULL
&& strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0)
{
if (htab->elf.dynobj == NULL)
htab->elf.dynobj = abfd;
if (!ppc_elf_create_got (htab->elf.dynobj, info))
return FALSE;
BFD_ASSERT (h == htab->elf.hgot);
}
r_type = ELF32_R_TYPE (rel->r_info);
switch (r_type)
{
case R_PPC_GOT_TLSLD16:
case R_PPC_GOT_TLSLD16_LO:
case R_PPC_GOT_TLSLD16_HI:
case R_PPC_GOT_TLSLD16_HA:
htab->tlsld_got.refcount += 1;
tls_type = TLS_TLS | TLS_LD;
goto dogottls;
case R_PPC_GOT_TLSGD16:
case R_PPC_GOT_TLSGD16_LO:
case R_PPC_GOT_TLSGD16_HI:
case R_PPC_GOT_TLSGD16_HA:
tls_type = TLS_TLS | TLS_GD;
goto dogottls;
case R_PPC_GOT_TPREL16:
case R_PPC_GOT_TPREL16_LO:
case R_PPC_GOT_TPREL16_HI:
case R_PPC_GOT_TPREL16_HA:
if (info->shared)
info->flags |= DF_STATIC_TLS;
tls_type = TLS_TLS | TLS_TPREL;
goto dogottls;
case R_PPC_GOT_DTPREL16:
case R_PPC_GOT_DTPREL16_LO:
case R_PPC_GOT_DTPREL16_HI:
case R_PPC_GOT_DTPREL16_HA:
tls_type = TLS_TLS | TLS_DTPREL;
dogottls:
sec->has_tls_reloc = 1;
case R_PPC_GOT16:
case R_PPC_GOT16_LO:
case R_PPC_GOT16_HI:
case R_PPC_GOT16_HA:
if (htab->got == NULL)
{
if (htab->elf.dynobj == NULL)
htab->elf.dynobj = abfd;
if (!ppc_elf_create_got (htab->elf.dynobj, info))
return FALSE;
}
if (h != NULL)
{
h->got.refcount += 1;
ppc_elf_hash_entry (h)->tls_mask |= tls_type;
}
else
if (!update_local_sym_info (abfd, symtab_hdr, r_symndx, tls_type))
return FALSE;
break;
case R_PPC_EMB_SDAI16:
if (info->shared)
{
bad_shared_reloc (abfd, r_type);
return FALSE;
}
if (htab->sdata[0].section == NULL
&& !ppc_elf_create_linker_section (abfd, info, 0,
&htab->sdata[0]))
return FALSE;
if (!elf_create_pointer_linker_section (abfd, &htab->sdata[0],
h, rel))
return FALSE;
if (h != NULL)
{
ppc_elf_hash_entry (h)->has_sda_refs = TRUE;
h->non_got_ref = TRUE;
}
break;
case R_PPC_EMB_SDA2I16:
if (info->shared)
{
bad_shared_reloc (abfd, r_type);
return FALSE;
}
if (htab->sdata[1].section == NULL
&& !ppc_elf_create_linker_section (abfd, info, SEC_READONLY,
&htab->sdata[1]))
return FALSE;
if (!elf_create_pointer_linker_section (abfd, &htab->sdata[1],
h, rel))
return FALSE;
if (h != NULL)
{
ppc_elf_hash_entry (h)->has_sda_refs = TRUE;
h->non_got_ref = TRUE;
}
break;
case R_PPC_SDAREL16:
if (info->shared)
{
bad_shared_reloc (abfd, r_type);
return FALSE;
}
if (htab->sdata[0].sym == NULL
&& !create_sdata_sym (htab, &htab->sdata[0]))
return FALSE;
if (h != NULL)
{
ppc_elf_hash_entry (h)->has_sda_refs = TRUE;
h->non_got_ref = TRUE;
}
break;
case R_PPC_EMB_SDA2REL:
if (info->shared)
{
bad_shared_reloc (abfd, r_type);
return FALSE;
}
if (htab->sdata[1].sym == NULL
&& !create_sdata_sym (htab, &htab->sdata[1]))
return FALSE;
if (h != NULL)
{
ppc_elf_hash_entry (h)->has_sda_refs = TRUE;
h->non_got_ref = TRUE;
}
break;
case R_PPC_EMB_SDA21:
case R_PPC_EMB_RELSDA:
if (info->shared)
{
bad_shared_reloc (abfd, r_type);
return FALSE;
}
if (htab->sdata[0].sym == NULL
&& !create_sdata_sym (htab, &htab->sdata[0]))
return FALSE;
if (htab->sdata[1].sym == NULL
&& !create_sdata_sym (htab, &htab->sdata[1]))
return FALSE;
if (h != NULL)
{
ppc_elf_hash_entry (h)->has_sda_refs = TRUE;
h->non_got_ref = TRUE;
}
break;
case R_PPC_EMB_NADDR32:
case R_PPC_EMB_NADDR16:
case R_PPC_EMB_NADDR16_LO:
case R_PPC_EMB_NADDR16_HI:
case R_PPC_EMB_NADDR16_HA:
if (info->shared)
{
bad_shared_reloc (abfd, r_type);
return FALSE;
}
if (h != NULL)
h->non_got_ref = TRUE;
break;
case R_PPC_PLT32:
case R_PPC_PLTREL24:
case R_PPC_PLTREL32:
case R_PPC_PLT16_LO:
case R_PPC_PLT16_HI:
case R_PPC_PLT16_HA:
#ifdef DEBUG
fprintf (stderr, "Reloc requires a PLT entry\n");
#endif
if (h == NULL)
{
(*_bfd_error_handler) (_("%B(%A+0x%lx): %s reloc against "
"local symbol"),
abfd,
sec,
(long) rel->r_offset,
ppc_elf_howto_table[r_type]->name);
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
else
{
bfd_vma addend = r_type == R_PPC_PLTREL24 ? rel->r_addend : 0;
h->needs_plt = 1;
if (!update_plt_info (abfd, h, got2, addend))
return FALSE;
}
break;
case R_PPC_SECTOFF:
case R_PPC_SECTOFF_LO:
case R_PPC_SECTOFF_HI:
case R_PPC_SECTOFF_HA:
case R_PPC_DTPREL16:
case R_PPC_DTPREL16_LO:
case R_PPC_DTPREL16_HI:
case R_PPC_DTPREL16_HA:
case R_PPC_TOC16:
break;
case R_PPC_REL16:
case R_PPC_REL16_LO:
case R_PPC_REL16_HI:
case R_PPC_REL16_HA:
htab->new_plt = 1;
break;
case R_PPC_TLS:
case R_PPC_EMB_MRKREF:
case R_PPC_NONE:
case R_PPC_max:
break;
case R_PPC_COPY:
case R_PPC_GLOB_DAT:
case R_PPC_JMP_SLOT:
case R_PPC_RELATIVE:
break;
case R_PPC_ADDR30:
case R_PPC_EMB_RELSEC16:
case R_PPC_EMB_RELST_LO:
case R_PPC_EMB_RELST_HI:
case R_PPC_EMB_RELST_HA:
case R_PPC_EMB_BIT_FLD:
break;
case R_PPC_LOCAL24PC:
if (h && h == htab->elf.hgot)
htab->old_plt = 1;
break;
case R_PPC_GNU_VTINHERIT:
if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
return FALSE;
break;
case R_PPC_GNU_VTENTRY:
if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
return FALSE;
break;
case R_PPC_TPREL32:
if (info->shared)
info->flags |= DF_STATIC_TLS;
goto dodyn;
case R_PPC_DTPMOD32:
case R_PPC_DTPREL32:
goto dodyn;
case R_PPC_TPREL16:
case R_PPC_TPREL16_LO:
case R_PPC_TPREL16_HI:
case R_PPC_TPREL16_HA:
if (info->shared)
info->flags |= DF_STATIC_TLS;
goto dodyn;
case R_PPC_REL32:
if (h == NULL
&& got2 != NULL
&& (sec->flags & SEC_CODE) != 0
&& (info->shared || info->pie)
&& !htab->old_plt)
{
asection *s;
s = bfd_section_from_r_symndx (abfd, &htab->sym_sec, sec,
r_symndx);
if (s == got2)
htab->old_plt = 1;
}
case R_PPC_REL24:
case R_PPC_REL14:
case R_PPC_REL14_BRTAKEN:
case R_PPC_REL14_BRNTAKEN:
if (h == NULL)
break;
if (h == htab->elf.hgot)
{
htab->old_plt = 1;
break;
}
case R_PPC_ADDR32:
case R_PPC_ADDR24:
case R_PPC_ADDR16:
case R_PPC_ADDR16_LO:
case R_PPC_ADDR16_HI:
case R_PPC_ADDR16_HA:
case R_PPC_ADDR14:
case R_PPC_ADDR14_BRTAKEN:
case R_PPC_ADDR14_BRNTAKEN:
case R_PPC_UADDR32:
case R_PPC_UADDR16:
if (h != NULL && !info->shared)
{
if (!update_plt_info (abfd, h, NULL, 0))
return FALSE;
h->non_got_ref = 1;
}
dodyn:
if ((info->shared
&& (MUST_BE_DYN_RELOC (r_type)
|| (h != NULL
&& (! info->symbolic
|| h->root.type == bfd_link_hash_defweak
|| !h->def_regular))))
|| (ELIMINATE_COPY_RELOCS
&& !info->shared
&& h != NULL
&& (h->root.type == bfd_link_hash_defweak
|| !h->def_regular)))
{
struct ppc_elf_dyn_relocs *p;
struct ppc_elf_dyn_relocs **head;
#ifdef DEBUG
fprintf (stderr,
"ppc_elf_check_relocs needs to "
"create relocation for %s\n",
(h && h->root.root.string
? h->root.root.string : "<unknown>"));
#endif
if (sreloc == NULL)
{
const char *name;
name = (bfd_elf_string_from_elf_section
(abfd,
elf_elfheader (abfd)->e_shstrndx,
elf_section_data (sec)->rel_hdr.sh_name));
if (name == NULL)
return FALSE;
BFD_ASSERT (strncmp (name, ".rela", 5) == 0
&& strcmp (bfd_get_section_name (abfd, sec),
name + 5) == 0);
if (htab->elf.dynobj == NULL)
htab->elf.dynobj = abfd;
sreloc = bfd_get_section_by_name (htab->elf.dynobj, name);
if (sreloc == NULL)
{
flagword flags;
flags = (SEC_HAS_CONTENTS | SEC_READONLY
| SEC_IN_MEMORY | SEC_LINKER_CREATED
| SEC_ALLOC | SEC_LOAD);
sreloc = bfd_make_section_with_flags (htab->elf.dynobj,
name,
flags);
if (sreloc == NULL
|| ! bfd_set_section_alignment (htab->elf.dynobj,
sreloc, 2))
return FALSE;
}
elf_section_data (sec)->sreloc = sreloc;
}
if (h != NULL)
{
head = &ppc_elf_hash_entry (h)->dyn_relocs;
}
else
{
asection *s;
s = bfd_section_from_r_symndx (abfd, &htab->sym_sec,
sec, r_symndx);
if (s == NULL)
return FALSE;
head = ((struct ppc_elf_dyn_relocs **)
&elf_section_data (s)->local_dynrel);
}
p = *head;
if (p == NULL || p->sec != sec)
{
p = bfd_alloc (htab->elf.dynobj, sizeof *p);
if (p == NULL)
return FALSE;
p->next = *head;
*head = p;
p->sec = sec;
p->count = 0;
p->pc_count = 0;
}
p->count += 1;
if (!MUST_BE_DYN_RELOC (r_type))
p->pc_count += 1;
}
break;
}
}
return TRUE;
}
static bfd_boolean
ppc_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
{
flagword old_flags;
flagword new_flags;
bfd_boolean error;
if (!is_ppc_elf_target (ibfd->xvec)
|| !is_ppc_elf_target (obfd->xvec))
return TRUE;
if (! _bfd_generic_verify_endian_match (ibfd, obfd))
return FALSE;
new_flags = elf_elfheader (ibfd)->e_flags;
old_flags = elf_elfheader (obfd)->e_flags;
if (!elf_flags_init (obfd))
{
elf_flags_init (obfd) = TRUE;
elf_elfheader (obfd)->e_flags = new_flags;
}
else if (new_flags == old_flags)
;
else
{
error = FALSE;
if ((new_flags & EF_PPC_RELOCATABLE) != 0
&& (old_flags & (EF_PPC_RELOCATABLE | EF_PPC_RELOCATABLE_LIB)) == 0)
{
error = TRUE;
(*_bfd_error_handler)
(_("%B: compiled with -mrelocatable and linked with "
"modules compiled normally"), ibfd);
}
else if ((new_flags & (EF_PPC_RELOCATABLE | EF_PPC_RELOCATABLE_LIB)) == 0
&& (old_flags & EF_PPC_RELOCATABLE) != 0)
{
error = TRUE;
(*_bfd_error_handler)
(_("%B: compiled normally and linked with "
"modules compiled with -mrelocatable"), ibfd);
}
if (! (new_flags & EF_PPC_RELOCATABLE_LIB))
elf_elfheader (obfd)->e_flags &= ~EF_PPC_RELOCATABLE_LIB;
if (! (elf_elfheader (obfd)->e_flags & EF_PPC_RELOCATABLE_LIB)
&& (new_flags & (EF_PPC_RELOCATABLE_LIB | EF_PPC_RELOCATABLE))
&& (old_flags & (EF_PPC_RELOCATABLE_LIB | EF_PPC_RELOCATABLE)))
elf_elfheader (obfd)->e_flags |= EF_PPC_RELOCATABLE;
elf_elfheader (obfd)->e_flags |= (new_flags & EF_PPC_EMB);
new_flags &= ~(EF_PPC_RELOCATABLE | EF_PPC_RELOCATABLE_LIB | EF_PPC_EMB);
old_flags &= ~(EF_PPC_RELOCATABLE | EF_PPC_RELOCATABLE_LIB | EF_PPC_EMB);
if (new_flags != old_flags)
{
error = TRUE;
(*_bfd_error_handler)
(_("%B: uses different e_flags (0x%lx) fields "
"than previous modules (0x%lx)"),
ibfd, (long) new_flags, (long) old_flags);
}
if (error)
{
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
}
return TRUE;
}
int
ppc_elf_select_plt_layout (bfd *output_bfd ATTRIBUTE_UNUSED,
struct bfd_link_info *info,
int force_old_plt)
{
struct ppc_elf_link_hash_table *htab;
flagword flags;
htab = ppc_elf_hash_table (info);
if (force_old_plt || !htab->new_plt)
htab->old_plt = 1;
if (htab->is_vxworks)
{
flags = SEC_ALLOC | SEC_CODE | SEC_IN_MEMORY | SEC_LINKER_CREATED
| SEC_HAS_CONTENTS | SEC_LOAD | SEC_READONLY;
if (htab->plt != NULL
&& !bfd_set_section_flags (htab->elf.dynobj, htab->plt, flags))
return -1;
}
else if (!htab->old_plt)
{
flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS
| SEC_IN_MEMORY | SEC_LINKER_CREATED);
if (htab->plt != NULL
&& !bfd_set_section_flags (htab->elf.dynobj, htab->plt, flags))
return -1;
if (htab->got != NULL
&& !bfd_set_section_flags (htab->elf.dynobj, htab->got, flags))
return -1;
}
else
{
if (htab->glink != NULL
&& !bfd_set_section_alignment (htab->elf.dynobj, htab->glink, 0))
return -1;
}
return !htab->old_plt;
}
static asection *
ppc_elf_gc_mark_hook (asection *sec,
struct bfd_link_info *info ATTRIBUTE_UNUSED,
Elf_Internal_Rela *rel,
struct elf_link_hash_entry *h,
Elf_Internal_Sym *sym)
{
if (h != NULL)
{
switch (ELF32_R_TYPE (rel->r_info))
{
case R_PPC_GNU_VTINHERIT:
case R_PPC_GNU_VTENTRY:
break;
default:
switch (h->root.type)
{
case bfd_link_hash_defined:
case bfd_link_hash_defweak:
return h->root.u.def.section;
case bfd_link_hash_common:
return h->root.u.c.p->section;
default:
break;
}
}
}
else
return bfd_section_from_elf_index (sec->owner, sym->st_shndx);
return NULL;
}
static bfd_boolean
ppc_elf_gc_sweep_hook (bfd *abfd,
struct bfd_link_info *info,
asection *sec,
const Elf_Internal_Rela *relocs)
{
struct ppc_elf_link_hash_table *htab;
Elf_Internal_Shdr *symtab_hdr;
struct elf_link_hash_entry **sym_hashes;
bfd_signed_vma *local_got_refcounts;
const Elf_Internal_Rela *rel, *relend;
asection *got2;
if ((sec->flags & SEC_ALLOC) == 0)
return TRUE;
elf_section_data (sec)->local_dynrel = NULL;
htab = ppc_elf_hash_table (info);
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
sym_hashes = elf_sym_hashes (abfd);
local_got_refcounts = elf_local_got_refcounts (abfd);
got2 = bfd_get_section_by_name (abfd, ".got2");
relend = relocs + sec->reloc_count;
for (rel = relocs; rel < relend; rel++)
{
unsigned long r_symndx;
enum elf_ppc_reloc_type r_type;
struct elf_link_hash_entry *h = NULL;
r_symndx = ELF32_R_SYM (rel->r_info);
if (r_symndx >= symtab_hdr->sh_info)
{
struct ppc_elf_dyn_relocs **pp, *p;
struct ppc_elf_link_hash_entry *eh;
h = sym_hashes[r_symndx - symtab_hdr->sh_info];
while (h->root.type == bfd_link_hash_indirect
|| h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
eh = (struct ppc_elf_link_hash_entry *) h;
for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next)
if (p->sec == sec)
{
*pp = p->next;
break;
}
}
r_type = ELF32_R_TYPE (rel->r_info);
switch (r_type)
{
case R_PPC_GOT_TLSLD16:
case R_PPC_GOT_TLSLD16_LO:
case R_PPC_GOT_TLSLD16_HI:
case R_PPC_GOT_TLSLD16_HA:
htab->tlsld_got.refcount -= 1;
case R_PPC_GOT_TLSGD16:
case R_PPC_GOT_TLSGD16_LO:
case R_PPC_GOT_TLSGD16_HI:
case R_PPC_GOT_TLSGD16_HA:
case R_PPC_GOT_TPREL16:
case R_PPC_GOT_TPREL16_LO:
case R_PPC_GOT_TPREL16_HI:
case R_PPC_GOT_TPREL16_HA:
case R_PPC_GOT_DTPREL16:
case R_PPC_GOT_DTPREL16_LO:
case R_PPC_GOT_DTPREL16_HI:
case R_PPC_GOT_DTPREL16_HA:
case R_PPC_GOT16:
case R_PPC_GOT16_LO:
case R_PPC_GOT16_HI:
case R_PPC_GOT16_HA:
if (h != NULL)
{
if (h->got.refcount > 0)
h->got.refcount--;
}
else if (local_got_refcounts != NULL)
{
if (local_got_refcounts[r_symndx] > 0)
local_got_refcounts[r_symndx]--;
}
break;
case R_PPC_REL24:
case R_PPC_REL14:
case R_PPC_REL14_BRTAKEN:
case R_PPC_REL14_BRNTAKEN:
case R_PPC_REL32:
if (h == NULL || h == htab->elf.hgot)
break;
case R_PPC_ADDR32:
case R_PPC_ADDR24:
case R_PPC_ADDR16:
case R_PPC_ADDR16_LO:
case R_PPC_ADDR16_HI:
case R_PPC_ADDR16_HA:
case R_PPC_ADDR14:
case R_PPC_ADDR14_BRTAKEN:
case R_PPC_ADDR14_BRNTAKEN:
case R_PPC_UADDR32:
case R_PPC_UADDR16:
if (info->shared)
break;
case R_PPC_PLT32:
case R_PPC_PLTREL24:
case R_PPC_PLTREL32:
case R_PPC_PLT16_LO:
case R_PPC_PLT16_HI:
case R_PPC_PLT16_HA:
if (h != NULL)
{
bfd_vma addend = r_type == R_PPC_PLTREL24 ? rel->r_addend : 0;
struct plt_entry *ent = find_plt_ent (h, got2, addend);
if (ent->plt.refcount > 0)
ent->plt.refcount -= 1;
}
break;
default:
break;
}
}
return TRUE;
}
asection *
ppc_elf_tls_setup (bfd *obfd, struct bfd_link_info *info)
{
struct ppc_elf_link_hash_table *htab;
htab = ppc_elf_hash_table (info);
if (!htab->old_plt
&& htab->plt != NULL
&& htab->plt->output_section != NULL)
{
elf_section_type (htab->plt->output_section) = SHT_PROGBITS;
elf_section_flags (htab->plt->output_section) = SHF_ALLOC + SHF_WRITE;
}
htab->tls_get_addr = elf_link_hash_lookup (&htab->elf, "__tls_get_addr",
FALSE, FALSE, TRUE);
return _bfd_elf_tls_setup (obfd, info);
}
bfd_boolean
ppc_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED,
struct bfd_link_info *info)
{
bfd *ibfd;
asection *sec;
struct ppc_elf_link_hash_table *htab;
if (info->relocatable || info->shared)
return TRUE;
htab = ppc_elf_hash_table (info);
for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
{
Elf_Internal_Sym *locsyms = NULL;
Elf_Internal_Shdr *symtab_hdr = &elf_tdata (ibfd)->symtab_hdr;
for (sec = ibfd->sections; sec != NULL; sec = sec->next)
if (sec->has_tls_reloc && !bfd_is_abs_section (sec->output_section))
{
Elf_Internal_Rela *relstart, *rel, *relend;
int expecting_tls_get_addr;
relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL,
info->keep_memory);
if (relstart == NULL)
return FALSE;
expecting_tls_get_addr = 0;
relend = relstart + sec->reloc_count;
for (rel = relstart; rel < relend; rel++)
{
enum elf_ppc_reloc_type r_type;
unsigned long r_symndx;
struct elf_link_hash_entry *h = NULL;
char *tls_mask;
char tls_set, tls_clear;
bfd_boolean is_local;
r_symndx = ELF32_R_SYM (rel->r_info);
if (r_symndx >= symtab_hdr->sh_info)
{
struct elf_link_hash_entry **sym_hashes;
sym_hashes = elf_sym_hashes (ibfd);
h = sym_hashes[r_symndx - symtab_hdr->sh_info];
while (h->root.type == bfd_link_hash_indirect
|| h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
}
is_local = FALSE;
if (h == NULL
|| !h->def_dynamic)
is_local = TRUE;
r_type = ELF32_R_TYPE (rel->r_info);
switch (r_type)
{
case R_PPC_GOT_TLSLD16:
case R_PPC_GOT_TLSLD16_LO:
case R_PPC_GOT_TLSLD16_HI:
case R_PPC_GOT_TLSLD16_HA:
expecting_tls_get_addr = 0;
htab->tlsld_got.refcount -= 1;
if (!is_local)
continue;
tls_set = 0;
tls_clear = TLS_LD;
expecting_tls_get_addr = 1;
break;
case R_PPC_GOT_TLSGD16:
case R_PPC_GOT_TLSGD16_LO:
case R_PPC_GOT_TLSGD16_HI:
case R_PPC_GOT_TLSGD16_HA:
if (is_local)
tls_set = 0;
else
tls_set = TLS_TLS | TLS_TPRELGD;
tls_clear = TLS_GD;
expecting_tls_get_addr = 1;
break;
case R_PPC_GOT_TPREL16:
case R_PPC_GOT_TPREL16_LO:
case R_PPC_GOT_TPREL16_HI:
case R_PPC_GOT_TPREL16_HA:
expecting_tls_get_addr = 0;
if (is_local)
{
tls_set = 0;
tls_clear = TLS_TPREL;
break;
}
else
continue;
case R_PPC_REL14:
case R_PPC_REL14_BRTAKEN:
case R_PPC_REL14_BRNTAKEN:
case R_PPC_REL24:
if (expecting_tls_get_addr
&& h != NULL
&& h == htab->tls_get_addr)
{
struct plt_entry *ent = find_plt_ent (h, NULL, 0);
if (ent != NULL && ent->plt.refcount > 0)
ent->plt.refcount -= 1;
}
expecting_tls_get_addr = 0;
continue;
default:
expecting_tls_get_addr = 0;
continue;
}
if (h != NULL)
{
if (tls_set == 0)
{
if (h->got.refcount > 0)
h->got.refcount -= 1;
}
tls_mask = &ppc_elf_hash_entry (h)->tls_mask;
}
else
{
Elf_Internal_Sym *sym;
bfd_signed_vma *lgot_refs;
char *lgot_masks;
if (locsyms == NULL)
{
locsyms = (Elf_Internal_Sym *) symtab_hdr->contents;
if (locsyms == NULL)
locsyms = bfd_elf_get_elf_syms (ibfd, symtab_hdr,
symtab_hdr->sh_info,
0, NULL, NULL, NULL);
if (locsyms == NULL)
{
if (elf_section_data (sec)->relocs != relstart)
free (relstart);
return FALSE;
}
}
sym = locsyms + r_symndx;
lgot_refs = elf_local_got_refcounts (ibfd);
if (lgot_refs == NULL)
abort ();
if (tls_set == 0)
{
if (lgot_refs[r_symndx] > 0)
lgot_refs[r_symndx] -= 1;
}
lgot_masks = (char *) (lgot_refs + symtab_hdr->sh_info);
tls_mask = &lgot_masks[r_symndx];
}
*tls_mask |= tls_set;
*tls_mask &= ~tls_clear;
}
if (elf_section_data (sec)->relocs != relstart)
free (relstart);
}
if (locsyms != NULL
&& (symtab_hdr->contents != (unsigned char *) locsyms))
{
if (!info->keep_memory)
free (locsyms);
else
symtab_hdr->contents = (unsigned char *) locsyms;
}
}
return TRUE;
}
static bfd_boolean
ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
struct elf_link_hash_entry *h)
{
struct ppc_elf_link_hash_table *htab;
asection *s;
unsigned int power_of_two;
#ifdef DEBUG
fprintf (stderr, "ppc_elf_adjust_dynamic_symbol called for %s\n",
h->root.root.string);
#endif
htab = ppc_elf_hash_table (info);
BFD_ASSERT (htab->elf.dynobj != NULL
&& (h->needs_plt
|| h->u.weakdef != NULL
|| (h->def_dynamic
&& h->ref_regular
&& !h->def_regular)));
if (h->type == STT_FUNC
|| h->needs_plt)
{
struct plt_entry *ent;
for (ent = h->plt.plist; ent != NULL; ent = ent->next)
if (ent->plt.refcount > 0)
break;
if (ent == NULL
|| SYMBOL_CALLS_LOCAL (info, h)
|| (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
&& h->root.type == bfd_link_hash_undefweak))
{
h->plt.plist = NULL;
h->needs_plt = 0;
}
return TRUE;
}
else
h->plt.plist = NULL;
if (h->u.weakdef != NULL)
{
BFD_ASSERT (h->u.weakdef->root.type == bfd_link_hash_defined
|| h->u.weakdef->root.type == bfd_link_hash_defweak);
h->root.u.def.section = h->u.weakdef->root.u.def.section;
h->root.u.def.value = h->u.weakdef->root.u.def.value;
if (ELIMINATE_COPY_RELOCS)
h->non_got_ref = h->u.weakdef->non_got_ref;
return TRUE;
}
if (info->shared)
return TRUE;
if (!h->non_got_ref)
return TRUE;
if (ELIMINATE_COPY_RELOCS
&& !ppc_elf_hash_entry (h)->has_sda_refs)
{
struct ppc_elf_dyn_relocs *p;
for (p = ppc_elf_hash_entry (h)->dyn_relocs; p != NULL; p = p->next)
{
s = p->sec->output_section;
if (s != NULL && (s->flags & SEC_READONLY) != 0)
break;
}
if (p == NULL)
{
h->non_got_ref = 0;
return TRUE;
}
}
if (ppc_elf_hash_entry (h)->has_sda_refs)
s = htab->dynsbss;
else
s = htab->dynbss;
BFD_ASSERT (s != NULL);
if ((h->root.u.def.section->flags & SEC_ALLOC) != 0)
{
asection *srel;
if (ppc_elf_hash_entry (h)->has_sda_refs)
srel = htab->relsbss;
else
srel = htab->relbss;
BFD_ASSERT (srel != NULL);
srel->size += sizeof (Elf32_External_Rela);
h->needs_copy = 1;
}
power_of_two = bfd_log2 (h->size);
if (power_of_two > 4)
power_of_two = 4;
s->size = BFD_ALIGN (s->size, (bfd_size_type) (1 << power_of_two));
if (power_of_two > bfd_get_section_alignment (htab->elf.dynobj, s))
{
if (! bfd_set_section_alignment (htab->elf.dynobj, s, power_of_two))
return FALSE;
}
h->root.u.def.section = s;
h->root.u.def.value = s->size;
s->size += h->size;
return TRUE;
}
static bfd_vma
allocate_got (struct ppc_elf_link_hash_table *htab, unsigned int need)
{
bfd_vma where;
unsigned int max_before_header = 32768;
if (htab->old_plt)
max_before_header = 32764;
if (htab->is_vxworks)
{
where = htab->got->size;
htab->got->size += need;
}
else if (need <= htab->got_gap)
{
where = max_before_header - htab->got_gap;
htab->got_gap -= need;
}
else
{
if (htab->got->size + need > max_before_header
&& htab->got->size <= max_before_header)
{
htab->got_gap = max_before_header - htab->got->size;
htab->got->size = max_before_header + htab->got_header_size;
}
where = htab->got->size;
htab->got->size += need;
}
return where;
}
static bfd_boolean
allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
{
struct bfd_link_info *info = inf;
struct ppc_elf_link_hash_entry *eh;
struct ppc_elf_link_hash_table *htab;
struct ppc_elf_dyn_relocs *p;
if (h->root.type == bfd_link_hash_indirect)
return TRUE;
if (h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
htab = ppc_elf_hash_table (info);
if (htab->elf.dynamic_sections_created)
{
struct plt_entry *ent;
bfd_boolean doneone = FALSE;
bfd_vma plt_offset = 0, glink_offset = 0;
for (ent = h->plt.plist; ent != NULL; ent = ent->next)
if (ent->plt.refcount > 0)
{
if (h->dynindx == -1
&& !h->forced_local)
{
if (! bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
}
if (info->shared
|| WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, 0, h))
{
asection *s = htab->plt;
if (!(htab->old_plt || htab->is_vxworks))
{
if (!doneone)
{
plt_offset = s->size;
s->size += 4;
}
ent->plt.offset = plt_offset;
s = htab->glink;
if (!doneone || info->shared || info->pie)
{
glink_offset = s->size;
s->size += GLINK_ENTRY_SIZE;
}
if (!doneone
&& !info->shared
&& !h->def_regular)
{
h->root.u.def.section = s;
h->root.u.def.value = glink_offset;
}
ent->glink_offset = glink_offset;
}
else
{
if (!doneone)
{
if (s->size == 0)
s->size += htab->plt_initial_entry_size;
plt_offset = (htab->plt_initial_entry_size
+ (htab->plt_slot_size
* ((s->size
- htab->plt_initial_entry_size)
/ htab->plt_entry_size)));
if (! info->shared
&& !h->def_regular)
{
h->root.u.def.section = s;
h->root.u.def.value = plt_offset;
}
s->size += htab->plt_entry_size;
if (!htab->is_vxworks
&& (s->size - htab->plt_initial_entry_size)
/ htab->plt_entry_size
> PLT_NUM_SINGLE_ENTRIES)
s->size += htab->plt_entry_size;
}
ent->plt.offset = plt_offset;
}
if (!doneone)
{
htab->relplt->size += sizeof (Elf32_External_Rela);
if (htab->is_vxworks)
{
if (!info->shared)
{
if (ent->plt.offset
== (bfd_vma) htab->plt_initial_entry_size)
{
htab->srelplt2->size
+= sizeof (Elf32_External_Rela)
* VXWORKS_PLTRESOLVE_RELOCS;
}
htab->srelplt2->size
+= sizeof (Elf32_External_Rela)
* VXWORKS_PLT_NON_JMP_SLOT_RELOCS;
}
htab->sgotplt->size += 4;
}
doneone = TRUE;
}
}
else
ent->plt.offset = (bfd_vma) -1;
if (!doneone)
{
h->plt.plist = NULL;
h->needs_plt = 0;
}
}
}
else
{
h->plt.plist = NULL;
h->needs_plt = 0;
}
eh = (struct ppc_elf_link_hash_entry *) h;
if (eh->elf.got.refcount > 0)
{
if (eh->elf.dynindx == -1
&& !eh->elf.forced_local)
{
if (!bfd_elf_link_record_dynamic_symbol (info, &eh->elf))
return FALSE;
}
if (eh->tls_mask == (TLS_TLS | TLS_LD)
&& !eh->elf.def_dynamic)
eh->elf.got.offset = (bfd_vma) -1;
else
{
bfd_boolean dyn;
unsigned int need = 0;
if ((eh->tls_mask & TLS_TLS) != 0)
{
if ((eh->tls_mask & TLS_LD) != 0)
need += 8;
if ((eh->tls_mask & TLS_GD) != 0)
need += 8;
if ((eh->tls_mask & (TLS_TPREL | TLS_TPRELGD)) != 0)
need += 4;
if ((eh->tls_mask & TLS_DTPREL) != 0)
need += 4;
}
else
need += 4;
eh->elf.got.offset = allocate_got (htab, need);
dyn = htab->elf.dynamic_sections_created;
if ((info->shared
|| WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, &eh->elf))
&& (ELF_ST_VISIBILITY (eh->elf.other) == STV_DEFAULT
|| eh->elf.root.type != bfd_link_hash_undefweak))
{
if ((eh->tls_mask & TLS_LD) != 0)
need -= 4;
htab->relgot->size += need * (sizeof (Elf32_External_Rela) / 4);
}
}
}
else
eh->elf.got.offset = (bfd_vma) -1;
if (eh->dyn_relocs == NULL)
return TRUE;
if (info->shared)
{
if (SYMBOL_CALLS_LOCAL (info, h))
{
struct ppc_elf_dyn_relocs **pp;
for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
{
p->count -= p->pc_count;
p->pc_count = 0;
if (p->count == 0)
*pp = p->next;
else
pp = &p->next;
}
}
if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
&& h->root.type == bfd_link_hash_undefweak)
eh->dyn_relocs = NULL;
if (info->pie
&& eh->dyn_relocs != NULL
&& h->dynindx == -1
&& h->root.type == bfd_link_hash_undefweak
&& !h->forced_local)
{
if (! bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
}
}
else if (ELIMINATE_COPY_RELOCS)
{
if (!h->non_got_ref
&& h->def_dynamic
&& !h->def_regular)
{
if (h->dynindx == -1
&& !h->forced_local)
{
if (! bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
}
if (h->dynindx != -1)
goto keep;
}
eh->dyn_relocs = NULL;
keep: ;
}
for (p = eh->dyn_relocs; p != NULL; p = p->next)
{
asection *sreloc = elf_section_data (p->sec)->sreloc;
sreloc->size += p->count * sizeof (Elf32_External_Rela);
}
return TRUE;
}
static bfd_boolean
readonly_dynrelocs (struct elf_link_hash_entry *h, void *info)
{
struct ppc_elf_dyn_relocs *p;
if (h->root.type == bfd_link_hash_indirect)
return TRUE;
if (h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
for (p = ppc_elf_hash_entry (h)->dyn_relocs; p != NULL; p = p->next)
{
asection *s = p->sec->output_section;
if (s != NULL
&& ((s->flags & (SEC_READONLY | SEC_ALLOC))
== (SEC_READONLY | SEC_ALLOC)))
{
((struct bfd_link_info *) info)->flags |= DF_TEXTREL;
return FALSE;
}
}
return TRUE;
}
static bfd_boolean
ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
struct bfd_link_info *info)
{
struct ppc_elf_link_hash_table *htab;
asection *s;
bfd_boolean relocs;
bfd *ibfd;
#ifdef DEBUG
fprintf (stderr, "ppc_elf_size_dynamic_sections called\n");
#endif
htab = ppc_elf_hash_table (info);
BFD_ASSERT (htab->elf.dynobj != NULL);
if (elf_hash_table (info)->dynamic_sections_created)
{
if (info->executable)
{
s = bfd_get_section_by_name (htab->elf.dynobj, ".interp");
BFD_ASSERT (s != NULL);
s->size = sizeof ELF_DYNAMIC_INTERPRETER;
s->contents = (unsigned char *) ELF_DYNAMIC_INTERPRETER;
}
}
if (htab->old_plt)
htab->got_header_size = 16;
else
htab->got_header_size = 12;
for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
{
bfd_signed_vma *local_got;
bfd_signed_vma *end_local_got;
char *lgot_masks;
bfd_size_type locsymcount;
Elf_Internal_Shdr *symtab_hdr;
if (!is_ppc_elf_target (ibfd->xvec))
continue;
for (s = ibfd->sections; s != NULL; s = s->next)
{
struct ppc_elf_dyn_relocs *p;
for (p = ((struct ppc_elf_dyn_relocs *)
elf_section_data (s)->local_dynrel);
p != NULL;
p = p->next)
{
if (!bfd_is_abs_section (p->sec)
&& bfd_is_abs_section (p->sec->output_section))
{
}
else if (p->count != 0)
{
elf_section_data (p->sec)->sreloc->size
+= p->count * sizeof (Elf32_External_Rela);
if ((p->sec->output_section->flags
& (SEC_READONLY | SEC_ALLOC))
== (SEC_READONLY | SEC_ALLOC))
info->flags |= DF_TEXTREL;
}
}
}
local_got = elf_local_got_refcounts (ibfd);
if (!local_got)
continue;
symtab_hdr = &elf_tdata (ibfd)->symtab_hdr;
locsymcount = symtab_hdr->sh_info;
end_local_got = local_got + locsymcount;
lgot_masks = (char *) end_local_got;
for (; local_got < end_local_got; ++local_got, ++lgot_masks)
if (*local_got > 0)
{
if (*lgot_masks == (TLS_TLS | TLS_LD))
{
htab->tlsld_got.refcount += 1;
*local_got = (bfd_vma) -1;
}
else
{
unsigned int need = 0;
if ((*lgot_masks & TLS_TLS) != 0)
{
if ((*lgot_masks & TLS_GD) != 0)
need += 8;
if ((*lgot_masks & (TLS_TPREL | TLS_TPRELGD)) != 0)
need += 4;
if ((*lgot_masks & TLS_DTPREL) != 0)
need += 4;
}
else
need += 4;
*local_got = allocate_got (htab, need);
if (info->shared)
htab->relgot->size += (need
* (sizeof (Elf32_External_Rela) / 4));
}
}
else
*local_got = (bfd_vma) -1;
}
if (htab->tlsld_got.refcount > 0)
{
htab->tlsld_got.offset = allocate_got (htab, 8);
if (info->shared)
htab->relgot->size += sizeof (Elf32_External_Rela);
}
else
htab->tlsld_got.offset = (bfd_vma) -1;
if (htab->is_vxworks)
{
if (htab->elf.hgot)
htab->elf.hgot->indx = -2;
htab->hplt = elf_link_hash_lookup (elf_hash_table (info),
"_PROCEDURE_LINKAGE_TABLE_",
FALSE, FALSE, FALSE);
if (htab->hplt)
htab->hplt->indx = -2;
if (htab->hplt && htab->plt->flags & SEC_CODE)
htab->hplt->type = STT_FUNC;
}
elf_link_hash_traverse (elf_hash_table (info), allocate_dynrelocs, info);
if (htab->got != NULL && !htab->is_vxworks)
{
unsigned int g_o_t = 32768;
if (htab->got->size <= 32768)
{
g_o_t = htab->got->size;
if (htab->old_plt)
g_o_t += 4;
htab->got->size += htab->got_header_size;
}
htab->elf.hgot->root.u.def.value = g_o_t;
}
if (htab->glink != NULL && htab->glink->size != 0)
{
htab->glink_pltresolve = htab->glink->size;
htab->glink->size += htab->glink->size / (GLINK_ENTRY_SIZE / 4) - 4;
htab->glink->size += -htab->glink->size & 15;
htab->glink->size += GLINK_PLTRESOLVE;
}
relocs = FALSE;
for (s = htab->elf.dynobj->sections; s != NULL; s = s->next)
{
bfd_boolean strip_section = TRUE;
if ((s->flags & SEC_LINKER_CREATED) == 0)
continue;
if (s == htab->plt
|| s == htab->glink
|| s == htab->got
|| s == htab->sgotplt
|| s == htab->sbss
|| s == htab->dynbss
|| s == htab->dynsbss)
{
if ((s == htab->plt || s == htab->got) && htab->hplt != NULL)
strip_section = FALSE;
}
else if (s == htab->sdata[0].section
|| s == htab->sdata[1].section)
{
}
else if (strncmp (bfd_get_section_name (dynobj, s), ".rela", 5) == 0)
{
if (s->size != 0)
{
relocs = TRUE;
s->reloc_count = 0;
}
}
else
{
continue;
}
if (s->size == 0 && strip_section)
{
s->flags |= SEC_EXCLUDE;
continue;
}
if ((s->flags & SEC_HAS_CONTENTS) == 0)
continue;
s->contents = bfd_zalloc (htab->elf.dynobj, s->size);
if (s->contents == NULL)
return FALSE;
}
if (htab->elf.dynamic_sections_created)
{
#define add_dynamic_entry(TAG, VAL) \
_bfd_elf_add_dynamic_entry (info, TAG, VAL)
if (info->executable)
{
if (!add_dynamic_entry (DT_DEBUG, 0))
return FALSE;
}
if (htab->plt != NULL && htab->plt->size != 0)
{
if (!add_dynamic_entry (DT_PLTGOT, 0)
|| !add_dynamic_entry (DT_PLTRELSZ, 0)
|| !add_dynamic_entry (DT_PLTREL, DT_RELA)
|| !add_dynamic_entry (DT_JMPREL, 0))
return FALSE;
}
if (htab->glink != NULL && htab->glink->size != 0)
{
if (!add_dynamic_entry (DT_PPC_GOT, 0))
return FALSE;
}
if (relocs)
{
if (!add_dynamic_entry (DT_RELA, 0)
|| !add_dynamic_entry (DT_RELASZ, 0)
|| !add_dynamic_entry (DT_RELAENT, sizeof (Elf32_External_Rela)))
return FALSE;
}
if ((info->flags & DF_TEXTREL) == 0)
elf_link_hash_traverse (elf_hash_table (info), readonly_dynrelocs,
info);
if ((info->flags & DF_TEXTREL) != 0)
{
if (!add_dynamic_entry (DT_TEXTREL, 0))
return FALSE;
}
}
#undef add_dynamic_entry
return TRUE;
}
#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
static const int shared_stub_entry[] =
{
0x7c0802a6,
0x429f0005,
0x7d6802a6,
0x3d6b0000,
0x396b0018,
0x7c0803a6,
0x7d6903a6,
0x4e800420,
};
static const int stub_entry[] =
{
0x3d600000,
0x396b0000,
0x7d6903a6,
0x4e800420,
};
static bfd_boolean
ppc_elf_relax_section (bfd *abfd,
asection *isec,
struct bfd_link_info *link_info,
bfd_boolean *again)
{
struct one_fixup
{
struct one_fixup *next;
asection *tsec;
bfd_vma toff;
bfd_vma trampoff;
};
Elf_Internal_Shdr *symtab_hdr;
bfd_byte *contents = NULL;
Elf_Internal_Sym *isymbuf = NULL;
Elf_Internal_Rela *internal_relocs = NULL;
Elf_Internal_Rela *irel, *irelend;
struct one_fixup *fixups = NULL;
bfd_boolean changed;
struct ppc_elf_link_hash_table *htab;
bfd_size_type trampoff;
asection *got2;
*again = FALSE;
if ((isec->flags & SEC_ALLOC) == 0
|| (isec->flags & SEC_RELOC) == 0
|| isec->reloc_count == 0)
return TRUE;
trampoff = (isec->size + 3) & (bfd_vma) -4;
trampoff += 4;
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
internal_relocs = _bfd_elf_link_read_relocs (abfd, isec, NULL, NULL,
link_info->keep_memory);
if (internal_relocs == NULL)
goto error_return;
htab = ppc_elf_hash_table (link_info);
got2 = bfd_get_section_by_name (abfd, ".got2");
irelend = internal_relocs + isec->reloc_count;
for (irel = internal_relocs; irel < irelend; irel++)
{
unsigned long r_type = ELF32_R_TYPE (irel->r_info);
bfd_vma symaddr, reladdr, toff, roff;
asection *tsec;
struct one_fixup *f;
size_t insn_offset = 0;
bfd_vma max_branch_offset, val;
bfd_byte *hit_addr;
unsigned long t0;
unsigned char sym_type;
switch (r_type)
{
case R_PPC_REL24:
case R_PPC_LOCAL24PC:
case R_PPC_PLTREL24:
max_branch_offset = 1 << 25;
break;
case R_PPC_REL14:
case R_PPC_REL14_BRTAKEN:
case R_PPC_REL14_BRNTAKEN:
max_branch_offset = 1 << 15;
break;
default:
continue;
}
if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
{
Elf_Internal_Sym *isym;
if (isymbuf == NULL)
{
isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
if (isymbuf == NULL)
isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr,
symtab_hdr->sh_info, 0,
NULL, NULL, NULL);
if (isymbuf == 0)
goto error_return;
}
isym = isymbuf + ELF32_R_SYM (irel->r_info);
if (isym->st_shndx == SHN_UNDEF)
continue;
else if (isym->st_shndx == SHN_ABS)
tsec = bfd_abs_section_ptr;
else if (isym->st_shndx == SHN_COMMON)
tsec = bfd_com_section_ptr;
else
tsec = bfd_section_from_elf_index (abfd, isym->st_shndx);
toff = isym->st_value;
sym_type = ELF_ST_TYPE (isym->st_info);
}
else
{
unsigned long indx;
struct elf_link_hash_entry *h;
indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info;
h = elf_sym_hashes (abfd)[indx];
while (h->root.type == bfd_link_hash_indirect
|| h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
tsec = NULL;
toff = 0;
if (r_type == R_PPC_PLTREL24
&& htab->plt != NULL)
{
struct plt_entry *ent = find_plt_ent (h, got2, irel->r_addend);
if (ent != NULL)
{
if (!htab->old_plt)
{
tsec = htab->glink;
toff = ent->glink_offset;
}
else
{
tsec = htab->plt;
toff = ent->plt.offset;
}
}
}
if (tsec != NULL)
;
else if (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak)
{
tsec = h->root.u.def.section;
toff = h->root.u.def.value;
}
else
continue;
sym_type = h->type;
}
if (tsec == isec)
continue;
if (0 && tsec->sec_info_type == ELF_INFO_TYPE_MERGE)
{
if (sym_type == STT_SECTION)
toff += irel->r_addend;
toff = _bfd_merged_section_offset (abfd, &tsec,
elf_section_data (tsec)->sec_info,
toff);
if (sym_type != STT_SECTION)
toff += irel->r_addend;
}
else if (r_type != R_PPC_PLTREL24)
toff += irel->r_addend;
symaddr = tsec->output_section->vma + tsec->output_offset + toff;
roff = irel->r_offset;
reladdr = isec->output_section->vma + isec->output_offset + roff;
if (symaddr - reladdr + max_branch_offset < 2 * max_branch_offset)
continue;
for (f = fixups; f ; f = f->next)
if (f->tsec == tsec && f->toff == toff)
break;
if (f == NULL)
{
size_t size;
unsigned long stub_rtype;
val = trampoff - roff;
if (val >= max_branch_offset)
continue;
if (link_info->shared)
{
size = 4 * ARRAY_SIZE (shared_stub_entry);
insn_offset = 12;
stub_rtype = R_PPC_RELAX32PC;
}
else
{
size = 4 * ARRAY_SIZE (stub_entry);
insn_offset = 0;
stub_rtype = R_PPC_RELAX32;
}
if (R_PPC_RELAX32_PLT - R_PPC_RELAX32
!= R_PPC_RELAX32PC_PLT - R_PPC_RELAX32PC)
abort ();
if (tsec == htab->plt
|| tsec == htab->glink)
stub_rtype += R_PPC_RELAX32_PLT - R_PPC_RELAX32;
irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info),
stub_rtype);
irel->r_offset = trampoff + insn_offset;
f = bfd_malloc (sizeof (*f));
f->next = fixups;
f->tsec = tsec;
f->toff = toff;
f->trampoff = trampoff;
fixups = f;
trampoff += size;
}
else
{
val = f->trampoff - roff;
if (val >= max_branch_offset)
continue;
irel->r_info = ELF32_R_INFO (0, R_PPC_NONE);
}
if (contents == NULL)
{
if (elf_section_data (isec)->this_hdr.contents != NULL)
contents = elf_section_data (isec)->this_hdr.contents;
else
{
if (!bfd_malloc_and_get_section (abfd, isec, &contents))
goto error_return;
}
}
hit_addr = contents + roff;
switch (r_type)
{
case R_PPC_REL24:
case R_PPC_LOCAL24PC:
case R_PPC_PLTREL24:
t0 = bfd_get_32 (abfd, hit_addr);
t0 &= ~0x3fffffc;
t0 |= val & 0x3fffffc;
bfd_put_32 (abfd, t0, hit_addr);
break;
case R_PPC_REL14:
case R_PPC_REL14_BRTAKEN:
case R_PPC_REL14_BRNTAKEN:
t0 = bfd_get_32 (abfd, hit_addr);
t0 &= ~0xfffc;
t0 |= val & 0xfffc;
bfd_put_32 (abfd, t0, hit_addr);
break;
}
}
changed = fixups != NULL;
if (fixups != NULL)
{
const int *stub;
bfd_byte *dest;
bfd_vma val;
int i, size;
do
{
struct one_fixup *f = fixups;
fixups = fixups->next;
free (f);
}
while (fixups);
contents = bfd_realloc (contents, trampoff);
if (contents == NULL)
goto error_return;
isec->size = (isec->size + 3) & (bfd_vma) -4;
val = trampoff - isec->size + 0x48000000;
dest = contents + isec->size;
isec->size = trampoff;
bfd_put_32 (abfd, val, dest);
dest += 4;
if (link_info->shared)
{
stub = shared_stub_entry;
size = ARRAY_SIZE (shared_stub_entry);
}
else
{
stub = stub_entry;
size = ARRAY_SIZE (stub_entry);
}
i = 0;
while (dest < contents + trampoff)
{
bfd_put_32 (abfd, stub[i], dest);
i++;
if (i == size)
i = 0;
dest += 4;
}
BFD_ASSERT (i == 0);
}
if (isymbuf != NULL
&& symtab_hdr->contents != (unsigned char *) isymbuf)
{
if (! link_info->keep_memory)
free (isymbuf);
else
{
symtab_hdr->contents = (unsigned char *) isymbuf;
}
}
if (contents != NULL
&& elf_section_data (isec)->this_hdr.contents != contents)
{
if (!changed && !link_info->keep_memory)
free (contents);
else
{
elf_section_data (isec)->this_hdr.contents = contents;
}
}
if (elf_section_data (isec)->relocs != internal_relocs)
{
if (!changed)
free (internal_relocs);
else
elf_section_data (isec)->relocs = internal_relocs;
}
*again = changed;
return TRUE;
error_return:
if (isymbuf != NULL && (unsigned char *) isymbuf != symtab_hdr->contents)
free (isymbuf);
if (contents != NULL
&& elf_section_data (isec)->this_hdr.contents != contents)
free (contents);
if (internal_relocs != NULL
&& elf_section_data (isec)->relocs != internal_relocs)
free (internal_relocs);
return FALSE;
}
static unsigned int
ppc_elf_action_discarded (asection *sec)
{
if (strcmp (".fixup", sec->name) == 0)
return 0;
if (strcmp (".got2", sec->name) == 0)
return 0;
return _bfd_elf_default_action_discarded (sec);
}
static bfd_vma
elf_finish_pointer_linker_section (bfd *input_bfd,
elf_linker_section_t *lsect,
struct elf_link_hash_entry *h,
bfd_vma relocation,
const Elf_Internal_Rela *rel)
{
elf_linker_section_pointers_t *linker_section_ptr;
BFD_ASSERT (lsect != NULL);
if (h != NULL)
{
struct ppc_elf_link_hash_entry *eh;
eh = (struct ppc_elf_link_hash_entry *) h;
BFD_ASSERT (eh->elf.def_regular);
linker_section_ptr = eh->linker_section_pointer;
}
else
{
unsigned long r_symndx = ELF32_R_SYM (rel->r_info);
BFD_ASSERT (elf_local_ptr_offsets (input_bfd) != NULL);
linker_section_ptr = elf_local_ptr_offsets (input_bfd)[r_symndx];
}
linker_section_ptr = elf_find_pointer_linker_section (linker_section_ptr,
rel->r_addend,
lsect);
BFD_ASSERT (linker_section_ptr != NULL);
if ((linker_section_ptr->offset & 1) == 0)
{
bfd_put_32 (lsect->section->owner,
relocation + linker_section_ptr->addend,
lsect->section->contents + linker_section_ptr->offset);
linker_section_ptr->offset += 1;
}
relocation = (lsect->section->output_offset
+ linker_section_ptr->offset - 1
- 0x8000);
#ifdef DEBUG
fprintf (stderr,
"Finish pointer in linker section %s, offset = %ld (0x%lx)\n",
lsect->name, (long) relocation, (long) relocation);
#endif
return relocation - linker_section_ptr->addend;
}
static bfd_boolean
ppc_elf_relocate_section (bfd *output_bfd,
struct bfd_link_info *info,
bfd *input_bfd,
asection *input_section,
bfd_byte *contents,
Elf_Internal_Rela *relocs,
Elf_Internal_Sym *local_syms,
asection **local_sections)
{
Elf_Internal_Shdr *symtab_hdr;
struct elf_link_hash_entry **sym_hashes;
struct ppc_elf_link_hash_table *htab;
Elf_Internal_Rela *rel;
Elf_Internal_Rela *relend;
Elf_Internal_Rela outrel;
bfd_byte *loc;
asection *got2, *sreloc = NULL;
bfd_vma *local_got_offsets;
bfd_boolean ret = TRUE;
#ifdef DEBUG
_bfd_error_handler ("ppc_elf_relocate_section called for %B section %A, "
"%ld relocations%s",
input_bfd, input_section,
(long) input_section->reloc_count,
(info->relocatable) ? " (relocatable)" : "");
#endif
got2 = bfd_get_section_by_name (input_bfd, ".got2");
if (info->relocatable)
{
if (got2 == NULL)
return TRUE;
rel = relocs;
relend = relocs + input_section->reloc_count;
for (; rel < relend; rel++)
{
enum elf_ppc_reloc_type r_type;
r_type = ELF32_R_TYPE (rel->r_info);
if (r_type == R_PPC_PLTREL24
&& rel->r_addend >= 32768)
{
rel->r_addend += got2->output_offset;
}
}
return TRUE;
}
if (!ppc_elf_howto_table[R_PPC_ADDR32])
ppc_elf_howto_init ();
htab = ppc_elf_hash_table (info);
local_got_offsets = elf_local_got_offsets (input_bfd);
symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
sym_hashes = elf_sym_hashes (input_bfd);
rel = relocs;
relend = relocs + input_section->reloc_count;
for (; rel < relend; rel++)
{
enum elf_ppc_reloc_type r_type;
bfd_vma addend;
bfd_reloc_status_type r;
Elf_Internal_Sym *sym;
asection *sec;
struct elf_link_hash_entry *h;
const char *sym_name;
reloc_howto_type *howto;
unsigned long r_symndx;
bfd_vma relocation;
bfd_vma branch_bit, insn, from;
bfd_boolean unresolved_reloc;
bfd_boolean warned;
unsigned int tls_type, tls_mask, tls_gd;
r_type = ELF32_R_TYPE (rel->r_info);
sym = NULL;
sec = NULL;
h = NULL;
unresolved_reloc = FALSE;
warned = FALSE;
r_symndx = ELF32_R_SYM (rel->r_info);
if (r_symndx < symtab_hdr->sh_info)
{
sym = local_syms + r_symndx;
sec = local_sections[r_symndx];
sym_name = bfd_elf_sym_name (input_bfd, symtab_hdr, sym, sec);
relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
}
else
{
RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
r_symndx, symtab_hdr, sym_hashes,
h, sec, relocation,
unresolved_reloc, warned);
sym_name = h->root.root.string;
}
tls_mask = 0;
tls_gd = 0;
if (IS_PPC_TLS_RELOC (r_type))
{
if (h != NULL)
tls_mask = ((struct ppc_elf_link_hash_entry *) h)->tls_mask;
else if (local_got_offsets != NULL)
{
char *lgot_masks;
lgot_masks = (char *) (local_got_offsets + symtab_hdr->sh_info);
tls_mask = lgot_masks[r_symndx];
}
}
if ((R_PPC_GOT_TLSLD16 & 3) != (R_PPC_GOT_TLSGD16 & 3)
|| (R_PPC_GOT_TLSLD16_LO & 3) != (R_PPC_GOT_TLSGD16_LO & 3)
|| (R_PPC_GOT_TLSLD16_HI & 3) != (R_PPC_GOT_TLSGD16_HI & 3)
|| (R_PPC_GOT_TLSLD16_HA & 3) != (R_PPC_GOT_TLSGD16_HA & 3)
|| (R_PPC_GOT_TLSLD16 & 3) != (R_PPC_GOT_TPREL16 & 3)
|| (R_PPC_GOT_TLSLD16_LO & 3) != (R_PPC_GOT_TPREL16_LO & 3)
|| (R_PPC_GOT_TLSLD16_HI & 3) != (R_PPC_GOT_TPREL16_HI & 3)
|| (R_PPC_GOT_TLSLD16_HA & 3) != (R_PPC_GOT_TPREL16_HA & 3))
abort ();
switch (r_type)
{
default:
break;
case R_PPC_GOT_TPREL16:
case R_PPC_GOT_TPREL16_LO:
if (tls_mask != 0
&& (tls_mask & TLS_TPREL) == 0)
{
bfd_vma insn;
insn = bfd_get_32 (output_bfd, contents + rel->r_offset - 2);
insn &= 31 << 21;
insn |= 0x3c020000;
bfd_put_32 (output_bfd, insn, contents + rel->r_offset - 2);
r_type = R_PPC_TPREL16_HA;
rel->r_info = ELF32_R_INFO (r_symndx, r_type);
}
break;
case R_PPC_TLS:
if (tls_mask != 0
&& (tls_mask & TLS_TPREL) == 0)
{
bfd_vma insn, rtra;
insn = bfd_get_32 (output_bfd, contents + rel->r_offset);
if ((insn & ((31 << 26) | (31 << 11)))
== ((31 << 26) | (2 << 11)))
rtra = insn & ((1 << 26) - (1 << 16));
else if ((insn & ((31 << 26) | (31 << 16)))
== ((31 << 26) | (2 << 16)))
rtra = (insn & (31 << 21)) | ((insn & (31 << 11)) << 5);
else
abort ();
if ((insn & ((1 << 11) - (1 << 1))) == 266 << 1)
insn = 14 << 26;
else if ((insn & (31 << 1)) == 23 << 1
&& ((insn & (31 << 6)) < 14 << 6
|| ((insn & (31 << 6)) >= 16 << 6
&& (insn & (31 << 6)) < 24 << 6)))
insn = (32 | ((insn >> 6) & 31)) << 26;
else if ((insn & (31 << 1)) == 21 << 1
&& (insn & (0x1a << 6)) == 0)
insn = (((58 | ((insn >> 6) & 4)) << 26)
| ((insn >> 6) & 1));
else if ((insn & (31 << 1)) == 21 << 1
&& (insn & ((1 << 11) - (1 << 1))) == 341 << 1)
insn = (58 << 26) | 2;
else
abort ();
insn |= rtra;
bfd_put_32 (output_bfd, insn, contents + rel->r_offset);
r_type = R_PPC_TPREL16_LO;
rel->r_info = ELF32_R_INFO (r_symndx, r_type);
rel->r_offset += 2;
}
break;
case R_PPC_GOT_TLSGD16_HI:
case R_PPC_GOT_TLSGD16_HA:
tls_gd = TLS_TPRELGD;
if (tls_mask != 0 && (tls_mask & TLS_GD) == 0)
goto tls_gdld_hi;
break;
case R_PPC_GOT_TLSLD16_HI:
case R_PPC_GOT_TLSLD16_HA:
if (tls_mask != 0 && (tls_mask & TLS_LD) == 0)
{
tls_gdld_hi:
if ((tls_mask & tls_gd) != 0)
r_type = (((r_type - (R_PPC_GOT_TLSGD16 & 3)) & 3)
+ R_PPC_GOT_TPREL16);
else
{
bfd_put_32 (output_bfd, NOP, contents + rel->r_offset);
rel->r_offset -= 2;
r_type = R_PPC_NONE;
}
rel->r_info = ELF32_R_INFO (r_symndx, r_type);
}
break;
case R_PPC_GOT_TLSGD16:
case R_PPC_GOT_TLSGD16_LO:
tls_gd = TLS_TPRELGD;
if (tls_mask != 0 && (tls_mask & TLS_GD) == 0)
goto tls_get_addr_check;
break;
case R_PPC_GOT_TLSLD16:
case R_PPC_GOT_TLSLD16_LO:
if (tls_mask != 0 && (tls_mask & TLS_LD) == 0)
{
tls_get_addr_check:
if (rel + 1 < relend)
{
enum elf_ppc_reloc_type r_type2;
unsigned long r_symndx2;
struct elf_link_hash_entry *h2;
bfd_vma insn1, insn2;
bfd_vma offset;
r_type2 = ELF32_R_TYPE (rel[1].r_info);
r_symndx2 = ELF32_R_SYM (rel[1].r_info);
if (r_symndx2 < symtab_hdr->sh_info
|| (r_type2 != R_PPC_REL14
&& r_type2 != R_PPC_REL14_BRTAKEN
&& r_type2 != R_PPC_REL14_BRNTAKEN
&& r_type2 != R_PPC_REL24
&& r_type2 != R_PPC_PLTREL24))
break;
h2 = sym_hashes[r_symndx2 - symtab_hdr->sh_info];
while (h2->root.type == bfd_link_hash_indirect
|| h2->root.type == bfd_link_hash_warning)
h2 = (struct elf_link_hash_entry *) h2->root.u.i.link;
if (h2 == NULL || h2 != htab->tls_get_addr)
break;
offset = rel[1].r_offset;
insn1 = bfd_get_32 (output_bfd,
contents + rel->r_offset - 2);
if ((tls_mask & tls_gd) != 0)
{
insn1 &= (1 << 26) - 1;
insn1 |= 32 << 26;
insn2 = 0x7c631214;
rel[1].r_info = ELF32_R_INFO (r_symndx2, R_PPC_NONE);
r_type = (((r_type - (R_PPC_GOT_TLSGD16 & 3)) & 3)
+ R_PPC_GOT_TPREL16);
rel->r_info = ELF32_R_INFO (r_symndx, r_type);
}
else
{
insn1 = 0x3c620000;
insn2 = 0x38630000;
if (tls_gd == 0)
{
r_symndx = 0;
rel->r_addend = htab->elf.tls_sec->vma + DTP_OFFSET;
rel[1].r_addend = htab->elf.tls_sec->vma + DTP_OFFSET;
}
r_type = R_PPC_TPREL16_HA;
rel->r_info = ELF32_R_INFO (r_symndx, r_type);
rel[1].r_info = ELF32_R_INFO (r_symndx,
R_PPC_TPREL16_LO);
rel[1].r_offset += 2;
}
bfd_put_32 (output_bfd, insn1, contents + rel->r_offset - 2);
bfd_put_32 (output_bfd, insn2, contents + offset);
if (tls_gd == 0)
{
rel--;
continue;
}
}
}
break;
}
branch_bit = 0;
switch (r_type)
{
default:
break;
case R_PPC_ADDR14_BRTAKEN:
case R_PPC_REL14_BRTAKEN:
branch_bit = BRANCH_PREDICT_BIT;
case R_PPC_ADDR14_BRNTAKEN:
case R_PPC_REL14_BRNTAKEN:
insn = bfd_get_32 (output_bfd, contents + rel->r_offset);
insn &= ~BRANCH_PREDICT_BIT;
insn |= branch_bit;
from = (rel->r_offset
+ input_section->output_offset
+ input_section->output_section->vma);
if ((bfd_signed_vma) (relocation + rel->r_addend - from) < 0)
insn ^= BRANCH_PREDICT_BIT;
bfd_put_32 (output_bfd, insn, contents + rel->r_offset);
break;
}
addend = rel->r_addend;
tls_type = 0;
howto = NULL;
if (r_type < R_PPC_max)
howto = ppc_elf_howto_table[r_type];
switch (r_type)
{
default:
(*_bfd_error_handler)
(_("%B: unknown relocation type %d for symbol %s"),
input_bfd, (int) r_type, sym_name);
bfd_set_error (bfd_error_bad_value);
ret = FALSE;
continue;
case R_PPC_NONE:
case R_PPC_TLS:
case R_PPC_EMB_MRKREF:
case R_PPC_GNU_VTINHERIT:
case R_PPC_GNU_VTENTRY:
continue;
case R_PPC_GOT_TLSGD16:
case R_PPC_GOT_TLSGD16_LO:
case R_PPC_GOT_TLSGD16_HI:
case R_PPC_GOT_TLSGD16_HA:
tls_type = TLS_TLS | TLS_GD;
goto dogot;
case R_PPC_GOT_TLSLD16:
case R_PPC_GOT_TLSLD16_LO:
case R_PPC_GOT_TLSLD16_HI:
case R_PPC_GOT_TLSLD16_HA:
tls_type = TLS_TLS | TLS_LD;
goto dogot;
case R_PPC_GOT_TPREL16:
case R_PPC_GOT_TPREL16_LO:
case R_PPC_GOT_TPREL16_HI:
case R_PPC_GOT_TPREL16_HA:
tls_type = TLS_TLS | TLS_TPREL;
goto dogot;
case R_PPC_GOT_DTPREL16:
case R_PPC_GOT_DTPREL16_LO:
case R_PPC_GOT_DTPREL16_HI:
case R_PPC_GOT_DTPREL16_HA:
tls_type = TLS_TLS | TLS_DTPREL;
goto dogot;
case R_PPC_GOT16:
case R_PPC_GOT16_LO:
case R_PPC_GOT16_HI:
case R_PPC_GOT16_HA:
dogot:
{
bfd_vma off;
bfd_vma *offp;
unsigned long indx;
if (htab->got == NULL)
abort ();
indx = 0;
if (tls_type == (TLS_TLS | TLS_LD)
&& (h == NULL
|| !h->def_dynamic))
offp = &htab->tlsld_got.offset;
else if (h != NULL)
{
bfd_boolean dyn;
dyn = htab->elf.dynamic_sections_created;
if (! WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h)
|| (info->shared
&& SYMBOL_REFERENCES_LOCAL (info, h)))
;
else
{
indx = h->dynindx;
unresolved_reloc = FALSE;
}
offp = &h->got.offset;
}
else
{
if (local_got_offsets == NULL)
abort ();
offp = &local_got_offsets[r_symndx];
}
off = *offp;
if ((off & 1) != 0)
off &= ~1;
else
{
unsigned int tls_m = (tls_mask
& (TLS_LD | TLS_GD | TLS_DTPREL
| TLS_TPREL | TLS_TPRELGD));
if (offp == &htab->tlsld_got.offset)
tls_m = TLS_LD;
else if (h == NULL
|| !h->def_dynamic)
tls_m &= ~TLS_LD;
do
{
int tls_ty = 0;
if ((tls_m & TLS_LD) != 0)
{
tls_ty = TLS_TLS | TLS_LD;
tls_m &= ~TLS_LD;
}
else if ((tls_m & TLS_GD) != 0)
{
tls_ty = TLS_TLS | TLS_GD;
tls_m &= ~TLS_GD;
}
else if ((tls_m & TLS_DTPREL) != 0)
{
tls_ty = TLS_TLS | TLS_DTPREL;
tls_m &= ~TLS_DTPREL;
}
else if ((tls_m & (TLS_TPREL | TLS_TPRELGD)) != 0)
{
tls_ty = TLS_TLS | TLS_TPREL;
tls_m = 0;
}
if ((info->shared || indx != 0)
&& (h == NULL
|| ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
|| h->root.type != bfd_link_hash_undefweak))
{
outrel.r_offset = (htab->got->output_section->vma
+ htab->got->output_offset
+ off);
outrel.r_addend = 0;
if (tls_ty & (TLS_LD | TLS_GD))
{
outrel.r_info = ELF32_R_INFO (indx, R_PPC_DTPMOD32);
if (tls_ty == (TLS_TLS | TLS_GD))
{
loc = htab->relgot->contents;
loc += (htab->relgot->reloc_count++
* sizeof (Elf32_External_Rela));
bfd_elf32_swap_reloca_out (output_bfd,
&outrel, loc);
outrel.r_offset += 4;
outrel.r_info
= ELF32_R_INFO (indx, R_PPC_DTPREL32);
}
}
else if (tls_ty == (TLS_TLS | TLS_DTPREL))
outrel.r_info = ELF32_R_INFO (indx, R_PPC_DTPREL32);
else if (tls_ty == (TLS_TLS | TLS_TPREL))
outrel.r_info = ELF32_R_INFO (indx, R_PPC_TPREL32);
else if (indx == 0)
outrel.r_info = ELF32_R_INFO (indx, R_PPC_RELATIVE);
else
outrel.r_info = ELF32_R_INFO (indx, R_PPC_GLOB_DAT);
if (indx == 0)
{
outrel.r_addend += relocation;
if (tls_ty & (TLS_GD | TLS_DTPREL | TLS_TPREL))
outrel.r_addend -= htab->elf.tls_sec->vma;
}
loc = htab->relgot->contents;
loc += (htab->relgot->reloc_count++
* sizeof (Elf32_External_Rela));
bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
}
else
{
bfd_vma value = relocation;
if (tls_ty == (TLS_TLS | TLS_LD))
value = 1;
else if (tls_ty != 0)
{
value -= htab->elf.tls_sec->vma + DTP_OFFSET;
if (tls_ty == (TLS_TLS | TLS_TPREL))
value += DTP_OFFSET - TP_OFFSET;
if (tls_ty == (TLS_TLS | TLS_GD))
{
bfd_put_32 (output_bfd, value,
htab->got->contents + off + 4);
value = 1;
}
}
bfd_put_32 (output_bfd, value,
htab->got->contents + off);
}
off += 4;
if (tls_ty & (TLS_LD | TLS_GD))
off += 4;
}
while (tls_m != 0);
off = *offp;
*offp = off | 1;
}
if (off >= (bfd_vma) -2)
abort ();
if ((tls_type & TLS_TLS) != 0)
{
if (tls_type != (TLS_TLS | TLS_LD))
{
if ((tls_mask & TLS_LD) != 0
&& !(h == NULL
|| !h->def_dynamic))
off += 8;
if (tls_type != (TLS_TLS | TLS_GD))
{
if ((tls_mask & TLS_GD) != 0)
off += 8;
if (tls_type != (TLS_TLS | TLS_DTPREL))
{
if ((tls_mask & TLS_DTPREL) != 0)
off += 4;
}
}
}
}
relocation = htab->got->output_offset + off;
relocation -= htab->elf.hgot->root.u.def.value;
if (addend != 0)
(*_bfd_error_handler)
(_("%B(%A+0x%lx): non-zero addend on %s reloc against `%s'"),
input_bfd,
input_section,
(long) rel->r_offset,
howto->name,
sym_name);
}
break;
case R_PPC_LOCAL24PC:
if (unresolved_reloc)
{
if (! (*info->callbacks->undefined_symbol) (info,
h->root.root.string,
input_bfd,
input_section,
rel->r_offset,
TRUE))
return FALSE;
continue;
}
break;
case R_PPC_DTPREL16:
case R_PPC_DTPREL16_LO:
case R_PPC_DTPREL16_HI:
case R_PPC_DTPREL16_HA:
addend -= htab->elf.tls_sec->vma + DTP_OFFSET;
break;
case R_PPC_TPREL16:
case R_PPC_TPREL16_LO:
case R_PPC_TPREL16_HI:
case R_PPC_TPREL16_HA:
addend -= htab->elf.tls_sec->vma + TP_OFFSET;
goto dodyn;
case R_PPC_TPREL32:
addend -= htab->elf.tls_sec->vma + TP_OFFSET;
goto dodyn;
case R_PPC_DTPREL32:
addend -= htab->elf.tls_sec->vma + DTP_OFFSET;
goto dodyn;
case R_PPC_DTPMOD32:
relocation = 1;
addend = 0;
goto dodyn;
case R_PPC_REL16:
case R_PPC_REL16_LO:
case R_PPC_REL16_HI:
case R_PPC_REL16_HA:
break;
case R_PPC_REL24:
case R_PPC_REL32:
case R_PPC_REL14:
case R_PPC_REL14_BRTAKEN:
case R_PPC_REL14_BRNTAKEN:
if (SYMBOL_REFERENCES_LOCAL (info, h)
|| h == htab->elf.hgot)
break;
case R_PPC_ADDR32:
case R_PPC_ADDR24:
case R_PPC_ADDR16:
case R_PPC_ADDR16_LO:
case R_PPC_ADDR16_HI:
case R_PPC_ADDR16_HA:
case R_PPC_ADDR14:
case R_PPC_ADDR14_BRTAKEN:
case R_PPC_ADDR14_BRNTAKEN:
case R_PPC_UADDR32:
case R_PPC_UADDR16:
dodyn:
if (r_symndx == 0)
break;
if ((input_section->flags & SEC_ALLOC) == 0)
break;
if ((info->shared
&& (h == NULL
|| ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
|| h->root.type != bfd_link_hash_undefweak)
&& (MUST_BE_DYN_RELOC (r_type)
|| !SYMBOL_CALLS_LOCAL (info, h)))
|| (ELIMINATE_COPY_RELOCS
&& !info->shared
&& h != NULL
&& h->dynindx != -1
&& !h->non_got_ref
&& h->def_dynamic
&& !h->def_regular))
{
int skip;
#ifdef DEBUG
fprintf (stderr, "ppc_elf_relocate_section needs to "
"create relocation for %s\n",
(h && h->root.root.string
? h->root.root.string : "<unknown>"));
#endif
if (sreloc == NULL)
{
const char *name;
name = (bfd_elf_string_from_elf_section
(input_bfd,
elf_elfheader (input_bfd)->e_shstrndx,
elf_section_data (input_section)->rel_hdr.sh_name));
if (name == NULL)
return FALSE;
BFD_ASSERT (strncmp (name, ".rela", 5) == 0
&& strcmp (bfd_get_section_name (input_bfd,
input_section),
name + 5) == 0);
sreloc = bfd_get_section_by_name (htab->elf.dynobj, name);
BFD_ASSERT (sreloc != NULL);
}
skip = 0;
outrel.r_offset =
_bfd_elf_section_offset (output_bfd, info, input_section,
rel->r_offset);
if (outrel.r_offset == (bfd_vma) -1
|| outrel.r_offset == (bfd_vma) -2)
skip = (int) outrel.r_offset;
outrel.r_offset += (input_section->output_section->vma
+ input_section->output_offset);
if (skip)
memset (&outrel, 0, sizeof outrel);
else if (!SYMBOL_REFERENCES_LOCAL (info, h))
{
unresolved_reloc = FALSE;
outrel.r_info = ELF32_R_INFO (h->dynindx, r_type);
outrel.r_addend = rel->r_addend;
}
else
{
outrel.r_addend = relocation + rel->r_addend;
if (r_type == R_PPC_ADDR32)
outrel.r_info = ELF32_R_INFO (0, R_PPC_RELATIVE);
else
{
long indx;
if (bfd_is_abs_section (sec))
indx = 0;
else if (sec == NULL || sec->owner == NULL)
{
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
else
{
asection *osec;
osec = sec->output_section;
indx = elf_section_data (osec)->dynindx;
BFD_ASSERT (indx > 0);
#ifdef DEBUG
if (indx <= 0)
printf ("indx=%d section=%s flags=%08x name=%s\n",
indx, osec->name, osec->flags,
h->root.root.string);
#endif
}
outrel.r_info = ELF32_R_INFO (indx, r_type);
}
}
loc = sreloc->contents;
loc += sreloc->reloc_count++ * sizeof (Elf32_External_Rela);
bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
if (skip == -1)
continue;
if (! skip
&& ((input_section->flags & SEC_ALLOC) != 0
|| ELF32_R_TYPE (outrel.r_info) != R_PPC_RELATIVE))
{
relocation = howto->pc_relative ? outrel.r_offset : 0;
addend = 0;
break;
}
}
break;
case R_PPC_RELAX32PC_PLT:
case R_PPC_RELAX32_PLT:
{
struct plt_entry *ent = find_plt_ent (h, got2, addend);
if (!htab->old_plt)
relocation = (htab->glink->output_section->vma
+ htab->glink->output_offset
+ ent->glink_offset);
else
relocation = (htab->plt->output_section->vma
+ htab->plt->output_offset
+ ent->plt.offset);
addend = 0;
}
if (r_type == R_PPC_RELAX32_PLT)
goto relax32;
case R_PPC_RELAX32PC:
relocation -= (input_section->output_section->vma
+ input_section->output_offset
+ rel->r_offset - 4);
case R_PPC_RELAX32:
relax32:
{
unsigned long t0;
unsigned long t1;
t0 = bfd_get_32 (output_bfd, contents + rel->r_offset);
t1 = bfd_get_32 (output_bfd, contents + rel->r_offset + 4);
t0 &= ~0xffff;
t1 &= ~0xffff;
relocation += addend;
t0 |= ((relocation + 0x8000) >> 16) & 0xffff;
t1 |= relocation & 0xffff;
bfd_put_32 (output_bfd, t0, contents + rel->r_offset);
bfd_put_32 (output_bfd, t1, contents + rel->r_offset + 4);
}
continue;
case R_PPC_EMB_SDAI16:
BFD_ASSERT (htab->sdata[0].section != NULL);
relocation
= elf_finish_pointer_linker_section (input_bfd, &htab->sdata[0],
h, relocation, rel);
break;
case R_PPC_EMB_SDA2I16:
BFD_ASSERT (htab->sdata[1].section != NULL);
relocation
= elf_finish_pointer_linker_section (input_bfd, &htab->sdata[1],
h, relocation, rel);
break;
case R_PPC_TOC16:
BFD_ASSERT (sec != NULL);
BFD_ASSERT (bfd_is_und_section (sec)
|| strcmp (bfd_get_section_name (abfd, sec), ".got") == 0
|| strcmp (bfd_get_section_name (abfd, sec), ".cgot") == 0);
addend -= sec->output_section->vma + sec->output_offset + 0x8000;
break;
case R_PPC_PLTREL24:
{
struct plt_entry *ent = find_plt_ent (h, got2, addend);
addend = 0;
if (ent == NULL
|| htab->plt == NULL)
{
break;
}
unresolved_reloc = FALSE;
if (!htab->old_plt)
relocation = (htab->glink->output_section->vma
+ htab->glink->output_offset
+ ent->glink_offset);
else
relocation = (htab->plt->output_section->vma
+ htab->plt->output_offset
+ ent->plt.offset);
}
break;
case R_PPC_SDAREL16:
{
const char *name;
struct elf_link_hash_entry *sh;
BFD_ASSERT (sec != NULL);
name = bfd_get_section_name (abfd, sec->output_section);
if (! ((strncmp (name, ".sdata", 6) == 0
&& (name[6] == 0 || name[6] == '.'))
|| (strncmp (name, ".sbss", 5) == 0
&& (name[5] == 0 || name[5] == '.'))))
{
(*_bfd_error_handler)
(_("%B: the target (%s) of a %s relocation is "
"in the wrong output section (%s)"),
input_bfd,
sym_name,
howto->name,
name);
}
sh = htab->sdata[0].sym;
addend -= (sh->root.u.def.value
+ sh->root.u.def.section->output_offset
+ sh->root.u.def.section->output_section->vma);
}
break;
case R_PPC_EMB_SDA2REL:
{
const char *name;
struct elf_link_hash_entry *sh;
BFD_ASSERT (sec != NULL);
name = bfd_get_section_name (abfd, sec->output_section);
if (! (strncmp (name, ".sdata2", 7) == 0
|| strncmp (name, ".sbss2", 6) == 0))
{
(*_bfd_error_handler)
(_("%B: the target (%s) of a %s relocation is "
"in the wrong output section (%s)"),
input_bfd,
sym_name,
howto->name,
name);
bfd_set_error (bfd_error_bad_value);
ret = FALSE;
continue;
}
sh = htab->sdata[1].sym;
addend -= (sh->root.u.def.value
+ sh->root.u.def.section->output_offset
+ sh->root.u.def.section->output_section->vma);
}
break;
case R_PPC_EMB_SDA21:
case R_PPC_EMB_RELSDA:
{
const char *name;
int reg;
struct elf_link_hash_entry *sh;
BFD_ASSERT (sec != NULL);
name = bfd_get_section_name (abfd, sec->output_section);
if (((strncmp (name, ".sdata", 6) == 0
&& (name[6] == 0 || name[6] == '.'))
|| (strncmp (name, ".sbss", 5) == 0
&& (name[5] == 0 || name[5] == '.'))))
{
reg = 13;
sh = htab->sdata[0].sym;
addend -= (sh->root.u.def.value
+ sh->root.u.def.section->output_offset
+ sh->root.u.def.section->output_section->vma);
}
else if (strncmp (name, ".sdata2", 7) == 0
|| strncmp (name, ".sbss2", 6) == 0)
{
reg = 2;
sh = htab->sdata[1].sym;
addend -= (sh->root.u.def.value
+ sh->root.u.def.section->output_offset
+ sh->root.u.def.section->output_section->vma);
}
else if (strcmp (name, ".PPC.EMB.sdata0") == 0
|| strcmp (name, ".PPC.EMB.sbss0") == 0)
{
reg = 0;
}
else
{
(*_bfd_error_handler)
(_("%B: the target (%s) of a %s relocation is "
"in the wrong output section (%s)"),
input_bfd,
sym_name,
howto->name,
name);
bfd_set_error (bfd_error_bad_value);
ret = FALSE;
continue;
}
if (r_type == R_PPC_EMB_SDA21)
{
insn = bfd_get_32 (output_bfd, contents + rel->r_offset);
insn = (insn & ~RA_REGISTER_MASK) | (reg << RA_REGISTER_SHIFT);
bfd_put_32 (output_bfd, insn, contents + rel->r_offset);
}
}
break;
case R_PPC_SECTOFF:
case R_PPC_SECTOFF_LO:
case R_PPC_SECTOFF_HI:
case R_PPC_SECTOFF_HA:
BFD_ASSERT (sec != NULL);
addend -= sec->output_section->vma;
break;
case R_PPC_EMB_NADDR32:
case R_PPC_EMB_NADDR16:
case R_PPC_EMB_NADDR16_LO:
case R_PPC_EMB_NADDR16_HI:
case R_PPC_EMB_NADDR16_HA:
addend -= 2 * relocation;
break;
case R_PPC_COPY:
case R_PPC_GLOB_DAT:
case R_PPC_JMP_SLOT:
case R_PPC_RELATIVE:
case R_PPC_PLT32:
case R_PPC_PLTREL32:
case R_PPC_PLT16_LO:
case R_PPC_PLT16_HI:
case R_PPC_PLT16_HA:
case R_PPC_ADDR30:
case R_PPC_EMB_RELSEC16:
case R_PPC_EMB_RELST_LO:
case R_PPC_EMB_RELST_HI:
case R_PPC_EMB_RELST_HA:
case R_PPC_EMB_BIT_FLD:
(*_bfd_error_handler)
(_("%B: relocation %s is not yet supported for symbol %s."),
input_bfd,
howto->name,
sym_name);
bfd_set_error (bfd_error_invalid_operation);
ret = FALSE;
continue;
}
switch (r_type)
{
default:
break;
case R_PPC_ADDR16_HA:
case R_PPC_REL16_HA:
case R_PPC_GOT16_HA:
case R_PPC_PLT16_HA:
case R_PPC_SECTOFF_HA:
case R_PPC_TPREL16_HA:
case R_PPC_DTPREL16_HA:
case R_PPC_GOT_TLSGD16_HA:
case R_PPC_GOT_TLSLD16_HA:
case R_PPC_GOT_TPREL16_HA:
case R_PPC_GOT_DTPREL16_HA:
case R_PPC_EMB_NADDR16_HA:
case R_PPC_EMB_RELST_HA:
if (sec != NULL)
addend += 0x8000;
break;
}
#ifdef DEBUG
fprintf (stderr, "\ttype = %s (%d), name = %s, symbol index = %ld, "
"offset = %ld, addend = %ld\n",
howto->name,
(int) r_type,
sym_name,
r_symndx,
(long) rel->r_offset,
(long) addend);
#endif
if (unresolved_reloc
&& !((input_section->flags & SEC_DEBUGGING) != 0
&& h->def_dynamic))
{
(*_bfd_error_handler)
(_("%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'"),
input_bfd,
input_section,
(long) rel->r_offset,
howto->name,
sym_name);
ret = FALSE;
}
r = _bfd_final_link_relocate (howto,
input_bfd,
input_section,
contents,
rel->r_offset,
relocation,
addend);
if (r != bfd_reloc_ok)
{
if (r == bfd_reloc_overflow)
{
if (warned)
continue;
if (h != NULL
&& h->root.type == bfd_link_hash_undefweak
&& howto->pc_relative)
{
continue;
}
if (! (*info->callbacks->reloc_overflow) (info,
(h ? &h->root : NULL),
sym_name,
howto->name,
rel->r_addend,
input_bfd,
input_section,
rel->r_offset))
return FALSE;
}
else
{
(*_bfd_error_handler)
(_("%B(%A+0x%lx): %s reloc against `%s': error %d"),
input_bfd, input_section,
(long) rel->r_offset, howto->name, sym_name, (int) r);
ret = FALSE;
}
}
}
#ifdef DEBUG
fprintf (stderr, "\n");
#endif
return ret;
}
#define PPC_LO(v) ((v) & 0xffff)
#define PPC_HI(v) (((v) >> 16) & 0xffff)
#define PPC_HA(v) PPC_HI ((v) + 0x8000)
static bfd_boolean
ppc_elf_finish_dynamic_symbol (bfd *output_bfd,
struct bfd_link_info *info,
struct elf_link_hash_entry *h,
Elf_Internal_Sym *sym)
{
struct ppc_elf_link_hash_table *htab;
struct plt_entry *ent;
bfd_boolean doneone;
#ifdef DEBUG
fprintf (stderr, "ppc_elf_finish_dynamic_symbol called for %s",
h->root.root.string);
#endif
htab = ppc_elf_hash_table (info);
BFD_ASSERT (htab->elf.dynobj != NULL);
doneone = FALSE;
for (ent = h->plt.plist; ent != NULL; ent = ent->next)
if (ent->plt.offset != (bfd_vma) -1)
{
if (!doneone)
{
Elf_Internal_Rela rela;
bfd_byte *loc;
bfd_vma reloc_index;
if (!(htab->old_plt || htab->is_vxworks))
reloc_index = ent->plt.offset / 4;
else
{
reloc_index = ((ent->plt.offset - htab->plt_initial_entry_size)
/ htab->plt_slot_size);
if (reloc_index > PLT_NUM_SINGLE_ENTRIES
&& !htab->is_vxworks)
reloc_index -= (reloc_index - PLT_NUM_SINGLE_ENTRIES) / 2;
}
if (htab->is_vxworks)
{
bfd_vma got_offset;
const bfd_vma *plt_entry;
got_offset = (reloc_index + 3) * 4;
plt_entry = info->shared ? ppc_elf_vxworks_pic_plt_entry
: ppc_elf_vxworks_plt_entry;
if (info->shared)
{
bfd_vma got_offset_hi = (got_offset >> 16)
+ ((got_offset & 0x8000) >> 15);
bfd_put_32 (output_bfd,
plt_entry[0] | (got_offset_hi & 0xffff),
htab->plt->contents + ent->plt.offset + 0);
bfd_put_32 (output_bfd,
plt_entry[1] | (got_offset & 0xffff),
htab->plt->contents + ent->plt.offset + 4);
}
else
{
bfd_vma got_loc
= (got_offset
+ htab->elf.hgot->root.u.def.value
+ htab->elf.hgot->root.u.def.section->output_offset
+ htab->elf.hgot->root.u.def.section->output_section->vma);
bfd_vma got_loc_hi = (got_loc >> 16)
+ ((got_loc & 0x8000) >> 15);
bfd_put_32 (output_bfd,
plt_entry[0] | (got_loc_hi & 0xffff),
htab->plt->contents + ent->plt.offset + 0);
bfd_put_32 (output_bfd,
plt_entry[1] | (got_loc & 0xffff),
htab->plt->contents + ent->plt.offset + 4);
}
bfd_put_32 (output_bfd, plt_entry[2],
htab->plt->contents + ent->plt.offset + 8);
bfd_put_32 (output_bfd, plt_entry[3],
htab->plt->contents + ent->plt.offset + 12);
bfd_put_32 (output_bfd,
plt_entry[4] | reloc_index,
htab->plt->contents + ent->plt.offset + 16);
bfd_put_32 (output_bfd,
(plt_entry[5]
| (-(ent->plt.offset + 20) & 0x03fffffc)),
htab->plt->contents + ent->plt.offset + 20);
bfd_put_32 (output_bfd, plt_entry[6],
htab->plt->contents + ent->plt.offset + 24);
bfd_put_32 (output_bfd, plt_entry[7],
htab->plt->contents + ent->plt.offset + 28);
bfd_put_32 (output_bfd, (htab->plt->output_section->vma
+ htab->plt->output_offset
+ ent->plt.offset + 16),
htab->sgotplt->contents + got_offset);
if (!info->shared)
{
loc = htab->srelplt2->contents
+ ((VXWORKS_PLTRESOLVE_RELOCS + reloc_index
* VXWORKS_PLT_NON_JMP_SLOT_RELOCS)
* sizeof (Elf32_External_Rela));
rela.r_offset = (htab->plt->output_section->vma
+ htab->plt->output_offset
+ ent->plt.offset + 2);
rela.r_info = ELF32_R_INFO (htab->elf.hgot->indx,
R_PPC_ADDR16_HA);
rela.r_addend = got_offset;
bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
loc += sizeof (Elf32_External_Rela);
rela.r_offset = (htab->plt->output_section->vma
+ htab->plt->output_offset
+ ent->plt.offset + 6);
rela.r_info = ELF32_R_INFO (htab->elf.hgot->indx,
R_PPC_ADDR16_LO);
rela.r_addend = got_offset;
bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
loc += sizeof (Elf32_External_Rela);
rela.r_offset = (htab->sgotplt->output_section->vma
+ htab->sgotplt->output_offset
+ got_offset);
rela.r_info = ELF32_R_INFO (htab->hplt->indx,
R_PPC_ADDR32);
rela.r_addend = ent->plt.offset + 16;
bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
}
rela.r_offset = (htab->sgotplt->output_section->vma
+ htab->sgotplt->output_offset
+ got_offset);
}
else
{
rela.r_offset = (htab->plt->output_section->vma
+ htab->plt->output_offset
+ ent->plt.offset);
if (htab->old_plt)
{
}
else
{
bfd_vma val = (htab->glink_pltresolve + ent->plt.offset
+ htab->glink->output_section->vma
+ htab->glink->output_offset);
bfd_put_32 (output_bfd, val,
htab->plt->contents + ent->plt.offset);
}
}
rela.r_info = ELF32_R_INFO (h->dynindx, R_PPC_JMP_SLOT);
rela.r_addend = 0;
loc = (htab->relplt->contents
+ reloc_index * sizeof (Elf32_External_Rela));
bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
if (!h->def_regular)
{
sym->st_shndx = SHN_UNDEF;
if (!h->ref_regular_nonweak)
sym->st_value = 0;
}
doneone = TRUE;
}
if (!htab->old_plt)
{
bfd_vma plt;
unsigned char *p;
plt = (ent->plt.offset
+ htab->plt->output_section->vma
+ htab->plt->output_offset);
p = (unsigned char *) htab->glink->contents + ent->glink_offset;
if (info->shared || info->pie)
{
bfd_vma got = 0;
if (ent->addend >= 32768)
got = (ent->addend
+ ent->sec->output_section->vma
+ ent->sec->output_offset);
else if (htab->elf.hgot != NULL)
got = (htab->elf.hgot->root.u.def.value
+ htab->elf.hgot->root.u.def.section->output_section->vma
+ htab->elf.hgot->root.u.def.section->output_offset);
plt -= got;
if (plt + 0x8000 < 0x10000)
{
bfd_put_32 (output_bfd, LWZ_11_30 + PPC_LO (plt), p);
p += 4;
bfd_put_32 (output_bfd, MTCTR_11, p);
p += 4;
bfd_put_32 (output_bfd, BCTR, p);
p += 4;
bfd_put_32 (output_bfd, NOP, p);
p += 4;
}
else
{
bfd_put_32 (output_bfd, ADDIS_11_30 + PPC_HA (plt), p);
p += 4;
bfd_put_32 (output_bfd, LWZ_11_11 + PPC_LO (plt), p);
p += 4;
bfd_put_32 (output_bfd, MTCTR_11, p);
p += 4;
bfd_put_32 (output_bfd, BCTR, p);
p += 4;
}
}
else
{
bfd_put_32 (output_bfd, LIS_11 + PPC_HA (plt), p);
p += 4;
bfd_put_32 (output_bfd, LWZ_11_11 + PPC_LO (plt), p);
p += 4;
bfd_put_32 (output_bfd, MTCTR_11, p);
p += 4;
bfd_put_32 (output_bfd, BCTR, p);
p += 4;
break;
}
}
else
break;
}
if (h->needs_copy)
{
asection *s;
Elf_Internal_Rela rela;
bfd_byte *loc;
#ifdef DEBUG
fprintf (stderr, ", copy");
#endif
BFD_ASSERT (h->dynindx != -1);
if (ppc_elf_hash_entry (h)->has_sda_refs)
s = htab->relsbss;
else
s = htab->relbss;
BFD_ASSERT (s != NULL);
rela.r_offset = (h->root.u.def.value
+ h->root.u.def.section->output_section->vma
+ h->root.u.def.section->output_offset);
rela.r_info = ELF32_R_INFO (h->dynindx, R_PPC_COPY);
rela.r_addend = 0;
loc = s->contents + s->reloc_count++ * sizeof (Elf32_External_Rela);
bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
}
#ifdef DEBUG
fprintf (stderr, "\n");
#endif
if (strcmp (h->root.root.string, "_DYNAMIC") == 0
|| (!htab->is_vxworks
&& (h == htab->elf.hgot
|| strcmp (h->root.root.string,
"_PROCEDURE_LINKAGE_TABLE_") == 0)))
sym->st_shndx = SHN_ABS;
return TRUE;
}
static enum elf_reloc_type_class
ppc_elf_reloc_type_class (const Elf_Internal_Rela *rela)
{
switch (ELF32_R_TYPE (rela->r_info))
{
case R_PPC_RELATIVE:
return reloc_class_relative;
case R_PPC_REL24:
case R_PPC_ADDR24:
case R_PPC_JMP_SLOT:
return reloc_class_plt;
case R_PPC_COPY:
return reloc_class_copy;
default:
return reloc_class_normal;
}
}
static bfd_boolean
ppc_elf_finish_dynamic_sections (bfd *output_bfd,
struct bfd_link_info *info)
{
asection *sdyn;
asection *splt;
struct ppc_elf_link_hash_table *htab;
bfd_vma got;
bfd * dynobj;
#ifdef DEBUG
fprintf (stderr, "ppc_elf_finish_dynamic_sections called\n");
#endif
htab = ppc_elf_hash_table (info);
dynobj = elf_hash_table (info)->dynobj;
sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
if (htab->is_vxworks)
splt = bfd_get_section_by_name (dynobj, ".plt");
else
splt = NULL;
got = 0;
if (htab->elf.hgot != NULL)
got = (htab->elf.hgot->root.u.def.value
+ htab->elf.hgot->root.u.def.section->output_section->vma
+ htab->elf.hgot->root.u.def.section->output_offset);
if (htab->elf.dynamic_sections_created)
{
Elf32_External_Dyn *dyncon, *dynconend;
BFD_ASSERT (htab->plt != NULL && sdyn != NULL);
dyncon = (Elf32_External_Dyn *) sdyn->contents;
dynconend = (Elf32_External_Dyn *) (sdyn->contents + sdyn->size);
for (; dyncon < dynconend; dyncon++)
{
Elf_Internal_Dyn dyn;
asection *s;
bfd_elf32_swap_dyn_in (dynobj, dyncon, &dyn);
switch (dyn.d_tag)
{
case DT_PLTGOT:
if (htab->is_vxworks)
s = htab->sgotplt;
else
s = htab->plt;
dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
break;
case DT_PLTRELSZ:
dyn.d_un.d_val = htab->relplt->size;
break;
case DT_JMPREL:
s = htab->relplt;
dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
break;
case DT_PPC_GOT:
dyn.d_un.d_ptr = got;
break;
case DT_RELASZ:
if (htab->is_vxworks)
{
if (htab->relplt)
dyn.d_un.d_ptr -= htab->relplt->size;
break;
}
continue;
default:
continue;
}
bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
}
}
if (htab->got != NULL)
{
unsigned char *p = htab->got->contents;
bfd_vma val;
p += htab->elf.hgot->root.u.def.value;
if (htab->old_plt && !htab->is_vxworks)
bfd_put_32 (output_bfd, 0x4e800021 , p - 4);
val = 0;
if (sdyn != NULL)
val = sdyn->output_section->vma + sdyn->output_offset;
bfd_put_32 (output_bfd, val, p);
elf_section_data (htab->got->output_section)->this_hdr.sh_entsize = 4;
}
if (splt && splt->size > 0)
{
static const bfd_vma *plt_entry = NULL;
plt_entry = info->shared ?
ppc_elf_vxworks_pic_plt0_entry : ppc_elf_vxworks_plt0_entry;
if (!info->shared)
{
bfd_vma got_value =
(htab->elf.hgot->root.u.def.section->output_section->vma
+ htab->elf.hgot->root.u.def.section->output_offset
+ htab->elf.hgot->root.u.def.value);
bfd_vma got_hi = (got_value >> 16) + ((got_value & 0x8000) >> 15);
bfd_put_32 (output_bfd, plt_entry[0] | (got_hi & 0xffff),
splt->contents + 0);
bfd_put_32 (output_bfd, plt_entry[1] | (got_value & 0xffff),
splt->contents + 4);
}
else
{
bfd_put_32 (output_bfd, plt_entry[0], splt->contents + 0);
bfd_put_32 (output_bfd, plt_entry[1], splt->contents + 4);
}
bfd_put_32 (output_bfd, plt_entry[2], splt->contents + 8);
bfd_put_32 (output_bfd, plt_entry[3], splt->contents + 12);
bfd_put_32 (output_bfd, plt_entry[4], splt->contents + 16);
bfd_put_32 (output_bfd, plt_entry[5], splt->contents + 20);
bfd_put_32 (output_bfd, plt_entry[6], splt->contents + 24);
bfd_put_32 (output_bfd, plt_entry[7], splt->contents + 28);
if (! info->shared)
{
Elf_Internal_Rela rela;
bfd_byte *loc;
loc = htab->srelplt2->contents;
rela.r_offset = (htab->plt->output_section->vma
+ htab->plt->output_offset
+ 2);
rela.r_info = ELF32_R_INFO (htab->elf.hgot->indx, R_PPC_ADDR16_HA);
rela.r_addend = 0;
bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
loc += sizeof (Elf32_External_Rela);
rela.r_offset = (htab->plt->output_section->vma
+ htab->plt->output_offset
+ 6);
rela.r_info = ELF32_R_INFO (htab->elf.hgot->indx, R_PPC_ADDR16_LO);
rela.r_addend = 0;
bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
loc += sizeof (Elf32_External_Rela);
while (loc < htab->srelplt2->contents + htab->srelplt2->size)
{
Elf_Internal_Rela rel;
bfd_elf32_swap_reloc_in (output_bfd, loc, &rel);
rel.r_info = ELF32_R_INFO (htab->elf.hgot->indx, R_PPC_ADDR16_HA);
bfd_elf32_swap_reloc_out (output_bfd, &rel, loc);
loc += sizeof (Elf32_External_Rela);
bfd_elf32_swap_reloc_in (output_bfd, loc, &rel);
rel.r_info = ELF32_R_INFO (htab->elf.hgot->indx, R_PPC_ADDR16_LO);
bfd_elf32_swap_reloc_out (output_bfd, &rel, loc);
loc += sizeof (Elf32_External_Rela);
bfd_elf32_swap_reloc_in (output_bfd, loc, &rel);
rel.r_info = ELF32_R_INFO (htab->hplt->indx, R_PPC_ADDR32);
bfd_elf32_swap_reloc_out (output_bfd, &rel, loc);
loc += sizeof (Elf32_External_Rela);
}
}
}
if (htab->glink != NULL && htab->glink->contents != NULL)
{
unsigned char *p;
unsigned char *endp;
bfd_vma res0;
unsigned int i;
static const unsigned int pic_plt_resolve[] =
{
ADDIS_11_11,
MFLR_0,
BCL_20_31,
ADDI_11_11,
MFLR_12,
MTLR_0,
SUB_11_11_12,
ADDIS_12_12,
LWZ_0_12,
LWZ_12_12,
MTCTR_0,
ADD_0_11_11,
ADD_11_0_11,
BCTR,
NOP,
NOP
};
static const unsigned int plt_resolve[] =
{
LIS_12,
ADDIS_11_11,
LWZ_0_12,
ADDI_11_11,
MTCTR_0,
ADD_0_11_11,
LWZ_12_12,
ADD_11_0_11,
BCTR,
NOP,
NOP,
NOP,
NOP,
NOP,
NOP,
NOP
};
if (ARRAY_SIZE (pic_plt_resolve) != GLINK_PLTRESOLVE / 4)
abort ();
if (ARRAY_SIZE (plt_resolve) != GLINK_PLTRESOLVE / 4)
abort ();
p = htab->glink->contents;
p += htab->glink_pltresolve;
endp = htab->glink->contents;
endp += htab->glink->size - GLINK_PLTRESOLVE;
while (p < endp - 8 * 4)
{
bfd_put_32 (output_bfd, B + endp - p, p);
p += 4;
}
while (p < endp)
{
bfd_put_32 (output_bfd, NOP, p);
p += 4;
}
res0 = (htab->glink_pltresolve
+ htab->glink->output_section->vma
+ htab->glink->output_offset);
if (info->shared || info->pie)
{
bfd_vma bcl;
for (i = 0; i < ARRAY_SIZE (pic_plt_resolve); i++)
{
bfd_put_32 (output_bfd, pic_plt_resolve[i], p);
p += 4;
}
p -= 4 * ARRAY_SIZE (pic_plt_resolve);
bcl = (htab->glink->size - GLINK_PLTRESOLVE + 3*4
+ htab->glink->output_section->vma
+ htab->glink->output_offset);
bfd_put_32 (output_bfd,
ADDIS_11_11 + PPC_HA (bcl - res0), p + 0*4);
bfd_put_32 (output_bfd,
ADDI_11_11 + PPC_LO (bcl - res0), p + 3*4);
bfd_put_32 (output_bfd,
ADDIS_12_12 + PPC_HA (got + 4 - bcl), p + 7*4);
if (PPC_HA (got + 4 - bcl) == PPC_HA (got + 8 - bcl))
{
bfd_put_32 (output_bfd,
LWZ_0_12 + PPC_LO (got + 4 - bcl), p + 8*4);
bfd_put_32 (output_bfd,
LWZ_12_12 + PPC_LO (got + 8 - bcl), p + 9*4);
}
else
{
bfd_put_32 (output_bfd,
LWZU_0_12 + PPC_LO (got + 4 - bcl), p + 8*4);
bfd_put_32 (output_bfd,
LWZ_12_12 + 4, p + 9*4);
}
}
else
{
for (i = 0; i < ARRAY_SIZE (plt_resolve); i++)
{
bfd_put_32 (output_bfd, plt_resolve[i], p);
p += 4;
}
p -= 4 * ARRAY_SIZE (plt_resolve);
bfd_put_32 (output_bfd,
LIS_12 + PPC_HA (got + 4), p + 0*4);
bfd_put_32 (output_bfd,
ADDIS_11_11 + PPC_HA (-res0), p + 1*4);
bfd_put_32 (output_bfd,
ADDI_11_11 + PPC_LO (-res0), p + 3*4);
if (PPC_HA (got + 4) == PPC_HA (got + 8))
{
bfd_put_32 (output_bfd,
LWZ_0_12 + PPC_LO (got + 4), p + 2*4);
bfd_put_32 (output_bfd,
LWZ_12_12 + PPC_LO (got + 8), p + 6*4);
}
else
{
bfd_put_32 (output_bfd,
LWZU_0_12 + PPC_LO (got + 4), p + 2*4);
bfd_put_32 (output_bfd,
LWZ_12_12 + 4, p + 6*4);
}
}
}
return TRUE;
}
#define TARGET_LITTLE_SYM bfd_elf32_powerpcle_vec
#define TARGET_LITTLE_NAME "elf32-powerpcle"
#define TARGET_BIG_SYM bfd_elf32_powerpc_vec
#define TARGET_BIG_NAME "elf32-powerpc"
#define ELF_ARCH bfd_arch_powerpc
#define ELF_MACHINE_CODE EM_PPC
#ifdef __QNXTARGET__
#define ELF_MAXPAGESIZE 0x1000
#else
#define ELF_MAXPAGESIZE 0x10000
#endif
#define ELF_MINPAGESIZE 0x1000
#define elf_info_to_howto ppc_elf_info_to_howto
#ifdef EM_CYGNUS_POWERPC
#define ELF_MACHINE_ALT1 EM_CYGNUS_POWERPC
#endif
#ifdef EM_PPC_OLD
#define ELF_MACHINE_ALT2 EM_PPC_OLD
#endif
#define elf_backend_plt_not_loaded 1
#define elf_backend_can_gc_sections 1
#define elf_backend_can_refcount 1
#define elf_backend_rela_normal 1
#define bfd_elf32_mkobject ppc_elf_mkobject
#define bfd_elf32_bfd_merge_private_bfd_data ppc_elf_merge_private_bfd_data
#define bfd_elf32_bfd_relax_section ppc_elf_relax_section
#define bfd_elf32_bfd_reloc_type_lookup ppc_elf_reloc_type_lookup
#define bfd_elf32_bfd_set_private_flags ppc_elf_set_private_flags
#define bfd_elf32_bfd_link_hash_table_create ppc_elf_link_hash_table_create
#define elf_backend_object_p ppc_elf_object_p
#define elf_backend_gc_mark_hook ppc_elf_gc_mark_hook
#define elf_backend_gc_sweep_hook ppc_elf_gc_sweep_hook
#define elf_backend_section_from_shdr ppc_elf_section_from_shdr
#define elf_backend_relocate_section ppc_elf_relocate_section
#define elf_backend_create_dynamic_sections ppc_elf_create_dynamic_sections
#define elf_backend_check_relocs ppc_elf_check_relocs
#define elf_backend_copy_indirect_symbol ppc_elf_copy_indirect_symbol
#define elf_backend_adjust_dynamic_symbol ppc_elf_adjust_dynamic_symbol
#define elf_backend_add_symbol_hook ppc_elf_add_symbol_hook
#define elf_backend_size_dynamic_sections ppc_elf_size_dynamic_sections
#define elf_backend_finish_dynamic_symbol ppc_elf_finish_dynamic_symbol
#define elf_backend_finish_dynamic_sections ppc_elf_finish_dynamic_sections
#define elf_backend_fake_sections ppc_elf_fake_sections
#define elf_backend_additional_program_headers ppc_elf_additional_program_headers
#define elf_backend_grok_prstatus ppc_elf_grok_prstatus
#define elf_backend_grok_psinfo ppc_elf_grok_psinfo
#define elf_backend_reloc_type_class ppc_elf_reloc_type_class
#define elf_backend_begin_write_processing ppc_elf_begin_write_processing
#define elf_backend_final_write_processing ppc_elf_final_write_processing
#define elf_backend_write_section ppc_elf_write_section
#define elf_backend_get_sec_type_attr ppc_elf_get_sec_type_attr
#define elf_backend_plt_sym_val ppc_elf_plt_sym_val
#define elf_backend_action_discarded ppc_elf_action_discarded
#include "elf32-target.h"
#undef TARGET_LITTLE_SYM
#undef TARGET_LITTLE_NAME
#undef TARGET_BIG_SYM
#define TARGET_BIG_SYM bfd_elf32_powerpc_vxworks_vec
#undef TARGET_BIG_NAME
#define TARGET_BIG_NAME "elf32-powerpc-vxworks"
static const struct bfd_elf_special_section *
ppc_elf_vxworks_get_sec_type_attr (bfd *abfd ATTRIBUTE_UNUSED, asection *sec)
{
if (sec->name == NULL)
return NULL;
if (strcmp (sec->name, ".plt") == 0)
return _bfd_elf_get_sec_type_attr (abfd, sec);
return ppc_elf_get_sec_type_attr (abfd, sec);
}
static struct bfd_link_hash_table *
ppc_elf_vxworks_link_hash_table_create (bfd *abfd)
{
struct bfd_link_hash_table *ret;
ret = ppc_elf_link_hash_table_create (abfd);
if (ret)
{
struct ppc_elf_link_hash_table *htab
= (struct ppc_elf_link_hash_table *)ret;
htab->is_vxworks = 1;
htab->plt_entry_size = VXWORKS_PLT_ENTRY_SIZE;
htab->plt_slot_size = VXWORKS_PLT_ENTRY_SIZE;
htab->plt_initial_entry_size = VXWORKS_PLT_INITIAL_ENTRY_SIZE;
}
return ret;
}
static bfd_boolean
ppc_elf_vxworks_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 (!elf_vxworks_add_symbol_hook(abfd, info, sym,namep, flagsp, secp,
valp))
return FALSE;
return ppc_elf_add_symbol_hook(abfd, info, sym,namep, flagsp, secp, valp);
}
static bfd_boolean
elf_i386_vxworks_link_output_symbol_hook (struct bfd_link_info *info
ATTRIBUTE_UNUSED,
const char *name,
Elf_Internal_Sym *sym,
asection *input_sec ATTRIBUTE_UNUSED,
struct elf_link_hash_entry *h
ATTRIBUTE_UNUSED)
{
if (!name)
return TRUE;
return elf_vxworks_link_output_symbol_hook (name, sym);
}
static void
ppc_elf_vxworks_final_write_processing (bfd *abfd, bfd_boolean linker)
{
ppc_elf_final_write_processing(abfd, linker);
elf_vxworks_final_write_processing(abfd, linker);
}
#undef elf_backend_want_plt_sym
#define elf_backend_want_plt_sym 1
#undef elf_backend_want_got_plt
#define elf_backend_want_got_plt 1
#undef elf_backend_got_symbol_offset
#define elf_backend_got_symbol_offset 0
#undef elf_backend_plt_not_loaded
#define elf_backend_plt_not_loaded 0
#undef elf_backend_plt_readonly
#define elf_backend_plt_readonly 1
#undef elf_backend_got_header_size
#define elf_backend_got_header_size 12
#undef bfd_elf32_bfd_link_hash_table_create
#define bfd_elf32_bfd_link_hash_table_create \
ppc_elf_vxworks_link_hash_table_create
#undef elf_backend_add_symbol_hook
#define elf_backend_add_symbol_hook \
ppc_elf_vxworks_add_symbol_hook
#undef elf_backend_link_output_symbol_hook
#define elf_backend_link_output_symbol_hook \
elf_i386_vxworks_link_output_symbol_hook
#undef elf_backend_final_write_processing
#define elf_backend_final_write_processing \
ppc_elf_vxworks_final_write_processing
#undef elf_backend_get_sec_type_attr
#define elf_backend_get_sec_type_attr \
ppc_elf_vxworks_get_sec_type_attr
#undef elf_backend_emit_relocs
#define elf_backend_emit_relocs \
elf_vxworks_emit_relocs
#undef elf32_bed
#define elf32_bed ppc_elf_vxworks_bed
#include "elf32-target.h"