X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=bfd%2Felf64-ppc.c;h=8e1935dfb9d1a941dcd3d97c18bfd54e9c66b7d1;hb=86fb1dece37497b267579ed4f062d280cd5760cd;hp=cca9b7f2bda6db3a58c2476c1a8ec869096ce569;hpb=0ad3a73b15990b52e60b99a55069a6756ea6eee8;p=deliverable%2Fbinutils-gdb.git diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c index cca9b7f2bd..8e1935dfb9 100644 --- a/bfd/elf64-ppc.c +++ b/bfd/elf64-ppc.c @@ -39,72 +39,32 @@ static reloc_howto_type *ppc64_elf_reloc_type_lookup PARAMS ((bfd *abfd, bfd_reloc_code_real_type code)); static void ppc64_elf_info_to_howto PARAMS ((bfd *abfd, arelent *cache_ptr, Elf64_Internal_Rela *dst)); -static bfd_reloc_status_type ppc64_elf_addr16_ha_reloc +static bfd_reloc_status_type ppc64_elf_ha_reloc PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **)); +static bfd_reloc_status_type ppc64_elf_brtaken_reloc + PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **)); +static bfd_reloc_status_type ppc64_elf_sectoff_reloc + PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **)); +static bfd_reloc_status_type ppc64_elf_sectoff_ha_reloc + PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **)); +static bfd_reloc_status_type ppc64_elf_toc_reloc + PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **)); +static bfd_reloc_status_type ppc64_elf_toc_ha_reloc + PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **)); +static bfd_reloc_status_type ppc64_elf_toc64_reloc + PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **)); +static bfd_reloc_status_type ppc64_elf_unhandled_reloc + PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **)); +static void ppc64_elf_get_symbol_info + PARAMS ((bfd *, asymbol *, symbol_info *)); static boolean ppc64_elf_set_private_flags PARAMS ((bfd *, flagword)); static boolean ppc64_elf_merge_private_bfd_data PARAMS ((bfd *, bfd *)); static boolean ppc64_elf_section_from_shdr - PARAMS ((bfd *, Elf64_Internal_Shdr *, char *)); -static struct bfd_hash_entry *link_hash_newfunc - PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *)); -static struct bfd_link_hash_table *ppc64_elf_link_hash_table_create - PARAMS ((bfd *)); -static boolean create_got_section - PARAMS ((bfd *, struct bfd_link_info *)); -static boolean ppc64_elf_create_dynamic_sections - PARAMS ((bfd *, struct bfd_link_info *)); -static void ppc64_elf_copy_indirect_symbol - PARAMS ((struct elf_link_hash_entry *, struct elf_link_hash_entry *)); -static boolean ppc64_elf_check_relocs - PARAMS ((bfd *, struct bfd_link_info *, asection *, - const Elf_Internal_Rela *)); -static asection * ppc64_elf_gc_mark_hook - PARAMS ((bfd *abfd, struct bfd_link_info *info, Elf_Internal_Rela *rel, - struct elf_link_hash_entry *h, Elf_Internal_Sym *sym)); -static boolean ppc64_elf_gc_sweep_hook - PARAMS ((bfd *abfd, struct bfd_link_info *info, asection *sec, - const Elf_Internal_Rela *relocs)); -static boolean func_desc_adjust - PARAMS ((struct elf_link_hash_entry *, PTR)); -static boolean ppc64_elf_func_desc_adjust - PARAMS ((bfd *, struct bfd_link_info *)); -static boolean ppc64_elf_adjust_dynamic_symbol - PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *)); -static void ppc64_elf_hide_symbol - PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *, boolean)); -static boolean allocate_dynrelocs - PARAMS ((struct elf_link_hash_entry *, PTR)); -static boolean readonly_dynrelocs - PARAMS ((struct elf_link_hash_entry *, PTR)); -static enum elf_reloc_type_class ppc64_elf_reloc_type_class - PARAMS ((const Elf_Internal_Rela *)); -static boolean ppc64_elf_size_dynamic_sections - PARAMS ((bfd *, struct bfd_link_info *)); -static bfd_byte *build_plt_stub - PARAMS ((bfd *, bfd_byte *, int, int)); -static boolean build_one_stub - PARAMS ((struct elf_link_hash_entry *, PTR)); -static boolean ppc64_elf_fake_sections - PARAMS ((bfd *, Elf64_Internal_Shdr *, asection *)); -static boolean ppc64_elf_relocate_section - PARAMS ((bfd *, struct bfd_link_info *info, bfd *, asection *, bfd_byte *, - Elf_Internal_Rela *relocs, Elf_Internal_Sym *local_syms, - asection **)); -static boolean ppc64_elf_finish_dynamic_symbol - PARAMS ((bfd *, struct bfd_link_info *, struct elf_link_hash_entry *, - Elf_Internal_Sym *)); -static boolean ppc64_elf_finish_dynamic_sections - PARAMS ((bfd *, struct bfd_link_info *)); + PARAMS ((bfd *, Elf64_Internal_Shdr *, const char *)); -/* Mask to set RA in memory instructions. */ -#define RA_REGISTER_MASK 0x001f0000 - -/* Value to shift register by to insert RA. */ -#define RA_REGISTER_SHIFT 16 - /* The name of the dynamic interpreter. This is put in the .interp section. */ #define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so.1" @@ -144,7 +104,11 @@ static boolean ppc64_elf_finish_dynamic_sections /* Pad with this. */ #define NOP 0x60000000 -/* .glink entries for the first 32k functions are two instructions. */ +/* Some other nops. */ +#define CROR_151515 0x4def7b82 +#define CROR_313131 0x4ffffb82 + +/* .glink entries for the first 32k functions are two instructions. */ #define LI_R0_0 0x38000000 /* li %r0,0 */ #define B_DOT 0x48000000 /* b . */ @@ -153,6 +117,11 @@ static boolean ppc64_elf_finish_dynamic_sections #define LIS_R0_0 0x3c000000 /* lis %r0,0 */ #define ORI_R0_R0_0 0x60000000 /* ori %r0,%r0,0 */ +/* Instructions to save and restore floating point regs. */ +#define STFD_FR0_0R1 0xd8010000 /* stfd %fr0,0(%r1) */ +#define LFD_FR0_0R1 0xc8010000 /* lfd %fr0,0(%r1) */ +#define BLR 0x4e800020 /* blr */ + /* Since .opd is an array of descriptors and each entry will end up with identical R_PPC64_RELATIVE relocs, there is really no need to propagate .opd relocs; The dynamic linker should be taught to @@ -266,7 +235,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ - ppc64_elf_addr16_ha_reloc, /* special_function */ + ppc64_elf_ha_reloc, /* special_function */ "R_PPC64_ADDR16_HA", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -299,7 +268,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_brtaken_reloc, /* special_function */ "R_PPC64_ADDR14_BRTAKEN",/* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -316,7 +285,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_brtaken_reloc, /* special_function */ "R_PPC64_ADDR14_BRNTAKEN",/* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -363,7 +332,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { true, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_brtaken_reloc, /* special_function */ "R_PPC64_REL14_BRTAKEN", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -380,7 +349,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { true, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_brtaken_reloc, /* special_function */ "R_PPC64_REL14_BRNTAKEN",/* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -396,7 +365,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_unhandled_reloc, /* special_function */ "R_PPC64_GOT16", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -412,7 +381,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_unhandled_reloc, /* special_function */ "R_PPC64_GOT16_LO", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -428,7 +397,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_unhandled_reloc, /* special_function */ "R_PPC64_GOT16_HI", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -444,7 +413,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ - ppc64_elf_addr16_ha_reloc, /* special_function */ + ppc64_elf_unhandled_reloc, /* special_function */ "R_PPC64_GOT16_HA", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -463,7 +432,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_unhandled_reloc, /* special_function */ "R_PPC64_COPY", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -479,7 +448,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_unhandled_reloc, /* special_function */ "R_PPC64_GLOB_DAT", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -495,7 +464,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_unhandled_reloc, /* special_function */ "R_PPC64_JMP_SLOT", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -556,7 +525,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { 32, /* bitsize */ true, /* pc_relative */ 0, /* bitpos */ - /* FIXME: Verify. Was complain_overflow_bitfield. */ + /* FIXME: Verify. Was complain_overflow_bitfield. */ complain_overflow_signed, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_PPC64_REL32", /* name */ @@ -573,7 +542,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_unhandled_reloc, /* special_function */ "R_PPC64_PLT32", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -605,7 +574,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_unhandled_reloc, /* special_function */ "R_PPC64_PLT16_LO", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -621,7 +590,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_unhandled_reloc, /* special_function */ "R_PPC64_PLT16_HI", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -637,31 +606,29 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ - ppc64_elf_addr16_ha_reloc, /* special_function */ + ppc64_elf_unhandled_reloc, /* special_function */ "R_PPC64_PLT16_HA", /* name */ false, /* partial_inplace */ 0, /* src_mask */ 0xffff, /* dst_mask */ false), /* pcrel_offset */ - /* 32-bit section relative relocation. */ - /* FIXME: Verify R_PPC64_SECTOFF. Seems strange with size=2 and - dst_mask=0. */ + /* 16-bit section relative relocation. */ HOWTO (R_PPC64_SECTOFF, /* type */ 0, /* rightshift */ - 2, /* size (0 = byte, 1 = short, 2 = long) */ - 32, /* bitsize */ - true, /* pc_relative */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + false, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_sectoff_reloc, /* special_function */ "R_PPC64_SECTOFF", /* name */ false, /* partial_inplace */ 0, /* src_mask */ - 0, /* dst_mask */ - true), /* pcrel_offset */ + 0xffff, /* dst_mask */ + false), /* pcrel_offset */ - /* 16-bit lower half section relative relocation. */ + /* Like R_PPC64_SECTOFF, but no overflow warning. */ HOWTO (R_PPC64_SECTOFF_LO, /* type */ 0, /* rightshift */ 1, /* size (0 = byte, 1 = short, 2 = long) */ @@ -669,7 +636,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_sectoff_reloc, /* special_function */ "R_PPC64_SECTOFF_LO", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -684,7 +651,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_sectoff_reloc, /* special_function */ "R_PPC64_SECTOFF_HI", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -699,7 +666,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ - ppc64_elf_addr16_ha_reloc, /* special_function */ + ppc64_elf_sectoff_ha_reloc, /* special_function */ "R_PPC64_SECTOFF_HA", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -707,8 +674,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false), /* pcrel_offset */ /* Like R_PPC64_REL24 without touching the two least significant - bits. */ - /* FIXME: Verify R_PPC64_ADDR30. */ + bits. Should have been named R_PPC64_REL30! */ HOWTO (R_PPC64_ADDR30, /* type */ 2, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ @@ -764,7 +730,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ - ppc64_elf_addr16_ha_reloc, /* special_function */ + ppc64_elf_ha_reloc, /* special_function */ "R_PPC64_ADDR16_HIGHERA", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -795,7 +761,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ - ppc64_elf_addr16_ha_reloc, /* special_function */ + ppc64_elf_ha_reloc, /* special_function */ "R_PPC64_ADDR16_HIGHESTA", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -832,7 +798,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { 0xffffffffffffffff, /* dst_mask */ true), /* pcrel_offset */ - /* 64-bit relocation to the symbol's procedure linkage table. */ + /* 64-bit relocation to the symbol's procedure linkage table. */ HOWTO (R_PPC64_PLT64, /* type */ 0, /* rightshift */ 4, /* size (0=byte, 1=short, 2=long, 4=64 bits) */ @@ -840,7 +806,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_unhandled_reloc, /* special_function */ "R_PPC64_PLT64", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -857,7 +823,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { true, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_unhandled_reloc, /* special_function */ "R_PPC64_PLTREL64", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -874,7 +840,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_toc_reloc, /* special_function */ "R_PPC64_TOC16", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -891,7 +857,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_toc_reloc, /* special_function */ "R_PPC64_TOC16_LO", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -908,7 +874,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_toc_reloc, /* special_function */ "R_PPC64_TOC16_HI", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -927,7 +893,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ - ppc64_elf_addr16_ha_reloc, /* special_function */ + ppc64_elf_toc_ha_reloc, /* special_function */ "R_PPC64_TOC16_HA", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -944,7 +910,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_toc64_reloc, /* special_function */ "R_PPC64_TOC", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -967,7 +933,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_unhandled_reloc, /* special_function */ "R_PPC64_PLTGOT16", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -983,7 +949,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_unhandled_reloc, /* special_function */ "R_PPC64_PLTGOT16_LO", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -999,7 +965,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_unhandled_reloc, /* special_function */ "R_PPC64_PLTGOT16_HI", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -1017,7 +983,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ - ppc64_elf_addr16_ha_reloc, /* special_function */ + ppc64_elf_unhandled_reloc, /* special_function */ "R_PPC64_PLTGOT16_HA", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -1062,7 +1028,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_unhandled_reloc, /* special_function */ "R_PPC64_GOT16_DS", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -1077,7 +1043,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_unhandled_reloc, /* special_function */ "R_PPC64_GOT16_LO_DS", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -1092,7 +1058,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_unhandled_reloc, /* special_function */ "R_PPC64_PLT16_LO_DS", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -1100,21 +1066,19 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false), /* pcrel_offset */ /* Like R_PPC64_SECTOFF, but for instructions with a DS field. */ - /* FIXME: Verify R_PPC64_SECTOFF. Seems strange with size=2 and - dst_mask=0. */ HOWTO (R_PPC64_SECTOFF_DS, /* type */ 0, /* rightshift */ - 2, /* size (0 = byte, 1 = short, 2 = long) */ - 32, /* bitsize */ - true, /* pc_relative */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + false, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_sectoff_reloc, /* special_function */ "R_PPC64_SECTOFF_DS", /* name */ false, /* partial_inplace */ 0, /* src_mask */ - 0, /* dst_mask */ - true), /* pcrel_offset */ + 0xfffc, /* dst_mask */ + false), /* pcrel_offset */ /* Like R_PPC64_SECTOFF_LO, but for instructions with a DS field. */ HOWTO (R_PPC64_SECTOFF_LO_DS, /* type */ @@ -1124,7 +1088,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_sectoff_reloc, /* special_function */ "R_PPC64_SECTOFF_LO_DS",/* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -1139,7 +1103,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_toc_reloc, /* special_function */ "R_PPC64_TOC16_DS", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -1154,7 +1118,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_toc_reloc, /* special_function */ "R_PPC64_TOC16_LO_DS", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -1170,7 +1134,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_unhandled_reloc, /* special_function */ "R_PPC64_PLTGOT16_DS", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -1186,7 +1150,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ - bfd_elf_generic_reloc, /* special_function */ + ppc64_elf_unhandled_reloc, /* special_function */ "R_PPC64_PLTGOT16_LO_DS",/* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -1312,7 +1276,7 @@ ppc64_elf_reloc_type_lookup (abfd, code) break; case BFD_RELOC_HI16_S_PLTOFF: ppc_reloc = R_PPC64_PLT16_HA; break; - case BFD_RELOC_32_BASEREL: ppc_reloc = R_PPC64_SECTOFF; + case BFD_RELOC_16_BASEREL: ppc_reloc = R_PPC64_SECTOFF; break; case BFD_RELOC_LO16_BASEREL: ppc_reloc = R_PPC64_SECTOFF_LO; break; @@ -1397,8 +1361,8 @@ ppc64_elf_info_to_howto (abfd, cache_ptr, dst) { unsigned int type; + /* Initialize howto table if needed. */ if (!ppc64_elf_howto_table[R_PPC64_ADDR32]) - /* Initialize howto table if needed. */ ppc_howto_init (); type = ELF64_R_TYPE (dst->r_info); @@ -1410,41 +1374,281 @@ ppc64_elf_info_to_howto (abfd, cache_ptr, dst) /* Handle the R_PPC_ADDR16_HA and similar relocs. */ static bfd_reloc_status_type -ppc64_elf_addr16_ha_reloc (abfd, reloc_entry, symbol, data, input_section, - output_bfd, error_message) - bfd *abfd ATTRIBUTE_UNUSED; +ppc64_elf_ha_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message) + bfd *abfd; + arelent *reloc_entry; + asymbol *symbol; + PTR data; + asection *input_section; + bfd *output_bfd; + char **error_message; +{ + /* If this is a relocatable link (output_bfd test tells us), just + call the generic function. Any adjustment will be done at final + link time. */ + if (output_bfd != NULL) + return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message); + + /* Adjust the addend for sign extension of the low 16 bits. + We won't actually be using the low 16 bits, so trashing them + doesn't matter. */ + reloc_entry->addend += 0x8000; + return bfd_reloc_continue; +} + +static bfd_reloc_status_type +ppc64_elf_brtaken_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message) + bfd *abfd; arelent *reloc_entry; asymbol *symbol; - PTR data ATTRIBUTE_UNUSED; + PTR data; asection *input_section; bfd *output_bfd; - char **error_message ATTRIBUTE_UNUSED; + char **error_message; { - bfd_vma relocation; + long insn; + enum elf_ppc_reloc_type r_type; + bfd_size_type octets; + /* Disabled until we sort out how ld should choose 'y' vs 'at'. */ + boolean is_power4 = false; + /* If this is a relocatable link (output_bfd test tells us), just + call the generic function. Any adjustment will be done at final + link time. */ if (output_bfd != NULL) + return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message); + + octets = reloc_entry->address * bfd_octets_per_byte (abfd); + insn = bfd_get_32 (abfd, (bfd_byte *) data + octets); + insn &= ~(0x01 << 21); + r_type = (enum elf_ppc_reloc_type) reloc_entry->howto->type; + if (r_type == R_PPC64_ADDR14_BRTAKEN + || r_type == R_PPC64_REL14_BRTAKEN) + insn |= 0x01 << 21; /* 'y' or 't' bit, lowest bit of BO field. */ + + if (is_power4) + { + /* Set 'a' bit. This is 0b00010 in BO field for branch + on CR(BI) insns (BO == 001at or 011at), and 0b01000 + for branch on CTR insns (BO == 1a00t or 1a01t). */ + if ((insn & (0x14 << 21)) == (0x04 << 21)) + insn |= 0x02 << 21; + else if ((insn & (0x14 << 21)) == (0x10 << 21)) + insn |= 0x08 << 21; + else + return bfd_reloc_continue; + } + else { - reloc_entry->address += input_section->output_offset; - return bfd_reloc_ok; + bfd_vma target = 0; + bfd_vma from; + + if (!bfd_is_com_section (symbol->section)) + target = symbol->value; + target += symbol->section->output_section->vma; + target += symbol->section->output_offset; + target += reloc_entry->addend; + + from = (reloc_entry->address + + input_section->output_offset + + input_section->output_section->vma); + + /* Invert 'y' bit if not the default. */ + if ((bfd_signed_vma) (target - from) < 0) + insn ^= 0x01 << 21; } + bfd_put_32 (abfd, (bfd_vma) insn, (bfd_byte *) data + octets); + return bfd_reloc_continue; +} - if (reloc_entry->address > input_section->_cooked_size) - return bfd_reloc_outofrange; +static bfd_reloc_status_type +ppc64_elf_sectoff_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message) + bfd *abfd; + arelent *reloc_entry; + asymbol *symbol; + PTR data; + asection *input_section; + bfd *output_bfd; + char **error_message; +{ + /* If this is a relocatable link (output_bfd test tells us), just + call the generic function. Any adjustment will be done at final + link time. */ + if (output_bfd != NULL) + return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message); - if (bfd_is_com_section (symbol->section)) - relocation = 0; - else - relocation = symbol->value; + /* Subtract the symbol section base address. */ + reloc_entry->addend -= symbol->section->output_section->vma; + return bfd_reloc_continue; +} + +static bfd_reloc_status_type +ppc64_elf_sectoff_ha_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message) + bfd *abfd; + arelent *reloc_entry; + asymbol *symbol; + PTR data; + asection *input_section; + bfd *output_bfd; + char **error_message; +{ + /* If this is a relocatable link (output_bfd test tells us), just + call the generic function. Any adjustment will be done at final + link time. */ + if (output_bfd != NULL) + return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message); + + /* Subtract the symbol section base address. */ + reloc_entry->addend -= symbol->section->output_section->vma; + + /* Adjust the addend for sign extension of the low 16 bits. */ + reloc_entry->addend += 0x8000; + return bfd_reloc_continue; +} + +static bfd_reloc_status_type +ppc64_elf_toc_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message) + bfd *abfd; + arelent *reloc_entry; + asymbol *symbol; + PTR data; + asection *input_section; + bfd *output_bfd; + char **error_message; +{ + bfd_vma TOCstart; + + /* If this is a relocatable link (output_bfd test tells us), just + call the generic function. Any adjustment will be done at final + link time. */ + if (output_bfd != NULL) + return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message); + + TOCstart = _bfd_get_gp_value (input_section->output_section->owner); + if (TOCstart == 0) + TOCstart = ppc64_elf_toc (input_section->output_section->owner); + + /* Subtract the TOC base address. */ + reloc_entry->addend -= TOCstart + TOC_BASE_OFF; + return bfd_reloc_continue; +} + +static bfd_reloc_status_type +ppc64_elf_toc_ha_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message) + bfd *abfd; + arelent *reloc_entry; + asymbol *symbol; + PTR data; + asection *input_section; + bfd *output_bfd; + char **error_message; +{ + bfd_vma TOCstart; + + /* If this is a relocatable link (output_bfd test tells us), just + call the generic function. Any adjustment will be done at final + link time. */ + if (output_bfd != NULL) + return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message); - relocation += symbol->section->output_section->vma; - relocation += symbol->section->output_offset; - relocation += reloc_entry->addend; + TOCstart = _bfd_get_gp_value (input_section->output_section->owner); + if (TOCstart == 0) + TOCstart = ppc64_elf_toc (input_section->output_section->owner); - reloc_entry->addend += (relocation & 0x8000) << 1; + /* Subtract the TOC base address. */ + reloc_entry->addend -= TOCstart + TOC_BASE_OFF; + /* Adjust the addend for sign extension of the low 16 bits. */ + reloc_entry->addend += 0x8000; return bfd_reloc_continue; } +static bfd_reloc_status_type +ppc64_elf_toc64_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message) + bfd *abfd; + arelent *reloc_entry; + asymbol *symbol; + PTR data; + asection *input_section; + bfd *output_bfd; + char **error_message; +{ + bfd_vma TOCstart; + bfd_size_type octets; + + /* If this is a relocatable link (output_bfd test tells us), just + call the generic function. Any adjustment will be done at final + link time. */ + if (output_bfd != NULL) + return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message); + + TOCstart = _bfd_get_gp_value (input_section->output_section->owner); + if (TOCstart == 0) + TOCstart = ppc64_elf_toc (input_section->output_section->owner); + + octets = reloc_entry->address * bfd_octets_per_byte (abfd); + bfd_put_64 (abfd, TOCstart + TOC_BASE_OFF, (bfd_byte *) data + octets); + return bfd_reloc_ok; +} + +static bfd_reloc_status_type +ppc64_elf_unhandled_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message) + bfd *abfd; + arelent *reloc_entry; + asymbol *symbol; + PTR data; + asection *input_section; + bfd *output_bfd; + char **error_message; +{ + /* If this is a relocatable link (output_bfd test tells us), just + call the generic function. Any adjustment will be done at final + link time. */ + if (output_bfd != NULL) + return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message); + + if (error_message != NULL) + { + static char buf[60]; + sprintf (buf, "generic linker can't handle %s", + reloc_entry->howto->name); + *error_message = buf; + } + return bfd_reloc_dangerous; +} + +/* Return symbol info as per usual for ELF targets, except that + symbols in .opd are given 'd' or 'D' for type. */ + +static void +ppc64_elf_get_symbol_info (abfd, symbol, ret) + bfd *abfd; + asymbol *symbol; + symbol_info *ret; +{ + _bfd_elf_get_symbol_info (abfd, symbol, ret); + if (ret->type == '?' + && (symbol->flags & (BSF_GLOBAL | BSF_LOCAL)) != 0 + && strcmp (symbol->section->name, ".opd") == 0) + ret->type = (symbol->flags & BSF_GLOBAL) != 0 ? 'D' : 'd'; +} + /* Function to set whether a module needs the -mrelocatable bit set. */ static boolean @@ -1571,7 +1775,7 @@ static boolean ppc64_elf_section_from_shdr (abfd, hdr, name) bfd *abfd; Elf64_Internal_Shdr *hdr; - char *name; + const char *name; { asection *newsect; flagword flags; @@ -1683,22 +1887,98 @@ struct ppc_dyn_relocs selects between relative and absolute types. */ #define IS_ABSOLUTE_RELOC(RTYPE) \ - ((RTYPE) != R_PPC64_REL14 \ - && (RTYPE) != R_PPC64_REL14_BRNTAKEN \ - && (RTYPE) != R_PPC64_REL14_BRTAKEN \ - && (RTYPE) != R_PPC64_REL24 \ - && (RTYPE) != R_PPC64_REL32 \ - && (RTYPE) != R_PPC64_REL64) + ((RTYPE) != R_PPC64_REL32 \ + && (RTYPE) != R_PPC64_REL64 \ + && (RTYPE) != R_PPC64_ADDR30) + +/* Section name for stubs is the associated section name plus this + string. */ +#define STUB_SUFFIX ".stub" + +/* Linker stubs. + ppc_stub_long_branch: + Used when a 14 bit branch (or even a 24 bit branch) can't reach its + destination, but a 24 bit branch in a stub section will reach. + . b dest + + ppc_stub_plt_branch: + Similar to the above, but a 24 bit branch in the stub section won't + reach its destination. + . addis %r12,%r2,xxx@ha + . ld %r11,xxx@l(%r12) + . mtctr %r11 + . bctr + + ppc_stub_plt_call: + Used to call a function in a shared library. + . addis %r12,%r2,xxx@ha + . std %r2,40(%r1) + . ld %r11,xxx+0@l(%r12) + . ld %r2,xxx+8@l(%r12) + . mtctr %r11 + . ld %r11,xxx+16@l(%r12) + . bctr +*/ + +enum ppc_stub_type { + ppc_stub_none, + ppc_stub_long_branch, + ppc_stub_plt_branch, + ppc_stub_plt_call +}; + +struct ppc_stub_hash_entry { + + /* Base hash table entry structure. */ + struct bfd_hash_entry root; + + /* The stub section. */ + asection *stub_sec; + + /* Offset within stub_sec of the beginning of this stub. */ + bfd_vma stub_offset; -/* ppc64 ELF linker hash entry. */ + /* Given the symbol's value and its section we can determine its final + value when building the stubs (so the stub knows where to jump. */ + bfd_vma target_value; + asection *target_section; + + enum ppc_stub_type stub_type; + + /* The symbol table entry, if any, that this was derived from. */ + struct ppc_link_hash_entry *h; + + /* Where this stub is being called from, or, in the case of combined + stub sections, the first input section in the group. */ + asection *id_sec; +}; + +struct ppc_branch_hash_entry { + + /* Base hash table entry structure. */ + struct bfd_hash_entry root; + + /* Offset within .branch_lt. */ + unsigned int offset; + + /* Generation marker. */ + unsigned int iter; +}; struct ppc_link_hash_entry { struct elf_link_hash_entry elf; + /* A pointer to the most recently used stub hash entry against this + symbol. */ + struct ppc_stub_hash_entry *stub_cache; + /* Track dynamic relocs copied for this symbol. */ struct ppc_dyn_relocs *dyn_relocs; + /* Link between function code and descriptor symbols. */ + struct elf_link_hash_entry *oh; + /* Flag function code and descriptor symbols. */ unsigned int is_func:1; unsigned int is_func_descriptor:1; @@ -1710,6 +1990,34 @@ struct ppc_link_hash_table { struct elf_link_hash_table elf; + /* The stub hash table. */ + struct bfd_hash_table stub_hash_table; + + /* Another hash table for plt_branch stubs. */ + struct bfd_hash_table branch_hash_table; + + /* Linker stub bfd. */ + bfd *stub_bfd; + + /* Linker call-backs. */ + asection * (*add_stub_section) PARAMS ((const char *, asection *)); + void (*layout_sections_again) PARAMS ((void)); + + /* Array to keep track of which stub sections have been created, and + information on stub grouping. */ + struct map_stub { + /* This is the section to which stubs in the group will be attached. */ + asection *link_sec; + /* The stub section. */ + asection *stub_sec; + } *stub_group; + + /* Assorted information used by ppc64_elf_size_stubs. */ + unsigned int bfd_count; + int top_index; + asection **input_list; + Elf_Internal_Sym **all_local_syms; + /* Short-cuts to get to dynamic linker sections. */ asection *sgot; asection *srelgot; @@ -1717,27 +2025,192 @@ struct ppc_link_hash_table asection *srelplt; asection *sdynbss; asection *srelbss; - asection *sstub; asection *sglink; + asection *sfpr; + asection *sbrlt; + asection *srelbrlt; /* Set on error. */ - int plt_overflow; + unsigned int stub_error; - /* Small local sym to section mapping cache. */ - struct sym_sec_cache sym_sec; -}; + /* Flag set when small branches are detected. Used to + select suitable defaults for the stub group size. */ + unsigned int has_14bit_branch; -/* Get the ppc64 ELF linker hash table from a link_info structure. */ + /* Set if we detect a reference undefined weak symbol. */ + unsigned int have_undefweak; -#define ppc_hash_table(p) \ - ((struct ppc_link_hash_table *) ((p)->hash)) + /* Incremented every time we size stubs. */ + unsigned int stub_iteration; -/* Create an entry in a ppc64 ELF linker hash table. */ + /* Small local sym to section mapping cache. */ + struct sym_sec_cache sym_sec; +}; -static struct bfd_hash_entry * -link_hash_newfunc (entry, table, string) - struct bfd_hash_entry *entry; - struct bfd_hash_table *table; +static struct bfd_hash_entry *stub_hash_newfunc + PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *)); +static struct bfd_hash_entry *branch_hash_newfunc + PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *)); +static struct bfd_hash_entry *link_hash_newfunc + PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *)); +static struct bfd_link_hash_table *ppc64_elf_link_hash_table_create + PARAMS ((bfd *)); +static void ppc64_elf_link_hash_table_free + PARAMS ((struct bfd_link_hash_table *)); +static char *ppc_stub_name + PARAMS ((const asection *, const asection *, + const struct ppc_link_hash_entry *, const Elf_Internal_Rela *)); +static struct ppc_stub_hash_entry *ppc_get_stub_entry + PARAMS ((const asection *, const asection *, struct elf_link_hash_entry *, + const Elf_Internal_Rela *, struct ppc_link_hash_table *)); +static struct ppc_stub_hash_entry *ppc_add_stub + PARAMS ((const char *, asection *, struct ppc_link_hash_table *)); +static boolean create_linkage_sections + PARAMS ((bfd *, struct bfd_link_info *)); +static boolean create_got_section + PARAMS ((bfd *, struct bfd_link_info *)); +static boolean ppc64_elf_create_dynamic_sections + PARAMS ((bfd *, struct bfd_link_info *)); +static void ppc64_elf_copy_indirect_symbol + PARAMS ((struct elf_link_hash_entry *, struct elf_link_hash_entry *)); +static boolean ppc64_elf_check_relocs + PARAMS ((bfd *, struct bfd_link_info *, asection *, + const Elf_Internal_Rela *)); +static asection * ppc64_elf_gc_mark_hook + PARAMS ((bfd *abfd, struct bfd_link_info *info, Elf_Internal_Rela *rel, + struct elf_link_hash_entry *h, Elf_Internal_Sym *sym)); +static boolean ppc64_elf_gc_sweep_hook + PARAMS ((bfd *abfd, struct bfd_link_info *info, asection *sec, + const Elf_Internal_Rela *relocs)); +static boolean func_desc_adjust + PARAMS ((struct elf_link_hash_entry *, PTR)); +static boolean ppc64_elf_func_desc_adjust + PARAMS ((bfd *, struct bfd_link_info *)); +static boolean ppc64_elf_adjust_dynamic_symbol + PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *)); +static void ppc64_elf_hide_symbol + PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *, boolean)); +static boolean allocate_dynrelocs + PARAMS ((struct elf_link_hash_entry *, PTR)); +static boolean readonly_dynrelocs + PARAMS ((struct elf_link_hash_entry *, PTR)); +static enum elf_reloc_type_class ppc64_elf_reloc_type_class + PARAMS ((const Elf_Internal_Rela *)); +static boolean ppc64_elf_size_dynamic_sections + PARAMS ((bfd *, struct bfd_link_info *)); +static INLINE enum ppc_stub_type ppc_type_of_stub + PARAMS ((asection *, const Elf_Internal_Rela *, + struct ppc_link_hash_entry **, bfd_vma)); +static bfd_byte *build_plt_stub + PARAMS ((bfd *, bfd_byte *, int, int)); +static boolean ppc_build_one_stub + PARAMS ((struct bfd_hash_entry *, PTR)); +static boolean ppc_size_one_stub + PARAMS ((struct bfd_hash_entry *, PTR)); +static void group_sections + PARAMS ((struct ppc_link_hash_table *, bfd_size_type, boolean)); +static boolean get_local_syms + PARAMS ((bfd *, struct ppc_link_hash_table *)); +static boolean ppc64_elf_fake_sections + PARAMS ((bfd *, Elf64_Internal_Shdr *, asection *)); +static boolean ppc64_elf_relocate_section + PARAMS ((bfd *, struct bfd_link_info *info, bfd *, asection *, bfd_byte *, + Elf_Internal_Rela *relocs, Elf_Internal_Sym *local_syms, + asection **)); +static boolean ppc64_elf_finish_dynamic_symbol + PARAMS ((bfd *, struct bfd_link_info *, struct elf_link_hash_entry *, + Elf_Internal_Sym *)); +static boolean ppc64_elf_finish_dynamic_sections + PARAMS ((bfd *, struct bfd_link_info *)); + +/* Get the ppc64 ELF linker hash table from a link_info structure. */ + +#define ppc_hash_table(p) \ + ((struct ppc_link_hash_table *) ((p)->hash)) + +#define ppc_stub_hash_lookup(table, string, create, copy) \ + ((struct ppc_stub_hash_entry *) \ + bfd_hash_lookup ((table), (string), (create), (copy))) + +#define ppc_branch_hash_lookup(table, string, create, copy) \ + ((struct ppc_branch_hash_entry *) \ + bfd_hash_lookup ((table), (string), (create), (copy))) + +/* Create an entry in the stub hash table. */ + +static struct bfd_hash_entry * +stub_hash_newfunc (entry, table, string) + struct bfd_hash_entry *entry; + struct bfd_hash_table *table; + const char *string; +{ + /* Allocate the structure if it has not already been allocated by a + subclass. */ + if (entry == NULL) + { + entry = bfd_hash_allocate (table, sizeof (struct ppc_stub_hash_entry)); + if (entry == NULL) + return entry; + } + + /* Call the allocation method of the superclass. */ + entry = bfd_hash_newfunc (entry, table, string); + if (entry != NULL) + { + struct ppc_stub_hash_entry *eh; + + /* Initialize the local fields. */ + eh = (struct ppc_stub_hash_entry *) entry; + eh->stub_sec = NULL; + eh->stub_offset = 0; + eh->target_value = 0; + eh->target_section = NULL; + eh->stub_type = ppc_stub_none; + eh->h = NULL; + eh->id_sec = NULL; + } + + return entry; +} + +/* Create an entry in the branch hash table. */ + +static struct bfd_hash_entry * +branch_hash_newfunc (entry, table, string) + struct bfd_hash_entry *entry; + struct bfd_hash_table *table; + const char *string; +{ + /* Allocate the structure if it has not already been allocated by a + subclass. */ + if (entry == NULL) + { + entry = bfd_hash_allocate (table, sizeof (struct ppc_branch_hash_entry)); + if (entry == NULL) + return entry; + } + + /* Call the allocation method of the superclass. */ + entry = bfd_hash_newfunc (entry, table, string); + if (entry != NULL) + { + struct ppc_branch_hash_entry *eh; + + /* Initialize the local fields. */ + eh = (struct ppc_branch_hash_entry *) entry; + eh->offset = 0; + eh->iter = 0; + } + + return entry; +} + +/* Create an entry in a ppc64 ELF linker hash table. */ + +static struct bfd_hash_entry * +link_hash_newfunc (entry, table, string) + struct bfd_hash_entry *entry; + struct bfd_hash_table *table; const char *string; { /* Allocate the structure if it has not already been allocated by a @@ -1755,7 +2228,9 @@ link_hash_newfunc (entry, table, string) { struct ppc_link_hash_entry *eh = (struct ppc_link_hash_entry *) entry; + eh->stub_cache = NULL; eh->dyn_relocs = NULL; + eh->oh = NULL; eh->is_func = 0; eh->is_func_descriptor = 0; } @@ -1772,30 +2247,257 @@ ppc64_elf_link_hash_table_create (abfd) struct ppc_link_hash_table *htab; bfd_size_type amt = sizeof (struct ppc_link_hash_table); - htab = (struct ppc_link_hash_table *) bfd_alloc (abfd, amt); + htab = (struct ppc_link_hash_table *) bfd_malloc (amt); if (htab == NULL) return NULL; if (! _bfd_elf_link_hash_table_init (&htab->elf, abfd, link_hash_newfunc)) { - bfd_release (abfd, htab); + free (htab); return NULL; } + /* Init the stub hash table too. */ + if (!bfd_hash_table_init (&htab->stub_hash_table, stub_hash_newfunc)) + return NULL; + + /* And the branch hash table. */ + if (!bfd_hash_table_init (&htab->branch_hash_table, branch_hash_newfunc)) + return NULL; + + htab->stub_bfd = NULL; + htab->add_stub_section = NULL; + htab->layout_sections_again = NULL; + htab->stub_group = NULL; htab->sgot = NULL; htab->srelgot = NULL; htab->splt = NULL; htab->srelplt = NULL; htab->sdynbss = NULL; htab->srelbss = NULL; - htab->sstub = NULL; htab->sglink = NULL; - htab->plt_overflow = 0; + htab->sfpr = NULL; + htab->sbrlt = NULL; + htab->srelbrlt = NULL; + htab->stub_error = 0; + htab->has_14bit_branch = 0; + htab->have_undefweak = 0; + htab->stub_iteration = 0; htab->sym_sec.abfd = NULL; return &htab->elf.root; } +/* Free the derived linker hash table. */ + +static void +ppc64_elf_link_hash_table_free (hash) + struct bfd_link_hash_table *hash; +{ + struct ppc_link_hash_table *ret = (struct ppc_link_hash_table *) hash; + + bfd_hash_table_free (&ret->stub_hash_table); + bfd_hash_table_free (&ret->branch_hash_table); + _bfd_generic_link_hash_table_free (hash); +} + +/* Build a name for an entry in the stub hash table. */ + +static char * +ppc_stub_name (input_section, sym_sec, h, rel) + const asection *input_section; + const asection *sym_sec; + const struct ppc_link_hash_entry *h; + const Elf_Internal_Rela *rel; +{ + char *stub_name; + bfd_size_type len; + + /* rel->r_addend is actually 64 bit, but who uses more than +/- 2^31 + offsets from a sym as a branch target? In fact, we could + probably assume the addend is always zero. */ + BFD_ASSERT (((int) rel->r_addend & 0xffffffff) == rel->r_addend); + + if (h) + { + len = 8 + 1 + strlen (h->elf.root.root.string) + 1 + 8 + 1; + stub_name = bfd_malloc (len); + if (stub_name != NULL) + { + sprintf (stub_name, "%08x_%s+%x", + input_section->id & 0xffffffff, + h->elf.root.root.string, + (int) rel->r_addend & 0xffffffff); + } + } + else + { + len = 8 + 1 + 8 + 1 + 8 + 1 + 16 + 1; + stub_name = bfd_malloc (len); + if (stub_name != NULL) + { + sprintf (stub_name, "%08x_%x:%x+%x", + input_section->id & 0xffffffff, + sym_sec->id & 0xffffffff, + (int) ELF64_R_SYM (rel->r_info) & 0xffffffff, + (int) rel->r_addend & 0xffffffff); + } + } + return stub_name; +} + +/* Look up an entry in the stub hash. Stub entries are cached because + creating the stub name takes a bit of time. */ + +static struct ppc_stub_hash_entry * +ppc_get_stub_entry (input_section, sym_sec, hash, rel, htab) + const asection *input_section; + const asection *sym_sec; + struct elf_link_hash_entry *hash; + const Elf_Internal_Rela *rel; + struct ppc_link_hash_table *htab; +{ + struct ppc_stub_hash_entry *stub_entry; + struct ppc_link_hash_entry *h = (struct ppc_link_hash_entry *) hash; + const asection *id_sec; + + /* If this input section is part of a group of sections sharing one + stub section, then use the id of the first section in the group. + Stub names need to include a section id, as there may well be + more than one stub used to reach say, printf, and we need to + distinguish between them. */ + id_sec = htab->stub_group[input_section->id].link_sec; + + if (h != NULL && h->stub_cache != NULL + && h->stub_cache->h == h + && h->stub_cache->id_sec == id_sec) + { + stub_entry = h->stub_cache; + } + else + { + char *stub_name; + + stub_name = ppc_stub_name (id_sec, sym_sec, h, rel); + if (stub_name == NULL) + return NULL; + + stub_entry = ppc_stub_hash_lookup (&htab->stub_hash_table, + stub_name, false, false); + if (h != NULL) + h->stub_cache = stub_entry; + + free (stub_name); + } + + return stub_entry; +} + +/* Add a new stub entry to the stub hash. Not all fields of the new + stub entry are initialised. */ + +static struct ppc_stub_hash_entry * +ppc_add_stub (stub_name, section, htab) + const char *stub_name; + asection *section; + struct ppc_link_hash_table *htab; +{ + asection *link_sec; + asection *stub_sec; + struct ppc_stub_hash_entry *stub_entry; + + link_sec = htab->stub_group[section->id].link_sec; + stub_sec = htab->stub_group[section->id].stub_sec; + if (stub_sec == NULL) + { + stub_sec = htab->stub_group[link_sec->id].stub_sec; + if (stub_sec == NULL) + { + bfd_size_type len; + char *s_name; + + len = strlen (link_sec->name) + sizeof (STUB_SUFFIX); + s_name = bfd_alloc (htab->stub_bfd, len); + if (s_name == NULL) + return NULL; + + strcpy (s_name, link_sec->name); + strcpy (s_name + len - sizeof (STUB_SUFFIX), STUB_SUFFIX); + stub_sec = (*htab->add_stub_section) (s_name, link_sec); + if (stub_sec == NULL) + return NULL; + htab->stub_group[link_sec->id].stub_sec = stub_sec; + } + htab->stub_group[section->id].stub_sec = stub_sec; + } + + /* Enter this entry into the linker stub hash table. */ + stub_entry = ppc_stub_hash_lookup (&htab->stub_hash_table, stub_name, + true, false); + if (stub_entry == NULL) + { + (*_bfd_error_handler) (_("%s: cannot create stub entry %s"), + bfd_archive_filename (section->owner), + stub_name); + return NULL; + } + + stub_entry->stub_sec = stub_sec; + stub_entry->stub_offset = 0; + stub_entry->id_sec = link_sec; + return stub_entry; +} + +/* Create sections for linker generated code. */ + +static boolean +create_linkage_sections (dynobj, info) + bfd *dynobj; + struct bfd_link_info *info; +{ + struct ppc_link_hash_table *htab; + flagword flags; + + htab = ppc_hash_table (info); + + /* Create .sfpr for code to save and restore fp regs. */ + flags = (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_READONLY + | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED); + htab->sfpr = bfd_make_section_anyway (dynobj, ".sfpr"); + if (htab->sfpr == NULL + || ! bfd_set_section_flags (dynobj, htab->sfpr, flags) + || ! bfd_set_section_alignment (dynobj, htab->sfpr, 2)) + return false; + + /* Create .glink for lazy dynamic linking support. */ + htab->sglink = bfd_make_section_anyway (dynobj, ".glink"); + if (htab->sglink == NULL + || ! bfd_set_section_flags (dynobj, htab->sglink, flags) + || ! bfd_set_section_alignment (dynobj, htab->sglink, 2)) + return false; + + /* Create .branch_lt for plt_branch stubs. */ + flags = (SEC_ALLOC | SEC_LOAD + | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED); + htab->sbrlt = bfd_make_section_anyway (dynobj, ".branch_lt"); + if (htab->sbrlt == NULL + || ! bfd_set_section_flags (dynobj, htab->sbrlt, flags) + || ! bfd_set_section_alignment (dynobj, htab->sbrlt, 3)) + return false; + + if (info->shared) + { + flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY + | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED); + htab->srelbrlt = bfd_make_section (dynobj, ".rela.branch_lt"); + if (!htab->srelbrlt + || ! bfd_set_section_flags (dynobj, htab->srelbrlt, flags) + || ! bfd_set_section_alignment (dynobj, htab->srelbrlt, 3)) + return false; + } + return true; +} + /* Create .got and .rela.got sections in DYNOBJ, and set up shortcuts to them in our hash table. */ @@ -1825,8 +2527,7 @@ create_got_section (dynobj, info) return true; } -/* Create the .stub and .glink sections as well as the ordinary - dynamic sections. */ +/* Create the dynamic sections, and set up shortcuts. */ static boolean ppc64_elf_create_dynamic_sections (dynobj, info) @@ -1834,7 +2535,6 @@ ppc64_elf_create_dynamic_sections (dynobj, info) struct bfd_link_info *info; { struct ppc_link_hash_table *htab; - flagword flags; htab = ppc_hash_table (info); if (!htab->sgot && !create_got_section (dynobj, info)) @@ -1853,20 +2553,6 @@ ppc64_elf_create_dynamic_sections (dynobj, info) || (!info->shared && !htab->srelbss)) abort (); - /* Create .stub and .glink for global linkage functions. */ - flags = (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_READONLY - | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED); - htab->sstub = bfd_make_section (dynobj, ".stub"); - if (htab->sstub == NULL - || ! bfd_set_section_flags (dynobj, htab->sstub, flags) - || ! bfd_set_section_alignment (dynobj, htab->sstub, 2)) - return false; - htab->sglink = bfd_make_section (dynobj, ".glink"); - if (htab->sglink == NULL - || ! bfd_set_section_flags (dynobj, htab->sglink, flags) - || ! bfd_set_section_alignment (dynobj, htab->sglink, 3)) - return false; - return true; } @@ -1955,6 +2641,12 @@ ppc64_elf_check_relocs (abfd, info, sec, relocs) sreloc = NULL; is_opd = strcmp (bfd_get_section_name (abfd, sec), ".opd") == 0; + if (htab->elf.dynobj == NULL) + htab->elf.dynobj = abfd; + if (htab->sfpr == NULL + && !create_linkage_sections (htab->elf.dynobj, info)) + return false; + rel_end = relocs + sec->reloc_count; for (rel = relocs; rel < rel_end; rel++) { @@ -1980,13 +2672,9 @@ ppc64_elf_check_relocs (abfd, info, sec, relocs) case R_PPC64_GOT16_LO_DS: /* This symbol requires a global offset table entry. */ - if (htab->sgot == NULL) - { - if (htab->elf.dynobj == NULL) - htab->elf.dynobj = abfd; - if (!create_got_section (htab->elf.dynobj, info)) - return false; - } + if (htab->sgot == NULL + && !create_got_section (htab->elf.dynobj, info)) + return false; if (h != NULL) { @@ -2068,6 +2756,12 @@ ppc64_elf_check_relocs (abfd, info, sec, relocs) return false; break; + case R_PPC64_REL14: + case R_PPC64_REL14_BRTAKEN: + case R_PPC64_REL14_BRNTAKEN: + htab->has_14bit_branch = 1; + /* Fall through. */ + case R_PPC64_REL24: if (h != NULL && h->root.root.string[0] == '.' @@ -2093,22 +2787,16 @@ ppc64_elf_check_relocs (abfd, info, sec, relocs) false, false, false); if (fdh != NULL) { - /* Ensure the function descriptor symbol string is - part of the code symbol string. We aren't - changing the name here, just allowing some tricks - in ppc64_elf_hide_symbol. */ - fdh->root.root.string = h->root.root.string + 1; ((struct ppc_link_hash_entry *) fdh)->is_func_descriptor = 1; + ((struct ppc_link_hash_entry *) fdh)->oh = h; ((struct ppc_link_hash_entry *) h)->is_func = 1; + ((struct ppc_link_hash_entry *) h)->oh = fdh; } } /* Fall through. */ case R_PPC64_REL64: case R_PPC64_REL32: - case R_PPC64_REL14: - case R_PPC64_REL14_BRTAKEN: - case R_PPC64_REL14_BRNTAKEN: case R_PPC64_ADDR14: case R_PPC64_ADDR14_BRNTAKEN: case R_PPC64_ADDR14_BRTAKEN: @@ -2197,9 +2885,6 @@ ppc64_elf_check_relocs (abfd, info, sec, relocs) bfd_set_error (bfd_error_bad_value); } - if (htab->elf.dynobj == NULL) - htab->elf.dynobj = abfd; - dynobj = htab->elf.dynobj; sreloc = bfd_get_section_by_name (dynobj, name); if (sreloc == NULL) @@ -2263,6 +2948,7 @@ ppc64_elf_check_relocs (abfd, info, sec, relocs) break; default: + break; } } @@ -2378,6 +3064,9 @@ ppc64_elf_gc_sweep_hook (abfd, info, sec, relocs) } break; + case R_PPC64_REL14: + case R_PPC64_REL14_BRNTAKEN: + case R_PPC64_REL14_BRTAKEN: case R_PPC64_REL24: if (r_symndx >= symtab_hdr->sh_info) { @@ -2387,9 +3076,6 @@ ppc64_elf_gc_sweep_hook (abfd, info, sec, relocs) } break; - case R_PPC64_REL14: - case R_PPC64_REL14_BRNTAKEN: - case R_PPC64_REL14_BRTAKEN: case R_PPC64_REL32: case R_PPC64_REL64: if (r_symndx >= symtab_hdr->sh_info) @@ -2472,10 +3158,12 @@ func_desc_adjust (h, inf) struct bfd_link_info *info; struct ppc_link_hash_table *htab; - if (h->root.type == bfd_link_hash_indirect - || h->root.type == bfd_link_hash_warning) + 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; + info = (struct bfd_link_info *) inf; htab = ppc_hash_table (info); @@ -2484,18 +3172,23 @@ func_desc_adjust (h, inf) if (!((struct ppc_link_hash_entry *) h)->is_func) return true; + if (h->root.type == bfd_link_hash_undefweak + && (h->elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR)) + htab->have_undefweak = true; + if (h->plt.refcount > 0 && h->root.root.string[0] == '.' && h->root.root.string[1] != '\0') { - struct elf_link_hash_entry *fdh; + struct elf_link_hash_entry *fdh = ((struct ppc_link_hash_entry *) h)->oh; boolean force_local; /* Find the corresponding function descriptor symbol. Create it as undefined if necessary. */ - fdh = elf_link_hash_lookup (&htab->elf, h->root.root.string + 1, - false, false, true); + if (fdh == NULL) + fdh = elf_link_hash_lookup (&htab->elf, h->root.root.string + 1, + false, false, true); if (fdh == NULL && info->shared @@ -2544,7 +3237,8 @@ func_desc_adjust (h, inf) fdh->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT; } ((struct ppc_link_hash_entry *) fdh)->is_func_descriptor = 1; - fdh->root.root.string = h->root.root.string + 1; + ((struct ppc_link_hash_entry *) fdh)->oh = h; + ((struct ppc_link_hash_entry *) h)->oh = fdh; } /* Now that the info is on the function descriptor, clear the @@ -2554,55 +3248,164 @@ func_desc_adjust (h, inf) been imported from another library. Function code syms that are really in the library we must leave global to prevent the linker dragging in a definition from a static library. */ - force_local = (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0; + force_local = (info->shared + && ((h->elf_link_hash_flags + & ELF_LINK_HASH_DEF_REGULAR) == 0 + || fdh == NULL + || (fdh->elf_link_hash_flags + & ELF_LINK_HASH_DEF_REGULAR) == 0 + || (fdh->elf_link_hash_flags + & ELF_LINK_FORCED_LOCAL) != 0)); _bfd_elf_link_hash_hide_symbol (info, h, force_local); } return true; } +#define MIN_SAVE_FPR 14 +#define MAX_SAVE_FPR 31 + /* Called near the start of bfd_elf_size_dynamic_sections. We use - this hook to transfer dynamic linking information gathered so far - on function code symbol entries, to their corresponding function - descriptor symbol entries. */ + this hook to a) provide some gcc support functions, and b) transfer + dynamic linking information gathered so far on function code symbol + entries, to their corresponding function descriptor symbol entries. */ static boolean ppc64_elf_func_desc_adjust (obfd, info) bfd *obfd ATTRIBUTE_UNUSED; struct bfd_link_info *info; { struct ppc_link_hash_table *htab; + unsigned int lowest_savef = MAX_SAVE_FPR + 2; + unsigned int lowest_restf = MAX_SAVE_FPR + 2; + unsigned int i; + struct elf_link_hash_entry *h; + bfd_byte *p; + char sym[10]; htab = ppc_hash_table (info); - elf_link_hash_traverse (&htab->elf, func_desc_adjust, (PTR) info); - return true; -} - -/* Adjust a symbol defined by a dynamic object and referenced by a - regular object. The current definition is in some section of the - dynamic object, but we're not including those sections. We have to - change the definition to something the rest of the link can - understand. */ -static boolean -ppc64_elf_adjust_dynamic_symbol (info, h) - struct bfd_link_info *info; - struct elf_link_hash_entry *h; -{ - struct ppc_link_hash_table *htab; - struct ppc_link_hash_entry * eh; - struct ppc_dyn_relocs *p; - asection *s; - unsigned int power_of_two; + if (htab->sfpr == NULL) + /* We don't have any relocs. */ + return true; - htab = ppc_hash_table (info); + /* First provide any missing ._savef* and ._restf* functions. */ + memcpy (sym, "._savef14", 10); + for (i = MIN_SAVE_FPR; i <= MAX_SAVE_FPR; i++) + { + sym[7] = i / 10 + '0'; + sym[8] = i % 10 + '0'; + h = elf_link_hash_lookup (&htab->elf, sym, false, false, true); + if (h != NULL + && h->root.type == bfd_link_hash_undefined) + { + if (lowest_savef > i) + lowest_savef = i; + h->root.type = bfd_link_hash_defined; + h->root.u.def.section = htab->sfpr; + h->root.u.def.value = (i - lowest_savef) * 4; + h->type = STT_FUNC; + h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR; + _bfd_elf_link_hash_hide_symbol (info, h, info->shared); + } + } - /* Deal with function syms. */ - if (h->type == STT_FUNC - || (h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0) + memcpy (sym, "._restf14", 10); + for (i = MIN_SAVE_FPR; i <= MAX_SAVE_FPR; i++) { - /* Clear procedure linkage table information for any symbol that - won't need a .plt entry. */ - if (!((struct ppc_link_hash_entry *) h)->is_func_descriptor + sym[7] = i / 10 + '0'; + sym[8] = i % 10 + '0'; + h = elf_link_hash_lookup (&htab->elf, sym, false, false, true); + if (h != NULL + && h->root.type == bfd_link_hash_undefined) + { + if (lowest_restf > i) + lowest_restf = i; + h->root.type = bfd_link_hash_defined; + h->root.u.def.section = htab->sfpr; + h->root.u.def.value = ((MAX_SAVE_FPR + 2 - lowest_savef) * 4 + + (i - lowest_restf) * 4); + h->type = STT_FUNC; + h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR; + _bfd_elf_link_hash_hide_symbol (info, h, info->shared); + } + } + + elf_link_hash_traverse (&htab->elf, func_desc_adjust, (PTR) info); + + htab->sfpr->_raw_size = ((MAX_SAVE_FPR + 2 - lowest_savef) * 4 + + (MAX_SAVE_FPR + 2 - lowest_restf) * 4); + + if (htab->sfpr->_raw_size == 0) + { + if (!htab->have_undefweak) + { + _bfd_strip_section_from_output (info, htab->sfpr); + return true; + } + + htab->sfpr->_raw_size = 4; + } + + p = (bfd_byte *) bfd_alloc (htab->elf.dynobj, htab->sfpr->_raw_size); + if (p == NULL) + return false; + htab->sfpr->contents = p; + + for (i = lowest_savef; i <= MAX_SAVE_FPR; i++) + { + unsigned int fpr = i << 21; + unsigned int stackoff = (1 << 16) - (MAX_SAVE_FPR + 1 - i) * 8; + bfd_put_32 (htab->elf.dynobj, STFD_FR0_0R1 + fpr + stackoff, p); + p += 4; + } + if (lowest_savef <= MAX_SAVE_FPR) + { + bfd_put_32 (htab->elf.dynobj, BLR, p); + p += 4; + } + + for (i = lowest_restf; i <= MAX_SAVE_FPR; i++) + { + unsigned int fpr = i << 21; + unsigned int stackoff = (1 << 16) - (MAX_SAVE_FPR + 1 - i) * 8; + bfd_put_32 (htab->elf.dynobj, LFD_FR0_0R1 + fpr + stackoff, p); + p += 4; + } + if (lowest_restf <= MAX_SAVE_FPR + || htab->sfpr->_raw_size == 4) + { + bfd_put_32 (htab->elf.dynobj, BLR, p); + } + + return true; +} + +/* Adjust a symbol defined by a dynamic object and referenced by a + regular object. The current definition is in some section of the + dynamic object, but we're not including those sections. We have to + change the definition to something the rest of the link can + understand. */ + +static boolean +ppc64_elf_adjust_dynamic_symbol (info, h) + struct bfd_link_info *info; + struct elf_link_hash_entry *h; +{ + struct ppc_link_hash_table *htab; + struct ppc_link_hash_entry * eh; + struct ppc_dyn_relocs *p; + asection *s; + unsigned int power_of_two; + + htab = ppc_hash_table (info); + + /* Deal with function syms. */ + if (h->type == STT_FUNC + || (h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0) + { + /* Clear procedure linkage table information for any symbol that + won't need a .plt entry. */ + if (!((struct ppc_link_hash_entry *) h)->is_func_descriptor || h->plt.refcount <= 0 || (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0 || (! info->shared @@ -2717,13 +3520,48 @@ ppc64_elf_hide_symbol (info, h, force_local) if (((struct ppc_link_hash_entry *) h)->is_func_descriptor) { - const char *name; - struct elf_link_hash_entry *fh; - struct ppc_link_hash_table *htab; + struct elf_link_hash_entry *fh = ((struct ppc_link_hash_entry *) h)->oh; - name = h->root.root.string - 1; - htab = ppc_hash_table (info); - fh = elf_link_hash_lookup (&htab->elf, name, false, false, false); + if (fh == NULL) + { + const char *p, *q; + struct ppc_link_hash_table *htab; + char save; + + /* We aren't supposed to use alloca in BFD because on + systems which do not have alloca the version in libiberty + calls xmalloc, which might cause the program to crash + when it runs out of memory. This function doesn't have a + return status, so there's no way to gracefully return an + error. So cheat. We know that string[-1] can be safely + dereferenced; It's either a string in an ELF string + table, or allocated in an objalloc structure. */ + + p = h->root.root.string - 1; + save = *p; + *(char *) p = '.'; + htab = ppc_hash_table (info); + fh = elf_link_hash_lookup (&htab->elf, p, false, false, false); + *(char *) p = save; + + /* Unfortunately, if it so happens that the string we were + looking for was allocated immediately before this string, + then we overwrote the string terminator. That's the only + reason the lookup should fail. */ + if (fh == NULL) + { + q = h->root.root.string + strlen (h->root.root.string); + while (q >= h->root.root.string && *q == *p) + --q, --p; + if (q < h->root.root.string && *p == '.') + fh = elf_link_hash_lookup (&htab->elf, p, false, false, false); + } + if (fh != NULL) + { + ((struct ppc_link_hash_entry *) h)->oh = fh; + ((struct ppc_link_hash_entry *) fh)->oh = h; + } + } if (fh != NULL) _bfd_elf_link_hash_hide_symbol (info, fh, force_local); } @@ -2754,10 +3592,12 @@ allocate_dynrelocs (h, inf) struct ppc_link_hash_entry *eh; struct ppc_dyn_relocs *p; - if (h->root.type == bfd_link_hash_indirect - || h->root.type == bfd_link_hash_warning) + 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; + info = (struct bfd_link_info *) inf; htab = ppc_hash_table (info); @@ -2780,10 +3620,7 @@ allocate_dynrelocs (h, inf) /* Make room for this entry. */ s->_raw_size += PLT_ENTRY_SIZE; - /* Make room for the .stub and .glink code. */ - s = htab->sstub; - s->_raw_size += PLT_CALL_STUB_SIZE; - + /* Make room for the .glink code. */ s = htab->sglink; if (s->_raw_size == 0) s->_raw_size += GLINK_CALL_STUB_SIZE; @@ -2913,6 +3750,9 @@ readonly_dynrelocs (h, inf) struct ppc_link_hash_entry *eh; struct ppc_dyn_relocs *p; + if (h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + eh = (struct ppc_link_hash_entry *) h; for (p = eh->dyn_relocs; p != NULL; p = p->next) { @@ -3034,15 +3874,15 @@ ppc64_elf_size_dynamic_sections (output_bfd, info) relocs = false; for (s = dynobj->sections; s != NULL; s = s->next) { - bfd_vma size; - if ((s->flags & SEC_LINKER_CREATED) == 0) continue; - if (s == htab->splt - || s == htab->sgot - || s == htab->sstub - || s == htab->sglink) + if (s == htab->sbrlt || s == htab->srelbrlt) + /* These haven't been allocated yet; don't strip. */ + continue; + else if (s == htab->splt + || s == htab->sgot + || s == htab->sglink) { /* Strip this section if we don't need it; see the comment below. */ @@ -3083,18 +3923,16 @@ ppc64_elf_size_dynamic_sections (output_bfd, info) continue; } + /* .plt is in the bss section. We don't initialise it. */ + if ((s->flags & SEC_LOAD) == 0) + continue; + /* Allocate memory for the section contents. We use bfd_zalloc here in case unused entries are not reclaimed before the section's contents are written out. This should not happen, but this way if it does, we get a R_PPC64_NONE reloc instead of garbage. */ - size = s->_raw_size; - if (s == htab->sstub) - { - /* .stub may grow. Allocate enough for the maximum growth. */ - size += (size + 65536 + 65535) / 65536 * 4; - } - s->contents = (bfd_byte *) bfd_zalloc (dynobj, size); + s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->_raw_size); if (s->contents == NULL) return false; } @@ -3115,7 +3953,7 @@ ppc64_elf_size_dynamic_sections (output_bfd, info) return false; } - if (htab->splt->_raw_size != 0) + if (htab->splt != NULL && htab->splt->_raw_size != 0) { if (!add_dynamic_entry (DT_PLTGOT, 0) || !add_dynamic_entry (DT_PLTRELSZ, 0) @@ -3157,124 +3995,55 @@ ppc64_elf_size_dynamic_sections (output_bfd, info) return true; } -/* Called after we have seen all the input files/sections, but before - final symbol resolution and section placement has been determined. +/* Determine the type of stub needed, if any, for a call. */ - We use this hook to provide a value for TOCstart, which we store in - the output bfd elf_gp. */ - -boolean -ppc64_elf_set_toc (obfd, info) - bfd *obfd; - struct bfd_link_info *info; +static INLINE enum ppc_stub_type +ppc_type_of_stub (input_sec, rel, hash, destination) + asection *input_sec; + const Elf_Internal_Rela *rel; + struct ppc_link_hash_entry **hash; + bfd_vma destination; { - if (!info->relocateable) - { - asection *s; - bfd_vma TOCstart; + struct ppc_link_hash_entry *h = *hash; + bfd_vma location; + bfd_vma branch_offset; + bfd_vma max_branch_offset; + unsigned int r_type; - /* The TOC consists of sections .got, .toc, .tocbss, .plt in that - order. The TOC starts where the first of these sections starts. */ - s = bfd_get_section_by_name (obfd, ".got"); - if (s == NULL) - s = bfd_get_section_by_name (obfd, ".toc"); - if (s == NULL) - s = bfd_get_section_by_name (obfd, ".tocbss"); - if (s == NULL) - s = bfd_get_section_by_name (obfd, ".plt"); - if (s == NULL) + if (h != NULL) + { + if (h->oh != NULL + && h->oh->plt.offset != (bfd_vma) -1 + && h->oh->dynindx != -1) { - /* This may happen for - o references to TOC base (SYM@toc / TOC[tc0]) without a - .toc directive - o bad linker script - o --gc-sections and empty TOC sections - - FIXME: Warn user? */ - - /* Look for a likely section. We probably won't even be - using TOCstart. */ - for (s = obfd->sections; s != NULL; s = s->next) - if ((s->flags & (SEC_ALLOC | SEC_SMALL_DATA | SEC_READONLY)) - == (SEC_ALLOC | SEC_SMALL_DATA)) - break; - if (s == NULL) - for (s = obfd->sections; s != NULL; s = s->next) - if ((s->flags & (SEC_ALLOC | SEC_SMALL_DATA)) - == (SEC_ALLOC | SEC_SMALL_DATA)) - break; - if (s == NULL) - for (s = obfd->sections; s != NULL; s = s->next) - if ((s->flags & (SEC_ALLOC | SEC_READONLY)) == SEC_ALLOC) - break; - if (s == NULL) - for (s = obfd->sections; s != NULL; s = s->next) - if ((s->flags & SEC_ALLOC) == SEC_ALLOC) - break; + *hash = (struct ppc_link_hash_entry *) h->oh; + return ppc_stub_plt_call; } - TOCstart = 0; - if (s != NULL) - TOCstart = s->output_section->vma + s->output_offset; - - elf_gp (obfd) = TOCstart; + if (h->elf.root.type == bfd_link_hash_undefweak + || h->elf.root.type == bfd_link_hash_undefined) + return ppc_stub_none; } - return true; -} -/* PowerPC64 .plt entries are 24 bytes long, which doesn't divide - evenly into 64k. Sometimes with a large enough .plt, we'll need to - use offsets differing in the high 16 bits when accessing a .plt - entry from a .plt call stub. This function adjusts the size of - .stub to accommodate the extra stub instruction needed in such - cases. */ + /* Determine where the call point is. */ + location = (input_sec->output_offset + + input_sec->output_section->vma + + rel->r_offset); -boolean -ppc64_elf_size_stubs (obfd, info, changed) - bfd *obfd; - struct bfd_link_info *info; - int *changed; -{ - struct ppc_link_hash_table *htab = ppc_hash_table (info); - bfd_vma plt_offset, next_64k; - long base, num, extra; + branch_offset = destination - location; + r_type = ELF64_R_TYPE (rel->r_info); - /* .plt and .stub should be both present, or both absent. */ - if ((htab->splt == NULL || htab->splt->_raw_size == 0) - != (htab->sstub == NULL || htab->sstub->_raw_size == 0)) - abort (); - - /* If no .plt, then nothing to do. */ - if (htab->splt == NULL || htab->splt->_raw_size == 0) - return true; + /* Determine if a long branch stub is needed. */ + max_branch_offset = 1 << 25; + if (r_type != (unsigned int) R_PPC64_REL24) + max_branch_offset = 1 << 15; - plt_offset = (htab->splt->output_section->vma - + htab->splt->output_offset - - elf_gp (obfd)); - next_64k = (plt_offset + 65535) & -65536; + if (branch_offset + max_branch_offset >= 2 * max_branch_offset) + /* We need a stub. Figure out whether a long_branch or plt_branch + is needed later. */ + return ppc_stub_long_branch; - /* If the .plt doesn't have any entries crossing a 64k boundary, - then there is no need for bigger stubs. */ - if (plt_offset + htab->splt->_raw_size <= next_64k) - return true; - - /* OK, so we have at least one transition. Since .plt entries are - 24 bytes long, we'll strike it lucky every 3*64k, with the 64k - boundary between .plt entries. */ - base = next_64k / 65536; - num = (plt_offset + htab->splt->_raw_size - next_64k) / 65536; - extra = (base % 3 + num + 1) * 2 / 3; - - /* Allow one extra instruction for each EXTRA. The change in .stub - may change the location of .toc and .plt. .toc and .plt ought to - move as a group, but someone might be playing with eg. .plt - alignment, so don't allow .stub size to decrease. */ - if (htab->sstub->_cooked_size < htab->sstub->_raw_size + extra * 4) - { - htab->sstub->_cooked_size = htab->sstub->_raw_size + extra * 4; - *changed = true; - } - return true; + return ppc_stub_none; } /* Build a .plt call stub. */ @@ -3309,139 +4078,969 @@ build_plt_stub (obfd, p, offset, glink) return p; } -/* Build the stubs for one function call. */ - static boolean -build_one_stub (h, inf) - struct elf_link_hash_entry *h; - PTR inf; +ppc_build_one_stub (gen_entry, in_arg) + struct bfd_hash_entry *gen_entry; + PTR in_arg; { + struct ppc_stub_hash_entry *stub_entry; + struct ppc_branch_hash_entry *br_entry; struct bfd_link_info *info; struct ppc_link_hash_table *htab; + asection *stub_sec; + bfd *stub_bfd; + bfd_byte *loc; + bfd_byte *p; + unsigned int indx; + bfd_vma off; + int size; - if (h->root.type == bfd_link_hash_indirect - || h->root.type == bfd_link_hash_warning) - return true; + /* Massage our args to the form they really have. */ + stub_entry = (struct ppc_stub_hash_entry *) gen_entry; + info = (struct bfd_link_info *) in_arg; - info = (struct bfd_link_info *) inf; htab = ppc_hash_table (info); + stub_sec = stub_entry->stub_sec; - if (htab->elf.dynamic_sections_created - && h->plt.offset != (bfd_vma) -1 - && ((struct ppc_link_hash_entry *) h)->is_func_descriptor) + /* Make a note of the offset within the stubs for this entry. */ + stub_entry->stub_offset = stub_sec->_cooked_size; + loc = stub_sec->contents + stub_entry->stub_offset; + + stub_bfd = stub_sec->owner; + + switch (stub_entry->stub_type) { - struct elf_link_hash_entry *fh; - asection *s; - bfd_vma plt_r2; - bfd_byte *p; - unsigned int indx; + case ppc_stub_long_branch: + /* Branches are relative. This is where we are going to. */ + off = (stub_entry->target_value + + stub_entry->target_section->output_offset + + stub_entry->target_section->output_section->vma); - fh = elf_link_hash_lookup (&htab->elf, h->root.root.string - 1, - false, false, true); + /* And this is where we are coming from. */ + off -= (stub_entry->stub_offset + + stub_sec->output_offset + + stub_sec->output_section->vma); - if (fh == NULL) - abort (); + BFD_ASSERT (off + (1 << 25) < (bfd_vma) (1 << 26)); - BFD_ASSERT (((struct ppc_link_hash_entry *) fh)->is_func); + bfd_put_32 (stub_bfd, (bfd_vma) B_DOT | (off & 0x3fffffc), loc); + size = 4; + break; - /* Build the .plt call stub. */ - plt_r2 = (htab->splt->output_section->vma - + htab->splt->output_offset - + h->plt.offset - - elf_gp (htab->splt->output_section->owner) - - TOC_BASE_OFF); + case ppc_stub_plt_branch: + br_entry = ppc_branch_hash_lookup (&htab->branch_hash_table, + stub_entry->root.string + 9, + false, false); + if (br_entry == NULL) + { + (*_bfd_error_handler) (_("can't find branch stub `%s'"), + stub_entry->root.string + 9); + htab->stub_error = true; + return false; + } + + off = (stub_entry->target_value + + stub_entry->target_section->output_offset + + stub_entry->target_section->output_section->vma); - if (plt_r2 + 0x80000000 > 0xffffffff - || (plt_r2 & 3) != 0) + bfd_put_64 (htab->sbrlt->owner, off, + htab->sbrlt->contents + br_entry->offset); + + if (info->shared) + { + /* Create a reloc for the branch lookup table entry. */ + Elf_Internal_Rela rela; + Elf64_External_Rela *r; + + rela.r_offset = (br_entry->offset + + htab->sbrlt->output_offset + + htab->sbrlt->output_section->vma); + rela.r_info = ELF64_R_INFO (0, R_PPC64_RELATIVE); + rela.r_addend = off; + + r = (Elf64_External_Rela *) htab->srelbrlt->contents; + r += htab->srelbrlt->reloc_count++; + bfd_elf64_swap_reloca_out (htab->srelbrlt->owner, &rela, r); + } + + off = (br_entry->offset + + htab->sbrlt->output_offset + + htab->sbrlt->output_section->vma + - elf_gp (htab->sbrlt->output_section->owner) + - TOC_BASE_OFF); + + if (off + 0x80000000 > 0xffffffff || (off & 7) != 0) { (*_bfd_error_handler) (_("linkage table error against `%s'"), - h->root.root.string); + stub_entry->root.string); bfd_set_error (bfd_error_bad_value); - htab->plt_overflow = true; + htab->stub_error = true; return false; } - s = htab->sstub; - /* Steal plt.offset to store the stub offset. */ - fh->plt.offset = s->_cooked_size; - p = s->contents + s->_cooked_size; - p = build_plt_stub (s->owner, p, (int) plt_r2, 0); - s->_cooked_size = p - s->contents; + indx = off; + bfd_put_32 (stub_bfd, (bfd_vma) ADDIS_R12_R2 | PPC_HA (indx), loc); + bfd_put_32 (stub_bfd, (bfd_vma) LD_R11_0R12 | PPC_LO (indx), loc + 4); + bfd_put_32 (stub_bfd, (bfd_vma) MTCTR_R11, loc + 8); + bfd_put_32 (stub_bfd, (bfd_vma) BCTR, loc + 12); + size = 16; + break; + case ppc_stub_plt_call: /* Build the .glink lazy link call stub. */ - s = htab->sglink; - p = s->contents + s->_cooked_size; - indx = s->reloc_count; + p = htab->sglink->contents + htab->sglink->_cooked_size; + indx = htab->sglink->reloc_count; if (indx < 0x8000) { - bfd_put_32 (s->owner, LI_R0_0 | indx, p); + bfd_put_32 (htab->sglink->owner, LI_R0_0 | indx, p); p += 4; } else { - bfd_put_32 (s->owner, LIS_R0_0 | PPC_HI (indx), p); + bfd_put_32 (htab->sglink->owner, LIS_R0_0 | PPC_HI (indx), p); p += 4; - bfd_put_32 (s->owner, ORI_R0_R0_0 | PPC_LO (indx), p); + bfd_put_32 (htab->sglink->owner, ORI_R0_R0_0 | PPC_LO (indx), p); p += 4; } - bfd_put_32 (s->owner, B_DOT | ((s->contents - p) & 0x3fffffc), p); + bfd_put_32 (htab->sglink->owner, + B_DOT | ((htab->sglink->contents - p) & 0x3fffffc), p); p += 4; - s->_cooked_size = p - s->contents; - s->reloc_count += 1; + htab->sglink->_cooked_size = p - htab->sglink->contents; + htab->sglink->reloc_count += 1; + + /* Do the best we can for shared libraries built without + exporting ".foo" for each "foo". This can happen when symbol + versioning scripts strip all bar a subset of symbols. */ + if (stub_entry->h->oh->root.type != bfd_link_hash_defined + && stub_entry->h->oh->root.type != bfd_link_hash_defweak) + { + /* Point the symbol at the stub. There may be multiple stubs, + we don't really care; The main thing is to make this sym + defined somewhere. */ + stub_entry->h->oh->root.type = bfd_link_hash_defined; + stub_entry->h->oh->root.u.def.section = stub_entry->stub_sec; + stub_entry->h->oh->root.u.def.value = stub_entry->stub_offset; + } + + /* Now build the stub. */ + off = stub_entry->h->elf.plt.offset; + if (off >= (bfd_vma) -2) + abort (); + + off &= ~ (bfd_vma) 1; + off += (htab->splt->output_offset + + htab->splt->output_section->vma + - elf_gp (htab->splt->output_section->owner) + - TOC_BASE_OFF); + + if (off + 0x80000000 > 0xffffffff || (off & 7) != 0) + { + (*_bfd_error_handler) + (_("linkage table error against `%s'"), + stub_entry->h->elf.root.root.string); + bfd_set_error (bfd_error_bad_value); + htab->stub_error = true; + return false; + } + + p = build_plt_stub (stub_bfd, loc, (int) off, 0); + size = p - loc; + break; + + default: + BFD_FAIL (); + return false; + } + + stub_sec->_cooked_size += size; + return true; +} + +/* As above, but don't actually build the stub. Just bump offset so + we know stub section sizes, and select plt_branch stubs where + long_branch stubs won't do. */ + +static boolean +ppc_size_one_stub (gen_entry, in_arg) + struct bfd_hash_entry *gen_entry; + PTR in_arg; +{ + struct ppc_stub_hash_entry *stub_entry; + struct ppc_link_hash_table *htab; + bfd_vma off; + int size; + + /* Massage our args to the form they really have. */ + stub_entry = (struct ppc_stub_hash_entry *) gen_entry; + htab = (struct ppc_link_hash_table *) in_arg; + + if (stub_entry->stub_type == ppc_stub_plt_call) + { + off = stub_entry->h->elf.plt.offset & ~(bfd_vma) 1; + off += (htab->splt->output_offset + + htab->splt->output_section->vma + - elf_gp (htab->splt->output_section->owner) + - TOC_BASE_OFF); + + size = 28; + if (PPC_HA ((int) off + 16) != PPC_HA ((int) off)) + size += 4; + } + else + { + /* ppc_stub_long_branch or ppc_stub_plt_branch. */ + stub_entry->stub_type = ppc_stub_long_branch; + size = 4; + + off = (stub_entry->target_value + + stub_entry->target_section->output_offset + + stub_entry->target_section->output_section->vma); + off -= (stub_entry->stub_sec->_raw_size + + stub_entry->stub_sec->output_offset + + stub_entry->stub_sec->output_section->vma); + + if (off + (1 << 25) >= (bfd_vma) (1 << 26)) + { + struct ppc_branch_hash_entry *br_entry; + + br_entry = ppc_branch_hash_lookup (&htab->branch_hash_table, + stub_entry->root.string + 9, + true, false); + if (br_entry == NULL) + { + (*_bfd_error_handler) (_("can't build branch stub `%s'"), + stub_entry->root.string + 9); + htab->stub_error = true; + return false; + } + + if (br_entry->iter != htab->stub_iteration) + { + br_entry->iter = htab->stub_iteration; + br_entry->offset = htab->sbrlt->_raw_size; + htab->sbrlt->_raw_size += 8; + } + stub_entry->stub_type = ppc_stub_plt_branch; + size = 16; + } } + + stub_entry->stub_sec->_raw_size += size; return true; } +/* Set up various things so that we can make a list of input sections + for each output section included in the link. Returns -1 on error, + 0 when no stubs will be needed, and 1 on success. */ + +int +ppc64_elf_setup_section_lists (output_bfd, info) + bfd *output_bfd; + struct bfd_link_info *info; +{ + bfd *input_bfd; + unsigned int bfd_count; + int top_id, top_index; + asection *section; + asection **input_list, **list; + bfd_size_type amt; + struct ppc_link_hash_table *htab = ppc_hash_table (info); + + if (htab->elf.root.creator->flavour != bfd_target_elf_flavour + || htab->sbrlt == NULL) + return 0; + + /* Count the number of input BFDs and find the top input section id. */ + for (input_bfd = info->input_bfds, bfd_count = 0, top_id = 0; + input_bfd != NULL; + input_bfd = input_bfd->link_next) + { + bfd_count += 1; + for (section = input_bfd->sections; + section != NULL; + section = section->next) + { + if (top_id < section->id) + top_id = section->id; + } + } + htab->bfd_count = bfd_count; + + amt = sizeof (struct map_stub) * (top_id + 1); + htab->stub_group = (struct map_stub *) bfd_zmalloc (amt); + if (htab->stub_group == NULL) + return -1; + + /* We can't use output_bfd->section_count here to find the top output + section index as some sections may have been removed, and + _bfd_strip_section_from_output doesn't renumber the indices. */ + for (section = output_bfd->sections, top_index = 0; + section != NULL; + section = section->next) + { + if (top_index < section->index) + top_index = section->index; + } + + htab->top_index = top_index; + amt = sizeof (asection *) * (top_index + 1); + input_list = (asection **) bfd_malloc (amt); + htab->input_list = input_list; + if (input_list == NULL) + return -1; + + /* For sections we aren't interested in, mark their entries with a + value we can check later. */ + list = input_list + top_index; + do + *list = bfd_abs_section_ptr; + while (list-- != input_list); + + for (section = output_bfd->sections; + section != NULL; + section = section->next) + { + if ((section->flags & SEC_CODE) != 0) + input_list[section->index] = NULL; + } + + return 1; +} + +/* The linker repeatedly calls this function for each input section, + in the order that input sections are linked into output sections. + Build lists of input sections to determine groupings between which + we may insert linker stubs. */ + +void +ppc64_elf_next_input_section (info, isec) + struct bfd_link_info *info; + asection *isec; +{ + struct ppc_link_hash_table *htab = ppc_hash_table (info); + + if (isec->output_section->index <= htab->top_index) + { + asection **list = htab->input_list + isec->output_section->index; + if (*list != bfd_abs_section_ptr) + { + /* Steal the link_sec pointer for our list. */ +#define PREV_SEC(sec) (htab->stub_group[(sec)->id].link_sec) + /* This happens to make the list in reverse order, + which is what we want. */ + PREV_SEC (isec) = *list; + *list = isec; + } + } +} + +/* See whether we can group stub sections together. Grouping stub + sections may result in fewer stubs. More importantly, we need to + put all .init* and .fini* stubs at the beginning of the .init or + .fini output sections respectively, because glibc splits the + _init and _fini functions into multiple parts. Putting a stub in + the middle of a function is not a good idea. */ + +static void +group_sections (htab, stub_group_size, stubs_always_before_branch) + struct ppc_link_hash_table *htab; + bfd_size_type stub_group_size; + boolean stubs_always_before_branch; +{ + asection **list = htab->input_list + htab->top_index; + do + { + asection *tail = *list; + if (tail == bfd_abs_section_ptr) + continue; + while (tail != NULL) + { + asection *curr; + asection *prev; + bfd_size_type total; + + curr = tail; + if (tail->_cooked_size) + total = tail->_cooked_size; + else + total = tail->_raw_size; + while ((prev = PREV_SEC (curr)) != NULL + && ((total += curr->output_offset - prev->output_offset) + < stub_group_size)) + curr = prev; + + /* OK, the size from the start of CURR to the end is less + than stub_group_size and thus can be handled by one stub + section. (or the tail section is itself larger than + stub_group_size, in which case we may be toast.) We + should really be keeping track of the total size of stubs + added here, as stubs contribute to the final output + section size. That's a little tricky, and this way will + only break if stubs added make the total size more than + 2^25, ie. for the default stub_group_size, if stubs total + more than 2834432 bytes, or over 100000 plt call stubs. */ + do + { + prev = PREV_SEC (tail); + /* Set up this stub group. */ + htab->stub_group[tail->id].link_sec = curr; + } + while (tail != curr && (tail = prev) != NULL); + + /* But wait, there's more! Input sections up to stub_group_size + bytes before the stub section can be handled by it too. */ + if (!stubs_always_before_branch) + { + total = 0; + while (prev != NULL + && ((total += tail->output_offset - prev->output_offset) + < stub_group_size)) + { + tail = prev; + prev = PREV_SEC (tail); + htab->stub_group[tail->id].link_sec = curr; + } + } + tail = prev; + } + } + while (list-- != htab->input_list); + free (htab->input_list); +#undef PREV_SEC +} + +/* Read in all local syms for all input bfds. */ + +static boolean +get_local_syms (input_bfd, htab) + bfd *input_bfd; + struct ppc_link_hash_table *htab; +{ + unsigned int bfd_indx; + Elf_Internal_Sym *local_syms, **all_local_syms; + + /* We want to read in symbol extension records only once. To do this + we need to read in the local symbols in parallel and save them for + later use; so hold pointers to the local symbols in an array. */ + bfd_size_type amt = sizeof (Elf_Internal_Sym *) * htab->bfd_count; + all_local_syms = (Elf_Internal_Sym **) bfd_zmalloc (amt); + htab->all_local_syms = all_local_syms; + if (all_local_syms == NULL) + return false; + + /* Walk over all the input BFDs, swapping in local symbols. + If we are creating a shared library, create hash entries for the + export stubs. */ + for (bfd_indx = 0; + input_bfd != NULL; + input_bfd = input_bfd->link_next, bfd_indx++) + { + Elf_Internal_Shdr *symtab_hdr; + Elf_Internal_Shdr *shndx_hdr; + Elf_Internal_Sym *isym; + Elf64_External_Sym *ext_syms, *esym, *end_sy; + Elf_External_Sym_Shndx *shndx_buf, *shndx; + bfd_size_type sec_size; + + /* We'll need the symbol table in a second. */ + symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; + if (symtab_hdr->sh_info == 0) + continue; + + /* We need an array of the local symbols attached to the input bfd. + Unfortunately, we're going to have to read & swap them in. */ + sec_size = symtab_hdr->sh_info; + sec_size *= sizeof (Elf_Internal_Sym); + local_syms = (Elf_Internal_Sym *) bfd_malloc (sec_size); + if (local_syms == NULL) + return false; + + all_local_syms[bfd_indx] = local_syms; + sec_size = symtab_hdr->sh_info; + sec_size *= sizeof (Elf64_External_Sym); + ext_syms = (Elf64_External_Sym *) bfd_malloc (sec_size); + if (ext_syms == NULL) + return false; + + if (bfd_seek (input_bfd, symtab_hdr->sh_offset, SEEK_SET) != 0 + || bfd_bread ((PTR) ext_syms, sec_size, input_bfd) != sec_size) + { + error_ret_free_ext_syms: + free (ext_syms); + return false; + } + + shndx_buf = NULL; + shndx_hdr = &elf_tdata (input_bfd)->symtab_shndx_hdr; + if (shndx_hdr->sh_size != 0) + { + sec_size = symtab_hdr->sh_info; + sec_size *= sizeof (Elf_External_Sym_Shndx); + shndx_buf = (Elf_External_Sym_Shndx *) bfd_malloc (sec_size); + if (shndx_buf == NULL) + goto error_ret_free_ext_syms; + + if (bfd_seek (input_bfd, shndx_hdr->sh_offset, SEEK_SET) != 0 + || bfd_bread ((PTR) shndx_buf, sec_size, input_bfd) != sec_size) + { + free (shndx_buf); + goto error_ret_free_ext_syms; + } + } + + /* Swap the local symbols in. */ + for (esym = ext_syms, end_sy = esym + symtab_hdr->sh_info, + isym = local_syms, shndx = shndx_buf; + esym < end_sy; + esym++, isym++, shndx = (shndx ? shndx + 1 : NULL)) + bfd_elf64_swap_symbol_in (input_bfd, esym, shndx, isym); + + /* Now we can free the external symbols. */ + free (shndx_buf); + free (ext_syms); + } + + return true; +} + +/* Determine and set the size of the stub section for a final link. + + The basic idea here is to examine all the relocations looking for + PC-relative calls to a target that is unreachable with a "bl" + instruction. */ + boolean -ppc64_elf_build_stubs (obfd, info) +ppc64_elf_size_stubs (output_bfd, stub_bfd, info, group_size, + add_stub_section, layout_sections_again) + bfd *output_bfd; + bfd *stub_bfd; + struct bfd_link_info *info; + bfd_signed_vma group_size; + asection * (*add_stub_section) PARAMS ((const char *, asection *)); + void (*layout_sections_again) PARAMS ((void)); +{ + bfd_size_type stub_group_size; + boolean stubs_always_before_branch; + boolean ret = false; + struct ppc_link_hash_table *htab = ppc_hash_table (info); + + /* Stash our params away. */ + htab->stub_bfd = stub_bfd; + htab->add_stub_section = add_stub_section; + htab->layout_sections_again = layout_sections_again; + stubs_always_before_branch = group_size < 0; + if (group_size < 0) + stub_group_size = -group_size; + else + stub_group_size = group_size; + if (stub_group_size == 1) + { + /* Default values. */ + stub_group_size = 30720000; + if (htab->has_14bit_branch) + stub_group_size = 30000; + } + + group_sections (htab, stub_group_size, stubs_always_before_branch); + + if (! get_local_syms (info->input_bfds, htab)) + { + if (htab->all_local_syms) + goto error_ret_free_local; + return false; + } + + while (1) + { + bfd *input_bfd; + unsigned int bfd_indx; + asection *stub_sec; + boolean stub_changed; + + htab->stub_iteration += 1; + stub_changed = false; + + for (input_bfd = info->input_bfds, bfd_indx = 0; + input_bfd != NULL; + input_bfd = input_bfd->link_next, bfd_indx++) + { + Elf_Internal_Shdr *symtab_hdr; + asection *section; + Elf_Internal_Sym *local_syms; + + /* We'll need the symbol table in a second. */ + symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; + if (symtab_hdr->sh_info == 0) + continue; + + local_syms = htab->all_local_syms[bfd_indx]; + + /* Walk over each section attached to the input bfd. */ + for (section = input_bfd->sections; + section != NULL; + section = section->next) + { + Elf_Internal_Shdr *input_rel_hdr; + Elf64_External_Rela *external_relocs, *erelaend, *erela; + Elf_Internal_Rela *internal_relocs, *irelaend, *irela; + bfd_size_type amt; + + /* If there aren't any relocs, then there's nothing more + to do. */ + if ((section->flags & SEC_RELOC) == 0 + || section->reloc_count == 0) + continue; + + /* If this section is a link-once section that will be + discarded, then don't create any stubs. */ + if (section->output_section == NULL + || section->output_section->owner != output_bfd) + continue; + + /* Allocate space for the external relocations. */ + amt = section->reloc_count; + amt *= sizeof (Elf64_External_Rela); + external_relocs = (Elf64_External_Rela *) bfd_malloc (amt); + if (external_relocs == NULL) + { + goto error_ret_free_local; + } + + /* Likewise for the internal relocations. */ + amt = section->reloc_count; + amt *= sizeof (Elf_Internal_Rela); + internal_relocs = (Elf_Internal_Rela *) bfd_malloc (amt); + if (internal_relocs == NULL) + { + free (external_relocs); + goto error_ret_free_local; + } + + /* Read in the external relocs. */ + input_rel_hdr = &elf_section_data (section)->rel_hdr; + if (bfd_seek (input_bfd, input_rel_hdr->sh_offset, SEEK_SET) != 0 + || bfd_bread ((PTR) external_relocs, + input_rel_hdr->sh_size, + input_bfd) != input_rel_hdr->sh_size) + { + free (external_relocs); + error_ret_free_internal: + free (internal_relocs); + goto error_ret_free_local; + } + + /* Swap in the relocs. */ + erela = external_relocs; + erelaend = erela + section->reloc_count; + irela = internal_relocs; + for (; erela < erelaend; erela++, irela++) + bfd_elf64_swap_reloca_in (input_bfd, erela, irela); + + /* We're done with the external relocs, free them. */ + free (external_relocs); + + /* Now examine each relocation. */ + irela = internal_relocs; + irelaend = irela + section->reloc_count; + for (; irela < irelaend; irela++) + { + unsigned int r_type, r_indx; + enum ppc_stub_type stub_type; + struct ppc_stub_hash_entry *stub_entry; + asection *sym_sec; + bfd_vma sym_value; + bfd_vma destination; + struct ppc_link_hash_entry *hash; + char *stub_name; + const asection *id_sec; + + r_type = ELF64_R_TYPE (irela->r_info); + r_indx = ELF64_R_SYM (irela->r_info); + + if (r_type >= (unsigned int) R_PPC_max) + { + bfd_set_error (bfd_error_bad_value); + goto error_ret_free_internal; + } + + /* Only look for stubs on branch instructions. */ + if (r_type != (unsigned int) R_PPC64_REL24 + && r_type != (unsigned int) R_PPC64_REL14 + && r_type != (unsigned int) R_PPC64_REL14_BRTAKEN + && r_type != (unsigned int) R_PPC64_REL14_BRNTAKEN) + continue; + + /* Now determine the call target, its name, value, + section. */ + sym_sec = NULL; + sym_value = 0; + destination = 0; + hash = NULL; + if (r_indx < symtab_hdr->sh_info) + { + /* It's a local symbol. */ + Elf_Internal_Sym *sym; + Elf_Internal_Shdr *hdr; + + sym = local_syms + r_indx; + hdr = elf_elfsections (input_bfd)[sym->st_shndx]; + sym_sec = hdr->bfd_section; + if (ELF_ST_TYPE (sym->st_info) != STT_SECTION) + sym_value = sym->st_value; + destination = (sym_value + irela->r_addend + + sym_sec->output_offset + + sym_sec->output_section->vma); + } + else + { + /* It's an external symbol. */ + int e_indx; + + e_indx = r_indx - symtab_hdr->sh_info; + hash = ((struct ppc_link_hash_entry *) + elf_sym_hashes (input_bfd)[e_indx]); + + while (hash->elf.root.type == bfd_link_hash_indirect + || hash->elf.root.type == bfd_link_hash_warning) + hash = ((struct ppc_link_hash_entry *) + hash->elf.root.u.i.link); + + if (hash->elf.root.type == bfd_link_hash_defined + || hash->elf.root.type == bfd_link_hash_defweak) + { + sym_sec = hash->elf.root.u.def.section; + sym_value = hash->elf.root.u.def.value; + if (sym_sec->output_section != NULL) + destination = (sym_value + irela->r_addend + + sym_sec->output_offset + + sym_sec->output_section->vma); + } + else if (hash->elf.root.type == bfd_link_hash_undefweak) + ; + else if (hash->elf.root.type == bfd_link_hash_undefined) + ; + else + { + bfd_set_error (bfd_error_bad_value); + goto error_ret_free_internal; + } + } + + /* Determine what (if any) linker stub is needed. */ + stub_type = ppc_type_of_stub (section, irela, &hash, + destination); + if (stub_type == ppc_stub_none) + continue; + + /* Support for grouping stub sections. */ + id_sec = htab->stub_group[section->id].link_sec; + + /* Get the name of this stub. */ + stub_name = ppc_stub_name (id_sec, sym_sec, hash, irela); + if (!stub_name) + goto error_ret_free_internal; + + stub_entry = ppc_stub_hash_lookup (&htab->stub_hash_table, + stub_name, false, false); + if (stub_entry != NULL) + { + /* The proper stub has already been created. */ + free (stub_name); + continue; + } + + stub_entry = ppc_add_stub (stub_name, section, htab); + if (stub_entry == NULL) + { + free (stub_name); + goto error_ret_free_local; + } + + stub_entry->target_value = sym_value; + stub_entry->target_section = sym_sec; + stub_entry->stub_type = stub_type; + stub_entry->h = hash; + stub_changed = true; + } + + /* We're done with the internal relocs, free them. */ + free (internal_relocs); + } + } + + if (!stub_changed) + break; + + /* OK, we've added some stubs. Find out the new size of the + stub sections. */ + for (stub_sec = htab->stub_bfd->sections; + stub_sec != NULL; + stub_sec = stub_sec->next) + { + stub_sec->_raw_size = 0; + stub_sec->_cooked_size = 0; + } + htab->sbrlt->_raw_size = 0; + htab->sbrlt->_cooked_size = 0; + + bfd_hash_traverse (&htab->stub_hash_table, ppc_size_one_stub, htab); + + /* Ask the linker to do its stuff. */ + (*htab->layout_sections_again) (); + } + + /* It would be nice to strip .branch_lt from the output if the + section is empty, but it's too late. If we strip sections here, + the dynamic symbol table is corrupted since the section symbol + for the stripped section isn't written. */ + + ret = true; + + error_ret_free_local: + while (htab->bfd_count-- > 0) + if (htab->all_local_syms[htab->bfd_count]) + free (htab->all_local_syms[htab->bfd_count]); + free (htab->all_local_syms); + + return ret; +} + +/* Called after we have determined section placement. If sections + move, we'll be called again. Provide a value for TOCstart. */ + +bfd_vma +ppc64_elf_toc (obfd) bfd *obfd; +{ + asection *s; + bfd_vma TOCstart; + + /* The TOC consists of sections .got, .toc, .tocbss, .plt in that + order. The TOC starts where the first of these sections starts. */ + s = bfd_get_section_by_name (obfd, ".got"); + if (s == NULL) + s = bfd_get_section_by_name (obfd, ".toc"); + if (s == NULL) + s = bfd_get_section_by_name (obfd, ".tocbss"); + if (s == NULL) + s = bfd_get_section_by_name (obfd, ".plt"); + if (s == NULL) + { + /* This may happen for + o references to TOC base (SYM@toc / TOC[tc0]) without a + .toc directive + o bad linker script + o --gc-sections and empty TOC sections + + FIXME: Warn user? */ + + /* Look for a likely section. We probably won't even be + using TOCstart. */ + for (s = obfd->sections; s != NULL; s = s->next) + if ((s->flags & (SEC_ALLOC | SEC_SMALL_DATA | SEC_READONLY)) + == (SEC_ALLOC | SEC_SMALL_DATA)) + break; + if (s == NULL) + for (s = obfd->sections; s != NULL; s = s->next) + if ((s->flags & (SEC_ALLOC | SEC_SMALL_DATA)) + == (SEC_ALLOC | SEC_SMALL_DATA)) + break; + if (s == NULL) + for (s = obfd->sections; s != NULL; s = s->next) + if ((s->flags & (SEC_ALLOC | SEC_READONLY)) == SEC_ALLOC) + break; + if (s == NULL) + for (s = obfd->sections; s != NULL; s = s->next) + if ((s->flags & SEC_ALLOC) == SEC_ALLOC) + break; + } + + TOCstart = 0; + if (s != NULL) + TOCstart = s->output_section->vma + s->output_offset; + + return TOCstart; +} + +/* Build all the stubs associated with the current output file. + The stubs are kept in a hash table attached to the main linker + hash table. This function is called via gldelf64ppc_finish. */ + +boolean +ppc64_elf_build_stubs (info) struct bfd_link_info *info; { struct ppc_link_hash_table *htab = ppc_hash_table (info); - bfd_vma old_stub_size; + asection *stub_sec; bfd_vma plt_r2; bfd_byte *p; - /* If no .plt stubs, then nothing to do. */ - if (htab->sstub == NULL || htab->sstub->_raw_size == 0) - return true; + for (stub_sec = htab->stub_bfd->sections; + stub_sec != NULL; + stub_sec = stub_sec->next) + { + bfd_size_type size; + + /* Allocate memory to hold the linker stubs. */ + size = stub_sec->_raw_size; + if (size != 0) + { + stub_sec->contents = (bfd_byte *) bfd_zalloc (htab->stub_bfd, size); + if (stub_sec->contents == NULL) + return false; + } + stub_sec->_cooked_size = 0; + } - old_stub_size = htab->sstub->_cooked_size; - htab->sstub->_cooked_size = 0; - - /* Build the .glink plt call stub. */ - plt_r2 = (htab->splt->output_section->vma - + htab->splt->output_offset - - elf_gp (obfd) - - TOC_BASE_OFF); - p = htab->sglink->contents; - p = build_plt_stub (htab->sglink->owner, p, (int) plt_r2, 1); - while (p - htab->sglink->contents < GLINK_CALL_STUB_SIZE) + if (htab->splt != NULL) { - bfd_put_32 (htab->sglink->owner, NOP, p); - p += 4; + /* Build the .glink plt call stub. */ + plt_r2 = (htab->splt->output_offset + + htab->splt->output_section->vma + - elf_gp (htab->splt->output_section->owner) + - TOC_BASE_OFF); + p = htab->sglink->contents; + p = build_plt_stub (htab->sglink->owner, p, (int) plt_r2, 1); + while (p - htab->sglink->contents < GLINK_CALL_STUB_SIZE) + { + bfd_put_32 (htab->sglink->owner, NOP, p); + p += 4; + } + htab->sglink->_cooked_size = p - htab->sglink->contents; + + /* Use reloc_count to count entries. */ + htab->sglink->reloc_count = 0; } - htab->sglink->_cooked_size = p - htab->sglink->contents; - /* Use reloc_count to count entries. */ - htab->sglink->reloc_count = 0; + if (htab->sbrlt->_raw_size != 0) + { + htab->sbrlt->contents = (bfd_byte *) bfd_zalloc (htab->sbrlt->owner, + htab->sbrlt->_raw_size); + if (htab->sbrlt->contents == NULL) + return false; + } - elf_link_hash_traverse (&htab->elf, build_one_stub, (PTR) info); + /* Build the stubs as directed by the stub hash table. */ + bfd_hash_traverse (&htab->stub_hash_table, ppc_build_one_stub, info); htab->sglink->reloc_count = 0; - if (htab->plt_overflow) - return false; + for (stub_sec = htab->stub_bfd->sections; + stub_sec != NULL; + stub_sec = stub_sec->next) + { + if (stub_sec->_raw_size != stub_sec->_cooked_size) + break; + } - if (old_stub_size != htab->sstub->_cooked_size + if (stub_sec != NULL || htab->sglink->_raw_size != htab->sglink->_cooked_size) { - (*_bfd_error_handler) - (_("stub section size doesn't match calculated size")); - bfd_set_error (bfd_error_bad_value); - return false; + htab->stub_error = true; + (*_bfd_error_handler) (_("stubs don't match calculated size")); } - return true; + + return !htab->stub_error; } /* Set up any other section flags and such that may be necessary. */ @@ -3511,6 +5110,11 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section, bfd_vma TOCstart; boolean ret = true; boolean is_opd; + /* Disabled until we sort out how ld should choose 'y' vs 'at'. */ + boolean is_power4 = false; + + if (info->relocateable) + return true; /* Initialize howto table if needed. */ if (!ppc64_elf_howto_table[R_PPC64_ADDR32]) @@ -3534,36 +5138,19 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section, Elf_Internal_Sym *sym; asection *sec; struct elf_link_hash_entry *h; + struct elf_link_hash_entry *fdh; const char *sym_name; unsigned long r_symndx; bfd_vma relocation; boolean unresolved_reloc; - boolean has_nop; + boolean warned; long insn; + struct ppc_stub_hash_entry *stub_entry; + bfd_vma max_br_offset; + bfd_vma from; r_type = (enum elf_ppc_reloc_type) ELF64_R_TYPE (rel->r_info); r_symndx = ELF64_R_SYM (rel->r_info); - - if (info->relocateable) - { - /* This is a relocatable link. We don't have to change - anything, unless the reloc is against a section symbol, - in which case we have to adjust according to where the - section symbol winds up in the output section. */ - if (r_symndx < symtab_hdr->sh_info) - { - sym = local_syms + r_symndx; - if ((unsigned) ELF_ST_TYPE (sym->st_info) == STT_SECTION) - { - sec = local_sections[r_symndx]; - rel->r_addend += sec->output_offset + sym->st_value; - } - } - continue; - } - - /* This is a final link. */ - offset = rel->r_offset; addend = rel->r_addend; r = bfd_reloc_other; @@ -3572,6 +5159,7 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section, h = (struct elf_link_hash_entry *) 0; sym_name = (const char *) 0; unresolved_reloc = false; + warned = false; if (r_type == R_PPC64_TOC) { @@ -3586,6 +5174,7 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section, sym_name = ""; relocation = _bfd_elf_rela_local_sym (output_bfd, sym, sec, rel); + /* rel may have changed, update our copy of addend. */ addend = rel->r_addend; } else @@ -3627,6 +5216,7 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section, || info->no_undefined || ELF_ST_VISIBILITY (h->other))))) return false; + warned = true; } } @@ -3640,78 +5230,106 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section, /* Branch taken prediction relocations. */ case R_PPC64_ADDR14_BRTAKEN: case R_PPC64_REL14_BRTAKEN: - insn = 0x01 << 21; /* Set 't' bit, lowest bit of BO field. */ - /* Fall thru. */ + insn = 0x01 << 21; /* 'y' or 't' bit, lowest bit of BO field. */ + /* Fall thru. */ /* Branch not taken prediction relocations. */ case R_PPC64_ADDR14_BRNTAKEN: case R_PPC64_REL14_BRNTAKEN: insn |= bfd_get_32 (output_bfd, contents + offset) & ~(0x01 << 21); - /* Set 'a' bit. This is 0b00010 in BO field for branch on CR(BI) - insns (BO == 001at or 011at), and 0b01000 for branch on CTR - insns (BO == 1a00t or 1a01t). */ - if ((insn & (0x14 << 21)) == (0x04 << 21)) - insn |= 0x02 << 21; - else if ((insn & (0x14 << 21)) == (0x10 << 21)) - insn |= 0x08 << 21; + if (is_power4) + { + /* Set 'a' bit. This is 0b00010 in BO field for branch + on CR(BI) insns (BO == 001at or 011at), and 0b01000 + for branch on CTR insns (BO == 1a00t or 1a01t). */ + if ((insn & (0x14 << 21)) == (0x04 << 21)) + insn |= 0x02 << 21; + else if ((insn & (0x14 << 21)) == (0x10 << 21)) + insn |= 0x08 << 21; + else + break; + } else - break; + { + from = (offset + + input_section->output_offset + + input_section->output_section->vma); + + /* Invert 'y' bit if not the default. */ + if ((bfd_signed_vma) (relocation + addend - from) < 0) + insn ^= 0x01 << 21; + } bfd_put_32 (output_bfd, (bfd_vma) insn, contents + offset); break; case R_PPC64_REL24: - case R_PPC64_ADDR24: - /* An ADDR24 or REL24 branching to a linkage function may be - followed by a nop that we have to replace with a ld in - order to restore the TOC base pointer. Only calls to - shared objects need to alter the TOC base. These are - recognized by their need for a PLT entry. */ - has_nop = 0; + /* A REL24 branching to a linkage function is followed by a + nop. We replace the nop with a ld in order to restore + the TOC base pointer. Only calls to shared objects need + to alter the TOC base. These are recognized by their + need for a PLT entry. */ if (h != NULL - && h->plt.offset != (bfd_vma) -1 - && htab->sstub != NULL) + && (fdh = ((struct ppc_link_hash_entry *) h)->oh) != NULL + && fdh->plt.offset != (bfd_vma) -1 + && (stub_entry = ppc_get_stub_entry (input_section, sec, fdh, + rel, htab)) != NULL) { - /* plt.offset here is the offset into the stub section. */ - relocation = (htab->sstub->output_section->vma - + htab->sstub->output_offset - + h->plt.offset); - unresolved_reloc = false; - - /* Make sure that there really is an instruction after - the branch that we can decode. */ + boolean can_plt_call = 0; + if (offset + 8 <= input_section->_cooked_size) { - bfd_byte *pnext; - - pnext = contents + offset + 4; - insn = bfd_get_32 (input_bfd, pnext); - - if (insn == 0x60000000 /* nop (ori r0,r0,0) */ - || insn == 0x4def7b82 /* cror 15,15,15 */ - || insn == 0x4ffffb82) /* cror 31,31,31 */ + insn = bfd_get_32 (input_bfd, contents + offset + 4); + if (insn == NOP + || insn == CROR_151515 || insn == CROR_313131) { - bfd_put_32 (input_bfd, - (bfd_vma) 0xe8410028, /* ld r2,40(r1) */ - pnext); - has_nop = 1; + bfd_put_32 (input_bfd, (bfd_vma) LD_R2_40R1, + contents + offset + 4); + can_plt_call = 1; } } + + if (!can_plt_call) + { + /* If this is a plain branch rather than a branch + and link, don't require a nop. */ + insn = bfd_get_32 (input_bfd, contents + offset); + if ((insn & 1) == 0) + can_plt_call = 1; + } + + if (can_plt_call) + { + relocation = (stub_entry->stub_offset + + stub_entry->stub_sec->output_offset + + stub_entry->stub_sec->output_section->vma); + addend = 0; + unresolved_reloc = false; + } } if (h != NULL && h->root.type == bfd_link_hash_undefweak - && r_type == R_PPC64_REL24 - && addend == 0 - && relocation == 0) + && relocation == 0 + && addend == 0) { - /* Tweak calls to undefined weak functions to behave as - if the "called" function immediately returns. We can - thus call to a weak function without first checking - whether the function is defined. */ - relocation = 4; - if (has_nop) - relocation = 8; + /* Tweak calls to undefined weak functions to point at a + blr. We can thus call a weak function without first + checking whether the function is defined. We have a + blr at the end of .sfpr. */ + BFD_ASSERT (htab->sfpr->_raw_size != 0); + relocation = (htab->sfpr->_raw_size - 4 + + htab->sfpr->output_offset + + htab->sfpr->output_section->vma); + from = (offset + + input_section->output_offset + + input_section->output_section->vma); + + /* But let's not be silly about it. If the blr isn't in + reach, just go to the next instruction. */ + if (relocation - from + (1 << 25) >= (1 << 26) + || htab->sfpr->_raw_size == 0) + relocation = from + 4; } break; } @@ -3890,14 +5508,14 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section, addend -= sec->output_section->vma; break; + case R_PPC64_REL14: + case R_PPC64_REL14_BRNTAKEN: + case R_PPC64_REL14_BRTAKEN: case R_PPC64_REL24: break; /* Relocations that may need to be propagated if this is a dynamic object. */ - case R_PPC64_REL14: - case R_PPC64_REL14_BRNTAKEN: - case R_PPC64_REL14_BRTAKEN: case R_PPC64_REL32: case R_PPC64_REL64: case R_PPC64_ADDR14: @@ -3962,22 +5580,21 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section, time. */ skip = false; + relocate = false; outrel.r_offset = _bfd_elf_section_offset (output_bfd, info, input_section, rel->r_offset); if (outrel.r_offset == (bfd_vma) -1) skip = true; - + else if (outrel.r_offset == (bfd_vma) -2) + skip = true, relocate = true; outrel.r_offset += (input_section->output_section->vma + input_section->output_offset); outrel.r_addend = addend; if (skip) - { - relocate = false; - memset (&outrel, 0, sizeof outrel); - } + memset (&outrel, 0, sizeof outrel); else if (h != NULL && h->dynindx != -1 && !is_opd @@ -3986,10 +5603,7 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section, || !info->symbolic || (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)) - { - relocate = false; - outrel.r_info = ELF64_R_INFO (h->dynindx, r_type); - } + outrel.r_info = ELF64_R_INFO (h->dynindx, r_type); else { /* This symbol is local, or marked to become local, @@ -3999,6 +5613,16 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section, relocate = true; if (r_type == R_PPC64_ADDR64 || r_type == R_PPC64_TOC) { + if (is_opd && h != NULL && info->shared) + { + /* Lie about opd entries. This case occurs + when building shared libraries and we + reference a function in another shared + lib. In that case we won't use the opd + entry in this lib; We ought to edit the + opd section to remove unused entries. */ + unresolved_reloc = false; + } outrel.r_info = ELF64_R_INFO (0, R_PPC64_RELATIVE); } else @@ -4118,6 +5742,34 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section, continue; } break; + + case R_PPC64_REL14: + case R_PPC64_REL14_BRNTAKEN: + case R_PPC64_REL14_BRTAKEN: + max_br_offset = 1 << 15; + goto branch_check; + + case R_PPC64_REL24: + max_br_offset = 1 << 25; + + branch_check: + /* If the branch is out of reach, then redirect the + call to the local stub for this function. */ + from = (offset + + input_section->output_offset + + input_section->output_section->vma); + if (relocation + addend - from + max_br_offset >= 2 * max_br_offset + && (stub_entry = ppc_get_stub_entry (input_section, sec, h, + rel, htab)) != NULL) + { + /* Munge up the value and addend so that we call the stub + rather than the procedure directly. */ + relocation = (stub_entry->stub_offset + + stub_entry->stub_sec->output_offset + + stub_entry->stub_sec->output_section->vma); + addend = 0; + } + break; } /* FIXME: Why do we allow debugging sections to escape this error? @@ -4128,12 +5780,15 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section, && !(info->shared && (input_section->flags & SEC_DEBUGGING) != 0 && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0)) - (*_bfd_error_handler) - (_("%s(%s+0x%lx): unresolvable relocation against symbol `%s'"), - bfd_archive_filename (input_bfd), - bfd_get_section_name (input_bfd, input_section), - (long) rel->r_offset, - h->root.root.string); + { + (*_bfd_error_handler) + (_("%s(%s+0x%lx): unresolvable relocation against symbol `%s'"), + bfd_archive_filename (input_bfd), + bfd_get_section_name (input_bfd, input_section), + (long) rel->r_offset, + h->root.root.string); + ret = false; + } r = _bfd_final_link_relocate (ppc64_elf_howto_table[(int) r_type], input_bfd, @@ -4143,9 +5798,7 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section, relocation, addend); - if (r == bfd_reloc_ok) - ; - else if (r == bfd_reloc_overflow) + if (r != bfd_reloc_ok) { const char *name; @@ -4176,13 +5829,25 @@ ppc64_elf_relocate_section (output_bfd, info, input_bfd, input_section, name = bfd_section_name (input_bfd, sec); } - if (! ((*info->callbacks->reloc_overflow) - (info, name, ppc64_elf_howto_table[(int) r_type]->name, - (bfd_vma) 0, input_bfd, input_section, offset))) - return false; + if (r == bfd_reloc_overflow) + { + if (warned) + continue; + if (!((*info->callbacks->reloc_overflow) + (info, name, ppc64_elf_howto_table[(int) r_type]->name, + rel->r_addend, input_bfd, input_section, offset))) + return false; + } + else + { + (*_bfd_error_handler) + (_("%s(%s+0x%lx): reloc against `%s': error %d"), + bfd_archive_filename (input_bfd), + bfd_get_section_name (input_bfd, input_section), + (long) rel->r_offset, name, (int) r); + ret = false; + } } - else - ret = false; } return ret; @@ -4458,11 +6123,14 @@ ppc64_elf_finish_dynamic_sections (output_bfd, info) #define elf_backend_plt_header_size PLT_INITIAL_ENTRY_SIZE #define elf_backend_can_gc_sections 1 #define elf_backend_can_refcount 1 +#define elf_backend_rela_normal 1 #define bfd_elf64_bfd_reloc_type_lookup ppc64_elf_reloc_type_lookup #define bfd_elf64_bfd_set_private_flags ppc64_elf_set_private_flags #define bfd_elf64_bfd_merge_private_bfd_data ppc64_elf_merge_private_bfd_data #define bfd_elf64_bfd_link_hash_table_create ppc64_elf_link_hash_table_create +#define bfd_elf64_bfd_link_hash_table_free ppc64_elf_link_hash_table_free +#define bfd_elf64_get_symbol_info ppc64_elf_get_symbol_info #define elf_backend_section_from_shdr ppc64_elf_section_from_shdr #define elf_backend_create_dynamic_sections ppc64_elf_create_dynamic_sections