@@ -135,6 +135,11 @@ struct alpha_elf_got_entry
/* Have we adjusted this entry for SEC_MERGE? */
unsigned char reloc_xlated;
+
+ /* Does a section that survives into the output refer to this entry?
+ Only such an entry is ever filled in, so only such an entry gets an
+ R_ALPHA_IRELATIVE reserved for it. */
+ unsigned char irel_live;
};
struct alpha_elf_reloc_entry
@@ -196,6 +201,12 @@ struct alpha_elf_link_hash_table
/* The most recent relax pass that we've seen. The GOTs
should be regenerated if this doesn't match. */
int relax_trip;
+
+ /* The part of the size of .rela.iplt that is due to data references to
+ an IFUNC, counted once the sections that survive into the output are
+ known. The rest comes from got entries and is recomputed whenever the
+ GOTs are. */
+ bfd_size_type irelplt_data_size;
};
/* Look up an entry in a Alpha ELF linker hash table. */
@@ -1808,6 +1819,260 @@ elf64_alpha_ifunc_reloc_p (unsigned long r_type)
return r_type == R_ALPHA_LITERAL || r_type == R_ALPHA_REFQUAD;
}
+/* True if a reference to the IFUNC described by H (global) or SYM (local)
+ needs its R_ALPHA_IRELATIVE in .rela.iplt: the link is not PIC, so
+ nothing has reserved a slot for it alongside the R_ALPHA_RELATIVE it
+ would otherwise have had, and the symbol is not preemptible. */
+
+static bool
+elf64_alpha_ifunc_irelplt_p (struct alpha_elf_link_hash_entry *h,
+ Elf_Internal_Sym *sym,
+ struct bfd_link_info *info)
+{
+ if (!elf64_alpha_ifunc_p (h, sym) || bfd_link_pic (info))
+ return false;
+
+ return h == NULL || !alpha_elf_dynamic_symbol_p (&h->root, info);
+}
+
+/* Create .rela.iplt. Called from check_relocs, since the section has to
+ exist before input sections are mapped to output sections. One that
+ stays empty is stripped by elf64_alpha_late_size_sections. */
+
+static bool
+elf64_alpha_create_irelplt (struct bfd_link_info *info)
+{
+ struct elf_link_hash_table *htab = elf_hash_table (info);
+ const struct elf_backend_data *bed;
+ asection *s;
+
+ if (htab->irelplt != NULL)
+ return true;
+
+ bed = get_elf_backend_data (info->output_bfd);
+ s = bfd_make_section_anyway_with_flags (htab->dynobj, ".rela.iplt",
+ (bed->dynamic_sec_flags
+ | SEC_READONLY));
+ if (s == NULL || !bfd_set_section_alignment (s, 3))
+ return false;
+
+ htab->irelplt = s;
+ return true;
+}
+
+/* Note that an R_ALPHA_IRELATIVE will be applied to SEC. A read-only one
+ needs the page made writable at startup, which only the dynamic linker
+ can do. Only a data relocation can land in such a section; the GOT is
+ always writable. */
+
+static void
+elf64_alpha_note_irelative (struct bfd_link_info *info, asection *sec)
+{
+ if ((sec->flags & SEC_READONLY) != 0)
+ {
+ info->flags |= DF_TEXTREL;
+ info->callbacks->minfo
+ (_("%pB: R_ALPHA_IRELATIVE in read-only section `%pA'\n"),
+ sec->owner, sec);
+ }
+}
+
+/* Note the references to an IFUNC that need an R_ALPHA_IRELATIVE in
+ .rela.iplt: mark the got entries that get one and count the data
+ relocations that do. Only the input sections that make it into the
+ output are relocated, and only their references are emitted, so this runs
+ once the output sections are known rather than in check_relocs, which
+ still sees the sections a linker script goes on to discard. */
+
+static bool
+elf64_alpha_scan_irelative (struct bfd_link_info *info)
+{
+ struct alpha_elf_link_hash_table *htab = alpha_elf_hash_table (info);
+ bfd *abfd;
+
+ htab->irelplt_data_size = 0;
+
+ for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link.next)
+ {
+ Elf_Internal_Shdr *symtab_hdr;
+ asection *sec;
+
+ if (!is_alpha_elf (abfd) || (abfd->flags & DYNAMIC) != 0)
+ continue;
+
+ symtab_hdr = &elf_symtab_hdr (abfd);
+
+ for (sec = abfd->sections; sec != NULL; sec = sec->next)
+ {
+ Elf_Internal_Rela *relocs, *rel, *relend;
+ bool noted = false;
+
+ /* The sections elf_link_input_bfd will relocate. One a linker
+ script discarded and one discarded as a duplicate both have the
+ absolute section for their output. */
+ if ((sec->flags & (SEC_ALLOC | SEC_RELOC | SEC_HAS_CONTENTS))
+ != (SEC_ALLOC | SEC_RELOC | SEC_HAS_CONTENTS)
+ || sec->reloc_count == 0
+ || sec->output_section == NULL
+ || bfd_is_abs_section (sec->output_section))
+ continue;
+
+ relocs = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL,
+ info->keep_memory);
+ if (relocs == NULL)
+ return false;
+
+ relend = relocs + sec->reloc_count;
+ for (rel = relocs; rel < relend; rel++)
+ {
+ unsigned long r_type = ELF64_R_TYPE (rel->r_info);
+ unsigned long r_symndx = ELF64_R_SYM (rel->r_info);
+ struct alpha_elf_link_hash_entry *h = NULL;
+ Elf_Internal_Sym *isym = NULL;
+ struct alpha_elf_got_entry *gotent;
+
+ if (!elf64_alpha_ifunc_reloc_p (r_type))
+ continue;
+
+ if (r_symndx < symtab_hdr->sh_info)
+ {
+ isym = bfd_sym_from_r_symndx
+ (&elf_hash_table (info)->sym_cache, abfd, r_symndx);
+ if (isym == NULL)
+ return false;
+ }
+ else
+ {
+ h = (alpha_elf_sym_hashes (abfd)
+ [r_symndx - symtab_hdr->sh_info]);
+ while (h->root.root.type == bfd_link_hash_indirect
+ || h->root.root.type == bfd_link_hash_warning)
+ h = ((struct alpha_elf_link_hash_entry *)
+ h->root.root.u.i.link);
+ }
+
+ /* A symbol defined in a section the linker discarded reaches
+ relocate_section as an error rather than as a relocation to
+ emit, so nothing may be reserved for it. */
+ if (h != NULL)
+ {
+ if ((h->root.root.type == bfd_link_hash_defined
+ || h->root.root.type == bfd_link_hash_defweak)
+ && discarded_section (h->root.root.u.def.section))
+ continue;
+ }
+ else if (isym->st_shndx < SHN_LORESERVE
+ || isym->st_shndx > SHN_HIRESERVE)
+ {
+ /* An index above the reserved range is one that
+ bfd_sym_from_r_symndx expanded from an SHN_XINDEX
+ entry; elf_link_input_bfd looks that one up too. */
+ asection *isec;
+
+ isec = bfd_section_from_elf_index (abfd, isym->st_shndx);
+ if (isec != NULL && discarded_section (isec))
+ continue;
+ }
+
+ if (!elf64_alpha_ifunc_irelplt_p (h, isym, info))
+ continue;
+
+ if (r_type == R_ALPHA_REFQUAD)
+ {
+ htab->irelplt_data_size += sizeof (Elf64_External_Rela);
+ if (!noted)
+ {
+ elf64_alpha_note_irelative (info, sec);
+ noted = true;
+ }
+ continue;
+ }
+
+ /* However many references reach a got entry, it holds one
+ address and gets one R_ALPHA_IRELATIVE. Find it the way
+ relocate_section will. */
+ if (h != NULL)
+ gotent = h->got_entries;
+ else if (alpha_elf_tdata (abfd)->local_got_entries != NULL)
+ gotent = alpha_elf_tdata (abfd)->local_got_entries[r_symndx];
+ else
+ gotent = NULL;
+
+ for (; gotent != NULL; gotent = gotent->next)
+ if (gotent->gotobj == alpha_elf_tdata (abfd)->gotobj
+ && gotent->reloc_type == r_type
+ && gotent->addend == rel->r_addend)
+ {
+ gotent->irel_live = 1;
+ break;
+ }
+ }
+
+ if (elf_section_data (sec)->relocs != relocs)
+ free (relocs);
+ }
+ }
+
+ return true;
+}
+
+/* Reserve space in .rela.iplt for the got entries of a global IFUNC that
+ elf64_alpha_scan_irelative marked. */
+
+static bool
+elf64_alpha_size_irelative (struct alpha_elf_link_hash_entry *h,
+ struct bfd_link_info *info)
+{
+ asection *irelplt = elf_hash_table (info)->irelplt;
+ struct alpha_elf_got_entry *gotent;
+
+ for (gotent = h->got_entries; gotent != NULL; gotent = gotent->next)
+ if (gotent->irel_live && gotent->use_count > 0)
+ irelplt->size += sizeof (Elf64_External_Rela);
+
+ return true;
+}
+
+/* Size .rela.iplt. The data references are counted once, since nothing
+ later changes them; the got entries of a global IFUNC can still merge
+ when the GOTs are re-merged during relaxation, so the part of the size
+ that comes from them is recomputed alongside them. */
+
+static void
+elf64_alpha_size_irelplt_section (struct bfd_link_info *info)
+{
+ struct alpha_elf_link_hash_table *htab = alpha_elf_hash_table (info);
+ asection *irelplt = elf_hash_table (info)->irelplt;
+ bfd *i;
+
+ if (irelplt == NULL)
+ return;
+
+ irelplt->size = htab->irelplt_data_size;
+ alpha_elf_link_hash_traverse (htab, elf64_alpha_size_irelative, info);
+
+ /* A local symbol has no hash entry for the traversal to reach. */
+ for (i = htab->got_list; i != NULL; i = alpha_elf_tdata (i)->got_link_next)
+ {
+ bfd *j;
+
+ for (j = i; j != NULL; j = alpha_elf_tdata (j)->in_got_link_next)
+ {
+ struct alpha_elf_got_entry **local_got_entries, *gotent;
+ int k, n;
+
+ local_got_entries = alpha_elf_tdata (j)->local_got_entries;
+ if (local_got_entries == NULL)
+ continue;
+
+ for (k = 0, n = elf_symtab_hdr (j).sh_info; k < n; ++k)
+ for (gotent = local_got_entries[k]; gotent; gotent = gotent->next)
+ if (gotent->irel_live && gotent->use_count > 0)
+ irelplt->size += sizeof (Elf64_External_Rela);
+ }
+ }
+}
+
/* Handle dynamic relocations when doing an Alpha ELF link. */
static bool
@@ -1844,14 +2109,29 @@ elf64_alpha_check_relocs (bfd *abfd, struct bfd_link_info *info,
unsigned long r_symndx, r_type;
struct alpha_elf_link_hash_entry *h;
+ Elf_Internal_Sym *isym;
unsigned int gotent_flags;
bool maybe_dynamic;
unsigned int need;
bfd_vma addend;
r_symndx = ELF64_R_SYM (rel->r_info);
+ r_type = ELF64_R_TYPE (rel->r_info);
+ isym = NULL;
if (r_symndx < symtab_hdr->sh_info)
- h = NULL;
+ {
+ h = NULL;
+
+ /* A local IFUNC needs the same treatment as a global one, but
+ only the symbol table says that it is one. */
+ if (elf64_alpha_ifunc_reloc_p (r_type))
+ {
+ isym = bfd_sym_from_r_symndx (&elf_hash_table (info)->sym_cache,
+ abfd, r_symndx);
+ if (isym == NULL)
+ return false;
+ }
+ }
else
{
h = sym_hashes[r_symndx - symtab_hdr->sh_info];
@@ -1877,9 +2157,16 @@ elf64_alpha_check_relocs (bfd *abfd, struct bfd_link_info *info,
|| h->root.root.type == bfd_link_hash_defweak))
maybe_dynamic = true;
+ /* Whether a global IFUNC needs .rela.iplt is not settled until
+ sizing, so create the section for every reference that might. */
+ if (elf64_alpha_ifunc_reloc_p (r_type)
+ && elf64_alpha_ifunc_p (h, isym)
+ && !bfd_link_pic (info)
+ && !elf64_alpha_create_irelplt (info))
+ return false;
+
need = 0;
gotent_flags = 0;
- r_type = ELF64_R_TYPE (rel->r_info);
addend = rel->r_addend;
switch (r_type)
@@ -1911,7 +2198,16 @@ elf64_alpha_check_relocs (bfd *abfd, struct bfd_link_info *info,
case R_ALPHA_REFLONG:
case R_ALPHA_REFQUAD:
- if (bfd_link_pic (info) || maybe_dynamic)
+ if (r_type == R_ALPHA_REFQUAD && elf64_alpha_ifunc_p (h, isym))
+ {
+ /* A reference that becomes an R_ALPHA_IRELATIVE in .rela.iplt
+ is sized later, from the sections that survive. Whether a
+ global one does is not settled until then, so record it
+ either way; a local symbol never becomes dynamic. */
+ if (h != NULL || !elf64_alpha_ifunc_irelplt_p (NULL, isym, info))
+ need = NEED_DYNREL;
+ }
+ else if (bfd_link_pic (info) || maybe_dynamic)
need = NEED_DYNREL;
break;
@@ -2349,6 +2645,7 @@ elf64_alpha_merge_gots (bfd *a, bfd *b)
&& ae->addend == be->addend)
{
ae->flags |= be->flags;
+ ae->irel_live |= be->irel_live;
ae->use_count += be->use_count;
*pbe = be->next;
memset (be, 0xa5, sizeof (*be));
@@ -2861,7 +3158,15 @@ elf64_alpha_late_size_sections (struct bfd_link_info *info)
elf64_alpha_size_rela_got_section (info);
elf64_alpha_size_plt_section (info);
}
- /* else we're not dynamic and by definition we don't need such things. */
+
+ /* The sizing above reserves nothing for a non-dynamic IFUNC, since the
+ dynamic linker never sees it; its relocations go in .rela.iplt. */
+ if (elf_hash_table (info)->irelplt != NULL)
+ {
+ if (!elf64_alpha_scan_irelative (info))
+ return false;
+ elf64_alpha_size_irelplt_section (info);
+ }
/* The check_relocs and adjust_dynamic_symbol entry points have
determined the sizes of the various dynamic sections. Allocate
@@ -3763,6 +4068,7 @@ elf64_alpha_relax_section (bfd *abfd, asection *sec,
elf64_alpha_size_plt_section (link_info);
elf64_alpha_size_rela_got_section (link_info);
}
+ elf64_alpha_size_irelplt_section (link_info);
}
symtab_hdr = &elf_symtab_hdr (abfd);
@@ -4416,9 +4722,14 @@ elf64_alpha_relocate_section (struct bfd_link_info *info,
/* If the symbol has been forced local, output a RELATIVE
reloc, otherwise it will be handled in finish_dynamic_symbol.
An IFUNC gets an IRELATIVE instead. */
- if (bfd_link_pic (info)
- && !dynamic_symbol_p
- && !undef_weak_ref)
+ if (elf64_alpha_ifunc_irelplt_p (h, sym, info))
+ elf64_alpha_emit_dynrel (info->output_bfd, info, sgot,
+ elf_hash_table (info)->irelplt,
+ gotent->got_offset, 0,
+ R_ALPHA_IRELATIVE, value);
+ else if (bfd_link_pic (info)
+ && !dynamic_symbol_p
+ && !undef_weak_ref)
elf64_alpha_emit_dynrel (info->output_bfd, info, sgot, srelgot,
gotent->got_offset, 0,
(elf64_alpha_ifunc_p (h, sym)
@@ -4547,6 +4858,7 @@ elf64_alpha_relocate_section (struct bfd_link_info *info,
{
long dynindx, dyntype = r_type;
bfd_vma dynaddend;
+ asection *srel_out = srel;
/* Careful here to remember RELATIVE relocations for global
variables for symbolic shared objects. */
@@ -4576,16 +4888,20 @@ elf64_alpha_relocate_section (struct bfd_link_info *info,
dynaddend = value - dtp_base;
}
else if (elf64_alpha_ifunc_reloc_p (r_type)
- && bfd_link_pic (info)
&& elf64_alpha_ifunc_p (h, sym)
&& (input_section->flags & SEC_ALLOC))
{
- /* The RELATIVE this would otherwise get would store the
- address of the resolver. Space for it is already
- reserved, so the IRELATIVE takes its place. */
+ /* In a PIC link the RELATIVE this would otherwise get would
+ store the address of the resolver, and space for it is
+ already reserved, so the IRELATIVE takes its place. A
+ non-PIC link reserved nothing there and puts it in
+ .rela.iplt instead. */
dynindx = 0;
dyntype = R_ALPHA_IRELATIVE;
dynaddend = value;
+
+ if (elf64_alpha_ifunc_irelplt_p (h, sym, info))
+ srel_out = elf_hash_table (info)->irelplt;
}
else if (bfd_link_pic (info)
&& r_symndx != STN_UNDEF
@@ -4615,7 +4931,7 @@ elf64_alpha_relocate_section (struct bfd_link_info *info,
if (input_section->flags & SEC_ALLOC)
elf64_alpha_emit_dynrel (info->output_bfd, info, input_section,
- srel, rel->r_offset, dynindx,
+ srel_out, rel->r_offset, dynindx,
dyntype, dynaddend);
}
goto default_reloc;
@@ -4850,6 +5166,12 @@ elf64_alpha_finish_dynamic_symbol (struct bfd_link_info *info,
{
struct alpha_elf_link_hash_entry *ah = (struct alpha_elf_link_hash_entry *)h;
+ /* Only a defined IFUNC gets here without dynamic sections. There is no
+ PLT to fill in, whatever needs_plt says, and relocate_section has
+ already written its GOT entries. */
+ if (!elf_hash_table (info)->dynamic_sections_created)
+ return true;
+
if (h->needs_plt)
{
/* Fill in the .plt entry for this symbol. */
@@ -66,6 +66,8 @@ set alphatests {
"" {got-realloc-a.s got-realloc-b.s}
{{readelf -SW got-realloc.rd}}
"got-realloc.so"}
+ {"IFUNC helper shared library" "-shared -melf64alpha" ""
+ "" {ifunc-shlib.s} {} "libalphaifunc.so"}
}
# Not implemented yet
@@ -75,6 +77,10 @@ set alphatests {
run_ld_link_tests $alphatests
+# The dump tests run after the link tests above, so a .d file may name one
+# of the shared libraries they leave in tmpdir. Several do: linking against
+# a shared library is the only way to make a dump test produce an
+# executable with dynamic sections.
set test_list [lsort [glob -nocomplain $srcdir/$subdir/*.d]]
foreach t $test_list {
# We need to strip the ".d", but can leave the dirname.
new file mode 100644
@@ -0,0 +1,11 @@
+ .section .text.grp,"axG",@progbits,grp,comdat
+ .type kept_ifunc, @gnu_indirect_function
+kept_ifunc:
+ ret
+
+ .text
+ .globl _start
+ .ent _start
+_start:
+ ret
+ .end _start
new file mode 100644
@@ -0,0 +1,14 @@
+ # This copy of the group is discarded in favour of the one in
+ # ifunc-comdat-a.s, taking the definition of local_ifunc with it.
+ .section .text.grp,"axG",@progbits,grp,comdat
+ .type local_ifunc, @gnu_indirect_function
+local_ifunc:
+ ret
+
+ # The reference survives, but nothing relocates it: the generic code
+ # reports it rather than letting relocate_section reach it. Nothing
+ # may be reserved in .rela.iplt for it either.
+ .data
+ .globl ptr
+ptr:
+ .quad local_ifunc
new file mode 100644
@@ -0,0 +1,4 @@
+#source: ifunc-comdat-a.s
+#source: ifunc-comdat-b.s
+#ld: -melf64alpha
+#error: \A`local_ifunc' referenced in section `\.data' of [^\n]*: defined in discarded section `\.text\.grp\[grp\]' of [^\n]*\n?\Z
new file mode 100644
@@ -0,0 +1,11 @@
+#source: ifunc-discard.s
+#ld: -melf64alpha --no-warn-rwx-segments -T $srcdir/$subdir/ifunc-discard.ld
+#readelf: -Wr
+
+# Only the reference that survives the script reserves anything. A slot
+# reserved for one the script discarded would be left as an R_ALPHA_NONE,
+# which libc's startup code would try to apply.
+Relocation section '\.rela\.dyn' .* contains 1 entry:
+#...
+[0-9a-f]+ +0+2a R_ALPHA_IRELATIVE +[0-9a-f]+
+#pass
new file mode 100644
@@ -0,0 +1,4 @@
+SECTIONS
+{
+ /DISCARD/ : { *(.data.discard) *(.text.discard) }
+}
new file mode 100644
@@ -0,0 +1,33 @@
+ .text
+
+ .globl global_ifunc
+ .type global_ifunc, @gnu_indirect_function
+global_ifunc:
+ ret
+
+ .type local_ifunc, @gnu_indirect_function
+local_ifunc:
+ ret
+
+ .globl _start
+ .ent _start
+_start:
+ ret
+ .end _start
+
+ # The one reference that survives, so that .rela.iplt is not simply
+ # empty.
+ .data
+ .globl ptr
+ptr:
+ .quad global_ifunc
+
+ # A linker script discards these after check_relocs has seen them.
+ # Nothing relocates them, so nothing may be reserved for them.
+ .section .data.discard,"aw",@progbits
+ .quad global_ifunc
+ .quad local_ifunc
+
+ .section .text.discard,"ax",@progbits
+ ldq $27, global_ifunc($29) !literal!1
+ ldq $27, local_ifunc($29) !literal!2
new file mode 100644
@@ -0,0 +1,14 @@
+#source: ifunc-extern.s
+#ld: -melf64alpha tmpdir/libalphaifunc.so
+#readelf: -Wr
+
+# The IFUNC is defined in another module, so the dynamic linker resolves
+# it: an ordinary PLT entry and a symbolic data relocation, no IRELATIVE.
+Relocation section '\.rela\.dyn' .* contains 1 entry:
+#...
+[0-9a-f]+ +[0-9a-f]+ +R_ALPHA_REFQUAD .*shlib_ifunc \+ 0
+#...
+Relocation section '\.rela\.plt' .* contains 1 entry:
+#...
+[0-9a-f]+ +[0-9a-f]+ +R_ALPHA_JMP_SLOT .*shlib_ifunc \+ 0
+#pass
new file mode 100644
@@ -0,0 +1,16 @@
+ .text
+
+ .globl _start
+ .ent _start
+_start:
+ ldgp $29, 0($27)
+ ldq $27, shlib_ifunc($29) !literal!1
+ jsr $26, ($27), 0 !lituse_jsr!1
+ ldgp $29, 0($26)
+ ret
+ .end _start
+
+ .data
+ .globl ptr
+ptr:
+ .quad shlib_ifunc
new file mode 100644
@@ -0,0 +1,8 @@
+#source: ifunc-gc-local.s
+#ld: -melf64alpha --gc-sections -e _start
+#readelf: -Wr
+
+Relocation section '\.rela\.dyn' .* contains 1 entry:
+#...
+[0-9a-f]+ +[0-9a-f]+ +R_ALPHA_IRELATIVE +[0-9a-f]+
+#pass
new file mode 100644
@@ -0,0 +1,32 @@
+ .section .text.ifunc,"ax",@progbits
+ .type local_ifunc, @gnu_indirect_function
+local_ifunc:
+ ret
+
+ # Both of these need an IRELATIVE, but --gc-sections drops the
+ # section holding the call before check_relocs runs, so only one is
+ # counted.
+ .section .text.dead,"ax",@progbits
+ .globl dead
+ .ent dead
+dead:
+ ldgp $29, 0($27)
+ ldq $27, local_ifunc($29) !literal!1
+ jsr $26, ($27), 0 !lituse_jsr!1
+ ldgp $29, 0($26)
+ ret
+ .end dead
+
+ .section .data.live,"aw",@progbits
+ .globl ptr
+ptr:
+ .quad local_ifunc
+
+ .section .text.start,"ax",@progbits
+ .globl _start
+ .ent _start
+_start:
+ ldgp $29, 0($27)
+ ldah $1, ptr($29) !gprelhigh
+ ret
+ .end _start
new file mode 100644
@@ -0,0 +1,8 @@
+#source: ifunc-gc.s
+#ld: -melf64alpha --gc-sections -e _start
+#readelf: -Wr
+
+Relocation section '\.rela\.dyn' .* contains 1 entry:
+#...
+[0-9a-f]+ +[0-9a-f]+ +R_ALPHA_IRELATIVE +[0-9a-f]+
+#pass
new file mode 100644
@@ -0,0 +1,32 @@
+ .section .text.ifunc,"ax",@progbits
+ .globl global_ifunc
+ .type global_ifunc, @gnu_indirect_function
+global_ifunc:
+ ret
+
+ # Both of these need an IRELATIVE, but --gc-sections drops the
+ # section holding the call, so only one may be reserved.
+ .section .text.dead,"ax",@progbits
+ .globl dead
+ .ent dead
+dead:
+ ldgp $29, 0($27)
+ ldq $27, global_ifunc($29) !literal!1
+ jsr $26, ($27), 0 !lituse_jsr!1
+ ldgp $29, 0($26)
+ ret
+ .end dead
+
+ .section .data.live,"aw",@progbits
+ .globl ptr
+ptr:
+ .quad global_ifunc
+
+ .section .text.start,"ax",@progbits
+ .globl _start
+ .ent _start
+_start:
+ ldgp $29, 0($27)
+ ldah $1, ptr($29) !gprelhigh
+ ret
+ .end _start
new file mode 100644
@@ -0,0 +1,9 @@
+#source: ifunc-global.s
+#ld: -melf64alpha tmpdir/libalphaifunc.so
+#readelf: -Wr
+
+Relocation section '\.rela\.dyn' .* contains 2 entries:
+#...
+[0-9a-f]+ +[0-9a-f]+ +R_ALPHA_IRELATIVE +[0-9a-f]+
+[0-9a-f]+ +[0-9a-f]+ +R_ALPHA_IRELATIVE +[0-9a-f]+
+#pass
new file mode 100644
@@ -0,0 +1,12 @@
+#source: ifunc-global.s
+#ld: -melf64alpha --export-dynamic tmpdir/libalphaifunc.so
+#readelf: -Wr
+
+# One IRELATIVE for the GOT entry and one for ptr, and nothing else. The
+# IFUNC is in .dynsym but is not preemptible in an executable, so the link
+# resolves it itself and no PLT entry is left over from the guess that
+# check_relocs makes about one.
+Relocation section '\.rela\.dyn' .* contains 2 entries:
+ +Offset +Info +Type +Symbol's Value +Symbol's Name \+ Addend
+[0-9a-f]+ +0+2a R_ALPHA_IRELATIVE +[0-9a-f]+
+[0-9a-f]+ +0+2a R_ALPHA_IRELATIVE +[0-9a-f]+
new file mode 100644
@@ -0,0 +1,9 @@
+#source: ifunc-global.s
+#ld: -melf64alpha -z nocombreloc
+#readelf: -Wr
+
+Relocation section '\.rela\.iplt' .* contains 2 entries:
+#...
+[0-9a-f]+ +[0-9a-f]+ +R_ALPHA_IRELATIVE +[0-9a-f]+
+[0-9a-f]+ +[0-9a-f]+ +R_ALPHA_IRELATIVE +[0-9a-f]+
+#pass
new file mode 100644
@@ -0,0 +1,17 @@
+#source: ifunc-global.s
+#ld: -melf64alpha
+#readelf: -Wrs
+
+# One IRELATIVE for the GOT entry and one for ptr. The symbol table
+# entries confirm that the resolver kept its IFUNC type.
+Relocation section '\.rela\.dyn' .* contains 2 entries:
+#...
+[0-9a-f]+ +0+2a R_ALPHA_IRELATIVE +[0-9a-f]+
+[0-9a-f]+ +0+2a R_ALPHA_IRELATIVE +[0-9a-f]+
+#...
+Symbol table '\.symtab' contains .* entries:
+#...
+ +[0-9]+: [0-9a-f]+ +0 IFUNC +GLOBAL +DEFAULT +[0-9]+ global_ifunc
+#...
+ +[0-9]+: [0-9a-f]+ +0 NOTYPE +GLOBAL +DEFAULT +[0-9]+ ptr
+#pass
new file mode 100644
@@ -0,0 +1,8 @@
+#source: ifunc-local-call.s
+#ld: -melf64alpha
+#readelf: -Wr
+
+Relocation section '\.rela\.dyn' .* contains 1 entry:
+#...
+[0-9a-f]+ +[0-9a-f]+ +R_ALPHA_IRELATIVE +[0-9a-f]+
+#pass
new file mode 100644
@@ -0,0 +1,9 @@
+#source: ifunc-local.s
+#ld: -melf64alpha tmpdir/libalphaifunc.so
+#readelf: -Wr
+
+Relocation section '\.rela\.dyn' .* contains 2 entries:
+#...
+[0-9a-f]+ +[0-9a-f]+ +R_ALPHA_IRELATIVE +[0-9a-f]+
+[0-9a-f]+ +[0-9a-f]+ +R_ALPHA_IRELATIVE +[0-9a-f]+
+#pass
new file mode 100644
@@ -0,0 +1,17 @@
+#source: ifunc-local.s
+#ld: -melf64alpha
+#readelf: -Wrs
+
+# One IRELATIVE for the GOT entry and one for ptr. The symbol table
+# entries confirm that the resolver kept its IFUNC type.
+Relocation section '\.rela\.dyn' .* contains 2 entries:
+#...
+[0-9a-f]+ +0+2a R_ALPHA_IRELATIVE +[0-9a-f]+
+[0-9a-f]+ +0+2a R_ALPHA_IRELATIVE +[0-9a-f]+
+#...
+Symbol table '\.symtab' contains .* entries:
+#...
+ +[0-9]+: [0-9a-f]+ +0 IFUNC +LOCAL +DEFAULT +[0-9]+ local_ifunc
+#...
+ +[0-9]+: [0-9a-f]+ +0 NOTYPE +GLOBAL +DEFAULT +[0-9]+ ptr
+#pass
new file mode 100644
@@ -0,0 +1,24 @@
+ .text
+
+ # One object defines the IFUNC and both call and reference it, so
+ # that the GOT entries merge into one and the two data references
+ # accumulate on the same hash table entry.
+ .globl global_ifunc
+ .type global_ifunc, @gnu_indirect_function
+global_ifunc:
+ ret
+
+ .globl _start
+ .ent _start
+_start:
+ ldgp $29, 0($27)
+ ldq $27, global_ifunc($29) !literal!1
+ jsr $26, ($27), 0 !lituse_jsr!1
+ ldgp $29, 0($26)
+ ret
+ .end _start
+
+ .data
+ .globl ptr_a
+ptr_a:
+ .quad global_ifunc
new file mode 100644
@@ -0,0 +1,16 @@
+ .text
+
+ .globl other
+ .ent other
+other:
+ ldgp $29, 0($27)
+ ldq $27, global_ifunc($29) !literal!1
+ jsr $26, ($27), 0 !lituse_jsr!1
+ ldgp $29, 0($26)
+ ret
+ .end other
+
+ .data
+ .globl ptr_b
+ptr_b:
+ .quad global_ifunc
new file mode 100644
@@ -0,0 +1,13 @@
+#source: ifunc-multi-a.s
+#source: ifunc-multi-b.s
+#ld: -melf64alpha
+#readelf: -Wr
+
+# One IRELATIVE for the merged GOT entry and one for each of the two data
+# references.
+Relocation section '\.rela\.dyn' .* contains 3 entries:
+#...
+[0-9a-f]+ +0+2a R_ALPHA_IRELATIVE +[0-9a-f]+
+[0-9a-f]+ +0+2a R_ALPHA_IRELATIVE +[0-9a-f]+
+[0-9a-f]+ +0+2a R_ALPHA_IRELATIVE +[0-9a-f]+
+#pass
new file mode 100644
@@ -0,0 +1,7 @@
+#source: ifunc-none.s
+#ld: -melf64alpha
+#readelf: -Wr
+
+# A link with nothing to resolve at startup gets no relocations at all, and
+# in particular no .rela.iplt.
+There are no relocations in this file\.
new file mode 100644
@@ -0,0 +1,12 @@
+ .text
+
+ .globl _start
+ .ent _start
+_start:
+ ret
+ .end _start
+
+ .data
+ .globl ptr
+ptr:
+ .quad _start
new file mode 100644
@@ -0,0 +1,32 @@
+/* 8000 got entries against undefined symbols, so relaxation cannot remove
+ any of them. That is 64000 bytes; with the entry for the IFUNC it is
+ just under MAX_GOT_SIZE, so this object keeps its own got subsection. */
+
+ .text
+ .globl global_ifunc
+ .type global_ifunc, @gnu_indirect_function
+global_ifunc:
+ ret
+
+ .globl _start
+ .ent _start
+_start:
+ ldgp $29,0($27)
+ .prologue 1
+ ldq $1,global_ifunc($29) !literal
+ .irpc w,01234567
+ .irpc x,0123456789
+ .irpc y,0123456789
+ .irpc z,0123456789
+ ldq $1,g\w\x\y\z($29) !literal
+ .endr
+ .endr
+ .endr
+ .endr
+ ret $31,($26),1
+ .end _start
+
+ .data
+ .globl ptr
+ptr:
+ .quad global_ifunc
new file mode 100644
@@ -0,0 +1,47 @@
+/* 251 got entries, of which relaxation removes the 150 that are only used
+ by a call to a local function. Adding this object's entries to the 8001
+ in ifunc-relax-a.s exceeds MAX_GOT_SIZE, so the two subsections cannot be
+ merged when they are first sized. Once relaxation has dropped the 150
+ they fit, and the two entries for the IFUNC merge into one, which is one
+ fewer R_ALPHA_IRELATIVE than the first sizing reserved. */
+
+ .macro mkcall name, seq
+ ldq $27,\name($29) !literal!\seq
+ jsr $26,($27),\name !lituse_jsr!\seq
+ .endm
+
+ .text
+
+ .irpc x,012
+ .irpc y,0123456789
+ .irpc z,01234
+ .ent lf\x\y\z
+lf\x\y\z:
+ ldgp $29,0($27)
+ .prologue 1
+ ret $31,($26),1
+ .end lf\x\y\z
+ .endr
+ .endr
+ .endr
+
+ .globl bfunc
+ .ent bfunc
+bfunc:
+ ldgp $29,0($27)
+ .prologue 1
+ ldq $1,global_ifunc($29) !literal
+ .irpc x,012
+ .irpc y,0123456789
+ .irpc z,01234
+ mkcall lf\x\y\z, 1\x\y\z
+ .endr
+ .endr
+ .endr
+ .irpc x,0123456789
+ .irpc y,0123456789
+ ldq $1,h\x\y($29) !literal
+ .endr
+ .endr
+ ret $31,($26),1
+ .end bfunc
new file mode 100644
@@ -0,0 +1,14 @@
+#source: ifunc-relax-discard.s
+#source: ifunc-relax-b.s
+#ld: -relax -melf64alpha --unresolved-symbols=ignore-all --no-warn-rwx-segments -T $srcdir/$subdir/ifunc-discard.ld
+#readelf: -Wr
+
+# The got entry that survives the merge during relaxation is the one whose
+# only reference the linker script discarded, so it carries no mark of its
+# own. It has to take the one from the entry it absorbs, or nothing is
+# reserved for the reference that does survive.
+Relocation section '\.rela\.dyn' .* contains 2 entries:
+#...
+[0-9a-f]+ +0+2a R_ALPHA_IRELATIVE +[0-9a-f]+
+[0-9a-f]+ +0+2a R_ALPHA_IRELATIVE +[0-9a-f]+
+#pass
new file mode 100644
@@ -0,0 +1,41 @@
+/* As ifunc-relax-a.s, but with this object's reference to the IFUNC in a
+ section that the linker script discards. */
+
+ .text
+ .globl global_ifunc
+ .type global_ifunc, @gnu_indirect_function
+global_ifunc:
+ ret
+
+ # The only reference in this object to the IFUNC's got entry, in a
+ # section the linker script discards. The entry is still created and
+ # still occupies the got, so it is the one that survives when the two
+ # got subsections merge, and it has to take the mark from the entry
+ # that the surviving reference in ifunc-relax-b.s made.
+ .section .text.discard,"ax",@progbits
+ ldgp $29,0($27)
+ ldq $1,global_ifunc($29) !literal
+ ret
+
+ .text
+ .globl _start
+ .ent _start
+_start:
+ ldgp $29,0($27)
+ .prologue 1
+ .irpc w,01234567
+ .irpc x,0123456789
+ .irpc y,0123456789
+ .irpc z,0123456789
+ ldq $1,g\w\x\y\z($29) !literal
+ .endr
+ .endr
+ .endr
+ .endr
+ ret $31,($26),1
+ .end _start
+
+ .data
+ .globl ptr
+ptr:
+ .quad global_ifunc
new file mode 100644
@@ -0,0 +1,14 @@
+#source: ifunc-relax-a.s
+#source: ifunc-relax-b.s
+#ld: -relax -melf64alpha --unresolved-symbols=ignore-all
+#readelf: -Wr
+
+# The two objects' got subsections cannot be merged until relaxation has
+# shrunk them, and the merge leaves one entry for the IFUNC where the first
+# sizing counted two. One IRELATIVE for that entry and one for ptr; a third
+# would mean .rela.iplt was not sized again after the got was.
+Relocation section '\.rela\.dyn' .* contains 2 entries:
+#...
+[0-9a-f]+ +0+2a R_ALPHA_IRELATIVE +[0-9a-f]+
+[0-9a-f]+ +0+2a R_ALPHA_IRELATIVE +[0-9a-f]+
+#pass
new file mode 100644
@@ -0,0 +1,9 @@
+ .text
+
+ # An IFUNC exported from a shared library. A reference from another
+ # module is resolved by the dynamic linker, not by the static linker,
+ # so it is treated as an ordinary function here.
+ .globl shlib_ifunc
+ .type shlib_ifunc, @gnu_indirect_function
+shlib_ifunc:
+ ret
new file mode 100644
@@ -0,0 +1,9 @@
+#source: ifunc-weak.s
+#ld: -melf64alpha tmpdir/libalphaifunc.so
+#readelf: -Wr
+
+Relocation section '\.rela\.dyn' .* contains 2 entries:
+#...
+[0-9a-f]+ +[0-9a-f]+ +R_ALPHA_IRELATIVE +[0-9a-f]+
+[0-9a-f]+ +[0-9a-f]+ +R_ALPHA_IRELATIVE +[0-9a-f]+
+#pass
new file mode 100644
@@ -0,0 +1,9 @@
+#source: ifunc-weak.s
+#ld: -melf64alpha
+#readelf: -Wr
+
+Relocation section '\.rela\.dyn' .* contains 2 entries:
+#...
+[0-9a-f]+ +[0-9a-f]+ +R_ALPHA_IRELATIVE +[0-9a-f]+
+[0-9a-f]+ +[0-9a-f]+ +R_ALPHA_IRELATIVE +[0-9a-f]+
+#pass
new file mode 100644
@@ -0,0 +1,5 @@
+#source: ifunc-weak-undef.s
+#ld: -melf64alpha tmpdir/libalphaifunc.so
+#readelf: -Wr
+
+There are no relocations in this file\.
new file mode 100644
@@ -0,0 +1,5 @@
+#source: ifunc-weak-undef.s
+#ld: -melf64alpha
+#readelf: -Wr
+
+There are no relocations in this file\.
new file mode 100644
@@ -0,0 +1,22 @@
+ .text
+
+ # A weak reference that carries the IFUNC type without a definition.
+ # It resolves to zero like any other undefined weak function; the
+ # type must not make the linker treat it as a defined IFUNC.
+ .weak weak_ifunc
+ .type weak_ifunc, @gnu_indirect_function
+
+ .globl _start
+ .ent _start
+_start:
+ ldgp $29, 0($27)
+ ldq $27, weak_ifunc($29) !literal!1
+ jsr $26, ($27), 0 !lituse_jsr!1
+ ldgp $29, 0($26)
+ ret
+ .end _start
+
+ .data
+ .globl ptr
+ptr:
+ .quad weak_ifunc
new file mode 100644
@@ -0,0 +1,24 @@
+ .text
+
+ # A weak IFUNC, both called and referenced. It is not preemptible
+ # in an executable, so this link resolves it itself, but its weak
+ # binding makes check_relocs guess that it might become dynamic.
+ .weak weak_ifunc
+ .type weak_ifunc, @gnu_indirect_function
+weak_ifunc:
+ ret
+
+ .globl _start
+ .ent _start
+_start:
+ ldgp $29, 0($27)
+ ldq $27, weak_ifunc($29) !literal!1
+ jsr $26, ($27), 0 !lituse_jsr!1
+ ldgp $29, 0($26)
+ ret
+ .end _start
+
+ .data
+ .globl ptr
+ptr:
+ .quad weak_ifunc