/* PowerPC64-specific support for 64-bit ELF.
- Copyright (C) 1999-2017 Free Software Foundation, Inc.
+ Copyright (C) 1999-2018 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.
#define ELF_TARGET_ID PPC64_ELF_DATA
#define ELF_MACHINE_CODE EM_PPC64
#define ELF_MAXPAGESIZE 0x10000
-#define ELF_COMMONPAGESIZE 0x10000
+#define ELF_COMMONPAGESIZE 0x1000
+#define ELF_RELROPAGESIZE ELF_MAXPAGESIZE
#define elf_info_to_howto ppc64_elf_info_to_howto
#define elf_backend_want_got_sym 0
#define elf_backend_notice_as_needed ppc64_elf_notice_as_needed
#define elf_backend_archive_symbol_lookup ppc64_elf_archive_symbol_lookup
#define elf_backend_check_relocs ppc64_elf_check_relocs
+#define elf_backend_relocs_compatible _bfd_elf_relocs_compatible
#define elf_backend_gc_keep ppc64_elf_gc_keep
#define elf_backend_gc_mark_dynamic_ref ppc64_elf_gc_mark_dynamic_ref
#define elf_backend_gc_mark_hook ppc64_elf_gc_mark_hook
-#define elf_backend_gc_sweep_hook ppc64_elf_gc_sweep_hook
#define elf_backend_adjust_dynamic_symbol ppc64_elf_adjust_dynamic_symbol
#define elf_backend_hide_symbol ppc64_elf_hide_symbol
#define elf_backend_maybe_function_sym ppc64_elf_maybe_function_sym
/* The size in bytes of an entry in the procedure linkage table. */
#define PLT_ENTRY_SIZE(htab) (htab->opd_abi ? 24 : 8)
+#define LOCAL_PLT_ENTRY_SIZE(htab) (htab->opd_abi ? 16 : 8)
/* The initial size of the plt reserved for the dynamic linker. */
#define PLT_INITIAL_ENTRY_SIZE(htab) (htab->opd_abi ? 24 : 16)
#define LD_R11_0R11 0xe96b0000 /* ld %r11,xxx+16@l(%r11) */
#define BCTR 0x4e800420 /* bctr */
-#define ADDI_R11_R11 0x396b0000 /* addi %r11,%r11,off@l */
-#define ADDIS_R2_R2 0x3c420000 /* addis %r2,%r2,off@ha */
-#define ADDI_R2_R2 0x38420000 /* addi %r2,%r2,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_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 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_0R2 0xe8420000 /* ld %r2,xxx+0(%r2) */
-#define LD_R2_0R1 0xe8410000 /* ld %r2,0(%r1) */
-#define LD_R2_0R12 0xe84c0000 /* ld %r2,0(%r12) */
-#define ADD_R2_R2_R12 0x7c426214 /* add %r2,%r2,%r12 */
+#define LD_R2_0R1 0xe8410000 /* ld %r2,0(%r1) */
+#define LD_R2_0R12 0xe84c0000 /* ld %r2,0(%r12) */
+#define ADD_R2_R2_R12 0x7c426214 /* add %r2,%r2,%r12 */
-#define LIS_R2 0x3c400000 /* lis %r2,xxx@ha */
+#define LIS_R2 0x3c400000 /* lis %r2,xxx@ha */
#define ADDIS_R2_R12 0x3c4c0000 /* addis %r2,%r12,xxx@ha */
#define ADDIS_R12_R2 0x3d820000 /* addis %r12,%r2,xxx@ha */
#define ADDIS_R12_R12 0x3d8c0000 /* addis %r12,%r12,xxx@ha */
-#define LD_R12_0R12 0xe98c0000 /* ld %r12,xxx@l(%r12) */
+#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)
+/* __glink_PLTresolve stub instructions. We enter with the index in R0. */
+#define GLINK_PLTRESOLVE_SIZE(htab) \
+ (8u + (htab->opd_abi ? 11 * 4 : 14 * 4))
/* 0: */
/* .quad plt0-1f */
/* __glink: */
/* Relocation HOWTO's. */
static reloc_howto_type *ppc64_elf_howto_table[(int) R_PPC64_max];
-static reloc_howto_type ppc64_elf_howto_raw[] = {
+static reloc_howto_type ppc64_elf_howto_raw[] =
+{
/* This reloc does nothing. */
HOWTO (R_PPC64_NONE, /* type */
0, /* rightshift */
0, /* dst_mask */
FALSE), /* pcrel_offset */
+ /* Marker reloc for optimizing r2 save in prologue rather than on
+ each plt call stub. */
HOWTO (R_PPC64_TOCSAVE,
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
0, /* dst_mask */
FALSE), /* pcrel_offset */
+ /* Marker relocs on inline plt call instructions. */
+ HOWTO (R_PPC64_PLTSEQ,
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_PPC64_PLTSEQ", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
+ HOWTO (R_PPC64_PLTCALL,
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_PPC64_PLTCALL", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
/* Computes the load module index of the load module that contains the
definition of its TLS sym. */
HOWTO (R_PPC64_DTPMOD64,
}
static reloc_howto_type *
-ppc64_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
+ppc64_elf_reloc_type_lookup (bfd *abfd,
bfd_reloc_code_real_type code)
{
enum elf_ppc64_reloc_type r = R_PPC64_NONE;
switch (code)
{
default:
+ /* xgettext:c-format */
+ _bfd_error_handler (_("%pB: unsupported relocation type %#x"), abfd, (int) code);
+ bfd_set_error (bfd_error_bad_value);
return NULL;
case BFD_RELOC_NONE: r = R_PPC64_NONE;
&& strcasecmp (ppc64_elf_howto_raw[i].name, r_name) == 0)
return &ppc64_elf_howto_raw[i];
+
return NULL;
}
/* Set the howto pointer for a PowerPC ELF reloc. */
-static void
+static bfd_boolean
ppc64_elf_info_to_howto (bfd *abfd, arelent *cache_ptr,
Elf_Internal_Rela *dst)
{
if (type >= ARRAY_SIZE (ppc64_elf_howto_table))
{
/* xgettext:c-format */
- _bfd_error_handler (_("%B: invalid relocation type %d"),
- abfd, (int) type);
- type = R_PPC64_NONE;
+ _bfd_error_handler (_("%pB: unsupported relocation type %#x"),
+ abfd, type);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
}
cache_ptr->howto = ppc64_elf_howto_table[type];
+ if (cache_ptr->howto == NULL || cache_ptr->howto->name == NULL)
+ {
+ /* xgettext:c-format */
+ _bfd_error_handler (_("%pB: unsupported relocation type %#x"),
+ abfd, type);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+
+ return TRUE;
}
/* Handle the R_PPC64_ADDR16_HA and similar relocs. */
case NT_PRPSINFO:
{
- char data[136];
+ char data[136] ATTRIBUTE_NONSTRING;
va_list ap;
va_start (ap, note_type);
memset (data, 0, sizeof (data));
strncpy (data + 40, va_arg (ap, const char *), 16);
+ DIAGNOSTIC_PUSH;
+ /* GCC 8.1 warns about 80 equals destination size with
+ -Wstringop-truncation:
+ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85643
+ */
+#if GCC_VERSION == 8001
+ DIAGNOSTIC_IGNORE_STRINGOP_TRUNCATION;
+#endif
strncpy (data + 56, va_arg (ap, const char *), 80);
+ DIAGNOSTIC_POP;
va_end (ap);
return elfcore_write_note (abfd, buf, bufsiz,
"CORE", note_type, data, sizeof (data));
static const struct bfd_elf_special_section ppc64_elf_special_sections[]=
{
- { STRING_COMMA_LEN (".plt"), 0, SHT_NOBITS, 0 },
- { STRING_COMMA_LEN (".sbss"), -2, SHT_NOBITS, SHF_ALLOC + SHF_WRITE },
+ { STRING_COMMA_LEN (".plt"), 0, SHT_NOBITS, 0 },
+ { STRING_COMMA_LEN (".sbss"), -2, SHT_NOBITS, SHF_ALLOC + SHF_WRITE },
{ STRING_COMMA_LEN (".sdata"), -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
- { STRING_COMMA_LEN (".toc"), 0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
- { STRING_COMMA_LEN (".toc1"), 0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
+ { STRING_COMMA_LEN (".toc"), 0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
+ { STRING_COMMA_LEN (".toc1"), 0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
{ STRING_COMMA_LEN (".tocbss"), 0, SHT_NOBITS, SHF_ALLOC + SHF_WRITE },
- { NULL, 0, 0, 0, 0 }
+ { NULL, 0, 0, 0, 0 }
};
enum _ppc64_sec_type {
/* Flag set when small branches are detected. Used to
select suitable defaults for the stub group size. */
unsigned int has_14bit_branch:1;
+
+ /* Flag set when PLTCALL relocs are detected. */
+ unsigned int has_pltcall:1;
};
#define ppc64_elf_section_data(sec) \
asymbol **ret)
{
asymbol *s;
- long i;
- long count;
+ size_t i, j, count;
char *names;
- long symcount, codesecsym, codesecsymend, secsymend, opdsymend;
+ size_t symcount, codesecsym, codesecsymend, secsymend, opdsymend;
asection *opd = NULL;
bfd_boolean relocatable = (abfd->flags & (EXEC_P | DYNAMIC)) == 0;
asymbol **syms;
return 0;
}
- symcount = static_count;
- if (!relocatable)
- symcount += dyn_count;
- if (symcount == 0)
- return 0;
-
- syms = bfd_malloc ((symcount + 1) * sizeof (*syms));
- if (syms == NULL)
- return -1;
-
- if (!relocatable && static_count != 0 && dyn_count != 0)
+ syms = NULL;
+ codesecsym = 0;
+ codesecsymend = 0;
+ secsymend = 0;
+ opdsymend = 0;
+ symcount = 0;
+ if (opd != NULL)
{
- /* Use both symbol tables. */
- memcpy (syms, static_syms, static_count * sizeof (*syms));
- memcpy (syms + static_count, dyn_syms, (dyn_count + 1) * sizeof (*syms));
- }
- else if (!relocatable && static_count == 0)
- memcpy (syms, dyn_syms, (symcount + 1) * sizeof (*syms));
- else
- memcpy (syms, static_syms, (symcount + 1) * sizeof (*syms));
+ symcount = static_count;
+ if (!relocatable)
+ symcount += dyn_count;
+ if (symcount == 0)
+ return 0;
+
+ syms = bfd_malloc ((symcount + 1) * sizeof (*syms));
+ if (syms == NULL)
+ return -1;
- synthetic_relocatable = relocatable;
- synthetic_opd = opd;
- qsort (syms, symcount, sizeof (*syms), compare_symbols);
+ if (!relocatable && static_count != 0 && dyn_count != 0)
+ {
+ /* Use both symbol tables. */
+ memcpy (syms, static_syms, static_count * sizeof (*syms));
+ memcpy (syms + static_count, dyn_syms,
+ (dyn_count + 1) * sizeof (*syms));
+ }
+ else if (!relocatable && static_count == 0)
+ memcpy (syms, dyn_syms, (symcount + 1) * sizeof (*syms));
+ else
+ memcpy (syms, static_syms, (symcount + 1) * sizeof (*syms));
- if (!relocatable && symcount > 1)
- {
- long j;
- /* Trim duplicate syms, since we may have merged the normal and
- dynamic symbols. Actually, we only care about syms that have
- different values, so trim any with the same value. */
- for (i = 1, j = 1; i < symcount; ++i)
- if (syms[i - 1]->value + syms[i - 1]->section->vma
- != syms[i]->value + syms[i]->section->vma)
+ /* Trim uninteresting symbols. Interesting symbols are section,
+ function, and notype symbols. */
+ for (i = 0, j = 0; i < symcount; ++i)
+ if ((syms[i]->flags & (BSF_FILE | BSF_OBJECT | BSF_THREAD_LOCAL
+ | BSF_RELC | BSF_SRELC)) == 0)
syms[j++] = syms[i];
symcount = j;
- }
- i = 0;
- /* Note that here and in compare_symbols we can't compare opd and
- sym->section directly. With separate debug info files, the
- symbols will be extracted from the debug file while abfd passed
- to this function is the real binary. */
- if (opd != NULL && strcmp (syms[i]->section->name, ".opd") == 0)
- ++i;
- codesecsym = i;
+ synthetic_relocatable = relocatable;
+ synthetic_opd = opd;
+ qsort (syms, symcount, sizeof (*syms), compare_symbols);
- for (; i < symcount; ++i)
- if (((syms[i]->section->flags & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL))
- != (SEC_CODE | SEC_ALLOC))
- || (syms[i]->flags & BSF_SECTION_SYM) == 0)
- break;
- codesecsymend = i;
+ if (!relocatable && symcount > 1)
+ {
+ /* Trim duplicate syms, since we may have merged the normal
+ and dynamic symbols. Actually, we only care about syms
+ that have different values, so trim any with the same
+ value. Don't consider ifunc and ifunc resolver symbols
+ duplicates however, because GDB wants to know whether a
+ text symbol is an ifunc resolver. */
+ for (i = 1, j = 1; i < symcount; ++i)
+ {
+ const asymbol *s0 = syms[i - 1];
+ const asymbol *s1 = syms[i];
+
+ if ((s0->value + s0->section->vma
+ != s1->value + s1->section->vma)
+ || ((s0->flags & BSF_GNU_INDIRECT_FUNCTION)
+ != (s1->flags & BSF_GNU_INDIRECT_FUNCTION)))
+ syms[j++] = syms[i];
+ }
+ symcount = j;
+ }
- for (; i < symcount; ++i)
- if ((syms[i]->flags & BSF_SECTION_SYM) == 0)
- break;
- secsymend = i;
+ i = 0;
+ /* Note that here and in compare_symbols we can't compare opd and
+ sym->section directly. With separate debug info files, the
+ symbols will be extracted from the debug file while abfd passed
+ to this function is the real binary. */
+ if (strcmp (syms[i]->section->name, ".opd") == 0)
+ ++i;
+ codesecsym = i;
+
+ for (; i < symcount; ++i)
+ if (((syms[i]->section->flags & (SEC_CODE | SEC_ALLOC
+ | SEC_THREAD_LOCAL))
+ != (SEC_CODE | SEC_ALLOC))
+ || (syms[i]->flags & BSF_SECTION_SYM) == 0)
+ break;
+ codesecsymend = i;
- if (opd != NULL)
- for (; i < symcount; ++i)
- if (strcmp (syms[i]->section->name, ".opd") != 0)
- break;
- opdsymend = i;
+ for (; i < symcount; ++i)
+ if ((syms[i]->flags & BSF_SECTION_SYM) == 0)
+ break;
+ secsymend = i;
- for (; i < symcount; ++i)
- if ((syms[i]->section->flags & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL))
- != (SEC_CODE | SEC_ALLOC))
- break;
- symcount = i;
+ for (; i < symcount; ++i)
+ if (strcmp (syms[i]->section->name, ".opd") != 0)
+ break;
+ opdsymend = i;
+ for (; i < symcount; ++i)
+ if ((syms[i]->section->flags & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL))
+ != (SEC_CODE | SEC_ALLOC))
+ break;
+ symcount = i;
+ }
count = 0;
if (relocatable)
bfd_boolean (*slurp_relocs) (bfd *, asection *, asymbol **, bfd_boolean);
arelent *r;
size_t size;
- long relcount;
+ size_t relcount;
if (opdsymend == secsymend)
goto done;
bfd_boolean (*slurp_relocs) (bfd *, asection *, asymbol **, bfd_boolean);
bfd_byte *contents = NULL;
size_t size;
- long plt_count = 0;
+ size_t plt_count = 0;
bfd_vma glink_vma = 0, resolv_vma = 0;
asection *dynamic, *glink = NULL, *relplt = NULL;
arelent *p;
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 + GLINK_CALL_STUB_SIZE - 8 * 4;
+ /* The first glink stub starts at DT_PPC64_GLINK plus 32.
+ See comment in ppc64_elf_finish_dynamic_sections. */
+ glink_vma = dyn.d_un.d_val + 8 * 4;
/* The .glink section usually does not survive the final
link; search for the section (usually .text) where the
glink stubs now reside. */
ent = bfd_get_64 (abfd, contents + syms[i]->value);
if (!sym_exists_at (syms, opdsymend, symcount, -1, ent))
{
- long lo, hi;
+ size_t lo, hi;
size_t len;
asection *sec = abfd->sections;
hi = codesecsymend;
while (lo < hi)
{
- long mid = (lo + hi) >> 1;
+ size_t mid = (lo + hi) >> 1;
if (syms[mid]->section->vma < ent)
lo = mid + 1;
else if (syms[mid]->section->vma > ent)
}
}
-/* Whether an undefined weak symbol should resolve to its link-time
- value, even in PIC or PIE objects. */
-#define UNDEFWEAK_NO_DYNAMIC_RELOC(INFO, H) \
- ((H)->root.type == bfd_link_hash_undefweak \
- && (ELF_ST_VISIBILITY ((H)->other) != STV_DEFAULT \
- || (INFO)->dynamic_undefined_weak == 0))
-
/* If ELIMINATE_COPY_RELOCS is non-zero, the linker will try to avoid
copying dynamic variables from a shared lib into an app's dynbss
section, and instead use a dynamic relocation to point into the
struct ppc_link_hash_entry *h;
struct plt_entry *plt_ent;
+ /* Symbol type. */
+ unsigned char symtype;
+
/* Symbol st_other. */
unsigned char other;
};
/* Track dynamic relocs copied for this symbol. */
struct elf_dyn_relocs *dyn_relocs;
- /* Chain of aliases referring to a weakdef. */
- struct ppc_link_hash_entry *weakref;
-
/* Link between function code and descriptor symbols. */
struct ppc_link_hash_entry *oh;
unsigned int non_zero_localentry:1;
/* Contexts in which symbol is used in the GOT (or TOC).
- TLS_GD .. TLS_EXPLICIT bits are or'd into the mask as the
- corresponding relocs are encountered during check_relocs.
- tls_optimize clears TLS_GD .. TLS_TPREL when optimizing to
- indicate the corresponding GOT entry type is not needed.
- tls_optimize may also set TLS_TPRELGD when a GD reloc turns into
- a TPREL one. We use a separate flag rather than setting TPREL
- just for convenience in distinguishing the two cases. */
-#define TLS_GD 1 /* GD reloc. */
-#define TLS_LD 2 /* LD reloc. */
-#define TLS_TPREL 4 /* TPREL reloc, => IE. */
-#define TLS_DTPREL 8 /* DTPREL reloc, => LD. */
-#define TLS_TLS 16 /* Any TLS reloc. */
-#define TLS_EXPLICIT 32 /* Marks TOC section TLS relocs. */
+ Bits are or'd into the mask as the corresponding relocs are
+ encountered during check_relocs, with TLS_TLS being set when any
+ of the other TLS bits are set. tls_optimize clears bits when
+ optimizing to indicate the corresponding GOT entry type is not
+ needed. If set, TLS_TLS is never cleared. tls_optimize may also
+ set TLS_TPRELGD when a GD reloc turns into a TPREL one. We use a
+ separate flag rather than setting TPREL just for convenience in
+ distinguishing the two cases.
+ These flags are also kept for local symbols. */
+#define TLS_TLS 1 /* Any TLS reloc. */
+#define TLS_GD 2 /* GD reloc. */
+#define TLS_LD 4 /* LD reloc. */
+#define TLS_TPREL 8 /* TPREL reloc, => IE. */
+#define TLS_DTPREL 16 /* DTPREL reloc, => LD. */
+#define TLS_MARK 32 /* __tls_get_addr call marked. */
#define TLS_TPRELGD 64 /* TPREL reloc resulting from GD->IE. */
-#define PLT_IFUNC 128 /* STT_GNU_IFUNC. */
+#define TLS_EXPLICIT 128 /* Marks TOC section TLS relocs. */
unsigned char tls_mask;
+
+ /* The above field is also used to mark function symbols. In which
+ case TLS_TLS will be 0. */
+#define PLT_IFUNC 2 /* STT_GNU_IFUNC. */
+#define PLT_KEEP 4 /* inline plt call requires plt entry. */
+#define NON_GOT 256 /* local symbol plt, not stored. */
};
/* ppc64 ELF linker hash table. */
/* Shortcuts to get to dynamic linker sections. */
asection *glink;
+ asection *global_entry;
asection *sfpr;
+ asection *pltlocal;
+ asection *relpltlocal;
asection *brlt;
asection *relbrlt;
asection *glink_eh_frame;
/* Set if tls optimization is enabled. */
unsigned int do_tls_opt:1;
+ /* Set if inline plt calls should be converted to direct calls. */
+ unsigned int can_convert_all_inline_plt:1;
+
/* Set on error. */
unsigned int stub_error:1;
/* Nonzero if this section has TLS related relocations. */
#define has_tls_reloc sec_flg0
-/* Nonzero if this section has a call to __tls_get_addr. */
+/* Nonzero if this section has an old-style call to __tls_get_addr. */
#define has_tls_get_addr_call sec_flg1
/* Nonzero if this section has any toc or got relocs. */
|| ! bfd_set_section_alignment (dynobj, htab->glink, 3))
return FALSE;
+ /* The part of .glink used by global entry stubs, separate so that
+ it can be aligned appropriately without affecting htab->glink. */
+ htab->global_entry = bfd_make_section_anyway_with_flags (dynobj, ".glink",
+ flags);
+ if (htab->global_entry == NULL
+ || ! bfd_set_section_alignment (dynobj, htab->global_entry, 2))
+ return FALSE;
+
if (!info->no_ld_generated_unwind_info)
{
flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS
|| ! bfd_set_section_alignment (dynobj, htab->brlt, 3))
return FALSE;
+ /* Local plt entries, put in .branch_lt but a separate section for
+ convenience. */
+ htab->pltlocal = bfd_make_section_anyway_with_flags (dynobj, ".branch_lt",
+ flags);
+ if (htab->pltlocal == NULL
+ || ! bfd_set_section_alignment (dynobj, htab->pltlocal, 3))
+ return FALSE;
+
if (!bfd_link_pic (info))
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);
+ 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;
+ htab->relpltlocal
+ = bfd_make_section_anyway_with_flags (dynobj, ".rela.branch_lt", flags);
+ if (htab->relpltlocal == NULL
+ || ! bfd_set_section_alignment (dynobj, htab->relpltlocal, 3))
+ return FALSE;
+
return TRUE;
}
if (stub_entry == NULL)
{
/* xgettext:c-format */
- info->callbacks->einfo (_("%P: %B: cannot create stub entry %s\n"),
- section->owner, stub_name);
+ _bfd_error_handler (_("%pB: cannot create stub entry %s"),
+ section->owner, stub_name);
return NULL;
}
if (eind->oh != NULL)
edir->oh = ppc_follow_link (eind->oh);
- /* If called to transfer flags for a weakdef during processing
- of elf_adjust_dynamic_symbol, don't copy NON_GOT_REF.
- We clear it ourselves for ELIMINATE_COPY_RELOCS. */
- if (!(ELIMINATE_COPY_RELOCS
- && eind->elf.root.type != bfd_link_hash_indirect
- && edir->elf.dynamic_adjusted))
- edir->elf.non_got_ref |= eind->elf.non_got_ref;
-
if (edir->elf.versioned != versioned_hidden)
edir->elf.ref_dynamic |= eind->elf.ref_dynamic;
edir->elf.ref_regular |= eind->elf.ref_regular;
edir->elf.ref_regular_nonweak |= eind->elf.ref_regular_nonweak;
+ edir->elf.non_got_ref |= eind->elf.non_got_ref;
edir->elf.needs_plt |= eind->elf.needs_plt;
edir->elf.pointer_equality_needed |= eind->elf.pointer_equality_needed;
in order to simplify readonly_dynrelocs and save a field in the
symbol hash entry, but that means dyn_relocs can't be used in any
tests about a specific symbol, or affect other symbol flags which
- are then tested.
- Chain weakdefs so we can get from the weakdef back to an alias.
- The list is circular so that we don't need to use u.weakdef as
- well as this list to look at all aliases. */
+ are then tested. */
if (eind->elf.root.type != bfd_link_hash_indirect)
- {
- struct ppc_link_hash_entry *cur, *add, *next;
-
- add = eind;
- do
- {
- cur = edir->weakref;
- if (cur != NULL)
- {
- do
- {
- /* We can be called twice for the same symbols.
- Don't make multiple loops. */
- if (cur == add)
- return;
- cur = cur->weakref;
- } while (cur != edir);
- }
- next = add->weakref;
- if (cur != add)
- {
- add->weakref = edir->weakref != NULL ? edir->weakref : edir;
- edir->weakref = add;
- }
- add = next;
- } while (add != NULL && add != eind);
- return;
- }
+ return;
/* Copy over any dynamic relocs we may have on the indirect sym. */
if (eind->dyn_relocs != NULL)
asection **sec,
bfd_vma *value)
{
- if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC
- && (ibfd->flags & DYNAMIC) == 0
- && bfd_get_flavour (info->output_bfd) == bfd_target_elf_flavour)
- elf_tdata (info->output_bfd)->has_gnu_symbols |= elf_gnu_symbol_ifunc;
-
if (*sec != NULL
&& strcmp ((*sec)->name, ".opd") == 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_error_handler (_("symbol '%s' has invalid st_other"
+ " for ABI version 1"), *name);
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
len = strlen (name);
dot_name = bfd_alloc (abfd, len + 2);
if (dot_name == NULL)
- return (struct elf_link_hash_entry *) 0 - 1;
+ return (struct elf_link_hash_entry *) -1;
dot_name[0] = '.';
memcpy (dot_name + 1, name, len + 1);
h = _bfd_elf_archive_symbol_lookup (abfd, info, dot_name);
if (opd != NULL && opd->size != 0)
{
+ BFD_ASSERT (ppc64_elf_section_data (opd)->sec_type == sec_normal);
+ ppc64_elf_section_data (opd)->sec_type = sec_opd;
+
if (abiversion (ibfd) == 0)
set_abiversion (ibfd, 1);
else if (abiversion (ibfd) >= 2)
{
/* xgettext:c-format */
- info->callbacks->einfo (_("%P: %B .opd not allowed in ABI"
- " version %d\n"),
- ibfd, abiversion (ibfd));
+ _bfd_error_handler (_("%pB .opd not allowed in ABI version %d"),
+ 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))
+ {
+ /* 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));
}
- if (!is_ppc64_elf (info->output_bfd))
- return TRUE;
htab = ppc_hash_table (info);
if (htab == NULL)
- return FALSE;
+ return TRUE;
+
+ if (opd != NULL && opd->size != 0
+ && (ibfd->flags & DYNAMIC) == 0
+ && (opd->flags & SEC_RELOC) != 0
+ && opd->reloc_count != 0
+ && !bfd_is_abs_section (opd->output_section)
+ && info->gc_sections)
+ {
+ /* 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;
+ Elf_Internal_Shdr *symtab_hdr;
+ Elf_Internal_Rela *relocs, *rel_end, *rel;
+
+ 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;
+ relocs = _bfd_elf_link_read_relocs (ibfd, opd, NULL, NULL,
+ info->keep_memory);
+ if (relocs == NULL)
+ return FALSE;
+ symtab_hdr = &elf_symtab_hdr (ibfd);
+ rel_end = relocs + opd->reloc_count - 1;
+ for (rel = relocs; rel < rel_end; rel++)
+ {
+ enum elf_ppc64_reloc_type r_type = ELF64_R_TYPE (rel->r_info);
+ unsigned long r_symndx = ELF64_R_SYM (rel->r_info);
+
+ if (r_type == R_PPC64_ADDR64
+ && ELF64_R_TYPE ((rel + 1)->r_info) == R_PPC64_TOC
+ && r_symndx < symtab_hdr->sh_info)
+ {
+ Elf_Internal_Sym *isym;
+ asection *s;
+
+ isym = bfd_sym_from_r_symndx (&htab->sym_cache, ibfd, r_symndx);
+ if (isym == NULL)
+ {
+ if (elf_section_data (opd)->relocs != relocs)
+ free (relocs);
+ return FALSE;
+ }
- /* 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));
+ s = bfd_section_from_elf_index (ibfd, isym->st_shndx);
+ if (s != NULL && s != opd)
+ opd_sym_map[OPD_NDX (rel->r_offset)] = s;
+ }
+ }
+ if (elf_section_data (opd)->relocs != relocs)
+ free (relocs);
+ }
p = &htab->dot_syms;
while ((eh = *p) != NULL)
elf_local_got_ents (abfd) = local_got_ents;
}
- if ((tls_type & (PLT_IFUNC | TLS_EXPLICIT)) == 0)
+ if ((tls_type & (NON_GOT | TLS_EXPLICIT)) == 0)
{
struct got_entry *ent;
local_plt = (struct plt_entry **) (local_got_ents + symtab_hdr->sh_info);
local_got_tls_masks = (unsigned char *) (local_plt + symtab_hdr->sh_info);
- local_got_tls_masks[r_symndx] |= tls_type;
+ local_got_tls_masks[r_symndx] |= tls_type & 0xff;
return local_plt + r_symndx;
}
|| r_type == R_PPC64_ADDR24
|| r_type == R_PPC64_ADDR14
|| r_type == R_PPC64_ADDR14_BRTAKEN
- || r_type == R_PPC64_ADDR14_BRNTAKEN);
+ || r_type == R_PPC64_ADDR14_BRNTAKEN
+ || r_type == R_PPC64_PLTCALL);
+}
+
+/* Relocs on inline plt call sequence insns prior to the call. */
+
+static bfd_boolean
+is_plt_seq_reloc (enum elf_ppc64_reloc_type r_type)
+{
+ return (r_type == R_PPC64_PLT16_HA
+ || r_type == R_PPC64_PLT16_HI
+ || r_type == R_PPC64_PLT16_LO
+ || r_type == R_PPC64_PLT16_LO_DS
+ || r_type == R_PPC64_PLTSEQ);
}
/* Look through the relocs for a section during the first phase, and
const Elf_Internal_Rela *rel;
const Elf_Internal_Rela *rel_end;
asection *sreloc;
- asection **opd_sym_map;
struct elf_link_hash_entry *tga, *dottga;
+ bfd_boolean is_opd;
if (bfd_link_relocatable (info))
return TRUE;
symtab_hdr = &elf_symtab_hdr (abfd);
sym_hashes = elf_sym_hashes (abfd);
sreloc = NULL;
- opd_sym_map = NULL;
- 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;
-
+ is_opd = ppc64_elf_section_data (sec)->sec_type == sec_opd;
rel_end = relocs + sec->reloc_count;
for (rel = relocs; rel < rel_end; rel++)
{
h = NULL;
else
{
- struct ppc_link_hash_entry *eh;
-
h = sym_hashes[r_symndx - symtab_hdr->sh_info];
h = elf_follow_link (h);
- eh = (struct ppc_link_hash_entry *) h;
-
- /* PR15323, ref flags aren't set for references in the same
- object. */
- h->root.non_ir_ref_regular = 1;
- if (eh->is_func && eh->oh != NULL)
- eh->oh->elf.root.non_ir_ref_regular = 1;
if (h == htab->elf.hgot)
sec->has_toc_reloc = 1;
if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
{
ifunc = update_local_sym_info (abfd, symtab_hdr, r_symndx,
- rel->r_addend, PLT_IFUNC);
+ rel->r_addend,
+ NON_GOT | PLT_IFUNC);
if (ifunc == NULL)
return FALSE;
}
case R_PPC64_TLSLD:
/* These special tls relocs tie a call to __tls_get_addr with
its parameter symbol. */
+ if (h != NULL)
+ ((struct ppc_link_hash_entry *) h)->tls_mask |= TLS_TLS | TLS_MARK;
+ else
+ if (!update_local_sym_info (abfd, symtab_hdr, r_symndx,
+ rel->r_addend,
+ NON_GOT | TLS_TLS | TLS_MARK))
+ return FALSE;
+ sec->has_tls_reloc = 1;
break;
case R_PPC64_GOT_TLSLD16:
case R_PPC64_PLT16_HA:
case R_PPC64_PLT16_HI:
case R_PPC64_PLT16_LO:
+ case R_PPC64_PLT16_LO_DS:
case R_PPC64_PLT32:
case R_PPC64_PLT64:
/* This symbol requires a procedure linkage table entry. */
if (h->root.root.string[0] == '.'
&& h->root.root.string[1] != '\0')
((struct ppc_link_hash_entry *) h)->is_func = 1;
+ ((struct ppc_link_hash_entry *) h)->tls_mask |= PLT_KEEP;
plt_list = &h->plt.plist;
}
if (plt_list == NULL)
- {
- /* It does not make sense to have a procedure linkage
- table entry for a non-ifunc local symbol. */
- info->callbacks->einfo
- /* xgettext:c-format */
- (_("%H: %s reloc against local symbol\n"),
- abfd, sec, rel->r_offset,
- ppc64_elf_howto_table[r_type]->name);
- bfd_set_error (bfd_error_bad_value);
- return FALSE;
- }
+ plt_list = update_local_sym_info (abfd, symtab_hdr, r_symndx,
+ rel->r_addend,
+ NON_GOT | PLT_KEEP);
if (!update_plt_info (abfd, plt_list, rel->r_addend))
return FALSE;
break;
ppc_howto_init ();
/* xgettext:c-format */
info->callbacks->einfo (_("%H: %s reloc unsupported "
- "in shared libraries and PIEs.\n"),
+ "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);
if (dest != sec)
ppc64_elf_section_data (sec)->has_14bit_branch = 1;
}
+ goto rel24;
+
+ case R_PPC64_PLTCALL:
+ ppc64_elf_section_data (sec)->has_pltcall = 1;
/* Fall through. */
case R_PPC64_REL24:
+ rel24:
plt_list = ifunc;
if (h != NULL)
{
goto dodyn;
case R_PPC64_ADDR64:
- if (opd_sym_map != NULL
+ if (is_opd
&& rel + 1 < rel_end
&& ELF64_R_TYPE ((rel + 1)->r_info) == R_PPC64_TOC)
{
if (h != NULL)
((struct ppc_link_hash_entry *) h)->is_func = 1;
- else
- {
- asection *s;
- Elf_Internal_Sym *isym;
-
- isym = bfd_sym_from_r_symndx (&htab->sym_cache,
- abfd, r_symndx);
- if (isym == NULL)
- return FALSE;
-
- s = bfd_section_from_elf_index (abfd, isym->st_shndx);
- if (s != NULL && s != sec)
- opd_sym_map[OPD_NDX (rel->r_offset)] = s;
- }
}
/* Fall through. */
h->non_got_ref = 1;
/* Don't propagate .opd relocs. */
- if (NO_OPD_RELOCS && opd_sym_map != NULL)
+ if (NO_OPD_RELOCS && is_opd)
break;
/* If we are creating a shared library, and this is a reloc
{
_bfd_error_handler
/* xgettext:c-format */
- (_("%B uses unknown e_flags 0x%lx"), ibfd, iflags);
+ (_("%pB uses unknown e_flags 0x%lx"), ibfd, iflags);
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
{
_bfd_error_handler
/* xgettext:c-format */
- (_("%B: ABI version %ld is not compatible with ABI version %ld output"),
+ (_("%pB: ABI version %ld is not compatible with ABI version %ld output"),
ibfd, iflags, oflags);
bfd_set_error (bfd_error_bad_value);
return FALSE;
if ((eh->elf.root.type == bfd_link_hash_defined
|| eh->elf.root.type == bfd_link_hash_defweak)
- && (eh->elf.ref_dynamic
+ && ((eh->elf.ref_dynamic && !eh->elf.forced_local)
|| ((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
a call reloc. Mark the function descriptor too
against garbage collection. */
fdh->elf.mark = 1;
- if (fdh->elf.u.weakdef != NULL)
- fdh->elf.u.weakdef->mark = 1;
+ if (fdh->elf.is_weakalias)
+ weakdef (&fdh->elf)->mark = 1;
eh = fdh;
}
return rsec;
}
-/* Update the .got, .plt. and dynamic reloc reference counts for the
- section being removed. */
+/* The maximum size of .sfpr. */
+#define SFPR_MAX (218*4)
-static bfd_boolean
-ppc64_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info,
- asection *sec, const Elf_Internal_Rela *relocs)
+struct sfpr_def_parms
{
- struct ppc_link_hash_table *htab;
- Elf_Internal_Shdr *symtab_hdr;
- struct elf_link_hash_entry **sym_hashes;
- struct got_entry **local_got_ents;
- const Elf_Internal_Rela *rel, *relend;
-
- if (bfd_link_relocatable (info))
- return TRUE;
+ const char name[12];
+ unsigned char lo, hi;
+ bfd_byte * (*write_ent) (bfd *, bfd_byte *, int);
+ bfd_byte * (*write_tail) (bfd *, bfd_byte *, int);
+};
- if ((sec->flags & SEC_ALLOC) == 0)
- return TRUE;
+/* Auto-generate _save*, _rest* functions in .sfpr.
+ If STUB_SEC is non-null, define alias symbols in STUB_SEC
+ instead. */
- elf_section_data (sec)->local_dynrel = NULL;
+static bfd_boolean
+sfpr_define (struct bfd_link_info *info,
+ const struct sfpr_def_parms *parm,
+ asection *stub_sec)
+{
+ struct ppc_link_hash_table *htab = ppc_hash_table (info);
+ unsigned int i;
+ size_t len = strlen (parm->name);
+ bfd_boolean writing = FALSE;
+ char sym[16];
- htab = ppc_hash_table (info);
if (htab == NULL)
return FALSE;
- symtab_hdr = &elf_symtab_hdr (abfd);
- sym_hashes = elf_sym_hashes (abfd);
- local_got_ents = elf_local_got_ents (abfd);
+ memcpy (sym, parm->name, len);
+ sym[len + 2] = 0;
- relend = relocs + sec->reloc_count;
- for (rel = relocs; rel < relend; rel++)
+ for (i = parm->lo; i <= parm->hi; i++)
{
- unsigned long r_symndx;
- enum elf_ppc64_reloc_type r_type;
- struct elf_link_hash_entry *h = NULL;
- struct plt_entry **plt_list = NULL;
- unsigned char tls_type = 0;
+ struct ppc_link_hash_entry *h;
- r_symndx = ELF64_R_SYM (rel->r_info);
- r_type = ELF64_R_TYPE (rel->r_info);
- if (r_symndx >= symtab_hdr->sh_info)
+ sym[len + 0] = i / 10 + '0';
+ sym[len + 1] = i % 10 + '0';
+ h = (struct ppc_link_hash_entry *)
+ elf_link_hash_lookup (&htab->elf, sym, writing, TRUE, TRUE);
+ if (stub_sec != NULL)
{
- struct ppc_link_hash_entry *eh;
- struct elf_dyn_relocs **pp;
- struct elf_dyn_relocs *p;
-
- h = sym_hashes[r_symndx - symtab_hdr->sh_info];
- h = elf_follow_link (h);
- eh = (struct ppc_link_hash_entry *) h;
-
- for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next)
- if (p->sec == sec)
- {
- /* Everything must go for SEC. */
- *pp = p->next;
- break;
- }
+ if (h != NULL
+ && h->elf.root.type == bfd_link_hash_defined
+ && h->elf.root.u.def.section == htab->sfpr)
+ {
+ struct elf_link_hash_entry *s;
+ char buf[32];
+ sprintf (buf, "%08x.%s", stub_sec->id & 0xffffffff, sym);
+ s = elf_link_hash_lookup (&htab->elf, buf, TRUE, TRUE, FALSE);
+ if (s == NULL)
+ return FALSE;
+ if (s->root.type == bfd_link_hash_new
+ || (s->root.type = bfd_link_hash_defined
+ && s->root.u.def.section == stub_sec))
+ {
+ s->root.type = bfd_link_hash_defined;
+ s->root.u.def.section = stub_sec;
+ s->root.u.def.value = (stub_sec->size - htab->sfpr->size
+ + h->elf.root.u.def.value);
+ s->ref_regular = 1;
+ s->def_regular = 1;
+ s->ref_regular_nonweak = 1;
+ s->forced_local = 1;
+ s->non_elf = 0;
+ s->root.linker_def = 1;
+ }
+ }
+ continue;
}
-
- switch (r_type)
- {
- case R_PPC64_GOT_TLSLD16:
- case R_PPC64_GOT_TLSLD16_LO:
- case R_PPC64_GOT_TLSLD16_HI:
- case R_PPC64_GOT_TLSLD16_HA:
- tls_type = TLS_TLS | TLS_LD;
- goto dogot;
-
- case R_PPC64_GOT_TLSGD16:
- case R_PPC64_GOT_TLSGD16_LO:
- case R_PPC64_GOT_TLSGD16_HI:
- case R_PPC64_GOT_TLSGD16_HA:
- tls_type = TLS_TLS | TLS_GD;
- goto dogot;
-
- case R_PPC64_GOT_TPREL16_DS:
- case R_PPC64_GOT_TPREL16_LO_DS:
- case R_PPC64_GOT_TPREL16_HI:
- case R_PPC64_GOT_TPREL16_HA:
- tls_type = TLS_TLS | TLS_TPREL;
- goto dogot;
-
- case R_PPC64_GOT_DTPREL16_DS:
- case R_PPC64_GOT_DTPREL16_LO_DS:
- case R_PPC64_GOT_DTPREL16_HI:
- case R_PPC64_GOT_DTPREL16_HA:
- tls_type = TLS_TLS | TLS_DTPREL;
- goto dogot;
-
- case R_PPC64_GOT16:
- case R_PPC64_GOT16_DS:
- case R_PPC64_GOT16_HA:
- case R_PPC64_GOT16_HI:
- case R_PPC64_GOT16_LO:
- case R_PPC64_GOT16_LO_DS:
- dogot:
- {
- struct got_entry *ent;
-
- if (h != NULL)
- ent = h->got.glist;
- else
- ent = local_got_ents[r_symndx];
-
- for (; ent != NULL; ent = ent->next)
- if (ent->addend == rel->r_addend
- && ent->owner == abfd
- && ent->tls_type == tls_type)
- break;
- if (ent == NULL)
- abort ();
- if (ent->got.refcount > 0)
- ent->got.refcount -= 1;
- }
- if (h != NULL && !bfd_link_pic (info) && abiversion (abfd) != 1)
- plt_list = &h->plt.plist;
- break;
-
- case R_PPC64_PLT16_HA:
- case R_PPC64_PLT16_HI:
- case R_PPC64_PLT16_LO:
- case R_PPC64_PLT32:
- case R_PPC64_PLT64:
- case R_PPC64_REL14:
- case R_PPC64_REL14_BRNTAKEN:
- case R_PPC64_REL14_BRTAKEN:
- case R_PPC64_REL24:
- if (h != NULL)
- plt_list = &h->plt.plist;
- else if (local_got_ents != NULL)
- {
- struct plt_entry **local_plt = (struct plt_entry **)
- (local_got_ents + symtab_hdr->sh_info);
- unsigned char *local_got_tls_masks = (unsigned char *)
- (local_plt + symtab_hdr->sh_info);
- if ((local_got_tls_masks[r_symndx] & PLT_IFUNC) != 0)
- plt_list = local_plt + r_symndx;
- }
- break;
-
- case R_PPC64_ADDR64:
- 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 && !bfd_link_pic (info) && abiversion (abfd) != 1
- && rel->r_addend == 0)
- plt_list = &h->plt.plist;
- break;
-
- default:
- break;
- }
- if (plt_list != NULL)
- {
- struct plt_entry *ent;
-
- for (ent = *plt_list; ent != NULL; ent = ent->next)
- if (ent->addend == rel->r_addend)
- break;
- if (ent != NULL && ent->plt.refcount > 0)
- ent->plt.refcount -= 1;
- }
- }
- return TRUE;
-}
-
-/* The maximum size of .sfpr. */
-#define SFPR_MAX (218*4)
-
-struct sfpr_def_parms
-{
- const char name[12];
- unsigned char lo, hi;
- bfd_byte * (*write_ent) (bfd *, bfd_byte *, int);
- bfd_byte * (*write_tail) (bfd *, bfd_byte *, int);
-};
-
-/* Auto-generate _save*, _rest* functions in .sfpr.
- If STUB_SEC is non-null, define alias symbols in STUB_SEC
- instead. */
-
-static bfd_boolean
-sfpr_define (struct bfd_link_info *info,
- const struct sfpr_def_parms *parm,
- asection *stub_sec)
-{
- struct ppc_link_hash_table *htab = ppc_hash_table (info);
- unsigned int i;
- size_t len = strlen (parm->name);
- bfd_boolean writing = FALSE;
- char sym[16];
-
- if (htab == NULL)
- return FALSE;
-
- memcpy (sym, parm->name, len);
- sym[len + 2] = 0;
-
- for (i = parm->lo; i <= parm->hi; i++)
- {
- struct ppc_link_hash_entry *h;
-
- sym[len + 0] = i / 10 + '0';
- sym[len + 1] = i % 10 + '0';
- h = (struct ppc_link_hash_entry *)
- elf_link_hash_lookup (&htab->elf, sym, writing, TRUE, TRUE);
- if (stub_sec != NULL)
- {
- if (h != NULL
- && h->elf.root.type == bfd_link_hash_defined
- && h->elf.root.u.def.section == htab->sfpr)
- {
- struct elf_link_hash_entry *s;
- char buf[32];
- sprintf (buf, "%08x.%s", stub_sec->id & 0xffffffff, sym);
- s = elf_link_hash_lookup (&htab->elf, buf, TRUE, TRUE, FALSE);
- if (s == NULL)
- return FALSE;
- if (s->root.type == bfd_link_hash_new
- || (s->root.type = bfd_link_hash_defined
- && s->root.u.def.section == stub_sec))
- {
- s->root.type = bfd_link_hash_defined;
- s->root.u.def.section = stub_sec;
- s->root.u.def.value = (stub_sec->size
- + h->elf.root.u.def.value);
- s->ref_regular = 1;
- s->def_regular = 1;
- s->ref_regular_nonweak = 1;
- s->forced_local = 1;
- s->non_elf = 0;
- s->root.linker_def = 1;
- }
- }
- continue;
- }
- if (h != NULL)
+ if (h != NULL)
{
h->save_res = 1;
if (!h->elf.def_regular)
return TRUE;
}
-/* Return true if we have dynamic relocs against H that apply to
- read-only sections. */
+/* Find dynamic relocs for H that apply to read-only sections. */
-static bfd_boolean
+static asection *
readonly_dynrelocs (struct elf_link_hash_entry *h)
{
struct ppc_link_hash_entry *eh;
asection *s = p->sec->output_section;
if (s != NULL && (s->flags & SEC_READONLY) != 0)
- return TRUE;
+ return p->sec;
}
- return FALSE;
+ return NULL;
}
/* Return true if we have dynamic relocs against H or any of its weak
- aliases, that apply to read-only sections. */
+ aliases, that apply to read-only sections. Cannot be used after
+ size_dynamic_sections. */
static bfd_boolean
alias_readonly_dynrelocs (struct elf_link_hash_entry *h)
{
if (readonly_dynrelocs (&eh->elf))
return TRUE;
- eh = eh->weakref;
+ eh = (struct ppc_link_hash_entry *) eh->elf.u.alias;
} while (eh != NULL && &eh->elf != h);
return FALSE;
|| h->type == STT_GNU_IFUNC
|| h->needs_plt)
{
+ bfd_boolean local = (((struct ppc_link_hash_entry *) h)->save_res
+ || SYMBOL_CALLS_LOCAL (info, h)
+ || UNDEFWEAK_NO_DYNAMIC_RELOC (info, h));
+ /* Discard dyn_relocs when non-pic if we've decided that a
+ function symbol is local and not an ifunc. We keep dynamic
+ relocs for ifuncs when local rather than always emitting a
+ plt call stub for them and defining the symbol on the call
+ stub. We can't do that for ELFv1 anyway (a function symbol
+ is defined on a descriptor, not code) and it can be faster at
+ run-time due to not needing to bounce through a stub. The
+ dyn_relocs for ifuncs will be applied even in a static
+ executable. */
+ if (!bfd_link_pic (info)
+ && h->type != STT_GNU_IFUNC
+ && local)
+ ((struct ppc_link_hash_entry *) h)->dyn_relocs = NULL;
+
/* Clear procedure linkage table information for any symbol that
won't need a .plt entry. */
struct plt_entry *ent;
break;
if (ent == NULL
|| (h->type != STT_GNU_IFUNC
- && (SYMBOL_CALLS_LOCAL (info, h)
- || UNDEFWEAK_NO_DYNAMIC_RELOC (info, h)))
- || ((struct ppc_link_hash_entry *) h)->save_res)
+ && local
+ && (htab->can_convert_all_inline_plt
+ || (((struct ppc_link_hash_entry *) h)->tls_mask
+ & (TLS_TLS | PLT_KEEP)) != PLT_KEEP)))
{
h->plt.plist = NULL;
h->needs_plt = 0;
relocs is that calling via a global entry stub costs a
few more instructions, and pointer_equality_needed causes
extra work in ld.so when resolving these symbols. */
- if (global_entry_stub (h)
- && !alias_readonly_dynrelocs (h))
+ if (global_entry_stub (h))
{
- h->pointer_equality_needed = 0;
- /* After adjust_dynamic_symbol, non_got_ref set in
- the non-pic case means that dyn_relocs for this
- symbol should be discarded. */
- h->non_got_ref = 0;
+ if (!readonly_dynrelocs (h))
+ {
+ h->pointer_equality_needed = 0;
+ /* If we haven't seen a branch reloc and the symbol
+ isn't an ifunc then we don't need a plt entry. */
+ if (!h->needs_plt)
+ h->plt.plist = NULL;
+ }
+ else if (!bfd_link_pic (info))
+ /* We are going to be defining the function symbol on the
+ plt stub, so no dyn_relocs needed when non-pic. */
+ ((struct ppc_link_hash_entry *) h)->dyn_relocs = NULL;
}
- /* If making a plt entry, then we don't need copy relocs. */
+ /* ELFv2 function symbols can't have copy relocs. */
+ return TRUE;
+ }
+ else if (!h->needs_plt
+ && !readonly_dynrelocs (h))
+ {
+ /* If we haven't seen a branch reloc and the symbol isn't an
+ ifunc then we don't need a plt entry. */
+ h->plt.plist = NULL;
+ h->pointer_equality_needed = 0;
return TRUE;
}
}
/* If this is a weak symbol, and there is a real definition, the
processor independent code will have arranged for us to see the
real definition first, and we can just use the same value. */
- if (h->u.weakdef != NULL)
- {
- BFD_ASSERT (h->u.weakdef->root.type == bfd_link_hash_defined
- || h->u.weakdef->root.type == bfd_link_hash_defweak);
- h->root.u.def.section = h->u.weakdef->root.u.def.section;
- h->root.u.def.value = h->u.weakdef->root.u.def.value;
- if (ELIMINATE_COPY_RELOCS)
- h->non_got_ref = h->u.weakdef->non_got_ref;
+ if (h->is_weakalias)
+ {
+ struct elf_link_hash_entry *def = weakdef (h);
+ BFD_ASSERT (def->root.type == bfd_link_hash_defined);
+ h->root.u.def.section = def->root.u.def.section;
+ h->root.u.def.value = def->root.u.def.value;
+ if (def->root.u.def.section == htab->elf.sdynbss
+ || def->root.u.def.section == htab->elf.sdynrelro)
+ ((struct ppc_link_hash_entry *) h)->dyn_relocs = NULL;
return TRUE;
}
/* If -z nocopyreloc was given, don't generate them either. */
|| info->nocopyreloc
- /* If we didn't find any dynamic relocs in read-only sections, then
+ /* If we don't find any dynamic relocs in read-only sections, then
we'll be keeping the dynamic relocs and avoiding the copy reloc. */
|| (ELIMINATE_COPY_RELOCS && !alias_readonly_dynrelocs (h))
definition for the variable. Text relocations are preferable
to an incorrect program. */
|| h->protected_def)
- {
- h->non_got_ref = 0;
- return TRUE;
- }
+ return TRUE;
if (h->plt.plist != NULL)
{
sections. Allow them to proceed, but warn that this might
break at runtime. */
info->callbacks->einfo
- (_("%P: copy reloc against `%T' requires lazy plt linking; "
+ (_("%P: copy reloc against `%pT' requires lazy plt linking; "
"avoid setting LD_BIND_NOW=1 or upgrade gcc\n"),
h->root.root.string);
}
determine the address it must put in the global offset table, so
both the dynamic object and the regular object will refer to the
same memory location for the variable. */
-
- /* We must generate a R_PPC64_COPY reloc to tell the dynamic linker
- to copy the initial value out of the dynamic object and into the
- runtime process image. We need to remember the offset into the
- .rela.bss section we are going to use. */
if ((h->root.u.def.section->flags & SEC_READONLY) != 0)
{
s = htab->elf.sdynrelro;
}
if ((h->root.u.def.section->flags & SEC_ALLOC) != 0 && h->size != 0)
{
+ /* We must generate a R_PPC64_COPY reloc to tell the dynamic
+ linker to copy the initial value out of the dynamic object
+ and into the runtime process image. */
srel->size += sizeof (Elf64_External_Rela);
h->needs_copy = 1;
}
+ /* We no longer want dyn_relocs. */
+ ((struct ppc_link_hash_entry *) h)->dyn_relocs = NULL;
return _bfd_elf_adjust_dynamic_copy (info, h, s);
}
if (!get_sym_h (&h, &sym, &sec, tls_maskp, locsymsp, r_symndx, ibfd))
return 0;
- if ((*tls_maskp != NULL && **tls_maskp != 0)
+ if ((*tls_maskp != NULL
+ && (**tls_maskp & TLS_TLS) != 0
+ && **tls_maskp != (TLS_TLS | TLS_MARK))
|| sec == NULL
|| ppc64_elf_section_data (sec) == NULL
|| ppc64_elf_section_data (sec)->sec_type != sec_toc)
if (ent.sec == NULL || ent.sec->output_section == NULL)
{
_bfd_error_handler
- (_("%B: undefined symbol on R_PPC64_TOCSAVE relocation"), ibfd);
+ (_("%pB: undefined symbol on R_PPC64_TOCSAVE relocation"), ibfd);
return NULL;
}
}
/* xgettext:c-format */
- info->callbacks->einfo (_("%P: dynreloc miscount for %B, section %A\n"),
- sec->owner, sec);
+ _bfd_error_handler (_("dynreloc miscount for %pB, section %pA"),
+ sec->owner, sec);
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
optimization for them! */
broken_opd:
_bfd_error_handler
- (_("%B: .opd is not a regular array of opd entries"), ibfd);
+ (_("%pB: .opd is not a regular array of opd entries"), ibfd);
broken = TRUE;
break;
}
{
_bfd_error_handler
/* xgettext:c-format */
- (_("%B: unexpected reloc type %u in .opd section"),
+ (_("%pB: unexpected reloc type %u in .opd section"),
ibfd, r_type);
broken = TRUE;
break;
_bfd_error_handler
/* xgettext:c-format */
- (_("%B: undefined sym `%s' in .opd section"),
+ (_("%pB: undefined sym `%s' in .opd section"),
ibfd, sym_name);
broken = TRUE;
break;
opd->adjust = bfd_zalloc (sec->owner, amt);
if (opd->adjust == NULL)
return FALSE;
- ppc64_elf_section_data (sec)->sec_type = sec_opd;
/* This seems a waste of time as input .opd sections are all
zeros as generated by gcc, but I suppose there's no reason
return TRUE;
}
+/* Analyze inline PLT call relocations to see whether calls to locally
+ defined functions can be converted to direct calls. */
+
+bfd_boolean
+ppc64_elf_inline_plt (struct bfd_link_info *info)
+{
+ struct ppc_link_hash_table *htab;
+ bfd *ibfd;
+ asection *sec;
+ bfd_vma low_vma, high_vma, limit;
+
+ htab = ppc_hash_table (info);
+ if (htab == NULL)
+ return FALSE;
+
+ /* A bl insn can reach -0x2000000 to 0x1fffffc. The limit is
+ reduced somewhat to cater for possible stubs that might be added
+ between the call and its destination. */
+ if (htab->params->group_size < 0)
+ {
+ limit = -htab->params->group_size;
+ if (limit == 1)
+ limit = 0x1e00000;
+ }
+ else
+ {
+ limit = htab->params->group_size;
+ if (limit == 1)
+ limit = 0x1c00000;
+ }
+
+ low_vma = -1;
+ high_vma = 0;
+ for (sec = info->output_bfd->sections; sec != NULL; sec = sec->next)
+ if ((sec->flags & (SEC_ALLOC | SEC_CODE)) == (SEC_ALLOC | SEC_CODE))
+ {
+ if (low_vma > sec->vma)
+ low_vma = sec->vma;
+ if (high_vma < sec->vma + sec->size)
+ high_vma = sec->vma + sec->size;
+ }
+
+ /* If a "bl" can reach anywhere in local code sections, then we can
+ convert all inline PLT sequences to direct calls when the symbol
+ is local. */
+ if (high_vma - low_vma < limit)
+ {
+ htab->can_convert_all_inline_plt = 1;
+ return TRUE;
+ }
+
+ /* Otherwise, go looking through relocs for cases where a direct
+ call won't reach. Mark the symbol on any such reloc to disable
+ the optimization and keep the PLT entry as it seems likely that
+ this will be better than creating trampolines. Note that this
+ will disable the optimization for all inline PLT calls to a
+ particular symbol, not just those that won't reach. The
+ difficulty in doing a more precise optimization is that the
+ linker needs to make a decision depending on whether a
+ particular R_PPC64_PLTCALL insn can be turned into a direct
+ call, for each of the R_PPC64_PLTSEQ and R_PPC64_PLT16* insns in
+ the sequence, and there is nothing that ties those relocs
+ together except their symbol. */
+
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+ {
+ Elf_Internal_Shdr *symtab_hdr;
+ Elf_Internal_Sym *local_syms;
+
+ if (!is_ppc64_elf (ibfd))
+ continue;
+
+ local_syms = NULL;
+ symtab_hdr = &elf_symtab_hdr (ibfd);
+
+ for (sec = ibfd->sections; sec != NULL; sec = sec->next)
+ if (ppc64_elf_section_data (sec)->has_pltcall
+ && !bfd_is_abs_section (sec->output_section))
+ {
+ Elf_Internal_Rela *relstart, *rel, *relend;
+
+ /* Read the relocations. */
+ relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL,
+ info->keep_memory);
+ if (relstart == NULL)
+ return FALSE;
+
+ relend = relstart + sec->reloc_count;
+ for (rel = relstart; rel < relend; )
+ {
+ enum elf_ppc64_reloc_type r_type;
+ unsigned long r_symndx;
+ asection *sym_sec;
+ struct elf_link_hash_entry *h;
+ Elf_Internal_Sym *sym;
+ unsigned char *tls_maskp;
+
+ r_type = ELF64_R_TYPE (rel->r_info);
+ if (r_type != R_PPC64_PLTCALL)
+ continue;
+
+ r_symndx = ELF64_R_SYM (rel->r_info);
+ if (!get_sym_h (&h, &sym, &sym_sec, &tls_maskp, &local_syms,
+ r_symndx, ibfd))
+ {
+ if (elf_section_data (sec)->relocs != relstart)
+ free (relstart);
+ if (local_syms != NULL
+ && symtab_hdr->contents != (unsigned char *) local_syms)
+ free (local_syms);
+ return FALSE;
+ }
+
+ if (sym_sec != NULL && sym_sec->output_section != NULL)
+ {
+ bfd_vma from, to;
+ if (h != NULL)
+ to = h->root.u.def.value;
+ else
+ to = sym->st_value;
+ to += (rel->r_addend
+ + sym_sec->output_offset
+ + sym_sec->output_section->vma);
+ from = (rel->r_offset
+ + sec->output_offset
+ + sec->output_section->vma);
+ if (to - from + limit < 2 * limit)
+ *tls_maskp &= ~PLT_KEEP;
+ }
+ }
+ if (elf_section_data (sec)->relocs != relstart)
+ free (relstart);
+ }
+
+ if (local_syms != NULL
+ && symtab_hdr->contents != (unsigned char *) local_syms)
+ {
+ if (!info->keep_memory)
+ free (local_syms);
+ else
+ symtab_hdr->contents = (unsigned char *) local_syms;
+ }
+ }
+
+ return TRUE;
+}
+
/* Set htab->tls_get_addr and call the generic ELF tls_setup function. */
asection *
if (htab->params->plt_localentry0
&& elf_link_hash_lookup (&htab->elf, "GLIBC_2.26",
FALSE, FALSE, FALSE) == NULL)
- info->callbacks->einfo
- (_("%P: warning: --plt-localentry is especially dangerous without "
- "ld.so support to detect ABI violations.\n"));
+ _bfd_error_handler
+ (_("warning: --plt-localentry is especially dangerous without "
+ "ld.so support to detect ABI violations"));
htab->tls_get_addr = ((struct ppc_link_hash_entry *)
elf_link_hash_lookup (&htab->elf, ".__tls_get_addr",
case R_PPC64_TLSGD:
case R_PPC64_TLSLD:
+ if (rel + 1 < relend
+ && is_plt_seq_reloc (ELF64_R_TYPE (rel[1].r_info)))
+ {
+ if (pass != 0
+ && ELF64_R_TYPE (rel[1].r_info) != R_PPC64_PLTSEQ)
+ {
+ r_symndx = ELF64_R_SYM (rel[1].r_info);
+ if (!get_sym_h (&h, NULL, NULL, NULL, &locsyms,
+ r_symndx, ibfd))
+ goto err_free_rel;
+ if (h != NULL)
+ {
+ struct plt_entry *ent = NULL;
+
+ for (ent = h->plt.plist;
+ ent != NULL;
+ ent = ent->next)
+ if (ent->addend == rel[1].r_addend)
+ break;
+
+ if (ent != NULL
+ && ent->plt.refcount > 0)
+ ent->plt.refcount -= 1;
+ }
+ }
+ continue;
+ }
found_tls_get_addr_arg = 1;
/* Fall through. */
goto err_free_rel;
if (toc_tls != NULL)
{
- if ((*toc_tls & (TLS_GD | TLS_LD)) != 0)
+ if ((*toc_tls & TLS_TLS) != 0
+ && ((*toc_tls & (TLS_GD | TLS_LD)) != 0))
found_tls_get_addr_arg = 1;
if (retval > 1)
toc_ref[toc_ref_index] = 1;
continue;
}
- if (expecting_tls_get_addr != 1)
- continue;
-
/* Uh oh, we didn't find the expected call. We
could just mark this symbol to exclude it
from tls optimization but it's safer to skip
goto err_free_rel;
}
- if (expecting_tls_get_addr && htab->tls_get_addr != NULL)
+ /* If we don't have old-style __tls_get_addr calls
+ without TLSGD/TLSLD marker relocs, and we haven't
+ found a new-style __tls_get_addr call with a
+ marker for this symbol, then we either have a
+ broken object file or an -mlongcall style
+ indirect call to __tls_get_addr without a marker.
+ Disable optimization in this case. */
+ if ((tls_clear & (TLS_GD | TLS_LD)) != 0
+ && (tls_set & TLS_EXPLICIT) == 0
+ && !sec->has_tls_get_addr_call
+ && ((*tls_mask & (TLS_TLS | TLS_MARK))
+ != (TLS_TLS | TLS_MARK)))
+ continue;
+
+ if (expecting_tls_get_addr)
{
- struct plt_entry *ent;
- for (ent = htab->tls_get_addr->elf.plt.plist;
- ent != NULL;
- ent = ent->next)
- if (ent->addend == 0)
- {
- if (ent->plt.refcount > 0)
- {
- ent->plt.refcount -= 1;
- expecting_tls_get_addr = 0;
- }
+ struct plt_entry *ent = NULL;
+
+ if (htab->tls_get_addr != NULL)
+ for (ent = htab->tls_get_addr->elf.plt.plist;
+ ent != NULL;
+ ent = ent->next)
+ if (ent->addend == 0)
break;
- }
- }
- if (expecting_tls_get_addr && htab->tls_get_addr_fd != NULL)
- {
- struct plt_entry *ent;
- for (ent = htab->tls_get_addr_fd->elf.plt.plist;
- ent != NULL;
- ent = ent->next)
- if (ent->addend == 0)
- {
- if (ent->plt.refcount > 0)
- ent->plt.refcount -= 1;
+ if (ent == NULL && htab->tls_get_addr_fd != NULL)
+ for (ent = htab->tls_get_addr_fd->elf.plt.plist;
+ ent != NULL;
+ ent = ent->next)
+ if (ent->addend == 0)
break;
- }
+
+ if (ent != NULL
+ && ent->plt.refcount > 0)
+ ent->plt.refcount -= 1;
}
if (tls_clear == 0)
info->callbacks->einfo
/* xgettext:c-format */
(_("%H: toc optimization is not supported for"
- " %s instruction.\n"),
+ " %s instruction\n"),
ibfd, sec, rel->r_offset & ~3, str);
}
}
htab->elf.irelplt->size += rentsize;
htab->got_reli_size += rentsize;
}
- else if ((bfd_link_pic (info)
+ else if (((bfd_link_pic (info)
+ && !((gent->tls_type & TLS_TPREL) != 0
+ && bfd_link_executable (info)
+ && SYMBOL_REFERENCES_LOCAL (info, h)))
|| (htab->elf.dynamic_sections_created
&& h->dynindx != -1
&& !SYMBOL_REFERENCES_LOCAL (info, h)))
eh = (struct ppc_link_hash_entry *) h;
/* Run through the TLS GD got entries first if we're changing them
to TPREL. */
- if ((eh->tls_mask & TLS_TPRELGD) != 0)
+ if ((eh->tls_mask & (TLS_TLS | TLS_TPRELGD)) == (TLS_TLS | TLS_TPRELGD))
for (gent = h->got.glist; gent != NULL; gent = gent->next)
if (gent->got.refcount > 0
&& (gent->tls_type & TLS_GD) != 0)
&& h->type != STT_GNU_IFUNC)
eh->dyn_relocs = NULL;
+ /* Discard relocs on undefined symbols that must be local. */
+ else if (h->root.type == bfd_link_hash_undefined
+ && ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
+ eh->dyn_relocs = NULL;
+
/* Also discard relocs on undefined weak syms with non-default
visibility, or when dynamic_undefined_weak says so. */
else if (UNDEFWEAK_NO_DYNAMIC_RELOC (info, h))
return FALSE;
}
}
- else if (h->type == STT_GNU_IFUNC)
- {
- /* A plt entry is always created when making direct calls to
- an ifunc, even when building a static executable, but
- that doesn't cover all cases. We may have only an ifunc
- initialised function pointer for a given ifunc symbol.
-
- For ELFv2, dynamic relocations are not required when
- generating a global entry PLT stub. */
- if (abiversion (info->output_bfd) >= 2)
- {
- if (global_entry_stub (h))
- eh->dyn_relocs = NULL;
- }
-
- /* For ELFv1 we have function descriptors. Descriptors need
- to be treated like PLT entries and thus have dynamic
- relocations. One exception is when the function
- descriptor is copied into .dynbss (which should only
- happen with ancient versions of gcc). */
- else if (h->needs_copy)
- eh->dyn_relocs = NULL;
- }
- else if (ELIMINATE_COPY_RELOCS)
+ else if (ELIMINATE_COPY_RELOCS && h->type != STT_GNU_IFUNC)
{
/* For the non-pic case, discard space for relocs against
symbols which turn out to need copy relocs or are not
dynamic. */
- if (!h->non_got_ref
- && !h->def_regular)
+ if (h->dynamic_adjusted
+ && !h->def_regular
+ && !ELF_COMMON_DEF_P (h))
{
/* Make sure this symbol is output as a dynamic symbol. */
if (!ensure_undef_dynamic (info, h))
}
}
- if ((htab->elf.dynamic_sections_created
- && h->dynindx != -1)
- || h->type == STT_GNU_IFUNC)
+ /* We might need a PLT entry when the symbol
+ a) is dynamic, or
+ b) is an ifunc, or
+ c) has plt16 relocs and has been processed by adjust_dynamic_symbol, or
+ d) has plt16 relocs and we are linking statically. */
+ if ((htab->elf.dynamic_sections_created && h->dynindx != -1)
+ || h->type == STT_GNU_IFUNC
+ || (h->needs_plt && h->dynamic_adjusted)
+ || (h->needs_plt
+ && h->def_regular
+ && !htab->elf.dynamic_sections_created
+ && !htab->can_convert_all_inline_plt
+ && (((struct ppc_link_hash_entry *) h)->tls_mask
+ & (TLS_TLS | PLT_KEEP)) == PLT_KEEP))
{
struct plt_entry *pent;
bfd_boolean doneone = FALSE;
if (!htab->elf.dynamic_sections_created
|| h->dynindx == -1)
{
- s = htab->elf.iplt;
- pent->plt.offset = s->size;
- s->size += PLT_ENTRY_SIZE (htab);
- s = htab->elf.irelplt;
+ if (h->type == STT_GNU_IFUNC)
+ {
+ s = htab->elf.iplt;
+ pent->plt.offset = s->size;
+ s->size += PLT_ENTRY_SIZE (htab);
+ s = htab->elf.irelplt;
+ }
+ else
+ {
+ s = htab->pltlocal;
+ pent->plt.offset = s->size;
+ s->size += LOCAL_PLT_ENTRY_SIZE (htab);
+ s = bfd_link_pic (info) ? htab->relpltlocal : NULL;
+ }
}
else
{
/* Make room for the .glink code. */
s = htab->glink;
if (s->size == 0)
- s->size += GLINK_CALL_STUB_SIZE;
+ s->size += GLINK_PLTRESOLVE_SIZE (htab);
if (htab->opd_abi)
{
/* We need bigger stubs past index 32767. */
- if (s->size >= GLINK_CALL_STUB_SIZE + 32768*2*4)
+ if (s->size >= GLINK_PLTRESOLVE_SIZE (htab) + 32768*2*4)
s->size += 4;
s->size += 2*4;
}
/* We also need to make an entry in the .rela.plt section. */
s = htab->elf.srelplt;
}
- s->size += sizeof (Elf64_External_Rela);
+ if (s != NULL)
+ s->size += sizeof (Elf64_External_Rela);
doneone = TRUE;
}
else
return TRUE;
}
+#define PPC_LO(v) ((v) & 0xffff)
+#define PPC_HI(v) (((v) >> 16) & 0xffff)
+#define PPC_HA(v) PPC_HI ((v) + 0x8000)
+
/* 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. */
struct bfd_link_info *info;
struct ppc_link_hash_table *htab;
struct plt_entry *pent;
- asection *s;
+ asection *s, *plt;
if (h->root.type == bfd_link_hash_indirect)
return TRUE;
if (htab == NULL)
return FALSE;
- s = htab->glink;
+ s = htab->global_entry;
+ plt = htab->elf.splt;
for (pent = h->plt.plist; pent != NULL; pent = pent->next)
if (pent->plt.offset != (bfd_vma) -1
&& pent->addend == 0)
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;
+ bfd_vma off, stub_align, stub_off, stub_size;
+ unsigned int align_power;
+
+ stub_size = 16;
+ stub_off = s->size;
+ if (htab->params->plt_stub_align >= 0)
+ align_power = htab->params->plt_stub_align;
+ else
+ align_power = -htab->params->plt_stub_align;
+ /* Setting section alignment is delayed until we know it is
+ non-empty. Otherwise the .text output section will be
+ aligned at least to plt_stub_align even when no global
+ entry stubs are needed. */
+ if (s->alignment_power < align_power)
+ s->alignment_power = align_power;
+ stub_align = (bfd_vma) 1 << align_power;
+ if (htab->params->plt_stub_align >= 0
+ || ((((stub_off + stub_size - 1) & -stub_align)
+ - (stub_off & -stub_align))
+ > ((stub_size - 1) & -stub_align)))
+ stub_off = (stub_off + stub_align - 1) & -stub_align;
+ off = pent->plt.offset + plt->output_offset + plt->output_section->vma;
+ off -= stub_off + s->output_offset + s->output_section->vma;
+ /* Note that for --plt-stub-align negative we have a possible
+ dependency between stub offset and size. Break that
+ dependency by assuming the max stub size when calculating
+ the stub offset. */
+ if (PPC_HA (off) == 0)
+ stub_size -= 4;
h->root.type = bfd_link_hash_defined;
h->root.u.def.section = s;
- h->root.u.def.value = s->size;
- s->size += 16;
+ h->root.u.def.value = stub_off;
+ s->size = stub_off + stub_size;
break;
}
return TRUE;
read-only sections. */
static bfd_boolean
-maybe_set_textrel (struct elf_link_hash_entry *h, void *info)
+maybe_set_textrel (struct elf_link_hash_entry *h, void *inf)
{
+ asection *sec;
+
if (h->root.type == bfd_link_hash_indirect)
return TRUE;
- if (readonly_dynrelocs (h))
+ sec = readonly_dynrelocs (h);
+ if (sec != NULL)
{
- ((struct bfd_link_info *) info)->flags |= DF_TEXTREL;
+ struct bfd_link_info *info = (struct bfd_link_info *) inf;
+
+ info->flags |= DF_TEXTREL;
+ info->callbacks->minfo
+ (_("%pB: dynamic relocation against `%pT' in read-only section `%pA'\n"),
+ sec->owner, h->root.root.string, sec);
/* Not an error, just cut short the traversal. */
return FALSE;
rel_size *= 2;
}
s->size += ent_size;
- if ((*lgot_masks & PLT_IFUNC) != 0)
+ if ((*lgot_masks & (TLS_TLS | PLT_IFUNC)) == PLT_IFUNC)
{
htab->elf.irelplt->size += rel_size;
htab->got_reli_size += rel_size;
}
- else if (bfd_link_pic (info))
+ else if (bfd_link_pic (info)
+ && !((ent->tls_type & TLS_TPREL) != 0
+ && bfd_link_executable (info)))
{
asection *srel = ppc64_elf_tdata (ibfd)->relgot;
srel->size += rel_size;
*pent = ent->next;
}
- /* Allocate space for calls to local STT_GNU_IFUNC syms in .iplt. */
- for (; local_plt < end_local_plt; ++local_plt)
+ /* Allocate space for plt calls to local syms. */
+ lgot_masks = (unsigned char *) end_local_plt;
+ for (; local_plt < end_local_plt; ++local_plt, ++lgot_masks)
{
struct plt_entry *ent;
for (ent = *local_plt; ent != NULL; ent = ent->next)
if (ent->plt.refcount > 0)
{
- s = htab->elf.iplt;
- ent->plt.offset = s->size;
- s->size += PLT_ENTRY_SIZE (htab);
-
- htab->elf.irelplt->size += sizeof (Elf64_External_Rela);
+ if ((*lgot_masks & (TLS_TLS | PLT_IFUNC)) == PLT_IFUNC)
+ {
+ s = htab->elf.iplt;
+ ent->plt.offset = s->size;
+ s->size += PLT_ENTRY_SIZE (htab);
+ htab->elf.irelplt->size += sizeof (Elf64_External_Rela);
+ }
+ else if (htab->can_convert_all_inline_plt
+ || (*lgot_masks & (TLS_TLS | PLT_KEEP)) != PLT_KEEP)
+ ent->plt.offset = (bfd_vma) -1;
+ else
+ {
+ s = htab->pltlocal;
+ ent->plt.offset = s->size;
+ s->size += LOCAL_PLT_ENTRY_SIZE (htab);
+ if (bfd_link_pic (info))
+ htab->relpltlocal->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 && !bfd_link_pic (info))
elf_link_hash_traverse (&htab->elf, size_global_entry_stubs, info);
else if (s == htab->elf.sgot
|| s == htab->elf.splt
|| s == htab->elf.iplt
+ || s == htab->pltlocal
|| s == htab->glink
+ || s == htab->global_entry
|| s == htab->elf.sdynbss
|| s == htab->elf.sdynrelro)
{
continue;
}
+ if (bfd_is_abs_section (s->output_section))
+ _bfd_error_handler (_("warning: discarding dynamic section %s"),
+ s->name);
+
if ((s->flags & SEC_HAS_CONTENTS) == 0)
continue;
/* Determine if a long branch stub is needed. */
max_branch_offset = 1 << 25;
- if (r_type != R_PPC64_REL24)
+ if (r_type == R_PPC64_REL14
+ || r_type == R_PPC64_REL14_BRTAKEN
+ || r_type == R_PPC64_REL14_BRNTAKEN)
max_branch_offset = 1 << 15;
if (branch_offset + max_branch_offset >= 2 * max_branch_offset - local_off)
#define ALWAYS_USE_FAKE_DEP 0
#define ALWAYS_EMIT_R2SAVE 0
-#define PPC_LO(v) ((v) & 0xffff)
-#define PPC_HI(v) (((v) >> 16) & 0xffff)
-#define PPC_HA(v) PPC_HI ((v) + 0x8000)
-
static inline unsigned int
plt_stub_size (struct ppc_link_hash_table *htab,
struct ppc_stub_hash_entry *stub_entry,
return size;
}
-/* If this stub would cross fewer 2**plt_stub_align boundaries if we align,
- then return the padding needed to do so. */
+/* Depending on the sign of plt_stub_align:
+ If positive, return the padding to align to a 2**plt_stub_align
+ boundary.
+ If negative, if this stub would cross fewer 2**plt_stub_align
+ boundaries if we align, then return the padding needed to do so. */
+
static inline unsigned int
plt_stub_pad (struct ppc_link_hash_table *htab,
struct ppc_stub_hash_entry *stub_entry,
bfd_vma plt_off)
{
- int stub_align = 1 << htab->params->plt_stub_align;
+ int stub_align;
unsigned stub_size = plt_stub_size (htab, stub_entry, plt_off);
bfd_vma stub_off = stub_entry->group->stub_sec->size;
+ if (htab->params->plt_stub_align >= 0)
+ {
+ stub_align = 1 << htab->params->plt_stub_align;
+ if ((stub_off & (stub_align - 1)) != 0)
+ return stub_align - (stub_off & (stub_align - 1));
+ return 0;
+ }
+
+ stub_align = 1 << -htab->params->plt_stub_align;
if (((stub_off + stub_size - 1) & -stub_align) - (stub_off & -stub_align)
> ((stub_size - 1) & -stub_align))
return stub_align - (stub_off & (stub_align - 1));
bfd_vma pltoff = stub_entry->plt_ent->plt.offset & ~1;
bfd_vma pltindex = ((pltoff - PLT_INITIAL_ENTRY_SIZE (htab))
/ PLT_ENTRY_SIZE (htab));
- bfd_vma glinkoff = GLINK_CALL_STUB_SIZE + pltindex * 8;
+ bfd_vma glinkoff = GLINK_PLTRESOLVE_SIZE (htab) + pltindex * 8;
bfd_vma to, from;
if (pltindex > 32768)
if (strcmp (opd->name, ".opd") != 0
|| opd->reloc_count != 0)
{
- info->callbacks->einfo (_("%P: cannot find opd entry toc for `%T'\n"),
+ info->callbacks->einfo (_("%P: cannot find opd entry toc for `%pT'\n"),
stub_entry->h->elf.root.root.string);
bfd_set_error (bfd_error_bad_value);
return (bfd_vma) -1;
bfd_byte *loc;
bfd_byte *p;
bfd_vma dest, off;
- int size;
Elf_Internal_Rela *r;
asection *plt;
+ stub_entry->group->stub_sec->output_offset
+ stub_entry->group->stub_sec->output_section->vma);
- size = 4;
+ p = loc;
if (stub_entry->stub_type == ppc_stub_long_branch_r2off)
{
bfd_vma r2off = get_r2off (info, stub_entry);
htab->stub_error = TRUE;
return FALSE;
}
- bfd_put_32 (htab->params->stub_bfd, STD_R2_0R1 + STK_TOC (htab), loc);
- loc += 4;
- size = 8;
+ bfd_put_32 (htab->params->stub_bfd, STD_R2_0R1 + STK_TOC (htab), p);
+ p += 4;
if (PPC_HA (r2off) != 0)
{
bfd_put_32 (htab->params->stub_bfd,
- ADDIS_R2_R2 | PPC_HA (r2off), loc);
- loc += 4;
- size += 4;
+ ADDIS_R2_R2 | PPC_HA (r2off), p);
+ p += 4;
}
if (PPC_LO (r2off) != 0)
{
bfd_put_32 (htab->params->stub_bfd,
- ADDI_R2_R2 | PPC_LO (r2off), loc);
- loc += 4;
- size += 4;
+ ADDI_R2_R2 | PPC_LO (r2off), p);
+ p += 4;
}
- off -= size - 4;
+ off -= p - loc;
}
- bfd_put_32 (htab->params->stub_bfd, B_DOT | (off & 0x3fffffc), loc);
+ bfd_put_32 (htab->params->stub_bfd, B_DOT | (off & 0x3fffffc), p);
+ p += 4;
if (off + (1 << 25) >= (bfd_vma) (1 << 26))
{
- info->callbacks->einfo
- (_("%P: long branch stub `%s' offset overflow\n"),
+ _bfd_error_handler
+ (_("long branch stub `%s' offset overflow"),
stub_entry->root.string);
htab->stub_error = TRUE;
return FALSE;
r = get_relocs (stub_entry->group->stub_sec, 1);
if (r == NULL)
return FALSE;
- r->r_offset = loc - stub_entry->group->stub_sec->contents;
+ r->r_offset = p - 4 - stub_entry->group->stub_sec->contents;
r->r_info = ELF64_R_INFO (0, R_PPC64_REL24);
r->r_addend = dest;
if (stub_entry->h != NULL)
FALSE, FALSE);
if (br_entry == NULL)
{
- info->callbacks->einfo (_("%P: can't find branch stub `%s'\n"),
- stub_entry->root.string);
+ _bfd_error_handler (_("can't find branch stub `%s'"),
+ stub_entry->root.string);
htab->stub_error = TRUE;
return FALSE;
}
+ htab->brlt->output_section->vma);
off = (dest
- - elf_gp (htab->brlt->output_section->owner)
+ - elf_gp (info->output_bfd)
- htab->sec_info[stub_entry->group->link_sec->id].toc_off);
if (off + 0x80008000 > 0xffffffff || (off & 7) != 0)
{
info->callbacks->einfo
- (_("%P: linkage table error against `%T'\n"),
+ (_("%P: linkage table error against `%pT'\n"),
stub_entry->root.string);
bfd_set_error (bfd_error_bad_value);
htab->stub_error = TRUE;
}
}
+ p = loc;
if (stub_entry->stub_type != ppc_stub_plt_branch_r2off)
{
if (PPC_HA (off) != 0)
{
- size = 16;
bfd_put_32 (htab->params->stub_bfd,
- ADDIS_R12_R2 | PPC_HA (off), loc);
- loc += 4;
+ ADDIS_R12_R2 | PPC_HA (off), p);
+ p += 4;
bfd_put_32 (htab->params->stub_bfd,
- LD_R12_0R12 | PPC_LO (off), loc);
+ LD_R12_0R12 | PPC_LO (off), p);
}
else
- {
- size = 12;
- bfd_put_32 (htab->params->stub_bfd,
- LD_R12_0R2 | PPC_LO (off), loc);
- }
+ bfd_put_32 (htab->params->stub_bfd,
+ LD_R12_0R2 | PPC_LO (off), p);
}
else
{
return FALSE;
}
- bfd_put_32 (htab->params->stub_bfd, STD_R2_0R1 + STK_TOC (htab), loc);
- loc += 4;
- size = 16;
+ bfd_put_32 (htab->params->stub_bfd, STD_R2_0R1 + STK_TOC (htab), p);
+ p += 4;
if (PPC_HA (off) != 0)
{
- size += 4;
bfd_put_32 (htab->params->stub_bfd,
- ADDIS_R12_R2 | PPC_HA (off), loc);
- loc += 4;
+ ADDIS_R12_R2 | PPC_HA (off), p);
+ p += 4;
bfd_put_32 (htab->params->stub_bfd,
- LD_R12_0R12 | PPC_LO (off), loc);
+ LD_R12_0R12 | PPC_LO (off), p);
}
else
- bfd_put_32 (htab->params->stub_bfd, LD_R12_0R2 | PPC_LO (off), loc);
+ bfd_put_32 (htab->params->stub_bfd, LD_R12_0R2 | PPC_LO (off), p);
if (PPC_HA (r2off) != 0)
{
- size += 4;
- loc += 4;
+ p += 4;
bfd_put_32 (htab->params->stub_bfd,
- ADDIS_R2_R2 | PPC_HA (r2off), loc);
+ ADDIS_R2_R2 | PPC_HA (r2off), p);
}
if (PPC_LO (r2off) != 0)
{
- size += 4;
- loc += 4;
+ p += 4;
bfd_put_32 (htab->params->stub_bfd,
- ADDI_R2_R2 | PPC_LO (r2off), loc);
+ ADDI_R2_R2 | PPC_LO (r2off), p);
}
}
- loc += 4;
- bfd_put_32 (htab->params->stub_bfd, MTCTR_R12, loc);
- loc += 4;
- bfd_put_32 (htab->params->stub_bfd, BCTR, loc);
+ p += 4;
+ bfd_put_32 (htab->params->stub_bfd, MTCTR_R12, p);
+ p += 4;
+ bfd_put_32 (htab->params->stub_bfd, BCTR, p);
+ p += 4;
break;
case ppc_stub_plt_call:
if (!htab->elf.dynamic_sections_created
|| stub_entry->h == NULL
|| stub_entry->h->elf.dynindx == -1)
- plt = htab->elf.iplt;
-
- dest += plt->output_offset + plt->output_section->vma;
-
- if (stub_entry->h == NULL
- && (stub_entry->plt_ent->plt.offset & 1) == 0)
{
- Elf_Internal_Rela rela;
- bfd_byte *rl;
-
- rela.r_offset = dest;
- if (htab->opd_abi)
- rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_IREL);
+ if (stub_entry->symtype == STT_GNU_IFUNC)
+ plt = htab->elf.iplt;
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->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;
- htab->local_ifunc_resolver = 1;
+ plt = htab->pltlocal;
}
+ dest += plt->output_offset + plt->output_section->vma;
+
off = (dest
- - elf_gp (plt->output_section->owner)
+ - elf_gp (info->output_bfd)
- htab->sec_info[stub_entry->group->link_sec->id].toc_off);
if (off + 0x80008000 > 0xffffffff || (off & 7) != 0)
{
info->callbacks->einfo
/* xgettext:c-format */
- (_("%P: linkage table error against `%T'\n"),
+ (_("%P: linkage table error against `%pT'\n"),
stub_entry->h != NULL
? stub_entry->h->elf.root.root.string
: "<local sym>");
p = build_tls_get_addr_stub (htab, stub_entry, loc, off, r);
else
p = build_plt_stub (htab, stub_entry, loc, off, r);
- size = p - loc;
break;
case ppc_stub_save_res:
return FALSE;
}
- stub_entry->group->stub_sec->size += size;
+ stub_entry->group->stub_sec->size += p - loc;
if (htab->params->emit_stub_syms)
{
if (!htab->elf.dynamic_sections_created
|| stub_entry->h == NULL
|| stub_entry->h->elf.dynindx == -1)
- plt = htab->elf.iplt;
+ {
+ if (stub_entry->symtype == STT_GNU_IFUNC)
+ plt = htab->elf.iplt;
+ else
+ plt = htab->pltlocal;
+ }
off += (plt->output_offset
+ plt->output_section->vma
- - elf_gp (plt->output_section->owner)
+ - elf_gp (info->output_bfd)
- htab->sec_info[stub_entry->group->link_sec->id].toc_off);
size = plt_stub_size (htab, stub_entry, off);
local_off = PPC64_LOCAL_ENTRY_OFFSET (stub_entry->other);
- /* If the branch offset if too big, use a ppc_stub_plt_branch.
+ /* If the branch offset is 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
TRUE, FALSE);
if (br_entry == NULL)
{
- info->callbacks->einfo (_("%P: can't build branch stub `%s'\n"),
- stub_entry->root.string);
+ _bfd_error_handler (_("can't build branch stub `%s'"),
+ stub_entry->root.string);
htab->stub_error = TRUE;
return FALSE;
}
off = (br_entry->offset
+ htab->brlt->output_offset
+ htab->brlt->output_section->vma
- - elf_gp (htab->brlt->output_section->owner)
+ - elf_gp (info->output_bfd)
- htab->sec_info[stub_entry->group->link_sec->id].toc_off);
if (info->emitrelocations)
if (htab == NULL)
return -1;
- htab->sec_info_arr_size = bfd_get_next_section_id ();
+ htab->sec_info_arr_size = _bfd_section_id;
amt = sizeof (*htab->sec_info) * (htab->sec_info_arr_size);
htab->sec_info = bfd_zmalloc (amt);
if (htab->sec_info == NULL)
output toc base plus 0x8000. Making the input elf_gp an
offset allows us to move the toc as a whole without
recalculating input elf_gp. */
- off = htab->toc_curr - elf_gp (isec->output_section->owner);
+ off = htab->toc_curr - elf_gp (info->output_bfd);
off += TOC_BASE_OFF;
/* Die if someone uses a linker script that doesn't keep input
}
addr = (htab->toc_first_sec->output_offset
+ htab->toc_first_sec->output_section->vma);
- off = addr - elf_gp (isec->output_section->owner) + TOC_BASE_OFF;
+ off = addr - elf_gp (info->output_bfd) + TOC_BASE_OFF;
elf_gp (isec->owner) = off;
return TRUE;
rel_size *= 2;
}
s->size += ent_size;
- if ((*lgot_masks & PLT_IFUNC) != 0)
+ if ((*lgot_masks & (TLS_TLS | PLT_IFUNC)) == PLT_IFUNC)
{
htab->elf.irelplt->size += rel_size;
htab->got_reli_size += rel_size;
}
- else if (bfd_link_pic (info))
+ else if (bfd_link_pic (info)
+ && !((ent->tls_type & TLS_TPREL) != 0
+ && bfd_link_executable (info)))
{
asection *srel = ppc64_elf_tdata (ibfd)->relgot;
srel->size += rel_size;
if (r_type != R_PPC64_REL24
&& r_type != R_PPC64_REL14
&& r_type != R_PPC64_REL14_BRTAKEN
- && r_type != R_PPC64_REL14_BRNTAKEN)
+ && r_type != R_PPC64_REL14_BRNTAKEN
+ && r_type != R_PPC64_PLTCALL)
continue;
r_symndx = ELF64_R_SYM (rel->r_info);
big_sec = total > group_size;
if (big_sec && !suppress_size_errors)
/* xgettext:c-format */
- _bfd_error_handler (_("%B section %A exceeds stub group size"),
+ _bfd_error_handler (_("%pB section %pA exceeds stub group size"),
tail->owner, tail);
curr_toc = htab->sec_info[tail->id].toc_off;
if (!get_tls_mask (&tls_mask, NULL, NULL, &local_syms,
irela - 1, input_bfd))
goto error_ret_free_internal;
- if (*tls_mask != 0)
+ if ((*tls_mask & TLS_TLS) != 0)
continue;
}
}
stub_entry->h = hash;
stub_entry->plt_ent = plt_ent;
+ stub_entry->symtype
+ = hash ? hash->elf.type : ELF_ST_TYPE (sym->st_info);
stub_entry->other = hash ? hash->elf.other : sym->st_other;
if (stub_entry->h != NULL)
stub_sec->flags &= ~SEC_RELOC;
}
+ if (htab->stub_iteration <= STUB_SHRINK_ITER
+ || htab->brlt->rawsize < htab->brlt->size)
+ htab->brlt->rawsize = htab->brlt->size;
htab->brlt->size = 0;
htab->brlt->reloc_count = 0;
htab->brlt->flags &= ~SEC_RELOC;
if (htab->glink_eh_frame != NULL
&& !bfd_is_abs_section (htab->glink_eh_frame->output_section)
- && htab->glink_eh_frame->output_section->size != 0)
+ && htab->glink_eh_frame->output_section->size > 8)
{
size_t size = 0, align = 4;
if (htab->params->plt_stub_align != 0)
for (group = htab->group; group != NULL; group = group->next)
if (group->stub_sec != NULL)
- group->stub_sec->size = ((group->stub_sec->size
- + (1 << htab->params->plt_stub_align) - 1)
- & -(1 << htab->params->plt_stub_align));
+ {
+ int align = abs (htab->params->plt_stub_align);
+ group->stub_sec->size
+ = (group->stub_sec->size + (1 << align) - 1) & -(1 << align);
+ }
for (group = htab->group; group != NULL; group = group->next)
if (group->stub_sec != NULL
break;
if (group == NULL
+ && (htab->brlt->rawsize == htab->brlt->size
+ || (htab->stub_iteration > STUB_SHRINK_ITER
+ && htab->brlt->rawsize > htab->brlt->size))
&& (htab->glink_eh_frame == NULL
|| htab->glink_eh_frame->rawsize == htab->glink_eh_frame->size))
break;
}
/* Called via elf_link_hash_traverse from ppc64_elf_build_stubs to
- write out any global entry stubs. */
+ write out any global entry stubs, and PLT relocations. */
+
+static bfd_boolean
+build_global_entry_stubs_and_plt (struct elf_link_hash_entry *h, void *inf)
+{
+ struct bfd_link_info *info;
+ struct ppc_link_hash_table *htab;
+ struct plt_entry *ent;
+ asection *s;
+
+ if (h->root.type == bfd_link_hash_indirect)
+ return TRUE;
+
+ info = inf;
+ htab = ppc_hash_table (info);
+ if (htab == NULL)
+ return FALSE;
+
+ for (ent = h->plt.plist; ent != NULL; ent = ent->next)
+ if (ent->plt.offset != (bfd_vma) -1)
+ {
+ /* This symbol has an entry in the procedure linkage
+ table. Set it up. */
+ Elf_Internal_Rela rela;
+ asection *plt, *relplt;
+ bfd_byte *loc;
-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 (!htab->elf.dynamic_sections_created
+ || h->dynindx == -1)
+ {
+ if (!(h->def_regular
+ && (h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)))
+ continue;
+ if (h->type == STT_GNU_IFUNC)
+ {
+ plt = htab->elf.iplt;
+ relplt = htab->elf.irelplt;
+ htab->local_ifunc_resolver = 1;
+ 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);
+ }
+ else
+ {
+ plt = htab->pltlocal;
+ if (bfd_link_pic (info))
+ {
+ relplt = htab->relpltlocal;
+ if (htab->opd_abi)
+ rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_SLOT);
+ else
+ rela.r_info = ELF64_R_INFO (0, R_PPC64_RELATIVE);
+ }
+ else
+ relplt = NULL;
+ }
+ 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);
- if (h->root.type == bfd_link_hash_indirect)
- return TRUE;
+ if (relplt == NULL)
+ {
+ loc = plt->contents + ent->plt.offset;
+ bfd_put_64 (info->output_bfd, rela.r_addend, loc);
+ if (htab->opd_abi)
+ {
+ bfd_vma toc = elf_gp (info->output_bfd);
+ toc += htab->sec_info[h->root.u.def.section->id].toc_off;
+ bfd_put_64 (info->output_bfd, toc, loc + 8);
+ }
+ }
+ else
+ {
+ rela.r_offset = (plt->output_section->vma
+ + plt->output_offset
+ + ent->plt.offset);
+ loc = relplt->contents + (relplt->reloc_count++
+ * sizeof (Elf64_External_Rela));
+ bfd_elf64_swap_reloca_out (info->output_bfd, &rela, loc);
+ }
+ }
+ else
+ {
+ 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->elf.srelplt->contents
+ + ((ent->plt.offset - PLT_INITIAL_ENTRY_SIZE (htab))
+ / PLT_ENTRY_SIZE (htab) * sizeof (Elf64_External_Rela)));
+ if (h->type == STT_GNU_IFUNC && is_static_defined (h))
+ htab->maybe_local_ifunc_resolver = 1;
+ bfd_elf64_swap_reloca_out (info->output_bfd, &rela, loc);
+ }
+ }
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->global_entry;
+ if (s == NULL || s->size == 0)
+ return TRUE;
- s = htab->glink;
- for (pent = h->plt.plist; pent != NULL; pent = pent->next)
- if (pent->plt.offset != (bfd_vma) -1
- && pent->addend == 0)
+ for (ent = h->plt.plist; ent != NULL; ent = ent->next)
+ if (ent->plt.offset != (bfd_vma) -1
+ && ent->addend == 0)
{
bfd_byte *p;
asection *plt;
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;
+ {
+ if (h->type == STT_GNU_IFUNC)
+ plt = htab->elf.iplt;
+ else
+ plt = htab->pltlocal;
+ }
+ off = ent->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"),
+ (_("%P: linkage table error against `%pT'\n"),
h->root.root.string);
bfd_set_error (bfd_error_bad_value);
htab->stub_error = TRUE;
return TRUE;
}
+/* Write PLT relocs for locals. */
+
+static bfd_boolean
+write_plt_relocs_for_local_syms (struct bfd_link_info *info)
+{
+ struct ppc_link_hash_table *htab = ppc_hash_table (info);
+ bfd *ibfd;
+
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+ {
+ struct got_entry **lgot_ents, **end_lgot_ents;
+ struct plt_entry **local_plt, **lplt, **end_local_plt;
+ Elf_Internal_Shdr *symtab_hdr;
+ bfd_size_type locsymcount;
+ Elf_Internal_Sym *local_syms = NULL;
+ struct plt_entry *ent;
+
+ if (!is_ppc64_elf (ibfd))
+ continue;
+
+ lgot_ents = elf_local_got_ents (ibfd);
+ if (!lgot_ents)
+ continue;
+
+ symtab_hdr = &elf_symtab_hdr (ibfd);
+ locsymcount = symtab_hdr->sh_info;
+ end_lgot_ents = lgot_ents + locsymcount;
+ local_plt = (struct plt_entry **) end_lgot_ents;
+ end_local_plt = local_plt + locsymcount;
+ for (lplt = local_plt; lplt < end_local_plt; ++lplt)
+ for (ent = *lplt; ent != NULL; ent = ent->next)
+ if (ent->plt.offset != (bfd_vma) -1)
+ {
+ Elf_Internal_Sym *sym;
+ asection *sym_sec;
+ asection *plt, *relplt;
+ bfd_byte *loc;
+ bfd_vma val;
+
+ if (!get_sym_h (NULL, &sym, &sym_sec, NULL, &local_syms,
+ lplt - local_plt, ibfd))
+ {
+ if (local_syms != NULL
+ && symtab_hdr->contents != (unsigned char *) local_syms)
+ free (local_syms);
+ return FALSE;
+ }
+
+ val = sym->st_value + ent->addend;
+ val += PPC64_LOCAL_ENTRY_OFFSET (sym->st_other);
+ if (sym_sec != NULL && sym_sec->output_section != NULL)
+ val += sym_sec->output_offset + sym_sec->output_section->vma;
+
+ if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+ {
+ htab->local_ifunc_resolver = 1;
+ plt = htab->elf.iplt;
+ relplt = htab->elf.irelplt;
+ }
+ else
+ {
+ plt = htab->pltlocal;
+ relplt = bfd_link_pic (info) ? htab->relpltlocal : NULL;
+ }
+
+ if (relplt == NULL)
+ {
+ loc = plt->contents + ent->plt.offset;
+ bfd_put_64 (info->output_bfd, val, loc);
+ if (htab->opd_abi)
+ {
+ bfd_vma toc = elf_gp (ibfd);
+ bfd_put_64 (info->output_bfd, toc, loc + 8);
+ }
+ }
+ else
+ {
+ Elf_Internal_Rela rela;
+ rela.r_offset = (ent->plt.offset
+ + plt->output_offset
+ + plt->output_section->vma);
+ if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+ {
+ 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);
+ }
+ else
+ {
+ if (htab->opd_abi)
+ rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_SLOT);
+ else
+ rela.r_info = ELF64_R_INFO (0, R_PPC64_RELATIVE);
+ }
+ rela.r_addend = val;
+ loc = relplt->contents + (relplt->reloc_count++
+ * sizeof (Elf64_External_Rela));
+ bfd_elf64_swap_reloca_out (info->output_bfd, &rela, loc);
+ }
+ }
+
+ if (local_syms != NULL
+ && symtab_hdr->contents != (unsigned char *) local_syms)
+ {
+ if (!info->keep_memory)
+ free (local_syms);
+ else
+ symtab_hdr->contents = (unsigned char *) local_syms;
+ }
+ }
+ 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_put_32 (htab->glink->owner, BCTR, p);
p += 4;
- while (p - htab->glink->contents < GLINK_CALL_STUB_SIZE)
- {
- bfd_put_32 (htab->glink->owner, NOP, p);
- p += 4;
- }
+ BFD_ASSERT (p == htab->glink->contents + GLINK_PLTRESOLVE_SIZE (htab));
/* Build the .glink lazy link call stubs. */
indx = 0;
- while (p < htab->glink->contents + htab->glink->rawsize)
+ while (p < htab->glink->contents + htab->glink->size)
{
if (htab->opd_abi)
{
indx++;
p += 4;
}
-
- /* Build .glink global entry stubs. */
- if (htab->glink->size > htab->glink->rawsize)
- elf_link_hash_traverse (&htab->elf, build_global_entry_stubs, info);
}
+ /* Build .glink global entry stubs, and PLT relocs for globals. */
+ elf_link_hash_traverse (&htab->elf, build_global_entry_stubs_and_plt, info);
+
+ if (!write_plt_relocs_for_local_syms (info))
+ return FALSE;
+
if (htab->brlt != NULL && htab->brlt->size != 0)
{
htab->brlt->contents = bfd_zalloc (htab->brlt->owner,
/* Build the stubs as directed by the stub hash table. */
bfd_hash_traverse (&htab->stub_hash_table, ppc_build_one_stub, info);
+ for (group = htab->group; group != NULL; group = group->next)
+ if (group->needs_save_res)
+ group->stub_sec->size += htab->sfpr->size;
+
+ if (htab->relbrlt != NULL)
+ htab->relbrlt->reloc_count = 0;
+
+ if (htab->params->plt_stub_align != 0)
+ for (group = htab->group; group != NULL; group = group->next)
+ if ((stub_sec = group->stub_sec) != NULL)
+ {
+ int align = abs (htab->params->plt_stub_align);
+ stub_sec->size = (stub_sec->size + (1 << align) - 1) & -(1 << align);
+ }
+
for (group = htab->group; group != NULL; group = group->next)
if (group->needs_save_res)
{
stub_sec = group->stub_sec;
- memcpy (stub_sec->contents + stub_sec->size, htab->sfpr->contents,
- htab->sfpr->size);
+ memcpy (stub_sec->contents + stub_sec->size - htab->sfpr->size,
+ htab->sfpr->contents, htab->sfpr->size);
if (htab->params->emit_stub_syms)
{
unsigned int i;
if (!sfpr_define (info, &save_res_funcs[i], stub_sec))
return FALSE;
}
- stub_sec->size += htab->sfpr->size;
}
- if (htab->relbrlt != NULL)
- htab->relbrlt->reloc_count = 0;
-
- if (htab->params->plt_stub_align != 0)
- for (group = htab->group; group != NULL; group = group->next)
- if ((stub_sec = group->stub_sec) != NULL)
- stub_sec->size = ((stub_sec->size
- + (1 << htab->params->plt_stub_align) - 1)
- & -(1 << htab->params->plt_stub_align));
-
for (group = htab->group; group != NULL; group = group->next)
if ((stub_sec = group->stub_sec) != NULL)
{
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 (group != NULL
- || (htab->glink_eh_frame != NULL
- && htab->glink_eh_frame->rawsize != htab->glink_eh_frame->size))
+ if (group != NULL)
{
htab->stub_error = TRUE;
- info->callbacks->einfo (_("%P: stubs don't match calculated size\n"));
+ _bfd_error_handler (_("stubs don't match calculated size"));
}
if (htab->stub_error)
if (stats != NULL)
{
+ size_t len;
*stats = bfd_malloc (500);
if (*stats == NULL)
return FALSE;
- sprintf (*stats, _("linker stubs in %u group%s\n"
- " branch %lu\n"
- " toc adjust %lu\n"
- " long branch %lu\n"
- " long toc adj %lu\n"
- " plt call %lu\n"
- " plt call toc %lu\n"
- " global entry %lu"),
- stub_sec_count,
- stub_sec_count == 1 ? "" : "s",
+ len = sprintf (*stats,
+ ngettext ("linker stubs in %u group\n",
+ "linker stubs in %u groups\n",
+ stub_sec_count),
+ stub_sec_count);
+ sprintf (*stats + len, _(" branch %lu\n"
+ " toc adjust %lu\n"
+ " long branch %lu\n"
+ " long toc adj %lu\n"
+ " plt call %lu\n"
+ " plt call toc %lu\n"
+ " global entry %lu"),
htab->stub_count[ppc_stub_long_branch - 1],
htab->stub_count[ppc_stub_long_branch_r2off - 1],
htab->stub_count[ppc_stub_plt_branch - 1],
unsigned char tls_mask, tls_gd, tls_type;
unsigned char sym_type;
bfd_vma relocation;
- bfd_boolean unresolved_reloc;
+ bfd_boolean unresolved_reloc, save_unresolved_reloc;
bfd_boolean warned;
enum { DEST_NORMAL, DEST_OPD, DEST_STUB } reloc_dest;
unsigned int insn;
wrel->r_addend = 0;
/* For ld -r, remove relocations in debug sections against
- sections defined in discarded sections. Not done for
+ symbols defined in discarded sections. Not done for
non-debug to preserve relocs in .eh_frame which the
eh_frame editing code expects to be present. */
if (bfd_link_relocatable (info)
(local_plt + symtab_hdr->sh_info);
tls_mask = lgot_masks[r_symndx];
}
- if (tls_mask == 0
+ if (((tls_mask & TLS_TLS) == 0 || tls_mask == (TLS_TLS | TLS_MARK))
&& (r_type == R_PPC64_TLS
|| r_type == R_PPC64_TLSGD
|| r_type == R_PPC64_TLSLD))
|| (sym_type == STT_SECTION
&& (sec->flags & SEC_THREAD_LOCAL) != 0))))
{
- if (tls_mask != 0
+ if ((tls_mask & TLS_TLS) != 0
&& (r_type == R_PPC64_TLS
|| r_type == R_PPC64_TLSGD
|| r_type == R_PPC64_TLSLD))
info->callbacks->einfo
(!IS_PPC64_TLS_RELOC (r_type)
/* xgettext:c-format */
- ? _("%H: %s used with TLS symbol `%T'\n")
+ ? _("%H: %s used with TLS symbol `%pT'\n")
/* xgettext:c-format */
- : _("%H: %s used with non-TLS symbol `%T'\n"),
+ : _("%H: %s used with non-TLS symbol `%pT'\n"),
input_bfd, input_section, rel->r_offset,
ppc64_elf_howto_table[r_type]->name,
sym_name);
if (r_type == R_PPC64_TOC16_DS
|| r_type == R_PPC64_TOC16_LO_DS)
{
- if (tls_mask != 0
+ if ((tls_mask & TLS_TLS) != 0
&& (tls_mask & (TLS_DTPREL | TLS_TPREL)) == 0)
goto toctprel;
}
if (retval == 2)
{
tls_gd = TLS_TPRELGD;
- if (tls_mask != 0 && (tls_mask & TLS_GD) == 0)
+ if ((tls_mask & TLS_TLS) != 0
+ && (tls_mask & TLS_GD) == 0)
goto tls_ldgd_opt;
}
else if (retval == 3)
{
- if (tls_mask != 0 && (tls_mask & TLS_LD) == 0)
+ if ((tls_mask & TLS_TLS) != 0
+ && (tls_mask & TLS_LD) == 0)
goto tls_ldgd_opt;
}
}
case R_PPC64_GOT_TPREL16_HI:
case R_PPC64_GOT_TPREL16_HA:
- if (tls_mask != 0
+ if ((tls_mask & TLS_TLS) != 0
&& (tls_mask & TLS_TPREL) == 0)
{
rel->r_offset -= d_offset;
case R_PPC64_GOT_TPREL16_DS:
case R_PPC64_GOT_TPREL16_LO_DS:
- if (tls_mask != 0
+ if ((tls_mask & TLS_TLS) != 0
&& (tls_mask & TLS_TPREL) == 0)
{
toctprel:
break;
case R_PPC64_TLS:
- if (tls_mask != 0
+ if ((tls_mask & TLS_TLS) != 0
&& (tls_mask & TLS_TPREL) == 0)
{
insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
case R_PPC64_GOT_TLSGD16_HI:
case R_PPC64_GOT_TLSGD16_HA:
tls_gd = TLS_TPRELGD;
- if (tls_mask != 0 && (tls_mask & TLS_GD) == 0)
+ if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0)
goto tls_gdld_hi;
break;
case R_PPC64_GOT_TLSLD16_HI:
case R_PPC64_GOT_TLSLD16_HA:
- if (tls_mask != 0 && (tls_mask & TLS_LD) == 0)
+ if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0)
{
tls_gdld_hi:
if ((tls_mask & tls_gd) != 0)
case R_PPC64_GOT_TLSGD16:
case R_PPC64_GOT_TLSGD16_LO:
tls_gd = TLS_TPRELGD;
- if (tls_mask != 0 && (tls_mask & TLS_GD) == 0)
+ if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0)
goto tls_ldgd_opt;
break;
case R_PPC64_GOT_TLSLD16:
case R_PPC64_GOT_TLSLD16_LO:
- if (tls_mask != 0 && (tls_mask & TLS_LD) == 0)
+ if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0)
{
unsigned int insn1, insn2;
bfd_vma offset;
break;
case R_PPC64_TLSGD:
- if (tls_mask != 0 && (tls_mask & TLS_GD) == 0)
+ if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0
+ && rel + 1 < relend)
{
unsigned int insn2;
bfd_vma offset = rel->r_offset;
+ if (is_plt_seq_reloc (ELF64_R_TYPE (rel[1].r_info)))
+ {
+ bfd_put_32 (output_bfd, NOP, contents + offset);
+ rel[1].r_info = ELF64_R_INFO (STN_UNDEF, R_PPC64_NONE);
+ break;
+ }
+
+ if (ELF64_R_TYPE (rel[1].r_info) == R_PPC64_PLTCALL)
+ bfd_put_32 (output_bfd, NOP, contents + offset + 4);
+
if ((tls_mask & TLS_TPRELGD) != 0)
{
/* IE */
break;
case R_PPC64_TLSLD:
- if (tls_mask != 0 && (tls_mask & TLS_LD) == 0)
+ if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0
+ && rel + 1 < relend)
{
unsigned int insn2;
bfd_vma offset = rel->r_offset;
+ if (is_plt_seq_reloc (ELF64_R_TYPE (rel[1].r_info)))
+ {
+ bfd_put_32 (output_bfd, NOP, contents + offset);
+ rel[1].r_info = ELF64_R_INFO (STN_UNDEF, R_PPC64_NONE);
+ break;
+ }
+
+ if (ELF64_R_TYPE (rel[1].r_info) == R_PPC64_PLTCALL)
+ bfd_put_32 (output_bfd, NOP, contents + offset + 4);
+
if (toc_symndx)
sec = local_sections[toc_symndx];
for (r_symndx = 0;
/* Fall through. */
case R_PPC64_REL24:
+ case R_PPC64_PLTCALL:
/* Calls to functions with a different TOC, such as calls to
shared objects, need to alter the TOC pointer. This is
done using a linkage stub. A REL24 branching to these
fdh = ppc_follow_link (h->oh);
stub_entry = ppc_get_stub_entry (input_section, sec, fdh, &orig_rel,
htab);
+ if (r_type == R_PPC64_PLTCALL
+ && stub_entry != NULL
+ && (stub_entry->stub_type == ppc_stub_plt_call
+ || stub_entry->stub_type == ppc_stub_plt_call_r2save))
+ stub_entry = NULL;
+
if (stub_entry != NULL
&& (stub_entry->stub_type == ppc_stub_plt_call
|| stub_entry->stub_type == ppc_stub_plt_call_r2save
nop = bfd_get_32 (input_bfd,
contents + rel->r_offset + 4);
- if (nop == NOP
- || nop == CROR_151515 || nop == CROR_313131)
+ if (nop == LD_R2_0R1 + STK_TOC (htab))
+ can_plt_call = TRUE;
+ else if (nop == NOP
+ || nop == CROR_151515
+ || nop == CROR_313131)
{
if (h != NULL
&& (h == htab->tls_get_addr_fd
|| stub_entry->stub_type == ppc_stub_plt_call_r2save)
info->callbacks->einfo
/* xgettext:c-format */
- (_("%H: call to `%T' lacks nop, can't restore toc; "
+ (_("%H: call to `%pT' lacks nop, can't restore toc; "
"recompile with -fPIC\n"),
input_bfd, input_section, rel->r_offset, sym_name);
else
info->callbacks->einfo
/* xgettext:c-format */
- (_("%H: call to `%T' lacks nop, can't restore toc; "
+ (_("%H: call to `%pT' lacks nop, can't restore toc; "
"(-mcmodel=small toc adjust stub)\n"),
input_bfd, input_section, rel->r_offset, sym_name);
/* Set `addend'. */
tls_type = 0;
+ save_unresolved_reloc = unresolved_reloc;
switch (r_type)
{
default:
- info->callbacks->einfo
- /* xgettext:c-format */
- (_("%P: %B: unknown relocation type %d for `%T'\n"),
- input_bfd, (int) r_type, sym_name);
+ /* xgettext:c-format */
+ _bfd_error_handler (_("%pB: %s unsupported"),
+ input_bfd, ppc64_elf_howto_table[r_type]->name);
bfd_set_error (bfd_error_bad_value);
ret = FALSE;
&& (h == NULL
|| !UNDEFWEAK_NO_DYNAMIC_RELOC (info, &h->elf)
|| (tls_type == (TLS_TLS | TLS_LD)
- && !h->elf.def_dynamic))))
+ && !h->elf.def_dynamic))
+ && !(tls_type == (TLS_TLS | TLS_TPREL)
+ && bfd_link_executable (info)
+ && SYMBOL_REFERENCES_LOCAL (info, &h->elf))))
relgot = ppc64_elf_tdata (ent->owner)->relgot;
if (relgot != NULL)
{
case R_PPC64_PLT16_HA:
case R_PPC64_PLT16_HI:
case R_PPC64_PLT16_LO:
+ case R_PPC64_PLT16_LO_DS:
case R_PPC64_PLT32:
case R_PPC64_PLT64:
+ case R_PPC64_PLTSEQ:
+ case R_PPC64_PLTCALL:
/* Relocation is to the entry for this symbol in the
procedure linkage table. */
+ unresolved_reloc = TRUE;
{
struct plt_entry **plt_list = NULL;
if (h != NULL)
{
struct plt_entry **local_plt = (struct plt_entry **)
(local_got_ents + symtab_hdr->sh_info);
- unsigned char *local_got_tls_masks = (unsigned char *)
- (local_plt + symtab_hdr->sh_info);
- if ((local_got_tls_masks[r_symndx] & PLT_IFUNC) != 0)
- plt_list = local_plt + r_symndx;
+ plt_list = local_plt + r_symndx;
}
if (plt_list)
{
&& ent->addend == orig_rel.r_addend)
{
asection *plt;
+ bfd_vma got;
plt = htab->elf.splt;
if (!htab->elf.dynamic_sections_created
|| h == NULL
|| h->elf.dynindx == -1)
- plt = htab->elf.iplt;
+ {
+ if (h != NULL
+ ? h->elf.type == STT_GNU_IFUNC
+ : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+ plt = htab->elf.iplt;
+ else
+ plt = htab->pltlocal;
+ }
relocation = (plt->output_section->vma
+ plt->output_offset
+ ent->plt.offset);
+ if (r_type == R_PPC64_PLT16_HA
+ || r_type ==R_PPC64_PLT16_HI
+ || r_type ==R_PPC64_PLT16_LO
+ || r_type ==R_PPC64_PLT16_LO_DS)
+ {
+ got = (elf_gp (output_bfd)
+ + htab->sec_info[input_section->id].toc_off);
+ relocation -= got;
+ }
addend = 0;
unresolved_reloc = FALSE;
break;
info->callbacks->einfo
/* xgettext:c-format */
(_("%H: %s for indirect "
- "function `%T' unsupported\n"),
+ "function `%pT' unsupported\n"),
input_bfd, input_section, rel->r_offset,
ppc64_elf_howto_table[r_type]->name,
sym_name);
info->callbacks->einfo
/* xgettext:c-format */
- (_("%P: %B: %s is not supported for `%T'\n"),
+ (_("%P: %pB: %s is not supported for `%pT'\n"),
input_bfd,
ppc64_elf_howto_table[r_type]->name, sym_name);
/* Multi-instruction sequences that access the TOC can be
optimized, eg. addis ra,r2,0; addi rb,ra,x;
- to nop; addi rb,r2,x; */
- howto = ppc64_elf_howto_table[(int) r_type];
+ to nop; addi rb,r2,x; */
switch (r_type)
{
default:
insn. */
break;
+ case R_PPC64_PLTCALL:
+ if (unresolved_reloc)
+ {
+ /* No plt entry. Make this into a direct call. */
+ bfd_byte *p = contents + rel->r_offset;
+ insn = bfd_get_32 (input_bfd, p);
+ insn &= 1;
+ bfd_put_32 (input_bfd, B_DOT | insn, p);
+ bfd_put_32 (input_bfd, NOP, p + 4);
+ unresolved_reloc = save_unresolved_reloc;
+ r_type = R_PPC64_REL24;
+ }
+ break;
+
+ case R_PPC64_PLTSEQ:
+ if (unresolved_reloc)
+ {
+ unresolved_reloc = FALSE;
+ goto nop_it;
+ }
+ break;
+
+ case R_PPC64_PLT16_HA:
+ if (unresolved_reloc)
+ {
+ unresolved_reloc = FALSE;
+ goto nop_it;
+ }
+ /* Fall through. */
case R_PPC64_GOT_TLSLD16_HA:
case R_PPC64_GOT_TLSGD16_HA:
case R_PPC64_GOT_TPREL16_HA:
if (htab->do_toc_opt && relocation + addend + 0x8000 < 0x10000
&& !ppc64_elf_tdata (input_bfd)->unexpected_toc_insn)
{
- bfd_byte *p = contents + (rel->r_offset & ~3);
+ bfd_byte *p;
+ nop_it:
+ p = contents + (rel->r_offset & ~3);
bfd_put_32 (input_bfd, NOP, p);
+ goto copy_reloc;
}
break;
+ case R_PPC64_PLT16_LO:
+ case R_PPC64_PLT16_LO_DS:
+ if (unresolved_reloc)
+ {
+ unresolved_reloc = FALSE;
+ goto nop_it;
+ }
+ /* Fall through. */
case R_PPC64_GOT_TLSLD16_LO:
case R_PPC64_GOT_TLSGD16_LO:
case R_PPC64_GOT_TPREL16_LO_DS:
/* xgettext:c-format */
info->callbacks->minfo
(_("%H: warning: %s unexpected insn %#x.\n"),
- input_bfd, input_section, rel->r_offset, howto->name, insn);
+ input_bfd, input_section, rel->r_offset,
+ ppc64_elf_howto_table[r_type]->name, insn);
else
- bfd_put_32 (input_bfd, NOP, p);
+ {
+ bfd_put_32 (input_bfd, NOP, p);
+ goto copy_reloc;
+ }
}
break;
/* xgettext:c-format */
(_("%H: error: %s not a multiple of %u\n"),
input_bfd, input_section, rel->r_offset,
- howto->name,
+ ppc64_elf_howto_table[r_type]->name,
mask + 1);
bfd_set_error (bfd_error_bad_value);
ret = FALSE;
/* Dynamic relocs are not propagated for SEC_DEBUGGING sections
because such sections are not SEC_ALLOC and thus ld.so will
not process them. */
+ howto = ppc64_elf_howto_table[(int) r_type];
if (unresolved_reloc
&& !((input_section->flags & SEC_DEBUGGING) != 0
&& h->elf.def_dynamic)
{
info->callbacks->einfo
/* xgettext:c-format */
- (_("%H: unresolvable %s against `%T'\n"),
+ (_("%H: unresolvable %s against `%pT'\n"),
input_bfd, input_section, rel->r_offset,
howto->name,
h->elf.root.root.string);
{
info->callbacks->einfo
/* xgettext:c-format */
- (_("%H: %s against `%T': error %d\n"),
+ (_("%H: %s against `%pT': error %d\n"),
input_bfd, input_section, rel->r_offset,
reloc_name, sym_name, (int) r);
ret = FALSE;
{
struct ppc_link_hash_table *htab;
struct plt_entry *ent;
- Elf_Internal_Rela rela;
- bfd_byte *loc;
htab = ppc_hash_table (info);
if (htab == NULL)
return FALSE;
- for (ent = h->plt.plist; ent != NULL; ent = ent->next)
- if (ent->plt.offset != (bfd_vma) -1)
- {
- /* This symbol has an entry in the procedure linkage
- table. Set it up. */
- if (!htab->elf.dynamic_sections_created
- || h->dynindx == -1)
- {
- BFD_ASSERT (h->type == STT_GNU_IFUNC
- && h->def_regular
- && (h->root.type == bfd_link_hash_defined
- || h->root.type == bfd_link_hash_defweak));
- rela.r_offset = (htab->elf.iplt->output_section->vma
- + htab->elf.iplt->output_offset
- + ent->plt.offset);
- 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->elf.irelplt->contents
- + (htab->elf.irelplt->reloc_count++
- * sizeof (Elf64_External_Rela)));
- htab->local_ifunc_resolver = 1;
- }
- else
- {
- 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->elf.srelplt->contents
- + ((ent->plt.offset - PLT_INITIAL_ENTRY_SIZE (htab))
- / PLT_ENTRY_SIZE (htab) * sizeof (Elf64_External_Rela)));
- if (h->type == STT_GNU_IFUNC && is_static_defined (h))
- htab->maybe_local_ifunc_resolver = 1;
- }
- 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 (!htab->opd_abi && !h->def_regular)
+ for (ent = h->plt.plist; ent != NULL; ent = ent->next)
+ if (ent->plt.offset != (bfd_vma) -1)
+ {
+ /* 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;
+ }
+ break;
+ }
if (h->needs_copy)
{
/* This symbol needs a copy reloc. Set it up. */
+ Elf_Internal_Rela rela;
asection *srel;
+ bfd_byte *loc;
if (h->dynindx == -1
|| (h->root.type != bfd_link_hash_defined
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 - 8 * 4;
+ dyn.d_un.d_ptr += GLINK_PLTRESOLVE_SIZE (htab) - 8 * 4;
break;
case DT_PPC64_OPD:
+ (p + 8 - htab->glink_eh_frame->contents));
if (val + 0x80000000 > 0xffffffff)
{
- info->callbacks->einfo
- (_("%P: %s offset too large for .eh_frame sdata4 encoding"),
+ _bfd_error_handler
+ (_("%s offset too large for .eh_frame sdata4 encoding"),
group->stub_sec->name);
return FALSE;
}
+ (p + 8 - htab->glink_eh_frame->contents));
if (val + 0x80000000 > 0xffffffff)
{
- info->callbacks->einfo
- (_("%P: %s offset too large for .eh_frame sdata4 encoding"),
+ _bfd_error_handler
+ (_("%s offset too large for .eh_frame sdata4 encoding"),
htab->glink->name);
return FALSE;
}