[PATCH 1/2] [RFC] Add IFUNC support for MIPS (v4)
Faraz Shahbazker
faraz.shahbazker@imgtec.com
Thu Jan 21 20:02:00 GMT 2016
Bump.
On 01/07/2016 04:10 PM, Faraz Shahbazker wrote:
> Hi,
>
> 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.
> * elf32-mips.c
> (elf_mips_eh_howto table): Howto for BFD_RELOC_MIPS_IRELATIVE.
> (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.
> (mips_elf_create_ifunc_sections): New function.
> (get_local_sym_hash): Likewise.
> (mips_elf_calculate_relocation): Create hash-table for local IFUNC
> 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
> IFUNC symbols in explicit GOT region to have dynamic relocations.
> (_bfd_mips_elf_section_processing): Size .igot section.
> (_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,
>
> 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)
> +
> /* Nonzero if ABFD is MIPS R6. */
> #define MIPSR6_P(abfd) \
> ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32R6 \
> @@ -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) */
> +};
> +
> +/* The format of 32 bit IPLT entries. */
> +static const bfd_vma mips32_exec_iplt_entry[] =
> +{
> + 0x3c0f0000, /* lui $15, %hi(.igot address) */
> + 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 */
> +};
> +
> +/* 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) */
> + 0x45b9, /* jrc $25 */
> +};
> +
> +/* 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;
>
> /* Create a new symbol. */
> @@ -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;
> +
> 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);
> + 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/libbfd.h b/bfd/libbfd.h
> index d7183d3..aa9b0c7 100644
> --- a/bfd/libbfd.h
> +++ b/bfd/libbfd.h
> @@ -1199,6 +1199,7 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
> "BFD_RELOC_MIPS_COPY",
> "BFD_RELOC_MIPS_JUMP_SLOT",
>
> + "BFD_RELOC_MIPS_IRELATIVE",
> "BFD_RELOC_MOXIE_10_PCREL",
>
> "BFD_RELOC_FT32_10",
> 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.
> +
> +ENUM
> BFD_RELOC_MOXIE_10_PCREL
> ENUMDOC
> Moxie ELF relocations.
> 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;
> }
> @@ -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;
>
> diff --git a/elfcpp/elfcpp.h b/elfcpp/elfcpp.h
> index e0eae42..c77b86e 100644
> --- a/elfcpp/elfcpp.h
> +++ b/elfcpp/elfcpp.h
> @@ -866,6 +866,8 @@ enum DT
> DT_MIPS_PLTGOT = 0x70000032,
> // Points to the base of a writable PLT.
> DT_MIPS_RWPLT = 0x70000034,
> + // The GOT index of the first implicitly relocated GOT entry.
> + DT_MIPS_GENERAL_GOTNO = 0x70000036,
>
> DT_AUXILIARY = 0x7ffffffd,
> DT_USED = 0x7ffffffe,
> diff --git a/include/elf/mips.h b/include/elf/mips.h
> index 46f63fe..0034884 100644
> --- a/include/elf/mips.h
> +++ b/include/elf/mips.h
> @@ -115,6 +115,9 @@ START_RELOC_NUMBERS (elf_mips_reloc_type)
> RELOC_NUMBER (R_MIPS_COPY, 126)
> RELOC_NUMBER (R_MIPS_JUMP_SLOT, 127)
>
> + /* STT_GNU_IFUNC support. */
> + RELOC_NUMBER (R_MIPS_IRELATIVE, 128)
> +
> /* These relocations are specific to microMIPS. */
> FAKE_RELOC (R_MICROMIPS_min, 130)
> RELOC_NUMBER (R_MICROMIPS_26_S1, 133)
> @@ -751,6 +754,10 @@ extern void bfd_mips_elf32_swap_reginfo_out
>
> /* Relative offset of run time loader map, used for debugging. */
> #define DT_MIPS_RLD_MAP_REL 0x70000035
> +
> +/* The GOT index of the first implicitly relocated GOT entry. */
> +#define DT_MIPS_GENERAL_GOTNO 0x70000036
> +
>
> /* Flags which may appear in a DT_MIPS_FLAGS entry. */
>
>
More information about the Binutils
mailing list