/* PowerPC64-specific support for 64-bit ELF.
- Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
- 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ Copyright (C) 1999-2015 Free Software Foundation, Inc.
Written by Linus Nordberg, Swox AB <info@swox.com>,
based on elf32-ppc.c by Ian Lance Taylor.
Largely rewritten by Alan Modra.
static bfd_vma opd_entry_value
(asection *, bfd_vma, asection **, bfd_vma *, bfd_boolean);
-#define TARGET_LITTLE_SYM bfd_elf64_powerpcle_vec
+#define TARGET_LITTLE_SYM powerpc_elf64_le_vec
#define TARGET_LITTLE_NAME "elf64-powerpcle"
-#define TARGET_BIG_SYM bfd_elf64_powerpc_vec
+#define TARGET_BIG_SYM powerpc_elf64_vec
#define TARGET_BIG_NAME "elf64-powerpc"
#define ELF_ARCH bfd_arch_powerpc
#define ELF_TARGET_ID PPC64_ELF_DATA
#define ELF_MACHINE_CODE EM_PPC64
#define ELF_MAXPAGESIZE 0x10000
-#define ELF_COMMONPAGESIZE 0x1000
+#define ELF_COMMONPAGESIZE 0x10000
#define elf_info_to_howto ppc64_elf_info_to_howto
#define elf_backend_want_got_sym 0
#define bfd_elf64_mkobject ppc64_elf_mkobject
#define bfd_elf64_bfd_reloc_type_lookup ppc64_elf_reloc_type_lookup
#define bfd_elf64_bfd_reloc_name_lookup ppc64_elf_reloc_name_lookup
-#define bfd_elf64_bfd_merge_private_bfd_data _bfd_generic_verify_endian_match
+#define bfd_elf64_bfd_merge_private_bfd_data ppc64_elf_merge_private_bfd_data
+#define bfd_elf64_bfd_print_private_bfd_data ppc64_elf_print_private_bfd_data
#define bfd_elf64_new_section_hook ppc64_elf_new_section_hook
#define bfd_elf64_bfd_link_hash_table_create ppc64_elf_link_hash_table_create
-#define bfd_elf64_bfd_link_hash_table_free ppc64_elf_link_hash_table_free
#define bfd_elf64_get_synthetic_symtab ppc64_elf_get_synthetic_symtab
#define bfd_elf64_bfd_link_just_syms ppc64_elf_link_just_syms
#define elf_backend_create_dynamic_sections ppc64_elf_create_dynamic_sections
#define elf_backend_copy_indirect_symbol ppc64_elf_copy_indirect_symbol
#define elf_backend_add_symbol_hook ppc64_elf_add_symbol_hook
-#define elf_backend_check_directives ppc64_elf_process_dot_syms
-#define elf_backend_as_needed_cleanup ppc64_elf_as_needed_cleanup
+#define elf_backend_check_directives ppc64_elf_before_check_relocs
+#define elf_backend_notice_as_needed ppc64_elf_notice_as_needed
#define elf_backend_archive_symbol_lookup ppc64_elf_archive_symbol_lookup
#define elf_backend_check_relocs ppc64_elf_check_relocs
#define elf_backend_gc_keep ppc64_elf_gc_keep
#define elf_backend_maybe_function_sym ppc64_elf_maybe_function_sym
#define elf_backend_always_size_sections ppc64_elf_func_desc_adjust
#define elf_backend_size_dynamic_sections ppc64_elf_size_dynamic_sections
+#define elf_backend_hash_symbol ppc64_elf_hash_symbol
#define elf_backend_init_index_section _bfd_elf_init_2_index_sections
#define elf_backend_action_discarded ppc64_elf_action_discarded
#define elf_backend_relocate_section ppc64_elf_relocate_section
#define elf_backend_finish_dynamic_sections ppc64_elf_finish_dynamic_sections
#define elf_backend_link_output_symbol_hook ppc64_elf_output_symbol_hook
#define elf_backend_special_sections ppc64_elf_special_sections
-#define elf_backend_post_process_headers _bfd_elf_set_osabi
+#define elf_backend_merge_symbol_attribute ppc64_elf_merge_symbol_attribute
/* The name of the dynamic interpreter. This is put in the .interp
section. */
#define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so.1"
/* The size in bytes of an entry in the procedure linkage table. */
-#define PLT_ENTRY_SIZE 24
+#define PLT_ENTRY_SIZE(htab) (htab->opd_abi ? 24 : 8)
/* The initial size of the plt reserved for the dynamic linker. */
-#define PLT_INITIAL_ENTRY_SIZE PLT_ENTRY_SIZE
+#define PLT_INITIAL_ENTRY_SIZE(htab) (htab->opd_abi ? 24 : 16)
+
+/* Offsets to some stack save slots. */
+#define STK_LR 16
+#define STK_TOC(htab) (htab->opd_abi ? 40 : 24)
+/* This one is dodgy. ELFv2 does not have a linker word, so use the
+ CR save slot. Used only by optimised __tls_get_addr call stub,
+ relying on __tls_get_addr_opt not saving CR.. */
+#define STK_LINKER(htab) (htab->opd_abi ? 32 : 8)
/* TOC base pointers offset from start of TOC. */
#define TOC_BASE_OFF 0x8000
/* .plt call stub instructions. The normal stub is like this, but
sometimes the .plt entry crosses a 64k boundary and we need to
- insert an addi to adjust r12. */
-#define PLT_CALL_STUB_SIZE (7*4)
-#define ADDIS_R12_R2 0x3d820000 /* addis %r12,%r2,xxx@ha */
-#define STD_R2_40R1 0xf8410028 /* std %r2,40(%r1) */
-#define LD_R11_0R12 0xe96c0000 /* ld %r11,xxx+0@l(%r12) */
-#define MTCTR_R11 0x7d6903a6 /* mtctr %r11 */
-#define LD_R2_0R12 0xe84c0000 /* ld %r2,xxx+8@l(%r12) */
- /* ld %r11,xxx+16@l(%r12) */
+ insert an addi to adjust r11. */
+#define STD_R2_0R1 0xf8410000 /* std %r2,0+40(%r1) */
+#define ADDIS_R11_R2 0x3d620000 /* addis %r11,%r2,xxx@ha */
+#define LD_R12_0R11 0xe98b0000 /* ld %r12,xxx+0@l(%r11) */
+#define MTCTR_R12 0x7d8903a6 /* mtctr %r12 */
+#define LD_R2_0R11 0xe84b0000 /* ld %r2,xxx+8@l(%r11) */
+#define LD_R11_0R11 0xe96b0000 /* ld %r11,xxx+16@l(%r11) */
#define BCTR 0x4e800420 /* bctr */
-
-#define ADDIS_R12_R12 0x3d8c0000 /* addis %r12,%r12,off@ha */
-#define ADDI_R12_R12 0x398c0000 /* addi %r12,%r12,off@l */
+#define ADDI_R11_R11 0x396b0000 /* addi %r11,%r11,off@l */
#define ADDIS_R2_R2 0x3c420000 /* addis %r2,%r2,off@ha */
#define ADDI_R2_R2 0x38420000 /* addi %r2,%r2,off@l */
-#define XOR_R11_R11_R11 0x7d6b5a78 /* xor %r11,%r11,%r11 */
-#define ADD_R12_R12_R11 0x7d8c5a14 /* add %r12,%r12,%r11 */
+#define XOR_R2_R12_R12 0x7d826278 /* xor %r2,%r12,%r12 */
+#define ADD_R11_R11_R2 0x7d6b1214 /* add %r11,%r11,%r2 */
+#define XOR_R11_R12_R12 0x7d8b6278 /* xor %r11,%r12,%r12 */
#define ADD_R2_R2_R11 0x7c425a14 /* add %r2,%r2,%r11 */
#define CMPLDI_R2_0 0x28220000 /* cmpldi %r2,0 */
#define BNECTR 0x4ca20420 /* bnectr+ */
#define BNECTR_P4 0x4ce20420 /* bnectr+ */
+#define LD_R12_0R2 0xe9820000 /* ld %r12,xxx+0(%r2) */
#define LD_R11_0R2 0xe9620000 /* ld %r11,xxx+0(%r2) */
#define LD_R2_0R2 0xe8420000 /* ld %r2,xxx+0(%r2) */
-#define LD_R2_40R1 0xe8410028 /* ld %r2,40(%r1) */
+#define LD_R2_0R1 0xe8410000 /* ld %r2,0(%r1) */
+
+#define ADDIS_R12_R2 0x3d820000 /* addis %r12,%r2,xxx@ha */
+#define ADDIS_R12_R12 0x3d8c0000 /* addis %r12,%r12,xxx@ha */
+#define LD_R12_0R12 0xe98c0000 /* ld %r12,xxx@l(%r12) */
/* glink call stub instructions. We enter with the index in R0. */
#define GLINK_CALL_STUB_SIZE (16*4)
#define BCL_20_31 0x429f0005 /* bcl 20,31,1f */
/* 1: */
#define MFLR_R11 0x7d6802a6 /* mflr %11 */
-#define LD_R2_M16R11 0xe84bfff0 /* ld %2,(0b-1b)(%11) */
+ /* ld %2,(0b-1b)(%11) */
#define MTLR_R12 0x7d8803a6 /* mtlr %12 */
-#define ADD_R12_R2_R11 0x7d825a14 /* add %12,%2,%11 */
- /* ld %11,0(%12) */
- /* ld %2,8(%12) */
- /* mtctr %11 */
- /* ld %11,16(%12) */
+#define ADD_R11_R2_R11 0x7d625a14 /* add %11,%2,%11 */
+ /* ld %12,0(%11) */
+ /* ld %2,8(%11) */
+ /* mtctr %12 */
+ /* ld %11,16(%11) */
/* bctr */
+#define MFLR_R0 0x7c0802a6 /* mflr %r0 */
+#define MTLR_R0 0x7c0803a6 /* mtlr %r0 */
+#define SUB_R12_R12_R11 0x7d8b6050 /* subf %r12,%r11,%r12 */
+#define ADDI_R0_R12 0x380c0000 /* addi %r0,%r12,0 */
+#define SRDI_R0_R0_2 0x7800f082 /* rldicl %r0,%r0,62,2 */
/* Pad with this. */
#define NOP 0x60000000
#ifndef NO_OPD_RELOCS
#define NO_OPD_RELOCS 0
#endif
+
+static inline int
+abiversion (bfd *abfd)
+{
+ return elf_elfheader (abfd)->e_flags & EF_PPC64_ABI;
+}
+
+static inline void
+set_abiversion (bfd *abfd, int ver)
+{
+ elf_elfheader (abfd)->e_flags &= ~EF_PPC64_ABI;
+ elf_elfheader (abfd)->e_flags |= ver & EF_PPC64_ABI;
+}
\f
#define ONES(n) (((bfd_vma) 1 << ((n) - 1) << 1) - 1)
/* This reloc does nothing. */
HOWTO (R_PPC64_NONE, /* type */
0, /* rightshift */
- 2, /* size (0 = byte, 1 = short, 2 = long) */
- 32, /* bitsize */
+ 3, /* size (0 = byte, 1 = short, 2 = long) */
+ 0, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_dont, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_PPC64_ADDR16_HI", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_dont, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_ha_reloc, /* special_function */
"R_PPC64_ADDR16_HA", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_bitfield, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_branch_reloc, /* special_function */
"R_PPC64_ADDR14", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_bitfield, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_brtaken_reloc, /* special_function */
"R_PPC64_ADDR14_BRTAKEN",/* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_bitfield, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_brtaken_reloc, /* special_function */
"R_PPC64_ADDR14_BRNTAKEN",/* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_dont,/* complain_on_overflow */
+ complain_overflow_signed,/* complain_on_overflow */
ppc64_elf_unhandled_reloc, /* special_function */
"R_PPC64_GOT16_HI", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_dont,/* complain_on_overflow */
+ complain_overflow_signed,/* complain_on_overflow */
ppc64_elf_unhandled_reloc, /* special_function */
"R_PPC64_GOT16_HA", /* name */
FALSE, /* partial_inplace */
32, /* bitsize */
TRUE, /* pc_relative */
0, /* bitpos */
- /* FIXME: Verify. Was complain_overflow_bitfield. */
complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_PPC64_REL32", /* name */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_dont, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_unhandled_reloc, /* special_function */
"R_PPC64_PLT16_HI", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_dont, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_unhandled_reloc, /* special_function */
"R_PPC64_PLT16_HA", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_bitfield, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_sectoff_reloc, /* special_function */
"R_PPC64_SECTOFF", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_dont, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_sectoff_reloc, /* special_function */
"R_PPC64_SECTOFF_HI", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_dont, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_sectoff_ha_reloc, /* special_function */
"R_PPC64_SECTOFF_HA", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_dont, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_toc_reloc, /* special_function */
"R_PPC64_TOC16_HI", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_dont, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_toc_ha_reloc, /* special_function */
"R_PPC64_TOC16_HA", /* name */
FALSE, /* partial_inplace */
64, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_bitfield, /* complain_on_overflow */
+ complain_overflow_dont, /* complain_on_overflow */
ppc64_elf_toc64_reloc, /* special_function */
"R_PPC64_TOC", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_dont, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_unhandled_reloc, /* special_function */
"R_PPC64_PLTGOT16_HI", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_dont,/* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_unhandled_reloc, /* special_function */
"R_PPC64_PLTGOT16_HA", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_bitfield, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_PPC64_ADDR16_DS", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_bitfield, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_sectoff_reloc, /* special_function */
"R_PPC64_SECTOFF_DS", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_dont, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_unhandled_reloc, /* special_function */
"R_PPC64_DTPREL16_HI", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_dont, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_unhandled_reloc, /* special_function */
"R_PPC64_DTPREL16_HA", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_dont, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_unhandled_reloc, /* special_function */
"R_PPC64_TPREL16_HI", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_dont, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_unhandled_reloc, /* special_function */
"R_PPC64_TPREL16_HA", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_dont, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_unhandled_reloc, /* special_function */
"R_PPC64_GOT_TLSGD16_HI", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_dont, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_unhandled_reloc, /* special_function */
"R_PPC64_GOT_TLSGD16_HA", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_dont, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_unhandled_reloc, /* special_function */
"R_PPC64_GOT_TLSLD16_HI", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_dont, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_unhandled_reloc, /* special_function */
"R_PPC64_GOT_TLSLD16_HA", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_dont, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_unhandled_reloc, /* special_function */
"R_PPC64_GOT_DTPREL16_HI", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_dont, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_unhandled_reloc, /* special_function */
"R_PPC64_GOT_DTPREL16_HA", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_dont, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_unhandled_reloc, /* special_function */
"R_PPC64_GOT_TPREL16_HI", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
- complain_overflow_dont, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_unhandled_reloc, /* special_function */
"R_PPC64_GOT_TPREL16_HA", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
TRUE, /* pc_relative */
0, /* bitpos */
- complain_overflow_bitfield, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_PPC64_REL16", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
TRUE, /* pc_relative */
0, /* bitpos */
- complain_overflow_dont, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_PPC64_REL16_HI", /* name */
FALSE, /* partial_inplace */
16, /* bitsize */
TRUE, /* pc_relative */
0, /* bitpos */
- complain_overflow_dont, /* complain_on_overflow */
+ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_ha_reloc, /* special_function */
"R_PPC64_REL16_HA", /* name */
FALSE, /* partial_inplace */
0xffff, /* dst_mask */
TRUE), /* pcrel_offset */
+ /* Like R_PPC64_ADDR16_HI, but no overflow. */
+ HOWTO (R_PPC64_ADDR16_HIGH, /* type */
+ 16, /* rightshift */
+ 1, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_PPC64_ADDR16_HIGH", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Like R_PPC64_ADDR16_HA, but no overflow. */
+ HOWTO (R_PPC64_ADDR16_HIGHA, /* type */
+ 16, /* rightshift */
+ 1, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ ppc64_elf_ha_reloc, /* special_function */
+ "R_PPC64_ADDR16_HIGHA", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Like R_PPC64_DTPREL16_HI, but no overflow. */
+ HOWTO (R_PPC64_DTPREL16_HIGH,
+ 16, /* rightshift */
+ 1, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ ppc64_elf_unhandled_reloc, /* special_function */
+ "R_PPC64_DTPREL16_HIGH", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Like R_PPC64_DTPREL16_HA, but no overflow. */
+ HOWTO (R_PPC64_DTPREL16_HIGHA,
+ 16, /* rightshift */
+ 1, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ ppc64_elf_unhandled_reloc, /* special_function */
+ "R_PPC64_DTPREL16_HIGHA", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Like R_PPC64_TPREL16_HI, but no overflow. */
+ HOWTO (R_PPC64_TPREL16_HIGH,
+ 16, /* rightshift */
+ 1, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ ppc64_elf_unhandled_reloc, /* special_function */
+ "R_PPC64_TPREL16_HIGH", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Like R_PPC64_TPREL16_HA, but no overflow. */
+ HOWTO (R_PPC64_TPREL16_HIGHA,
+ 16, /* rightshift */
+ 1, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ ppc64_elf_unhandled_reloc, /* special_function */
+ "R_PPC64_TPREL16_HIGHA", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ /* Like ADDR64, but use local entry point of function. */
+ HOWTO (R_PPC64_ADDR64_LOCAL, /* type */
+ 0, /* rightshift */
+ 4, /* size (0=byte, 1=short, 2=long, 4=64 bits) */
+ 64, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_PPC64_ADDR64_LOCAL", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ ONES (64), /* dst_mask */
+ FALSE), /* pcrel_offset */
+
/* GNU extension to record C++ vtable hierarchy. */
HOWTO (R_PPC64_GNU_VTINHERIT, /* type */
0, /* rightshift */
break;
case BFD_RELOC_HI16: r = R_PPC64_ADDR16_HI;
break;
+ case BFD_RELOC_PPC64_ADDR16_HIGH: r = R_PPC64_ADDR16_HIGH;
+ break;
case BFD_RELOC_HI16_S: r = R_PPC64_ADDR16_HA;
break;
+ case BFD_RELOC_PPC64_ADDR16_HIGHA: r = R_PPC64_ADDR16_HIGHA;
+ break;
case BFD_RELOC_PPC_BA16: r = R_PPC64_ADDR14;
break;
case BFD_RELOC_PPC_BA16_BRTAKEN: r = R_PPC64_ADDR14_BRTAKEN;
break;
case BFD_RELOC_PPC_TPREL16_HI: r = R_PPC64_TPREL16_HI;
break;
+ case BFD_RELOC_PPC64_TPREL16_HIGH: r = R_PPC64_TPREL16_HIGH;
+ break;
case BFD_RELOC_PPC_TPREL16_HA: r = R_PPC64_TPREL16_HA;
break;
+ case BFD_RELOC_PPC64_TPREL16_HIGHA: r = R_PPC64_TPREL16_HIGHA;
+ break;
case BFD_RELOC_PPC_TPREL: r = R_PPC64_TPREL64;
break;
case BFD_RELOC_PPC_DTPREL16: r = R_PPC64_DTPREL16;
break;
case BFD_RELOC_PPC_DTPREL16_HI: r = R_PPC64_DTPREL16_HI;
break;
+ case BFD_RELOC_PPC64_DTPREL16_HIGH: r = R_PPC64_DTPREL16_HIGH;
+ break;
case BFD_RELOC_PPC_DTPREL16_HA: r = R_PPC64_DTPREL16_HA;
break;
+ case BFD_RELOC_PPC64_DTPREL16_HIGHA: r = R_PPC64_DTPREL16_HIGHA;
+ break;
case BFD_RELOC_PPC_DTPREL: r = R_PPC64_DTPREL64;
break;
case BFD_RELOC_PPC_GOT_TLSGD16: r = R_PPC64_GOT_TLSGD16;
break;
case BFD_RELOC_HI16_S_PCREL: r = R_PPC64_REL16_HA;
break;
+ case BFD_RELOC_PPC64_ADDR64_LOCAL: r = R_PPC64_ADDR64_LOCAL;
+ break;
case BFD_RELOC_VTABLE_INHERIT: r = R_PPC64_GNU_VTINHERIT;
break;
case BFD_RELOC_VTABLE_ENTRY: r = R_PPC64_GNU_VTENTRY;
+ symbol->section->output_section->vma
+ symbol->section->output_offset);
}
+ else
+ {
+ elf_symbol_type *elfsym = (elf_symbol_type *) symbol;
+
+ if (symbol->section->owner != abfd
+ && abiversion (symbol->section->owner) >= 2)
+ {
+ unsigned int i;
+
+ for (i = 0; i < symbol->section->owner->symcount; ++i)
+ {
+ asymbol *symdef = symbol->section->owner->outsymbols[i];
+
+ if (strcmp (symdef->name, symbol->name) == 0)
+ {
+ elfsym = (elf_symbol_type *) symdef;
+ break;
+ }
+ }
+ }
+ reloc_entry->addend
+ += PPC64_LOCAL_ENTRY_OFFSET (elfsym->internal_elf_sym.st_other);
+ }
return bfd_reloc_continue;
}
TOCstart = _bfd_get_gp_value (input_section->output_section->owner);
if (TOCstart == 0)
- TOCstart = ppc64_elf_toc (input_section->output_section->owner);
+ TOCstart = ppc64_elf_set_toc (NULL, input_section->output_section->owner);
/* Subtract the TOC base address. */
reloc_entry->addend -= TOCstart + TOC_BASE_OFF;
TOCstart = _bfd_get_gp_value (input_section->output_section->owner);
if (TOCstart == 0)
- TOCstart = ppc64_elf_toc (input_section->output_section->owner);
+ TOCstart = ppc64_elf_set_toc (NULL, input_section->output_section->owner);
/* Subtract the TOC base address. */
reloc_entry->addend -= TOCstart + TOC_BASE_OFF;
TOCstart = _bfd_get_gp_value (input_section->output_section->owner);
if (TOCstart == 0)
- TOCstart = ppc64_elf_toc (input_section->output_section->owner);
+ TOCstart = ppc64_elf_set_toc (NULL, 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);
sections means we potentially need one of these for each input bfd. */
struct got_entry tlsld_got;
- /* A copy of relocs before they are modified for --emit-relocs. */
- Elf_Internal_Rela *opd_relocs;
+ union {
+ /* A copy of relocs before they are modified for --emit-relocs. */
+ Elf_Internal_Rela *relocs;
+
+ /* Section contents. */
+ bfd_byte *contents;
+ } opd;
/* Nonzero if this bfd has small toc/got relocs, ie. that expect
the reloc to be in the range -32768 to 32767. */
union
{
- /* An array with one entry for each opd function descriptor. */
+ /* An array with one entry for each opd function descriptor,
+ and some spares since opd entries may be either 16 or 24 bytes. */
+#define OPD_NDX(OFF) ((OFF) >> 4)
struct _opd_sec_data
{
/* Points to the function code section for local opd entries. */
long count;
char *names;
long symcount, codesecsym, codesecsymend, secsymend, opdsymend;
- asection *opd;
+ asection *opd = NULL;
bfd_boolean relocatable = (abfd->flags & (EXEC_P | DYNAMIC)) == 0;
asymbol **syms;
+ int abi = abiversion (abfd);
*ret = NULL;
- opd = bfd_get_section_by_name (abfd, ".opd");
- if (opd == NULL)
- return 0;
+ if (abi < 2)
+ {
+ opd = bfd_get_section_by_name (abfd, ".opd");
+ if (opd == NULL && abi == 1)
+ return 0;
+ }
symcount = static_count;
if (!relocatable)
else
{
bfd_boolean (*slurp_relocs) (bfd *, asection *, asymbol **, bfd_boolean);
- bfd_byte *contents;
+ bfd_byte *contents = NULL;
size_t size;
long plt_count = 0;
bfd_vma glink_vma = 0, resolv_vma = 0;
asection *dynamic, *glink = NULL, *relplt = NULL;
arelent *p;
- if (!bfd_malloc_and_get_section (abfd, opd, &contents))
+ if (opd != NULL && !bfd_malloc_and_get_section (abfd, opd, &contents))
{
+ free_contents_and_exit:
if (contents)
- {
- free_contents_and_exit:
- free (contents);
- }
+ free (contents);
count = -1;
goto done;
}
if (dyn.d_tag == DT_PPC64_GLINK)
{
- /* The first glink stub starts at offset 32; see comment in
- ppc64_elf_finish_dynamic_sections. */
- glink_vma = dyn.d_un.d_val + 32;
+ /* The first glink stub starts at offset 32; see
+ comment in ppc64_elf_finish_dynamic_sections. */
+ glink_vma = dyn.d_un.d_val + GLINK_CALL_STUB_SIZE - 8 * 4;
/* The .glink section usually does not survive the final
link; search for the section (usually .text) where the
glink stubs now reside. */
/* Determine __glink trampoline by reading the relative branch
from the first glink stub. */
bfd_byte buf[4];
- if (bfd_get_section_contents (abfd, glink, buf,
- glink_vma + 4 - glink->vma, 4))
+ unsigned int off = 0;
+
+ while (bfd_get_section_contents (abfd, glink, buf,
+ glink_vma + off - glink->vma, 4))
{
unsigned int insn = bfd_get_32 (abfd, buf);
insn ^= B_DOT;
if ((insn & ~0x3fffffc) == 0)
- resolv_vma = glink_vma + 4 + (insn ^ 0x2000000) - 0x2000000;
+ {
+ resolv_vma = glink_vma + off + (insn ^ 0x2000000) - 0x2000000;
+ break;
+ }
+ off += 4;
+ if (off > 4)
+ break;
}
if (resolv_vma)
memcpy (names, "@plt", sizeof ("@plt"));
names += sizeof ("@plt");
s++;
- glink_vma += 8;
- if (i >= 0x8000)
+ if (abi < 2)
+ {
+ glink_vma += 8;
+ if (i >= 0x8000)
+ glink_vma += 4;
+ }
+ else
glink_vma += 4;
}
count += plt_count;
.
.
. .foo_stub:
- . addis 12,2,Lfoo@toc@ha # in practice, the call stub
- . addi 12,12,Lfoo@toc@l # is slightly optimized, but
- . std 2,40(1) # this is the general idea
- . ld 11,0(12)
- . ld 2,8(12)
- . mtctr 11
- . ld 11,16(12)
+ . std 2,40(1) # in practice, the call stub
+ . addis 11,2,Lfoo@toc@ha # is slightly optimized, but
+ . addi 11,11,Lfoo@toc@l # this is the general idea
+ . ld 12,0(11)
+ . ld 2,8(11)
+ . mtctr 12
+ . ld 11,16(11)
. bctr
.
. .section .plt
case R_PPC64_TPREL16_HA:
case R_PPC64_TPREL16_DS:
case R_PPC64_TPREL16_LO_DS:
+ case R_PPC64_TPREL16_HIGH:
+ case R_PPC64_TPREL16_HIGHA:
case R_PPC64_TPREL16_HIGHER:
case R_PPC64_TPREL16_HIGHERA:
case R_PPC64_TPREL16_HIGHEST:
ppc_stub_plt_branch:
Similar to the above, but a 24 bit branch in the stub section won't
reach its destination.
- . addis %r12,%r2,xxx@toc@ha
- . ld %r11,xxx@toc@l(%r12)
- . mtctr %r11
+ . addis %r11,%r2,xxx@toc@ha
+ . ld %r12,xxx@toc@l(%r11)
+ . mtctr %r12
. bctr
ppc_stub_plt_call:
Used to call a function in a shared library. If it so happens that
the plt entry referenced crosses a 64k boundary, then an extra
- "addi %r12,%r12,xxx@toc@l" will be inserted before the "mtctr".
- . addis %r12,%r2,xxx@toc@ha
+ "addi %r11,%r11,xxx@toc@l" will be inserted before the "mtctr".
. std %r2,40(%r1)
- . ld %r11,xxx+0@toc@l(%r12)
- . mtctr %r11
- . ld %r2,xxx+8@toc@l(%r12)
- . ld %r11,xxx+16@toc@l(%r12)
+ . addis %r11,%r2,xxx@toc@ha
+ . ld %r12,xxx+0@toc@l(%r11)
+ . mtctr %r12
+ . ld %r2,xxx+8@toc@l(%r11)
+ . ld %r11,xxx+16@toc@l(%r11)
. bctr
ppc_stub_long_branch and ppc_stub_plt_branch may also have additional
A ppc_stub_plt_branch with an r2 offset looks like:
. std %r2,40(%r1)
- . addis %r12,%r2,xxx@toc@ha
- . ld %r11,xxx@toc@l(%r12)
+ . addis %r11,%r2,xxx@toc@ha
+ . ld %r12,xxx@toc@l(%r11)
. addis %r2,%r2,off@ha
. addi %r2,%r2,off@l
- . mtctr %r11
+ . mtctr %r12
. bctr
In cases where the "addis" instruction would add zero, the "addis" is
ppc_stub_plt_branch,
ppc_stub_plt_branch_r2off,
ppc_stub_plt_call,
- ppc_stub_plt_call_r2save
+ ppc_stub_plt_call_r2save,
+ ppc_stub_global_entry
};
struct ppc_stub_hash_entry {
struct ppc_link_hash_entry *h;
struct plt_entry *plt_ent;
- /* And the reloc addend that this was derived from. */
- bfd_vma addend;
-
/* 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;
+
+ /* Symbol st_other. */
+ unsigned char other;
};
struct ppc_branch_hash_entry {
/* Hash table for function prologue tocsave. */
htab_t tocsave_htab;
- /* Linker stub bfd. */
- bfd *stub_bfd;
-
- /* Linker call-backs. */
- asection * (*add_stub_section) (const char *, asection *);
- void (*layout_sections_again) (void);
+ /* Various options and other info passed from the linker. */
+ struct ppc64_elf_params *params;
/* Array to keep track of which stub sections have been created, and
information on stub grouping. */
/* List of input sections for each output section. */
asection **input_list;
- /* Short-cuts to get to dynamic linker sections. */
- asection *got;
- asection *plt;
- asection *relplt;
- asection *iplt;
- asection *reliplt;
+ /* Shortcuts to get to dynamic linker sections. */
asection *dynbss;
asection *relbss;
asection *glink;
struct ppc_link_hash_entry *tls_get_addr;
struct ppc_link_hash_entry *tls_get_addr_fd;
- /* The special .TOC. symbol. */
- struct ppc_link_hash_entry *dot_toc_dot;
-
/* The size of reliplt used by got entry relocs. */
bfd_size_type got_reli_size;
/* Statistics. */
- unsigned long stub_count[ppc_stub_plt_call_r2save];
+ unsigned long stub_count[ppc_stub_global_entry];
/* Number of stubs against global syms. */
unsigned long stub_globals;
- /* Alignment of PLT call stubs. */
- unsigned int plt_stub_align:4;
-
- /* Set if PLT call stubs should load r11. */
- unsigned int plt_static_chain:1;
-
- /* Set if PLT call stubs need a read-read barrier. */
- unsigned int plt_thread_safe:1;
-
- /* Set if we should emit symbols for stubs. */
- unsigned int emit_stub_syms:1;
-
- /* Set if __tls_get_addr optimization should not be done. */
- unsigned int no_tls_get_addr_opt:1;
+ /* Set if we're linking code with function descriptors. */
+ unsigned int opd_abi:1;
/* Support for multiple toc sections. */
unsigned int do_multi_toc:1;
/* Set on error. */
unsigned int stub_error:1;
- /* Temp used by ppc64_elf_process_dot_syms. */
+ /* Temp used by ppc64_elf_before_check_relocs. */
unsigned int twiddled_syms:1;
/* Incremented every time we size stubs. */
eh->target_value = 0;
eh->target_section = NULL;
eh->h = NULL;
+ eh->plt_ent = NULL;
eh->id_sec = NULL;
+ eh->other = 0;
}
return entry;
return e1->sec == e2->sec && e1->offset == e2->offset;
}
+/* Destroy a ppc64 ELF linker hash table. */
+
+static void
+ppc64_elf_link_hash_table_free (bfd *obfd)
+{
+ struct ppc_link_hash_table *htab;
+
+ htab = (struct ppc_link_hash_table *) obfd->link.hash;
+ if (htab->tocsave_htab)
+ htab_delete (htab->tocsave_htab);
+ bfd_hash_table_free (&htab->branch_hash_table);
+ bfd_hash_table_free (&htab->stub_hash_table);
+ _bfd_elf_link_hash_table_free (obfd);
+}
+
/* Create a ppc64 ELF linker hash table. */
static struct bfd_link_hash_table *
/* Init the stub hash table too. */
if (!bfd_hash_table_init (&htab->stub_hash_table, stub_hash_newfunc,
sizeof (struct ppc_stub_hash_entry)))
- return NULL;
+ {
+ _bfd_elf_link_hash_table_free (abfd);
+ return NULL;
+ }
/* And the branch hash table. */
if (!bfd_hash_table_init (&htab->branch_hash_table, branch_hash_newfunc,
sizeof (struct ppc_branch_hash_entry)))
- return NULL;
+ {
+ bfd_hash_table_free (&htab->stub_hash_table);
+ _bfd_elf_link_hash_table_free (abfd);
+ return NULL;
+ }
htab->tocsave_htab = htab_try_create (1024,
tocsave_htab_hash,
tocsave_htab_eq,
NULL);
if (htab->tocsave_htab == NULL)
- return NULL;
+ {
+ ppc64_elf_link_hash_table_free (abfd);
+ return NULL;
+ }
+ htab->elf.root.hash_table_free = ppc64_elf_link_hash_table_free;
/* Initializing two fields of the union is just cosmetic. We really
only care about glist, but when compiled on a 32-bit host the
return &htab->elf.root;
}
-/* Free the derived linker hash table. */
-
-static void
-ppc64_elf_link_hash_table_free (struct bfd_link_hash_table *hash)
-{
- struct ppc_link_hash_table *htab = (struct ppc_link_hash_table *) hash;
-
- bfd_hash_table_free (&htab->stub_hash_table);
- bfd_hash_table_free (&htab->branch_hash_table);
- if (htab->tocsave_htab)
- htab_delete (htab->tocsave_htab);
- _bfd_elf_link_hash_table_free (hash);
-}
-
-/* Satisfy the ELF linker by filling in some fields in our fake bfd. */
+/* Create sections for linker generated code. */
-void
-ppc64_elf_init_stub_bfd (bfd *abfd, struct bfd_link_info *info)
+static bfd_boolean
+create_linkage_sections (bfd *dynobj, struct bfd_link_info *info)
{
struct ppc_link_hash_table *htab;
+ flagword flags;
- elf_elfheader (abfd)->e_ident[EI_CLASS] = ELFCLASS64;
-
-/* Always hook our dynamic sections into the first bfd, which is the
- linker created stub bfd. This ensures that the GOT header is at
- the start of the output TOC section. */
htab = ppc_hash_table (info);
- if (htab == NULL)
- return;
- htab->stub_bfd = abfd;
- htab->elf.dynobj = abfd;
+
+ /* 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_with_flags (dynobj, ".sfpr",
+ flags);
+ if (htab->sfpr == NULL
+ || ! bfd_set_section_alignment (dynobj, htab->sfpr, 2))
+ return FALSE;
+
+ /* Create .glink for lazy dynamic linking support. */
+ htab->glink = bfd_make_section_anyway_with_flags (dynobj, ".glink",
+ flags);
+ if (htab->glink == NULL
+ || ! bfd_set_section_alignment (dynobj, htab->glink, 3))
+ return FALSE;
+
+ if (!info->no_ld_generated_unwind_info)
+ {
+ flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS
+ | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+ htab->glink_eh_frame = bfd_make_section_anyway_with_flags (dynobj,
+ ".eh_frame",
+ flags);
+ if (htab->glink_eh_frame == NULL
+ || !bfd_set_section_alignment (dynobj, htab->glink_eh_frame, 2))
+ return FALSE;
+ }
+
+ flags = SEC_ALLOC | SEC_LINKER_CREATED;
+ htab->elf.iplt = bfd_make_section_anyway_with_flags (dynobj, ".iplt", flags);
+ if (htab->elf.iplt == NULL
+ || ! bfd_set_section_alignment (dynobj, htab->elf.iplt, 3))
+ return FALSE;
+
+ flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
+ | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+ htab->elf.irelplt
+ = bfd_make_section_anyway_with_flags (dynobj, ".rela.iplt", flags);
+ if (htab->elf.irelplt == NULL
+ || ! bfd_set_section_alignment (dynobj, htab->elf.irelplt, 3))
+ return FALSE;
+
+ /* Create branch lookup table for plt_branch stubs. */
+ flags = (SEC_ALLOC | SEC_LOAD
+ | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+ htab->brlt = bfd_make_section_anyway_with_flags (dynobj, ".branch_lt",
+ flags);
+ if (htab->brlt == NULL
+ || ! bfd_set_section_alignment (dynobj, htab->brlt, 3))
+ return FALSE;
+
+ if (!info->shared)
+ return TRUE;
+
+ flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
+ | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+ htab->relbrlt = bfd_make_section_anyway_with_flags (dynobj,
+ ".rela.branch_lt",
+ flags);
+ if (htab->relbrlt == NULL
+ || ! bfd_set_section_alignment (dynobj, htab->relbrlt, 3))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Satisfy the ELF linker by filling in some fields in our fake bfd. */
+
+bfd_boolean
+ppc64_elf_init_stub_bfd (struct bfd_link_info *info,
+ struct ppc64_elf_params *params)
+{
+ struct ppc_link_hash_table *htab;
+
+ elf_elfheader (params->stub_bfd)->e_ident[EI_CLASS] = ELFCLASS64;
+
+/* Always hook our dynamic sections into the first bfd, which is the
+ linker created stub bfd. This ensures that the GOT header is at
+ the start of the output TOC section. */
+ htab = ppc_hash_table (info);
+ if (htab == NULL)
+ return FALSE;
+ htab->elf.dynobj = params->stub_bfd;
+ htab->params = params;
+
+ if (info->relocatable)
+ return TRUE;
+
+ return create_linkage_sections (htab->elf.dynobj, info);
}
/* Build a name for an entry in the stub hash table. */
namelen = strlen (link_sec->name);
len = namelen + sizeof (STUB_SUFFIX);
- s_name = bfd_alloc (htab->stub_bfd, len);
+ s_name = bfd_alloc (htab->params->stub_bfd, len);
if (s_name == NULL)
return NULL;
memcpy (s_name, link_sec->name, namelen);
memcpy (s_name + namelen, STUB_SUFFIX, sizeof (STUB_SUFFIX));
- stub_sec = (*htab->add_stub_section) (s_name, link_sec);
+ stub_sec = (*htab->params->add_stub_section) (s_name, link_sec);
if (stub_sec == NULL)
return NULL;
htab->stub_group[link_sec->id].stub_sec = stub_sec;
return stub_entry;
}
-/* Create sections for linker generated code. */
-
-static bfd_boolean
-create_linkage_sections (bfd *dynobj, struct bfd_link_info *info)
-{
- struct ppc_link_hash_table *htab;
- flagword flags;
-
- htab = ppc_hash_table (info);
- if (htab == NULL)
- return FALSE;
-
- /* 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_with_flags (dynobj, ".sfpr",
- flags);
- if (htab->sfpr == NULL
- || ! bfd_set_section_alignment (dynobj, htab->sfpr, 2))
- return FALSE;
-
- /* Create .glink for lazy dynamic linking support. */
- htab->glink = bfd_make_section_anyway_with_flags (dynobj, ".glink",
- flags);
- if (htab->glink == NULL
- || ! bfd_set_section_alignment (dynobj, htab->glink, 3))
- return FALSE;
-
- if (!info->no_ld_generated_unwind_info)
- {
- flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS
- | SEC_IN_MEMORY | SEC_LINKER_CREATED);
- htab->glink_eh_frame = bfd_make_section_anyway_with_flags (dynobj,
- ".eh_frame",
- flags);
- if (htab->glink_eh_frame == NULL
- || !bfd_set_section_alignment (dynobj, htab->glink_eh_frame, 2))
- return FALSE;
- }
-
- flags = SEC_ALLOC | SEC_LINKER_CREATED;
- htab->iplt = bfd_make_section_anyway_with_flags (dynobj, ".iplt", flags);
- if (htab->iplt == NULL
- || ! bfd_set_section_alignment (dynobj, htab->iplt, 3))
- return FALSE;
-
- flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
- | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
- htab->reliplt = bfd_make_section_anyway_with_flags (dynobj,
- ".rela.iplt",
- flags);
- if (htab->reliplt == NULL
- || ! bfd_set_section_alignment (dynobj, htab->reliplt, 3))
- return FALSE;
-
- /* Create branch lookup table for plt_branch stubs. */
- flags = (SEC_ALLOC | SEC_LOAD
- | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
- htab->brlt = bfd_make_section_anyway_with_flags (dynobj, ".branch_lt",
- flags);
- if (htab->brlt == NULL
- || ! bfd_set_section_alignment (dynobj, htab->brlt, 3))
- return FALSE;
-
- if (!info->shared)
- return TRUE;
-
- flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
- | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
- htab->relbrlt = bfd_make_section_anyway_with_flags (dynobj,
- ".rela.branch_lt",
- flags);
- if (htab->relbrlt == NULL
- || ! bfd_set_section_alignment (dynobj, htab->relbrlt, 3))
- return FALSE;
-
- return TRUE;
-}
-
/* Create .got and .rela.got sections in ABFD, and .got in dynobj if
not already done. */
if (htab == NULL)
return FALSE;
- if (!htab->got)
- {
- if (! _bfd_elf_create_got_section (htab->elf.dynobj, info))
- return FALSE;
-
- htab->got = bfd_get_linker_section (htab->elf.dynobj, ".got");
- if (!htab->got)
- abort ();
- }
+ if (!htab->elf.sgot
+ && !_bfd_elf_create_got_section (htab->elf.dynobj, info))
+ return FALSE;
flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
| SEC_LINKER_CREATED);
if (htab == NULL)
return FALSE;
- if (!htab->got)
- htab->got = bfd_get_linker_section (dynobj, ".got");
- htab->plt = bfd_get_linker_section (dynobj, ".plt");
- htab->relplt = bfd_get_linker_section (dynobj, ".rela.plt");
htab->dynbss = bfd_get_linker_section (dynobj, ".dynbss");
if (!info->shared)
htab->relbss = bfd_get_linker_section (dynobj, ".rela.bss");
- if (!htab->got || !htab->plt || !htab->relplt || !htab->dynbss
+ if (!htab->elf.sgot || !htab->elf.splt || !htab->elf.srelplt || !htab->dynbss
|| (!info->shared && !htab->relbss))
abort ();
edir->elf.ref_regular |= eind->elf.ref_regular;
edir->elf.ref_regular_nonweak |= eind->elf.ref_regular_nonweak;
edir->elf.needs_plt |= eind->elf.needs_plt;
+ edir->elf.pointer_equality_needed |= eind->elf.pointer_equality_needed;
/* Copy over any dynamic relocs we may have on the indirect sym. */
if (eind->dyn_relocs != NULL)
ppc64_elf_add_symbol_hook (bfd *ibfd,
struct bfd_link_info *info,
Elf_Internal_Sym *isym,
- const char **name ATTRIBUTE_UNUSED,
+ const char **name,
flagword *flags ATTRIBUTE_UNUSED,
asection **sec,
- bfd_vma *value ATTRIBUTE_UNUSED)
+ bfd_vma *value)
{
- if ((ibfd->flags & DYNAMIC) == 0
- && ELF_ST_BIND (isym->st_info) == STB_GNU_UNIQUE)
+ if ((ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC
+ || ELF_ST_BIND (isym->st_info) == STB_GNU_UNIQUE)
+ && (ibfd->flags & DYNAMIC) == 0
+ && bfd_get_flavour (info->output_bfd) == bfd_target_elf_flavour)
elf_tdata (info->output_bfd)->has_gnu_symbols = TRUE;
- if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
+ if (*sec != NULL
+ && strcmp ((*sec)->name, ".opd") == 0)
{
- if ((ibfd->flags & DYNAMIC) == 0)
- elf_tdata (info->output_bfd)->has_gnu_symbols = TRUE;
+ asection *code_sec;
+
+ if (!(ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC
+ || ELF_ST_TYPE (isym->st_info) == STT_FUNC))
+ isym->st_info = ELF_ST_INFO (ELF_ST_BIND (isym->st_info), STT_FUNC);
+
+ /* If the symbol is a function defined in .opd, and the function
+ code is in a discarded group, let it appear to be undefined. */
+ if (!info->relocatable
+ && (*sec)->reloc_count != 0
+ && opd_entry_value (*sec, *value, &code_sec, NULL,
+ FALSE) != (bfd_vma) -1
+ && discarded_section (code_sec))
+ {
+ *sec = bfd_und_section_ptr;
+ isym->st_shndx = SHN_UNDEF;
+ }
}
- else if (ELF_ST_TYPE (isym->st_info) == STT_FUNC)
- ;
else if (*sec != NULL
- && strcmp ((*sec)->name, ".opd") == 0)
- isym->st_info = ELF_ST_INFO (ELF_ST_BIND (isym->st_info), STT_FUNC);
+ && strcmp ((*sec)->name, ".toc") == 0
+ && ELF_ST_TYPE (isym->st_info) == STT_OBJECT)
+ {
+ struct ppc_link_hash_table *htab = ppc_hash_table (info);
+ if (htab != NULL)
+ htab->params->object_in_toc = 1;
+ }
+
+ if ((STO_PPC64_LOCAL_MASK & isym->st_other) != 0)
+ {
+ if (abiversion (ibfd) == 0)
+ set_abiversion (ibfd, 2);
+ else if (abiversion (ibfd) == 1)
+ {
+ info->callbacks->einfo (_("%P: symbol '%s' has invalid st_other"
+ " for ABI version 1\n"), name);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+ }
return TRUE;
}
+/* Merge non-visibility st_other attributes: local entry point. */
+
+static void
+ppc64_elf_merge_symbol_attribute (struct elf_link_hash_entry *h,
+ const Elf_Internal_Sym *isym,
+ bfd_boolean definition,
+ bfd_boolean dynamic)
+{
+ if (definition && !dynamic)
+ h->other = ((isym->st_other & ~ELF_ST_VISIBILITY (-1))
+ | ELF_ST_VISIBILITY (h->other));
+}
+
/* This function makes an old ABI object reference to ".bar" cause the
inclusion of a new ABI object archive that defines "bar".
NAME is a symbol defined in an archive. Return a symbol in the hash
return TRUE;
}
-/* Process list of dot-symbols we made in link_hash_newfunc. */
+/* Set up opd section info and abiversion for IBFD, and process list
+ of dot-symbols we made in link_hash_newfunc. */
static bfd_boolean
-ppc64_elf_process_dot_syms (bfd *ibfd, struct bfd_link_info *info)
+ppc64_elf_before_check_relocs (bfd *ibfd, struct bfd_link_info *info)
{
struct ppc_link_hash_table *htab;
struct ppc_link_hash_entry **p, *eh;
+ asection *opd = bfd_get_section_by_name (ibfd, ".opd");
+
+ if (opd != NULL && opd->size != 0)
+ {
+ if (abiversion (ibfd) == 0)
+ set_abiversion (ibfd, 1);
+ else if (abiversion (ibfd) == 2)
+ {
+ info->callbacks->einfo (_("%P: %B .opd not allowed in ABI"
+ " version %d\n"),
+ ibfd, abiversion (ibfd));
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+
+ if ((ibfd->flags & DYNAMIC) == 0
+ && (opd->flags & SEC_RELOC) != 0
+ && opd->reloc_count != 0
+ && !bfd_is_abs_section (opd->output_section))
+ {
+ /* Garbage collection needs some extra help with .opd sections.
+ We don't want to necessarily keep everything referenced by
+ relocs in .opd, as that would keep all functions. Instead,
+ if we reference an .opd symbol (a function descriptor), we
+ want to keep the function code symbol's section. This is
+ easy for global symbols, but for local syms we need to keep
+ information about the associated function section. */
+ bfd_size_type amt;
+ asection **opd_sym_map;
+
+ amt = OPD_NDX (opd->size) * sizeof (*opd_sym_map);
+ opd_sym_map = bfd_zalloc (ibfd, amt);
+ if (opd_sym_map == NULL)
+ return FALSE;
+ ppc64_elf_section_data (opd)->u.opd.func_sec = opd_sym_map;
+ BFD_ASSERT (ppc64_elf_section_data (opd)->sec_type == sec_normal);
+ ppc64_elf_section_data (opd)->sec_type = sec_opd;
+ }
+ }
if (!is_ppc64_elf (info->output_bfd))
return TRUE;
if (htab == NULL)
return FALSE;
- if (is_ppc64_elf (ibfd))
+ /* For input files without an explicit abiversion in e_flags
+ we should have flagged any with symbol st_other bits set
+ as ELFv1 and above flagged those with .opd as ELFv2.
+ Set the output abiversion if not yet set, and for any input
+ still ambiguous, take its abiversion from the output.
+ Differences in ABI are reported later. */
+ if (abiversion (info->output_bfd) == 0)
+ set_abiversion (info->output_bfd, abiversion (ibfd));
+ else if (abiversion (ibfd) == 0)
+ set_abiversion (ibfd, abiversion (info->output_bfd));
+
+ p = &htab->dot_syms;
+ while ((eh = *p) != NULL)
{
- p = &htab->dot_syms;
- while ((eh = *p) != NULL)
- {
- *p = NULL;
- if (!add_symbol_adjust (eh, info))
- return FALSE;
- p = &eh->u.next_dot_sym;
- }
+ *p = NULL;
+ if (&eh->elf == htab->elf.hgot)
+ ;
+ else if (htab->elf.hgot == NULL
+ && strcmp (eh->elf.root.root.string, ".TOC.") == 0)
+ htab->elf.hgot = &eh->elf;
+ else if (!add_symbol_adjust (eh, info))
+ return FALSE;
+ p = &eh->u.next_dot_sym;
}
/* Clear the list for non-ppc64 input files. */
not to be needed. */
static bfd_boolean
-ppc64_elf_as_needed_cleanup (bfd *ibfd ATTRIBUTE_UNUSED,
- struct bfd_link_info *info)
+ppc64_elf_notice_as_needed (bfd *ibfd,
+ struct bfd_link_info *info,
+ enum notice_asneeded_action act)
{
- struct ppc_link_hash_table *htab = ppc_hash_table (info);
+ if (act == notice_not_needed)
+ {
+ struct ppc_link_hash_table *htab = ppc_hash_table (info);
- if (htab == NULL)
- return FALSE;
+ if (htab == NULL)
+ return FALSE;
- htab->dot_syms = NULL;
- return TRUE;
+ htab->dot_syms = NULL;
+ }
+ return _bfd_elf_notice_as_needed (ibfd, info, act);
}
/* If --just-symbols against a final linked binary, then assume we need
&& (sec->owner->flags & (EXEC_P | DYNAMIC)) != 0
&& is_ppc64_elf (sec->owner))
{
- asection *got = bfd_get_section_by_name (sec->owner, ".got");
- if (got != NULL
- && got->size >= elf_backend_got_header_size
- && bfd_get_section_by_name (sec->owner, ".opd") != NULL)
+ if (abiversion (sec->owner) >= 2
+ || bfd_get_section_by_name (sec->owner, ".opd") != NULL)
sec->has_toc_reloc = 1;
}
_bfd_elf_link_just_syms (sec, info);
sym_hashes = elf_sym_hashes (abfd);
sreloc = NULL;
opd_sym_map = NULL;
- if (strcmp (sec->name, ".opd") == 0)
- {
- /* Garbage collection needs some extra help with .opd sections.
- We don't want to necessarily keep everything referenced by
- relocs in .opd, as that would keep all functions. Instead,
- if we reference an .opd symbol (a function descriptor), we
- want to keep the function code symbol's section. This is
- easy for global symbols, but for local syms we need to keep
- information about the associated function section. */
- bfd_size_type amt;
-
- amt = sec->size * sizeof (*opd_sym_map) / 8;
- opd_sym_map = bfd_zalloc (abfd, amt);
- if (opd_sym_map == NULL)
- return FALSE;
- ppc64_elf_section_data (sec)->u.opd.func_sec = opd_sym_map;
- BFD_ASSERT (ppc64_elf_section_data (sec)->sec_type == sec_normal);
- ppc64_elf_section_data (sec)->sec_type = sec_opd;
- }
-
- if (htab->sfpr == NULL
- && !create_linkage_sections (htab->elf.dynobj, info))
- return FALSE;
+ if (ppc64_elf_section_data (sec) != NULL
+ && ppc64_elf_section_data (sec)->sec_type == sec_opd)
+ opd_sym_map = ppc64_elf_section_data (sec)->u.opd.func_sec;
rel_end = relocs + sec->reloc_count;
for (rel = relocs; rel < rel_end; rel++)
/* PR15323, ref flags aren't set for references in the same
object. */
h->root.non_ir_ref = 1;
+
+ if (h == htab->elf.hgot)
+ sec->has_toc_reloc = 1;
}
tls_type = 0;
case R_PPC64_GOT_TPREL16_LO_DS:
case R_PPC64_GOT_TPREL16_HI:
case R_PPC64_GOT_TPREL16_HA:
- if (!info->executable)
+ if (info->shared)
info->flags |= DF_STATIC_TLS;
tls_type = TLS_TLS | TLS_TPREL;
goto dogottls;
if (!update_local_sym_info (abfd, symtab_hdr, r_symndx,
rel->r_addend, tls_type))
return FALSE;
+
+ /* We may also need a plt entry if the symbol turns out to be
+ an ifunc. */
+ if (h != NULL && !info->shared && abiversion (abfd) != 1)
+ {
+ if (!update_plt_info (abfd, &h->plt.plist, rel->r_addend))
+ return FALSE;
+ }
break;
case R_PPC64_PLT16_HA:
case R_PPC64_DTPREL16_HA:
case R_PPC64_DTPREL16_DS:
case R_PPC64_DTPREL16_LO_DS:
+ case R_PPC64_DTPREL16_HIGH:
+ case R_PPC64_DTPREL16_HIGHA:
case R_PPC64_DTPREL16_HIGHER:
case R_PPC64_DTPREL16_HIGHERA:
case R_PPC64_DTPREL16_HIGHEST:
case R_PPC64_REL16_HA:
break;
+ /* Not supported as a dynamic relocation. */
+ case R_PPC64_ADDR64_LOCAL:
+ if (info->shared)
+ {
+ if (!ppc64_elf_howto_table[R_PPC64_ADDR32])
+ ppc_howto_init ();
+ info->callbacks->einfo (_("%P: %H: %s reloc unsupported "
+ "in shared libraries and PIEs.\n"),
+ abfd, sec, rel->r_offset,
+ ppc64_elf_howto_table[r_type]->name);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+ break;
+
case R_PPC64_TOC16:
case R_PPC64_TOC16_DS:
htab->do_multi_toc = 1;
case R_PPC64_TPREL64:
tls_type = TLS_EXPLICIT | TLS_TLS | TLS_TPREL;
- if (!info->executable)
+ if (info->shared)
info->flags |= DF_STATIC_TLS;
goto dotlstoc;
case R_PPC64_TPREL16_HA:
case R_PPC64_TPREL16_DS:
case R_PPC64_TPREL16_LO_DS:
+ case R_PPC64_TPREL16_HIGH:
+ case R_PPC64_TPREL16_HIGHA:
case R_PPC64_TPREL16_HIGHER:
case R_PPC64_TPREL16_HIGHERA:
case R_PPC64_TPREL16_HIGHEST:
case R_PPC64_TPREL16_HIGHESTA:
if (info->shared)
{
- if (!info->executable)
- info->flags |= DF_STATIC_TLS;
+ info->flags |= DF_STATIC_TLS;
goto dodyn;
}
break;
s = bfd_section_from_elf_index (abfd, isym->st_shndx);
if (s != NULL && s != sec)
- opd_sym_map[rel->r_offset / 8] = s;
+ opd_sym_map[OPD_NDX (rel->r_offset)] = s;
}
}
/* Fall through. */
- case R_PPC64_REL30:
- case R_PPC64_REL32:
- case R_PPC64_REL64:
- case R_PPC64_ADDR14:
- case R_PPC64_ADDR14_BRNTAKEN:
- case R_PPC64_ADDR14_BRTAKEN:
case R_PPC64_ADDR16:
case R_PPC64_ADDR16_DS:
case R_PPC64_ADDR16_HA:
case R_PPC64_ADDR16_HI:
+ case R_PPC64_ADDR16_HIGH:
+ case R_PPC64_ADDR16_HIGHA:
case R_PPC64_ADDR16_HIGHER:
case R_PPC64_ADDR16_HIGHERA:
case R_PPC64_ADDR16_HIGHEST:
case R_PPC64_ADDR16_HIGHESTA:
case R_PPC64_ADDR16_LO:
case R_PPC64_ADDR16_LO_DS:
+ if (h != NULL && !info->shared && abiversion (abfd) != 1
+ && rel->r_addend == 0)
+ {
+ /* We may need a .plt entry if this reloc refers to a
+ function in a shared lib. */
+ if (!update_plt_info (abfd, &h->plt.plist, rel->r_addend))
+ return FALSE;
+ h->pointer_equality_needed = 1;
+ }
+ /* Fall through. */
+
+ case R_PPC64_REL30:
+ case R_PPC64_REL32:
+ case R_PPC64_REL64:
+ case R_PPC64_ADDR14:
+ case R_PPC64_ADDR14_BRNTAKEN:
+ case R_PPC64_ADDR14_BRTAKEN:
case R_PPC64_ADDR24:
case R_PPC64_ADDR32:
case R_PPC64_UADDR16:
return TRUE;
}
+/* Merge backend specific data from an object file to the output
+ object file when linking. */
+
+static bfd_boolean
+ppc64_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
+{
+ unsigned long iflags, oflags;
+
+ if ((ibfd->flags & BFD_LINKER_CREATED) != 0)
+ return TRUE;
+
+ if (!is_ppc64_elf (ibfd) || !is_ppc64_elf (obfd))
+ return TRUE;
+
+ if (!_bfd_generic_verify_endian_match (ibfd, obfd))
+ return FALSE;
+
+ iflags = elf_elfheader (ibfd)->e_flags;
+ oflags = elf_elfheader (obfd)->e_flags;
+
+ if (iflags & ~EF_PPC64_ABI)
+ {
+ (*_bfd_error_handler)
+ (_("%B uses unknown e_flags 0x%lx"), ibfd, iflags);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+ else if (iflags != oflags && iflags != 0)
+ {
+ (*_bfd_error_handler)
+ (_("%B: ABI version %ld is not compatible with ABI version %ld output"),
+ ibfd, iflags, oflags);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+
+ /* Merge Tag_compatibility attributes and any common GNU ones. */
+ _bfd_elf_merge_object_attributes (ibfd, obfd);
+
+ return TRUE;
+}
+
+static bfd_boolean
+ppc64_elf_print_private_bfd_data (bfd *abfd, void *ptr)
+{
+ /* Print normal ELF private data. */
+ _bfd_elf_print_private_bfd_data (abfd, ptr);
+
+ if (elf_elfheader (abfd)->e_flags != 0)
+ {
+ FILE *file = ptr;
+
+ /* xgettext:c-format */
+ fprintf (file, _("private flags = 0x%lx:"),
+ elf_elfheader (abfd)->e_flags);
+
+ if ((elf_elfheader (abfd)->e_flags & EF_PPC64_ABI) != 0)
+ fprintf (file, _(" [abiv%ld]"),
+ elf_elfheader (abfd)->e_flags & EF_PPC64_ABI);
+ fputc ('\n', file);
+ }
+
+ return TRUE;
+}
+
/* OFFSET in OPD_SEC specifies a function descriptor. Return the address
- of the code entry point, and its section. */
+ of the code entry point, and its section, which must be in the same
+ object as OPD_SEC. Returns (bfd_vma) -1 on error. */
static bfd_vma
opd_entry_value (asection *opd_sec,
at a final linked executable with addr2line or somesuch. */
if (opd_sec->reloc_count == 0)
{
- char buf[8];
+ bfd_byte *contents = ppc64_elf_tdata (opd_bfd)->opd.contents;
- if (!bfd_get_section_contents (opd_bfd, opd_sec, buf, offset, 8))
- return (bfd_vma) -1;
+ if (contents == NULL)
+ {
+ if (!bfd_malloc_and_get_section (opd_bfd, opd_sec, &contents))
+ return (bfd_vma) -1;
+ ppc64_elf_tdata (opd_bfd)->opd.contents = contents;
+ }
- val = bfd_get_64 (opd_bfd, buf);
+ val = bfd_get_64 (opd_bfd, contents + offset);
if (code_sec != NULL)
{
asection *sec, *likely = NULL;
BFD_ASSERT (is_ppc64_elf (opd_bfd));
- relocs = ppc64_elf_tdata (opd_bfd)->opd_relocs;
+ relocs = ppc64_elf_tdata (opd_bfd)->opd.relocs;
if (relocs == NULL)
relocs = _bfd_elf_link_read_relocs (opd_bfd, opd_sec, NULL, NULL, TRUE);
+ /* PR 17512: file: df8e1fd6. */
+ if (relocs == NULL)
+ return (bfd_vma) -1;
/* Go find the opd reloc at the sym address. */
lo = relocs;
&& ELF64_R_TYPE ((look + 1)->r_info) == R_PPC64_TOC)
{
unsigned long symndx = ELF64_R_SYM (look->r_info);
- asection *sec;
+ asection *sec = NULL;
+
+ if (symndx >= symtab_hdr->sh_info
+ && elf_sym_hashes (opd_bfd) != NULL)
+ {
+ struct elf_link_hash_entry **sym_hashes;
+ struct elf_link_hash_entry *rh;
+
+ sym_hashes = elf_sym_hashes (opd_bfd);
+ rh = sym_hashes[symndx - symtab_hdr->sh_info];
+ if (rh != NULL)
+ {
+ rh = elf_follow_link (rh);
+ BFD_ASSERT (rh->root.type == bfd_link_hash_defined
+ || rh->root.type == bfd_link_hash_defweak);
+ val = rh->root.u.def.value;
+ sec = rh->root.u.def.section;
+ if (sec->owner != opd_bfd)
+ {
+ sec = NULL;
+ val = (bfd_vma) -1;
+ }
+ }
+ }
- if (symndx < symtab_hdr->sh_info
- || elf_sym_hashes (opd_bfd) == NULL)
+ if (sec == NULL)
{
Elf_Internal_Sym *sym;
- sym = (Elf_Internal_Sym *) symtab_hdr->contents;
- if (sym == NULL)
+ if (symndx < symtab_hdr->sh_info)
+ {
+ sym = (Elf_Internal_Sym *) symtab_hdr->contents;
+ if (sym == NULL)
+ {
+ size_t symcnt = symtab_hdr->sh_info;
+ sym = bfd_elf_get_elf_syms (opd_bfd, symtab_hdr,
+ symcnt, 0,
+ NULL, NULL, NULL);
+ if (sym == NULL)
+ break;
+ symtab_hdr->contents = (bfd_byte *) sym;
+ }
+ sym += symndx;
+ }
+ else
{
- size_t symcnt = symtab_hdr->sh_info;
- if (elf_sym_hashes (opd_bfd) == NULL)
- symcnt = symtab_hdr->sh_size / symtab_hdr->sh_entsize;
- sym = bfd_elf_get_elf_syms (opd_bfd, symtab_hdr, symcnt,
- 0, NULL, NULL, NULL);
+ sym = bfd_elf_get_elf_syms (opd_bfd, symtab_hdr,
+ 1, symndx,
+ NULL, NULL, NULL);
if (sym == NULL)
break;
- symtab_hdr->contents = (bfd_byte *) sym;
}
-
- sym += symndx;
- val = sym->st_value;
sec = bfd_section_from_elf_index (opd_bfd, sym->st_shndx);
+ if (sec == NULL)
+ break;
BFD_ASSERT ((sec->flags & SEC_MERGE) == 0);
+ val = sym->st_value;
}
- else
- {
- struct elf_link_hash_entry **sym_hashes;
- struct elf_link_hash_entry *rh;
- sym_hashes = elf_sym_hashes (opd_bfd);
- rh = sym_hashes[symndx - symtab_hdr->sh_info];
- rh = elf_follow_link (rh);
- BFD_ASSERT (rh->root.type == bfd_link_hash_defined
- || rh->root.type == bfd_link_hash_defweak);
- val = rh->root.u.def.value;
- sec = rh->root.u.def.section;
- }
val += look->r_addend;
if (code_off != NULL)
*code_off = val;
else
*code_sec = sec;
}
- if (sec != NULL && sec->output_section != NULL)
+ if (sec->output_section != NULL)
val += sec->output_section->vma + sec->output_offset;
}
break;
struct bfd_link_info *info = (struct bfd_link_info *) inf;
struct ppc_link_hash_entry *eh = (struct ppc_link_hash_entry *) h;
struct ppc_link_hash_entry *fdh;
+ struct bfd_elf_dynamic_list *d = info->dynamic_list;
/* Dynamic linking info is on the func descriptor sym. */
fdh = defined_func_desc (eh);
if ((eh->elf.root.type == bfd_link_hash_defined
|| eh->elf.root.type == bfd_link_hash_defweak)
&& (eh->elf.ref_dynamic
- || (!info->executable
- && eh->elf.def_regular
+ || ((eh->elf.def_regular || ELF_COMMON_DEF_P (&eh->elf))
&& ELF_ST_VISIBILITY (eh->elf.other) != STV_INTERNAL
&& ELF_ST_VISIBILITY (eh->elf.other) != STV_HIDDEN
+ && (!info->executable
+ || info->export_dynamic
+ || (eh->elf.dynamic
+ && d != NULL
+ && (*d->match) (&d->head, NULL, eh->elf.root.root.string)))
&& (strchr (eh->elf.root.root.string, ELF_VER_CHR) != NULL
|| !bfd_hide_sym_by_version (info->version_info,
eh->elf.root.root.string)))))
{
rsec->gc_mark = 1;
- rsec = opd->func_sec[(sym->st_value + rel->r_addend) / 8];
+ rsec = opd->func_sec[OPD_NDX (sym->st_value + rel->r_addend)];
}
}
savegpr0_tail (bfd *abfd, bfd_byte *p, int r)
{
p = savegpr0 (abfd, p, r);
- bfd_put_32 (abfd, STD_R0_0R1 + 16, p);
+ bfd_put_32 (abfd, STD_R0_0R1 + STK_LR, p);
p = p + 4;
bfd_put_32 (abfd, BLR, p);
return p + 4;
static bfd_byte *
restgpr0_tail (bfd *abfd, bfd_byte *p, int r)
{
- bfd_put_32 (abfd, LD_R0_0R1 + 16, p);
+ bfd_put_32 (abfd, LD_R0_0R1 + STK_LR, p);
p = p + 4;
p = restgpr0 (abfd, p, r);
bfd_put_32 (abfd, MTLR_R0, p);
savefpr0_tail (bfd *abfd, bfd_byte *p, int r)
{
p = savefpr (abfd, p, r);
- bfd_put_32 (abfd, STD_R0_0R1 + 16, p);
+ bfd_put_32 (abfd, STD_R0_0R1 + STK_LR, p);
p = p + 4;
bfd_put_32 (abfd, BLR, p);
return p + 4;
static bfd_byte *
restfpr0_tail (bfd *abfd, bfd_byte *p, int r)
{
- bfd_put_32 (abfd, LD_R0_0R1 + 16, p);
+ bfd_put_32 (abfd, LD_R0_0R1 + STK_LR, p);
p = p + 4;
p = restfpr (abfd, p, r);
bfd_put_32 (abfd, MTLR_R0, p);
if (htab == NULL)
return FALSE;
+ if (!info->relocatable
+ && htab->elf.hgot != NULL)
+ {
+ _bfd_elf_link_hash_hide_symbol (info, htab->elf.hgot, TRUE);
+ /* Make .TOC. defined so as to prevent it being made dynamic.
+ The wrong value here is fixed later in ppc64_elf_set_toc. */
+ htab->elf.hgot->type = STT_OBJECT;
+ htab->elf.hgot->root.type = bfd_link_hash_defined;
+ htab->elf.hgot->root.u.def.value = 0;
+ htab->elf.hgot->root.u.def.section = bfd_abs_section_ptr;
+ htab->elf.hgot->def_regular = 1;
+ htab->elf.hgot->other = ((htab->elf.hgot->other & ~ELF_ST_VISIBILITY (-1))
+ | STV_HIDDEN);
+ }
+
if (htab->sfpr == NULL)
/* We don't have any relocs. */
return TRUE;
/* Provide any missing _save* and _rest* functions. */
htab->sfpr->size = 0;
- if (!info->relocatable)
+ if (htab->params->save_restore_funcs)
for (i = 0; i < sizeof (funcs) / sizeof (funcs[0]); i++)
if (!sfpr_define (info, &funcs[i]))
return FALSE;
return TRUE;
}
+/* Return true if we have dynamic relocs that apply to read-only sections. */
+
+static bfd_boolean
+readonly_dynrelocs (struct elf_link_hash_entry *h)
+{
+ struct ppc_link_hash_entry *eh;
+ struct elf_dyn_relocs *p;
+
+ eh = (struct ppc_link_hash_entry *) h;
+ for (p = eh->dyn_relocs; p != NULL; p = p->next)
+ {
+ asection *s = p->sec->output_section;
+
+ if (s != NULL && (s->flags & SEC_READONLY) != 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
/* 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
{
h->plt.plist = NULL;
h->needs_plt = 0;
+ h->pointer_equality_needed = 0;
+ }
+ else if (abiversion (info->output_bfd) == 2)
+ {
+ /* Taking a function's address in a read/write section
+ doesn't require us to define the function symbol in the
+ executable on a global entry stub. A dynamic reloc can
+ be used instead. */
+ if (h->pointer_equality_needed
+ && h->type != STT_GNU_IFUNC
+ && !readonly_dynrelocs (h))
+ {
+ h->pointer_equality_needed = 0;
+ h->non_got_ref = 0;
+ }
+
+ /* After adjust_dynamic_symbol, non_got_ref set in the
+ non-shared case means that we have allocated space in
+ .dynbss for the symbol and thus dyn_relocs for this
+ symbol should be discarded.
+ If we get here we know we are making a PLT entry for this
+ symbol, and in an executable we'd normally resolve
+ relocations against this symbol to the PLT entry. Allow
+ dynamic relocs if the reference is weak, and the dynamic
+ relocs will not cause text relocation. */
+ else if (!h->ref_regular_nonweak
+ && h->non_got_ref
+ && h->type != STT_GNU_IFUNC
+ && !readonly_dynrelocs (h))
+ h->non_got_ref = 0;
+
+ /* If making a plt entry, then we don't need copy relocs. */
+ return TRUE;
}
}
else
if (!h->def_dynamic || !h->ref_regular || h->def_regular)
return TRUE;
- if (ELIMINATE_COPY_RELOCS)
+ /* If we didn't find any dynamic relocs in read-only sections, then
+ we'll be keeping the dynamic relocs and avoiding the copy reloc. */
+ if (ELIMINATE_COPY_RELOCS && !readonly_dynrelocs (h))
{
- struct ppc_link_hash_entry * eh;
- struct elf_dyn_relocs *p;
-
- eh = (struct ppc_link_hash_entry *) h;
- for (p = eh->dyn_relocs; p != NULL; p = p->next)
- {
- s = p->sec->output_section;
- if (s != NULL && (s->flags & SEC_READONLY) != 0)
- break;
- }
-
- /* If we didn't find any dynamic relocs in read-only sections, then
- we'll be keeping the dynamic relocs and avoiding the copy reloc. */
- if (p == NULL)
- {
- h->non_got_ref = 0;
- return TRUE;
- }
+ h->non_got_ref = 0;
+ return TRUE;
}
if (h->plt.plist != NULL)
s = htab->dynbss;
- return _bfd_elf_adjust_dynamic_copy (h, s);
+ return _bfd_elf_adjust_dynamic_copy (info, h, s);
}
/* If given a function descriptor symbol, hide both the function code
opd = get_opd_info (sym_sec);
if (opd != NULL && opd->adjust != NULL)
{
- long adjust = opd->adjust[eh->elf.root.u.def.value / 8];
+ long adjust = opd->adjust[OPD_NDX (eh->elf.root.u.def.value)];
if (adjust == -1)
{
/* This entry has been deleted. */
case R_PPC64_TPREL16_HA:
case R_PPC64_TPREL16_DS:
case R_PPC64_TPREL16_LO_DS:
+ case R_PPC64_TPREL16_HIGH:
+ case R_PPC64_TPREL16_HIGHA:
case R_PPC64_TPREL16_HIGHER:
case R_PPC64_TPREL16_HIGHERA:
case R_PPC64_TPREL16_HIGHEST:
case R_PPC64_ADDR16_DS:
case R_PPC64_ADDR16_HA:
case R_PPC64_ADDR16_HI:
+ case R_PPC64_ADDR16_HIGH:
+ case R_PPC64_ADDR16_HIGHA:
case R_PPC64_ADDR16_HIGHER:
case R_PPC64_ADDR16_HIGHERA:
case R_PPC64_ADDR16_HIGHEST:
applications. */
bfd_boolean
-ppc64_elf_edit_opd (struct bfd_link_info *info, bfd_boolean non_overlapping)
+ppc64_elf_edit_opd (struct bfd_link_info *info)
{
bfd *ibfd;
bfd_boolean some_edited = FALSE;
asection *need_pad = NULL;
+ struct ppc_link_hash_table *htab;
+
+ htab = ppc_hash_table (info);
+ if (htab == NULL)
+ return FALSE;
- for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
{
asection *sec;
Elf_Internal_Rela *relstart, *rel, *relend;
Elf_Internal_Shdr *symtab_hdr;
Elf_Internal_Sym *local_syms;
- bfd_vma offset;
struct _opd_sec_data *opd;
- bfd_boolean need_edit, add_aux_fields;
+ bfd_boolean need_edit, add_aux_fields, broken;
bfd_size_type cnt_16b = 0;
if (!is_ppc64_elf (ibfd))
/* First run through the relocs to check they are sane, and to
determine whether we need to edit this opd section. */
need_edit = FALSE;
+ broken = FALSE;
need_pad = sec;
- offset = 0;
relend = relstart + sec->reloc_count;
for (rel = relstart; rel < relend; )
{
asection *sym_sec;
struct elf_link_hash_entry *h;
Elf_Internal_Sym *sym;
+ bfd_vma offset;
- /* .opd contains a regular array of 16 or 24 byte entries. We're
+ /* .opd contains an array of 16 or 24 byte entries. We're
only interested in the reloc pointing to a function entry
point. */
- if (rel->r_offset != offset
- || rel + 1 >= relend
- || (rel + 1)->r_offset != offset + 8)
+ offset = rel->r_offset;
+ if (rel + 1 == relend
+ || rel[1].r_offset != offset + 8)
{
/* If someone messes with .opd alignment then after a
"ld -r" we might have padding in the middle of .opd.
broken_opd:
(*_bfd_error_handler)
(_("%B: .opd is not a regular array of opd entries"), ibfd);
- need_edit = FALSE;
+ broken = TRUE;
break;
}
(*_bfd_error_handler)
(_("%B: unexpected reloc type %u in .opd section"),
ibfd, r_type);
- need_edit = FALSE;
+ broken = TRUE;
break;
}
(*_bfd_error_handler)
(_("%B: undefined sym `%s' in .opd section"),
ibfd, sym_name);
- need_edit = FALSE;
+ broken = TRUE;
break;
}
need_edit = TRUE;
rel += 2;
- if (rel == relend
- || (rel + 1 == relend && rel->r_offset == offset + 16))
+ if (rel + 1 == relend
+ || (rel + 2 < relend
+ && ELF64_R_TYPE (rel[2].r_info) == R_PPC64_TOC))
+ ++rel;
+
+ if (rel == relend)
{
if (sec->size == offset + 24)
{
need_pad = NULL;
break;
}
- if (rel == relend && sec->size == offset + 16)
+ if (sec->size == offset + 16)
{
cnt_16b++;
break;
}
goto broken_opd;
}
-
- if (rel->r_offset == offset + 24)
- offset += 24;
- else if (rel->r_offset != offset + 16)
- goto broken_opd;
else if (rel + 1 < relend
&& ELF64_R_TYPE (rel[0].r_info) == R_PPC64_ADDR64
&& ELF64_R_TYPE (rel[1].r_info) == R_PPC64_TOC)
{
- offset += 16;
- cnt_16b++;
- }
- else if (rel + 2 < relend
- && ELF64_R_TYPE (rel[1].r_info) == R_PPC64_ADDR64
- && ELF64_R_TYPE (rel[2].r_info) == R_PPC64_TOC)
- {
- offset += 24;
- rel += 1;
+ if (rel[0].r_offset == offset + 16)
+ cnt_16b++;
+ else if (rel[0].r_offset != offset + 24)
+ goto broken_opd;
}
else
goto broken_opd;
}
- add_aux_fields = non_overlapping && cnt_16b > 0;
+ add_aux_fields = htab->params->non_overlapping_opd && cnt_16b > 0;
- if (need_edit || add_aux_fields)
+ if (!broken && (need_edit || add_aux_fields))
{
Elf_Internal_Rela *write_rel;
Elf_Internal_Shdr *rel_hdr;
bfd_byte *rptr, *wptr;
bfd_byte *new_contents;
- bfd_boolean skip;
- long opd_ent_size;
bfd_size_type amt;
new_contents = NULL;
- amt = sec->size * sizeof (long) / 8;
+ amt = OPD_NDX (sec->size) * sizeof (long);
opd = &ppc64_elf_section_data (sec)->u.opd;
opd->adjust = bfd_zalloc (sec->owner, amt);
if (opd->adjust == NULL)
new_contents = bfd_malloc (sec->size + cnt_16b * 8);
if (new_contents == NULL)
return FALSE;
- need_pad = FALSE;
+ need_pad = NULL;
}
wptr = new_contents;
rptr = sec->contents;
-
write_rel = relstart;
- skip = FALSE;
- offset = 0;
- opd_ent_size = 0;
- for (rel = relstart; rel < relend; rel++)
+ for (rel = relstart; rel < relend; )
{
unsigned long r_symndx;
asection *sym_sec;
struct elf_link_hash_entry *h;
+ struct ppc_link_hash_entry *fdh = NULL;
Elf_Internal_Sym *sym;
+ long opd_ent_size;
+ Elf_Internal_Rela *next_rel;
+ bfd_boolean skip;
r_symndx = ELF64_R_SYM (rel->r_info);
if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms,
r_symndx, ibfd))
goto error_ret;
- if (rel->r_offset == offset)
+ next_rel = rel + 2;
+ if (next_rel + 1 == relend
+ || (next_rel + 2 < relend
+ && ELF64_R_TYPE (next_rel[2].r_info) == R_PPC64_TOC))
+ ++next_rel;
+
+ /* See if the .opd entry is full 24 byte or
+ 16 byte (with fd_aux entry overlapped with next
+ fd_func). */
+ opd_ent_size = 24;
+ if (next_rel == relend)
{
- struct ppc_link_hash_entry *fdh = NULL;
-
- /* See if the .opd entry is full 24 byte or
- 16 byte (with fd_aux entry overlapped with next
- fd_func). */
- opd_ent_size = 24;
- if ((rel + 2 == relend && sec->size == offset + 16)
- || (rel + 3 < relend
- && rel[2].r_offset == offset + 16
- && rel[3].r_offset == offset + 24
- && ELF64_R_TYPE (rel[2].r_info) == R_PPC64_ADDR64
- && ELF64_R_TYPE (rel[3].r_info) == R_PPC64_TOC))
+ if (sec->size == rel->r_offset + 16)
opd_ent_size = 16;
+ }
+ else if (next_rel->r_offset == rel->r_offset + 16)
+ opd_ent_size = 16;
- if (h != NULL
- && h->root.root.string[0] == '.')
- {
- struct ppc_link_hash_table *htab;
-
- htab = ppc_hash_table (info);
- if (htab != NULL)
- fdh = lookup_fdh ((struct ppc_link_hash_entry *) h,
- htab);
- if (fdh != NULL
- && fdh->elf.root.type != bfd_link_hash_defined
- && fdh->elf.root.type != bfd_link_hash_defweak)
- fdh = NULL;
- }
+ if (h != NULL
+ && h->root.root.string[0] == '.')
+ {
+ fdh = lookup_fdh ((struct ppc_link_hash_entry *) h, htab);
+ if (fdh != NULL
+ && fdh->elf.root.type != bfd_link_hash_defined
+ && fdh->elf.root.type != bfd_link_hash_defweak)
+ fdh = NULL;
+ }
- skip = (sym_sec->owner != ibfd
- || sym_sec->output_section == bfd_abs_section_ptr);
- if (skip)
+ skip = (sym_sec->owner != ibfd
+ || sym_sec->output_section == bfd_abs_section_ptr);
+ if (skip)
+ {
+ if (fdh != NULL && sym_sec->owner == ibfd)
{
- if (fdh != NULL && sym_sec->owner == ibfd)
- {
- /* Arrange for the function descriptor sym
- to be dropped. */
- fdh->elf.root.u.def.value = 0;
- fdh->elf.root.u.def.section = sym_sec;
- }
- opd->adjust[rel->r_offset / 8] = -1;
+ /* Arrange for the function descriptor sym
+ to be dropped. */
+ fdh->elf.root.u.def.value = 0;
+ fdh->elf.root.u.def.section = sym_sec;
}
- else
- {
- /* We'll be keeping this opd entry. */
+ opd->adjust[OPD_NDX (rel->r_offset)] = -1;
- if (fdh != NULL)
- {
- /* Redefine the function descriptor symbol to
- this location in the opd section. It is
- necessary to update the value here rather
- than using an array of adjustments as we do
- for local symbols, because various places
- in the generic ELF code use the value
- stored in u.def.value. */
- fdh->elf.root.u.def.value = wptr - new_contents;
- fdh->adjust_done = 1;
- }
+ if (NO_OPD_RELOCS || info->relocatable)
+ rel = next_rel;
+ else
+ while (1)
+ {
+ if (!dec_dynrel_count (rel->r_info, sec, info,
+ NULL, h, sym))
+ goto error_ret;
- /* Local syms are a bit tricky. We could
- tweak them as they can be cached, but
- we'd need to look through the local syms
- for the function descriptor sym which we
- don't have at the moment. So keep an
- array of adjustments. */
- opd->adjust[rel->r_offset / 8]
- = (wptr - new_contents) - (rptr - sec->contents);
-
- if (wptr != rptr)
- memcpy (wptr, rptr, opd_ent_size);
- wptr += opd_ent_size;
- if (add_aux_fields && opd_ent_size == 16)
- {
- memset (wptr, '\0', 8);
- wptr += 8;
- }
- }
- rptr += opd_ent_size;
- offset += opd_ent_size;
- }
+ if (++rel == next_rel)
+ break;
- if (skip)
- {
- if (!NO_OPD_RELOCS
- && !info->relocatable
- && !dec_dynrel_count (rel->r_info, sec, info,
- NULL, h, sym))
- goto error_ret;
+ r_symndx = ELF64_R_SYM (rel->r_info);
+ if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms,
+ r_symndx, ibfd))
+ goto error_ret;
+ }
}
else
{
+ /* We'll be keeping this opd entry. */
+ long adjust;
+
+ if (fdh != NULL)
+ {
+ /* Redefine the function descriptor symbol to
+ this location in the opd section. It is
+ necessary to update the value here rather
+ than using an array of adjustments as we do
+ for local symbols, because various places
+ in the generic ELF code use the value
+ stored in u.def.value. */
+ fdh->elf.root.u.def.value = wptr - new_contents;
+ fdh->adjust_done = 1;
+ }
+
+ /* Local syms are a bit tricky. We could
+ tweak them as they can be cached, but
+ we'd need to look through the local syms
+ for the function descriptor sym which we
+ don't have at the moment. So keep an
+ array of adjustments. */
+ adjust = (wptr - new_contents) - (rptr - sec->contents);
+ opd->adjust[OPD_NDX (rel->r_offset)] = adjust;
+
+ if (wptr != rptr)
+ memcpy (wptr, rptr, opd_ent_size);
+ wptr += opd_ent_size;
+ if (add_aux_fields && opd_ent_size == 16)
+ {
+ memset (wptr, '\0', 8);
+ wptr += 8;
+ }
+
/* We need to adjust any reloc offsets to point to the
- new opd entries. While we're at it, we may as well
- remove redundant relocs. */
- rel->r_offset += opd->adjust[(offset - opd_ent_size) / 8];
- if (write_rel != rel)
- memcpy (write_rel, rel, sizeof (*rel));
- ++write_rel;
+ new opd entries. */
+ for ( ; rel != next_rel; ++rel)
+ {
+ rel->r_offset += adjust;
+ if (write_rel != rel)
+ memcpy (write_rel, rel, sizeof (*rel));
+ ++write_rel;
+ }
}
+
+ rptr += opd_ent_size;
}
sec->size = wptr - new_contents;
/* Set htab->tls_get_addr and call the generic ELF tls_setup function. */
asection *
-ppc64_elf_tls_setup (struct bfd_link_info *info,
- int no_tls_get_addr_opt,
- int *no_multi_toc)
+ppc64_elf_tls_setup (struct bfd_link_info *info)
{
struct ppc_link_hash_table *htab;
if (htab == NULL)
return NULL;
- if (*no_multi_toc)
+ if (abiversion (info->output_bfd) == 1)
+ htab->opd_abi = 1;
+
+ if (htab->params->no_multi_toc)
htab->do_multi_toc = 0;
else if (!htab->do_multi_toc)
- *no_multi_toc = 1;
+ htab->params->no_multi_toc = 1;
htab->tls_get_addr = ((struct ppc_link_hash_entry *)
elf_link_hash_lookup (&htab->elf, ".__tls_get_addr",
htab->tls_get_addr_fd = ((struct ppc_link_hash_entry *)
elf_link_hash_lookup (&htab->elf, "__tls_get_addr",
FALSE, FALSE, TRUE));
- if (!no_tls_get_addr_opt)
+ if (!htab->params->no_tls_get_addr_opt)
{
struct elf_link_hash_entry *opt, *opt_fd, *tga, *tga_fd;
}
}
else
- no_tls_get_addr_opt = TRUE;
+ htab->params->no_tls_get_addr_opt = TRUE;
}
- htab->no_tls_get_addr_opt = no_tls_get_addr_opt;
return _bfd_elf_tls_setup (info->output_bfd, info);
}
and plt refcounts. */
toc_ref = NULL;
for (pass = 0; pass < 2; ++pass)
- for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
{
Elf_Internal_Sym *locsyms = NULL;
asection *toc = bfd_get_section_by_name (ibfd, ".toc");
relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL,
info->keep_memory);
if (relstart == NULL)
- return FALSE;
+ {
+ free (toc_ref);
+ return FALSE;
+ }
relend = relstart + sec->reloc_count;
for (rel = relstart; rel < relend; rel++)
else
value = sym->st_value;
value += rel->r_addend;
- BFD_ASSERT (value < toc->size && value % 8 == 0);
+ if (value % 8 != 0)
+ continue;
+ BFD_ASSERT (value < toc->size
+ && toc->output_offset % 8 == 0);
toc_ref_index = (value + toc->output_offset) / 8;
if (r_type == R_PPC64_TLS
|| r_type == R_PPC64_TLSGD
htab->do_toc_opt = 1;
toc_inf.global_toc_syms = TRUE;
- for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
{
asection *toc, *sec;
Elf_Internal_Shdr *symtab_hdr;
|| discarded_section (sym_sec))
continue;
- if (!SYMBOL_CALLS_LOCAL (info, h))
+ if (!SYMBOL_REFERENCES_LOCAL (info, h))
continue;
if (h != NULL)
relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL,
info->keep_memory);
if (relstart == NULL)
- goto error_ret;
+ {
+ free (used);
+ goto error_ret;
+ }
/* Mark toc entries referenced as used. */
do
dyn = htab->elf.dynamic_sections_created;
if (h->type == STT_GNU_IFUNC)
{
- htab->reliplt->size += rentsize;
+ htab->elf.irelplt->size += rentsize;
htab->got_reli_size += rentsize;
}
else if ((info->shared
if (!htab->elf.dynamic_sections_created
|| h->dynindx == -1)
{
- s = htab->iplt;
+ s = htab->elf.iplt;
pent->plt.offset = s->size;
- s->size += PLT_ENTRY_SIZE;
- s = htab->reliplt;
+ s->size += PLT_ENTRY_SIZE (htab);
+ s = htab->elf.irelplt;
}
else
{
/* If this is the first .plt entry, make room for the special
first entry. */
- s = htab->plt;
+ s = htab->elf.splt;
if (s->size == 0)
- s->size += PLT_INITIAL_ENTRY_SIZE;
+ s->size += PLT_INITIAL_ENTRY_SIZE (htab);
pent->plt.offset = s->size;
/* Make room for this entry. */
- s->size += PLT_ENTRY_SIZE;
+ s->size += PLT_ENTRY_SIZE (htab);
/* Make room for the .glink code. */
s = htab->glink;
if (s->size == 0)
s->size += GLINK_CALL_STUB_SIZE;
- /* We need bigger stubs past index 32767. */
- if (s->size >= GLINK_CALL_STUB_SIZE + 32768*2*4)
+ if (htab->opd_abi)
+ {
+ /* We need bigger stubs past index 32767. */
+ if (s->size >= GLINK_CALL_STUB_SIZE + 32768*2*4)
+ s->size += 4;
+ s->size += 2*4;
+ }
+ else
s->size += 4;
- s->size += 2*4;
/* We also need to make an entry in the .rela.plt section. */
- s = htab->relplt;
+ s = htab->elf.srelplt;
}
s->size += sizeof (Elf64_External_Rela);
doneone = TRUE;
{
asection *sreloc = elf_section_data (p->sec)->sreloc;
if (eh->elf.type == STT_GNU_IFUNC)
- sreloc = htab->reliplt;
+ sreloc = htab->elf.irelplt;
sreloc->size += p->count * sizeof (Elf64_External_Rela);
}
return TRUE;
}
-/* Find any dynamic relocs that apply to read-only sections. */
+/* Called via elf_link_hash_traverse from ppc64_elf_size_dynamic_sections
+ to set up space for global entry stubs. These are put in glink,
+ after the branch table. */
+
+static bfd_boolean
+size_global_entry_stubs (struct elf_link_hash_entry *h, void *inf)
+{
+ struct bfd_link_info *info;
+ struct ppc_link_hash_table *htab;
+ struct plt_entry *pent;
+ asection *s;
+
+ if (h->root.type == bfd_link_hash_indirect)
+ return TRUE;
+
+ if (!h->pointer_equality_needed)
+ return TRUE;
+
+ if (h->def_regular)
+ return TRUE;
+
+ info = inf;
+ htab = ppc_hash_table (info);
+ if (htab == NULL)
+ return FALSE;
+
+ s = htab->glink;
+ for (pent = h->plt.plist; pent != NULL; pent = pent->next)
+ if (pent->plt.offset != (bfd_vma) -1
+ && pent->addend == 0)
+ {
+ /* For ELFv2, if this symbol is not defined in a regular file
+ and we are not generating a shared library or pie, then we
+ need to define the symbol in the executable on a call stub.
+ This is to avoid text relocations. */
+ s->size = (s->size + 15) & -16;
+ h->root.u.def.section = s;
+ h->root.u.def.value = s->size;
+ s->size += 16;
+ break;
+ }
+ return TRUE;
+}
+
+/* Set DF_TEXTREL if we find any dynamic relocs that apply to
+ read-only sections. */
static bfd_boolean
-readonly_dynrelocs (struct elf_link_hash_entry *h, void *inf)
+maybe_set_textrel (struct elf_link_hash_entry *h, void *info)
{
- struct ppc_link_hash_entry *eh;
- struct elf_dyn_relocs *p;
+ if (h->root.type == bfd_link_hash_indirect)
+ return TRUE;
- eh = (struct ppc_link_hash_entry *) h;
- for (p = eh->dyn_relocs; p != NULL; p = p->next)
+ if (readonly_dynrelocs (h))
{
- asection *s = p->sec->output_section;
-
- if (s != NULL && (s->flags & SEC_READONLY) != 0)
- {
- struct bfd_link_info *info = inf;
+ ((struct bfd_link_info *) info)->flags |= DF_TEXTREL;
- info->flags |= DF_TEXTREL;
-
- /* Not an error, just cut short the traversal. */
- return FALSE;
- }
+ /* Not an error, just cut short the traversal. */
+ return FALSE;
}
return TRUE;
}
/* Set the sizes of the dynamic sections. */
static bfd_boolean
-ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
+ppc64_elf_size_dynamic_sections (bfd *output_bfd,
struct bfd_link_info *info)
{
struct ppc_link_hash_table *htab;
/* Set up .got offsets for local syms, and space for local dynamic
relocs. */
- for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
{
struct got_entry **lgot_ents;
struct got_entry **end_lgot_ents;
{
asection *srel = elf_section_data (p->sec)->sreloc;
if (p->ifunc)
- srel = htab->reliplt;
+ srel = htab->elf.irelplt;
srel->size += p->count * sizeof (Elf64_External_Rela);
if ((p->sec->output_section->flags & SEC_READONLY) != 0)
info->flags |= DF_TEXTREL;
s->size += ent_size;
if ((*lgot_masks & PLT_IFUNC) != 0)
{
- htab->reliplt->size += rel_size;
+ htab->elf.irelplt->size += rel_size;
htab->got_reli_size += rel_size;
}
else if (info->shared)
for (ent = *local_plt; ent != NULL; ent = ent->next)
if (ent->plt.refcount > 0)
{
- s = htab->iplt;
+ s = htab->elf.iplt;
ent->plt.offset = s->size;
- s->size += PLT_ENTRY_SIZE;
+ s->size += PLT_ENTRY_SIZE (htab);
- htab->reliplt->size += sizeof (Elf64_External_Rela);
+ htab->elf.irelplt->size += sizeof (Elf64_External_Rela);
}
else
ent->plt.offset = (bfd_vma) -1;
/* Allocate global sym .plt and .got entries, and space for global
sym dynamic relocs. */
elf_link_hash_traverse (&htab->elf, allocate_dynrelocs, info);
+ /* Stash the end of glink branch table. */
+ if (htab->glink != NULL)
+ htab->glink->rawsize = htab->glink->size;
+
+ if (!htab->opd_abi && !info->shared)
+ elf_link_hash_traverse (&htab->elf, size_global_entry_stubs, info);
first_tlsld = NULL;
- for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
{
struct got_entry *ent;
if (s == htab->brlt || s == htab->relbrlt)
/* These haven't been allocated yet; don't strip. */
continue;
- else if (s == htab->got
- || s == htab->plt
- || s == htab->iplt
+ else if (s == htab->elf.sgot
+ || s == htab->elf.splt
+ || s == htab->elf.iplt
|| s == htab->glink
|| s == htab->dynbss)
{
{
if (s->size != 0)
{
- if (s != htab->relplt)
+ if (s != htab->elf.srelplt)
relocs = TRUE;
/* We use the reloc_count field as a counter if we need
return FALSE;
}
- for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
{
if (!is_ppc64_elf (ibfd))
continue;
s = ppc64_elf_tdata (ibfd)->got;
- if (s != NULL && s != htab->got)
+ if (s != NULL && s != htab->elf.sgot)
{
if (s->size == 0)
s->flags |= SEC_EXCLUDE;
if (htab->elf.dynamic_sections_created)
{
+ bfd_boolean tls_opt;
+
/* Add some entries to the .dynamic section. We fill in the
values later, in ppc64_elf_finish_dynamic_sections, but we
must add the entries now so that we get the correct size for
return FALSE;
}
- if (htab->plt != NULL && htab->plt->size != 0)
+ if (htab->elf.splt != NULL && htab->elf.splt->size != 0)
{
if (!add_dynamic_entry (DT_PLTGOT, 0)
|| !add_dynamic_entry (DT_PLTRELSZ, 0)
return FALSE;
}
- if (NO_OPD_RELOCS)
+ if (NO_OPD_RELOCS && abiversion (output_bfd) <= 1)
{
if (!add_dynamic_entry (DT_PPC64_OPD, 0)
|| !add_dynamic_entry (DT_PPC64_OPDSZ, 0))
return FALSE;
}
- if (!htab->no_tls_get_addr_opt
- && htab->tls_get_addr_fd != NULL
- && htab->tls_get_addr_fd->elf.plt.plist != NULL
- && !add_dynamic_entry (DT_PPC64_TLSOPT, 0))
- return FALSE;
+ tls_opt = (!htab->params->no_tls_get_addr_opt
+ && htab->tls_get_addr_fd != NULL
+ && htab->tls_get_addr_fd->elf.plt.plist != NULL);
+ if (tls_opt || !htab->opd_abi)
+ {
+ if (!add_dynamic_entry (DT_PPC64_OPT, tls_opt ? PPC64_OPT_TLS : 0))
+ return FALSE;
+ }
if (relocs)
{
/* If any dynamic relocs apply to a read-only section,
then we need a DT_TEXTREL entry. */
if ((info->flags & DF_TEXTREL) == 0)
- elf_link_hash_traverse (&htab->elf, readonly_dynrelocs, info);
+ elf_link_hash_traverse (&htab->elf, maybe_set_textrel, info);
if ((info->flags & DF_TEXTREL) != 0)
{
return TRUE;
}
+/* Return TRUE if symbol should be hashed in the `.gnu.hash' section. */
+
+static bfd_boolean
+ppc64_elf_hash_symbol (struct elf_link_hash_entry *h)
+{
+ if (h->plt.plist != NULL
+ && !h->def_regular
+ && !h->pointer_equality_needed)
+ return FALSE;
+
+ return _bfd_elf_hash_symbol (h);
+}
+
/* Determine the type of stub needed, if any, for a call. */
static inline enum ppc_stub_type
const Elf_Internal_Rela *rel,
struct ppc_link_hash_entry **hash,
struct plt_entry **plt_ent,
- bfd_vma destination)
+ bfd_vma destination,
+ unsigned long local_off)
{
struct ppc_link_hash_entry *h = *hash;
bfd_vma location;
if (r_type != R_PPC64_REL24)
max_branch_offset = 1 << 15;
- if (branch_offset + max_branch_offset >= 2 * max_branch_offset)
+ if (branch_offset + max_branch_offset >= 2 * max_branch_offset - local_off)
/* We need a stub. Figure out whether a long_branch or plt_branch
is needed later. */
return ppc_stub_long_branch;
the appropriate glink entry if so.
. fake dep barrier compare
- . ld 11,xxx(2) ld 11,xxx(2)
- . mtctr 11 mtctr 11
- . xor 11,11,11 ld 2,xxx+8(2)
+ . ld 12,xxx(2) ld 12,xxx(2)
+ . mtctr 12 mtctr 12
+ . xor 11,12,12 ld 2,xxx+8(2)
. add 2,2,11 cmpldi 2,0
. ld 2,xxx+8(2) bnectr+
. bctr b <glink_entry>
struct ppc_stub_hash_entry *stub_entry,
bfd_vma off)
{
- unsigned size = PLT_CALL_STUB_SIZE;
-
- if (!(ALWAYS_EMIT_R2SAVE
- || stub_entry->stub_type == ppc_stub_plt_call_r2save))
- size -= 4;
- if (!htab->plt_static_chain)
- size -= 4;
- if (htab->plt_thread_safe)
- size += 8;
- if (PPC_HA (off) == 0)
- size -= 4;
- if (PPC_HA (off + 8 + 8 * htab->plt_static_chain) != PPC_HA (off))
+ unsigned size = 12;
+
+ if (ALWAYS_EMIT_R2SAVE
+ || stub_entry->stub_type == ppc_stub_plt_call_r2save)
+ size += 4;
+ if (PPC_HA (off) != 0)
size += 4;
+ if (htab->opd_abi)
+ {
+ size += 4;
+ if (htab->params->plt_static_chain)
+ size += 4;
+ if (htab->params->plt_thread_safe)
+ size += 8;
+ if (PPC_HA (off + 8 + 8 * htab->params->plt_static_chain) != PPC_HA (off))
+ size += 4;
+ }
if (stub_entry->h != NULL
&& (stub_entry->h == htab->tls_get_addr_fd
|| stub_entry->h == htab->tls_get_addr)
- && !htab->no_tls_get_addr_opt)
+ && !htab->params->no_tls_get_addr_opt)
size += 13 * 4;
return size;
}
struct ppc_stub_hash_entry *stub_entry,
bfd_vma plt_off)
{
- int stub_align = 1 << htab->plt_stub_align;
+ int stub_align = 1 << htab->params->plt_stub_align;
unsigned stub_size = plt_stub_size (htab, stub_entry, plt_off);
bfd_vma stub_off = stub_entry->stub_sec->size;
if (((stub_off + stub_size - 1) & -stub_align) - (stub_off & -stub_align)
- > (stub_size & -stub_align))
+ > ((stub_size - 1) & -stub_align))
return stub_align - (stub_off & (stub_align - 1));
return 0;
}
struct ppc_stub_hash_entry *stub_entry,
bfd_byte *p, bfd_vma offset, Elf_Internal_Rela *r)
{
- bfd *obfd = htab->stub_bfd;
- bfd_boolean plt_static_chain = htab->plt_static_chain;
- bfd_boolean plt_thread_safe = htab->plt_thread_safe;
+ bfd *obfd = htab->params->stub_bfd;
+ bfd_boolean plt_load_toc = htab->opd_abi;
+ bfd_boolean plt_static_chain = htab->params->plt_static_chain;
+ bfd_boolean plt_thread_safe = htab->params->plt_thread_safe;
bfd_boolean use_fake_dep = plt_thread_safe;
bfd_vma cmp_branch_off = 0;
if (!ALWAYS_USE_FAKE_DEP
+ && plt_load_toc
&& plt_thread_safe
&& !(stub_entry->h != NULL
&& (stub_entry->h == htab->tls_get_addr_fd
|| stub_entry->h == htab->tls_get_addr)
- && !htab->no_tls_get_addr_opt))
+ && !htab->params->no_tls_get_addr_opt))
{
bfd_vma pltoff = stub_entry->plt_ent->plt.offset & ~1;
- bfd_vma pltindex = (pltoff - PLT_INITIAL_ENTRY_SIZE) / PLT_ENTRY_SIZE;
+ bfd_vma pltindex = ((pltoff - PLT_INITIAL_ENTRY_SIZE (htab))
+ / PLT_ENTRY_SIZE (htab));
bfd_vma glinkoff = GLINK_CALL_STUB_SIZE + pltindex * 8;
bfd_vma to, from;
r[1].r_offset = r[0].r_offset + 4;
r[1].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_LO_DS);
r[1].r_addend = r[0].r_addend;
- if (PPC_HA (offset + 8 + 8 * plt_static_chain) != PPC_HA (offset))
- {
- r[2].r_offset = r[1].r_offset + 4;
- r[2].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_LO);
- r[2].r_addend = r[0].r_addend;
- }
- else
+ if (plt_load_toc)
{
- r[2].r_offset = r[1].r_offset + 8 + 8 * use_fake_dep;
- r[2].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_LO_DS);
- r[2].r_addend = r[0].r_addend + 8;
- if (plt_static_chain)
+ if (PPC_HA (offset + 8 + 8 * plt_static_chain) != PPC_HA (offset))
+ {
+ r[2].r_offset = r[1].r_offset + 4;
+ r[2].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_LO);
+ r[2].r_addend = r[0].r_addend;
+ }
+ else
{
- r[3].r_offset = r[2].r_offset + 4;
- r[3].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_LO_DS);
- r[3].r_addend = r[0].r_addend + 16;
+ r[2].r_offset = r[1].r_offset + 8 + 8 * use_fake_dep;
+ r[2].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_LO_DS);
+ r[2].r_addend = r[0].r_addend + 8;
+ if (plt_static_chain)
+ {
+ r[3].r_offset = r[2].r_offset + 4;
+ r[3].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_LO_DS);
+ r[3].r_addend = r[0].r_addend + 16;
+ }
}
}
}
if (ALWAYS_EMIT_R2SAVE
|| stub_entry->stub_type == ppc_stub_plt_call_r2save)
- bfd_put_32 (obfd, STD_R2_40R1, p), p += 4;
- bfd_put_32 (obfd, ADDIS_R12_R2 | PPC_HA (offset), p), p += 4;
- bfd_put_32 (obfd, LD_R11_0R12 | PPC_LO (offset), p), p += 4;
- if (PPC_HA (offset + 8 + 8 * plt_static_chain) != PPC_HA (offset))
+ bfd_put_32 (obfd, STD_R2_0R1 + STK_TOC (htab), p), p += 4;
+ if (plt_load_toc)
+ {
+ bfd_put_32 (obfd, ADDIS_R11_R2 | PPC_HA (offset), p), p += 4;
+ bfd_put_32 (obfd, LD_R12_0R11 | PPC_LO (offset), p), p += 4;
+ }
+ else
+ {
+ bfd_put_32 (obfd, ADDIS_R12_R2 | PPC_HA (offset), p), p += 4;
+ bfd_put_32 (obfd, LD_R12_0R12 | PPC_LO (offset), p), p += 4;
+ }
+ if (plt_load_toc
+ && PPC_HA (offset + 8 + 8 * plt_static_chain) != PPC_HA (offset))
{
- bfd_put_32 (obfd, ADDI_R12_R12 | PPC_LO (offset), p), p += 4;
+ bfd_put_32 (obfd, ADDI_R11_R11 | PPC_LO (offset), p), p += 4;
offset = 0;
}
- bfd_put_32 (obfd, MTCTR_R11, p), p += 4;
- if (use_fake_dep)
+ bfd_put_32 (obfd, MTCTR_R12, p), p += 4;
+ if (plt_load_toc)
{
- bfd_put_32 (obfd, XOR_R11_R11_R11, p), p += 4;
- bfd_put_32 (obfd, ADD_R12_R12_R11, p), p += 4;
+ if (use_fake_dep)
+ {
+ bfd_put_32 (obfd, XOR_R2_R12_R12, p), p += 4;
+ bfd_put_32 (obfd, ADD_R11_R11_R2, p), p += 4;
+ }
+ bfd_put_32 (obfd, LD_R2_0R11 | PPC_LO (offset + 8), p), p += 4;
+ if (plt_static_chain)
+ bfd_put_32 (obfd, LD_R11_0R11 | PPC_LO (offset + 16), p), p += 4;
}
- bfd_put_32 (obfd, LD_R2_0R12 | PPC_LO (offset + 8), p), p += 4;
- if (plt_static_chain)
- bfd_put_32 (obfd, LD_R11_0R12 | PPC_LO (offset + 16), p), p += 4;
}
else
{
|| stub_entry->stub_type == ppc_stub_plt_call_r2save)
r[0].r_offset += 4;
r[0].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_DS);
- if (PPC_HA (offset + 8 + 8 * plt_static_chain) != PPC_HA (offset))
- {
- r[1].r_offset = r[0].r_offset + 4;
- r[1].r_info = ELF64_R_INFO (0, R_PPC64_TOC16);
- r[1].r_addend = r[0].r_addend;
- }
- else
+ if (plt_load_toc)
{
- r[1].r_offset = r[0].r_offset + 8 + 8 * use_fake_dep;
- r[1].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_DS);
- r[1].r_addend = r[0].r_addend + 8 + 8 * plt_static_chain;
- if (plt_static_chain)
+ if (PPC_HA (offset + 8 + 8 * plt_static_chain) != PPC_HA (offset))
{
- r[2].r_offset = r[1].r_offset + 4;
- r[2].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_DS);
- r[2].r_addend = r[0].r_addend + 8;
+ r[1].r_offset = r[0].r_offset + 4;
+ r[1].r_info = ELF64_R_INFO (0, R_PPC64_TOC16);
+ r[1].r_addend = r[0].r_addend;
+ }
+ else
+ {
+ r[1].r_offset = r[0].r_offset + 8 + 8 * use_fake_dep;
+ r[1].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_DS);
+ r[1].r_addend = r[0].r_addend + 8 + 8 * plt_static_chain;
+ if (plt_static_chain)
+ {
+ r[2].r_offset = r[1].r_offset + 4;
+ r[2].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_DS);
+ r[2].r_addend = r[0].r_addend + 8;
+ }
}
}
}
if (ALWAYS_EMIT_R2SAVE
|| stub_entry->stub_type == ppc_stub_plt_call_r2save)
- bfd_put_32 (obfd, STD_R2_40R1, p), p += 4;
- bfd_put_32 (obfd, LD_R11_0R2 | PPC_LO (offset), p), p += 4;
- if (PPC_HA (offset + 8 + 8 * plt_static_chain) != PPC_HA (offset))
+ bfd_put_32 (obfd, STD_R2_0R1 + STK_TOC (htab), p), p += 4;
+ bfd_put_32 (obfd, LD_R12_0R2 | PPC_LO (offset), p), p += 4;
+ if (plt_load_toc
+ && PPC_HA (offset + 8 + 8 * plt_static_chain) != PPC_HA (offset))
{
bfd_put_32 (obfd, ADDI_R2_R2 | PPC_LO (offset), p), p += 4;
offset = 0;
}
- bfd_put_32 (obfd, MTCTR_R11, p), p += 4;
- if (use_fake_dep)
+ bfd_put_32 (obfd, MTCTR_R12, p), p += 4;
+ if (plt_load_toc)
{
- bfd_put_32 (obfd, XOR_R11_R11_R11, p), p += 4;
- bfd_put_32 (obfd, ADD_R2_R2_R11, p), p += 4;
+ if (use_fake_dep)
+ {
+ bfd_put_32 (obfd, XOR_R11_R12_R12, p), p += 4;
+ bfd_put_32 (obfd, ADD_R2_R2_R11, p), p += 4;
+ }
+ if (plt_static_chain)
+ bfd_put_32 (obfd, LD_R11_0R2 | PPC_LO (offset + 16), p), p += 4;
+ bfd_put_32 (obfd, LD_R2_0R2 | PPC_LO (offset + 8), p), p += 4;
}
- if (plt_static_chain)
- bfd_put_32 (obfd, LD_R11_0R2 | PPC_LO (offset + 16), p), p += 4;
- bfd_put_32 (obfd, LD_R2_0R2 | PPC_LO (offset + 8), p), p += 4;
}
- if (plt_thread_safe && !use_fake_dep)
+ if (plt_load_toc && plt_thread_safe && !use_fake_dep)
{
bfd_put_32 (obfd, CMPLDI_R2_0, p), p += 4;
bfd_put_32 (obfd, BNECTR_P4, p), p += 4;
#define ADD_R3_R12_R13 0x7c6c6a14
#define BEQLR 0x4d820020
#define MR_R3_R0 0x7c030378
-#define MFLR_R11 0x7d6802a6
#define STD_R11_0R1 0xf9610000
#define BCTRL 0x4e800421
#define LD_R11_0R1 0xe9610000
-#define LD_R2_0R1 0xe8410000
#define MTLR_R11 0x7d6803a6
static inline bfd_byte *
struct ppc_stub_hash_entry *stub_entry,
bfd_byte *p, bfd_vma offset, Elf_Internal_Rela *r)
{
- bfd *obfd = htab->stub_bfd;
+ bfd *obfd = htab->params->stub_bfd;
bfd_put_32 (obfd, LD_R11_0R3 + 0, p), p += 4;
bfd_put_32 (obfd, LD_R12_0R3 + 8, p), p += 4;
bfd_put_32 (obfd, BEQLR, p), p += 4;
bfd_put_32 (obfd, MR_R3_R0, p), p += 4;
bfd_put_32 (obfd, MFLR_R11, p), p += 4;
- bfd_put_32 (obfd, STD_R11_0R1 + 32, p), p += 4;
+ bfd_put_32 (obfd, STD_R11_0R1 + STK_LINKER (htab), p), p += 4;
if (r != NULL)
r[0].r_offset += 9 * 4;
p = build_plt_stub (htab, stub_entry, p, offset, r);
bfd_put_32 (obfd, BCTRL, p - 4);
- bfd_put_32 (obfd, LD_R11_0R1 + 32, p), p += 4;
- bfd_put_32 (obfd, LD_R2_0R1 + 40, p), p += 4;
+ bfd_put_32 (obfd, LD_R11_0R1 + STK_LINKER (htab), p), p += 4;
+ bfd_put_32 (obfd, LD_R2_0R1 + STK_TOC (htab), p), p += 4;
bfd_put_32 (obfd, MTLR_R11, p), p += 4;
bfd_put_32 (obfd, BLR, p), p += 4;
/* Support linking -R objects. Get the toc pointer from the
opd entry. */
char buf[8];
+ if (!htab->opd_abi)
+ return r2off;
asection *opd = stub_entry->h->elf.root.u.def.section;
bfd_vma opd_off = stub_entry->h->elf.root.u.def.value;
case ppc_stub_long_branch:
case ppc_stub_long_branch_r2off:
/* Branches are relative. This is where we are going to. */
- off = dest = (stub_entry->target_value
- + stub_entry->target_section->output_offset
- + stub_entry->target_section->output_section->vma);
+ dest = (stub_entry->target_value
+ + stub_entry->target_section->output_offset
+ + stub_entry->target_section->output_section->vma);
+ dest += PPC64_LOCAL_ENTRY_OFFSET (stub_entry->other);
+ off = dest;
/* And this is where we are coming from. */
off -= (stub_entry->stub_offset
htab->stub_error = TRUE;
return FALSE;
}
- bfd_put_32 (htab->stub_bfd, STD_R2_40R1, loc);
+ bfd_put_32 (htab->params->stub_bfd, STD_R2_0R1 + STK_TOC (htab), loc);
loc += 4;
size = 12;
if (PPC_HA (r2off) != 0)
{
size = 16;
- bfd_put_32 (htab->stub_bfd, ADDIS_R2_R2 | PPC_HA (r2off), loc);
+ bfd_put_32 (htab->params->stub_bfd,
+ ADDIS_R2_R2 | PPC_HA (r2off), loc);
loc += 4;
}
- bfd_put_32 (htab->stub_bfd, ADDI_R2_R2 | PPC_LO (r2off), loc);
+ bfd_put_32 (htab->params->stub_bfd, ADDI_R2_R2 | PPC_LO (r2off), loc);
loc += 4;
off -= size - 4;
}
- bfd_put_32 (htab->stub_bfd, B_DOT | (off & 0x3fffffc), loc);
+ bfd_put_32 (htab->params->stub_bfd, B_DOT | (off & 0x3fffffc), loc);
if (off + (1 << 25) >= (bfd_vma) (1 << 26))
{
unsigned long symndx;
struct ppc_link_hash_entry *h;
- hashes = elf_sym_hashes (htab->stub_bfd);
+ hashes = elf_sym_hashes (htab->params->stub_bfd);
if (hashes == NULL)
{
bfd_size_type hsize;
hsize = (htab->stub_globals + 1) * sizeof (*hashes);
- hashes = bfd_zalloc (htab->stub_bfd, hsize);
+ hashes = bfd_zalloc (htab->params->stub_bfd, hsize);
if (hashes == NULL)
return FALSE;
- elf_sym_hashes (htab->stub_bfd) = hashes;
+ elf_sym_hashes (htab->params->stub_bfd) = hashes;
htab->stub_globals = 1;
}
symndx = htab->stub_globals++;
dest = (stub_entry->target_value
+ stub_entry->target_section->output_offset
+ stub_entry->target_section->output_section->vma);
+ if (stub_entry->stub_type != ppc_stub_plt_branch_r2off)
+ dest += PPC64_LOCAL_ENTRY_OFFSET (stub_entry->other);
bfd_put_64 (htab->brlt->owner, dest,
htab->brlt->contents + br_entry->offset);
if (PPC_HA (off) != 0)
{
size = 16;
- bfd_put_32 (htab->stub_bfd, ADDIS_R12_R2 | PPC_HA (off), loc);
+ bfd_put_32 (htab->params->stub_bfd,
+ ADDIS_R12_R2 | PPC_HA (off), loc);
loc += 4;
- bfd_put_32 (htab->stub_bfd, LD_R11_0R12 | PPC_LO (off), loc);
+ bfd_put_32 (htab->params->stub_bfd,
+ LD_R12_0R12 | PPC_LO (off), loc);
}
else
{
size = 12;
- bfd_put_32 (htab->stub_bfd, LD_R11_0R2 | PPC_LO (off), loc);
+ bfd_put_32 (htab->params->stub_bfd,
+ LD_R12_0R2 | PPC_LO (off), loc);
}
}
else
{
bfd_vma r2off = get_r2off (info, stub_entry);
- if (r2off == 0)
+ if (r2off == 0 && htab->opd_abi)
{
htab->stub_error = TRUE;
return FALSE;
}
- bfd_put_32 (htab->stub_bfd, STD_R2_40R1, loc);
+ bfd_put_32 (htab->params->stub_bfd, STD_R2_0R1 + STK_TOC (htab), loc);
loc += 4;
- size = 20;
+ size = 16;
if (PPC_HA (off) != 0)
{
size += 4;
- bfd_put_32 (htab->stub_bfd, ADDIS_R12_R2 | PPC_HA (off), loc);
- loc += 4;
- bfd_put_32 (htab->stub_bfd, LD_R11_0R12 | PPC_LO (off), loc);
+ bfd_put_32 (htab->params->stub_bfd,
+ ADDIS_R12_R2 | PPC_HA (off), loc);
loc += 4;
+ bfd_put_32 (htab->params->stub_bfd,
+ LD_R12_0R12 | PPC_LO (off), loc);
}
else
+ bfd_put_32 (htab->params->stub_bfd, LD_R12_0R2 | PPC_LO (off), loc);
+
+ if (PPC_HA (r2off) != 0)
{
- bfd_put_32 (htab->stub_bfd, LD_R11_0R2 | PPC_LO (off), loc);
+ size += 4;
loc += 4;
+ bfd_put_32 (htab->params->stub_bfd,
+ ADDIS_R2_R2 | PPC_HA (r2off), loc);
}
-
- if (PPC_HA (r2off) != 0)
+ if (PPC_LO (r2off) != 0)
{
size += 4;
- bfd_put_32 (htab->stub_bfd, ADDIS_R2_R2 | PPC_HA (r2off), loc);
loc += 4;
+ bfd_put_32 (htab->params->stub_bfd,
+ ADDI_R2_R2 | PPC_LO (r2off), loc);
}
- bfd_put_32 (htab->stub_bfd, ADDI_R2_R2 | PPC_LO (r2off), loc);
}
loc += 4;
- bfd_put_32 (htab->stub_bfd, MTCTR_R11, loc);
+ bfd_put_32 (htab->params->stub_bfd, MTCTR_R12, loc);
loc += 4;
- bfd_put_32 (htab->stub_bfd, BCTR, loc);
+ bfd_put_32 (htab->params->stub_bfd, BCTR, loc);
break;
case ppc_stub_plt_call:
if (dest >= (bfd_vma) -2)
abort ();
- plt = htab->plt;
+ plt = htab->elf.splt;
if (!htab->elf.dynamic_sections_created
|| stub_entry->h == NULL
|| stub_entry->h->elf.dynindx == -1)
- plt = htab->iplt;
+ plt = htab->elf.iplt;
dest += plt->output_offset + plt->output_section->vma;
bfd_byte *rl;
rela.r_offset = dest;
- rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_IREL);
+ if (htab->opd_abi)
+ rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_IREL);
+ else
+ rela.r_info = ELF64_R_INFO (0, R_PPC64_IRELATIVE);
rela.r_addend = (stub_entry->target_value
+ stub_entry->target_section->output_offset
+ stub_entry->target_section->output_section->vma);
- rl = (htab->reliplt->contents
- + (htab->reliplt->reloc_count++
+ rl = (htab->elf.irelplt->contents
+ + (htab->elf.irelplt->reloc_count++
* sizeof (Elf64_External_Rela)));
bfd_elf64_swap_reloca_out (info->output_bfd, &rela, rl);
stub_entry->plt_ent->plt.offset |= 1;
return FALSE;
}
- if (htab->plt_stub_align != 0)
+ if (htab->params->plt_stub_align != 0)
{
unsigned pad = plt_stub_pad (htab, stub_entry, off);
if (info->emitrelocations)
{
r = get_relocs (stub_entry->stub_sec,
- (2
- + (PPC_HA (off) != 0)
- + (htab->plt_static_chain
- && PPC_HA (off + 16) == PPC_HA (off))));
+ ((PPC_HA (off) != 0)
+ + (htab->opd_abi
+ ? 2 + (htab->params->plt_static_chain
+ && PPC_HA (off + 16) == PPC_HA (off))
+ : 1)));
if (r == NULL)
return FALSE;
r[0].r_offset = loc - stub_entry->stub_sec->contents;
if (stub_entry->h != NULL
&& (stub_entry->h == htab->tls_get_addr_fd
|| stub_entry->h == htab->tls_get_addr)
- && !htab->no_tls_get_addr_opt)
+ && !htab->params->no_tls_get_addr_opt)
p = build_tls_get_addr_stub (htab, stub_entry, loc, off, r);
else
p = build_plt_stub (htab, stub_entry, loc, off, r);
stub_entry->stub_sec->size += size;
- if (htab->emit_stub_syms)
+ if (htab->params->emit_stub_syms)
{
struct elf_link_hash_entry *h;
size_t len1, len2;
off = stub_entry->plt_ent->plt.offset & ~(bfd_vma) 1;
if (off >= (bfd_vma) -2)
abort ();
- plt = htab->plt;
+ plt = htab->elf.splt;
if (!htab->elf.dynamic_sections_created
|| stub_entry->h == NULL
|| stub_entry->h->elf.dynindx == -1)
- plt = htab->iplt;
+ plt = htab->elf.iplt;
off += (plt->output_offset
+ plt->output_section->vma
- elf_gp (plt->output_section->owner)
- htab->stub_group[stub_entry->id_sec->id].toc_off);
size = plt_stub_size (htab, stub_entry, off);
- if (htab->plt_stub_align)
+ if (htab->params->plt_stub_align)
size += plt_stub_pad (htab, stub_entry, off);
if (info->emitrelocations)
{
stub_entry->stub_sec->reloc_count
- += (2
- + (PPC_HA (off) != 0)
- + (htab->plt_static_chain
- && PPC_HA (off + 16) == PPC_HA (off)));
+ += ((PPC_HA (off) != 0)
+ + (htab->opd_abi
+ ? 2 + (htab->params->plt_static_chain
+ && PPC_HA (off + 16) == PPC_HA (off))
+ : 1));
stub_entry->stub_sec->flags |= SEC_RELOC;
}
}
/* ppc_stub_long_branch or ppc_stub_plt_branch, or their r2off
variants. */
bfd_vma r2off = 0;
+ bfd_vma local_off = 0;
off = (stub_entry->target_value
+ stub_entry->target_section->output_offset
if (stub_entry->stub_type == ppc_stub_long_branch_r2off)
{
r2off = get_r2off (info, stub_entry);
- if (r2off == 0)
+ if (r2off == 0 && htab->opd_abi)
{
htab->stub_error = TRUE;
return FALSE;
off -= size - 4;
}
- /* If the branch offset if too big, use a ppc_stub_plt_branch. */
- if (off + (1 << 25) >= (bfd_vma) (1 << 26))
+ local_off = PPC64_LOCAL_ENTRY_OFFSET (stub_entry->other);
+
+ /* If the branch offset if too big, use a ppc_stub_plt_branch.
+ Do the same for -R objects without function descriptors. */
+ if (off + (1 << 25) >= (bfd_vma) (1 << 26) - local_off
+ || (stub_entry->stub_type == ppc_stub_long_branch_r2off
+ && r2off == 0))
{
struct ppc_branch_hash_entry *br_entry;
}
else
{
- size = 20;
+ size = 16;
if (PPC_HA (off) != 0)
size += 4;
if (PPC_HA (r2off) != 0)
size += 4;
+ if (PPC_LO (r2off) != 0)
+ size += 4;
}
}
else if (info->emitrelocations)
0 when no stubs will be needed, and 1 on success. */
int
-ppc64_elf_setup_section_lists
- (struct bfd_link_info *info,
- asection *(*add_stub_section) (const char *, asection *),
- void (*layout_sections_again) (void))
+ppc64_elf_setup_section_lists (struct bfd_link_info *info)
{
bfd *input_bfd;
int top_id, top_index, id;
if (htab == NULL)
return -1;
- /* Stash our params away. */
- htab->add_stub_section = add_stub_section;
- htab->layout_sections_again = layout_sections_again;
-
- if (htab->brlt == NULL)
- return 0;
/* Find the top input section id. */
for (input_bfd = info->input_bfds, top_id = 3;
input_bfd != NULL;
- input_bfd = input_bfd->link_next)
+ input_bfd = input_bfd->link.next)
{
for (section = input_bfd->sections;
section != NULL;
{
struct ppc_link_hash_table *htab = ppc_hash_table (info);
- elf_gp (info->output_bfd) = ppc64_elf_toc (info->output_bfd);
- htab->toc_curr = elf_gp (info->output_bfd);
+ htab->toc_curr = ppc64_elf_set_toc (info, info->output_bfd);
htab->toc_bfd = NULL;
htab->toc_first_sec = NULL;
}
elf_link_hash_traverse (&htab->elf, merge_global_got, info);
/* And tlsld_got. */
- for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
{
struct got_entry *ent, *ent2;
if (!ent->is_indirect
&& ent->got.offset != (bfd_vma) -1)
{
- for (ibfd2 = ibfd->link_next; ibfd2 != NULL; ibfd2 = ibfd2->link_next)
+ for (ibfd2 = ibfd->link.next; ibfd2 != NULL; ibfd2 = ibfd2->link.next)
{
if (!is_ppc64_elf (ibfd2))
continue;
}
/* Zap sizes of got sections. */
- htab->reliplt->rawsize = htab->reliplt->size;
- htab->reliplt->size -= htab->got_reli_size;
+ htab->elf.irelplt->rawsize = htab->elf.irelplt->size;
+ htab->elf.irelplt->size -= htab->got_reli_size;
htab->got_reli_size = 0;
- for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
{
asection *got, *relgot;
/* Now reallocate the got, local syms first. We don't need to
allocate section contents again since we never increase size. */
- for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
{
struct got_entry **lgot_ents;
struct got_entry **end_lgot_ents;
s->size += ent_size;
if ((*lgot_masks & PLT_IFUNC) != 0)
{
- htab->reliplt->size += rel_size;
+ htab->elf.irelplt->size += rel_size;
htab->got_reli_size += rel_size;
}
else if (info->shared)
elf_link_hash_traverse (&htab->elf, reallocate_got, info);
- for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
{
struct got_entry *ent;
}
}
- done_something = htab->reliplt->rawsize != htab->reliplt->size;
+ done_something = htab->elf.irelplt->rawsize != htab->elf.irelplt->size;
if (!done_something)
- for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
{
asection *got;
}
if (done_something)
- (*htab->layout_sections_again) ();
+ (*htab->params->layout_sections_again) ();
/* Set up for second pass over toc sections to recalculate elf_gp
on input sections. */
{
long adjust;
- adjust = opd->adjust[sym->st_value / 8];
+ adjust = opd->adjust[OPD_NDX (sym->st_value)];
if (adjust == -1)
/* Assume deleted functions won't ever be called. */
continue;
need a plt_branch stub. A plt_branch stub uses r2. */
else if (dest - (isec->output_offset
+ isec->output_section->vma
- + rel->r_offset) + (1 << 25) >= (2 << 25))
+ + rel->r_offset) + (1 << 25)
+ >= (2u << 25) - PPC64_LOCAL_ENTRY_OFFSET (h
+ ? h->other
+ : sym->st_other))
{
ret = 1;
break;
if (htab->multi_toc_needed)
{
- /* If a code section has a function that uses the TOC then we need
- to use the right TOC (obviously). Also, make sure that .opd gets
- the correct TOC value for R_PPC64_TOC relocs that don't have or
- can't find their function symbol (shouldn't ever happen now).
- Also specially treat .fixup for the linux kernel. .fixup
- contains branches, but only back to the function that hit an
- exception. */
- if (isec->has_toc_reloc
- || (isec->flags & SEC_CODE) == 0
- || strcmp (isec->name, ".fixup") == 0)
- {
- if (elf_gp (isec->owner) != 0)
- htab->toc_curr = elf_gp (isec->owner);
- }
- else
+ /* Analyse sections that aren't already flagged as needing a
+ valid toc pointer. Exclude .fixup for the linux kernel.
+ .fixup contains branches, but only back to the function that
+ hit an exception. */
+ if (!(isec->has_toc_reloc
+ || (isec->flags & SEC_CODE) == 0
+ || strcmp (isec->name, ".fixup") == 0
+ || isec->call_check_done))
{
- if (!isec->call_check_done
- && toc_adjusting_stub_needed (info, isec) < 0)
+ if (toc_adjusting_stub_needed (info, isec) < 0)
return FALSE;
- /* If we make a local call from this section, ie. a branch
- without a following nop, then we have no place to put a
- toc restoring insn. We must use the same toc group as
- the callee.
- Testing makes_toc_func_call actually tests for *any*
- calls to functions that need a good toc pointer. A more
- precise test would be better, as this one will set
- incorrect values for pasted .init/.fini fragments.
- (Fixed later in check_pasted_section.) */
- if (isec->makes_toc_func_call
- && elf_gp (isec->owner) != 0)
- htab->toc_curr = elf_gp (isec->owner);
- }
- }
-
- /* Functions that don't use the TOC can belong in any TOC group.
- Use the last TOC base. */
+ }
+ /* Make all sections use the TOC assigned for this object file.
+ This will be wrong for pasted sections; We fix that in
+ check_pasted_section(). */
+ if (elf_gp (isec->owner) != 0)
+ htab->toc_curr = elf_gp (isec->owner);
+ }
+
htab->stub_group[isec->id].toc_off = htab->toc_curr;
return TRUE;
}
bfd_boolean suppress_size_errors;
suppress_size_errors = FALSE;
- stub14_group_size = stub_group_size;
+ stub14_group_size = stub_group_size >> 10;
if (stub_group_size == 1)
{
/* Default values. */
65, /* RA reg. */
1, /* Augmentation size. */
DW_EH_PE_pcrel | DW_EH_PE_sdata4, /* FDE encoding. */
- DW_CFA_def_cfa, 1, 0 /* def_cfa: r1 offset 0. */
+ DW_CFA_def_cfa, 1, 0, /* def_cfa: r1 offset 0. */
+ 0, 0, 0, 0
};
/* Stripping output sections is normally done before dynamic section
instruction. */
bfd_boolean
-ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
- bfd_boolean plt_static_chain, int plt_thread_safe,
- int plt_stub_align)
+ppc64_elf_size_stubs (struct bfd_link_info *info)
{
bfd_size_type stub_group_size;
bfd_boolean stubs_always_before_branch;
if (htab == NULL)
return FALSE;
- htab->plt_static_chain = plt_static_chain;
- htab->plt_stub_align = plt_stub_align;
- if (plt_thread_safe == -1 && !info->executable)
- plt_thread_safe = 1;
- if (plt_thread_safe == -1)
+ if (htab->params->plt_thread_safe == -1 && !info->executable)
+ htab->params->plt_thread_safe = 1;
+ if (!htab->opd_abi)
+ htab->params->plt_thread_safe = 0;
+ else if (htab->params->plt_thread_safe == -1)
{
static const char *const thread_starter[] =
{
/* libanl */
"getaddrinfo_a",
/* libgomp */
+ "GOMP_parallel",
"GOMP_parallel_start",
+ "GOMP_parallel_loop_static",
"GOMP_parallel_loop_static_start",
+ "GOMP_parallel_loop_dynamic",
"GOMP_parallel_loop_dynamic_start",
+ "GOMP_parallel_loop_guided",
"GOMP_parallel_loop_guided_start",
+ "GOMP_parallel_loop_runtime",
"GOMP_parallel_loop_runtime_start",
+ "GOMP_parallel_sections",
"GOMP_parallel_sections_start",
+ /* libgo */
+ "__go_go",
};
unsigned i;
struct elf_link_hash_entry *h;
h = elf_link_hash_lookup (&htab->elf, thread_starter[i],
FALSE, FALSE, TRUE);
- plt_thread_safe = h != NULL && h->ref_regular;
- if (plt_thread_safe)
+ htab->params->plt_thread_safe = h != NULL && h->ref_regular;
+ if (htab->params->plt_thread_safe)
break;
}
}
- htab->plt_thread_safe = plt_thread_safe;
- htab->dot_toc_dot = ((struct ppc_link_hash_entry *)
- elf_link_hash_lookup (&htab->elf, ".TOC.",
- FALSE, FALSE, TRUE));
- stubs_always_before_branch = group_size < 0;
- if (group_size < 0)
- stub_group_size = -group_size;
+ stubs_always_before_branch = htab->params->group_size < 0;
+ if (htab->params->group_size < 0)
+ stub_group_size = -htab->params->group_size;
else
- stub_group_size = group_size;
+ stub_group_size = htab->params->group_size;
group_sections (htab, stub_group_size, stubs_always_before_branch);
for (input_bfd = info->input_bfds, bfd_indx = 0;
input_bfd != NULL;
- input_bfd = input_bfd->link_next, bfd_indx++)
+ input_bfd = input_bfd->link.next, bfd_indx++)
{
Elf_Internal_Shdr *symtab_hdr;
asection *section;
asection *sym_sec, *code_sec;
bfd_vma sym_value, code_value;
bfd_vma destination;
+ unsigned long local_off;
bfd_boolean ok_dest;
struct ppc_link_hash_entry *hash;
struct ppc_link_hash_entry *fdh;
}
destination = 0;
+ local_off = 0;
if (ok_dest)
{
sym_value += irela->r_addend;
destination = (sym_value
+ sym_sec->output_offset
+ sym_sec->output_section->vma);
+ local_off = PPC64_LOCAL_ENTRY_OFFSET (hash
+ ? hash->elf.other
+ : sym->st_other);
}
code_sec = sym_sec;
if (hash == NULL && opd->adjust != NULL)
{
- long adjust = opd->adjust[sym_value / 8];
+ long adjust = opd->adjust[OPD_NDX (sym_value)];
if (adjust == -1)
continue;
code_value += adjust;
/* Determine what (if any) linker stub is needed. */
plt_ent = NULL;
stub_type = ppc_type_of_stub (section, irela, &hash,
- &plt_ent, destination);
+ &plt_ent, destination,
+ local_off);
if (stub_type != ppc_stub_plt_call)
{
}
stub_entry->h = hash;
stub_entry->plt_ent = plt_ent;
- stub_entry->addend = irela->r_addend;
+ stub_entry->other = hash ? hash->elf.other : sym->st_other;
if (stub_entry->h != NULL)
htab->stub_globals += 1;
/* We may have added some stubs. Find out the new size of the
stub sections. */
- for (stub_sec = htab->stub_bfd->sections;
+ for (stub_sec = htab->params->stub_bfd->sections;
stub_sec != NULL;
stub_sec = stub_sec->next)
if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
{
size_t size = 0, align;
- for (stub_sec = htab->stub_bfd->sections;
+ for (stub_sec = htab->params->stub_bfd->sections;
stub_sec != NULL;
stub_sec = stub_sec->next)
if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
- size += 20;
+ size += 24;
if (htab->glink != NULL && htab->glink->size != 0)
size += 24;
if (size != 0)
htab->glink_eh_frame->size = size;
}
- if (htab->plt_stub_align != 0)
- for (stub_sec = htab->stub_bfd->sections;
+ if (htab->params->plt_stub_align != 0)
+ for (stub_sec = htab->params->stub_bfd->sections;
stub_sec != NULL;
stub_sec = stub_sec->next)
if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
- stub_sec->size = ((stub_sec->size + (1 << htab->plt_stub_align) - 1)
- & (-1 << htab->plt_stub_align));
+ stub_sec->size = ((stub_sec->size
+ + (1 << htab->params->plt_stub_align) - 1)
+ & (-1 << htab->params->plt_stub_align));
- for (stub_sec = htab->stub_bfd->sections;
+ for (stub_sec = htab->params->stub_bfd->sections;
stub_sec != NULL;
stub_sec = stub_sec->next)
if ((stub_sec->flags & SEC_LINKER_CREATED) == 0
break;
/* Ask the linker to do its stuff. */
- (*htab->layout_sections_again) ();
+ (*htab->params->layout_sections_again) ();
+ }
+
+ if (htab->glink_eh_frame != NULL
+ && htab->glink_eh_frame->size != 0)
+ {
+ bfd_vma val;
+ bfd_byte *p, *last_fde;
+ size_t last_fde_len, size, align, pad;
+ asection *stub_sec;
+
+ p = bfd_zalloc (htab->glink_eh_frame->owner, htab->glink_eh_frame->size);
+ if (p == NULL)
+ return FALSE;
+ htab->glink_eh_frame->contents = p;
+ last_fde = p;
+
+ memcpy (p, glink_eh_frame_cie, sizeof (glink_eh_frame_cie));
+ /* CIE length (rewrite in case little-endian). */
+ last_fde_len = sizeof (glink_eh_frame_cie) - 4;
+ bfd_put_32 (htab->elf.dynobj, last_fde_len, p);
+ p += sizeof (glink_eh_frame_cie);
+
+ for (stub_sec = htab->params->stub_bfd->sections;
+ stub_sec != NULL;
+ stub_sec = stub_sec->next)
+ if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
+ {
+ last_fde = p;
+ last_fde_len = 20;
+ /* FDE length. */
+ bfd_put_32 (htab->elf.dynobj, 20, p);
+ p += 4;
+ /* CIE pointer. */
+ val = p - htab->glink_eh_frame->contents;
+ bfd_put_32 (htab->elf.dynobj, val, p);
+ p += 4;
+ /* Offset to stub section, written later. */
+ p += 4;
+ /* stub section size. */
+ bfd_put_32 (htab->elf.dynobj, stub_sec->size, p);
+ p += 4;
+ /* Augmentation. */
+ p += 1;
+ /* Pad. */
+ p += 7;
+ }
+ if (htab->glink != NULL && htab->glink->size != 0)
+ {
+ last_fde = p;
+ last_fde_len = 20;
+ /* FDE length. */
+ bfd_put_32 (htab->elf.dynobj, 20, p);
+ p += 4;
+ /* CIE pointer. */
+ val = p - htab->glink_eh_frame->contents;
+ bfd_put_32 (htab->elf.dynobj, val, p);
+ p += 4;
+ /* Offset to .glink, written later. */
+ p += 4;
+ /* .glink size. */
+ bfd_put_32 (htab->elf.dynobj, htab->glink->size - 8, p);
+ p += 4;
+ /* Augmentation. */
+ p += 1;
+
+ *p++ = DW_CFA_advance_loc + 1;
+ *p++ = DW_CFA_register;
+ *p++ = 65;
+ *p++ = 12;
+ *p++ = DW_CFA_advance_loc + 4;
+ *p++ = DW_CFA_restore_extended;
+ *p++ = 65;
+ }
+ /* Subsume any padding into the last FDE if user .eh_frame
+ sections are aligned more than glink_eh_frame. Otherwise any
+ zero padding will be seen as a terminator. */
+ size = p - htab->glink_eh_frame->contents;
+ align = 1;
+ align <<= htab->glink_eh_frame->output_section->alignment_power;
+ align -= 1;
+ pad = ((size + align) & ~align) - size;
+ htab->glink_eh_frame->size = size + pad;
+ bfd_put_32 (htab->elf.dynobj, last_fde_len + pad, last_fde);
}
maybe_strip_output (info, htab->brlt);
move, we'll be called again. Provide a value for TOCstart. */
bfd_vma
-ppc64_elf_toc (bfd *obfd)
+ppc64_elf_set_toc (struct bfd_link_info *info, bfd *obfd)
{
asection *s;
bfd_vma TOCstart;
if (s != NULL)
TOCstart = s->output_section->vma + s->output_offset;
+ _bfd_set_gp_value (obfd, TOCstart);
+
+ if (info != NULL && s != NULL)
+ {
+ struct ppc_link_hash_table *htab = ppc_hash_table (info);
+
+ if (htab != NULL)
+ {
+ if (htab->elf.hgot != NULL)
+ {
+ htab->elf.hgot->root.u.def.value = TOC_BASE_OFF;
+ htab->elf.hgot->root.u.def.section = s;
+ }
+ }
+ else
+ {
+ struct bfd_link_hash_entry *bh = NULL;
+ _bfd_generic_link_add_one_symbol (info, obfd, ".TOC.", BSF_GLOBAL,
+ s, TOC_BASE_OFF, NULL, FALSE,
+ FALSE, &bh);
+ }
+ }
return TOCstart;
}
+/* Called via elf_link_hash_traverse from ppc64_elf_build_stubs to
+ write out any global entry stubs. */
+
+static bfd_boolean
+build_global_entry_stubs (struct elf_link_hash_entry *h, void *inf)
+{
+ struct bfd_link_info *info;
+ struct ppc_link_hash_table *htab;
+ struct plt_entry *pent;
+ asection *s;
+
+ if (h->root.type == bfd_link_hash_indirect)
+ return TRUE;
+
+ if (!h->pointer_equality_needed)
+ return TRUE;
+
+ if (h->def_regular)
+ return TRUE;
+
+ info = inf;
+ htab = ppc_hash_table (info);
+ if (htab == NULL)
+ return FALSE;
+
+ s = htab->glink;
+ for (pent = h->plt.plist; pent != NULL; pent = pent->next)
+ if (pent->plt.offset != (bfd_vma) -1
+ && pent->addend == 0)
+ {
+ bfd_byte *p;
+ asection *plt;
+ bfd_vma off;
+
+ p = s->contents + h->root.u.def.value;
+ plt = htab->elf.splt;
+ if (!htab->elf.dynamic_sections_created
+ || h->dynindx == -1)
+ plt = htab->elf.iplt;
+ off = pent->plt.offset + plt->output_offset + plt->output_section->vma;
+ off -= h->root.u.def.value + s->output_offset + s->output_section->vma;
+
+ if (off + 0x80008000 > 0xffffffff || (off & 3) != 0)
+ {
+ info->callbacks->einfo
+ (_("%P: linkage table error against `%T'\n"),
+ h->root.root.string);
+ bfd_set_error (bfd_error_bad_value);
+ htab->stub_error = TRUE;
+ }
+
+ htab->stub_count[ppc_stub_global_entry - 1] += 1;
+ if (htab->params->emit_stub_syms)
+ {
+ size_t len = strlen (h->root.root.string);
+ char *name = bfd_malloc (sizeof "12345678.global_entry." + len);
+
+ if (name == NULL)
+ return FALSE;
+
+ sprintf (name, "%08x.global_entry.%s", s->id, h->root.root.string);
+ h = elf_link_hash_lookup (&htab->elf, name, TRUE, FALSE, FALSE);
+ if (h == NULL)
+ return FALSE;
+ if (h->root.type == bfd_link_hash_new)
+ {
+ h->root.type = bfd_link_hash_defined;
+ h->root.u.def.section = s;
+ h->root.u.def.value = p - s->contents;
+ h->ref_regular = 1;
+ h->def_regular = 1;
+ h->ref_regular_nonweak = 1;
+ h->forced_local = 1;
+ h->non_elf = 0;
+ }
+ }
+
+ if (PPC_HA (off) != 0)
+ {
+ bfd_put_32 (s->owner, ADDIS_R12_R12 | PPC_HA (off), p);
+ p += 4;
+ }
+ bfd_put_32 (s->owner, LD_R12_0R12 | PPC_LO (off), p);
+ p += 4;
+ bfd_put_32 (s->owner, MTCTR_R12, p);
+ p += 4;
+ bfd_put_32 (s->owner, BCTR, p);
+ break;
+ }
+ return TRUE;
+}
+
/* 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. */
bfd_boolean
-ppc64_elf_build_stubs (bfd_boolean emit_stub_syms,
- struct bfd_link_info *info,
+ppc64_elf_build_stubs (struct bfd_link_info *info,
char **stats)
{
struct ppc_link_hash_table *htab = ppc_hash_table (info);
if (htab == NULL)
return FALSE;
- htab->emit_stub_syms = emit_stub_syms;
-
/* Allocate memory to hold the linker stubs. */
- for (stub_sec = htab->stub_bfd->sections;
+ for (stub_sec = htab->params->stub_bfd->sections;
stub_sec != NULL;
stub_sec = stub_sec->next)
if ((stub_sec->flags & SEC_LINKER_CREATED) == 0
&& stub_sec->size != 0)
{
- stub_sec->contents = bfd_zalloc (htab->stub_bfd, stub_sec->size);
+ stub_sec->contents = bfd_zalloc (htab->params->stub_bfd, stub_sec->size);
if (stub_sec->contents == NULL)
return FALSE;
/* We want to check that built size is the same as calculated
bfd_vma plt0;
/* Build the .glink plt call stub. */
- if (htab->emit_stub_syms)
+ if (htab->params->emit_stub_syms)
{
struct elf_link_hash_entry *h;
h = elf_link_hash_lookup (&htab->elf, "__glink_PLTresolve",
h->non_elf = 0;
}
}
- plt0 = htab->plt->output_section->vma + htab->plt->output_offset - 16;
+ plt0 = (htab->elf.splt->output_section->vma
+ + htab->elf.splt->output_offset
+ - 16);
if (info->emitrelocations)
{
Elf_Internal_Rela *r = get_relocs (htab->glink, 1);
plt0 -= htab->glink->output_section->vma + htab->glink->output_offset;
bfd_put_64 (htab->glink->owner, plt0, p);
p += 8;
- bfd_put_32 (htab->glink->owner, MFLR_R12, p);
- p += 4;
- bfd_put_32 (htab->glink->owner, BCL_20_31, p);
- p += 4;
- bfd_put_32 (htab->glink->owner, MFLR_R11, p);
- p += 4;
- bfd_put_32 (htab->glink->owner, LD_R2_M16R11, p);
- p += 4;
- bfd_put_32 (htab->glink->owner, MTLR_R12, p);
- p += 4;
- bfd_put_32 (htab->glink->owner, ADD_R12_R2_R11, p);
- p += 4;
- bfd_put_32 (htab->glink->owner, LD_R11_0R12, p);
- p += 4;
- bfd_put_32 (htab->glink->owner, LD_R2_0R12 | 8, p);
- p += 4;
- bfd_put_32 (htab->glink->owner, MTCTR_R11, p);
- p += 4;
- bfd_put_32 (htab->glink->owner, LD_R11_0R12 | 16, p);
- p += 4;
+ if (htab->opd_abi)
+ {
+ bfd_put_32 (htab->glink->owner, MFLR_R12, p);
+ p += 4;
+ bfd_put_32 (htab->glink->owner, BCL_20_31, p);
+ p += 4;
+ bfd_put_32 (htab->glink->owner, MFLR_R11, p);
+ p += 4;
+ bfd_put_32 (htab->glink->owner, LD_R2_0R11 | (-16 & 0xfffc), p);
+ p += 4;
+ bfd_put_32 (htab->glink->owner, MTLR_R12, p);
+ p += 4;
+ bfd_put_32 (htab->glink->owner, ADD_R11_R2_R11, p);
+ p += 4;
+ bfd_put_32 (htab->glink->owner, LD_R12_0R11, p);
+ p += 4;
+ bfd_put_32 (htab->glink->owner, LD_R2_0R11 | 8, p);
+ p += 4;
+ bfd_put_32 (htab->glink->owner, MTCTR_R12, p);
+ p += 4;
+ bfd_put_32 (htab->glink->owner, LD_R11_0R11 | 16, p);
+ p += 4;
+ }
+ else
+ {
+ bfd_put_32 (htab->glink->owner, MFLR_R0, p);
+ p += 4;
+ bfd_put_32 (htab->glink->owner, BCL_20_31, p);
+ p += 4;
+ bfd_put_32 (htab->glink->owner, MFLR_R11, p);
+ p += 4;
+ bfd_put_32 (htab->glink->owner, LD_R2_0R11 | (-16 & 0xfffc), p);
+ p += 4;
+ bfd_put_32 (htab->glink->owner, MTLR_R0, p);
+ p += 4;
+ bfd_put_32 (htab->glink->owner, SUB_R12_R12_R11, p);
+ p += 4;
+ bfd_put_32 (htab->glink->owner, ADD_R11_R2_R11, p);
+ p += 4;
+ bfd_put_32 (htab->glink->owner, ADDI_R0_R12 | (-48 & 0xffff), p);
+ p += 4;
+ bfd_put_32 (htab->glink->owner, LD_R12_0R11, p);
+ p += 4;
+ bfd_put_32 (htab->glink->owner, SRDI_R0_R0_2, p);
+ p += 4;
+ bfd_put_32 (htab->glink->owner, MTCTR_R12, p);
+ p += 4;
+ bfd_put_32 (htab->glink->owner, LD_R11_0R11 | 8, p);
+ p += 4;
+ }
bfd_put_32 (htab->glink->owner, BCTR, p);
p += 4;
while (p - htab->glink->contents < GLINK_CALL_STUB_SIZE)
/* Build the .glink lazy link call stubs. */
indx = 0;
- while (p < htab->glink->contents + htab->glink->size)
+ while (p < htab->glink->contents + htab->glink->rawsize)
{
- if (indx < 0x8000)
+ if (htab->opd_abi)
{
- bfd_put_32 (htab->glink->owner, LI_R0_0 | indx, p);
- p += 4;
- }
- else
- {
- bfd_put_32 (htab->glink->owner, LIS_R0_0 | PPC_HI (indx), p);
- p += 4;
- bfd_put_32 (htab->glink->owner, ORI_R0_R0_0 | PPC_LO (indx), p);
- p += 4;
+ if (indx < 0x8000)
+ {
+ bfd_put_32 (htab->glink->owner, LI_R0_0 | indx, p);
+ p += 4;
+ }
+ else
+ {
+ bfd_put_32 (htab->glink->owner, LIS_R0_0 | PPC_HI (indx), p);
+ p += 4;
+ bfd_put_32 (htab->glink->owner, ORI_R0_R0_0 | PPC_LO (indx),
+ p);
+ p += 4;
+ }
}
bfd_put_32 (htab->glink->owner,
B_DOT | ((htab->glink->contents - p + 8) & 0x3fffffc), p);
indx++;
p += 4;
}
- htab->glink->rawsize = p - htab->glink->contents;
+
+ /* Build .glink global entry stubs. */
+ if (htab->glink->size > htab->glink->rawsize)
+ elf_link_hash_traverse (&htab->elf, build_global_entry_stubs, info);
}
- if (htab->brlt->size != 0)
+ if (htab->brlt != NULL && htab->brlt->size != 0)
{
htab->brlt->contents = bfd_zalloc (htab->brlt->owner,
htab->brlt->size);
return FALSE;
}
- if (htab->glink_eh_frame != NULL
- && htab->glink_eh_frame->size != 0)
- {
- bfd_vma val;
- bfd_byte *last_fde;
- size_t last_fde_len, size, align, pad;
-
- p = bfd_zalloc (htab->glink_eh_frame->owner, htab->glink_eh_frame->size);
- if (p == NULL)
- return FALSE;
- htab->glink_eh_frame->contents = p;
- last_fde = p;
-
- htab->glink_eh_frame->rawsize = htab->glink_eh_frame->size;
-
- memcpy (p, glink_eh_frame_cie, sizeof (glink_eh_frame_cie));
- /* CIE length (rewrite in case little-endian). */
- last_fde_len = sizeof (glink_eh_frame_cie) - 4;
- bfd_put_32 (htab->elf.dynobj, last_fde_len, p);
- p += sizeof (glink_eh_frame_cie);
-
- for (stub_sec = htab->stub_bfd->sections;
- stub_sec != NULL;
- stub_sec = stub_sec->next)
- if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
- {
- last_fde = p;
- last_fde_len = 16;
- /* FDE length. */
- bfd_put_32 (htab->elf.dynobj, 16, p);
- p += 4;
- /* CIE pointer. */
- val = p - htab->glink_eh_frame->contents;
- bfd_put_32 (htab->elf.dynobj, val, p);
- p += 4;
- /* Offset to stub section. */
- val = (stub_sec->output_section->vma
- + stub_sec->output_offset);
- val -= (htab->glink_eh_frame->output_section->vma
- + htab->glink_eh_frame->output_offset);
- val -= p - htab->glink_eh_frame->contents;
- if (val + 0x80000000 > 0xffffffff)
- {
- info->callbacks->einfo
- (_("%P: %s offset too large for .eh_frame sdata4 encoding"),
- stub_sec->name);
- return FALSE;
- }
- bfd_put_32 (htab->elf.dynobj, val, p);
- p += 4;
- /* stub section size. */
- bfd_put_32 (htab->elf.dynobj, stub_sec->rawsize, p);
- p += 4;
- /* Augmentation. */
- p += 1;
- /* Pad. */
- p += 3;
- }
- if (htab->glink != NULL && htab->glink->size != 0)
- {
- last_fde = p;
- last_fde_len = 20;
- /* FDE length. */
- bfd_put_32 (htab->elf.dynobj, 20, p);
- p += 4;
- /* CIE pointer. */
- val = p - htab->glink_eh_frame->contents;
- bfd_put_32 (htab->elf.dynobj, val, p);
- p += 4;
- /* Offset to .glink. */
- val = (htab->glink->output_section->vma
- + htab->glink->output_offset
- + 8);
- val -= (htab->glink_eh_frame->output_section->vma
- + htab->glink_eh_frame->output_offset);
- val -= p - htab->glink_eh_frame->contents;
- if (val + 0x80000000 > 0xffffffff)
- {
- info->callbacks->einfo
- (_("%P: %s offset too large for .eh_frame sdata4 encoding"),
- htab->glink->name);
- return FALSE;
- }
- bfd_put_32 (htab->elf.dynobj, val, p);
- p += 4;
- /* .glink size. */
- bfd_put_32 (htab->elf.dynobj, htab->glink->rawsize - 8, p);
- p += 4;
- /* Augmentation. */
- p += 1;
-
- *p++ = DW_CFA_advance_loc + 1;
- *p++ = DW_CFA_register;
- *p++ = 65;
- *p++ = 12;
- *p++ = DW_CFA_advance_loc + 4;
- *p++ = DW_CFA_restore_extended;
- *p++ = 65;
- }
- /* Subsume any padding into the last FDE if user .eh_frame
- sections are aligned more than glink_eh_frame. Otherwise any
- zero padding will be seen as a terminator. */
- size = p - htab->glink_eh_frame->contents;
- align = 1;
- align <<= htab->glink_eh_frame->output_section->alignment_power;
- align -= 1;
- pad = ((size + align) & ~align) - size;
- htab->glink_eh_frame->size = size + pad;
- bfd_put_32 (htab->elf.dynobj, last_fde_len + pad, last_fde);
- }
-
/* Build the stubs as directed by the stub hash table. */
bfd_hash_traverse (&htab->stub_hash_table, ppc_build_one_stub, info);
if (htab->relbrlt != NULL)
htab->relbrlt->reloc_count = 0;
- if (htab->plt_stub_align != 0)
- for (stub_sec = htab->stub_bfd->sections;
+ if (htab->params->plt_stub_align != 0)
+ for (stub_sec = htab->params->stub_bfd->sections;
stub_sec != NULL;
stub_sec = stub_sec->next)
if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
- stub_sec->size = ((stub_sec->size + (1 << htab->plt_stub_align) - 1)
- & (-1 << htab->plt_stub_align));
+ stub_sec->size = ((stub_sec->size
+ + (1 << htab->params->plt_stub_align) - 1)
+ & (-1 << htab->params->plt_stub_align));
- for (stub_sec = htab->stub_bfd->sections;
+ for (stub_sec = htab->params->stub_bfd->sections;
stub_sec != NULL;
stub_sec = stub_sec->next)
if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
break;
}
+ /* Note that the glink_eh_frame check here is not only testing that
+ the generated size matched the calculated size but also that
+ bfd_elf_discard_info didn't make any changes to the section. */
if (stub_sec != NULL
- || htab->glink->rawsize != htab->glink->size
|| (htab->glink_eh_frame != NULL
&& htab->glink_eh_frame->rawsize != htab->glink_eh_frame->size))
{
" long branch %lu\n"
" long toc adj %lu\n"
" plt call %lu\n"
- " plt call toc %lu"),
+ " plt call toc %lu\n"
+ " global entry %lu"),
stub_sec_count,
stub_sec_count == 1 ? "" : "s",
htab->stub_count[ppc_stub_long_branch - 1],
htab->stub_count[ppc_stub_plt_branch - 1],
htab->stub_count[ppc_stub_plt_branch_r2off - 1],
htab->stub_count[ppc_stub_plt_call - 1],
- htab->stub_count[ppc_stub_plt_call_r2save - 1]);
+ htab->stub_count[ppc_stub_plt_call_r2save - 1],
+ htab->stub_count[ppc_stub_global_entry - 1]);
}
return TRUE;
}
return FALSE;
/* Don't relocate stub sections. */
- if (input_section->owner == htab->stub_bfd)
+ if (input_section->owner == htab->params->stub_bfd)
return TRUE;
BFD_ASSERT (is_ppc64_elf (input_bfd));
bfd_vma max_br_offset;
bfd_vma from;
const Elf_Internal_Rela orig_rel = *rel;
+ reloc_howto_type *howto;
+ struct reloc_howto_struct alt_howto;
r_type = ELF64_R_TYPE (rel->r_info);
r_symndx = ELF64_R_SYM (rel->r_info);
opd = get_opd_info (sec);
if (opd != NULL && opd->adjust != NULL)
{
- long adjust = opd->adjust[(sym->st_value + rel->r_addend) / 8];
+ long adjust = opd->adjust[OPD_NDX (sym->st_value
+ + rel->r_addend)];
if (adjust == -1)
relocation = 0;
else
}
else
{
+ bfd_boolean ignored;
+
RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
r_symndx, symtab_hdr, sym_hashes,
h_elf, sec, relocation,
- unresolved_reloc, warned);
+ unresolved_reloc, warned, ignored);
sym_name = h_elf->root.root.string;
sym_type = h_elf->type;
if (sec != NULL
}
}
}
- if (h_elf == &htab->dot_toc_dot->elf)
- {
- relocation = (TOCstart
- + htab->stub_group[input_section->id].toc_off);
- sec = bfd_abs_section_ptr;
- unresolved_reloc = FALSE;
- }
}
h = (struct ppc_link_hash_entry *) h_elf;
if (info->relocatable)
continue;
+ if (h != NULL && &h->elf == htab->elf.hgot)
+ {
+ relocation = (TOCstart
+ + htab->stub_group[input_section->id].toc_off);
+ sec = bfd_abs_section_ptr;
+ unresolved_reloc = FALSE;
+ }
+
/* TLS optimizations. Replace instruction sequences and relocs
based on information we collected in tls_optimize. We edit
RELOCS so that --emit-relocs will output something sensible
htab->tls_get_addr,
htab->tls_get_addr_fd))
offset = rel[1].r_offset;
+ /* We read the low GOT_TLS (or TOC16) insn because we
+ need to keep the destination reg. It may be
+ something other than the usual r3, and moved to r3
+ before the call by intervening code. */
+ insn1 = bfd_get_32 (output_bfd,
+ contents + rel->r_offset - d_offset);
if ((tls_mask & tls_gd) != 0)
{
/* IE */
- insn1 = bfd_get_32 (output_bfd,
- contents + rel->r_offset - d_offset);
- insn1 &= (1 << 26) - (1 << 2);
+ insn1 &= (0x1f << 21) | (0x1f << 16);
insn1 |= 58 << 26; /* ld */
insn2 = 0x7c636a14; /* add 3,3,13 */
if (offset != (bfd_vma) -1)
else
{
/* LE */
- insn1 = 0x3c6d0000; /* addis 3,13,0 */
+ insn1 &= 0x1f << 21;
+ insn1 |= 0x3c0d0000; /* addis r,13,0 */
insn2 = 0x38630000; /* addi 3,3,0 */
if (tls_gd == 0)
{
rel->r_info = ELF64_R_INFO (r_symndx, r_type);
}
break;
+
+ case R_PPC64_REL16_HA:
+ /* If we are generating a non-PIC executable, edit
+ . 0: addis 2,12,.TOC.-0b@ha
+ . addi 2,2,.TOC.-0b@l
+ used by ELFv2 global entry points to set up r2, to
+ . lis 2,.TOC.@ha
+ . addi 2,2,.TOC.@l
+ if .TOC. is in range. */
+ if (!info->shared
+ && !info->traditional_format
+ && h != NULL && &h->elf == htab->elf.hgot
+ && rel + 1 < relend
+ && rel[1].r_info == ELF64_R_INFO (r_symndx, R_PPC64_REL16_LO)
+ && rel[1].r_offset == rel->r_offset + 4
+ && rel[1].r_addend == rel->r_addend + 4
+ && relocation + 0x80008000 <= 0xffffffff)
+ {
+ unsigned int insn1, insn2;
+ bfd_vma offset = rel->r_offset - d_offset;
+ insn1 = bfd_get_32 (output_bfd, contents + offset);
+ insn2 = bfd_get_32 (output_bfd, contents + offset + 4);
+ if ((insn1 & 0xffff0000) == 0x3c4c0000 /* addis 2,12 */
+ && (insn2 & 0xffff0000) == 0x38420000 /* addi 2,2 */)
+ {
+ r_type = R_PPC64_ADDR16_HA;
+ rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+ rel->r_addend -= d_offset;
+ rel[1].r_info = ELF64_R_INFO (r_symndx, R_PPC64_ADDR16_LO);
+ rel[1].r_addend -= d_offset + 4;
+ bfd_put_32 (output_bfd, 0x3c400000, contents + offset);
+ }
+ }
+ break;
}
/* Handle other relocations that tweak non-addend part of insn. */
insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
if (insn == NOP
|| insn == CROR_151515 || insn == CROR_313131)
- bfd_put_32 (input_bfd, STD_R2_40R1,
+ bfd_put_32 (input_bfd,
+ STD_R2_0R1 + STK_TOC (htab),
contents + rel->r_offset);
}
break;
{
bfd_boolean can_plt_call = FALSE;
+ /* All of these stubs will modify r2, so there must be a
+ branch and link followed by a nop. The nop is
+ replaced by an insn to restore r2. */
if (rel->r_offset + 8 <= input_section->size)
{
- unsigned long nop;
- nop = bfd_get_32 (input_bfd, contents + rel->r_offset + 4);
- if (nop == NOP
- || nop == CROR_151515 || nop == CROR_313131)
+ unsigned long br;
+
+ br = bfd_get_32 (input_bfd,
+ contents + rel->r_offset);
+ if ((br & 1) != 0)
{
- if (h != NULL
- && (h == htab->tls_get_addr_fd
- || h == htab->tls_get_addr)
- && !htab->no_tls_get_addr_opt)
+ unsigned long nop;
+
+ nop = bfd_get_32 (input_bfd,
+ contents + rel->r_offset + 4);
+ if (nop == NOP
+ || nop == CROR_151515 || nop == CROR_313131)
{
- /* Special stub used, leave nop alone. */
+ if (h != NULL
+ && (h == htab->tls_get_addr_fd
+ || h == htab->tls_get_addr)
+ && !htab->params->no_tls_get_addr_opt)
+ {
+ /* Special stub used, leave nop alone. */
+ }
+ else
+ bfd_put_32 (input_bfd,
+ LD_R2_0R1 + STK_TOC (htab),
+ contents + rel->r_offset + 4);
+ can_plt_call = TRUE;
}
- else
- bfd_put_32 (input_bfd, LD_R2_40R1,
- contents + rel->r_offset + 4);
- can_plt_call = TRUE;
}
}
- if (!can_plt_call)
+ if (!can_plt_call && h != NULL)
{
- if (stub_entry->stub_type == ppc_stub_plt_call
- || stub_entry->stub_type == ppc_stub_plt_call_r2save)
- {
- /* If this is a plain branch rather than a branch
- and link, don't require a nop. However, don't
- allow tail calls in a shared library as they
- will result in r2 being corrupted. */
- unsigned long br;
- br = bfd_get_32 (input_bfd, contents + rel->r_offset);
- if (info->executable && (br & 1) == 0)
- can_plt_call = TRUE;
- else
- stub_entry = NULL;
- }
- else if (h != NULL
- && strcmp (h->elf.root.root.string,
- ".__libc_start_main") == 0)
+ const char *name = h->elf.root.root.string;
+
+ if (*name == '.')
+ ++name;
+
+ if (strncmp (name, "__libc_start_main", 17) == 0
+ && (name[17] == 0 || name[17] == '@'))
{
- /* Allow crt1 branch to go via a toc adjusting stub. */
+ /* Allow crt1 branch to go via a toc adjusting
+ stub. Other calls that never return could do
+ the same, if we could detect such. */
can_plt_call = TRUE;
}
- else
+ }
+
+ if (!can_plt_call)
+ {
+ /* g++ as of 20130507 emits self-calls without a
+ following nop. This is arguably wrong since we
+ have conflicting information. On the one hand a
+ global symbol and on the other a local call
+ sequence, but don't error for this special case.
+ It isn't possible to cheaply verify we have
+ exactly such a call. Allow all calls to the same
+ section. */
+ asection *code_sec = sec;
+
+ if (get_opd_info (sec) != NULL)
{
- info->callbacks->einfo
- (_("%P: %H: call to `%T' lacks nop, can't restore toc; "
- "recompile with -fPIC"),
- input_bfd, input_section, rel->r_offset, sym_name);
+ bfd_vma off = (relocation + addend
+ - sec->output_section->vma
+ - sec->output_offset);
- bfd_set_error (bfd_error_bad_value);
- ret = FALSE;
+ opd_entry_value (sec, off, &code_sec, NULL, FALSE);
}
+ if (code_sec == input_section)
+ can_plt_call = TRUE;
+ }
+
+ if (!can_plt_call)
+ {
+ if (stub_entry->stub_type == ppc_stub_plt_call
+ || stub_entry->stub_type == ppc_stub_plt_call_r2save)
+ info->callbacks->einfo
+ (_("%P: %H: call to `%T' lacks nop, can't restore toc; "
+ "recompile with -fPIC\n"),
+ input_bfd, input_section, rel->r_offset, sym_name);
+ else
+ info->callbacks->einfo
+ (_("%P: %H: call to `%T' lacks nop, can't restore toc; "
+ "(-mcmodel=small toc adjust stub)\n"),
+ input_bfd, input_section, rel->r_offset, sym_name);
+
+ bfd_set_error (bfd_error_bad_value);
+ ret = FALSE;
}
if (can_plt_call
+ input_section->output_offset
+ input_section->output_section->vma);
+ relocation += PPC64_LOCAL_ENTRY_OFFSET (fdh
+ ? fdh->elf.other
+ : sym->st_other);
+
if (stub_entry != NULL
&& (stub_entry->stub_type == ppc_stub_long_branch
|| stub_entry->stub_type == ppc_stub_plt_branch)
if (!WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared,
&h->elf)
|| (info->shared
- && SYMBOL_CALLS_LOCAL (info, &h->elf)))
+ && SYMBOL_REFERENCES_LOCAL (info, &h->elf)))
/* This is actually a static link, or it is a
-Bsymbolic link and the symbol is defined
locally, or the symbol was forced to be local
? h->elf.type == STT_GNU_IFUNC
: ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC);
if (ifunc)
- relgot = htab->reliplt;
+ relgot = htab->elf.irelplt;
else if ((info->shared || indx != 0)
&& (h == NULL
|| (tls_type == (TLS_TLS | TLS_LD)
symbol. This happens when statically linking PIC code,
or when using -Bsymbolic. Go find a match if there is a
PLT entry. */
- if (htab->plt != NULL)
+ if (htab->elf.splt != NULL)
{
struct plt_entry *ent;
for (ent = h->elf.plt.plist; ent != NULL; ent = ent->next)
- if (ent->addend == orig_rel.r_addend
- && ent->plt.offset != (bfd_vma) -1)
+ if (ent->plt.offset != (bfd_vma) -1
+ && ent->addend == orig_rel.r_addend)
{
- relocation = (htab->plt->output_section->vma
- + htab->plt->output_offset
+ relocation = (htab->elf.splt->output_section->vma
+ + htab->elf.splt->output_offset
+ ent->plt.offset);
unresolved_reloc = FALSE;
+ break;
}
}
break;
case R_PPC64_TPREL16_HA:
case R_PPC64_TPREL16_DS:
case R_PPC64_TPREL16_LO_DS:
+ case R_PPC64_TPREL16_HIGH:
+ case R_PPC64_TPREL16_HIGHA:
case R_PPC64_TPREL16_HIGHER:
case R_PPC64_TPREL16_HIGHERA:
case R_PPC64_TPREL16_HIGHEST:
case R_PPC64_DTPREL16_HA:
case R_PPC64_DTPREL16_DS:
case R_PPC64_DTPREL16_LO_DS:
+ case R_PPC64_DTPREL16_HIGH:
+ case R_PPC64_DTPREL16_HIGHA:
case R_PPC64_DTPREL16_HIGHER:
case R_PPC64_DTPREL16_HIGHERA:
case R_PPC64_DTPREL16_HIGHEST:
addend -= htab->elf.tls_sec->vma + DTP_OFFSET;
break;
+ case R_PPC64_ADDR64_LOCAL:
+ addend += PPC64_LOCAL_ENTRY_OFFSET (h != NULL
+ ? h->elf.other
+ : sym->st_other);
+ break;
+
case R_PPC64_DTPMOD64:
relocation = 1;
addend = 0;
case R_PPC64_ADDR16_DS:
case R_PPC64_ADDR16_HA:
case R_PPC64_ADDR16_HI:
+ case R_PPC64_ADDR16_HIGH:
+ case R_PPC64_ADDR16_HIGHA:
case R_PPC64_ADDR16_HIGHER:
case R_PPC64_ADDR16_HIGHERA:
case R_PPC64_ADDR16_HIGHEST:
if (skip)
memset (&outrel, 0, sizeof outrel);
- else if (!SYMBOL_CALLS_LOCAL (info, &h->elf)
+ else if (!SYMBOL_REFERENCES_LOCAL (info, &h->elf)
&& !is_opd
&& r_type != R_PPC64_TOC)
{
if (h != NULL
? h->elf.type == STT_GNU_IFUNC
: ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
- sreloc = htab->reliplt;
+ sreloc = htab->elf.irelplt;
if (sreloc == NULL)
abort ();
}
/* Do any further special processing. */
+ howto = ppc64_elf_howto_table[(int) r_type];
switch (r_type)
{
default:
break;
- case R_PPC64_ADDR16_HA:
case R_PPC64_REL16_HA:
+ case R_PPC64_ADDR16_HA:
+ case R_PPC64_ADDR16_HIGHA:
case R_PPC64_ADDR16_HIGHERA:
case R_PPC64_ADDR16_HIGHESTA:
case R_PPC64_TOC16_HA:
case R_PPC64_SECTOFF_HA:
case R_PPC64_TPREL16_HA:
- case R_PPC64_DTPREL16_HA:
- case R_PPC64_TPREL16_HIGHER:
+ case R_PPC64_TPREL16_HIGHA:
case R_PPC64_TPREL16_HIGHERA:
- case R_PPC64_TPREL16_HIGHEST:
case R_PPC64_TPREL16_HIGHESTA:
- case R_PPC64_DTPREL16_HIGHER:
+ case R_PPC64_DTPREL16_HA:
+ case R_PPC64_DTPREL16_HIGHA:
case R_PPC64_DTPREL16_HIGHERA:
- case R_PPC64_DTPREL16_HIGHEST:
case R_PPC64_DTPREL16_HIGHESTA:
/* It's just possible that this symbol is a weak symbol
that's not actually defined anywhere. In that case,
info->callbacks->einfo
(_("%P: %H: error: %s not a multiple of %u\n"),
input_bfd, input_section, rel->r_offset,
- ppc64_elf_howto_table[r_type]->name,
+ howto->name,
mask + 1);
bfd_set_error (bfd_error_bad_value);
ret = FALSE;
info->callbacks->einfo
(_("%P: %H: unresolvable %s against `%T'\n"),
input_bfd, input_section, rel->r_offset,
- ppc64_elf_howto_table[(int) r_type]->name,
+ howto->name,
h->elf.root.root.string);
ret = FALSE;
}
- r = _bfd_final_link_relocate (ppc64_elf_howto_table[(int) r_type],
- input_bfd,
- input_section,
- contents,
- rel->r_offset,
- relocation,
- addend);
+ /* 16-bit fields in insns mostly have signed values, but a
+ few insns have 16-bit unsigned values. Really, we should
+ have different reloc types. */
+ if (howto->complain_on_overflow != complain_overflow_dont
+ && howto->dst_mask == 0xffff
+ && (input_section->flags & SEC_CODE) != 0)
+ {
+ enum complain_overflow complain = complain_overflow_signed;
+
+ insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3));
+ if ((insn & (0x3f << 26)) == 10u << 26 /* cmpli */)
+ complain = complain_overflow_bitfield;
+ else if (howto->rightshift == 0
+ ? ((insn & (0x3f << 26)) == 28u << 26 /* andi */
+ || (insn & (0x3f << 26)) == 24u << 26 /* ori */
+ || (insn & (0x3f << 26)) == 26u << 26 /* xori */)
+ : ((insn & (0x3f << 26)) == 29u << 26 /* andis */
+ || (insn & (0x3f << 26)) == 25u << 26 /* oris */
+ || (insn & (0x3f << 26)) == 27u << 26 /* xoris */))
+ complain = complain_overflow_unsigned;
+ if (howto->complain_on_overflow != complain)
+ {
+ alt_howto = *howto;
+ alt_howto.complain_on_overflow = complain;
+ howto = &alt_howto;
+ }
+ }
+
+ r = _bfd_final_link_relocate (howto, input_bfd, input_section, contents,
+ rel->r_offset, relocation, addend);
if (r != bfd_reloc_ok)
{
char *more_info = NULL;
- const char *reloc_name = ppc64_elf_howto_table[r_type]->name;
+ const char *reloc_name = howto->name;
if (reloc_dest != DEST_NORMAL)
{
continue;
if (h != NULL
&& h->elf.root.type == bfd_link_hash_undefweak
- && ppc64_elf_howto_table[r_type]->pc_relative)
+ && howto->pc_relative)
{
/* Assume this is a call protected by other code that
detects the symbol is undefined. If this is the case,
bfd_size_type amt;
amt = input_section->reloc_count * sizeof (Elf_Internal_Rela);
rel = bfd_alloc (input_bfd, amt);
- BFD_ASSERT (ppc64_elf_tdata (input_bfd)->opd_relocs == NULL);
- ppc64_elf_tdata (input_bfd)->opd_relocs = rel;
+ BFD_ASSERT (ppc64_elf_tdata (input_bfd)->opd.relocs == NULL);
+ ppc64_elf_tdata (input_bfd)->opd.relocs = rel;
if (rel == NULL)
return FALSE;
memcpy (rel, relocs, amt);
if (!info->relocatable)
value -= input_sec->output_section->vma;
- adjust = opd->adjust[value / 8];
+ adjust = opd->adjust[OPD_NDX (value)];
if (adjust == -1)
return 2;
&& h->def_regular
&& (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak));
- rela.r_offset = (htab->iplt->output_section->vma
- + htab->iplt->output_offset
+ rela.r_offset = (htab->elf.iplt->output_section->vma
+ + htab->elf.iplt->output_offset
+ ent->plt.offset);
- rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_IREL);
+ if (htab->opd_abi)
+ rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_IREL);
+ else
+ rela.r_info = ELF64_R_INFO (0, R_PPC64_IRELATIVE);
rela.r_addend = (h->root.u.def.value
+ h->root.u.def.section->output_offset
+ h->root.u.def.section->output_section->vma
+ ent->addend);
- loc = (htab->reliplt->contents
- + (htab->reliplt->reloc_count++
+ loc = (htab->elf.irelplt->contents
+ + (htab->elf.irelplt->reloc_count++
* sizeof (Elf64_External_Rela)));
}
else
{
- rela.r_offset = (htab->plt->output_section->vma
- + htab->plt->output_offset
+ rela.r_offset = (htab->elf.splt->output_section->vma
+ + htab->elf.splt->output_offset
+ ent->plt.offset);
rela.r_info = ELF64_R_INFO (h->dynindx, R_PPC64_JMP_SLOT);
rela.r_addend = ent->addend;
- loc = (htab->relplt->contents
- + ((ent->plt.offset - PLT_INITIAL_ENTRY_SIZE)
- / (PLT_ENTRY_SIZE / sizeof (Elf64_External_Rela))));
+ loc = (htab->elf.srelplt->contents
+ + ((ent->plt.offset - PLT_INITIAL_ENTRY_SIZE (htab))
+ / PLT_ENTRY_SIZE (htab) * sizeof (Elf64_External_Rela)));
}
bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
+
+ if (!htab->opd_abi)
+ {
+ if (!h->def_regular)
+ {
+ /* Mark the symbol as undefined, rather than as
+ defined in glink. Leave the value if there were
+ any relocations where pointer equality matters
+ (this is a clue for the dynamic linker, to make
+ function pointer comparisons work between an
+ application and shared library), otherwise set it
+ to zero. */
+ sym->st_shndx = SHN_UNDEF;
+ if (!h->pointer_equality_needed)
+ sym->st_value = 0;
+ else if (!h->ref_regular_nonweak)
+ {
+ /* This breaks function pointer comparisons, but
+ that is better than breaking tests for a NULL
+ function pointer. */
+ sym->st_value = 0;
+ }
+ }
+ }
}
if (h->needs_copy)
enum elf_ppc64_reloc_type r_type;
struct ppc_link_hash_table *htab = ppc_hash_table (info);
- if (rel_sec == htab->reliplt)
+ if (rel_sec == htab->elf.irelplt)
return reloc_class_ifunc;
r_type = ELF64_R_TYPE (rela->r_info);
{
Elf64_External_Dyn *dyncon, *dynconend;
- if (sdyn == NULL || htab->got == NULL)
+ if (sdyn == NULL || htab->elf.sgot == NULL)
abort ();
dyncon = (Elf64_External_Dyn *) sdyn->contents;
of glink rather than the first entry point, which is
what ld.so needs, and now have a bigger stub to
support automatic multiple TOCs. */
- dyn.d_un.d_ptr += GLINK_CALL_STUB_SIZE - 32;
+ dyn.d_un.d_ptr += GLINK_CALL_STUB_SIZE - 8 * 4;
break;
case DT_PPC64_OPD:
dyn.d_un.d_ptr = s->vma;
break;
+ case DT_PPC64_OPT:
+ if (htab->do_multi_toc && htab->multi_toc_needed)
+ dyn.d_un.d_val |= PPC64_OPT_MULTI_TOC;
+ break;
+
case DT_PPC64_OPDSZ:
s = bfd_get_section_by_name (output_bfd, ".opd");
if (s == NULL)
break;
case DT_PLTGOT:
- s = htab->plt;
+ s = htab->elf.splt;
dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
break;
case DT_JMPREL:
- s = htab->relplt;
+ s = htab->elf.srelplt;
dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
break;
case DT_PLTRELSZ:
- dyn.d_un.d_val = htab->relplt->size;
+ dyn.d_un.d_val = htab->elf.srelplt->size;
break;
case DT_RELASZ:
/* Don't count procedure linkage table relocs in the
overall reloc count. */
- s = htab->relplt;
+ s = htab->elf.srelplt;
if (s == NULL)
continue;
dyn.d_un.d_val -= s->size;
/* We may not be using the standard ELF linker script.
If .rela.plt is the first .rela section, we adjust
DT_RELA to not include it. */
- s = htab->relplt;
+ s = htab->elf.srelplt;
if (s == NULL)
continue;
if (dyn.d_un.d_ptr != s->output_section->vma + s->output_offset)
}
}
- if (htab->got != NULL && htab->got->size != 0)
+ if (htab->elf.sgot != NULL && htab->elf.sgot->size != 0)
{
/* Fill in the first entry in the global offset table.
We use it to hold the link-time TOCbase. */
bfd_put_64 (output_bfd,
elf_gp (output_bfd) + TOC_BASE_OFF,
- htab->got->contents);
+ htab->elf.sgot->contents);
/* Set .got entry size. */
- elf_section_data (htab->got->output_section)->this_hdr.sh_entsize = 8;
+ elf_section_data (htab->elf.sgot->output_section)->this_hdr.sh_entsize = 8;
}
- if (htab->plt != NULL && htab->plt->size != 0)
+ if (htab->elf.splt != NULL && htab->elf.splt->size != 0)
{
/* Set .plt entry size. */
- elf_section_data (htab->plt->output_section)->this_hdr.sh_entsize
- = PLT_ENTRY_SIZE;
+ elf_section_data (htab->elf.splt->output_section)->this_hdr.sh_entsize
+ = PLT_ENTRY_SIZE (htab);
}
/* brlt is SEC_LINKER_CREATED, so we need to write out relocs for
NULL))
return FALSE;
-
if (htab->glink_eh_frame != NULL
- && htab->glink_eh_frame->sec_info_type == SEC_INFO_TYPE_EH_FRAME
- && !_bfd_elf_write_section_eh_frame (output_bfd, info,
- htab->glink_eh_frame,
- htab->glink_eh_frame->contents))
- return FALSE;
+ && htab->glink_eh_frame->size != 0)
+ {
+ bfd_vma val;
+ bfd_byte *p;
+ asection *stub_sec;
+
+ p = htab->glink_eh_frame->contents + sizeof (glink_eh_frame_cie);
+ for (stub_sec = htab->params->stub_bfd->sections;
+ stub_sec != NULL;
+ stub_sec = stub_sec->next)
+ if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
+ {
+ /* FDE length. */
+ p += 4;
+ /* CIE pointer. */
+ p += 4;
+ /* Offset to stub section. */
+ val = (stub_sec->output_section->vma
+ + stub_sec->output_offset);
+ val -= (htab->glink_eh_frame->output_section->vma
+ + htab->glink_eh_frame->output_offset
+ + (p - htab->glink_eh_frame->contents));
+ if (val + 0x80000000 > 0xffffffff)
+ {
+ info->callbacks->einfo
+ (_("%P: %s offset too large for .eh_frame sdata4 encoding"),
+ stub_sec->name);
+ return FALSE;
+ }
+ bfd_put_32 (dynobj, val, p);
+ p += 4;
+ /* stub section size. */
+ p += 4;
+ /* Augmentation. */
+ p += 1;
+ /* Pad. */
+ p += 7;
+ }
+ if (htab->glink != NULL && htab->glink->size != 0)
+ {
+ /* FDE length. */
+ p += 4;
+ /* CIE pointer. */
+ p += 4;
+ /* Offset to .glink. */
+ val = (htab->glink->output_section->vma
+ + htab->glink->output_offset
+ + 8);
+ val -= (htab->glink_eh_frame->output_section->vma
+ + htab->glink_eh_frame->output_offset
+ + (p - htab->glink_eh_frame->contents));
+ if (val + 0x80000000 > 0xffffffff)
+ {
+ info->callbacks->einfo
+ (_("%P: %s offset too large for .eh_frame sdata4 encoding"),
+ htab->glink->name);
+ return FALSE;
+ }
+ bfd_put_32 (dynobj, val, p);
+ p += 4;
+ /* .glink size. */
+ p += 4;
+ /* Augmentation. */
+ p += 1;
+ /* Ops. */
+ p += 7;
+ }
+
+ if (htab->glink_eh_frame->sec_info_type == SEC_INFO_TYPE_EH_FRAME
+ && !_bfd_elf_write_section_eh_frame (output_bfd, info,
+ htab->glink_eh_frame,
+ htab->glink_eh_frame->contents))
+ return FALSE;
+ }
/* We need to handle writing out multiple GOT sections ourselves,
since we didn't add them to DYNOBJ. We know dynobj is the first
bfd. */
- while ((dynobj = dynobj->link_next) != NULL)
+ while ((dynobj = dynobj->link.next) != NULL)
{
asection *s;
#undef TARGET_LITTLE_NAME
#undef TARGET_BIG_SYM
-#define TARGET_BIG_SYM bfd_elf64_powerpc_freebsd_vec
+#define TARGET_BIG_SYM powerpc_elf64_fbsd_vec
#undef TARGET_BIG_NAME
#define TARGET_BIG_NAME "elf64-powerpc-freebsd"