This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
Re: [PATCH 1/2] [RFC] Add IFUNC support for MIPS (v4)
- From: "Maciej W. Rozycki" <macro at imgtec dot com>
- To: Faraz Shahbazker <faraz dot shahbazker at imgtec dot com>
- Cc: <binutils at sourceware dot org>, Richard Sandiford <rdsandiford at googlemail dot com>
- Date: Sun, 6 Mar 2016 07:13:44 +0000
- Subject: Re: [PATCH 1/2] [RFC] Add IFUNC support for MIPS (v4)
- Authentication-results: sourceware.org; auth=none
- References: <5583540C dot 7070800 at imgtec dot com> <87381jtr31 dot fsf at googlemail dot com> <55899D52 dot 1050000 at imgtec dot com> <87vbeegucz dot fsf at googlemail dot com> <5589AFCD dot 10905 at imgtec dot com> <DCB1C42372B1674B8F912A294CCB775A71680718 at BADAG02 dot ba dot imgtec dot org> <87615awnv8 dot fsf at googlemail dot com> <5600517C dot 1030608 at imgtec dot com> <874mig14xs dot fsf at googlemail dot com> <561D2820 dot 10107 at imgtec dot com> <871tc0acam dot fsf at googlemail dot com> <5678829D dot 4080108 at imgtec dot com> <877fk6jewx dot fsf at googlemail dot com> <568EFE7B dot 60708 at imgtec dot com>
Hi Faraz,
This is a partial review only to let you proceed as I'm heading off for
two weeks now, for a holiday/vacation. The pieces I have already deleted
are fine and these which are left here uncommented may or may not require
a further action -- I'll handle them when I'm back. Apologies for the
slow progress.
> Spec (attached) updated to reflect creation of IPLT/IGOT entries for symbols that
> bind locally. Test cases in following patch.
>
> Regards,
> Faraz Shahbazker
>
> bfd/ChangeLog:
>
> * bfd-in2.h (BFD_RELOC_MIPS_IRELATIVE): New relocation.
This is a generated file, so please list it at the end and say:
"Regenerate." only, i.e.:
* bfd-in2.h: Regenerate.
You did regenerate it according to the instructions at the top rather than
hand-editing, right?
> * elf32-mips.c
> (elf_mips_eh_howto table): Howto for BFD_RELOC_MIPS_IRELATIVE.
Please put the object name on the same line as the file name, i.e.:
* elf32-mips.c (elf_mips_eh_howto table): Howto for
BFD_RELOC_MIPS_IRELATIVE.
Likewise throughout.
> (bfd_elf32_bfd_reloc_type_lookup): Case for R_MIPS_IRELATIVE.
> (bfd_elf32_bfd_reloc_name_lookup): Case for R_MIPS_IRELATIVE.
> (mips_elf32_rtype_to_howto): Case for R_MIPS_IRELATIVE
> * elfxx-mips.c
> (struct mips_got_info): New fields general_gotno and
> assigned_general_gotno.
> (struct mips_elf_link_hash_entry): New fields for offset in to
> IPLT/IGOT, flags to indicate if symbol needs IPLT/IRELOC/IGOT
> and if it has normal GOT-based relocations.
> (mips_elf_link_hash_table): New fields for size of IPLT stubs
> and hash-table for local IFUNC symbols.
> (MIPS16_P): New macro to check ASE flag.
> (mips16_exec_iplt_entry): Template for mips16 IPLT stub.
> (mips32_exec_iplt_entry): Template for mips32 IPLT stub.
> (mips32r6_exec_iplt_entry): Template for mips32 R6 IPLT stub.
> (micromips32_exec_iplt_entry): Template for micromips32 stub.
> (mips64_exec_iplt_entry): Template for mips64 IPLT stub.
> (mips64_48b_exec_iplt_entry): Template for mips64 IPLT stub.
> (mips_elf_link_hash_newfunc): Initialization of new
> mips_elf_link_hash_entry elements.
> (mips_elf_create_stub_symbol): Set ISA bit in address for micromips
> & mips16 IPLT stubs. New argument to set st_other value of stub.
> (mips_elf_add_la25_intro): Add argument other to the call to
> mips_elf_create_stub_symbol.
> (mips_elf_add_la25_trampoline): Add argument other to the call to
> mips_elf_create_stub_symbol.
> (mips_elf_rel_dyn_section): Moved up to avoid forward declaration.
> (mips_get_irel_section): New function.
> (mips_elf_allocate_ireloc): Likewise.
> (mips_elf_allocate_iplt): Likewise.
> (mips_elf_check_ifunc_symbols): Likewise.
> (mips_elf_check_symbols): Call mips_elf_check_ifunc_symbols to
> allocate an IPLT entry for an IFUNC symbol and set has_gnu_symbols.
> Skip creation of la25 stubs for IFUNCs having IPLT stubs.
> (mips_elf_count_got_entry): Count GOT entries for IFUNCs that do
> not have an IPLT stub under the general GOT region and allocate
> space for an IRELATIVE relocation for each.
> (mips_elf_got16_entry): Add argument h and pass it to
> mips_elf_create_local_got_entry instead of NULL.
> (mips_elf_create_local_got_entry): Change hash-lookup for IFUNCs
> to use non-NULL input BFD and a pointer to the
> mips_elf_link_hash_entry. Add check for sufficient general GOT
> entries. Assign local IFUNCs from general GOT entries pool.
> Allow early return for existing hash entry only if gotidx has
> been assigned. Allocate general GOT only if symbol does not
> have an IGOT entry.
> (mips_elf_record_local_got_symbol): New argument for pointer to
> mips_elf_link_hash_entry of the local symbol. If non-NULL, this
> is used to set d.h in the GOT has entry instead of d.addend.
> (mips_use_local_got_p): Return TRUE for all IFUNC symbol definitions.
Please limit line lengths in your ChangeLog entries to fit within 74
columns.
Rationale:
<https://sourceware.org/gdb/wiki/ContributionChecklist#Properly_Formatted_GNU_ChangeLog>
-- there's a `fill-column' setting to enforce this for GNU Emacs users at
the bottom of bfd/ChangeLog and our other ChangeLog files in case you
wondered whether the GDB contribution checklist applied to binutils as
well; in any case I won't complain whenever you do comply with that
checklist wherever applicable with your binutils submissions related to my
area of responsibility even though other maintainers might let changes
through regardless.
> (mips_elf_create_ifunc_sections): New function.
> (get_local_sym_hash): Likewise.
> (mips_elf_calculate_relocation): Create hash-table for local IFUNC
Formatting here, please use a leading tab rather than spaces (I take it
as an oversight; if you have tools like I do to catch cases like this and
eliminate them at the commit time, then I won't speak up on these again).
> symbols and condition them to be accessed through GOT.
> Point IFUNC symbol value to IPLT stub for symbols that need IPLT.
> Force global IFUNC to be allocated from local GOT region.
> (mips_elf_perform_relocation): New argument flag indication relocation
> targets IFUNC. Disable JALR to BAL optimization for IFUNC symobls.
> (mips_elf_create_dynamic_relocation): Emit IRELATIVE relocation
> instead of REL32 for local IFUNC reference. Relax assertion for
Two spaces after a full stop please.
> IFUNC symbols in explicit GOT region to have dynamic relocations.
> (_bfd_mips_elf_section_processing): Size .igot section.
Leading tab vs spaces too.
> (_bfd_mips_elf_check_relocs): Check need to create IFUNC sections.
> If symbol is an IFUNC, don't convert it to an STT_FUNC. Relax error
> checking to allow local IFUNCs to be accessed via call16 reloc.
> Record calls to a local IFUNC symbols and pre-allocate GOT entries
> for call16 and got16 relocations.
> (_bfd_mips_elf_adjust_dynamic_symbol): Add STT_GNU_IFUNC to sanity
> check.
> (_bfd_mips_elf_always_size_sections): Allocate IPLTs for local IFUNCs.
> (mips_elf_lay_out_got): Offset local GOT entries to follow general
> GOT entries.
> (_bfd_mips_elf_size_dynamic_sections): Exclude IPLT and IGOT.
> Create dynamic tag for DT_MIPS_GENERAL_GOTNO if needed.
> (_bfd_mips_elf_relocate_section): Relax error checking to allow
> local IFUNCs to be accessed via standalone got16 reloc. Pass
> gnu_ifunc_p flag when calling mips_elf_perform_relocation.
> (mips_elf_create_iplt): New function.
> (mips_elf_check_local_got_index): Likewise.
> (mips_elf_create_ireloc): Likewise.
> (_bfd_mips_elf_finish_dynamic_symbol): Create IPLT stub/IRELATIVE
> relocation for IFUNC symbols as necessary. Set IFUNC symbol value
> to the IPLT entry address for executable objects.
> (_bfd_mips_elf_finish_local_dynamic_symbol): New function.
> (_bfd_mips_elf_finish_dynamic_sections): Call
> _bfd_mips_elf_finish_local_dynamic_symbol for all local IFUNCs.
> Set values of dynamic tag - DT_MIPS_GENERAL_GOTNO.
> (local_htab_hash): New function, hash table for local IFUNCs.
> (loc_htab_eq): New comparison function for local IFUNC hash table.
> (_bfd_mips_elf_link_hash_table_free): New function.
> (_bfd_mips_elf_link_hash_table_create): Allocate a hash table for
> local IFUNCs.
> (_bfd_mips_elf_get_target_dtag): Add cases for dynamic tag -
> DT_MIPS_GENERAL_GOTNO.
> (_bfd_mips_post_process_headers): If ELF uses GNU IFUNCs, increase
> ABIVERSION to 4.
> * libbfd.h
> (bfd_reloc_code_real_names): Entry for BFD_RELOC_MIPS_IRELATIVE.
> * reloc.c
> (ENUMDOC): BFD_RELOC_MIPS_IRELATIVE entry.
>
> binutils/ChangeLog:
>
> * readelf.c:
> (get_mips_dynamic_type): Add case for DT_MIPS_GENERAL_GOTNO.
> (dynamic_section_mips_val): Add case for DT_MIPS_GENERAL_GOTNO.
>
> elfcpp/ChangeLog:
>
> * elfcpp.h
> (enum DT): Add cases for dynamic tag DT_MIPS_GENERAL_GOTNO.
>
> include/elf/ChangeLog:
>
> * mips.h
> (START_RELOC_NUMBERS): Entry for R_MIPS_IRELATIVE.
> (DT_MIPS_GENERAL_GOTNO): New dynamic tag.
> ---
> bfd/bfd-in2.h | 3 +
> bfd/elf32-mips.c | 22 ++
> bfd/elfxx-mips.c | 1076 +++++++++++++++++++++++++++++++++++++++++++++++-----
> bfd/libbfd.h | 1 +
> bfd/reloc.c | 5 +
> binutils/readelf.c | 3 +
> elfcpp/elfcpp.h | 2 +
> include/elf/mips.h | 7 +
> 8 files changed, 1030 insertions(+), 89 deletions(-)
>
> diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
> index fb4858c..0218164 100644
> --- a/bfd/bfd-in2.h
> +++ b/bfd/bfd-in2.h
> @@ -3062,6 +3062,9 @@ to compensate for the borrow when the low bits are added. */
> BFD_RELOC_MIPS_JUMP_SLOT,
>
>
> +/* MIPS support for STT_GNU_IFUNC. */
> + BFD_RELOC_MIPS_IRELATIVE,
> +
> /* Moxie ELF relocations. */
> BFD_RELOC_MOXIE_10_PCREL,
Please group the reloc with MIPS rather than moxie relocations, i.e. one
newline before it and two afterwards.
> diff --git a/bfd/elf32-mips.c b/bfd/elf32-mips.c
> index 752f386..e8f1079 100644
> --- a/bfd/elf32-mips.c
> +++ b/bfd/elf32-mips.c
> @@ -1646,6 +1646,22 @@ static reloc_howto_type elf_mips_eh_howto =
> 0xffffffff, /* dst_mask */
> FALSE); /* pcrel_offset */
>
> +/* STT_GNU_IFUNC support. */
> +static reloc_howto_type elf_mips_irelative_howto =
> + HOWTO (R_MIPS_IRELATIVE, /* type */
> + 0, /* rightshift */
> + 2, /* size (0 = byte, 1 = short, 2 = long) */
> + 32, /* bitsize */
> + FALSE, /* pc_relative */
> + 0, /* bitpos */
> + complain_overflow_bitfield,/* complain_on_overflow */
> + bfd_elf_generic_reloc, /* special_function */
> + "R_MIPS_IRELATIVE", /* name */
> + TRUE, /* partial_inplace */
> + 0xffffffff, /* src_mask */
> + 0xffffffff, /* dst_mask */
> + FALSE); /* pcrel_offset */
> +
> /* Set the GP value for OUTPUT_BFD. Returns FALSE if this is a
> dangerous relocation. */
>
> @@ -2126,6 +2142,8 @@ bfd_elf32_bfd_reloc_type_lookup (bfd *abfd, bfd_reloc_code_real_type code)
> return &elf_mips_jump_slot_howto;
> case BFD_RELOC_MIPS_EH:
> return &elf_mips_eh_howto;
> + case BFD_RELOC_MIPS_IRELATIVE:
> + return &elf_mips_irelative_howto;
> }
> }
>
> @@ -2173,6 +2191,8 @@ bfd_elf32_bfd_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
> return &elf_mips_jump_slot_howto;
> if (strcasecmp (elf_mips_eh_howto.name, r_name) == 0)
> return &elf_mips_eh_howto;
> + if (strcasecmp (elf_mips_irelative_howto.name, r_name) == 0)
> + return &elf_mips_irelative_howto;
>
> return NULL;
> }
> @@ -2199,6 +2219,8 @@ mips_elf32_rtype_to_howto (unsigned int r_type,
> return &elf_mips_jump_slot_howto;
> case R_MIPS_EH:
> return &elf_mips_eh_howto;
> + case R_MIPS_IRELATIVE:
> + return &elf_mips_irelative_howto;
> default:
> if (r_type >= R_MICROMIPS_min && r_type < R_MICROMIPS_max)
> return &elf_micromips_howto_table_rel[r_type - R_MICROMIPS_min];
> diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
> index 4ece819..6f23387 100644
> --- a/bfd/elfxx-mips.c
> +++ b/bfd/elfxx-mips.c
> @@ -45,6 +45,7 @@
> #include "coff/mips.h"
>
> #include "hashtab.h"
> +#include "objalloc.h"
>
> /* Types of TLS GOT entry. */
> enum mips_got_tls_type {
> @@ -165,10 +166,14 @@ struct mips_got_info
> unsigned int tls_assigned_gotno;
> /* The number of local .got entries, eventually including page entries. */
> unsigned int local_gotno;
> + /* The number of explicitly relocated .got entries. */
> + unsigned int general_gotno;
> /* The maximum number of page entries needed. */
> unsigned int page_gotno;
> /* The number of relocations needed for the GOT entries. */
> unsigned int relocs;
> + /* The first unused general .got entry. */
> + unsigned int assigned_general_gotno;
> /* The first unused local .got entry. */
> unsigned int assigned_low_gotno;
> /* The last unused local .got entry. */
> @@ -375,6 +380,12 @@ struct mips_elf_link_hash_entry
> being called returns a floating point value. */
> asection *call_fp_stub;
>
> + /* Offset into the IPLT table. */
> + bfd_vma iplt_offset;
> +
> + /* Offset into the IGOT table. */
> + int igot_offset;
> +
> /* The highest GGA_* value that satisfies all references to this symbol. */
> unsigned int global_got_area : 2;
>
> @@ -392,6 +403,9 @@ struct mips_elf_link_hash_entry
> cannot possibly be made dynamic). */
> unsigned int has_static_relocs : 1;
>
> + /* True if there is a got16 or call16 relocation against this symbol. */
> + unsigned int has_got_relocs : 1;
> +
> /* True if we must not create a .MIPS.stubs entry for this symbol.
> This is set, for example, if there are relocations related to
> taking the function's address, i.e. any but R_MIPS_CALL*16 ones.
> @@ -413,6 +427,15 @@ struct mips_elf_link_hash_entry
>
> /* Does this symbol resolve to a PLT entry? */
> unsigned int use_plt_entry : 1;
> +
> + /* Does this symbol need an IPLT stub? */
> + unsigned int needs_iplt : 1;
> +
> + /* Does this symbol need an IPLT stub? */
> + unsigned int needs_igot : 1;
> +
> + /* Does this ifunc symbol need an IRELATIVE relocation? */
> + unsigned int needs_ireloc : 1;
> };
>
> /* MIPS ELF linker hash table. */
> @@ -485,6 +508,9 @@ struct mips_elf_link_hash_table
> /* The index of the next .got.plt entry to create. */
> bfd_vma plt_got_index;
>
> + /* The size of an IPLT entry in bytes. */
> + bfd_vma iplt_entry_size;
> +
> /* The number of functions that need a lazy-binding stub. */
> bfd_vma lazy_stub_count;
>
> @@ -516,6 +542,10 @@ struct mips_elf_link_hash_table
>
> /* Is the PLT header compressed? */
> unsigned int plt_header_is_comp : 1;
> +
> + /* Used by local STT_GNU_IFUNC symbols. */
> + htab_t loc_hash_table;
> + void *loc_hash_memory;
> };
>
> /* Get the MIPS ELF linker hash table from a link_info structure. */
> @@ -800,6 +830,10 @@ static bfd *reldyn_sorting_bfd;
> #define MICROMIPS_P(abfd) \
> ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_MICROMIPS) != 0)
>
> +/* Nonzero if ABFD has mips16 code. */
> +#define MIPS16_P(abfd) \
> + ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_M16) != 0)
> +
This is unreliable, objects produced with older tools won't have the flag
set even if they contain MIPS16 code. You may end up with a mixture of
old MIPS16 objects built with pre-IFUNC tools and new MIPS32 objects with
IFUNC definitions causing those old MIPS16 objects calling the IPLT.
You shouldn't need to check this flag though, see below.
> @@ -1185,6 +1219,69 @@ static const bfd_vma mips_vxworks_shared_plt_entry[] =
> 0x10000000, /* b .PLT_resolver */
> 0x24180000 /* li t8, <pltindex> */
> };
> +
> +/* The format of MIPS16 o32 IPLT entries. We use v0 ($2)
> + and v1 ($3) as temporaries because t8 ($24) and t9 ($25) are not
> + directly addressable. */
> +static const bfd_vma mips16_exec_iplt_entry[] =
> +{
> + 0xb202, /* lw $2, 8($pc) */
> + 0x9a60, /* lw $3, 0($2) */
> + 0xeb00, /* jr $3 */
> + 0x653b, /* move $25, $3 */
> + 0x0000, 0x0000 /* .word (.igot address) */
> +};
No need to use both $2 and $3 here, please reencode to use $2 only.
> +
> +/* The format of 32 bit IPLT entries. */
> +static const bfd_vma mips32_exec_iplt_entry[] =
> +{
> + 0x3c0f0000, /* lui $15, %hi(.igot address) */
Please use a tab rather than spaces to separate the comment from code.
Likewise throughout these entries.
> + 0x8df90000, /* lw $25, %lo(.igot address)($15) */
> + 0x03200008, /* jr $25 */
> + 0x00000000 /* nop */
> +};
> +
> +/* Format of 32 bit IPLT entries for R6. JR encoding differs. */
> +static const bfd_vma mips32r6_exec_iplt_entry[] =
> +{
> + 0x3c0f0000, /* lui $15, %hi(.igot address) */
> + 0x8df90000, /* lw $25, %lo(.igot address)($15) */
> + 0x03200009, /* jr $25 */
> + 0x00000000 /* nop */
> +};
Please do use a compact R6 jump encoding here, as Richard originally
advised, i.e.:
0xd8190000, /* jrc $25 */
There's no need for a trailing NOP then as jumps have no forbidden slot.
> +
> +/* The format of 32-bit micromips IPLT entries. */
> +static const bfd_vma micromips32_exec_iplt_entry[] =
> +{
> + 0x41a30000, /* lui $2, %hi(.igot address) */
> + 0xff230000, /* lw $2, %lo(.igot address)($2) */
NB you've got $3 encoded here rather than $2 as the temporary. Please
use $15 hovever, which is standard here, as there's no specific need to be
unusual here. Also the load target is $25 rather than $2, but you've got
this encoded right, so just adjust the comment.
And please use separate halfwords for 32-bit instructions like with other
microMIPS entries. The use of `bfd_put_micromips_32' is attractive and we
can consider it later on as a small optimisation, but your choice breaks
automatic array size calculation in `mips_elf_create_ifunc_sections' by
requiring an implicit assumption (subtracting 2). Besides, we use such an
arrangement for all the other microMIPS entries.
> + 0x45b9, /* jrc $25 */
> +};
The compact jump is OK here, but please do retain the original NOP, for
alignment (even though it won't be executed), as there's an inevitable
performance penalty when a 32-bit instruction jumped to from elsewhere
spans a cache line boundary, because in this case the processor has to
fill two cache lines before it can even fetch the whole instruction.
See the existing MIPS16 and microMIPS entries for examples.
> +
> +/* The format of 64-bit IPLT entries. */
> +static const bfd_vma mips64_exec_iplt_entry[] =
> +{
> + 0x3c0f0000, /* lui $15, %highest(.got.iplt entry) */
> + 0x3c0e0000, /* lui $14, %hi(.got.iplt entry) */
> + 0x25ef0000, /* addiu $15, $15, %higher(.got.iplt entry) */
> + 0x000f783c, /* dsll32 $15, $15, 0x0 */
> + 0x01ee782d, /* daddu $15, $15, $14 */
> + 0xddf90000, /* ld $25, %lo(.got.iplt entry)($15) */
> + 0x03200008, /* jr $25 */
> + 0x00000000, /* nop */
> +};
> +
> +/* The format of 64-bit IPLT entries for 48bit address. */
> +static const bfd_vma mips64_48b_exec_iplt_entry[] =
> +{
> + 0x3c0f0000, /* lui $15, %higher(.got.iplt entry) */
> + 0x25ef0000, /* addiu $15, $15, %high(.got.iplt entry) */
> + 0x000f7c38, /* dsll $15, $15, 16 */
> + 0xddf90000, /* ld $25, %lo(.got.iplt entry)($15) */
> + 0x03200008, /* jr $25 */
> + 0x00000000, /* nop */
> +};
> +
>
> /* microMIPS 32-bit opcode helper installer. */
>
> @@ -1278,11 +1375,17 @@ mips_elf_link_hash_newfunc (struct bfd_hash_entry *entry,
> ret->got_only_for_calls = TRUE;
> ret->readonly_reloc = FALSE;
> ret->has_static_relocs = FALSE;
> + ret->has_got_relocs = FALSE;
> ret->no_fn_stub = FALSE;
> ret->need_fn_stub = FALSE;
> ret->has_nonpic_branches = FALSE;
> ret->needs_lazy_stub = FALSE;
> ret->use_plt_entry = FALSE;
> + ret->needs_iplt = FALSE;
> + ret->needs_igot = FALSE;
> + ret->needs_ireloc = FALSE;
> + ret->iplt_offset = -1;
> + ret->igot_offset = -1;
> }
>
> return (struct bfd_hash_entry *) ret;
> @@ -1576,13 +1679,14 @@ static bfd_boolean
> mips_elf_create_stub_symbol (struct bfd_link_info *info,
> struct mips_elf_link_hash_entry *h,
> const char *prefix, asection *s, bfd_vma value,
> - bfd_vma size)
> + bfd_vma size, unsigned int other)
> {
> struct bfd_link_hash_entry *bh;
> struct elf_link_hash_entry *elfh;
> const char *name;
>
> - if (ELF_ST_IS_MICROMIPS (h->root.other))
> + if (ELF_ST_IS_MICROMIPS (h->root.other)
> + || (ELF_ST_IS_MIPS16 (h->root.other) && h->root.type == STT_GNU_IFUNC))
> value |= 1;
Why do you need to check for STT_GNU_IFUNC, is checking ELF_ST_IS_MIPS16
not enough here? NB existing users won't call this function because
MIPS16 functions never need LA25 stubs.
> @@ -1598,6 +1702,8 @@ mips_elf_create_stub_symbol (struct bfd_link_info *info,
> elfh->type = ELF_ST_INFO (STB_LOCAL, STT_FUNC);
> elfh->size = size;
> elfh->forced_local = 1;
> + elfh->other = other;
> +
Why do you need to set `other' while you have already set the ISA bit in
`value'? Our rule for handling is to set either rather than both at a
time, why do you need an exception here?
> return TRUE;
> }
>
> @@ -1863,7 +1969,7 @@ mips_elf_add_la25_intro (struct mips_elf_la25_stub *stub,
> s->size = (1 << align) - 8;
>
> /* Create a symbol for the stub. */
> - mips_elf_create_stub_symbol (info, stub->h, ".pic.", s, s->size, 8);
> + mips_elf_create_stub_symbol (info, stub->h, ".pic.", s, s->size, 8, 0);
> stub->stub_section = s;
> stub->offset = s->size;
>
> @@ -1900,7 +2006,7 @@ mips_elf_add_la25_trampoline (struct mips_elf_la25_stub *stub,
> }
>
> /* Create a symbol for the stub. */
> - mips_elf_create_stub_symbol (info, stub->h, ".pic.", s, s->size, 16);
> + mips_elf_create_stub_symbol (info, stub->h, ".pic.", s, s->size, 16, 0);
> stub->stub_section = s;
> stub->offset = s->size;
>
> @@ -1963,6 +2069,156 @@ mips_elf_add_la25_stub (struct bfd_link_info *info,
> : mips_elf_add_la25_intro (stub, info));
> }
>
> +/* Return the dynamic relocation section. If it doesn't exist, try to
> + create a new one if CREATE_P, otherwise return NULL. Also return NULL
> + if creation fails. */
> +
> +static asection *
> +mips_elf_rel_dyn_section (struct bfd_link_info *info, bfd_boolean create_p)
> +{
> + const char *dname;
> + asection *sreloc;
> + bfd *dynobj;
> +
> + dname = MIPS_ELF_REL_DYN_NAME (info);
> + dynobj = elf_hash_table (info)->dynobj;
> + sreloc = bfd_get_linker_section (dynobj, dname);
> + if (sreloc == NULL && create_p)
> + {
> + sreloc = bfd_make_section_anyway_with_flags (dynobj, dname,
> + (SEC_ALLOC
> + | SEC_LOAD
> + | SEC_HAS_CONTENTS
> + | SEC_IN_MEMORY
> + | SEC_LINKER_CREATED
> + | SEC_READONLY));
> + if (sreloc == NULL
> + || !bfd_set_section_alignment (dynobj, sreloc,
> + MIPS_ELF_LOG_FILE_ALIGN (dynobj)))
> + return NULL;
> + }
> + return sreloc;
> +}
> +
> +/* Return section for IRELATIVE relocations. If the link is dynamic, the
> + relocations should go in .dynrel, otherwise they should go in the special
> + .rel.iplt section. */
> +
> +static asection *
> +mips_get_irel_section (struct bfd_link_info *info,
> + struct mips_elf_link_hash_table *htab)
> +{
> + asection *srel = (elf_hash_table (info)->dynamic_sections_created)
> + ? mips_elf_rel_dyn_section (info, FALSE)
> + : htab->root.irelplt;
> + BFD_ASSERT (srel != NULL);
> + return srel;
> +}
> +
> +/* Reserve space in the rel.iplt section for an IRELATIVE relocation. */
> +
> +static bfd_boolean
> +mips_elf_allocate_ireloc (struct bfd_link_info *info,
> + struct mips_elf_link_hash_table *mhtab,
> + struct mips_elf_link_hash_entry *mh)
> +{
> + asection *srel;
> + bfd *dynobj;
> +
> + srel = mips_get_irel_section (info, mhtab);
> + dynobj = elf_hash_table (info)->dynobj;
> + if (srel != mhtab->root.irelplt && srel->size == 0)
> + {
> + /* Make room for a null element. */
> + srel->size += MIPS_ELF_REL_SIZE (dynobj);
> + ++srel->reloc_count;
> + }
> + srel->size += MIPS_ELF_REL_SIZE (dynobj);
> + mh->needs_ireloc = TRUE;
> +
> + return TRUE;
> +}
> +
> +/* Reserve space in the iplt and igot tables for an ifunc entry
> + and allocate space for an IRELATIVE relocation. */
> +
> +static bfd_boolean
> +mips_elf_allocate_iplt (struct bfd_link_info *info,
> + struct mips_elf_link_hash_table *mhtab,
> + struct mips_elf_link_hash_entry *mh)
> +{
> + asection *s;
> + bfd *abfd = info->output_bfd;
> + unsigned int other = 0;
> +
> + BFD_ASSERT (!mh->needs_iplt);
> + BFD_ASSERT (mhtab->root.iplt != NULL);
> +
> + s = mhtab->root.iplt;
> + if (ELF_ST_IS_MIPS16 (mh->root.other))
> + other = STO_MIPS16;
> +
> + mh->iplt_offset = s->size;
> + mips_elf_create_stub_symbol (info, mh, ".iplt.", mhtab->root.iplt,
> + s->size, mhtab->iplt_entry_size, other);
> + s->size += mhtab->iplt_entry_size;
> +
> + BFD_ASSERT (mhtab->root.igotplt != NULL);
> +
> + /* Only create IGOT entry if there are no GOT relocations, or when
> + there are non-CALL references to the symbol. In the latter case,
> + existing GOT entry must point to IPLT, so an IGOT entry is needed
> + to catch the result of the IRELATIVE relocation resolution. */
> + if (!mh->has_got_relocs || mh->root.pointer_equality_needed)
> + {
> + mh->igot_offset = mhtab->root.igotplt->size;
> + mhtab->root.igotplt->size += MIPS_ELF_GOT_SIZE (abfd);
> + mh->needs_igot = TRUE;
> + }
> +
> + mh->needs_iplt = TRUE;
> +
> + /* IRELATIVE fixup will be needed for each local IFUNC. */
> + if (!mips_elf_allocate_ireloc (info, mips_elf_hash_table (info), mh))
> + return FALSE;
> +
> + return TRUE;
> +}
> +
> +/* hash_traverse callback that is called before sizing sections.
> + DATA points to a mips_htab_traverse_info structure. */
> +
> +static bfd_boolean
> +mips_elf_check_ifunc_symbols (void **slot, void *data)
> +{
> + struct mips_htab_traverse_info *hti =
> + (struct mips_htab_traverse_info *) data;
> + struct mips_elf_link_hash_entry *h =
> + (struct mips_elf_link_hash_entry *) *slot;
> +
> + if (h
> + && !h->needs_iplt
> + && h->root.type == STT_GNU_IFUNC
> + && h->root.def_regular)
> + {
> + struct bfd_link_info *info = hti->info;
> + elf_tdata (info->output_bfd)->has_gnu_symbols |= elf_gnu_symbol_ifunc;
> +
> + /* For global symbols, .iplt entry is needed only for all non-shared-
> + objects. For local symbols, it is needed only if the symbol has
> + static relocations. */
> + if (((h->root.forced_local && h->has_static_relocs)
> + || (!h->root.forced_local && !bfd_link_pic (info)))
> + && !mips_elf_allocate_iplt (info, mips_elf_hash_table (info), h))
> + {
> + hti->error = TRUE;
> + return FALSE;
> + }
> + }
> +
> + return TRUE;
> +}
> +
> /* A mips_elf_link_hash_traverse callback that is called before sizing
> sections. DATA points to a mips_htab_traverse_info structure. */
>
> @@ -1975,6 +2231,12 @@ mips_elf_check_symbols (struct mips_elf_link_hash_entry *h, void *data)
> if (!bfd_link_relocatable (hti->info))
> mips_elf_check_mips16_stubs (hti->info, h);
>
> + /* Create stubs and relocations for IFUNC symbols. */
> + if (h
> + && h->root.type == STT_GNU_IFUNC
> + && !mips_elf_check_ifunc_symbols ((void **)&h, hti))
> + return FALSE;
> +
> if (mips_elf_local_pic_function_p (h))
> {
> /* PR 12845: If H is in a section that has been garbage
> @@ -1986,13 +2248,15 @@ mips_elf_check_symbols (struct mips_elf_link_hash_entry *h, void *data)
> If we're creating a non-PIC relocatable object, mark H as
> being PIC. If we're creating a non-relocatable object with
> non-PIC branches and jumps to H, make sure that H has an la25
> - stub. */
> + stub. IFUNCs with IPLT stubs don't need an la25 stub. */
> if (bfd_link_relocatable (hti->info))
> {
> if (!PIC_OBJECT_P (hti->output_bfd))
> h->root.other = ELF_ST_SET_MIPS_PIC (h->root.other);
> }
> - else if (h->has_nonpic_branches && !mips_elf_add_la25_stub (hti->info, h))
> + else if (h->has_nonpic_branches
> + && (h->root.type != STT_GNU_IFUNC || !h->needs_iplt)
> + && !mips_elf_add_la25_stub (hti->info, h))
> {
> hti->error = TRUE;
> return FALSE;
> @@ -3130,37 +3394,6 @@ mips_elf_replace_bfd_got (bfd *abfd, struct mips_got_info *g)
> tdata->got = g;
> }
>
> -/* Return the dynamic relocation section. If it doesn't exist, try to
> - create a new it if CREATE_P, otherwise return NULL. Also return NULL
> - if creation fails. */
> -
> -static asection *
> -mips_elf_rel_dyn_section (struct bfd_link_info *info, bfd_boolean create_p)
> -{
> - const char *dname;
> - asection *sreloc;
> - bfd *dynobj;
> -
> - dname = MIPS_ELF_REL_DYN_NAME (info);
> - dynobj = elf_hash_table (info)->dynobj;
> - sreloc = bfd_get_linker_section (dynobj, dname);
> - if (sreloc == NULL && create_p)
> - {
> - sreloc = bfd_make_section_anyway_with_flags (dynobj, dname,
> - (SEC_ALLOC
> - | SEC_LOAD
> - | SEC_HAS_CONTENTS
> - | SEC_IN_MEMORY
> - | SEC_LINKER_CREATED
> - | SEC_READONLY));
> - if (sreloc == NULL
> - || ! bfd_set_section_alignment (dynobj, sreloc,
> - MIPS_ELF_LOG_FILE_ALIGN (dynobj)))
> - return NULL;
> - }
> - return sreloc;
> -}
> -
> /* Return the GOT_TLS_* type required by relocation type R_TYPE. */
>
> static int
> @@ -3254,8 +3487,21 @@ mips_elf_count_got_entry (struct bfd_link_info *info,
> entry->symndx < 0
> ? &entry->d.h->root : NULL);
> }
> - else if (entry->symndx >= 0 || entry->d.h->global_got_area == GGA_NONE)
> - g->local_gotno += 1;
> + else if (entry->symndx >= 0 || (entry->d.h->global_got_area == GGA_NONE))
> + {
> + /* Count IFUNCs as general GOT entries with explicit relocations. */
> + if (entry->symndx < 0
> + && entry->d.h->root.type == STT_GNU_IFUNC
> + && entry->d.h->root.def_regular
> + && !entry->d.h->needs_igot)
> + {
> + g->general_gotno += 1;
> + mips_elf_allocate_ireloc (info, mips_elf_hash_table (info),
> + entry->d.h);
> + }
> + else
> + g->local_gotno += 1;
> + }
> else
> g->global_gotno += 1;
> }
> @@ -3587,7 +3833,8 @@ mips_elf_got_page (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
>
> static bfd_vma
> mips_elf_got16_entry (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
> - bfd_vma value, bfd_boolean external)
> + bfd_vma value, bfd_boolean external,
> + struct mips_elf_link_hash_entry *h)
> {
> struct mips_got_entry *entry;
>
> @@ -3602,7 +3849,7 @@ mips_elf_got16_entry (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
> R_MIPS16_GOT16, R_MIPS_CALL16, etc. The format of the entry is the
> same in all cases. */
> entry = mips_elf_create_local_got_entry (abfd, info, ibfd, value, 0,
> - NULL, R_MIPS_GOT16);
> + h, R_MIPS_GOT16);
> if (entry)
> return entry->gotidx;
> else
> @@ -3691,18 +3938,28 @@ mips_elf_create_local_got_entry (bfd *abfd, struct bfd_link_info *info,
> return entry;
> }
>
> - lookup.abfd = NULL;
> lookup.symndx = -1;
> - lookup.d.address = value;
> + if (h && h->root.type == STT_GNU_IFUNC)
> + {
> + lookup.abfd = ibfd;
> + lookup.d.h = h;
> + }
> + else
> + {
> + lookup.abfd = NULL;
> + lookup.d.address = value;
> + }
> +
> loc = htab_find_slot (g->got_entries, &lookup, INSERT);
> if (!loc)
> return NULL;
>
> entry = (struct mips_got_entry *) *loc;
> - if (entry)
> + if (entry && entry->gotidx >= 0)
> return entry;
>
> - if (g->assigned_low_gotno > g->assigned_high_gotno)
> + if (g->assigned_low_gotno > g->assigned_high_gotno
> + || g->assigned_general_gotno > g->local_gotno)
> {
> /* We didn't allocate enough space in the GOT. */
> (*_bfd_error_handler)
> @@ -3715,7 +3972,15 @@ mips_elf_create_local_got_entry (bfd *abfd, struct bfd_link_info *info,
> if (!entry)
> return NULL;
>
> - if (got16_reloc_p (r_type)
> + if (h && h->needs_ireloc && !h->needs_igot)
> + /* Allocate IFUNC slots in the general GOT region since they
> + will need explicit IRELATIVE relocations. */
> + {
> + lookup.gotidx = MIPS_ELF_GOT_SIZE (abfd) * g->assigned_general_gotno++;
> + if (h->needs_iplt)
> + h->igot_offset = lookup.gotidx;
> + }
> + else if (got16_reloc_p (r_type)
> || call16_reloc_p (r_type)
> || got_page_reloc_p (r_type)
> || got_disp_reloc_p (r_type))
> @@ -3958,7 +4223,8 @@ mips_elf_record_global_got_symbol (struct elf_link_hash_entry *h,
>
> static bfd_boolean
> mips_elf_record_local_got_symbol (bfd *abfd, long symndx, bfd_vma addend,
> - struct bfd_link_info *info, int r_type)
> + struct bfd_link_info *info, int r_type,
> + struct mips_elf_link_hash_entry *h)
> {
> struct mips_elf_link_hash_table *htab;
> struct mips_got_info *g;
> @@ -3972,7 +4238,10 @@ mips_elf_record_local_got_symbol (bfd *abfd, long symndx, bfd_vma addend,
>
> entry.abfd = abfd;
> entry.symndx = symndx;
> - entry.d.addend = addend;
> + if (h)
> + entry.d.h = h;
> + else
> + entry.d.addend = addend;
> entry.tls_type = mips_elf_reloc_tls_type (r_type);
> return mips_elf_record_got_entry (info, abfd, &entry);
> }
> @@ -4389,7 +4658,11 @@ mips_use_local_got_p (struct bfd_link_info *info,
> local GOT. This includes symbols that are completely undefined
> and which therefore don't bind locally. We'll report undefined
> symbols later if appropriate. */
> - if (h->root.dynindx == -1)
> +
> + /* Both global & local IFUNC symbols actually use the explicitly relocated
> + GOT region, but we don't distinguish it from the local GOT just yet. */
> + if (h->root.dynindx == -1
> + || (h->root.type == STT_GNU_IFUNC && h->root.def_regular))
> return TRUE;
>
> /* Symbols that bind locally can (and in the case of forced-local
> @@ -5075,6 +5348,65 @@ mips_elf_create_compact_rel_section
> return TRUE;
> }
>
> +/* Create the .iplt, .rel(a).iplt and .igot sections. */
> +
> +static bfd_boolean
> +mips_elf_create_ifunc_sections (struct bfd_link_info *info)
> +{
> + struct mips_elf_link_hash_table * volatile htab;
> + const struct elf_backend_data *bed;
> + bfd *dynobj;
> + asection *s;
> + flagword flags;
> +
> + htab = mips_elf_hash_table (info);
> + dynobj = htab->root.dynobj;
> + bed = get_elf_backend_data (dynobj);
> + flags = bed->dynamic_sec_flags;
> +
> + if (!bfd_link_pic (info))
> + {
> + if (ABI_64_P (dynobj))
> + htab->iplt_entry_size = 4 * ARRAY_SIZE (mips64_exec_iplt_entry);
> + else if (MIPS16_P (dynobj))
> + htab->iplt_entry_size = 2 * ARRAY_SIZE (mips16_exec_iplt_entry);
> + else if (MICROMIPS_P (dynobj))
> + htab->iplt_entry_size = (4 * ARRAY_SIZE (micromips32_exec_iplt_entry)
> + - 2);
You don't need to subtract 2 here if using 16-bit words in
`micromips32_exec_iplt_entry'.
Also you cannot fix IPLT entries based on global MIPS16/microMIPS object
flags as you may have to create mixed IPLT entries in a single output
binary if mixed regular/compressed code is present in input. You may even
have to create two IPLT entries for a single function symbol. Otherwise
you'll have issues with tail calls and short delay slots and even if you
escape them you may cause unnecessary mode switches that are costly. So
you need to decide based on the referring reloc type.
Fortunately all the necessary details will have already been collected in
case ordinary PLT is to be produced. So you can just reuse that stuff for
your purpose as well.
> + else
> + htab->iplt_entry_size = 4 * (ARRAY_SIZE (mips32_exec_iplt_entry)
> + + (LOAD_INTERLOCKS_P (dynobj) ? 0 : 1));
> +
> + s = bfd_make_section_anyway_with_flags (dynobj, ".iplt",
> + flags | SEC_READONLY | SEC_CODE);
> + if (s == NULL ||
> + !bfd_set_section_alignment (dynobj, s, bed->plt_alignment))
> + return FALSE;
> +
> + htab->root.iplt = s;
> +
> + BFD_ASSERT (htab->root.igotplt == NULL);
> +
> + s = bfd_make_section_anyway_with_flags (dynobj, ".igot", flags);
> + if (s == NULL
> + || !bfd_set_section_alignment (dynobj, s, bed->s->log_file_align))
> + return FALSE;
> + htab->root.igotplt = s;
> + mips_elf_section_data (s)->elf.this_hdr.sh_flags
> + |= (SHF_ALLOC | SHF_WRITE);
> + }
> +
> + BFD_ASSERT (htab->root.irelplt == NULL);
> +
> + s = bfd_make_section_with_flags (dynobj, ".rel.iplt", flags | SEC_READONLY);
> + if (s == NULL
> + || !bfd_set_section_alignment (dynobj, s, bed->s->log_file_align))
> + return FALSE;
> +
> + htab->root.irelplt = s;
> + return TRUE;
> +}
> +
> /* Create the .got section to hold the global offset table. */
>
> static bfd_boolean
> @@ -5191,6 +5523,74 @@ mips_elf_relocation_needs_la25_stub (bfd *input_bfd, int r_type,
> return FALSE;
> }
> }
> +
> +/* Find and/or create a hash entry for local symbol. */
> +
> +static struct mips_elf_link_hash_entry *
> +get_local_sym_hash (struct mips_elf_link_hash_table *htab,
> + bfd *abfd, const Elf_Internal_Rela *rel)
> +{
> + struct mips_elf_link_hash_entry e, *ret;
> + asection *sec;
> + hashval_t h;
> + void **slot;
> + Elf_Internal_Sym *isym;
> + Elf_Internal_Shdr *symtab_hdr;
> + char *namep;
> +
> + sec = abfd->sections;
> + h = ELF_LOCAL_SYMBOL_HASH (sec->id, ELF_R_SYM (abfd, rel->r_info));
> + isym = bfd_sym_from_r_symndx (&htab->sym_cache, abfd,
> + ELF_R_SYM (abfd, rel->r_info));
> + symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
> + namep = bfd_elf_string_from_elf_section (abfd, symtab_hdr->sh_link,
> + isym->st_name);
> +
> + e.root.indx = sec->id;
> + e.root.dynstr_index = ELF_R_SYM (abfd, rel->r_info);
> + e.root.root.root.string = namep;
> +
> + slot = htab_find_slot_with_hash (htab->loc_hash_table, &e, h, INSERT);
> + if (!slot)
> + return NULL;
> +
> + /* Found match. */
> + if (*slot)
> + {
> + ret = (struct mips_elf_link_hash_entry *) *slot;
> + return ret;
> + }
> +
> + /* Allocate new slot. */
> + ret = (struct mips_elf_link_hash_entry *)
> + objalloc_alloc ((struct objalloc *) htab->loc_hash_memory,
> + sizeof (struct mips_elf_link_hash_entry));
> +
> + if (ret)
> + {
> + memset (ret, 0, sizeof (*ret));
> + ret->root.indx = sec->id;
> + ret->root.dynstr_index = ELF_R_SYM (abfd, rel->r_info);
> + ret->root.dynindx = -1;
> + ret->root.root.root.string = namep;
> + ret->root.root.u.def.section = sec;
> + ret->root.root.u.def.value = isym->st_value;
> + ret->root.got.offset = (bfd_vma) -1;
> + ret->global_got_area = GGA_NONE;
> + ret->root.type = STT_GNU_IFUNC;
> + ret->root.def_regular = 1;
> + ret->root.ref_regular = 1;
> + ret->root.forced_local = 1;
> + ret->root.root.type = bfd_link_hash_defined;
> + ret->igot_offset = -1;
> + ret->root.other = isym->st_other;
> + ret->got_only_for_calls = TRUE;
> +
> + *slot = ret;
> + }
> +
> + return ret;
> +}
>
> /* Calculate the value produced by the RELOCATION (which comes from
> the INPUT_BFD). The ADDEND is the addend to use for this
> @@ -5241,6 +5641,8 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
> /* TRUE if the symbol referred to by this relocation is a local
> symbol. */
> bfd_boolean local_p, was_local_p;
> + /* TRUE if the symbol referred to by this relocation is a local IFUNC. */
> + bfd_boolean local_gnu_ifunc_p = FALSE;
> /* TRUE if the symbol referred to by this relocation is "_gp_disp". */
> bfd_boolean gp_disp_p = FALSE;
> /* TRUE if the symbol referred to by this relocation is
> @@ -5321,6 +5723,13 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
>
> target_is_16_bit_code_p = ELF_ST_IS_MIPS16 (sym->st_other);
> target_is_micromips_code_p = ELF_ST_IS_MICROMIPS (sym->st_other);
> +
> + if (sym->st_info == STT_GNU_IFUNC)
> + {
> + h = get_local_sym_hash (mips_elf_hash_table (info), input_bfd,
> + relocation);
> + local_gnu_ifunc_p = TRUE;
> + }
> }
> else
> {
> @@ -5545,6 +5954,21 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
> target_is_16_bit_code_p = !micromips_p;
> target_is_micromips_code_p = micromips_p;
> }
> + /* If this symbol is an ifunc, point to the iplt stub for it. */
> + else if (h
> + && h->needs_iplt
> + && (h->needs_igot
> + || (!call16_reloc_p (r_type)
> + && !got16_reloc_p (r_type))))
> + {
> + BFD_ASSERT (htab->root.iplt != NULL);
> + symbol = (htab->root.iplt->output_section->vma
> + + htab->root.iplt->output_offset
> + + h->iplt_offset);
> + /* Set ISA bit in address for compressed code. */
> + if (ELF_ST_IS_COMPRESSED (h->root.other))
> + symbol |= 1;
> + }
>
> /* Make sure MIPS16 and microMIPS are not used together. */
> if ((r_type == R_MIPS16_26 && target_is_micromips_code_p)
> @@ -5640,6 +6064,16 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
> BFD_ASSERT (h->root.needs_plt);
> g = mips_elf_gotplt_index (info, &h->root);
> }
> + /* IFUNCs use the explicitly-relocated GOT region, however we don't
> + distinguish it from the local GOT at this stage. */
> + else if (h && h->needs_ireloc && !h->needs_igot)
> + {
> + g = mips_elf_local_got_index (abfd, input_bfd, info,
> + symbol + addend, r_symndx,
> + h, r_type);
> + if (g == MINUS_ONE)
> + return bfd_reloc_outofrange;
> + }
> else
> {
> BFD_ASSERT (addend == 0);
> @@ -5931,8 +6365,11 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
> R_MIPS*_GOT16; every relocation evaluates to "G". */
> if (!htab->is_vxworks && local_p)
> {
> + /* Local IFUNC symbols must be accessed through GOT, similar to
> + global symbols, to allow for indirection. */
> value = mips_elf_got16_entry (abfd, input_bfd, info,
> - symbol + addend, !was_local_p);
> + symbol + addend,
> + !was_local_p || local_gnu_ifunc_p, h);
> if (value == MINUS_ONE)
> return bfd_reloc_outofrange;
> value
> @@ -6209,7 +6646,8 @@ mips_elf_perform_relocation (struct bfd_link_info *info,
> const Elf_Internal_Rela *relocation,
> bfd_vma value, bfd *input_bfd,
> asection *input_section, bfd_byte *contents,
> - bfd_boolean cross_mode_jump_p)
> + bfd_boolean cross_mode_jump_p,
> + bfd_boolean ifunc_p)
> {
> bfd_vma x;
> bfd_byte *location;
> @@ -6279,6 +6717,7 @@ mips_elf_perform_relocation (struct bfd_link_info *info,
> && r_type == R_MIPS_26
> && (x >> 26) == 0x3) /* jal addr */
> || (JALR_TO_BAL_P (input_bfd)
> + && !ifunc_p
> && r_type == R_MIPS_JALR
> && x == 0x0320f809) /* jalr t9 */
> || (JR_TO_B_P (input_bfd)
> @@ -6377,7 +6816,8 @@ mips_elf_create_dynamic_relocation (bfd *output_bfd,
> in the relocation. */
> if (h != NULL && ! SYMBOL_REFERENCES_LOCAL (info, &h->root))
> {
> - BFD_ASSERT (htab->is_vxworks || h->global_got_area != GGA_NONE);
> + BFD_ASSERT (htab->is_vxworks || h->global_got_area != GGA_NONE
> + || h->root.type == STT_GNU_IFUNC);
> indx = h->root.dynindx;
> if (SGI_COMPAT (output_bfd))
> defined_p = h->root.def_regular;
> @@ -6436,31 +6876,44 @@ mips_elf_create_dynamic_relocation (bfd *output_bfd,
> if (defined_p && r_type != R_MIPS_REL32)
> *addendp += symbol;
>
> - if (htab->is_vxworks)
> - /* VxWorks uses non-relative relocations for this. */
> - outrel[0].r_info = ELF32_R_INFO (indx, R_MIPS_32);
> + /* Morph REL32 in to IRELATIVE fix-up for local IFUNC reference. */
> + if (h
> + && h->root.type == STT_GNU_IFUNC
> + && SYMBOL_REFERENCES_LOCAL (info, &h->root))
> + {
> + outrel[0].r_info = ELF_R_INFO (output_bfd, 0,
> + R_MIPS_IRELATIVE);
> + outrel[1].r_info = ELF_R_INFO (output_bfd, 0,
> + R_MIPS_NONE);
> + }
> else
> - /* The relocation is always an REL32 relocation because we don't
> - know where the shared library will wind up at load-time. */
> - outrel[0].r_info = ELF_R_INFO (output_bfd, (unsigned long) indx,
> - R_MIPS_REL32);
> -
> - /* For strict adherence to the ABI specification, we should
> - generate a R_MIPS_64 relocation record by itself before the
> - _REL32/_64 record as well, such that the addend is read in as
> - a 64-bit value (REL32 is a 32-bit relocation, after all).
> - However, since none of the existing ELF64 MIPS dynamic
> - loaders seems to care, we don't waste space with these
> - artificial relocations. If this turns out to not be true,
> - mips_elf_allocate_dynamic_relocation() should be tweaked so
> - as to make room for a pair of dynamic relocations per
> - invocation if ABI_64_P, and here we should generate an
> - additional relocation record with R_MIPS_64 by itself for a
> - NULL symbol before this relocation record. */
> - outrel[1].r_info = ELF_R_INFO (output_bfd, 0,
> - ABI_64_P (output_bfd)
> - ? R_MIPS_64
> - : R_MIPS_NONE);
> + {
> + if (htab->is_vxworks)
> + /* VxWorks uses non-relative relocations for this. */
> + outrel[0].r_info = ELF32_R_INFO (indx, R_MIPS_32);
> + else
> + /* The relocation is always an REL32 relocation because we don't
> + know where the shared library will wind up at load-time. */
> + outrel[0].r_info = ELF_R_INFO (output_bfd, (unsigned long) indx,
> + R_MIPS_REL32);
> +
> + /* For strict adherence to the ABI specification, we should
> + generate a R_MIPS_64 relocation record by itself before the
> + _REL32/_64 record as well, such that the addend is read in as
> + a 64-bit value (REL32 is a 32-bit relocation, after all).
> + However, since none of the existing ELF64 MIPS dynamic
> + loaders seems to care, we don't waste space with these
> + artificial relocations. If this turns out to not be true,
> + mips_elf_allocate_dynamic_relocation() should be tweaked so
> + as to make room for a pair of dynamic relocations per
> + invocation if ABI_64_P, and here we should generate an
> + additional relocation record with R_MIPS_64 by itself for a
> + NULL symbol before this relocation record. */
> + outrel[1].r_info = ELF_R_INFO (output_bfd, 0,
> + ABI_64_P (output_bfd)
> + ? R_MIPS_64
> + : R_MIPS_NONE);
> + }
> outrel[2].r_info = ELF_R_INFO (output_bfd, 0, R_MIPS_NONE);
>
> /* Adjust the output offset of the relocation to reference the
> @@ -7007,6 +7460,8 @@ _bfd_mips_elf_section_processing (bfd *abfd, Elf_Internal_Shdr *hdr)
> hdr->sh_size += hdr->sh_addralign - adjust;
> }
> }
> + else if (strcmp (name, ".igot") == 0)
> + hdr->sh_entsize = MIPS_ELF_GOT_SIZE (abfd);
> }
>
> return TRUE;
> @@ -7920,6 +8375,13 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
> bed = get_elf_backend_data (abfd);
> rel_end = relocs + sec->reloc_count * bed->s->int_rels_per_ext_rel;
>
> + /* This needs to happen early. If the sections aren't needed
> + they will not get generated. */
> + if (htab->root.dynobj == NULL)
> + htab->root.dynobj = abfd;
> + if (!htab->root.irelplt && !mips_elf_create_ifunc_sections (info))
> + return FALSE;
> +
> /* Check for the mips16 stub sections. */
>
> name = bfd_get_section_name (abfd, sec);
> @@ -8178,6 +8640,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
> unsigned long r_symndx;
> unsigned int r_type;
> struct elf_link_hash_entry *h;
> + struct mips_elf_link_hash_entry *ih = NULL;
> bfd_boolean can_make_dynamic_p;
> bfd_boolean call_reloc_p;
> bfd_boolean constrain_symbol_p;
> @@ -8186,7 +8649,23 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
> r_type = ELF_R_TYPE (abfd, rel->r_info);
>
> if (r_symndx < extsymoff)
> - h = NULL;
> + {
> + Elf_Internal_Sym *isym;
> + isym = bfd_sym_from_r_symndx (&htab->sym_cache, abfd, r_symndx);
> +
> + if (isym == NULL)
> + return FALSE;
> +
> + /* Relocation is for local STT_GNU_IFUNC symbol. */
> + if (isym->st_info == STT_GNU_IFUNC)
> + {
> + /* Ensure that we have a hash entry for this symbol. */
> + if ((ih = get_local_sym_hash (htab, abfd, rel)) == NULL)
> + return FALSE;
> + }
> +
> + h = NULL;
> + }
> else if (r_symndx >= extsymoff + NUM_SHDR_ENTRIES (symtab_hdr))
> {
> (*_bfd_error_handler)
> @@ -8370,6 +8849,13 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
> info->flags |= DF_TEXTREL;
> }
> }
> + else if (ih)
> + {
> + if (!bfd_link_pic (info) && !can_make_dynamic_p)
> + ih->has_static_relocs = 1;
> + if (!call_reloc_p)
> + ih->root.pointer_equality_needed = 1;
> + }
> else if (call_lo16_reloc_p (r_type)
> || got_lo16_reloc_p (r_type)
> || got_disp_reloc_p (r_type)
> @@ -8384,7 +8870,8 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
> R_MIPS_CALL_HI16 because these are always followed by an
> R_MIPS_GOT_LO16 or R_MIPS_CALL_LO16. */
> if (!mips_elf_record_local_got_symbol (abfd, r_symndx,
> - rel->r_addend, info, r_type))
> + rel->r_addend, info,
> + r_type, NULL))
> return FALSE;
> }
>
> @@ -8398,7 +8885,8 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
> case R_MIPS_CALL16:
> case R_MIPS16_CALL16:
> case R_MICROMIPS_CALL16:
> - if (h == NULL)
> + /* Exclude local IFUNCs from check. */
> + if (h == NULL && ih == NULL)
> {
> (*_bfd_error_handler)
> (_("%B: CALL16 reloc at 0x%lx not against global symbol"),
> @@ -8406,6 +8894,10 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
> bfd_set_error (bfd_error_bad_value);
> return FALSE;
> }
> + if (h && h->type == STT_GNU_IFUNC)
> + ((struct mips_elf_link_hash_entry *)h)->has_got_relocs = TRUE;
> + else if (ih)
> + ih->has_got_relocs = TRUE;
> /* Fall through. */
>
> case R_MIPS_CALL_HI16:
> @@ -8423,10 +8915,17 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
>
> /* We need a stub, not a plt entry for the undefined
> function. But we record it as if it needs plt. See
> - _bfd_elf_adjust_dynamic_symbol. */
> + _bfd_elf_adjust_dynamic_symbol. If it is an ifunc
> + symbol it will go into an iplt section and not plt. */
> h->needs_plt = 1;
> - h->type = STT_FUNC;
> + if (h->type != STT_GNU_IFUNC)
> + h->type = STT_FUNC;
> }
> + else
> + if (ih &&
> + !mips_elf_record_local_got_symbol (abfd, -1, rel->r_addend,
> + info, r_type, ih))
> + return FALSE;
> break;
>
> case R_MIPS_GOT_PAGE:
> @@ -8459,9 +8958,18 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
> }
> else
> addend = rel->r_addend;
> - if (!mips_elf_record_got_page_ref (info, abfd, r_symndx,
> - h, addend))
> + if (ih &&
> + !mips_elf_record_local_got_symbol (abfd, -1, rel->r_addend,
> + info, r_type, ih))
> return FALSE;
> + else if (!mips_elf_record_got_page_ref (info, abfd, r_symndx,
> + h, addend))
> + return FALSE;
> +
> + if (h && h->type == STT_GNU_IFUNC)
> + ((struct mips_elf_link_hash_entry *)h)->has_got_relocs = TRUE;
> + else if (ih)
> + ih->has_got_relocs = TRUE;
>
> if (h)
> {
> @@ -8518,7 +9026,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
> {
> if (!mips_elf_record_local_got_symbol (abfd, r_symndx,
> rel->r_addend,
> - info, r_type))
> + info, r_type, NULL))
> return FALSE;
> }
> break;
> @@ -8964,6 +9472,7 @@ _bfd_mips_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
> /* Make sure we know what is going on here. */
> BFD_ASSERT (dynobj != NULL
> && (h->needs_plt
> + || h->type == STT_GNU_IFUNC
> || h->u.weakdef != NULL
> || (h->def_dynamic
> && h->ref_regular
> @@ -9242,6 +9751,10 @@ _bfd_mips_elf_always_size_sections (bfd *output_bfd,
> hti.error = FALSE;
> mips_elf_link_hash_traverse (mips_elf_hash_table (info),
> mips_elf_check_symbols, &hti);
> +
> + /* Allocate relocs for local IFUNC symbols. */
> + htab_traverse (htab->loc_hash_table, mips_elf_check_ifunc_symbols, &hti);
> +
> if (hti.error)
> return FALSE;
>
> @@ -9289,6 +9802,10 @@ mips_elf_lay_out_got (bfd *output_bfd, struct bfd_link_info *info)
> if (!mips_elf_resolve_final_got_entries (info, g))
> return FALSE;
>
> + g->assigned_general_gotno = htab->reserved_gotno;
> + g->local_gotno += g->general_gotno;
> + g->assigned_low_gotno += g->general_gotno;
> +
> /* Calculate the total loadable size of the output. That
> will give us the maximum number of GOT_PAGE entries
> required. */
> @@ -9730,6 +10247,8 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd,
> else if (! CONST_STRNEQ (name, ".init")
> && s != htab->sgot
> && s != htab->sgotplt
> + && s != htab->root.iplt
> + && s != htab->root.igotplt
> && s != htab->sstubs
> && s != htab->sdynbss)
> {
> @@ -9875,6 +10394,13 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd,
> if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_PLTGOT, 0))
> return FALSE;
> }
> +
> + if (elf_tdata (output_bfd)->has_gnu_symbols & elf_gnu_symbol_ifunc)
> + {
> + if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_GENERAL_GOTNO, 0))
> + return FALSE;
> + }
> +
> if (htab->is_vxworks
> && !elf_vxworks_add_dynamic_entries (output_bfd, info))
> return FALSE;
> @@ -10006,6 +10532,8 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
> Elf_Internal_Shdr *symtab_hdr;
> struct elf_link_hash_entry *h;
> bfd_boolean rel_reloc;
> + bfd_boolean local_gnu_ifunc_p = FALSE;
> + bfd_boolean gnu_ifunc_p = FALSE;
>
> rel_reloc = (NEWABI_P (input_bfd)
> && mips_elf_rel_relocation_p (input_bfd, input_section,
> @@ -10019,6 +10547,23 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
> {
> sec = local_sections[r_symndx];
> h = NULL;
> +
> + Elf_Internal_Sym *isym;
> + struct mips_elf_link_hash_table *htab;
> + htab = mips_elf_hash_table (info);
> + isym = bfd_sym_from_r_symndx (&htab->sym_cache, input_bfd, r_symndx);
> +
> + if (isym == NULL)
> + return FALSE;
> +
> + /* Relocation is for local STT_GNU_IFUNC symbol. */
> + if (isym->st_info == STT_GNU_IFUNC)
> + {
> + /* Ensure that we have a hash entry for this symbol. */
> + if (get_local_sym_hash (htab, input_bfd, rel) == NULL)
> + return FALSE;
> + local_gnu_ifunc_p = TRUE;
> + }
> }
> else
> {
> @@ -10046,6 +10591,8 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
> continue;
> }
>
> + gnu_ifunc_p = (local_gnu_ifunc_p || (h && h->type == STT_GNU_IFUNC));
> +
> if (r_type == R_MIPS_64 && ! NEWABI_P (input_bfd))
> {
> /* Some 32-bit code uses R_MIPS_64. In particular, people use
> @@ -10077,7 +10624,8 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
> if (hi16_reloc_p (r_type)
> || (got16_reloc_p (r_type)
> && mips_elf_local_relocation_p (input_bfd, rel,
> - local_sections)))
> + local_sections)
> + && !local_gnu_ifunc_p))
> {
> if (!mips_elf_add_lo16_rel_addend (input_bfd, rel, relend,
> contents, &addend))
> @@ -10168,7 +10716,7 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
>
> if (! mips_elf_perform_relocation (info, howto, rel, addend,
> input_bfd, input_section,
> - contents, FALSE))
> + contents, FALSE, gnu_ifunc_p))
> return FALSE;
> }
>
> @@ -10322,7 +10870,8 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
> /* Actually perform the relocation. */
> if (! mips_elf_perform_relocation (info, howto, rel, value,
> input_bfd, input_section,
> - contents, cross_mode_jump_p))
> + contents, cross_mode_jump_p,
> + gnu_ifunc_p))
> return FALSE;
> }
>
> @@ -10467,6 +11016,242 @@ mips_elf_irix6_finish_dynamic_symbol (bfd *abfd ATTRIBUTE_UNUSED,
> }
> }
>
> +/* Create the contents of the iplt entry for an IFUNC symbol. */
> +
> +static bfd_boolean
> +mips_elf_create_iplt (bfd *output_bfd,
> + struct mips_elf_link_hash_table *htab,
> + struct mips_elf_link_hash_entry *hmips,
> + bfd_vma igotplt_address)
> +{
> + bfd_byte *loc;
> + const bfd_vma *iplt_entry;
> + bfd_vma high = mips_elf_high (igotplt_address);
> + bfd_vma low = igotplt_address & 0xffff;
> +
> + /* Find out where the .iplt entry should go. */
> + if (!htab->root.iplt->contents)
> + {
> + htab->root.iplt->contents = bfd_zalloc (output_bfd,
> + htab->root.iplt->size);
> + if (!htab->root.iplt->contents)
> + return FALSE;
> + }
> + loc = htab->root.iplt->contents + hmips->iplt_offset;
> +
> + /* Fill in the IPLT entry itself. */
> + if (ABI_64_P (output_bfd))
> + {
> + bfd_vma highest = mips_elf_highest (igotplt_address);
> + bfd_vma higher = mips_elf_higher (igotplt_address);
> + iplt_entry = mips64_exec_iplt_entry;
> +
> + if (highest)
> + {
> + /* Full 64-bit address space. */
> + bfd_put_32 (output_bfd, iplt_entry[0] | highest, loc);
> + bfd_put_32 (output_bfd, iplt_entry[1] | high, loc + 4);
> + bfd_put_32 (output_bfd, iplt_entry[2] | higher, loc + 8);
> + bfd_put_32 (output_bfd, iplt_entry[3], loc + 12);
> + bfd_put_32 (output_bfd, iplt_entry[4], loc + 16);
> + bfd_put_32 (output_bfd, iplt_entry[5] | low, loc + 20);
> + bfd_put_32 (output_bfd, iplt_entry[6], loc + 24);
> + bfd_put_32 (output_bfd, iplt_entry[7], loc + 28);
> + }
> + else if (higher)
> + {
> + /* 48-bit address space. */
> + iplt_entry = mips64_48b_exec_iplt_entry;
> + bfd_put_32 (output_bfd, iplt_entry[0] | higher, loc);
> + bfd_put_32 (output_bfd, iplt_entry[1] | high, loc + 4);
> + bfd_put_32 (output_bfd, iplt_entry[2], loc + 8);
> + bfd_put_32 (output_bfd, iplt_entry[3] | low, loc + 12);
> + bfd_put_32 (output_bfd, iplt_entry[4], loc + 16);
> + bfd_put_32 (output_bfd, iplt_entry[5], loc + 20);
> + }
> + else
> + {
> + /* 32-bit address space. */
> + bfd_put_32 (output_bfd, iplt_entry[0] | high, loc);
> + bfd_put_32 (output_bfd, iplt_entry[5] | low, loc + 4);
> + bfd_put_32 (output_bfd, iplt_entry[6], loc + 8);
> + bfd_put_32 (output_bfd, iplt_entry[7], loc + 12);
> + }
> + }
> + else if (ELF_ST_IS_MIPS16 (hmips->root.other))
> + {
> + iplt_entry = mips16_exec_iplt_entry;
> + bfd_put_16 (output_bfd, iplt_entry[0], loc);
> + bfd_put_16 (output_bfd, iplt_entry[1], loc + 2);
> + bfd_put_16 (output_bfd, iplt_entry[2], loc + 4);
> + bfd_put_16 (output_bfd, iplt_entry[3], loc + 6);
> + bfd_put_32 (output_bfd, igotplt_address, loc + 8);
> + }
> + else if (ELF_ST_IS_MICROMIPS (hmips->root.other))
> + {
> + iplt_entry = micromips32_exec_iplt_entry;
> + bfd_put_micromips_32 (output_bfd, iplt_entry[0] | high, loc);
> + bfd_put_micromips_32 (output_bfd, iplt_entry[1] | low , loc + 4);
> +
> + bfd_put_16 (output_bfd, iplt_entry[2], loc + 8);
> + bfd_put_16 (output_bfd, iplt_entry[3], loc + 10);
> + }
> + else
> + {
> + if (MIPSR6_P (output_bfd))
> + iplt_entry = mips32r6_exec_iplt_entry;
> + else
> + iplt_entry = mips32_exec_iplt_entry;
> + bfd_put_32 (output_bfd, iplt_entry[0] | high, loc);
> + bfd_put_32 (output_bfd, iplt_entry[1] | low, loc + 4);
> + if (LOAD_INTERLOCKS_P (output_bfd))
> + {
> + bfd_put_32 (output_bfd, iplt_entry[2], loc + 8);
> + bfd_put_32 (output_bfd, iplt_entry[3], loc + 12);
> + }
> + else
> + {
> + bfd_put_32 (output_bfd, iplt_entry[3], loc + 8);
> + bfd_put_32 (output_bfd, iplt_entry[2], loc + 12);
> + bfd_put_32 (output_bfd, iplt_entry[3], loc + 16);
> + }
> + }
> +
> + return TRUE;
> +}
> +
> +/* Find local GOT index for VALUE. Return -1 if no GOT slot is found. */
> +
> +static bfd_vma
> +mips_elf_check_local_got_index (bfd *abfd, struct bfd_link_info *info,
> + struct mips_elf_link_hash_entry *h)
> +{
> + struct mips_got_entry lookup, *entry;
> + void **loc;
> + struct mips_got_info *g;
> + struct mips_elf_link_hash_table *htab;
> +
> + htab = mips_elf_hash_table (info);
> + BFD_ASSERT (htab != NULL);
> +
> + g = mips_elf_bfd_got (abfd, FALSE);
> +
> + /* Check for existing local GOT entry. */
> + if (g != NULL)
> + {
> + lookup.abfd = abfd;
> + lookup.symndx = -1;
> + lookup.d.h = h;
> + lookup.tls_type = GOT_TLS_NONE;
> + loc = htab_find_slot (g->got_entries, &lookup, NO_INSERT);
> + }
> + else
> + return -1;
> +
> + if (loc && *loc)
> + {
> + entry = (struct mips_got_entry *) *loc;
> + return entry->gotidx;
> + }
> + else
> + return -1;
> +}
> +
> +/* Create the IRELATIVE relocation for an IFUNC symbol. */
> +
> +static bfd_boolean
> +mips_elf_create_ireloc (bfd *output_bfd,
> + bfd *dynobj,
> + struct mips_elf_link_hash_table *htab,
> + struct mips_elf_link_hash_entry *hmips,
> + Elf_Internal_Sym *sym,
> + struct bfd_link_info *info)
> +{
> + bfd_vma igotplt_address = 0;
> + int igot_offset = -1;
> + asection *gotsect, *relsect;
> + bfd_vma value = sym->st_value;
> +
> + if (MIPS16_P (output_bfd) || MICROMIPS_P (output_bfd))
> + value |= 1;
> +
> + if (!hmips->needs_igot)
> + {
> + gotsect = htab->sgot;
> + /* Check if IFUNC symbol already has an assigned GOT slot. */
> + igot_offset = mips_elf_check_local_got_index (output_bfd, info, hmips);
> + }
> + else
> + {
> + bfd_byte *loc;
> + bfd_vma igot_index;
> + gotsect = htab->root.igotplt;
> + igot_offset = hmips->igot_offset;
> +
> + /* Calculate the address of the IGOT entry. */
> + igot_index = igot_offset / MIPS_ELF_GOT_SIZE (dynobj);
> +
> + if (!gotsect->contents)
> + {
> + gotsect->contents = bfd_zalloc (output_bfd, gotsect->size);
> + if (!gotsect->contents)
> + return FALSE;
> + }
> +
> + /* Initially point the .igot entry at the IFUNC resolver routine. */
> + loc = ((bfd_byte *) gotsect->contents
> + + igot_index * MIPS_ELF_GOT_SIZE (dynobj));
> +
> + if (ABI_64_P (output_bfd))
> + bfd_put_64 (output_bfd, value, loc);
> + else
> + bfd_put_32 (output_bfd, value, loc);
> + }
> +
> + igotplt_address = (gotsect->output_section->vma + gotsect->output_offset
> + + igot_offset);
> +
> + relsect = mips_get_irel_section (info, htab);
> +
> + if (igot_offset >= 0)
> + {
> + if (hmips->needs_iplt && relsect->contents == NULL)
> + {
> + /* Allocate memory for the relocation section contents. */
> + relsect->contents = bfd_zalloc (dynobj, relsect->size);
> + if (relsect->contents == NULL)
> + return FALSE;
> + }
> +
> + if (hmips->needs_iplt || SYMBOL_REFERENCES_LOCAL (info, &hmips->root))
> + /* Emit an IRELATIVE relocation against the [I]GOT entry. */
> + mips_elf_output_dynamic_relocation (output_bfd, relsect,
> + relsect->reloc_count++, 0,
> + R_MIPS_IRELATIVE, igotplt_address);
> + else
> + {
> + /* Emit an R_MIPS_REL32 relocation against global GOT entry for
> + a preemptible symbol. */
> + asection *sec = hmips->root.root.u.def.section;
> + Elf_Internal_Rela rel[3];
> +
> + memset (rel, 0, sizeof (rel));
> + rel[0].r_info = ELF_R_INFO (output_bfd, 0, R_MIPS_REL32);
> + rel[0].r_offset = rel[1].r_offset = rel[2].r_offset = igot_offset;
> +
> + mips_elf_create_dynamic_relocation (output_bfd, info, rel, hmips,
> + sec, value, NULL,
> + gotsect);
> + }
> + }
> + /* If necessary, generate the corresponding .iplt entry. */
> + if (hmips->needs_iplt
> + && !mips_elf_create_iplt (output_bfd, htab, hmips, igotplt_address))
> + return FALSE;
> +
> + return TRUE;
> +}
> +
> /* Finish up dynamic symbol handling. We set the contents of various
> dynamic sections here. */
>
> @@ -10794,6 +11579,19 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd,
> sym->st_other = other;
> }
>
> + if (hmips->root.type == STT_GNU_IFUNC)
> + {
> + if (hmips->needs_ireloc
> + && !mips_elf_create_ireloc (output_bfd, dynobj, htab,
> + hmips, sym, info))
> + return FALSE;
> +
> + if (!elf_hash_table (info)->dynamic_sections_created)
> + return TRUE;
> + if (h->dynindx == -1 && !h->forced_local)
> + return TRUE;
> + }
> +
> /* If we have a MIPS16 function with a stub, the dynamic symbol must
> refer to the stub, since only the stub uses the standard calling
> conventions. */
> @@ -10957,9 +11755,41 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd,
> sym->st_other -= STO_MICROMIPS;
> }
>
> + if (hmips->needs_iplt)
> + {
> + /* Point at the iplt stub for this ifunc symbol. */
> + sym->st_value = (htab->root.iplt->output_section->vma
> + + htab->root.iplt->output_offset
> + + hmips->iplt_offset);
> + sym->st_info = ELF_ST_INFO (STB_GLOBAL, STT_FUNC);
> + if (ELF_ST_IS_COMPRESSED (hmips->root.other))
> + sym->st_value |= 1;
> + }
> +
> return TRUE;
> }
>
> +/* Finish up local dynamic symbol handling. We set the contents of
> + various dynamic sections here. */
> +
> +static bfd_boolean
> +_bfd_mips_elf_finish_local_dynamic_symbol (void **slot, void *inf)
> +{
> + struct mips_elf_link_hash_entry *h = (struct mips_elf_link_hash_entry *) *slot;
> + struct bfd_link_info *info = (struct bfd_link_info *)inf;
> + Elf_Internal_Sym isym;
> +
> + isym.st_value = (h->root.root.u.def.section->output_section->vma
> + + h->root.root.u.def.section->output_offset
> + + h->root.root.u.def.value);
> + isym.st_other = h->root.other;
> + if (ELF_ST_IS_COMPRESSED (isym.st_other))
> + isym.st_value |= 1;
> +
> + return _bfd_mips_elf_finish_dynamic_symbol (info->output_bfd, info,
> + &h->root, &isym);
> +}
> +
> /* Likewise, for VxWorks. */
>
> bfd_boolean
> @@ -11345,6 +12175,13 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd,
> sgot = htab->sgot;
> gg = htab->got_info;
>
> + if (htab_elements (htab->loc_hash_table) > 0)
> + {
> + /* Fill PLT and GOT entries for local STT_GNU_IFUNC symbols. */
> + htab_traverse (htab->loc_hash_table,
> + _bfd_mips_elf_finish_local_dynamic_symbol, info);
> + }
> +
> if (elf_hash_table (info)->dynamic_sections_created)
> {
> bfd_byte *b;
> @@ -11515,6 +12352,10 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd,
> dyn.d_un.d_ptr = s->vma;
> break;
>
> + case DT_MIPS_GENERAL_GOTNO:
> + dyn.d_un.d_val = htab->reserved_gotno + gg->general_gotno;
> + break;
> +
> case DT_RELASZ:
> BFD_ASSERT (htab->is_vxworks);
> /* The count does not include the JUMP_SLOT relocations. */
> @@ -13849,6 +14690,48 @@ _bfd_mips_elf_relax_section (bfd *abfd, asection *sec,
> return FALSE;
> }
>
> +/* Compute a hash of a local hash entry. We use elf_link_hash_entry
> + for local symbol so that we can handle local STT_GNU_IFUNC symbols
> + as global symbol. We reuse indx and dynstr_index for local symbol
> + hash since they aren't used by global symbols in this backend. */
> +
> +static hashval_t
> +local_htab_hash (const void *ptr)
> +{
> + struct mips_elf_link_hash_entry *h =
> + (struct mips_elf_link_hash_entry *) ptr;
> + return ELF_LOCAL_SYMBOL_HASH (h->root.indx, h->root.dynstr_index);
> +}
> +
> +/* Compare local hash entries. */
> +
> +static int
> +local_htab_eq (const void *ptr1, const void *ptr2)
> +{
> + struct mips_elf_link_hash_entry *h1 =
> + (struct mips_elf_link_hash_entry *) ptr1;
> + struct mips_elf_link_hash_entry *h2 =
> + (struct mips_elf_link_hash_entry *) ptr2;
> +
> + return (h1->root.indx == h2->root.indx &&
> + h1->root.dynstr_index == h2->root.dynstr_index);
> +}
> +
> +/* Destroy a MIPS ELF linker hash table. */
> +
> +static void
> +_bfd_mips_elf_link_hash_table_free (bfd *obfd)
> +{
> + struct mips_elf_link_hash_table *htab;
> +
> + htab = (struct mips_elf_link_hash_table *) obfd->link.hash;
> + if (htab->loc_hash_table)
> + htab_delete (htab->loc_hash_table);
> + if (htab->loc_hash_memory)
> + objalloc_free ((struct objalloc *) htab->loc_hash_memory);
> + _bfd_elf_link_hash_table_free (obfd);
> +}
> +
> /* Create a MIPS ELF linker hash table. */
>
> struct bfd_link_hash_table *
> @@ -13872,6 +14755,16 @@ _bfd_mips_elf_link_hash_table_create (bfd *abfd)
> ret->root.init_plt_refcount.plist = NULL;
> ret->root.init_plt_offset.plist = NULL;
>
> + /* Create hash table for local IFUNC symbols. */
> + ret->loc_hash_table = htab_try_create (1024, local_htab_hash,
> + local_htab_eq, NULL);
> + ret->loc_hash_memory = objalloc_create ();
> + if (!ret->loc_hash_table || !ret->loc_hash_memory)
> + {
> + _bfd_mips_elf_link_hash_table_free (abfd);
> + return NULL;
> + }
> +
> return &ret->root.root;
> }
>
> @@ -15509,7 +16402,9 @@ _bfd_mips_elf_get_target_dtag (bfd_vma dtag)
> return "DT_MIPS_PLTGOT";
> case DT_MIPS_RWPLT:
> return "DT_MIPS_RWPLT";
> - }
> + case DT_MIPS_GENERAL_GOTNO:
> + return "DT_MIPS_GENERAL_GOTNO";
> + }
> }
>
> /* Return the meaning of Tag_GNU_MIPS_ABI_FP value FP, or null if
> @@ -16123,7 +17018,7 @@ _bfd_mips_elf_get_synthetic_symtab (bfd *abfd,
> void
> _bfd_mips_post_process_headers (bfd *abfd, struct bfd_link_info *link_info)
> {
> - struct mips_elf_link_hash_table *htab;
> + struct mips_elf_link_hash_table *htab = NULL;
> Elf_Internal_Ehdr *i_ehdrp;
>
> i_ehdrp = elf_elfheader (abfd);
> @@ -16141,6 +17036,9 @@ _bfd_mips_post_process_headers (bfd *abfd, struct bfd_link_info *link_info)
> if (mips_elf_tdata (abfd)->abiflags.fp_abi == Val_GNU_MIPS_ABI_FP_64
> || mips_elf_tdata (abfd)->abiflags.fp_abi == Val_GNU_MIPS_ABI_FP_64A)
> i_ehdrp->e_ident[EI_ABIVERSION] = 3;
> +
> + if (elf_tdata (abfd)->has_gnu_symbols & elf_gnu_symbol_ifunc)
> + i_ehdrp->e_ident[EI_ABIVERSION] = 4;
> }
>
> int
> diff --git a/bfd/reloc.c b/bfd/reloc.c
> index 3ef704f..496c465 100644
> --- a/bfd/reloc.c
> +++ b/bfd/reloc.c
> @@ -2450,6 +2450,11 @@ ENUMDOC
> COMMENT
>
> ENUM
> + BFD_RELOC_MIPS_IRELATIVE
> +ENUMDOC
> + MIPS support for STT_GNU_IFUNC.
> +
Missing COMMENT here.
> diff --git a/binutils/readelf.c b/binutils/readelf.c
> index 47ac1ad..0091539 100644
> --- a/binutils/readelf.c
> +++ b/binutils/readelf.c
> @@ -1729,6 +1729,8 @@ get_mips_dynamic_type (unsigned long type)
> case DT_MIPS_AUX_DYNAMIC: return "MIPS_AUX_DYNAMIC";
> case DT_MIPS_PLTGOT: return "MIPS_PLTGOT";
> case DT_MIPS_RWPLT: return "MIPS_RWPLT";
> + case DT_MIPS_GENERAL_GOTNO: return "MIPS_GENERAL_GOTNO";
> +
> default:
> return NULL;
> }
Please don't add this extra newline here.
> @@ -8612,6 +8614,7 @@ dynamic_section_mips_val (Elf_Internal_Dyn * entry)
> case DT_MIPS_DELTA_SYM_NO:
> case DT_MIPS_DELTA_CLASSSYM_NO:
> case DT_MIPS_COMPACT_SIZE:
> + case DT_MIPS_GENERAL_GOTNO:
> print_vma (entry->d_un.d_ptr, DEC);
> break;
Hmm, I wonder why this is using `d_ptr' rather than `d_val' as the value
associated with this tag is clearly not an address, but likewise does
DT_MIPS_LOCAL_GOTNO and a number of other tags which the MIPS psABI
specifies as using `d_val', so let's defer it to a follow-up cleanup.
Maciej