Line 1280... |
Line 1280... |
FALSE, /* partial_inplace */
|
FALSE, /* partial_inplace */
|
0, /* src_mask */
|
0, /* src_mask */
|
0, /* dst_mask */
|
0, /* dst_mask */
|
FALSE), /* pcrel_offset */
|
FALSE), /* pcrel_offset */
|
|
|
|
HOWTO (R_PPC64_TOCSAVE,
|
|
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_TOCSAVE", /* 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
|
/* Computes the load module index of the load module that contains the
|
definition of its TLS sym. */
|
definition of its TLS sym. */
|
HOWTO (R_PPC64_DTPMOD64,
|
HOWTO (R_PPC64_DTPMOD64,
|
0, /* rightshift */
|
0, /* rightshift */
|
4, /* size (0 = byte, 1 = short, 2 = long) */
|
4, /* size (0 = byte, 1 = short, 2 = long) */
|
Line 2596... |
Line 2610... |
/* A copy of relocs before they are modified for --emit-relocs. */
|
/* A copy of relocs before they are modified for --emit-relocs. */
|
Elf_Internal_Rela *opd_relocs;
|
Elf_Internal_Rela *opd_relocs;
|
|
|
/* Nonzero if this bfd has small toc/got relocs, ie. that expect
|
/* Nonzero if this bfd has small toc/got relocs, ie. that expect
|
the reloc to be in the range -32768 to 32767. */
|
the reloc to be in the range -32768 to 32767. */
|
unsigned int has_small_toc_reloc;
|
unsigned int has_small_toc_reloc : 1;
|
|
|
|
/* Set if toc/got ha relocs detected not using r2, or lo reloc
|
|
instruction not one we handle. */
|
|
unsigned int unexpected_toc_insn : 1;
|
};
|
};
|
|
|
#define ppc64_elf_tdata(bfd) \
|
#define ppc64_elf_tdata(bfd) \
|
((struct ppc64_elf_obj_tdata *) (bfd)->tdata.any)
|
((struct ppc64_elf_obj_tdata *) (bfd)->tdata.any)
|
|
|
Line 3672... |
Line 3690... |
struct bfd_hash_table stub_hash_table;
|
struct bfd_hash_table stub_hash_table;
|
|
|
/* Another hash table for plt_branch stubs. */
|
/* Another hash table for plt_branch stubs. */
|
struct bfd_hash_table branch_hash_table;
|
struct bfd_hash_table branch_hash_table;
|
|
|
|
/* Hash table for function prologue tocsave. */
|
|
htab_t tocsave_htab;
|
|
|
/* Linker stub bfd. */
|
/* Linker stub bfd. */
|
bfd *stub_bfd;
|
bfd *stub_bfd;
|
|
|
/* Linker call-backs. */
|
/* Linker call-backs. */
|
asection * (*add_stub_section) (const char *, asection *);
|
asection * (*add_stub_section) (const char *, asection *);
|
Line 3918... |
Line 3939... |
}
|
}
|
|
|
return entry;
|
return entry;
|
}
|
}
|
|
|
|
struct tocsave_entry {
|
|
asection *sec;
|
|
bfd_vma offset;
|
|
};
|
|
|
|
static hashval_t
|
|
tocsave_htab_hash (const void *p)
|
|
{
|
|
const struct tocsave_entry *e = (const struct tocsave_entry *) p;
|
|
return ((bfd_vma)(intptr_t) e->sec ^ e->offset) >> 3;
|
|
}
|
|
|
|
static int
|
|
tocsave_htab_eq (const void *p1, const void *p2)
|
|
{
|
|
const struct tocsave_entry *e1 = (const struct tocsave_entry *) p1;
|
|
const struct tocsave_entry *e2 = (const struct tocsave_entry *) p2;
|
|
return e1->sec == e2->sec && e1->offset == e2->offset;
|
|
}
|
|
|
/* Create a ppc64 ELF linker hash table. */
|
/* Create a ppc64 ELF linker hash table. */
|
|
|
static struct bfd_link_hash_table *
|
static struct bfd_link_hash_table *
|
ppc64_elf_link_hash_table_create (bfd *abfd)
|
ppc64_elf_link_hash_table_create (bfd *abfd)
|
{
|
{
|
Line 3948... |
Line 3989... |
/* And the branch hash table. */
|
/* And the branch hash table. */
|
if (!bfd_hash_table_init (&htab->branch_hash_table, branch_hash_newfunc,
|
if (!bfd_hash_table_init (&htab->branch_hash_table, branch_hash_newfunc,
|
sizeof (struct ppc_branch_hash_entry)))
|
sizeof (struct ppc_branch_hash_entry)))
|
return NULL;
|
return NULL;
|
|
|
|
htab->tocsave_htab = htab_try_create (1024,
|
|
tocsave_htab_hash,
|
|
tocsave_htab_eq,
|
|
NULL);
|
|
if (htab->tocsave_htab == NULL)
|
|
return NULL;
|
|
|
/* Initializing two fields of the union is just cosmetic. We really
|
/* Initializing two fields of the union is just cosmetic. We really
|
only care about glist, but when compiled on a 32-bit host the
|
only care about glist, but when compiled on a 32-bit host the
|
bfd_vma fields are larger. Setting the bfd_vma to zero makes
|
bfd_vma fields are larger. Setting the bfd_vma to zero makes
|
debugger inspection of these fields look nicer. */
|
debugger inspection of these fields look nicer. */
|
htab->elf.init_got_refcount.refcount = 0;
|
htab->elf.init_got_refcount.refcount = 0;
|
Line 3969... |
Line 4017... |
/* Free the derived linker hash table. */
|
/* Free the derived linker hash table. */
|
|
|
static void
|
static void
|
ppc64_elf_link_hash_table_free (struct bfd_link_hash_table *hash)
|
ppc64_elf_link_hash_table_free (struct bfd_link_hash_table *hash)
|
{
|
{
|
struct ppc_link_hash_table *ret = (struct ppc_link_hash_table *) hash;
|
struct ppc_link_hash_table *htab = (struct ppc_link_hash_table *) hash;
|
|
|
bfd_hash_table_free (&ret->stub_hash_table);
|
bfd_hash_table_free (&htab->stub_hash_table);
|
bfd_hash_table_free (&ret->branch_hash_table);
|
bfd_hash_table_free (&htab->branch_hash_table);
|
|
if (htab->tocsave_htab)
|
|
htab_delete (htab->tocsave_htab);
|
_bfd_generic_link_hash_table_free (hash);
|
_bfd_generic_link_hash_table_free (hash);
|
}
|
}
|
|
|
/* Satisfy the ELF linker by filling in some fields in our fake bfd. */
|
/* Satisfy the ELF linker by filling in some fields in our fake bfd. */
|
|
|
Line 5666... |
Line 5716... |
|| eh->elf.root.type == bfd_link_hash_defweak)
|
|| eh->elf.root.type == bfd_link_hash_defweak)
|
&& (eh->elf.ref_dynamic
|
&& (eh->elf.ref_dynamic
|
|| (!info->executable
|
|| (!info->executable
|
&& eh->elf.def_regular
|
&& eh->elf.def_regular
|
&& ELF_ST_VISIBILITY (eh->elf.other) != STV_INTERNAL
|
&& ELF_ST_VISIBILITY (eh->elf.other) != STV_INTERNAL
|
&& ELF_ST_VISIBILITY (eh->elf.other) != STV_HIDDEN)))
|
&& ELF_ST_VISIBILITY (eh->elf.other) != STV_HIDDEN
|
|
&& (strchr (eh->elf.root.root.string, ELF_VER_CHR) != NULL
|
|
|| !bfd_hide_sym_by_version (info->version_info,
|
|
eh->elf.root.root.string)))))
|
{
|
{
|
asection *code_sec;
|
asection *code_sec;
|
struct ppc_link_hash_entry *fh;
|
struct ppc_link_hash_entry *fh;
|
|
|
eh->elf.root.u.def.section->flags |= SEC_KEEP;
|
eh->elf.root.u.def.section->flags |= SEC_KEEP;
|
Line 6709... |
Line 6762... |
&& (next_r == -1 || next_r == -2))
|
&& (next_r == -1 || next_r == -2))
|
return 1 - next_r;
|
return 1 - next_r;
|
return 1;
|
return 1;
|
}
|
}
|
|
|
|
/* Find (or create) an entry in the tocsave hash table. */
|
|
|
|
static struct tocsave_entry *
|
|
tocsave_find (struct ppc_link_hash_table *htab,
|
|
enum insert_option insert,
|
|
Elf_Internal_Sym **local_syms,
|
|
const Elf_Internal_Rela *irela,
|
|
bfd *ibfd)
|
|
{
|
|
unsigned long r_indx;
|
|
struct elf_link_hash_entry *h;
|
|
Elf_Internal_Sym *sym;
|
|
struct tocsave_entry ent, *p;
|
|
hashval_t hash;
|
|
struct tocsave_entry **slot;
|
|
|
|
r_indx = ELF64_R_SYM (irela->r_info);
|
|
if (!get_sym_h (&h, &sym, &ent.sec, NULL, local_syms, r_indx, ibfd))
|
|
return NULL;
|
|
if (ent.sec == NULL || ent.sec->output_section == NULL)
|
|
{
|
|
(*_bfd_error_handler)
|
|
(_("%B: undefined symbol on R_PPC64_TOCSAVE relocation"));
|
|
return NULL;
|
|
}
|
|
|
|
if (h != NULL)
|
|
ent.offset = h->root.u.def.value;
|
|
else
|
|
ent.offset = sym->st_value;
|
|
ent.offset += irela->r_addend;
|
|
|
|
hash = tocsave_htab_hash (&ent);
|
|
slot = ((struct tocsave_entry **)
|
|
htab_find_slot_with_hash (htab->tocsave_htab, &ent, hash, insert));
|
|
if (slot == NULL)
|
|
return NULL;
|
|
|
|
if (*slot == NULL)
|
|
{
|
|
p = (struct tocsave_entry *) bfd_alloc (ibfd, sizeof (*p));
|
|
if (p == NULL)
|
|
return NULL;
|
|
*p = ent;
|
|
*slot = p;
|
|
}
|
|
return *slot;
|
|
}
|
|
|
/* Adjust all global syms defined in opd sections. In gcc generated
|
/* Adjust all global syms defined in opd sections. In gcc generated
|
code for the old ABI, these will already have been done. */
|
code for the old ABI, these will already have been done. */
|
|
|
static bfd_boolean
|
static bfd_boolean
|
adjust_opd_syms (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED)
|
adjust_opd_syms (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED)
|
Line 7879... |
Line 7981... |
{
|
{
|
struct ppc_link_hash_entry *eh;
|
struct ppc_link_hash_entry *eh;
|
struct adjust_toc_info *toc_inf = (struct adjust_toc_info *) inf;
|
struct adjust_toc_info *toc_inf = (struct adjust_toc_info *) inf;
|
unsigned long i;
|
unsigned long i;
|
|
|
if (h->root.type == bfd_link_hash_indirect)
|
|
return TRUE;
|
|
|
|
if (h->root.type != bfd_link_hash_defined
|
if (h->root.type != bfd_link_hash_defined
|
&& h->root.type != bfd_link_hash_defweak)
|
&& h->root.type != bfd_link_hash_defweak)
|
return TRUE;
|
return TRUE;
|
|
|
eh = (struct ppc_link_hash_entry *) h;
|
eh = (struct ppc_link_hash_entry *) h;
|
Line 7916... |
Line 8015... |
toc_inf->global_toc_syms = TRUE;
|
toc_inf->global_toc_syms = TRUE;
|
|
|
return TRUE;
|
return TRUE;
|
}
|
}
|
|
|
|
/* Return TRUE iff INSN is one we expect on a _LO variety toc/got reloc. */
|
|
|
|
static bfd_boolean
|
|
ok_lo_toc_insn (unsigned int insn)
|
|
{
|
|
return ((insn & (0x3f << 26)) == 14u << 26 /* addi */
|
|
|| (insn & (0x3f << 26)) == 32u << 26 /* lwz */
|
|
|| (insn & (0x3f << 26)) == 34u << 26 /* lbz */
|
|
|| (insn & (0x3f << 26)) == 36u << 26 /* stw */
|
|
|| (insn & (0x3f << 26)) == 38u << 26 /* stb */
|
|
|| (insn & (0x3f << 26)) == 40u << 26 /* lhz */
|
|
|| (insn & (0x3f << 26)) == 42u << 26 /* lha */
|
|
|| (insn & (0x3f << 26)) == 44u << 26 /* sth */
|
|
|| (insn & (0x3f << 26)) == 46u << 26 /* lmw */
|
|
|| (insn & (0x3f << 26)) == 47u << 26 /* stmw */
|
|
|| (insn & (0x3f << 26)) == 48u << 26 /* lfs */
|
|
|| (insn & (0x3f << 26)) == 50u << 26 /* lfd */
|
|
|| (insn & (0x3f << 26)) == 52u << 26 /* stfs */
|
|
|| (insn & (0x3f << 26)) == 54u << 26 /* stfd */
|
|
|| ((insn & (0x3f << 26)) == 58u << 26 /* lwa,ld,lmd */
|
|
&& (insn & 3) != 1)
|
|
|| ((insn & (0x3f << 26)) == 62u << 26 /* std, stmd */
|
|
&& ((insn & 3) == 0 || (insn & 3) == 3))
|
|
|| (insn & (0x3f << 26)) == 12u << 26 /* addic */);
|
|
}
|
|
|
/* Examine all relocs referencing .toc sections in order to remove
|
/* Examine all relocs referencing .toc sections in order to remove
|
unused .toc entries. */
|
unused .toc entries. */
|
|
|
bfd_boolean
|
bfd_boolean
|
ppc64_elf_edit_toc (struct bfd_link_info *info)
|
ppc64_elf_edit_toc (struct bfd_link_info *info)
|
Line 8170... |
Line 8295... |
unsigned long r_symndx;
|
unsigned long r_symndx;
|
asection *sym_sec;
|
asection *sym_sec;
|
struct elf_link_hash_entry *h;
|
struct elf_link_hash_entry *h;
|
Elf_Internal_Sym *sym;
|
Elf_Internal_Sym *sym;
|
bfd_vma val;
|
bfd_vma val;
|
|
enum {no_check, check_lo, check_ha} insn_check;
|
|
|
r_type = ELF64_R_TYPE (rel->r_info);
|
r_type = ELF64_R_TYPE (rel->r_info);
|
switch (r_type)
|
switch (r_type)
|
{
|
{
|
|
default:
|
|
insn_check = no_check;
|
|
break;
|
|
|
|
case R_PPC64_GOT_TLSLD16_HA:
|
|
case R_PPC64_GOT_TLSGD16_HA:
|
|
case R_PPC64_GOT_TPREL16_HA:
|
|
case R_PPC64_GOT_DTPREL16_HA:
|
|
case R_PPC64_GOT16_HA:
|
|
case R_PPC64_TOC16_HA:
|
|
insn_check = check_ha;
|
|
break;
|
|
|
|
case R_PPC64_GOT_TLSLD16_LO:
|
|
case R_PPC64_GOT_TLSGD16_LO:
|
|
case R_PPC64_GOT_TPREL16_LO_DS:
|
|
case R_PPC64_GOT_DTPREL16_LO_DS:
|
|
case R_PPC64_GOT16_LO:
|
|
case R_PPC64_GOT16_LO_DS:
|
|
case R_PPC64_TOC16_LO:
|
|
case R_PPC64_TOC16_LO_DS:
|
|
insn_check = check_lo;
|
|
break;
|
|
}
|
|
|
|
if (insn_check != no_check)
|
|
{
|
|
bfd_vma off = rel->r_offset & ~3;
|
|
unsigned char buf[4];
|
|
unsigned int insn;
|
|
|
|
if (!bfd_get_section_contents (ibfd, sec, buf, off, 4))
|
|
{
|
|
free (used);
|
|
goto error_ret;
|
|
}
|
|
insn = bfd_get_32 (ibfd, buf);
|
|
if (insn_check == check_lo
|
|
? !ok_lo_toc_insn (insn)
|
|
: ((insn & ((0x3f << 26) | 0x1f << 16))
|
|
!= ((15u << 26) | (2 << 16)) /* addis rt,2,imm */))
|
|
{
|
|
char str[12];
|
|
|
|
ppc64_elf_tdata (ibfd)->unexpected_toc_insn = 1;
|
|
sprintf (str, "%#08x", insn);
|
|
info->callbacks->einfo
|
|
(_("%P: %H: toc optimization is not supported for"
|
|
" %s instruction.\n"),
|
|
ibfd, sec, rel->r_offset & ~3, str);
|
|
}
|
|
}
|
|
|
|
switch (r_type)
|
|
{
|
case R_PPC64_TOC16:
|
case R_PPC64_TOC16:
|
case R_PPC64_TOC16_LO:
|
case R_PPC64_TOC16_LO:
|
case R_PPC64_TOC16_HI:
|
case R_PPC64_TOC16_HI:
|
case R_PPC64_TOC16_HA:
|
case R_PPC64_TOC16_HA:
|
case R_PPC64_TOC16_DS:
|
case R_PPC64_TOC16_DS:
|
Line 8221... |
Line 8402... |
break;
|
break;
|
|
|
case R_PPC64_TOC16_LO_DS:
|
case R_PPC64_TOC16_LO_DS:
|
off = rel->r_offset + (bfd_big_endian (ibfd) ? -2 : 3);
|
off = rel->r_offset + (bfd_big_endian (ibfd) ? -2 : 3);
|
if (!bfd_get_section_contents (ibfd, sec, &opc, off, 1))
|
if (!bfd_get_section_contents (ibfd, sec, &opc, off, 1))
|
return FALSE;
|
{
|
|
free (used);
|
|
goto error_ret;
|
|
}
|
if ((opc & (0x3f << 2)) == (58u << 2))
|
if ((opc & (0x3f << 2)) == (58u << 2))
|
break;
|
break;
|
/* Fall thru */
|
/* Fall thru */
|
|
|
default:
|
default:
|
Line 8265... |
Line 8449... |
*drop &= ~ref_from_discarded;
|
*drop &= ~ref_from_discarded;
|
if ((*drop & can_optimize) != 0)
|
if ((*drop & can_optimize) != 0)
|
some_unused = 1;
|
some_unused = 1;
|
last = 0;
|
last = 0;
|
}
|
}
|
else if (*drop & ref_from_discarded)
|
else if ((*drop & ref_from_discarded) != 0)
|
{
|
{
|
some_unused = 1;
|
some_unused = 1;
|
last = ref_from_discarded;
|
last = ref_from_discarded;
|
}
|
}
|
else
|
else
|
Line 8383... |
Line 8567... |
case R_PPC64_TOC16_LO_DS:
|
case R_PPC64_TOC16_LO_DS:
|
rel->r_info = ELF64_R_INFO (tsym, R_PPC64_LO_DS_OPT);
|
rel->r_info = ELF64_R_INFO (tsym, R_PPC64_LO_DS_OPT);
|
break;
|
break;
|
|
|
default:
|
default:
|
abort ();
|
if (!ppc64_elf_howto_table[R_PPC64_ADDR32])
|
|
ppc_howto_init ();
|
|
info->callbacks->einfo
|
|
(_("%P: %H: %s relocation references "
|
|
"optimized away TOC entry\n"),
|
|
ibfd, sec, rel->r_offset,
|
|
ppc64_elf_howto_table[r_type]->name);
|
|
bfd_set_error (bfd_error_bad_value);
|
|
goto error_ret;
|
}
|
}
|
rel->r_addend = tocrel->r_addend;
|
rel->r_addend = tocrel->r_addend;
|
elf_section_data (sec)->relocs = relstart;
|
elf_section_data (sec)->relocs = relstart;
|
continue;
|
continue;
|
}
|
}
|
Line 9285... |
Line 9477... |
|
|
if (PPC_HA (offset) != 0)
|
if (PPC_HA (offset) != 0)
|
{
|
{
|
if (r != NULL)
|
if (r != NULL)
|
{
|
{
|
|
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 + 8;
|
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;
|
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[2].r_offset = r[1].r_offset + 4;
|
r[2].r_offset = r[1].r_offset + 4;
|
Line 9308... |
Line 9501... |
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;
|
}
|
}
|
}
|
}
|
}
|
}
|
bfd_put_32 (obfd, ADDIS_R12_R2 | PPC_HA (offset), p), p += 4;
|
|
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, 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;
|
Line 11138... |
Line 11331... |
goto error_ret_free_internal;
|
goto error_ret_free_internal;
|
if (*tls_mask != 0)
|
if (*tls_mask != 0)
|
continue;
|
continue;
|
}
|
}
|
|
|
|
if (stub_type == ppc_stub_plt_call
|
|
&& irela + 1 < irelaend
|
|
&& irela[1].r_offset == irela->r_offset + 4
|
|
&& ELF64_R_TYPE (irela[1].r_info) == R_PPC64_TOCSAVE
|
|
&& !tocsave_find (htab, INSERT,
|
|
&local_syms, irela + 1, input_bfd))
|
|
goto error_ret_free_internal;
|
|
|
/* 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. */
|
stub_name = ppc_stub_name (id_sec, sym_sec, hash, irela);
|
stub_name = ppc_stub_name (id_sec, sym_sec, hash, irela);
|
Line 11666... |
Line 11867... |
return 0;
|
return 0;
|
|
|
return _bfd_elf_default_action_discarded (sec);
|
return _bfd_elf_default_action_discarded (sec);
|
}
|
}
|
|
|
/* REL points to a low-part reloc on a largetoc instruction sequence.
|
|
Find the matching high-part reloc instruction and verify that it
|
|
is addis REG,x,imm. If so, set *REG to x and return a pointer to
|
|
the high-part reloc. */
|
|
|
|
static const Elf_Internal_Rela *
|
|
ha_reloc_match (const Elf_Internal_Rela *relocs,
|
|
const Elf_Internal_Rela *rel,
|
|
unsigned int *reg,
|
|
bfd_boolean match_addend,
|
|
const bfd *input_bfd,
|
|
const bfd_byte *contents)
|
|
{
|
|
enum elf_ppc64_reloc_type r_type, r_type_ha;
|
|
bfd_vma r_info_ha, r_addend;
|
|
|
|
r_type = ELF64_R_TYPE (rel->r_info);
|
|
switch (r_type)
|
|
{
|
|
case R_PPC64_GOT_TLSLD16_LO:
|
|
case R_PPC64_GOT_TLSGD16_LO:
|
|
case R_PPC64_GOT_TPREL16_LO_DS:
|
|
case R_PPC64_GOT_DTPREL16_LO_DS:
|
|
case R_PPC64_GOT16_LO:
|
|
case R_PPC64_TOC16_LO:
|
|
r_type_ha = r_type + 2;
|
|
break;
|
|
case R_PPC64_GOT16_LO_DS:
|
|
r_type_ha = R_PPC64_GOT16_HA;
|
|
break;
|
|
case R_PPC64_TOC16_LO_DS:
|
|
r_type_ha = R_PPC64_TOC16_HA;
|
|
break;
|
|
default:
|
|
abort ();
|
|
}
|
|
r_info_ha = ELF64_R_INFO (ELF64_R_SYM (rel->r_info), r_type_ha);
|
|
r_addend = rel->r_addend;
|
|
|
|
while (--rel >= relocs)
|
|
if (rel->r_info == r_info_ha
|
|
&& (!match_addend
|
|
|| rel->r_addend == r_addend))
|
|
{
|
|
const bfd_byte *p = contents + (rel->r_offset & ~3);
|
|
unsigned int insn = bfd_get_32 (input_bfd, p);
|
|
if ((insn & (0x3f << 26)) == (15u << 26) /* addis rt,x,imm */
|
|
&& (insn & (0x1f << 21)) == (*reg << 21))
|
|
{
|
|
*reg = (insn >> 16) & 0x1f;
|
|
return rel;
|
|
}
|
|
break;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* The RELOCATE_SECTION function is called by the ELF backend linker
|
/* The RELOCATE_SECTION function is called by the ELF backend linker
|
to handle the relocations for a section.
|
to handle the relocations for a section.
|
|
|
The relocs are always passed as Rela structures; if the section
|
The relocs are always passed as Rela structures; if the section
|
actually uses Rel structures, the r_addend field will always be
|
actually uses Rel structures, the r_addend field will always be
|
Line 11770... |
Line 11914... |
Elf_Internal_Rela *rel;
|
Elf_Internal_Rela *rel;
|
Elf_Internal_Rela *relend;
|
Elf_Internal_Rela *relend;
|
Elf_Internal_Rela outrel;
|
Elf_Internal_Rela outrel;
|
bfd_byte *loc;
|
bfd_byte *loc;
|
struct got_entry **local_got_ents;
|
struct got_entry **local_got_ents;
|
unsigned char *ha_opt;
|
|
bfd_vma TOCstart;
|
bfd_vma TOCstart;
|
bfd_boolean no_ha_opt;
|
|
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'. */
|
/* Disabled until we sort out how ld should choose 'y' vs 'at'. */
|
bfd_boolean is_power4 = FALSE;
|
bfd_boolean is_power4 = FALSE;
|
bfd_vma d_offset = (bfd_big_endian (output_bfd) ? 2 : 0);
|
bfd_vma d_offset = (bfd_big_endian (output_bfd) ? 2 : 0);
|
Line 11798... |
Line 11940... |
local_got_ents = elf_local_got_ents (input_bfd);
|
local_got_ents = elf_local_got_ents (input_bfd);
|
TOCstart = elf_gp (output_bfd);
|
TOCstart = elf_gp (output_bfd);
|
symtab_hdr = &elf_symtab_hdr (input_bfd);
|
symtab_hdr = &elf_symtab_hdr (input_bfd);
|
sym_hashes = elf_sym_hashes (input_bfd);
|
sym_hashes = elf_sym_hashes (input_bfd);
|
is_opd = ppc64_elf_section_data (input_section)->sec_type == sec_opd;
|
is_opd = ppc64_elf_section_data (input_section)->sec_type == sec_opd;
|
ha_opt = NULL;
|
|
no_ha_opt = FALSE;
|
|
|
|
rel = relocs;
|
rel = relocs;
|
relend = relocs + input_section->reloc_count;
|
relend = relocs + input_section->reloc_count;
|
for (; rel < relend; rel++)
|
for (; rel < relend; rel++)
|
{
|
{
|
Line 12363... |
Line 12503... |
switch (r_type)
|
switch (r_type)
|
{
|
{
|
default:
|
default:
|
break;
|
break;
|
|
|
|
case R_PPC64_TOCSAVE:
|
|
if (relocation + addend == (rel->r_offset
|
|
+ input_section->output_offset
|
|
+ input_section->output_section->vma)
|
|
&& tocsave_find (htab, NO_INSERT,
|
|
&local_syms, rel, input_bfd))
|
|
{
|
|
insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
|
|
if (insn == NOP
|
|
|| insn == CROR_151515 || insn == CROR_313131)
|
|
bfd_put_32 (input_bfd, STD_R2_40R1,
|
|
contents + rel->r_offset);
|
|
}
|
|
break;
|
|
|
/* Branch taken prediction relocations. */
|
/* Branch taken prediction relocations. */
|
case R_PPC64_ADDR14_BRTAKEN:
|
case R_PPC64_ADDR14_BRTAKEN:
|
case R_PPC64_REL14_BRTAKEN:
|
case 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. */
|
/* Fall thru. */
|
/* Fall thru. */
|
Line 12515... |
Line 12670... |
rather than the procedure directly. */
|
rather than the procedure directly. */
|
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
|
|
&& rel + 1 < relend
|
|
&& rel[1].r_offset == rel->r_offset + 4
|
|
&& ELF64_R_TYPE (rel[1].r_info) == R_PPC64_TOCSAVE)
|
|
relocation += 4;
|
}
|
}
|
|
|
if (insn != 0)
|
if (insn != 0)
|
{
|
{
|
if (is_power4)
|
if (is_power4)
|
Line 12574... |
Line 12735... |
|
|
case R_PPC64_NONE:
|
case R_PPC64_NONE:
|
case R_PPC64_TLS:
|
case R_PPC64_TLS:
|
case R_PPC64_TLSGD:
|
case R_PPC64_TLSGD:
|
case R_PPC64_TLSLD:
|
case R_PPC64_TLSLD:
|
|
case R_PPC64_TOCSAVE:
|
case R_PPC64_GNU_VTINHERIT:
|
case R_PPC64_GNU_VTINHERIT:
|
case R_PPC64_GNU_VTENTRY:
|
case R_PPC64_GNU_VTENTRY:
|
continue;
|
continue;
|
|
|
/* GOT16 relocations. Like an ADDR16 using the symbol's
|
/* GOT16 relocations. Like an ADDR16 using the symbol's
|
Line 13211... |
Line 13373... |
case R_PPC64_GOT_TLSGD16_HA:
|
case R_PPC64_GOT_TLSGD16_HA:
|
case R_PPC64_GOT_TPREL16_HA:
|
case R_PPC64_GOT_TPREL16_HA:
|
case R_PPC64_GOT_DTPREL16_HA:
|
case R_PPC64_GOT_DTPREL16_HA:
|
case R_PPC64_GOT16_HA:
|
case R_PPC64_GOT16_HA:
|
case R_PPC64_TOC16_HA:
|
case R_PPC64_TOC16_HA:
|
/* nop is done later. */
|
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_put_32 (input_bfd, NOP, p);
|
|
}
|
break;
|
break;
|
|
|
case R_PPC64_GOT_TLSLD16_LO:
|
case R_PPC64_GOT_TLSLD16_LO:
|
case R_PPC64_GOT_TLSGD16_LO:
|
case R_PPC64_GOT_TLSGD16_LO:
|
case R_PPC64_GOT_TPREL16_LO_DS:
|
case R_PPC64_GOT_TPREL16_LO_DS:
|
case R_PPC64_GOT_DTPREL16_LO_DS:
|
case R_PPC64_GOT_DTPREL16_LO_DS:
|
case R_PPC64_GOT16_LO:
|
case R_PPC64_GOT16_LO:
|
case R_PPC64_GOT16_LO_DS:
|
case R_PPC64_GOT16_LO_DS:
|
case R_PPC64_TOC16_LO:
|
case R_PPC64_TOC16_LO:
|
case R_PPC64_TOC16_LO_DS:
|
case R_PPC64_TOC16_LO_DS:
|
if (htab->do_toc_opt && relocation + addend + 0x8000 < 0x10000)
|
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 = contents + (rel->r_offset & ~3);
|
insn = bfd_get_32 (input_bfd, p);
|
insn = bfd_get_32 (input_bfd, p);
|
if ((insn & (0x3f << 26)) == 14u << 26 /* addi */
|
if ((insn & (0x3f << 26)) == 12u << 26 /* addic */)
|
|| (insn & (0x3f << 26)) == 32u << 26 /* lwz */
|
|
|| (insn & (0x3f << 26)) == 34u << 26 /* lbz */
|
|
|| (insn & (0x3f << 26)) == 36u << 26 /* stw */
|
|
|| (insn & (0x3f << 26)) == 38u << 26 /* stb */
|
|
|| (insn & (0x3f << 26)) == 40u << 26 /* lhz */
|
|
|| (insn & (0x3f << 26)) == 42u << 26 /* lha */
|
|
|| (insn & (0x3f << 26)) == 44u << 26 /* sth */
|
|
|| (insn & (0x3f << 26)) == 46u << 26 /* lmw */
|
|
|| (insn & (0x3f << 26)) == 47u << 26 /* stmw */
|
|
|| (insn & (0x3f << 26)) == 48u << 26 /* lfs */
|
|
|| (insn & (0x3f << 26)) == 50u << 26 /* lfd */
|
|
|| (insn & (0x3f << 26)) == 52u << 26 /* stfs */
|
|
|| (insn & (0x3f << 26)) == 54u << 26 /* stfd */
|
|
|| ((insn & (0x3f << 26)) == 58u << 26 /* lwa,ld,lmd */
|
|
&& (insn & 3) != 1)
|
|
|| ((insn & (0x3f << 26)) == 62u << 26 /* std, stmd */
|
|
&& ((insn & 3) == 0 || (insn & 3) == 3)))
|
|
{
|
|
unsigned int reg = (insn >> 16) & 0x1f;
|
|
const Elf_Internal_Rela *ha;
|
|
bfd_boolean match_addend;
|
|
|
|
match_addend = (sym != NULL
|
|
&& ELF_ST_TYPE (sym->st_info) == STT_SECTION);
|
|
ha = ha_reloc_match (relocs, rel, ®, match_addend,
|
|
input_bfd, contents);
|
|
if (ha != NULL)
|
|
{
|
{
|
insn &= ~(0x1f << 16);
|
/* Transform addic to addi when we change reg. */
|
insn |= reg << 16;
|
insn &= ~((0x3f << 26) | (0x1f << 16));
|
bfd_put_32 (input_bfd, insn, p);
|
insn |= (14u << 26) | (2 << 16);
|
if (ha_opt == NULL)
|
|
{
|
|
ha_opt = bfd_zmalloc (input_section->reloc_count);
|
|
if (ha_opt == NULL)
|
|
return FALSE;
|
|
}
|
|
ha_opt[ha - relocs] = 1;
|
|
}
|
}
|
else
|
else
|
/* If we don't find a matching high part insn,
|
{
|
something is fishy. Refuse to nop any high
|
insn &= ~(0x1f << 16);
|
part insn in this section. */
|
insn |= 2 << 16;
|
no_ha_opt = TRUE;
|
|
}
|
}
|
|
bfd_put_32 (input_bfd, insn, p);
|
}
|
}
|
break;
|
break;
|
}
|
}
|
|
|
/* Do any further special processing. */
|
/* Do any further special processing. */
|
Line 13366... |
Line 13500... |
/* Dynamic relocs are not propagated for SEC_DEBUGGING sections
|
/* Dynamic relocs are not propagated for SEC_DEBUGGING sections
|
because such sections are not SEC_ALLOC and thus ld.so will
|
because such sections are not SEC_ALLOC and thus ld.so will
|
not process them. */
|
not process them. */
|
if (unresolved_reloc
|
if (unresolved_reloc
|
&& !((input_section->flags & SEC_DEBUGGING) != 0
|
&& !((input_section->flags & SEC_DEBUGGING) != 0
|
&& h->elf.def_dynamic))
|
&& h->elf.def_dynamic)
|
|
&& _bfd_elf_section_offset (output_bfd, info, input_section,
|
|
rel->r_offset) != (bfd_vma) -1)
|
{
|
{
|
info->callbacks->einfo
|
info->callbacks->einfo
|
(_("%P: %H: unresolvable %s relocation against symbol `%s'\n"),
|
(_("%P: %H: unresolvable %s relocation against symbol `%s'\n"),
|
input_bfd, input_section, rel->r_offset,
|
input_bfd, input_section, rel->r_offset,
|
ppc64_elf_howto_table[(int) r_type]->name,
|
ppc64_elf_howto_table[(int) r_type]->name,
|
Line 13424... |
Line 13560... |
ret = FALSE;
|
ret = FALSE;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
if (ha_opt != NULL)
|
|
{
|
|
if (!no_ha_opt)
|
|
{
|
|
unsigned char *opt = ha_opt;
|
|
rel = relocs;
|
|
relend = relocs + input_section->reloc_count;
|
|
for (; rel < relend; opt++, rel++)
|
|
if (*opt != 0)
|
|
{
|
|
bfd_byte *p = contents + (rel->r_offset & ~3);
|
|
bfd_put_32 (input_bfd, NOP, p);
|
|
}
|
|
}
|
|
free (ha_opt);
|
|
}
|
|
|
|
/* If we're emitting relocations, then shortly after this function
|
/* If we're emitting relocations, then shortly after this function
|
returns, reloc offsets and addends for this section will be
|
returns, reloc offsets and addends for this section will be
|
adjusted. Worse, reloc symbol indices will be for the output
|
adjusted. Worse, reloc symbol indices will be for the output
|
file rather than the input. Save a copy of the relocs for
|
file rather than the input. Save a copy of the relocs for
|
opd_entry_value. */
|
opd_entry_value. */
|