[RFC] Add IFUNC support for MIPS
Faraz Shahbazker
Faraz.Shahbazker@imgtec.com
Tue Jul 21 12:08:00 GMT 2015
Hi,
I have revised the patch with more suggestions from the previous discussions.
Detailed description is in the attached text file. I think I have got a better handle on
symbol preemption with this version, but I would really appreciate some input on that
aspect.
Previous discussions for reference:
https://www.sourceware.org/ml/binutils/2013-08/msg00121.html
https://www.sourceware.org/ml/binutils/2013-11/msg00155.html
https://sourceware.org/ml/binutils/2013-12/msg00058.html
Tests have been split out in to a separate patch and attached for review.
These are carried forward from the previous iteration. I am working on translating
more of my informal tests to the correct format.
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 and flags to indicate symbol needs IPLT or IRELOC.
(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 and ST_MIPS16
type for mips16 IPLT stubs.
(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_local_symbols): Likewise.
(mips_elf_check_symbols): Allocate an IPLT entry and/or dynamic
relocation for IFUNC symbols.
(mips_elf_count_got_entry): Count local IFUNCs under general
GOT entries instead of local.
(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): Add check for sufficient general
GOT entries. Assign local IFUNCs from general GOT entries pool.
(mips_elf_count_general_got_symbols): New function.
(mips_elf_deallocate_irelocs): Likewise.
(mips_elf_multi_got): Traverse primary GOT entries and deallocate
previously allocated IRELOCs for symbols marked RELOC_ONLY.
(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.
(mips_elf_create_dynamic_relocation): Emit IRELATIVE relocation
instead of REL32 for local IFUNC reference.
(_bfd_mips_elf_section_processing): Size .igot section.
(_bfd_mips_elf_add_symbol_hook): Mark output as has_gnu_symbols.
(_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.
(_bfd_mips_elf_adjust_dynamic_symbol): Add STT_GNU_IFUNC to sanity
check.
(_bfd_mips_elf_always_size_sections): Allocate relocs for local IFUNCs.
(mips_elf_lay_out_got): Count general GOT entries. Offset local entries
to follow general 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.
(mips_elf_create_iplt): New function.
(misp_elf_check_local_got_index): Likewise.
(mips_elf_create_ireloc): Likewise.
(_bfd_mips_elf_finish_dynamic_symbol): Create IPLT stub/IREL
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.
* 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 | 991 ++++++++++++++++++++++++++++++++++++++++++++++++----
bfd/libbfd.h | 1 +
bfd/reloc.c | 5 +
binutils/readelf.c | 3 +
elfcpp/elfcpp.h | 2 +
include/elf/mips.h | 7 +
8 files changed, 964 insertions(+), 70 deletions(-)
diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index c27db8d..7eec1b5 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -3038,6 +3038,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 fff08e5..7f9c9b4 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 9932453..521fcd6 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. */
+ bfd_vma igot_offset;
+
/* The highest GGA_* value that satisfies all references to this symbol. */
unsigned int global_got_area : 2;
@@ -413,6 +424,12 @@ 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 ifunc symbol need an IGOT entry? */
+ unsigned int needs_ireloc : 1;
};
/* MIPS ELF linker hash table. */
@@ -485,6 +502,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 +536,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 +824,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 \
@@ -1191,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, /* ld $2, %lo(.igot address)($2) */
+ 0x4599, /* jr $25 */
+ 0x0c00, /* nop */
+};
+
+/* 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 */
+};
+
+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. */
@@ -1289,6 +1380,10 @@ mips_elf_link_hash_newfunc (struct bfd_hash_entry *entry,
ret->has_nonpic_branches = FALSE;
ret->needs_lazy_stub = FALSE;
ret->use_plt_entry = FALSE;
+ ret->needs_iplt = FALSE;
+ ret->needs_ireloc = FALSE;
+ ret->iplt_offset = -1;
+ ret->igot_offset = 0;
}
return (struct bfd_hash_entry *) ret;
@@ -1588,8 +1683,9 @@ mips_elf_create_stub_symbol (struct bfd_link_info *info,
struct elf_link_hash_entry *elfh;
const char *name;
- if (ELF_ST_IS_MICROMIPS (h->root.other))
- value |= 1;
+ 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. */
name = ACONCAT ((prefix, h->root.root.root.string, NULL));
@@ -1604,6 +1700,10 @@ 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;
+
+ if (ELF_ST_IS_MIPS16 (h->root.other) && h->needs_iplt)
+ elfh->other = STO_MIPS16;
+
return TRUE;
}
@@ -1966,6 +2066,137 @@ 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 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 section for R_MIPS_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 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;
+
+ if (mh->needs_iplt || mh->global_got_area == GGA_NORMAL
+ || mh->root.forced_local)
+ {
+ 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;
+
+ BFD_ASSERT (!mh->needs_iplt);
+ BFD_ASSERT (mhtab->root.iplt != NULL);
+
+ s = mhtab->root.iplt;
+
+ mh->iplt_offset = s->size;
+ mips_elf_create_stub_symbol (info, mh, ".iplt.", mhtab->root.iplt,
+ s->size, mhtab->iplt_entry_size);
+ s->size += mhtab->iplt_entry_size;
+
+ BFD_ASSERT (mhtab->root.igotplt != NULL);
+ mh->igot_offset = mhtab->root.igotplt->size;
+ mhtab->root.igotplt->size += MIPS_ELF_GOT_SIZE (abfd);
+ mh->needs_iplt = TRUE;
+
+ 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_local_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 the referenced symbol is ifunc, allocate an iplt for it. */
+ if (h && !h->needs_iplt && h->root.type == STT_GNU_IFUNC)
+ {
+ struct bfd_link_info *info = hti->info;
+ /* .iplt entry is needed only for executable objects. */
+ if (!info->shared &&
+ !mips_elf_allocate_iplt (info, mips_elf_hash_table (info), h))
+ return FALSE;
+
+ /* IRELATIVE fixup will be needed for each local IFUNC. */
+ if (!mips_elf_allocate_ireloc (info, mips_elf_hash_table (info), h))
+ 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. */
@@ -1978,6 +2209,19 @@ mips_elf_check_symbols (struct mips_elf_link_hash_entry *h, void *data)
if (!hti->info->relocatable)
mips_elf_check_mips16_stubs (hti->info, h);
+ if (h && !h->needs_iplt &&
+ h->root.type == STT_GNU_IFUNC)
+ {
+ struct bfd_link_info *info = hti->info;
+ /* .iplt entry is needed only for executable objects. */
+ if (!info->shared
+ && !mips_elf_allocate_iplt (info, mips_elf_hash_table (info), h))
+ return FALSE;
+ /* Allocate an extra relocation for this IFUNC, if needed. */
+ if (!mips_elf_allocate_ireloc (info, mips_elf_hash_table (info), h))
+ return FALSE;
+ }
+
if (mips_elf_local_pic_function_p (h))
{
/* PR 12845: If H is in a section that has been garbage
@@ -3145,37 +3389,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
@@ -3270,7 +3483,14 @@ mips_elf_count_got_entry (struct bfd_link_info *info,
? &entry->d.h->root : NULL);
}
else if (entry->symndx >= 0 || entry->d.h->global_got_area == GGA_NONE)
- g->local_gotno += 1;
+ {
+ /* Count IFUNCs as general GOT entries since they will need
+ explicit IRELATIVE relocations. */
+ if (entry->symndx < 0 && entry->d.h->root.type == STT_GNU_IFUNC)
+ g->general_gotno += 1;
+ else
+ g->local_gotno += 1;
+ }
else
g->global_gotno += 1;
}
@@ -3600,7 +3820,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;
@@ -3615,7 +3836,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
@@ -3715,7 +3936,8 @@ mips_elf_create_local_got_entry (bfd *abfd, struct bfd_link_info *info,
if (entry)
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->assigned_low_gotno)
{
/* We didn't allocate enough space in the GOT. */
(*_bfd_error_handler)
@@ -3728,7 +3950,11 @@ 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->root.type == STT_GNU_IFUNC)
+ /* 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++;
+ else if (got16_reloc_p (r_type)
|| call16_reloc_p (r_type)
|| got_page_reloc_p (r_type)
|| got_disp_reloc_p (r_type))
@@ -4460,6 +4686,29 @@ mips_elf_count_got_symbols (struct mips_elf_link_hash_entry *h, void *data)
}
return 1;
}
+
+/* A elf_link_hash_traverse callback for which INF points to the
+ link_info structure. Count the number of GOT entries that need
+ explicit relocations by iterating over the local hash table. */
+
+static int
+mips_elf_count_general_got_symbols (void **slot, void *inf)
+{
+ struct mips_elf_link_hash_entry *h =
+ (struct mips_elf_link_hash_entry *) *slot;
+ struct bfd_link_info *info;
+ struct mips_elf_link_hash_table *htab;
+ struct mips_got_info *g;
+
+ info = (struct bfd_link_info *) inf;
+ htab = mips_elf_hash_table (info);
+
+ g = htab->got_info;
+ if (h != NULL && h->root.type == STT_GNU_IFUNC &&
+ (h->root.forced_local || h->global_got_area == GGA_NONE))
+ g->general_gotno++;
+ return 1;
+}
?
/* A htab_traverse callback for GOT entries. Add each one to the GOT
given in mips_elf_traverse_got_arg DATA. Clear DATA->G on error. */
@@ -4698,6 +4947,31 @@ mips_elf_set_global_got_area (void **entryp, void *data)
return 1;
}
+/* A htab_traverse callback for GOT entries, where DATA points to a
+ mips_elf_traverse_got_arg. Deallocate dedicated REL32 relocations
+ for global IFUNCs that are marked RELOC_ONLY. */
+
+static int
+mips_elf_deallocate_irelocs (void **entryp, void *data)
+{
+ struct mips_got_entry *entry;
+
+ entry = (struct mips_got_entry *) *entryp;
+ if (entry->abfd != NULL
+ && entry->symndx == -1
+ && entry->d.h->global_got_area == GGA_RELOC_ONLY
+ && entry->d.h->root.type == STT_GNU_IFUNC
+ && entry->d.h->needs_ireloc)
+ {
+ struct mips_elf_traverse_got_arg *arg
+ = (struct mips_elf_traverse_got_arg *) data;
+ asection *srel = mips_elf_rel_dyn_section (arg->info, FALSE);
+ srel->size -= MIPS_ELF_REL_SIZE (elf_hash_table (arg->info)->dynobj);
+ entry->d.h->needs_ireloc = FALSE;
+ }
+ return 1;
+}
+
/* A htab_traverse callback for secondary GOT entries, where DATA points
to a mips_elf_traverse_got_arg. Assign GOT indices to global entries
and record the number of relocations they require. DATA->value is
@@ -4852,6 +5126,11 @@ mips_elf_multi_got (bfd *abfd, struct bfd_link_info *info,
tga.value = GGA_NORMAL;
htab_traverse (g->got_entries, mips_elf_set_global_got_area, &tga);
+ /* Deallocate extra relocation intended for IFUNCs that have been marked
+ as RELOC_ONLY. These entries will get explicit relocs due to multi-got
+ which will work just as well for IFUNC resolution. */
+ htab_traverse (gg->got_entries, mips_elf_deallocate_irelocs, &tga);
+
/* Now go through the GOTs assigning them offset ranges.
[assigned_low_gotno, local_gotno[ will be set to the range of local
entries in each GOT. We can then compute the end of a GOT by
@@ -5087,6 +5366,64 @@ 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;
+
+ 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;
+
+ if (!info->shared)
+ {
+ 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))
+ /* Multiply by compression ratio for micromips. */
+ htab->iplt_entry_size = 4
+ * (ARRAY_SIZE (micromips32_exec_iplt_entry) * 3 / 4);
+ else
+ htab->iplt_entry_size = 4 * (ARRAY_SIZE (mips32_exec_iplt_entry)
+ + (LOAD_INTERLOCKS_P (dynobj)? 0 : 1));
+ }
+
+ 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
@@ -5203,6 +5540,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 = bfd_get_section_by_name (abfd, ".text");
+ 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);
+
+ if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
+ elf_tdata (abfd)->has_gnu_symbols = TRUE;
+
+ 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;
+
+ *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
@@ -5253,6 +5658,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
@@ -5333,6 +5740,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
{
@@ -5557,6 +5971,17 @@ 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)
+ {
+ 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)
@@ -5941,10 +6366,13 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
case R_MICROMIPS_CALL16:
/* VxWorks does not have separate local and global semantics for
R_MIPS*_GOT16; every relocation evaluates to "G". */
+ /* Local IFUNC symbols must be accessed through GOT, similar to global
+ symbols, to allow for indirection */
if (!htab->is_vxworks && local_p)
{
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
@@ -6448,31 +6876,41 @@ 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);
+ if (h && !indx && h->root.type == STT_GNU_IFUNC)
+ {
+ outrel[0].r_info = ELF_R_INFO (output_bfd, (unsigned long) indx,
+ 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
@@ -7019,6 +7457,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;
@@ -7381,6 +7821,9 @@ _bfd_mips_elf_add_symbol_hook (bfd *abfd, struct bfd_link_info *info,
flagword *flagsp ATTRIBUTE_UNUSED,
asection **secp, bfd_vma *valp)
{
+ if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+ elf_tdata (info->output_bfd)->has_gnu_symbols = TRUE;
+
if (SGI_COMPAT (abfd)
&& (abfd->flags & DYNAMIC) != 0
&& strcmp (*namep, "_rld_new_interface") == 0)
@@ -7932,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.iplt && !mips_elf_create_ifunc_sections (info))
+ return FALSE;
+
/* Check for the mips16 stub sections. */
name = bfd_get_section_name (abfd, sec);
@@ -8193,12 +8643,30 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
bfd_boolean can_make_dynamic_p;
bfd_boolean call_reloc_p;
bfd_boolean constrain_symbol_p;
+ bfd_boolean local_gnu_ifunc_p = FALSE;
r_symndx = ELF_R_SYM (abfd, rel->r_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 (get_local_sym_hash (htab, abfd, rel) == NULL)
+ return FALSE;
+ local_gnu_ifunc_p = TRUE;
+ }
+
+ h = NULL;
+ }
else if (r_symndx >= extsymoff + NUM_SHDR_ENTRIES (symtab_hdr))
{
(*_bfd_error_handler)
@@ -8410,7 +8878,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 && !local_gnu_ifunc_p)
{
(*_bfd_error_handler)
(_("%B: CALL16 reloc at 0x%lx not against global symbol"),
@@ -8435,9 +8904,11 @@ _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;
}
break;
@@ -8976,6 +9447,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
@@ -9254,6 +9726,11 @@ _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_local_symbols, &hti);
+
if (hti.error)
return FALSE;
@@ -9298,9 +9775,17 @@ mips_elf_lay_out_got (bfd *output_bfd, struct bfd_link_info *info)
count the number of reloc-only GOT symbols. */
mips_elf_link_hash_traverse (htab, mips_elf_count_got_symbols, info);
+ /* Count local IFUNC symbols. These need general GOT entries that
+ are fixed-up by explicit IRELATIVE relocations. */
+ htab_traverse (htab->loc_hash_table, mips_elf_count_general_got_symbols, 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. */
@@ -9742,6 +10227,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)
{
@@ -9887,6 +10374,13 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd,
if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_PLTGOT, 0))
return FALSE;
}
+
+ if (htab->got_info->general_gotno > 0)
+ {
+ 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;
@@ -10018,6 +10512,7 @@ _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;
rel_reloc = (NEWABI_P (input_bfd)
&& mips_elf_rel_relocation_p (input_bfd, input_section,
@@ -10031,6 +10526,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
{
@@ -10091,8 +10603,9 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
&& mips_elf_local_relocation_p (input_bfd, rel,
local_sections)))
{
- if (!mips_elf_add_lo16_rel_addend (input_bfd, rel, relend,
- contents, &addend))
+ if (!local_gnu_ifunc_p
+ && !mips_elf_add_lo16_rel_addend (input_bfd, rel, relend,
+ contents, &addend))
{
if (h)
name = h->root.root.string;
@@ -10479,6 +10992,237 @@ 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,
+ bfd_vma value)
+{
+ 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);
+ BFD_ASSERT (g != NULL);
+
+ lookup.abfd = NULL;
+ lookup.symndx = -1;
+ lookup.d.address = value;
+ lookup.tls_type = GOT_TLS_NONE;
+ loc = htab_find_slot (g->got_entries, &lookup, NO_INSERT);
+ if (!loc)
+ return -1;
+
+ entry = (struct mips_got_entry *) *loc;
+ if (entry)
+ 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;
+
+ if (!hmips->needs_iplt)
+ {
+ gotsect = htab->sgot;
+ if (hmips->global_got_area == GGA_NORMAL)
+ igot_offset = mips_elf_primary_global_got_index (output_bfd,
+ info, &hmips->root);
+ else if (hmips->global_got_area == GGA_NONE)
+ igot_offset = mips_elf_check_local_got_index (output_bfd, info,
+ sym->st_value);
+ }
+ 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, sym->st_value, loc);
+ else
+ bfd_put_32 (output_bfd, sym->st_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 || hmips->global_got_area == GGA_NONE)
+ /* Emit an R_MIPS_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 if (hmips->global_got_area == GGA_NORMAL)
+ {
+ /* 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, sym->st_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. */
@@ -10807,6 +11551,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. */
@@ -10970,9 +11727,37 @@ _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;
+
+ return _bfd_mips_elf_finish_dynamic_symbol (info->output_bfd, info,
+ &h->root, &isym);
+}
+
/* Likewise, for VxWorks. */
bfd_boolean
@@ -11358,6 +12143,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;
@@ -11528,6 +12320,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. */
@@ -13862,6 +14658,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 *
@@ -13885,6 +14723,17 @@ _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 +16358,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
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index 18e3c40..2d0742d 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 3478006..18ed890 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 c313db4..7f97758 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -1724,6 +1724,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;
}
@@ -8514,6 +8516,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 722984e..f919fb8 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 57de3bc..4005047 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. */
--
1.7.9.5
-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: Spec.txt
URL: <https://sourceware.org/pipermail/binutils/attachments/20150721/008b1ec6/attachment.txt>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0002-RFC-Add-IFUNC-tests-for-MIPS.patch
Type: text/x-patch
Size: 45869 bytes
Desc: 0002-RFC-Add-IFUNC-tests-for-MIPS.patch
URL: <https://sourceware.org/pipermail/binutils/attachments/20150721/008b1ec6/attachment.bin>
More information about the Binutils
mailing list