/* PowerPC-specific support for 32-bit ELF
- Copyright (C) 1994-2015 Free Software Foundation, Inc.
+ Copyright (C) 1994-2016 Free Software Foundation, Inc.
Written by Ian Lance Taylor, Cygnus Support.
This file is part of BFD, the Binary File Descriptor library.
#include "elf32-ppc.h"
#include "elf-vxworks.h"
#include "dwarf2.h"
-#include "elf-linux-psinfo.h"
typedef enum split16_format_type
{
0xffff, /* dst_mask */
TRUE), /* pcrel_offset */
+ /* Like R_PPC_REL16_HA but for split field in addpcis. */
+ HOWTO (R_PPC_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 */
+ ppc_elf_addr16_ha_reloc, /* special_function */
+ "R_PPC_REL16DX_HA", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x1fffc1, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
/* GNU extension to record C++ vtable hierarchy. */
HOWTO (R_PPC_GNU_VTINHERIT, /* type */
0, /* rightshift */
char pr_psargs[80]; /* Initial part of arg list. */
};
-/* Helper macro to swap (properly handling endianess) things from the
- `elf_internal_prpsinfo' structure to the `elf_external_ppc_prpsinfo32'
- structure.
-
- Note that FROM should be a pointer, and TO should be the explicit type. */
-
-#define PPC_LINUX_PRPSINFO32_SWAP_FIELDS(abfd, from, to) \
- do \
- { \
- H_PUT_8 (abfd, from->pr_state, &to.pr_state); \
- H_PUT_8 (abfd, from->pr_sname, &to.pr_sname); \
- H_PUT_8 (abfd, from->pr_zomb, &to.pr_zomb); \
- H_PUT_8 (abfd, from->pr_nice, &to.pr_nice); \
- H_PUT_32 (abfd, from->pr_flag, to.pr_flag); \
- H_PUT_32 (abfd, from->pr_uid, to.pr_uid); \
- H_PUT_32 (abfd, from->pr_gid, to.pr_gid); \
- H_PUT_32 (abfd, from->pr_pid, to.pr_pid); \
- H_PUT_32 (abfd, from->pr_ppid, to.pr_ppid); \
- H_PUT_32 (abfd, from->pr_pgrp, to.pr_pgrp); \
- H_PUT_32 (abfd, from->pr_sid, to.pr_sid); \
- strncpy (to.pr_fname, from->pr_fname, sizeof (to.pr_fname)); \
- strncpy (to.pr_psargs, from->pr_psargs, sizeof (to.pr_psargs)); \
- } while (0)
+/* Helper function to copy an elf_internal_linux_prpsinfo in host
+ endian to an elf_external_ppc_linux_prpsinfo32 in target endian. */
+static inline void
+swap_ppc_linux_prpsinfo32_out (bfd *obfd,
+ const struct elf_internal_linux_prpsinfo *from,
+ struct elf_external_ppc_linux_prpsinfo32 *to)
+{
+ bfd_put_8 (obfd, from->pr_state, &to->pr_state);
+ bfd_put_8 (obfd, from->pr_sname, &to->pr_sname);
+ bfd_put_8 (obfd, from->pr_zomb, &to->pr_zomb);
+ bfd_put_8 (obfd, from->pr_nice, &to->pr_nice);
+ bfd_put_32 (obfd, from->pr_flag, to->pr_flag);
+ bfd_put_32 (obfd, from->pr_uid, to->pr_uid);
+ bfd_put_32 (obfd, from->pr_gid, to->pr_gid);
+ bfd_put_32 (obfd, from->pr_pid, to->pr_pid);
+ bfd_put_32 (obfd, from->pr_ppid, to->pr_ppid);
+ bfd_put_32 (obfd, from->pr_pgrp, to->pr_pgrp);
+ bfd_put_32 (obfd, from->pr_sid, to->pr_sid);
+ strncpy (to->pr_fname, from->pr_fname, sizeof (to->pr_fname));
+ strncpy (to->pr_psargs, from->pr_psargs, sizeof (to->pr_psargs));
+}
\f
/* Initialize the ppc_elf_howto_table, so that linear accesses can be done. */
case BFD_RELOC_LO16_PCREL: r = R_PPC_REL16_LO; break;
case BFD_RELOC_HI16_PCREL: r = R_PPC_REL16_HI; break;
case BFD_RELOC_HI16_S_PCREL: r = R_PPC_REL16_HA; break;
+ case BFD_RELOC_PPC_REL16DX_HA: r = R_PPC_REL16DX_HA; break;
case BFD_RELOC_VTABLE_INHERIT: r = R_PPC_GNU_VTINHERIT; break;
case BFD_RELOC_VTABLE_ENTRY: r = R_PPC_GNU_VTENTRY; break;
}
bfd *output_bfd,
char **error_message ATTRIBUTE_UNUSED)
{
- bfd_vma relocation;
+ enum elf_ppc_reloc_type r_type;
+ long insn;
+ bfd_size_type octets;
+ bfd_vma value;
if (output_bfd != NULL)
{
return bfd_reloc_ok;
}
- if (bfd_is_com_section (symbol->section))
- relocation = 0;
- else
- relocation = symbol->value;
-
- relocation += symbol->section->output_section->vma;
- relocation += symbol->section->output_offset;
- relocation += reloc_entry->addend;
- if (reloc_entry->howto->pc_relative)
- relocation -= reloc_entry->address;
-
- reloc_entry->addend += (relocation & 0x8000) << 1;
-
- return bfd_reloc_continue;
+ reloc_entry->addend += 0x8000;
+ r_type = reloc_entry->howto->type;
+ if (r_type != R_PPC_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 >>= 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);
+ return bfd_reloc_ok;
}
static bfd_reloc_status_type
}
char *
-elfcore_write_ppc_linux_prpsinfo32 (bfd *abfd, char *buf, int *bufsiz,
- const struct elf_internal_linux_prpsinfo *prpsinfo)
+elfcore_write_ppc_linux_prpsinfo32
+ (bfd *abfd,
+ char *buf,
+ int *bufsiz,
+ const struct elf_internal_linux_prpsinfo *prpsinfo)
{
struct elf_external_ppc_linux_prpsinfo32 data;
- memset (&data, 0, sizeof (data));
- PPC_LINUX_PRPSINFO32_SWAP_FIELDS (abfd, prpsinfo, data);
-
+ swap_ppc_linux_prpsinfo32_out (abfd, prpsinfo, &data);
return elfcore_write_note (abfd, buf, bufsiz,
"CORE", NT_PRPSINFO, &data, sizeof (data));
}
enum elf_ppc_reloc_type r_type;
struct elf_link_hash_entry *h;
int tls_type;
+ struct plt_entry **ifunc;
r_symndx = ELF32_R_SYM (rel->r_info);
if (r_symndx < symtab_hdr->sh_info)
tls_type = 0;
r_type = ELF32_R_TYPE (rel->r_info);
+ ifunc = NULL;
if (h == NULL && !htab->is_vxworks)
{
Elf_Internal_Sym *isym = bfd_sym_from_r_symndx (&htab->sym_cache,
if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
{
- struct plt_entry **ifunc;
-
/* Set PLT_IFUNC flag for this sym, no GOT entry yet. */
ifunc = update_local_sym_info (abfd, symtab_hdr, r_symndx,
PLT_IFUNC);
#ifdef DEBUG
fprintf (stderr, "Reloc requires a PLT entry\n");
#endif
- /* This symbol requires a procedure linkage table entry. We
- actually build the entry in finish_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. */
-
+ /* This symbol requires a procedure linkage table entry. */
if (h == NULL)
{
- /* It does not make sense to have a procedure linkage
- table entry for a local symbol. */
- info->callbacks->einfo (_("%P: %H: %s reloc against local symbol\n"),
- abfd, sec, rel->r_offset,
- ppc_elf_howto_table[r_type]->name);
- bfd_set_error (bfd_error_bad_value);
- return FALSE;
+ if (ifunc == 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,
+ ppc_elf_howto_table[r_type]->name);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
}
else
{
case R_PPC_REL16_LO:
case R_PPC_REL16_HI:
case R_PPC_REL16_HA:
+ case R_PPC_REL16DX_HA:
ppc_elf_tdata (abfd)->has_rel16 = 1;
break;
{
if (bfd_link_pic (info))
{
- info->callbacks->einfo (_("%P: %H: @local call to ifunc %s\n"),
- abfd, sec, rel->r_offset,
- h->root.root.string);
+ info->callbacks->einfo
+ (_("%P: %H: @local call to ifunc %s\n"),
+ abfd, sec, rel->r_offset,
+ h->root.root.string);
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
struct ppc_elf_link_hash_table *htab;
int pass;
- if (bfd_link_relocatable (info) || !bfd_link_executable (info))
+ if (!bfd_link_executable (info))
return TRUE;
htab = ppc_elf_hash_table (info);
if (elf_hash_table (info)->dynamic_sections_created)
{
/* Set the contents of the .interp section to the interpreter. */
- if (bfd_link_executable (info))
+ if (bfd_link_executable (info) && !info->nointerp)
{
s = bfd_get_linker_section (htab->elf.dynobj, ".interp");
BFD_ASSERT (s != NULL);
optimise away the call. We won't be needing a long-
branch stub in that case. */
if (bfd_link_executable (link_info)
- && !bfd_link_relocatable (link_info)
&& h == htab->tls_get_addr
&& irel != internal_relocs)
{
static bfd_boolean
is_insn_dq_form (unsigned int insn)
{
- return (insn & (0x3f << 26)) == 56u << 26; /* lq */
+ return ((insn & (0x3f << 26)) == 56u << 26 /* lq */
+ || ((insn & (0x3f << 26)) == (61u << 26) /* lxv, stxv */
+ && (insn & 3) == 1));
}
/* The RELOCATE_SECTION function is called by the ELF backend linker
struct elf_link_hash_entry **sym_hashes;
struct ppc_elf_link_hash_table *htab;
Elf_Internal_Rela *rel;
+ Elf_Internal_Rela *wrel;
Elf_Internal_Rela *relend;
Elf_Internal_Rela outrel;
asection *got2;
".tls_vars"));
if (input_section->sec_info_type == SEC_INFO_TYPE_TARGET)
relax_info = elf_section_data (input_section)->sec_info;
- rel = relocs;
+ rel = wrel = relocs;
relend = relocs + input_section->reloc_count;
- for (; rel < relend; rel++)
+ for (; rel < relend; wrel++, rel++)
{
enum elf_ppc_reloc_type r_type;
bfd_vma addend;
struct plt_entry **ifunc;
struct reloc_howto_struct alt_howto;
+ again:
r_type = ELF32_R_TYPE (rel->r_info);
sym = NULL;
sec = NULL;
howto = NULL;
if (r_type < R_PPC_max)
howto = ppc_elf_howto_table[r_type];
- RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
- rel, 1, relend, howto, 0, contents);
+
+ _bfd_clear_contents (howto, 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))
if (r_type != R_PPC_RELAX_PLT
&& r_type != R_PPC_RELAX_PLTREL24
&& r_type != R_PPC_RELAX)
- continue;
+ goto copy_reloc;
}
/* TLS optimizations. Replace instruction sequences and relocs
{
bfd_vma insn;
- 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 |= 0x3c020000; /* addis 0,2,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_PPC_TPREL16_HA;
rel->r_info = ELF32_R_INFO (r_symndx, r_type);
}
{
/* We changed the symbol on an LD reloc. Start over
in order to get h, sym, sec etc. right. */
- rel--;
- continue;
+ goto again;
}
}
break;
/* Zap the reloc on the _tls_get_addr call too. */
BFD_ASSERT (rel->r_offset - d_offset == rel[1].r_offset);
rel[1].r_info = ELF32_R_INFO (STN_UNDEF, R_PPC_NONE);
- rel--;
- continue;
+ goto again;
}
break;
}
got_addr = (htab->got->output_section->vma
+ htab->got->output_offset
+ (h->got.offset & ~1));
- rel->r_info = ELF32_R_INFO (0, R_PPC_ADDR16_HA);
- rel->r_addend = got_addr;
- rel->r_offset = (p - contents) + d_offset;
+ wrel->r_offset = (p - contents) + d_offset;
+ wrel->r_info = ELF32_R_INFO (0, R_PPC_ADDR16_HA);
+ wrel->r_addend = got_addr;
insn &= ~0xffff;
insn |= ((unsigned int )(got_addr + 0x8000) >> 16) & 0xffff;
bfd_put_32 (output_bfd, insn, p);
/* Use one of the spare relocs, so --emit-relocs
output is reasonable. */
memmove (rel + 1, rel, (relend - rel - 1) * sizeof (*rel));
- rel++;
+ wrel++, rel++;
+ rel->r_offset = wrel[-1].r_offset + 4;
rel->r_info = ELF32_R_INFO (0, R_PPC_ADDR16_LO);
- rel->r_offset += 4;
+ rel->r_addend = wrel[-1].r_addend;
/* Continue on as if we had a got reloc, to output
dynamic reloc. */
bfd_set_error (bfd_error_bad_value);
ret = FALSE;
- continue;
+ goto copy_reloc;
case R_PPC_NONE:
case R_PPC_TLS:
case R_PPC_EMB_MRKREF:
case R_PPC_GNU_VTINHERIT:
case R_PPC_GNU_VTENTRY:
- continue;
+ goto copy_reloc;
/* GOT16 relocations. Like an ADDR16 using the symbol's
address in the GOT as relocation value instead of the
/* If here for a picfixup, we're done. */
if (r_type != ELF32_R_TYPE (rel->r_info))
- continue;
+ goto copy_reloc;
relocation = (htab->got->output_section->vma
+ htab->got->output_offset
rel->r_offset,
TRUE))
return FALSE;
- continue;
+ goto copy_reloc;
}
break;
case R_PPC_REL16_LO:
case R_PPC_REL16_HI:
case R_PPC_REL16_HA:
+ case R_PPC_REL16DX_HA:
break;
case R_PPC_REL32:
bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
if (skip == -1)
- continue;
+ goto copy_reloc;
/* This reloc will be computed at runtime. We clear the memory
so that it contains predictable value. */
relocs to describe this relocation. */
BFD_ASSERT (ELF32_R_TYPE (relend[-1].r_info) == R_PPC_NONE);
/* The relocs are at the bottom 2 bytes */
- rel[0].r_offset += d_offset;
- memmove (rel + 1, rel, (relend - rel - 1) * sizeof (*rel));
- rel[0].r_info = ELF32_R_INFO (r_symndx, R_PPC_ADDR16_HA);
- rel[1].r_offset += 4;
- rel[1].r_info = ELF32_R_INFO (r_symndx, R_PPC_ADDR16_LO);
- rel++;
+ wrel->r_offset = rel->r_offset + d_offset;
+ wrel->r_info = ELF32_R_INFO (r_symndx, R_PPC_ADDR16_HA);
+ wrel->r_addend = rel->r_addend;
+ memmove (wrel + 1, wrel, (relend - wrel - 1) * sizeof (*wrel));
+ wrel++, rel++;
+ wrel->r_offset += 4;
+ wrel->r_info = ELF32_R_INFO (r_symndx, R_PPC_ADDR16_LO);
}
continue;
case R_PPC_PLTREL24:
if (h != NULL && ifunc == NULL)
{
- struct plt_entry *ent = find_plt_ent (&h->plt.plist, got2,
- bfd_link_pic (info) ? addend : 0);
+ struct plt_entry *ent;
+
+ ent = find_plt_ent (&h->plt.plist, got2,
+ bfd_link_pic (info) ? addend : 0);
if (ent == NULL
|| htab->plt == NULL)
{
relocation = relocation + addend;
ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
relocation, split16a_type);
- continue;
+ goto copy_reloc;
case R_PPC_VLE_LO16D:
relocation = relocation + addend;
ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
relocation, split16d_type);
- continue;
+ goto copy_reloc;
case R_PPC_VLE_HI16A:
relocation = (relocation + addend) >> 16;
ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
relocation, split16a_type);
- continue;
+ goto copy_reloc;
case R_PPC_VLE_HI16D:
relocation = (relocation + addend) >> 16;
ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
relocation, split16d_type);
- continue;
+ goto copy_reloc;
case R_PPC_VLE_HA16A:
relocation = (relocation + addend + 0x8000) >> 16;
ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
relocation, split16a_type);
- continue;
+ goto copy_reloc;
case R_PPC_VLE_HA16D:
relocation = (relocation + addend + 0x8000) >> 16;
ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
relocation, split16d_type);
- continue;
+ goto copy_reloc;
/* Relocate against either _SDA_BASE_, _SDA2_BASE_, or 0. */
case R_PPC_EMB_SDA21:
bfd_set_error (bfd_error_bad_value);
ret = FALSE;
- continue;
+ goto copy_reloc;
}
if (sda != NULL)
if (r_type == R_PPC_VLE_SDA21
&& ((relocation + 0x80000) & 0xffffffff) > 0x100000)
goto overflow;
- continue;
+ goto copy_reloc;
}
else if (r_type == R_PPC_EMB_SDA21
|| r_type == R_PPC_VLE_SDA21
bfd_set_error (bfd_error_bad_value);
ret = FALSE;
- continue;
+ goto copy_reloc;
}
if (sda != NULL)
value, split16d_type);
}
}
- continue;
+ goto copy_reloc;
/* Relocate against the beginning of the section. */
case R_PPC_SECTOFF:
bfd_set_error (bfd_error_invalid_operation);
ret = FALSE;
- continue;
+ goto copy_reloc;
}
/* Do any further special processing. */
case R_PPC_ADDR16_HA:
case R_PPC_REL16_HA:
+ case R_PPC_REL16DX_HA:
case R_PPC_SECTOFF_HA:
case R_PPC_TPREL16_HA:
case R_PPC_DTPREL16_HA:
that make up part of the insn opcode. */
unsigned int insn, mask, lobit;
- insn = bfd_get_32 (output_bfd, contents + rel->r_offset - d_offset);
+ insn = bfd_get_32 (output_bfd,
+ contents + rel->r_offset - d_offset);
mask = 0;
if (is_insn_ds_form (insn))
mask = 3;
mask = 15;
else
break;
- lobit = mask & (relocation + addend);
+ relocation += addend;
+ addend = insn & mask;
+ lobit = mask & relocation;
if (lobit != 0)
{
- addend -= lobit;
+ relocation ^= lobit;
info->callbacks->einfo
(_("%P: %H: error: %s against `%s' not a multiple of %u\n"),
input_bfd, input_section, rel->r_offset,
bfd_set_error (bfd_error_bad_value);
ret = FALSE;
}
- addend += insn & mask;
}
break;
}
}
}
- r = _bfd_final_link_relocate (howto, input_bfd, input_section, contents,
- rel->r_offset, relocation, addend);
+ if (r_type == R_PPC_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
+ {
+ unsigned int insn;
+
+ relocation += addend;
+ relocation -= (rel->r_offset
+ + input_section->output_offset
+ + input_section->output_section->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;
+ }
+ }
+ else
+ r = _bfd_final_link_relocate (howto, input_bfd, input_section, contents,
+ rel->r_offset, relocation, addend);
if (r != bfd_reloc_ok)
{
ret = FALSE;
}
}
+ 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;
+ wrel++;
+ }
+ relend = wrel;
+ rel_hdr = _bfd_elf_single_rel_hdr (input_section);
+ rel_hdr->sh_size -= rel_hdr->sh_entsize * deleted;
+ input_section->reloc_count -= deleted;
}
#ifdef DEBUG