/* PowerPC64-specific support for 64-bit ELF.
- Copyright (C) 1999-2015 Free Software Foundation, Inc.
+ Copyright (C) 1999-2016 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 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 ADDIS_R12_R2 0x3d820000 /* addis %r12,%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) */
0xffff, /* dst_mask */
TRUE), /* pcrel_offset */
+ /* Like R_PPC64_REL16_HA but for split field in addpcis. */
+ HOWTO (R_PPC64_REL16DX_HA, /* type */
+ 16, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ ppc64_elf_ha_reloc, /* special_function */
+ "R_PPC64_REL16DX_HA", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x1fffc1, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
/* Like R_PPC64_ADDR16_HI, but no overflow. */
HOWTO (R_PPC64_ADDR16_HIGH, /* type */
16, /* rightshift */
0xffff, /* dst_mask */
FALSE), /* pcrel_offset */
+ /* Marker reloc on ELFv2 large-model function entry. */
+ HOWTO (R_PPC64_ENTRY,
+ 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_ENTRY", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
/* Like ADDR64, but use local entry point of function. */
HOWTO (R_PPC64_ADDR64_LOCAL, /* type */
0, /* rightshift */
break;
case BFD_RELOC_HI16_S_PCREL: r = R_PPC64_REL16_HA;
break;
+ case BFD_RELOC_PPC_REL16DX_HA: r = R_PPC64_REL16DX_HA;
+ break;
+ case BFD_RELOC_PPC64_ENTRY: r = R_PPC64_ENTRY;
+ break;
case BFD_RELOC_PPC64_ADDR64_LOCAL: r = R_PPC64_ADDR64_LOCAL;
break;
case BFD_RELOC_VTABLE_INHERIT: r = R_PPC64_GNU_VTINHERIT;
void *data, asection *input_section,
bfd *output_bfd, char **error_message)
{
+ enum elf_ppc64_reloc_type r_type;
+ long insn;
+ bfd_size_type octets;
+ bfd_vma value;
+
/* If this is a relocatable link (output_bfd test tells us), just
call the generic function. Any adjustment will be done at final
link time. */
We won't actually be using the low 16 bits, so trashing them
doesn't matter. */
reloc_entry->addend += 0x8000;
- return bfd_reloc_continue;
+ r_type = reloc_entry->howto->type;
+ if (r_type != R_PPC64_REL16DX_HA)
+ return bfd_reloc_continue;
+
+ value = 0;
+ if (!bfd_is_com_section (symbol->section))
+ value = symbol->value;
+ value += (reloc_entry->addend
+ + symbol->section->output_offset
+ + symbol->section->output_section->vma);
+ value -= (reloc_entry->address
+ + input_section->output_offset
+ + input_section->output_section->vma);
+ value = (bfd_signed_vma) value >> 16;
+
+ octets = reloc_entry->address * bfd_octets_per_byte (abfd);
+ insn = bfd_get_32 (abfd, (bfd_byte *) data + octets);
+ insn &= ~0x1fffc1;
+ insn |= (value & 0xffc1) | ((value & 0x3e) << 15);
+ bfd_put_32 (abfd, insn, (bfd_byte *) data + octets);
+ if (value + 0x8000 > 0xffff)
+ return bfd_reloc_overflow;
+ return bfd_reloc_ok;
}
static bfd_reloc_status_type
htab = ppc_hash_table (info);
- /* Create .sfpr for code to save and restore fp regs. */
flags = (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_READONLY
| SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
- htab->sfpr = bfd_make_section_anyway_with_flags (dynobj, ".sfpr",
- flags);
- if (htab->sfpr == NULL
- || ! bfd_set_section_alignment (dynobj, htab->sfpr, 2))
- return FALSE;
+ if (htab->params->save_restore_funcs)
+ {
+ /* Create .sfpr for code to save and restore fp regs. */
+ htab->sfpr = bfd_make_section_anyway_with_flags (dynobj, ".sfpr",
+ flags);
+ if (htab->sfpr == NULL
+ || ! bfd_set_section_alignment (dynobj, htab->sfpr, 2))
+ return FALSE;
+ }
+
+ if (bfd_link_relocatable (info))
+ return TRUE;
/* Create .glink for lazy dynamic linking support. */
htab->glink = bfd_make_section_anyway_with_flags (dynobj, ".glink",
linker created stub bfd. This ensures that the GOT header is at
the start of the output TOC section. */
htab = ppc_hash_table (info);
- if (htab == NULL)
- return FALSE;
htab->elf.dynobj = params->stub_bfd;
htab->params = params;
- if (bfd_link_relocatable (info))
- return TRUE;
-
return create_linkage_sections (htab->elf.dynobj, info);
}
enum elf_ppc64_reloc_type r_type;
int tls_type;
struct _ppc64_elf_section_data *ppc64_sec;
- struct plt_entry **ifunc;
+ struct plt_entry **ifunc, **plt_list;
r_symndx = ELF64_R_SYM (rel->r_info);
if (r_symndx < symtab_hdr->sh_info)
return FALSE;
}
}
- r_type = ELF64_R_TYPE (rel->r_info);
- if (is_branch_reloc (r_type))
- {
- if (h != NULL && (h == tga || h == dottga))
- {
- if (rel != relocs
- && (ELF64_R_TYPE (rel[-1].r_info) == R_PPC64_TLSGD
- || ELF64_R_TYPE (rel[-1].r_info) == R_PPC64_TLSLD))
- /* We have a new-style __tls_get_addr call with a marker
- reloc. */
- ;
- else
- /* Mark this section as having an old-style call. */
- sec->has_tls_get_addr_call = 1;
- }
-
- /* STT_GNU_IFUNC symbols must have a PLT entry. */
- if (ifunc != NULL
- && !update_plt_info (abfd, ifunc, rel->r_addend))
- return FALSE;
- }
+ r_type = ELF64_R_TYPE (rel->r_info);
switch (r_type)
{
case R_PPC64_TLSGD:
case R_PPC64_PLT16_LO:
case R_PPC64_PLT32:
case R_PPC64_PLT64:
- /* This symbol requires a procedure linkage table entry. We
- actually build the entry in adjust_dynamic_symbol,
- because this might be a case of linking PIC code without
- linking in any dynamic objects, in which case we don't
- need to generate a procedure linkage table after all. */
- if (h == NULL)
- {
- /* It does not make sense to have a procedure linkage
- table entry for a local symbol. */
- bfd_set_error (bfd_error_bad_value);
- return FALSE;
- }
- else
+ /* This symbol requires a procedure linkage table entry. */
+ plt_list = ifunc;
+ if (h != NULL)
{
- if (!update_plt_info (abfd, &h->plt.plist, rel->r_addend))
- return FALSE;
h->needs_plt = 1;
if (h->root.root.string[0] == '.'
&& h->root.root.string[1] != '\0')
((struct ppc_link_hash_entry *) h)->is_func = 1;
+ 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
+ (_("%P: %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;
+ }
+ if (!update_plt_info (abfd, plt_list, rel->r_addend))
+ return FALSE;
break;
/* The following relocations don't need to propagate the
case R_PPC64_REL16_LO:
case R_PPC64_REL16_HI:
case R_PPC64_REL16_HA:
+ case R_PPC64_REL16DX_HA:
break;
/* Not supported as a dynamic relocation. */
sec->has_toc_reloc = 1;
break;
+ /* Marker reloc. */
+ case R_PPC64_ENTRY:
+ break;
+
/* This relocation describes the C++ object vtable hierarchy.
Reconstruct it for later use during GC. */
case R_PPC64_GNU_VTINHERIT:
/* Fall through. */
case R_PPC64_REL24:
- if (h != NULL && ifunc == NULL)
+ plt_list = ifunc;
+ if (h != NULL)
{
- /* We may need a .plt entry if the function this reloc
- refers to is in a shared lib. */
- if (!update_plt_info (abfd, &h->plt.plist, rel->r_addend))
- return FALSE;
h->needs_plt = 1;
if (h->root.root.string[0] == '.'
&& h->root.root.string[1] != '\0')
((struct ppc_link_hash_entry *) h)->is_func = 1;
+
if (h == tga || h == dottga)
- sec->has_tls_reloc = 1;
+ {
+ sec->has_tls_reloc = 1;
+ if (rel != relocs
+ && (ELF64_R_TYPE (rel[-1].r_info) == R_PPC64_TLSGD
+ || ELF64_R_TYPE (rel[-1].r_info) == R_PPC64_TLSLD))
+ /* We have a new-style __tls_get_addr call with
+ a marker reloc. */
+ ;
+ else
+ /* Mark this section as having an old-style call. */
+ sec->has_tls_get_addr_call = 1;
+ }
+ plt_list = &h->plt.plist;
}
+
+ /* We may need a .plt entry if the function this reloc
+ refers to is in a shared lib. */
+ if (plt_list
+ && !update_plt_info (abfd, plt_list, rel->r_addend))
+ return FALSE;
break;
+ case R_PPC64_ADDR14:
+ case R_PPC64_ADDR14_BRNTAKEN:
+ case R_PPC64_ADDR14_BRTAKEN:
+ case R_PPC64_ADDR24:
+ goto dodyn;
+
case R_PPC64_TPREL64:
tls_type = TLS_EXPLICIT | TLS_TLS | TLS_TPREL;
if (bfd_link_pic (info))
case R_PPC64_REL30:
case R_PPC64_REL32:
case R_PPC64_REL64:
- case R_PPC64_ADDR14:
- case R_PPC64_ADDR14_BRNTAKEN:
- case R_PPC64_ADDR14_BRTAKEN:
- case R_PPC64_ADDR24:
case R_PPC64_ADDR32:
case R_PPC64_UADDR16:
case R_PPC64_UADDR32:
unsigned long r_symndx;
enum elf_ppc64_reloc_type r_type;
struct elf_link_hash_entry *h = NULL;
+ struct plt_entry **plt_list;
unsigned char tls_type = 0;
r_symndx = ELF64_R_SYM (rel->r_info);
}
}
- if (is_branch_reloc (r_type))
- {
- struct plt_entry **ifunc = NULL;
- if (h != NULL)
- {
- if (h->type == STT_GNU_IFUNC)
- ifunc = &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)
- ifunc = local_plt + r_symndx;
- }
- if (ifunc != NULL)
- {
- struct plt_entry *ent;
-
- for (ent = *ifunc; ent != NULL; ent = ent->next)
- if (ent->addend == rel->r_addend)
- break;
- if (ent == NULL)
- abort ();
- if (ent->plt.refcount > 0)
- ent->plt.refcount -= 1;
- continue;
- }
- }
-
switch (r_type)
{
case R_PPC64_GOT_TLSLD16:
case R_PPC64_REL14_BRNTAKEN:
case R_PPC64_REL14_BRTAKEN:
case R_PPC64_REL24:
+ plt_list = NULL;
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;
+ }
+ if (plt_list)
{
struct plt_entry *ent;
- for (ent = h->plt.plist; ent != NULL; ent = ent->next)
+ for (ent = *plt_list; ent != NULL; ent = ent->next)
if (ent->addend == rel->r_addend)
break;
if (ent != NULL && ent->plt.refcount > 0)
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, FALSE, FALSE, TRUE);
+ elf_link_hash_lookup (&htab->elf, sym, writing, TRUE, TRUE);
if (stub_sec != NULL)
{
if (h != NULL
h->elf.root.u.def.value = htab->sfpr->size;
h->elf.type = STT_FUNC;
h->elf.def_regular = 1;
+ h->elf.non_elf = 0;
_bfd_elf_link_hash_hide_symbol (info, &h->elf, TRUE);
writing = TRUE;
if (htab->sfpr->contents == NULL)
struct bfd_link_info *info)
{
struct ppc_link_hash_table *htab;
- unsigned int i;
htab = ppc_hash_table (info);
if (htab == NULL)
return FALSE;
- if (!bfd_link_relocatable (info)
- && htab->elf.hgot != NULL)
+ /* Provide any missing _save* and _rest* functions. */
+ if (htab->sfpr != NULL)
+ {
+ unsigned int i;
+
+ htab->sfpr->size = 0;
+ for (i = 0; i < ARRAY_SIZE (save_res_funcs); i++)
+ if (!sfpr_define (info, &save_res_funcs[i], NULL))
+ return FALSE;
+ if (htab->sfpr->size == 0)
+ htab->sfpr->flags |= SEC_EXCLUDE;
+ }
+
+ if (bfd_link_relocatable (info))
+ return TRUE;
+
+ if (htab->elf.hgot != NULL)
{
_bfd_elf_link_hash_hide_symbol (info, htab->elf.hgot, TRUE);
/* Make .TOC. defined so as to prevent it being made dynamic.
| STV_HIDDEN);
}
- if (htab->sfpr == NULL)
- /* We don't have any relocs. */
- return TRUE;
-
- /* Provide any missing _save* and _rest* functions. */
- htab->sfpr->size = 0;
- if (htab->params->save_restore_funcs)
- for (i = 0; i < ARRAY_SIZE (save_res_funcs); i++)
- if (!sfpr_define (info, &save_res_funcs[i], NULL))
- return FALSE;
-
elf_link_hash_traverse (&htab->elf, func_desc_adjust, info);
- if (htab->sfpr->size == 0)
- htab->sfpr->flags |= SEC_EXCLUDE;
-
return TRUE;
}
tga_fd->root.type = bfd_link_hash_indirect;
tga_fd->root.u.i.link = &opt_fd->root;
ppc64_elf_copy_indirect_symbol (info, opt_fd, tga_fd);
+ opt_fd->forced_local = 0;
if (opt_fd->dynindx != -1)
{
/* Use __tls_get_addr_opt in dynamic relocations. */
tga->root.type = bfd_link_hash_indirect;
tga->root.u.i.link = &opt->root;
ppc64_elf_copy_indirect_symbol (info, opt, tga);
+ opt->forced_local = 0;
_bfd_elf_link_hash_hide_symbol (info, opt,
tga->forced_local);
htab->tls_get_addr = (struct ppc_link_hash_entry *) opt;
if (h != NULL
&& h->root.type == bfd_link_hash_undefweak)
ok_tprel = TRUE;
- else
+ else if (sym_sec != NULL
+ && sym_sec->output_section != NULL)
{
value += sym_sec->output_offset;
value += sym_sec->output_section->vma;
goto error_ret;
if (sym_sec == NULL
+ || sym_sec->output_section == NULL
|| discarded_section (sym_sec))
continue;
if (hash == NULL)
{
sym_value = sym->st_value;
- ok_dest = TRUE;
+ if (sym_sec != NULL
+ && sym_sec->output_section != NULL)
+ ok_dest = TRUE;
}
else if (hash->elf.root.type == bfd_link_hash_defined
|| hash->elf.root.type == bfd_link_hash_defweak)
if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
stub_sec->size = ((stub_sec->size
+ (1 << htab->params->plt_stub_align) - 1)
- & (-1 << htab->params->plt_stub_align));
+ & -(1 << htab->params->plt_stub_align));
for (stub_sec = htab->params->stub_bfd->sections;
stub_sec != NULL;
if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
stub_sec->size = ((stub_sec->size
+ (1 << htab->params->plt_stub_align) - 1)
- & (-1 << htab->params->plt_stub_align));
+ & -(1 << htab->params->plt_stub_align));
for (stub_sec = htab->params->stub_bfd->sections;
stub_sec != NULL;
Elf_Internal_Shdr *symtab_hdr;
struct elf_link_hash_entry **sym_hashes;
Elf_Internal_Rela *rel;
+ Elf_Internal_Rela *wrel;
Elf_Internal_Rela *relend;
Elf_Internal_Rela outrel;
bfd_byte *loc;
sym_hashes = elf_sym_hashes (input_bfd);
is_opd = ppc64_elf_section_data (input_section)->sec_type == sec_opd;
- rel = relocs;
+ rel = wrel = relocs;
relend = relocs + input_section->reloc_count;
- for (; rel < relend; rel++)
+ for (; rel < relend; wrel++, rel++)
{
enum elf_ppc64_reloc_type r_type;
bfd_vma addend;
struct ppc_stub_hash_entry *stub_entry;
bfd_vma max_br_offset;
bfd_vma from;
- const Elf_Internal_Rela orig_rel = *rel;
+ Elf_Internal_Rela orig_rel;
reloc_howto_type *howto;
struct reloc_howto_struct alt_howto;
+ again:
+ orig_rel = *rel;
+
r_type = ELF64_R_TYPE (rel->r_info);
r_symndx = ELF64_R_SYM (rel->r_info);
symbol of the previous ADDR64 reloc. The symbol gives us the
proper TOC base to use. */
if (rel->r_info == ELF64_R_INFO (0, R_PPC64_TOC)
- && rel != relocs
- && ELF64_R_TYPE (rel[-1].r_info) == R_PPC64_ADDR64
+ && wrel != relocs
+ && ELF64_R_TYPE (wrel[-1].r_info) == R_PPC64_ADDR64
&& is_opd)
- r_symndx = ELF64_R_SYM (rel[-1].r_info);
+ r_symndx = ELF64_R_SYM (wrel[-1].r_info);
sym = NULL;
sec = NULL;
h = (struct ppc_link_hash_entry *) h_elf;
if (sec != NULL && discarded_section (sec))
- RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
- rel, 1, relend,
- ppc64_elf_howto_table[r_type], 0,
- contents);
+ {
+ _bfd_clear_contents (ppc64_elf_howto_table[r_type],
+ input_bfd, input_section,
+ contents + rel->r_offset);
+ wrel->r_offset = rel->r_offset;
+ wrel->r_info = 0;
+ wrel->r_addend = 0;
+
+ /* For ld -r, remove relocations in debug sections against
+ sections 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)
+ && (input_section->flags & SEC_DEBUGGING))
+ wrel--;
+
+ continue;
+ }
if (bfd_link_relocatable (info))
- continue;
+ goto copy_reloc;
if (h != NULL && &h->elf == htab->elf.hgot)
{
&& (tls_mask & TLS_TPREL) == 0)
{
toctprel:
- insn = bfd_get_32 (output_bfd, contents + rel->r_offset - d_offset);
+ insn = bfd_get_32 (output_bfd,
+ contents + rel->r_offset - d_offset);
insn &= 31 << 21;
insn |= 0x3c0d0000; /* addis 0,13,0 */
- bfd_put_32 (output_bfd, insn, contents + rel->r_offset - d_offset);
+ bfd_put_32 (output_bfd, insn,
+ contents + rel->r_offset - d_offset);
r_type = R_PPC64_TPREL16_HA;
if (toc_symndx != 0)
{
rel->r_addend = toc_addend;
/* We changed the symbol. Start over in order to
get h, sym, sec etc. right. */
- rel--;
- continue;
+ goto again;
}
else
rel->r_info = ELF64_R_INFO (r_symndx, r_type);
rel->r_addend = toc_addend;
/* We changed the symbol. Start over in order to
get h, sym, sec etc. right. */
- rel--;
- continue;
+ goto again;
}
else
rel->r_info = ELF64_R_INFO (r_symndx, r_type);
{
/* We changed the symbol. Start over in order
to get h, sym, sec etc. right. */
- rel--;
- continue;
+ goto again;
}
}
break;
}
bfd_put_32 (output_bfd, insn2, contents + offset);
if ((tls_mask & TLS_TPRELGD) == 0 && toc_symndx != 0)
- {
- rel--;
- continue;
- }
+ goto again;
}
break;
insn2 = NOP;
}
bfd_put_32 (output_bfd, insn2, contents + offset);
- rel--;
- continue;
+ goto again;
}
break;
}
break;
+ case R_PPC64_ENTRY:
+ relocation = TOCstart + htab->sec_info[input_section->id].toc_off;
+ if (!bfd_link_pic (info)
+ && !info->traditional_format
+ && relocation + 0x80008000 <= 0xffffffff)
+ {
+ unsigned int insn1, insn2;
+
+ insn1 = bfd_get_32 (input_bfd, contents + rel->r_offset);
+ insn2 = bfd_get_32 (input_bfd, contents + rel->r_offset + 4);
+ if ((insn1 & ~0xfffc) == LD_R2_0R12
+ && insn2 == ADD_R2_R2_R12)
+ {
+ bfd_put_32 (output_bfd,
+ LIS_R2 + PPC_HA (relocation),
+ contents + rel->r_offset);
+ bfd_put_32 (output_bfd,
+ ADDI_R2_R2 + PPC_LO (relocation),
+ contents + rel->r_offset + 4);
+ }
+ }
+ else
+ {
+ relocation -= (rel->r_offset
+ + input_section->output_offset
+ + input_section->output_section->vma);
+ if (relocation + 0x80008000 <= 0xffffffff)
+ {
+ unsigned int insn1, insn2;
+
+ insn1 = bfd_get_32 (input_bfd, contents + rel->r_offset);
+ insn2 = bfd_get_32 (input_bfd, contents + rel->r_offset + 4);
+ if ((insn1 & ~0xfffc) == LD_R2_0R12
+ && insn2 == ADD_R2_R2_R12)
+ {
+ bfd_put_32 (output_bfd,
+ ADDIS_R2_R12 + PPC_HA (relocation),
+ contents + rel->r_offset);
+ bfd_put_32 (output_bfd,
+ ADDI_R2_R2 + PPC_LO (relocation),
+ contents + rel->r_offset + 4);
+ }
+ }
+ }
+ break;
+
case R_PPC64_REL16_HA:
/* If we are generating a non-PIC executable, edit
. 0: addis 2,12,.TOC.-0b@ha
if .TOC. is in range. */
if (!bfd_link_pic (info)
&& !info->traditional_format
+ && !htab->opd_abi
+ && rel->r_addend == d_offset
&& h != NULL && &h->elf == htab->elf.hgot
&& rel + 1 < relend
&& rel[1].r_info == ELF64_R_INFO (r_symndx, R_PPC64_REL16_LO)
bfd_vma offset = rel->r_offset - d_offset;
insn1 = bfd_get_32 (output_bfd, contents + offset);
insn2 = bfd_get_32 (output_bfd, contents + offset + 4);
- if ((insn1 & 0xffff0000) == 0x3c4c0000 /* addis 2,12 */
- && (insn2 & 0xffff0000) == 0x38420000 /* addi 2,2 */)
+ if ((insn1 & 0xffff0000) == ADDIS_R2_R12
+ && (insn2 & 0xffff0000) == ADDI_R2_R2)
{
r_type = R_PPC64_ADDR16_HA;
rel->r_info = ELF64_R_INFO (r_symndx, r_type);
rel->r_addend -= d_offset;
rel[1].r_info = ELF64_R_INFO (r_symndx, R_PPC64_ADDR16_LO);
rel[1].r_addend -= d_offset + 4;
- bfd_put_32 (output_bfd, 0x3c400000, contents + offset);
+ bfd_put_32 (output_bfd, LIS_R2, contents + offset);
}
}
break;
&& addend == 0)
{
bfd_put_32 (output_bfd, NOP, contents + rel->r_offset);
- continue;
+ goto copy_reloc;
}
break;
}
bfd_set_error (bfd_error_bad_value);
ret = FALSE;
- continue;
+ goto copy_reloc;
case R_PPC64_NONE:
case R_PPC64_TLS:
case R_PPC64_TOCSAVE:
case R_PPC64_GNU_VTINHERIT:
case R_PPC64_GNU_VTENTRY:
- continue;
+ case R_PPC64_ENTRY:
+ goto copy_reloc;
/* GOT16 relocations. Like an ADDR16 using the symbol's
address in the GOT as relocation value instead of the
case R_PPC64_PLT64:
/* Relocation is to the entry for this symbol in the
procedure linkage table. */
+ {
+ struct plt_entry **plt_list = NULL;
+ if (h != NULL)
+ plt_list = &h->elf.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;
+ }
+ if (plt_list)
+ {
+ struct plt_entry *ent;
- /* Resolve a PLT reloc against a local symbol directly,
- without using the procedure linkage table. */
- if (h == NULL)
- break;
-
- /* It's possible that we didn't make a PLT entry for this
- symbol. This happens when statically linking PIC code,
- or when using -Bsymbolic. Go find a match if there is a
- PLT entry. */
- if (htab->elf.splt != NULL)
- {
- struct plt_entry *ent;
- for (ent = h->elf.plt.plist; ent != NULL; ent = ent->next)
- if (ent->plt.offset != (bfd_vma) -1
- && ent->addend == orig_rel.r_addend)
- {
- relocation = (htab->elf.splt->output_section->vma
- + htab->elf.splt->output_offset
- + ent->plt.offset);
- unresolved_reloc = FALSE;
- break;
- }
- }
+ for (ent = *plt_list; ent != NULL; ent = ent->next)
+ if (ent->plt.offset != (bfd_vma) -1
+ && ent->addend == orig_rel.r_addend)
+ {
+ asection *plt;
+
+ plt = htab->elf.splt;
+ if (!htab->elf.dynamic_sections_created
+ || h == NULL
+ || h->elf.dynindx == -1)
+ plt = htab->elf.iplt;
+ relocation = (plt->output_section->vma
+ + plt->output_offset
+ + ent->plt.offset);
+ addend = 0;
+ unresolved_reloc = FALSE;
+ break;
+ }
+ }
+ }
break;
case R_PPC64_TOC:
case R_PPC64_REL16_LO:
case R_PPC64_REL16_HI:
case R_PPC64_REL16_HA:
+ case R_PPC64_REL16DX_HA:
break;
case R_PPC64_REL14:
bfd_set_error (bfd_error_invalid_operation);
ret = FALSE;
- continue;
+ goto copy_reloc;
}
/* Multi-instruction sequences that access the TOC can be
break;
case R_PPC64_REL16_HA:
+ case R_PPC64_REL16DX_HA:
case R_PPC64_ADDR16_HA:
case R_PPC64_ADDR16_HIGHA:
case R_PPC64_ADDR16_HIGHERA:
case R_PPC64_DTPREL16_LO_DS:
insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3));
mask = 3;
- /* If this reloc is against an lq insn, then the value must be
- a multiple of 16. This is somewhat of a hack, but the
- "correct" way to do this by defining _DQ forms of all the
- _DS relocs bloats all reloc switches in this file. It
- doesn't seem to make much sense to use any of these relocs
- in data, so testing the insn should be safe. */
- if ((insn & (0x3f << 26)) == (56u << 26))
+ /* If this reloc is against an lq, lxv, or stxv insn, then
+ the value must be a multiple of 16. This is somewhat of
+ a hack, but the "correct" way to do this by defining _DQ
+ forms of all the _DS relocs bloats all reloc switches in
+ this file. It doesn't make much sense to use these
+ relocs in data, so testing the insn should be safe. */
+ if ((insn & (0x3f << 26)) == (56u << 26)
+ || ((insn & (0x3f << 26)) == (61u << 26) && (insn & 3) == 1))
mask = 15;
- if (((relocation + addend) & mask) != 0)
+ relocation += addend;
+ addend = insn & (mask ^ 3);
+ if ((relocation & mask) != 0)
{
+ relocation ^= relocation & mask;
info->callbacks->einfo
(_("%P: %H: error: %s not a multiple of %u\n"),
input_bfd, input_section, rel->r_offset,
mask + 1);
bfd_set_error (bfd_error_bad_value);
ret = FALSE;
- continue;
+ goto copy_reloc;
}
break;
}
}
}
- r = _bfd_final_link_relocate (howto, input_bfd, input_section, contents,
- rel->r_offset, relocation, addend);
+ if (r_type == R_PPC64_REL16DX_HA)
+ {
+ /* Split field reloc isn't handled by _bfd_final_link_relocate. */
+ if (rel->r_offset + 4 > input_section->size)
+ r = bfd_reloc_outofrange;
+ else
+ {
+ relocation += addend;
+ relocation -= (rel->r_offset
+ + input_section->output_offset
+ + input_section->output_section->vma);
+ relocation = (bfd_signed_vma) relocation >> 16;
+ insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+ insn &= ~0x1fffc1;
+ insn |= (relocation & 0xffc1) | ((relocation & 0x3e) << 15);
+ bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+ r = bfd_reloc_ok;
+ if (relocation + 0x8000 > 0xffff)
+ r = bfd_reloc_overflow;
+ }
+ }
+ else
+ r = _bfd_final_link_relocate (howto, input_bfd, input_section, contents,
+ rel->r_offset, relocation, addend);
if (r != bfd_reloc_ok)
{
if (more_info != NULL)
free (more_info);
}
+ copy_reloc:
+ if (wrel != rel)
+ *wrel = *rel;
+ }
+
+ if (wrel != rel)
+ {
+ Elf_Internal_Shdr *rel_hdr;
+ size_t deleted = rel - wrel;
+
+ rel_hdr = _bfd_elf_single_rel_hdr (input_section->output_section);
+ rel_hdr->sh_size -= rel_hdr->sh_entsize * deleted;
+ if (rel_hdr->sh_size == 0)
+ {
+ /* It is too late to remove an empty reloc section. Leave
+ one NONE reloc.
+ ??? What is wrong with an empty section??? */
+ rel_hdr->sh_size = rel_hdr->sh_entsize;
+ deleted -= 1;
+ }
+ rel_hdr = _bfd_elf_single_rel_hdr (input_section);
+ rel_hdr->sh_size -= rel_hdr->sh_entsize * deleted;
+ input_section->reloc_count -= deleted;
}
/* If we're emitting relocations, then shortly after this function