Line 1... |
Line 1... |
/* PowerPC64-specific support for 64-bit ELF.
|
/* PowerPC64-specific support for 64-bit ELF.
|
Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
|
Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
|
2009, 2010, 2011 Free Software Foundation, Inc.
|
2009, 2010, 2011, 2012 Free Software Foundation, Inc.
|
Written by Linus Nordberg, Swox AB <info@swox.com>,
|
Written by Linus Nordberg, Swox AB <info@swox.com>,
|
based on elf32-ppc.c by Ian Lance Taylor.
|
based on elf32-ppc.c by Ian Lance Taylor.
|
Largely rewritten by Alan Modra.
|
Largely rewritten by Alan Modra.
|
|
|
This file is part of BFD, the Binary File Descriptor library.
|
This file is part of BFD, the Binary File Descriptor library.
|
Line 103... |
Line 103... |
#define elf_backend_gc_mark_dynamic_ref ppc64_elf_gc_mark_dynamic_ref
|
#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_mark_hook ppc64_elf_gc_mark_hook
|
#define elf_backend_gc_sweep_hook ppc64_elf_gc_sweep_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_adjust_dynamic_symbol ppc64_elf_adjust_dynamic_symbol
|
#define elf_backend_hide_symbol ppc64_elf_hide_symbol
|
#define elf_backend_hide_symbol ppc64_elf_hide_symbol
|
|
#define elf_backend_maybe_function_sym ppc64_elf_maybe_function_sym
|
#define elf_backend_always_size_sections ppc64_elf_func_desc_adjust
|
#define elf_backend_always_size_sections ppc64_elf_func_desc_adjust
|
#define elf_backend_size_dynamic_sections ppc64_elf_size_dynamic_sections
|
#define elf_backend_size_dynamic_sections ppc64_elf_size_dynamic_sections
|
#define elf_backend_init_index_section _bfd_elf_init_2_index_sections
|
#define elf_backend_init_index_section _bfd_elf_init_2_index_sections
|
#define elf_backend_action_discarded ppc64_elf_action_discarded
|
#define elf_backend_action_discarded ppc64_elf_action_discarded
|
#define elf_backend_relocate_section ppc64_elf_relocate_section
|
#define elf_backend_relocate_section ppc64_elf_relocate_section
|
Line 150... |
Line 151... |
#define ADDIS_R12_R12 0x3d8c0000 /* addis %r12,%r12,off@ha */
|
#define ADDIS_R12_R12 0x3d8c0000 /* addis %r12,%r12,off@ha */
|
#define ADDI_R12_R12 0x398c0000 /* addi %r12,%r12,off@l */
|
#define ADDI_R12_R12 0x398c0000 /* addi %r12,%r12,off@l */
|
#define ADDIS_R2_R2 0x3c420000 /* addis %r2,%r2,off@ha */
|
#define ADDIS_R2_R2 0x3c420000 /* addis %r2,%r2,off@ha */
|
#define ADDI_R2_R2 0x38420000 /* addi %r2,%r2,off@l */
|
#define ADDI_R2_R2 0x38420000 /* addi %r2,%r2,off@l */
|
|
|
|
#define XOR_R11_R11_R11 0x7d6b5a78 /* xor %r11,%r11,%r11 */
|
|
#define ADD_R12_R12_R11 0x7d8c5a14 /* add %r12,%r12,%r11 */
|
|
#define 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_R11_0R2 0xe9620000 /* ld %r11,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_40R1 0xe8410028 /* ld %r2,40(%r1) */
|
#define LD_R2_40R1 0xe8410028 /* ld %r2,40(%r1) */
|
|
|
Line 2354... |
Line 2362... |
bfd *output_bfd, char **error_message)
|
bfd *output_bfd, char **error_message)
|
{
|
{
|
long insn;
|
long insn;
|
enum elf_ppc64_reloc_type r_type;
|
enum elf_ppc64_reloc_type r_type;
|
bfd_size_type octets;
|
bfd_size_type octets;
|
/* Disabled until we sort out how ld should choose 'y' vs 'at'. */
|
/* Assume 'at' branch hints. */
|
bfd_boolean is_power4 = FALSE;
|
bfd_boolean is_isa_v2 = TRUE;
|
|
|
/* If this is a relocatable link (output_bfd test tells us), just
|
/* If this is a relocatable link (output_bfd test tells us), just
|
call the generic function. Any adjustment will be done at final
|
call the generic function. Any adjustment will be done at final
|
link time. */
|
link time. */
|
if (output_bfd != NULL)
|
if (output_bfd != NULL)
|
Line 2372... |
Line 2380... |
r_type = reloc_entry->howto->type;
|
r_type = reloc_entry->howto->type;
|
if (r_type == R_PPC64_ADDR14_BRTAKEN
|
if (r_type == R_PPC64_ADDR14_BRTAKEN
|
|| r_type == R_PPC64_REL14_BRTAKEN)
|
|| r_type == R_PPC64_REL14_BRTAKEN)
|
insn |= 0x01 << 21; /* 'y' or 't' bit, lowest bit of BO field. */
|
insn |= 0x01 << 21; /* 'y' or 't' bit, lowest bit of BO field. */
|
|
|
if (is_power4)
|
if (is_isa_v2)
|
{
|
{
|
/* Set 'a' bit. This is 0b00010 in BO field for branch
|
/* Set 'a' bit. This is 0b00010 in BO field for branch
|
on CR(BI) insns (BO == 001at or 011at), and 0b01000
|
on CR(BI) insns (BO == 001at or 011at), and 0b01000
|
for branch on CTR insns (BO == 1a00t or 1a01t). */
|
for branch on CTR insns (BO == 1a00t or 1a01t). */
|
if ((insn & (0x14 << 21)) == (0x04 << 21))
|
if ((insn & (0x14 << 21)) == (0x04 << 21))
|
Line 2712... |
Line 2720... |
{
|
{
|
char data[136];
|
char data[136];
|
va_list ap;
|
va_list ap;
|
|
|
va_start (ap, note_type);
|
va_start (ap, note_type);
|
memset (data, 0, 40);
|
memset (data, 0, sizeof (data));
|
strncpy (data + 40, va_arg (ap, const char *), 16);
|
strncpy (data + 40, va_arg (ap, const char *), 16);
|
strncpy (data + 56, va_arg (ap, const char *), 80);
|
strncpy (data + 56, va_arg (ap, const char *), 80);
|
va_end (ap);
|
va_end (ap);
|
return elfcore_write_note (abfd, buf, bufsiz,
|
return elfcore_write_note (abfd, buf, bufsiz,
|
"CORE", note_type, data, sizeof (data));
|
"CORE", note_type, data, sizeof (data));
|
Line 3582... |
Line 3590... |
ppc_stub_none,
|
ppc_stub_none,
|
ppc_stub_long_branch,
|
ppc_stub_long_branch,
|
ppc_stub_long_branch_r2off,
|
ppc_stub_long_branch_r2off,
|
ppc_stub_plt_branch,
|
ppc_stub_plt_branch,
|
ppc_stub_plt_branch_r2off,
|
ppc_stub_plt_branch_r2off,
|
ppc_stub_plt_call
|
ppc_stub_plt_call,
|
|
ppc_stub_plt_call_r2save
|
};
|
};
|
|
|
struct ppc_stub_hash_entry {
|
struct ppc_stub_hash_entry {
|
|
|
/* Base hash table entry structure. */
|
/* Base hash table entry structure. */
|
Line 3750... |
Line 3759... |
|
|
/* The size of reliplt used by got entry relocs. */
|
/* The size of reliplt used by got entry relocs. */
|
bfd_size_type got_reli_size;
|
bfd_size_type got_reli_size;
|
|
|
/* Statistics. */
|
/* Statistics. */
|
unsigned long stub_count[ppc_stub_plt_call];
|
unsigned long stub_count[ppc_stub_plt_call_r2save];
|
|
|
/* Number of stubs against global syms. */
|
/* Number of stubs against global syms. */
|
unsigned long stub_globals;
|
unsigned long stub_globals;
|
|
|
|
/* Alignment of PLT call stubs. */
|
|
unsigned int plt_stub_align:4;
|
|
|
/* Set if PLT call stubs should load r11. */
|
/* Set if PLT call stubs should load r11. */
|
unsigned int plt_static_chain:1;
|
unsigned int plt_static_chain:1;
|
|
|
|
/* Set if PLT call stubs need a read-read barrier. */
|
|
unsigned int plt_thread_safe:1;
|
|
|
/* Set if we should emit symbols for stubs. */
|
/* Set if we should emit symbols for stubs. */
|
unsigned int emit_stub_syms:1;
|
unsigned int emit_stub_syms:1;
|
|
|
/* Set if __tls_get_addr optimization should not be done. */
|
/* Set if __tls_get_addr optimization should not be done. */
|
unsigned int no_tls_get_addr_opt:1;
|
unsigned int no_tls_get_addr_opt:1;
|
Line 4433... |
Line 4448... |
edir->elf.ref_dynamic |= eind->elf.ref_dynamic;
|
edir->elf.ref_dynamic |= eind->elf.ref_dynamic;
|
edir->elf.ref_regular |= eind->elf.ref_regular;
|
edir->elf.ref_regular |= eind->elf.ref_regular;
|
edir->elf.ref_regular_nonweak |= eind->elf.ref_regular_nonweak;
|
edir->elf.ref_regular_nonweak |= eind->elf.ref_regular_nonweak;
|
edir->elf.needs_plt |= eind->elf.needs_plt;
|
edir->elf.needs_plt |= eind->elf.needs_plt;
|
|
|
/* If we were called to copy over info for a weak sym, that's all. */
|
|
if (eind->elf.root.type != bfd_link_hash_indirect)
|
|
return;
|
|
|
|
/* Copy over any dynamic relocs we may have on the indirect sym. */
|
/* Copy over any dynamic relocs we may have on the indirect sym. */
|
if (eind->dyn_relocs != NULL)
|
if (eind->dyn_relocs != NULL)
|
{
|
{
|
if (edir->dyn_relocs != NULL)
|
if (edir->dyn_relocs != NULL)
|
{
|
{
|
Line 4469... |
Line 4480... |
|
|
edir->dyn_relocs = eind->dyn_relocs;
|
edir->dyn_relocs = eind->dyn_relocs;
|
eind->dyn_relocs = NULL;
|
eind->dyn_relocs = NULL;
|
}
|
}
|
|
|
|
/* If we were called to copy over info for a weak sym, that's all.
|
|
You might think dyn_relocs need not be copied over; After all,
|
|
both syms will be dynamic or both non-dynamic so we're just
|
|
moving reloc accounting around. However, ELIMINATE_COPY_RELOCS
|
|
code in ppc64_elf_adjust_dynamic_symbol needs to check for
|
|
dyn_relocs in read-only sections, and it does so on what is the
|
|
DIR sym here. */
|
|
if (eind->elf.root.type != bfd_link_hash_indirect)
|
|
return;
|
|
|
/* Copy over got entries that we may have already seen to the
|
/* Copy over got entries that we may have already seen to the
|
symbol which just became indirect. */
|
symbol which just became indirect. */
|
if (eind->elf.got.glist != NULL)
|
if (eind->elf.got.glist != NULL)
|
{
|
{
|
if (edir->elf.got.glist != NULL)
|
if (edir->elf.got.glist != NULL)
|
Line 5506... |
Line 5527... |
bfd *opd_bfd = opd_sec->owner;
|
bfd *opd_bfd = opd_sec->owner;
|
Elf_Internal_Rela *relocs;
|
Elf_Internal_Rela *relocs;
|
Elf_Internal_Rela *lo, *hi, *look;
|
Elf_Internal_Rela *lo, *hi, *look;
|
bfd_vma val;
|
bfd_vma val;
|
|
|
/* No relocs implies we are linking a --just-symbols object. */
|
/* No relocs implies we are linking a --just-symbols object, or looking
|
|
at a final linked executable with addr2line or somesuch. */
|
if (opd_sec->reloc_count == 0)
|
if (opd_sec->reloc_count == 0)
|
{
|
{
|
char buf[8];
|
char buf[8];
|
|
|
if (!bfd_get_section_contents (opd_bfd, opd_sec, buf, offset, 8))
|
if (!bfd_get_section_contents (opd_bfd, opd_sec, buf, offset, 8))
|
Line 5609... |
Line 5631... |
}
|
}
|
|
|
return val;
|
return val;
|
}
|
}
|
|
|
|
/* Return TRUE iff the ELF symbol SYM might be a function. Set *CODE_SEC
|
|
and *CODE_OFF to the function's entry point. */
|
|
|
|
static bfd_boolean
|
|
ppc64_elf_maybe_function_sym (const asymbol *sym,
|
|
asection **code_sec, bfd_vma *code_off)
|
|
{
|
|
if (_bfd_elf_maybe_function_sym (sym, code_sec, code_off))
|
|
{
|
|
if (strcmp (sym->section->name, ".opd") == 0)
|
|
opd_entry_value (sym->section, sym->value, code_sec, code_off);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
/* Return true if symbol is defined in a regular object file. */
|
/* Return true if symbol is defined in a regular object file. */
|
|
|
static bfd_boolean
|
static bfd_boolean
|
is_static_defined (struct elf_link_hash_entry *h)
|
is_static_defined (struct elf_link_hash_entry *h)
|
{
|
{
|
Line 9463... |
Line 9501... |
return ppc_stub_long_branch;
|
return ppc_stub_long_branch;
|
|
|
return ppc_stub_none;
|
return ppc_stub_none;
|
}
|
}
|
|
|
/* Build a .plt call stub. */
|
/* With power7 weakly ordered memory model, it is possible for ld.so
|
|
to update a plt entry in one thread and have another thread see a
|
|
stale zero toc entry. To avoid this we need some sort of acquire
|
|
barrier in the call stub. One solution is to make the load of the
|
|
toc word seem to appear to depend on the load of the function entry
|
|
word. Another solution is to test for r2 being zero, and branch to
|
|
the appropriate glink entry if so.
|
|
|
|
. fake dep barrier compare
|
|
. ld 11,xxx(2) ld 11,xxx(2)
|
|
. mtctr 11 mtctr 11
|
|
. xor 11,11,11 ld 2,xxx+8(2)
|
|
. add 2,2,11 cmpldi 2,0
|
|
. ld 2,xxx+8(2) bnectr+
|
|
. bctr b <glink_entry>
|
|
|
|
The solution involving the compare turns out to be faster, so
|
|
that's what we use unless the branch won't reach. */
|
|
|
|
#define ALWAYS_USE_FAKE_DEP 0
|
|
#define ALWAYS_EMIT_R2SAVE 0
|
|
|
static inline bfd_byte *
|
|
build_plt_stub (bfd *obfd, bfd_byte *p, int offset, Elf_Internal_Rela *r,
|
|
bfd_boolean plt_static_chain)
|
|
{
|
|
#define PPC_LO(v) ((v) & 0xffff)
|
#define PPC_LO(v) ((v) & 0xffff)
|
#define PPC_HI(v) (((v) >> 16) & 0xffff)
|
#define PPC_HI(v) (((v) >> 16) & 0xffff)
|
#define PPC_HA(v) PPC_HI ((v) + 0x8000)
|
#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,
|
|
bfd_vma off)
|
|
{
|
|
unsigned size = PLT_CALL_STUB_SIZE;
|
|
|
|
if (!(ALWAYS_EMIT_R2SAVE
|
|
|| stub_entry->stub_type == ppc_stub_plt_call_r2save))
|
|
size -= 4;
|
|
if (!htab->plt_static_chain)
|
|
size -= 4;
|
|
if (htab->plt_thread_safe)
|
|
size += 8;
|
|
if (PPC_HA (off) == 0)
|
|
size -= 4;
|
|
if (PPC_HA (off + 8 + 8 * htab->plt_static_chain) != PPC_HA (off))
|
|
size += 4;
|
|
if (stub_entry->h != NULL
|
|
&& (stub_entry->h == htab->tls_get_addr_fd
|
|
|| stub_entry->h == htab->tls_get_addr)
|
|
&& !htab->no_tls_get_addr_opt)
|
|
size += 13 * 4;
|
|
return size;
|
|
}
|
|
|
|
/* 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->plt_stub_align;
|
|
unsigned stub_size = plt_stub_size (htab, stub_entry, plt_off);
|
|
bfd_vma stub_off = stub_entry->stub_sec->size;
|
|
|
|
if (((stub_off + stub_size - 1) & -stub_align) - (stub_off & -stub_align)
|
|
> (stub_size & -stub_align))
|
|
return stub_align - (stub_off & (stub_align - 1));
|
|
return 0;
|
|
}
|
|
|
|
/* Build a .plt call stub. */
|
|
|
|
static inline bfd_byte *
|
|
build_plt_stub (struct ppc_link_hash_table *htab,
|
|
struct ppc_stub_hash_entry *stub_entry,
|
|
bfd_byte *p, bfd_vma offset, Elf_Internal_Rela *r)
|
|
{
|
|
bfd *obfd = htab->stub_bfd;
|
|
bfd_boolean plt_static_chain = htab->plt_static_chain;
|
|
bfd_boolean plt_thread_safe = htab->plt_thread_safe;
|
|
bfd_boolean use_fake_dep = plt_thread_safe;
|
|
bfd_vma cmp_branch_off = 0;
|
|
|
|
if (!ALWAYS_USE_FAKE_DEP
|
|
&& plt_thread_safe
|
|
&& !(stub_entry->h != NULL
|
|
&& (stub_entry->h == htab->tls_get_addr_fd
|
|
|| stub_entry->h == htab->tls_get_addr)
|
|
&& !htab->no_tls_get_addr_opt))
|
|
{
|
|
bfd_vma pltoff = stub_entry->plt_ent->plt.offset & ~1;
|
|
bfd_vma pltindex = (pltoff - PLT_INITIAL_ENTRY_SIZE) / PLT_ENTRY_SIZE;
|
|
bfd_vma glinkoff = GLINK_CALL_STUB_SIZE + pltindex * 8;
|
|
bfd_vma to, from;
|
|
|
|
if (pltindex > 32767)
|
|
glinkoff += (pltindex - 32767) * 4;
|
|
to = (glinkoff
|
|
+ htab->glink->output_offset
|
|
+ htab->glink->output_section->vma);
|
|
from = (p - stub_entry->stub_sec->contents
|
|
+ 4 * (ALWAYS_EMIT_R2SAVE
|
|
|| stub_entry->stub_type == ppc_stub_plt_call_r2save)
|
|
+ 4 * (PPC_HA (offset) != 0)
|
|
+ 4 * (PPC_HA (offset + 8 + 8 * plt_static_chain)
|
|
!= PPC_HA (offset))
|
|
+ 4 * (plt_static_chain != 0)
|
|
+ 20
|
|
+ stub_entry->stub_sec->output_offset
|
|
+ stub_entry->stub_sec->output_section->vma);
|
|
cmp_branch_off = to - from;
|
|
use_fake_dep = cmp_branch_off + (1 << 25) >= (1 << 26);
|
|
}
|
|
|
if (PPC_HA (offset) != 0)
|
if (PPC_HA (offset) != 0)
|
{
|
{
|
if (r != NULL)
|
if (r != NULL)
|
{
|
{
|
|
if (ALWAYS_EMIT_R2SAVE
|
|
|| stub_entry->stub_type == ppc_stub_plt_call_r2save)
|
r[0].r_offset += 4;
|
r[0].r_offset += 4;
|
r[0].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_HA);
|
r[0].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_HA);
|
r[1].r_offset = r[0].r_offset + 4;
|
r[1].r_offset = r[0].r_offset + 4;
|
r[1].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_LO_DS);
|
r[1].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_LO_DS);
|
r[1].r_addend = r[0].r_addend;
|
r[1].r_addend = r[0].r_addend;
|
Line 9490... |
Line 9633... |
r[2].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_LO);
|
r[2].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_LO);
|
r[2].r_addend = r[0].r_addend;
|
r[2].r_addend = r[0].r_addend;
|
}
|
}
|
else
|
else
|
{
|
{
|
r[2].r_offset = r[1].r_offset + 8;
|
r[2].r_offset = r[1].r_offset + 8 + 8 * use_fake_dep;
|
r[2].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_LO_DS);
|
r[2].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_LO_DS);
|
r[2].r_addend = r[0].r_addend + 8;
|
r[2].r_addend = r[0].r_addend + 8;
|
if (plt_static_chain)
|
if (plt_static_chain)
|
{
|
{
|
r[3].r_offset = r[2].r_offset + 4;
|
r[3].r_offset = r[2].r_offset + 4;
|
r[3].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_LO_DS);
|
r[3].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_LO_DS);
|
r[3].r_addend = r[0].r_addend + 16;
|
r[3].r_addend = r[0].r_addend + 16;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
if (ALWAYS_EMIT_R2SAVE
|
|
|| stub_entry->stub_type == ppc_stub_plt_call_r2save)
|
bfd_put_32 (obfd, STD_R2_40R1, p), p += 4;
|
bfd_put_32 (obfd, STD_R2_40R1, p), p += 4;
|
bfd_put_32 (obfd, ADDIS_R12_R2 | PPC_HA (offset), p), p += 4;
|
bfd_put_32 (obfd, ADDIS_R12_R2 | PPC_HA (offset), p), p += 4;
|
bfd_put_32 (obfd, LD_R11_0R12 | PPC_LO (offset), p), p += 4;
|
bfd_put_32 (obfd, LD_R11_0R12 | PPC_LO (offset), p), p += 4;
|
if (PPC_HA (offset + 8 + 8 * plt_static_chain) != PPC_HA (offset))
|
if (PPC_HA (offset + 8 + 8 * plt_static_chain) != PPC_HA (offset))
|
{
|
{
|
bfd_put_32 (obfd, ADDI_R12_R12 | PPC_LO (offset), p), p += 4;
|
bfd_put_32 (obfd, ADDI_R12_R12 | PPC_LO (offset), p), p += 4;
|
offset = 0;
|
offset = 0;
|
}
|
}
|
bfd_put_32 (obfd, MTCTR_R11, p), p += 4;
|
bfd_put_32 (obfd, MTCTR_R11, p), p += 4;
|
|
if (use_fake_dep)
|
|
{
|
|
bfd_put_32 (obfd, XOR_R11_R11_R11, p), p += 4;
|
|
bfd_put_32 (obfd, ADD_R12_R12_R11, p), p += 4;
|
|
}
|
bfd_put_32 (obfd, LD_R2_0R12 | PPC_LO (offset + 8), p), p += 4;
|
bfd_put_32 (obfd, LD_R2_0R12 | PPC_LO (offset + 8), p), p += 4;
|
if (plt_static_chain)
|
if (plt_static_chain)
|
bfd_put_32 (obfd, LD_R11_0R12 | PPC_LO (offset + 16), p), p += 4;
|
bfd_put_32 (obfd, LD_R11_0R12 | PPC_LO (offset + 16), p), p += 4;
|
bfd_put_32 (obfd, BCTR, p), p += 4;
|
|
}
|
}
|
else
|
else
|
{
|
{
|
if (r != NULL)
|
if (r != NULL)
|
{
|
{
|
|
if (ALWAYS_EMIT_R2SAVE
|
|
|| stub_entry->stub_type == ppc_stub_plt_call_r2save)
|
r[0].r_offset += 4;
|
r[0].r_offset += 4;
|
r[0].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_DS);
|
r[0].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_DS);
|
if (PPC_HA (offset + 8 + 8 * plt_static_chain) != PPC_HA (offset))
|
if (PPC_HA (offset + 8 + 8 * plt_static_chain) != PPC_HA (offset))
|
{
|
{
|
r[1].r_offset = r[0].r_offset + 4;
|
r[1].r_offset = r[0].r_offset + 4;
|
r[1].r_info = ELF64_R_INFO (0, R_PPC64_TOC16);
|
r[1].r_info = ELF64_R_INFO (0, R_PPC64_TOC16);
|
r[1].r_addend = r[0].r_addend;
|
r[1].r_addend = r[0].r_addend;
|
}
|
}
|
else
|
else
|
{
|
{
|
r[1].r_offset = r[0].r_offset + 8;
|
r[1].r_offset = r[0].r_offset + 8 + 8 * use_fake_dep;
|
r[1].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_DS);
|
r[1].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_DS);
|
r[1].r_addend = r[0].r_addend + 8 + 8 * plt_static_chain;
|
r[1].r_addend = r[0].r_addend + 8 + 8 * plt_static_chain;
|
if (plt_static_chain)
|
if (plt_static_chain)
|
{
|
{
|
r[2].r_offset = r[1].r_offset + 4;
|
r[2].r_offset = r[1].r_offset + 4;
|
r[2].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_DS);
|
r[2].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_DS);
|
r[2].r_addend = r[0].r_addend + 8;
|
r[2].r_addend = r[0].r_addend + 8;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
if (ALWAYS_EMIT_R2SAVE
|
|
|| stub_entry->stub_type == ppc_stub_plt_call_r2save)
|
bfd_put_32 (obfd, STD_R2_40R1, p), p += 4;
|
bfd_put_32 (obfd, STD_R2_40R1, p), p += 4;
|
bfd_put_32 (obfd, LD_R11_0R2 | PPC_LO (offset), p), p += 4;
|
bfd_put_32 (obfd, LD_R11_0R2 | PPC_LO (offset), p), p += 4;
|
if (PPC_HA (offset + 8 + 8 * plt_static_chain) != PPC_HA (offset))
|
if (PPC_HA (offset + 8 + 8 * plt_static_chain) != PPC_HA (offset))
|
{
|
{
|
bfd_put_32 (obfd, ADDI_R2_R2 | PPC_LO (offset), p), p += 4;
|
bfd_put_32 (obfd, ADDI_R2_R2 | PPC_LO (offset), p), p += 4;
|
offset = 0;
|
offset = 0;
|
}
|
}
|
bfd_put_32 (obfd, MTCTR_R11, p), p += 4;
|
bfd_put_32 (obfd, MTCTR_R11, p), p += 4;
|
|
if (use_fake_dep)
|
|
{
|
|
bfd_put_32 (obfd, XOR_R11_R11_R11, p), p += 4;
|
|
bfd_put_32 (obfd, ADD_R2_R2_R11, p), p += 4;
|
|
}
|
if (plt_static_chain)
|
if (plt_static_chain)
|
bfd_put_32 (obfd, LD_R11_0R2 | PPC_LO (offset + 16), p), p += 4;
|
bfd_put_32 (obfd, LD_R11_0R2 | PPC_LO (offset + 16), p), p += 4;
|
bfd_put_32 (obfd, LD_R2_0R2 | PPC_LO (offset + 8), p), p += 4;
|
bfd_put_32 (obfd, LD_R2_0R2 | PPC_LO (offset + 8), p), p += 4;
|
bfd_put_32 (obfd, BCTR, p), p += 4;
|
|
}
|
}
|
|
if (plt_thread_safe && !use_fake_dep)
|
|
{
|
|
bfd_put_32 (obfd, CMPLDI_R2_0, p), p += 4;
|
|
bfd_put_32 (obfd, BNECTR_P4, p), p += 4;
|
|
bfd_put_32 (obfd, B_DOT + cmp_branch_off, p), p += 4;
|
|
}
|
|
else
|
|
bfd_put_32 (obfd, BCTR, p), p += 4;
|
return p;
|
return p;
|
}
|
}
|
|
|
/* Build a special .plt call stub for __tls_get_addr. */
|
/* Build a special .plt call stub for __tls_get_addr. */
|
|
|
Line 9573... |
Line 9738... |
#define LD_R11_0R1 0xe9610000
|
#define LD_R11_0R1 0xe9610000
|
#define LD_R2_0R1 0xe8410000
|
#define LD_R2_0R1 0xe8410000
|
#define MTLR_R11 0x7d6803a6
|
#define MTLR_R11 0x7d6803a6
|
|
|
static inline bfd_byte *
|
static inline bfd_byte *
|
build_tls_get_addr_stub (bfd *obfd, bfd_byte *p, int offset,
|
build_tls_get_addr_stub (struct ppc_link_hash_table *htab,
|
Elf_Internal_Rela *r, bfd_boolean plt_static_chain)
|
struct ppc_stub_hash_entry *stub_entry,
|
|
bfd_byte *p, bfd_vma offset, Elf_Internal_Rela *r)
|
{
|
{
|
|
bfd *obfd = htab->stub_bfd;
|
|
|
bfd_put_32 (obfd, LD_R11_0R3 + 0, p), p += 4;
|
bfd_put_32 (obfd, LD_R11_0R3 + 0, p), p += 4;
|
bfd_put_32 (obfd, LD_R12_0R3 + 8, p), p += 4;
|
bfd_put_32 (obfd, LD_R12_0R3 + 8, p), p += 4;
|
bfd_put_32 (obfd, MR_R0_R3, p), p += 4;
|
bfd_put_32 (obfd, MR_R0_R3, p), p += 4;
|
bfd_put_32 (obfd, CMPDI_R11_0, p), p += 4;
|
bfd_put_32 (obfd, CMPDI_R11_0, p), p += 4;
|
bfd_put_32 (obfd, ADD_R3_R12_R13, p), p += 4;
|
bfd_put_32 (obfd, ADD_R3_R12_R13, p), p += 4;
|
Line 9588... |
Line 9756... |
bfd_put_32 (obfd, MFLR_R11, p), p += 4;
|
bfd_put_32 (obfd, MFLR_R11, p), p += 4;
|
bfd_put_32 (obfd, STD_R11_0R1 + 32, p), p += 4;
|
bfd_put_32 (obfd, STD_R11_0R1 + 32, p), p += 4;
|
|
|
if (r != NULL)
|
if (r != NULL)
|
r[0].r_offset += 9 * 4;
|
r[0].r_offset += 9 * 4;
|
p = build_plt_stub (obfd, p, offset, r, plt_static_chain);
|
p = build_plt_stub (htab, stub_entry, p, offset, r);
|
bfd_put_32 (obfd, BCTRL, p - 4);
|
bfd_put_32 (obfd, BCTRL, p - 4);
|
|
|
bfd_put_32 (obfd, LD_R11_0R1 + 32, p), p += 4;
|
bfd_put_32 (obfd, LD_R11_0R1 + 32, p), p += 4;
|
bfd_put_32 (obfd, LD_R2_0R1 + 40, p), p += 4;
|
bfd_put_32 (obfd, LD_R2_0R1 + 40, p), p += 4;
|
bfd_put_32 (obfd, MTLR_R11, p), p += 4;
|
bfd_put_32 (obfd, MTLR_R11, p), p += 4;
|
Line 9935... |
Line 10103... |
loc += 4;
|
loc += 4;
|
bfd_put_32 (htab->stub_bfd, BCTR, loc);
|
bfd_put_32 (htab->stub_bfd, BCTR, loc);
|
break;
|
break;
|
|
|
case ppc_stub_plt_call:
|
case ppc_stub_plt_call:
|
|
case ppc_stub_plt_call_r2save:
|
if (stub_entry->h != NULL
|
if (stub_entry->h != NULL
|
&& stub_entry->h->is_func_descriptor
|
&& stub_entry->h->is_func_descriptor
|
&& stub_entry->h->oh != NULL)
|
&& stub_entry->h->oh != NULL)
|
{
|
{
|
struct ppc_link_hash_entry *fh = ppc_follow_link (stub_entry->h->oh);
|
struct ppc_link_hash_entry *fh = ppc_follow_link (stub_entry->h->oh);
|
Line 10001... |
Line 10170... |
bfd_set_error (bfd_error_bad_value);
|
bfd_set_error (bfd_error_bad_value);
|
htab->stub_error = TRUE;
|
htab->stub_error = TRUE;
|
return FALSE;
|
return FALSE;
|
}
|
}
|
|
|
|
if (htab->plt_stub_align != 0)
|
|
{
|
|
unsigned pad = plt_stub_pad (htab, stub_entry, off);
|
|
|
|
stub_entry->stub_sec->size += pad;
|
|
stub_entry->stub_offset = stub_entry->stub_sec->size;
|
|
loc += pad;
|
|
}
|
|
|
r = NULL;
|
r = NULL;
|
if (info->emitrelocations)
|
if (info->emitrelocations)
|
{
|
{
|
r = get_relocs (stub_entry->stub_sec,
|
r = get_relocs (stub_entry->stub_sec,
|
(2
|
(2
|
Line 10020... |
Line 10198... |
}
|
}
|
if (stub_entry->h != NULL
|
if (stub_entry->h != NULL
|
&& (stub_entry->h == htab->tls_get_addr_fd
|
&& (stub_entry->h == htab->tls_get_addr_fd
|
|| stub_entry->h == htab->tls_get_addr)
|
|| stub_entry->h == htab->tls_get_addr)
|
&& !htab->no_tls_get_addr_opt)
|
&& !htab->no_tls_get_addr_opt)
|
p = build_tls_get_addr_stub (htab->stub_bfd, loc, off, r,
|
p = build_tls_get_addr_stub (htab, stub_entry, loc, off, r);
|
htab->plt_static_chain);
|
|
else
|
else
|
p = build_plt_stub (htab->stub_bfd, loc, off, r,
|
p = build_plt_stub (htab, stub_entry, loc, off, r);
|
htab->plt_static_chain);
|
|
size = p - loc;
|
size = p - loc;
|
break;
|
break;
|
|
|
default:
|
default:
|
BFD_FAIL ();
|
BFD_FAIL ();
|
Line 10044... |
Line 10220... |
char *name;
|
char *name;
|
const char *const stub_str[] = { "long_branch",
|
const char *const stub_str[] = { "long_branch",
|
"long_branch_r2off",
|
"long_branch_r2off",
|
"plt_branch",
|
"plt_branch",
|
"plt_branch_r2off",
|
"plt_branch_r2off",
|
|
"plt_call",
|
"plt_call" };
|
"plt_call" };
|
|
|
len1 = strlen (stub_str[stub_entry->stub_type - 1]);
|
len1 = strlen (stub_str[stub_entry->stub_type - 1]);
|
len2 = strlen (stub_entry->root.string);
|
len2 = strlen (stub_entry->root.string);
|
name = bfd_malloc (len1 + len2 + 2);
|
name = bfd_malloc (len1 + len2 + 2);
|
Line 10094... |
Line 10271... |
|
|
htab = ppc_hash_table (info);
|
htab = ppc_hash_table (info);
|
if (htab == NULL)
|
if (htab == NULL)
|
return FALSE;
|
return FALSE;
|
|
|
if (stub_entry->stub_type == ppc_stub_plt_call)
|
if (stub_entry->stub_type == ppc_stub_plt_call
|
|
|| stub_entry->stub_type == ppc_stub_plt_call_r2save)
|
{
|
{
|
asection *plt;
|
asection *plt;
|
off = stub_entry->plt_ent->plt.offset & ~(bfd_vma) 1;
|
off = stub_entry->plt_ent->plt.offset & ~(bfd_vma) 1;
|
if (off >= (bfd_vma) -2)
|
if (off >= (bfd_vma) -2)
|
abort ();
|
abort ();
|
Line 10110... |
Line 10288... |
off += (plt->output_offset
|
off += (plt->output_offset
|
+ plt->output_section->vma
|
+ plt->output_section->vma
|
- elf_gp (plt->output_section->owner)
|
- elf_gp (plt->output_section->owner)
|
- htab->stub_group[stub_entry->id_sec->id].toc_off);
|
- htab->stub_group[stub_entry->id_sec->id].toc_off);
|
|
|
size = PLT_CALL_STUB_SIZE;
|
size = plt_stub_size (htab, stub_entry, off);
|
if (!htab->plt_static_chain)
|
if (htab->plt_stub_align)
|
size -= 4;
|
size += plt_stub_pad (htab, stub_entry, off);
|
if (PPC_HA (off) == 0)
|
|
size -= 4;
|
|
if (PPC_HA (off + 8 + 8 * htab->plt_static_chain) != PPC_HA (off))
|
|
size += 4;
|
|
if (stub_entry->h != NULL
|
|
&& (stub_entry->h == htab->tls_get_addr_fd
|
|
|| stub_entry->h == htab->tls_get_addr)
|
|
&& !htab->no_tls_get_addr_opt)
|
|
size += 13 * 4;
|
|
if (info->emitrelocations)
|
if (info->emitrelocations)
|
{
|
{
|
stub_entry->stub_sec->reloc_count
|
stub_entry->stub_sec->reloc_count
|
+= (2
|
+= (2
|
+ (PPC_HA (off) != 0)
|
+ (PPC_HA (off) != 0)
|
Line 11090... |
Line 11259... |
PC-relative calls to a target that is unreachable with a "bl"
|
PC-relative calls to a target that is unreachable with a "bl"
|
instruction. */
|
instruction. */
|
|
|
bfd_boolean
|
bfd_boolean
|
ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
|
ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
|
bfd_boolean plt_static_chain)
|
bfd_boolean plt_static_chain, int plt_thread_safe,
|
|
int plt_stub_align)
|
{
|
{
|
bfd_size_type stub_group_size;
|
bfd_size_type stub_group_size;
|
bfd_boolean stubs_always_before_branch;
|
bfd_boolean stubs_always_before_branch;
|
struct ppc_link_hash_table *htab = ppc_hash_table (info);
|
struct ppc_link_hash_table *htab = ppc_hash_table (info);
|
|
|
if (htab == NULL)
|
if (htab == NULL)
|
return FALSE;
|
return FALSE;
|
|
|
htab->plt_static_chain = plt_static_chain;
|
htab->plt_static_chain = plt_static_chain;
|
|
htab->plt_stub_align = plt_stub_align;
|
|
if (plt_thread_safe == -1)
|
|
{
|
|
const char *const thread_starter[] =
|
|
{
|
|
"pthread_create",
|
|
/* libstdc++ */
|
|
"_ZNSt6thread15_M_start_threadESt10shared_ptrINS_10_Impl_baseEE",
|
|
/* librt */
|
|
"aio_init", "aio_read", "aio_write", "aio_fsync", "lio_listio",
|
|
"mq_notify", "create_timer",
|
|
/* libanl */
|
|
"getaddrinfo_a",
|
|
/* libgomp */
|
|
"GOMP_parallel_start",
|
|
"GOMP_parallel_loop_static_start",
|
|
"GOMP_parallel_loop_dynamic_start",
|
|
"GOMP_parallel_loop_guided_start",
|
|
"GOMP_parallel_loop_runtime_start",
|
|
"GOMP_parallel_sections_start",
|
|
};
|
|
unsigned i;
|
|
|
|
for (i = 0; i < sizeof (thread_starter)/ sizeof (thread_starter[0]); i++)
|
|
{
|
|
struct elf_link_hash_entry *h;
|
|
h = elf_link_hash_lookup (&htab->elf, thread_starter[i],
|
|
FALSE, FALSE, TRUE);
|
|
plt_thread_safe = h != NULL && h->ref_regular;
|
|
if (plt_thread_safe)
|
|
break;
|
|
}
|
|
}
|
|
htab->plt_thread_safe = plt_thread_safe;
|
stubs_always_before_branch = group_size < 0;
|
stubs_always_before_branch = group_size < 0;
|
if (group_size < 0)
|
if (group_size < 0)
|
stub_group_size = -group_size;
|
stub_group_size = -group_size;
|
else
|
else
|
stub_group_size = group_size;
|
stub_group_size = group_size;
|
Line 11334... |
Line 11538... |
}
|
}
|
|
|
if (stub_type == ppc_stub_plt_call
|
if (stub_type == ppc_stub_plt_call
|
&& irela + 1 < irelaend
|
&& irela + 1 < irelaend
|
&& irela[1].r_offset == irela->r_offset + 4
|
&& irela[1].r_offset == irela->r_offset + 4
|
&& ELF64_R_TYPE (irela[1].r_info) == R_PPC64_TOCSAVE
|
&& ELF64_R_TYPE (irela[1].r_info) == R_PPC64_TOCSAVE)
|
&& !tocsave_find (htab, INSERT,
|
{
|
|
if (!tocsave_find (htab, INSERT,
|
&local_syms, irela + 1, input_bfd))
|
&local_syms, irela + 1, input_bfd))
|
goto error_ret_free_internal;
|
goto error_ret_free_internal;
|
|
}
|
|
else if (stub_type == ppc_stub_plt_call)
|
|
stub_type = ppc_stub_plt_call_r2save;
|
|
|
/* Support for grouping stub sections. */
|
/* Support for grouping stub sections. */
|
id_sec = htab->stub_group[section->id].link_sec;
|
id_sec = htab->stub_group[section->id].link_sec;
|
|
|
/* Get the name of this stub. */
|
/* Get the name of this stub. */
|
Line 11353... |
Line 11561... |
stub_name, FALSE, FALSE);
|
stub_name, FALSE, FALSE);
|
if (stub_entry != NULL)
|
if (stub_entry != NULL)
|
{
|
{
|
/* The proper stub has already been created. */
|
/* The proper stub has already been created. */
|
free (stub_name);
|
free (stub_name);
|
|
if (stub_type == ppc_stub_plt_call_r2save)
|
|
stub_entry->stub_type = stub_type;
|
continue;
|
continue;
|
}
|
}
|
|
|
stub_entry = ppc_add_stub (stub_name, section, info);
|
stub_entry = ppc_add_stub (stub_name, section, info);
|
if (stub_entry == NULL)
|
if (stub_entry == NULL)
|
Line 11372... |
Line 11582... |
free (local_syms);
|
free (local_syms);
|
return FALSE;
|
return FALSE;
|
}
|
}
|
|
|
stub_entry->stub_type = stub_type;
|
stub_entry->stub_type = stub_type;
|
if (stub_type != ppc_stub_plt_call)
|
if (stub_type != ppc_stub_plt_call
|
|
&& stub_type != ppc_stub_plt_call_r2save)
|
{
|
{
|
stub_entry->target_value = code_value;
|
stub_entry->target_value = code_value;
|
stub_entry->target_section = code_sec;
|
stub_entry->target_section = code_sec;
|
}
|
}
|
else
|
else
|
Line 11452... |
Line 11663... |
size += sizeof (glink_eh_frame_cie);
|
size += sizeof (glink_eh_frame_cie);
|
htab->glink_eh_frame->rawsize = htab->glink_eh_frame->size;
|
htab->glink_eh_frame->rawsize = htab->glink_eh_frame->size;
|
htab->glink_eh_frame->size = size;
|
htab->glink_eh_frame->size = size;
|
}
|
}
|
|
|
|
if (htab->plt_stub_align != 0)
|
|
for (stub_sec = htab->stub_bfd->sections;
|
|
stub_sec != NULL;
|
|
stub_sec = stub_sec->next)
|
|
if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
|
|
stub_sec->size = ((stub_sec->size + (1 << htab->plt_stub_align) - 1)
|
|
& (-1 << htab->plt_stub_align));
|
|
|
for (stub_sec = htab->stub_bfd->sections;
|
for (stub_sec = htab->stub_bfd->sections;
|
stub_sec != NULL;
|
stub_sec != NULL;
|
stub_sec = stub_sec->next)
|
stub_sec = stub_sec->next)
|
if ((stub_sec->flags & SEC_LINKER_CREATED) == 0
|
if ((stub_sec->flags & SEC_LINKER_CREATED) == 0
|
&& stub_sec->rawsize != stub_sec->size)
|
&& stub_sec->rawsize != stub_sec->size)
|
Line 11777... |
Line 11996... |
bfd_hash_traverse (&htab->stub_hash_table, ppc_build_one_stub, info);
|
bfd_hash_traverse (&htab->stub_hash_table, ppc_build_one_stub, info);
|
|
|
if (htab->relbrlt != NULL)
|
if (htab->relbrlt != NULL)
|
htab->relbrlt->reloc_count = 0;
|
htab->relbrlt->reloc_count = 0;
|
|
|
|
if (htab->plt_stub_align != 0)
|
|
for (stub_sec = htab->stub_bfd->sections;
|
|
stub_sec != NULL;
|
|
stub_sec = stub_sec->next)
|
|
if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
|
|
stub_sec->size = ((stub_sec->size + (1 << htab->plt_stub_align) - 1)
|
|
& (-1 << htab->plt_stub_align));
|
|
|
for (stub_sec = htab->stub_bfd->sections;
|
for (stub_sec = htab->stub_bfd->sections;
|
stub_sec != NULL;
|
stub_sec != NULL;
|
stub_sec = stub_sec->next)
|
stub_sec = stub_sec->next)
|
if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
|
if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
|
{
|
{
|
Line 11810... |
Line 12037... |
sprintf (*stats, _("linker stubs in %u group%s\n"
|
sprintf (*stats, _("linker stubs in %u group%s\n"
|
" branch %lu\n"
|
" branch %lu\n"
|
" toc adjust %lu\n"
|
" toc adjust %lu\n"
|
" long branch %lu\n"
|
" long branch %lu\n"
|
" long toc adj %lu\n"
|
" long toc adj %lu\n"
|
" plt call %lu"),
|
" plt call %lu\n"
|
|
" plt call toc %lu"),
|
stub_sec_count,
|
stub_sec_count,
|
stub_sec_count == 1 ? "" : "s",
|
stub_sec_count == 1 ? "" : "s",
|
htab->stub_count[ppc_stub_long_branch - 1],
|
htab->stub_count[ppc_stub_long_branch - 1],
|
htab->stub_count[ppc_stub_long_branch_r2off - 1],
|
htab->stub_count[ppc_stub_long_branch_r2off - 1],
|
htab->stub_count[ppc_stub_plt_branch - 1],
|
htab->stub_count[ppc_stub_plt_branch - 1],
|
htab->stub_count[ppc_stub_plt_branch_r2off - 1],
|
htab->stub_count[ppc_stub_plt_branch_r2off - 1],
|
htab->stub_count[ppc_stub_plt_call - 1]);
|
htab->stub_count[ppc_stub_plt_call - 1],
|
|
htab->stub_count[ppc_stub_plt_call_r2save - 1]);
|
}
|
}
|
return TRUE;
|
return TRUE;
|
}
|
}
|
|
|
/* This function undoes the changes made by add_symbol_adjust. */
|
/* This function undoes the changes made by add_symbol_adjust. */
|
Line 11917... |
Line 12146... |
bfd_byte *loc;
|
bfd_byte *loc;
|
struct got_entry **local_got_ents;
|
struct got_entry **local_got_ents;
|
bfd_vma TOCstart;
|
bfd_vma TOCstart;
|
bfd_boolean ret = TRUE;
|
bfd_boolean ret = TRUE;
|
bfd_boolean is_opd;
|
bfd_boolean is_opd;
|
/* Disabled until we sort out how ld should choose 'y' vs 'at'. */
|
/* Assume 'at' branch hints. */
|
bfd_boolean is_power4 = FALSE;
|
bfd_boolean is_isa_v2 = TRUE;
|
bfd_vma d_offset = (bfd_big_endian (output_bfd) ? 2 : 0);
|
bfd_vma d_offset = (bfd_big_endian (output_bfd) ? 2 : 0);
|
|
|
/* Initialize howto table if needed. */
|
/* Initialize howto table if needed. */
|
if (!ppc64_elf_howto_table[R_PPC64_ADDR32])
|
if (!ppc64_elf_howto_table[R_PPC64_ADDR32])
|
ppc_howto_init ();
|
ppc_howto_init ();
|
Line 12550... |
Line 12779... |
&& h->oh->is_func_descriptor)
|
&& h->oh->is_func_descriptor)
|
fdh = ppc_follow_link (h->oh);
|
fdh = ppc_follow_link (h->oh);
|
stub_entry = ppc_get_stub_entry (input_section, sec, fdh, rel, htab);
|
stub_entry = ppc_get_stub_entry (input_section, sec, fdh, rel, htab);
|
if (stub_entry != NULL
|
if (stub_entry != NULL
|
&& (stub_entry->stub_type == ppc_stub_plt_call
|
&& (stub_entry->stub_type == ppc_stub_plt_call
|
|
|| stub_entry->stub_type == ppc_stub_plt_call_r2save
|
|| stub_entry->stub_type == ppc_stub_plt_branch_r2off
|
|| stub_entry->stub_type == ppc_stub_plt_branch_r2off
|
|| stub_entry->stub_type == ppc_stub_long_branch_r2off))
|
|| stub_entry->stub_type == ppc_stub_long_branch_r2off))
|
{
|
{
|
bfd_boolean can_plt_call = FALSE;
|
bfd_boolean can_plt_call = FALSE;
|
|
|
Line 12578... |
Line 12808... |
}
|
}
|
}
|
}
|
|
|
if (!can_plt_call)
|
if (!can_plt_call)
|
{
|
{
|
if (stub_entry->stub_type == ppc_stub_plt_call)
|
if (stub_entry->stub_type == ppc_stub_plt_call
|
|
|| stub_entry->stub_type == ppc_stub_plt_call_r2save)
|
{
|
{
|
/* If this is a plain branch rather than a branch
|
/* If this is a plain branch rather than a branch
|
and link, don't require a nop. However, don't
|
and link, don't require a nop. However, don't
|
allow tail calls in a shared library as they
|
allow tail calls in a shared library as they
|
will result in r2 being corrupted. */
|
will result in r2 being corrupted. */
|
Line 12625... |
Line 12856... |
ret = FALSE;
|
ret = FALSE;
|
}
|
}
|
}
|
}
|
|
|
if (can_plt_call
|
if (can_plt_call
|
&& stub_entry->stub_type == ppc_stub_plt_call)
|
&& (stub_entry->stub_type == ppc_stub_plt_call
|
|
|| stub_entry->stub_type == ppc_stub_plt_call_r2save))
|
unresolved_reloc = FALSE;
|
unresolved_reloc = FALSE;
|
}
|
}
|
|
|
if ((stub_entry == NULL
|
if ((stub_entry == NULL
|
|| stub_entry->stub_type == ppc_stub_long_branch
|
|| stub_entry->stub_type == ppc_stub_long_branch
|
Line 12671... |
Line 12903... |
relocation = (stub_entry->stub_offset
|
relocation = (stub_entry->stub_offset
|
+ stub_entry->stub_sec->output_offset
|
+ stub_entry->stub_sec->output_offset
|
+ stub_entry->stub_sec->output_section->vma);
|
+ stub_entry->stub_sec->output_section->vma);
|
addend = 0;
|
addend = 0;
|
|
|
if (stub_entry->stub_type == ppc_stub_plt_call
|
if ((stub_entry->stub_type == ppc_stub_plt_call
|
|
|| stub_entry->stub_type == ppc_stub_plt_call_r2save)
|
|
&& (ALWAYS_EMIT_R2SAVE
|
|
|| stub_entry->stub_type == ppc_stub_plt_call_r2save)
|
&& rel + 1 < relend
|
&& rel + 1 < relend
|
&& rel[1].r_offset == rel->r_offset + 4
|
&& rel[1].r_offset == rel->r_offset + 4
|
&& ELF64_R_TYPE (rel[1].r_info) == R_PPC64_TOCSAVE)
|
&& ELF64_R_TYPE (rel[1].r_info) == R_PPC64_TOCSAVE)
|
relocation += 4;
|
relocation += 4;
|
}
|
}
|
|
|
if (insn != 0)
|
if (insn != 0)
|
{
|
{
|
if (is_power4)
|
if (is_isa_v2)
|
{
|
{
|
/* Set 'a' bit. This is 0b00010 in BO field for branch
|
/* Set 'a' bit. This is 0b00010 in BO field for branch
|
on CR(BI) insns (BO == 001at or 011at), and 0b01000
|
on CR(BI) insns (BO == 001at or 011at), and 0b01000
|
for branch on CTR insns (BO == 1a00t or 1a01t). */
|
for branch on CTR insns (BO == 1a00t or 1a01t). */
|
if ((insn & (0x14 << 21)) == (0x04 << 21))
|
if ((insn & (0x14 << 21)) == (0x04 << 21))
|
Line 13898... |
Line 14133... |
return TRUE;
|
return TRUE;
|
}
|
}
|
|
|
#include "elf64-target.h"
|
#include "elf64-target.h"
|
|
|
No newline at end of file
|
No newline at end of file
|
|
/* FreeBSD support */
|
|
|
|
#undef TARGET_LITTLE_SYM
|
|
#undef TARGET_LITTLE_NAME
|
|
|
|
#undef TARGET_BIG_SYM
|
|
#define TARGET_BIG_SYM bfd_elf64_powerpc_freebsd_vec
|
|
#undef TARGET_BIG_NAME
|
|
#define TARGET_BIG_NAME "elf64-powerpc-freebsd"
|
|
|
|
#undef ELF_OSABI
|
|
#define ELF_OSABI ELFOSABI_FREEBSD
|
|
|
|
#undef elf64_bed
|
|
#define elf64_bed elf64_powerpc_fbsd_bed
|
|
|
|
#include "elf64-target.h"
|
|
|
|
|
No newline at end of file
|
No newline at end of file
|