PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
static struct bfd_link_hash_table *ppc64_elf_link_hash_table_create
PARAMS ((bfd *));
+static boolean create_linkage_sections
+ PARAMS ((bfd *, struct bfd_link_info *));
static boolean create_got_section
PARAMS ((bfd *, struct bfd_link_info *));
static boolean ppc64_elf_create_dynamic_sections
#define LIS_R0_0 0x3c000000 /* lis %r0,0 */
#define ORI_R0_R0_0 0x60000000 /* ori %r0,%r0,0 */
+/* Instructions to save and restore floating point regs. */
+#define STFD_FR0_0R1 0xd8010000 /* stfd %fr0,0(%r1) */
+#define LFD_FR0_0R1 0xc8010000 /* lfd %fr0,0(%r1) */
+#define BLR 0x4e800020 /* blr */
+
/* Since .opd is an array of descriptors and each entry will end up
with identical R_PPC64_RELATIVE relocs, there is really no need to
propagate .opd relocs; The dynamic linker should be taught to
- relocate .opd without reloc entries. FIXME: the dynamic linker
- will need to know where and how large .opd is via a couple of new
- DT_PPC64_* tags, or perhaps just with one reloc that specifies the
- start of .opd via its offset and the size via its addend. Also,
- .opd should be trimmed of unused values. */
+ relocate .opd without reloc entries. FIXME: .opd should be trimmed
+ of unused values. */
#ifndef NO_OPD_RELOCS
#define NO_OPD_RELOCS 0
#endif
asection *srelbss;
asection *sstub;
asection *sglink;
+ asection *sfpr;
/* Set on error. */
int plt_overflow;
struct ppc_link_hash_table *htab;
bfd_size_type amt = sizeof (struct ppc_link_hash_table);
- htab = (struct ppc_link_hash_table *) bfd_alloc (abfd, amt);
+ htab = (struct ppc_link_hash_table *) bfd_malloc (amt);
if (htab == NULL)
return NULL;
if (! _bfd_elf_link_hash_table_init (&htab->elf, abfd, link_hash_newfunc))
{
- bfd_release (abfd, htab);
+ free (htab);
return NULL;
}
htab->srelbss = NULL;
htab->sstub = NULL;
htab->sglink = NULL;
+ htab->sfpr = NULL;
htab->plt_overflow = 0;
htab->sym_sec.abfd = NULL;
return &htab->elf.root;
}
+/* Create sections for linker generated code. */
+
+static boolean
+create_linkage_sections (dynobj, info)
+ bfd *dynobj;
+ struct bfd_link_info *info;
+{
+ struct ppc_link_hash_table *htab;
+ flagword flags;
+
+ 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 (dynobj, ".sfpr");
+ if (htab->sfpr == NULL
+ || ! bfd_set_section_flags (dynobj, htab->sfpr, flags)
+ || ! bfd_set_section_alignment (dynobj, htab->sfpr, 2))
+ return false;
+
+ /* Create .stub and .glink for global linkage functions. */
+ htab->sstub = bfd_make_section (dynobj, ".stub");
+ if (htab->sstub == NULL
+ || ! bfd_set_section_flags (dynobj, htab->sstub, flags)
+ || ! bfd_set_section_alignment (dynobj, htab->sstub, 2))
+ return false;
+ htab->sglink = bfd_make_section (dynobj, ".glink");
+ if (htab->sglink == NULL
+ || ! bfd_set_section_flags (dynobj, htab->sglink, flags)
+ || ! bfd_set_section_alignment (dynobj, htab->sglink, 2))
+ return false;
+
+ return true;
+}
+
/* Create .got and .rela.got sections in DYNOBJ, and set up
shortcuts to them in our hash table. */
return true;
}
-/* Create the .stub and .glink sections as well as the ordinary
- dynamic sections. */
+/* Create the dynamic sections, and set up shortcuts. */
static boolean
ppc64_elf_create_dynamic_sections (dynobj, info)
struct bfd_link_info *info;
{
struct ppc_link_hash_table *htab;
- flagword flags;
htab = ppc_hash_table (info);
if (!htab->sgot && !create_got_section (dynobj, info))
|| (!info->shared && !htab->srelbss))
abort ();
- /* Create .stub and .glink for global linkage functions. */
- flags = (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_READONLY
- | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
- htab->sstub = bfd_make_section (dynobj, ".stub");
- if (htab->sstub == NULL
- || ! bfd_set_section_flags (dynobj, htab->sstub, flags)
- || ! bfd_set_section_alignment (dynobj, htab->sstub, 2))
- return false;
- htab->sglink = bfd_make_section (dynobj, ".glink");
- if (htab->sglink == NULL
- || ! bfd_set_section_flags (dynobj, htab->sglink, flags)
- || ! bfd_set_section_alignment (dynobj, htab->sglink, 3))
- return false;
-
return true;
}
sreloc = NULL;
is_opd = strcmp (bfd_get_section_name (abfd, sec), ".opd") == 0;
+ if (htab->elf.dynobj == NULL)
+ htab->elf.dynobj = abfd;
+ if (htab->sfpr == NULL
+ && !create_linkage_sections (htab->elf.dynobj, info))
+ return false;
+
rel_end = relocs + sec->reloc_count;
for (rel = relocs; rel < rel_end; rel++)
{
case R_PPC64_GOT16_LO_DS:
/* This symbol requires a global offset table entry. */
- if (htab->sgot == NULL)
- {
- if (htab->elf.dynobj == NULL)
- htab->elf.dynobj = abfd;
- if (!create_got_section (htab->elf.dynobj, info))
- return false;
- }
+ if (htab->sgot == NULL
+ && !create_got_section (htab->elf.dynobj, info))
+ return false;
if (h != NULL)
{
bfd_set_error (bfd_error_bad_value);
}
- if (htab->elf.dynobj == NULL)
- htab->elf.dynobj = abfd;
-
dynobj = htab->elf.dynobj;
sreloc = bfd_get_section_by_name (dynobj, name);
if (sreloc == NULL)
break;
default:
+ break;
}
}
struct bfd_link_info *info;
struct ppc_link_hash_table *htab;
- if (h->root.type == bfd_link_hash_indirect
- || h->root.type == bfd_link_hash_warning)
+ if (h->root.type == bfd_link_hash_indirect)
return true;
+ if (h->root.type == bfd_link_hash_warning)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
info = (struct bfd_link_info *) inf;
htab = ppc_hash_table (info);
if (fdh->dynindx == -1)
if (! bfd_elf64_link_record_dynamic_symbol (info, fdh))
return false;
- fdh->plt.refcount = h->plt.refcount;
fdh->elf_link_hash_flags |= (h->elf_link_hash_flags
& (ELF_LINK_HASH_REF_REGULAR
| ELF_LINK_HASH_REF_DYNAMIC
| ELF_LINK_HASH_REF_REGULAR_NONWEAK
| ELF_LINK_NON_GOT_REF));
- fdh->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
+ if (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT)
+ {
+ fdh->plt.refcount = h->plt.refcount;
+ fdh->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
+ }
((struct ppc_link_hash_entry *) fdh)->is_func_descriptor = 1;
fdh->root.root.string = h->root.root.string + 1;
}
This prevents a shared library from exporting syms that have
been imported from another library. Function code syms that
are really in the library we must leave global to prevent the
- linker dragging a definition in from a static library. */
- force_local = (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0;
+ linker dragging in a definition from a static library. */
+ force_local = ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0
+ && info->shared);
_bfd_elf_link_hash_hide_symbol (info, h, force_local);
}
return true;
}
+#define MIN_SAVE_FPR 14
+#define MAX_SAVE_FPR 31
+
/* Called near the start of bfd_elf_size_dynamic_sections. We use
- this hook to transfer dynamic linking information gathered so far
- on function code symbol entries, to their corresponding function
- descriptor symbol entries. */
+ this hook to a) provide some gcc support functions, and b) transfer
+ dynamic linking information gathered so far on function code symbol
+ entries, to their corresponding function descriptor symbol entries. */
static boolean
ppc64_elf_func_desc_adjust (obfd, info)
bfd *obfd ATTRIBUTE_UNUSED;
struct bfd_link_info *info;
{
struct ppc_link_hash_table *htab;
+ unsigned int lowest_savef = MAX_SAVE_FPR + 2;
+ unsigned int lowest_restf = MAX_SAVE_FPR + 2;
+ unsigned int i;
+ struct elf_link_hash_entry *h;
+ char sym[10];
htab = ppc_hash_table (info);
+
+ if (htab->sfpr == NULL)
+ /* We don't have any relocs. */
+ return true;
+
+ /* First provide any missing ._savef* and ._restf* functions. */
+ memcpy (sym, "._savef14", 10);
+ for (i = MIN_SAVE_FPR; i <= MAX_SAVE_FPR; i++)
+ {
+ sym[7] = i / 10 + '0';
+ sym[8] = i % 10 + '0';
+ h = elf_link_hash_lookup (&htab->elf, sym, false, false, true);
+ if (h != NULL
+ && h->root.type == bfd_link_hash_undefined)
+ {
+ if (lowest_savef > i)
+ lowest_savef = i;
+ h->root.type = bfd_link_hash_defined;
+ h->root.u.def.section = htab->sfpr;
+ h->root.u.def.value = (i - lowest_savef) * 4;
+ h->type = STT_FUNC;
+ h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
+ _bfd_elf_link_hash_hide_symbol (info, h, info->shared);
+ }
+ }
+
+ memcpy (sym, "._restf14", 10);
+ for (i = MIN_SAVE_FPR; i <= MAX_SAVE_FPR; i++)
+ {
+ sym[7] = i / 10 + '0';
+ sym[8] = i % 10 + '0';
+ h = elf_link_hash_lookup (&htab->elf, sym, false, false, true);
+ if (h != NULL
+ && h->root.type == bfd_link_hash_undefined)
+ {
+ if (lowest_restf > i)
+ lowest_restf = i;
+ h->root.type = bfd_link_hash_defined;
+ h->root.u.def.section = htab->sfpr;
+ h->root.u.def.value = ((MAX_SAVE_FPR + 2 - lowest_savef) * 4
+ + (i - lowest_restf) * 4);
+ h->type = STT_FUNC;
+ h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
+ _bfd_elf_link_hash_hide_symbol (info, h, info->shared);
+ }
+ }
+
+ htab->sfpr->_raw_size = ((MAX_SAVE_FPR + 2 - lowest_savef) * 4
+ + (MAX_SAVE_FPR + 2 - lowest_restf) * 4);
+
+ if (htab->sfpr->_raw_size == 0)
+ {
+ _bfd_strip_section_from_output (info, htab->sfpr);
+ }
+ else
+ {
+ bfd_byte *p = (bfd_byte *) bfd_alloc (htab->elf.dynobj,
+ htab->sfpr->_raw_size);
+ if (p == NULL)
+ return false;
+ htab->sfpr->contents = p;
+
+ for (i = lowest_savef; i <= MAX_SAVE_FPR; i++)
+ {
+ unsigned int fpr = i << 21;
+ unsigned int stackoff = (1 << 16) - (MAX_SAVE_FPR + 1 - i) * 8;
+ bfd_put_32 (htab->elf.dynobj, STFD_FR0_0R1 + fpr + stackoff, p);
+ p += 4;
+ }
+ bfd_put_32 (htab->elf.dynobj, BLR, p);
+ p += 4;
+
+ for (i = lowest_restf; i <= MAX_SAVE_FPR; i++)
+ {
+ unsigned int fpr = i << 21;
+ unsigned int stackoff = (1 << 16) - (MAX_SAVE_FPR + 1 - i) * 8;
+ bfd_put_32 (htab->elf.dynobj, LFD_FR0_0R1 + fpr + stackoff, p);
+ p += 4;
+ }
+ bfd_put_32 (htab->elf.dynobj, BLR, p);
+ p += 4;
+ }
+
elf_link_hash_traverse (&htab->elf, func_desc_adjust, (PTR) info);
return true;
}
struct ppc_link_hash_entry *eh;
struct ppc_dyn_relocs *p;
- if (h->root.type == bfd_link_hash_indirect
- || h->root.type == bfd_link_hash_warning)
+ if (h->root.type == bfd_link_hash_indirect)
return true;
+ if (h->root.type == bfd_link_hash_warning)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
info = (struct bfd_link_info *) inf;
htab = ppc_hash_table (info);
struct ppc_link_hash_entry *eh;
struct ppc_dyn_relocs *p;
+ if (h->root.type == bfd_link_hash_warning)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
eh = (struct ppc_link_hash_entry *) h;
for (p = eh->dyn_relocs; p != NULL; p = p->next)
{
linker script /DISCARD/, so we'll be discarding
the relocs too. */
}
- else
+ else if (p->count != 0)
{
srel = elf_section_data (p->sec)->sreloc;
srel->_raw_size += p->count * sizeof (Elf64_External_Rela);
+ if ((p->sec->output_section->flags & SEC_READONLY) != 0)
+ info->flags |= DF_TEXTREL;
}
}
}
return false;
}
+ if (NO_OPD_RELOCS)
+ {
+ if (!add_dynamic_entry (DT_PPC64_OPD, 0)
+ || !add_dynamic_entry (DT_PPC64_OPDSZ, 0))
+ return false;
+ }
+
if (relocs)
{
if (!add_dynamic_entry (DT_RELA, 0)
/* If any dynamic relocs apply to a read-only section,
then we need a DT_TEXTREL entry. */
- elf_link_hash_traverse (&htab->elf, readonly_dynrelocs, (PTR) info);
+ if ((info->flags & DF_TEXTREL) == 0)
+ elf_link_hash_traverse (&htab->elf, readonly_dynrelocs,
+ (PTR) info);
if ((info->flags & DF_TEXTREL) != 0)
{
/* If the .plt doesn't have any entries crossing a 64k boundary,
then there is no need for bigger stubs. */
- if (next_64k <= plt_offset + htab->splt->_raw_size)
+ if (plt_offset + htab->splt->_raw_size <= next_64k)
return true;
/* OK, so we have at least one transition. Since .plt entries are
bfd_vma TOCstart;
boolean ret = true;
boolean is_opd;
+ /* Disabled until we sort out how ld should choose 'y' vs 'at'. */
+ boolean is_power4 = false;
/* Initialize howto table if needed. */
if (!ppc64_elf_howto_table[R_PPC64_ADDR32])
/* Branch taken prediction relocations. */
case R_PPC64_ADDR14_BRTAKEN:
case R_PPC64_REL14_BRTAKEN:
- insn = 0x01 << 21; /* Set 't' bit, lowest bit of BO field. */
+ insn = 0x01 << 21; /* 'y' or 't' bit, lowest bit of BO field. */
/* Fall thru. */
/* Branch not taken prediction relocations. */
case R_PPC64_ADDR14_BRNTAKEN:
case R_PPC64_REL14_BRNTAKEN:
insn |= bfd_get_32 (output_bfd, contents + offset) & ~(0x01 << 21);
- /* Set 'a' bit. This is 0b00010 in BO field for branch on CR(BI)
- insns (BO == 001at or 011at), and 0b01000 for branch on CTR
- insns (BO == 1a00t or 1a01t). */
- if ((insn & (0x14 << 21)) == (0x04 << 21))
- insn |= 0x02 << 21;
- else if ((insn & (0x14 << 21)) == (0x10 << 21))
- insn |= 0x08 << 21;
+ if (is_power4)
+ {
+ /* Set 'a' bit. This is 0b00010 in BO field for branch
+ on CR(BI) insns (BO == 001at or 011at), and 0b01000
+ for branch on CTR insns (BO == 1a00t or 1a01t). */
+ if ((insn & (0x14 << 21)) == (0x04 << 21))
+ insn |= 0x02 << 21;
+ else if ((insn & (0x14 << 21)) == (0x10 << 21))
+ insn |= 0x08 << 21;
+ else
+ break;
+ }
else
- break;
+ {
+ /* Invert 'y' bit if not the default. */
+ if ((bfd_signed_vma) (relocation - offset) < 0)
+ insn ^= 0x01 << 21;
+ }
bfd_put_32 (output_bfd, (bfd_vma) insn, contents + offset);
break;
time. */
skip = false;
+ relocate = false;
outrel.r_offset =
_bfd_elf_section_offset (output_bfd, info, input_section,
rel->r_offset);
if (outrel.r_offset == (bfd_vma) -1)
skip = true;
-
+ else if (outrel.r_offset == (bfd_vma) -2)
+ skip = true, relocate = true;
outrel.r_offset += (input_section->output_section->vma
+ input_section->output_offset);
outrel.r_addend = addend;
if (skip)
- {
- relocate = false;
- memset (&outrel, 0, sizeof outrel);
- }
+ memset (&outrel, 0, sizeof outrel);
else if (h != NULL
&& h->dynindx != -1
&& !is_opd
|| !info->symbolic
|| (h->elf_link_hash_flags
& ELF_LINK_HASH_DEF_REGULAR) == 0))
- {
- relocate = false;
- outrel.r_info = ELF64_R_INFO (h->dynindx, r_type);
- }
+ outrel.r_info = ELF64_R_INFO (h->dynindx, r_type);
else
{
/* This symbol is local, or marked to become local,
for (; dyncon < dynconend; dyncon++)
{
Elf_Internal_Dyn dyn;
+ asection *s;
bfd_elf64_swap_dyn_in (dynobj, dyncon, &dyn);
+ htab->sglink->output_offset);
break;
+ case DT_PPC64_OPD:
+ s = bfd_get_section_by_name (output_bfd, ".opd");
+ if (s != NULL)
+ dyn.d_un.d_ptr = s->vma;
+ break;
+
+ case DT_PPC64_OPDSZ:
+ s = bfd_get_section_by_name (output_bfd, ".opd");
+ if (s != NULL)
+ dyn.d_un.d_val = s->_raw_size;
+ break;
+
case DT_PLTGOT:
dyn.d_un.d_ptr = (htab->splt->output_section->vma
+ htab->splt->output_offset);