aboutsummaryrefslogtreecommitdiff
path: root/bfd
diff options
context:
space:
mode:
Diffstat (limited to 'bfd')
-rw-r--r--bfd/elfnn-loongarch.c341
-rw-r--r--bfd/elfxx-loongarch.h8
2 files changed, 287 insertions, 62 deletions
diff --git a/bfd/elfnn-loongarch.c b/bfd/elfnn-loongarch.c
index ed5d264..8aaf157 100644
--- a/bfd/elfnn-loongarch.c
+++ b/bfd/elfnn-loongarch.c
@@ -42,9 +42,6 @@ struct loongarch_elf_link_hash_entry
{
struct elf_link_hash_entry elf;
- /* Track dynamic relocs copied for this symbol. */
- struct elf_dyn_relocs *dyn_relocs;
-
#define GOT_UNKNOWN 0
#define GOT_NORMAL 1
#define GOT_TLS_GD 2
@@ -238,7 +235,6 @@ link_hash_newfunc (struct bfd_hash_entry *entry, struct bfd_hash_table *table,
if (entry != NULL)
{
eh = (struct loongarch_elf_link_hash_entry *) entry;
- eh->dyn_relocs = NULL;
eh->tls_type = GOT_UNKNOWN;
}
@@ -632,11 +628,21 @@ loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
h = (struct elf_link_hash_entry *) h->root.u.i.link;
}
+ /* It is referenced by a non-shared object. */
+ if (h != NULL)
+ h->ref_regular = 1;
+
if (h && h->type == STT_GNU_IFUNC)
{
if (htab->elf.dynobj == NULL)
htab->elf.dynobj = abfd;
+ /* Create the ifunc sections, iplt and ipltgot, for static
+ executables. */
+ if ((r_type == R_LARCH_64 || r_type == R_LARCH_32)
+ && !_bfd_elf_create_ifunc_sections (htab->elf.dynobj, info))
+ return false;
+
if (!htab->elf.splt
&& !_bfd_elf_create_ifunc_sections (htab->elf.dynobj, info))
/* If '.plt' not represent, create '.iplt' to deal with ifunc. */
@@ -752,6 +758,24 @@ loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
if (h != NULL)
h->non_got_ref = 1;
+
+ if (h != NULL
+ && (!bfd_link_pic (info)
+ || h->type == STT_GNU_IFUNC))
+ {
+ /* This reloc might not bind locally. */
+ h->non_got_ref = 1;
+ h->pointer_equality_needed = 1;
+
+ if (!h->def_regular
+ || (sec->flags & (SEC_CODE | SEC_READONLY)) != 0)
+ {
+ /* We may need a .plt entry if the symbol is a function
+ defined in a shared lib or is a function referenced
+ from the code or read-only section. */
+ h->plt.refcount += 1;
+ }
+ }
break;
case R_LARCH_GNU_VTINHERIT:
@@ -790,7 +814,7 @@ loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
/* If this is a global symbol, we count the number of
relocations we need for this symbol. */
if (h != NULL)
- head = &((struct loongarch_elf_link_hash_entry *) h)->dyn_relocs;
+ head = &h->dyn_relocs;
else
{
/* Track dynamic relocs needed for local syms too.
@@ -837,7 +861,7 @@ readonly_dynrelocs (struct elf_link_hash_entry *h)
{
struct elf_dyn_relocs *p;
- for (p = loongarch_elf_hash_entry (h)->dyn_relocs; p != NULL; p = p->next)
+ for (p = h->dyn_relocs; p != NULL; p = p->next)
{
asection *s = p->sec->output_section;
@@ -986,13 +1010,15 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
{
struct bfd_link_info *info;
struct loongarch_elf_link_hash_table *htab;
- struct loongarch_elf_link_hash_entry *eh;
struct elf_dyn_relocs *p;
if (h->root.type == bfd_link_hash_indirect)
return true;
- eh = (struct loongarch_elf_link_hash_entry *) h;
+ if (h->type == STT_GNU_IFUNC
+ && h->def_regular)
+ return true;
+
info = (struct bfd_link_info *) inf;
htab = loongarch_elf_hash_table (info);
BFD_ASSERT (htab != NULL);
@@ -1041,6 +1067,18 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
gotplt->size += GOT_ENTRY_SIZE;
relplt->size += sizeof (ElfNN_External_Rela);
+ /* If this symbol is not defined in a regular file, and we are
+ not generating a shared library, then set the symbol to this
+ location in the .plt. This is required to make function
+ pointers compare as equal between the normal executable and
+ the shared library. */
+ if (!bfd_link_pic(info)
+ && !h->def_regular)
+ {
+ h->root.u.def.section = plt;
+ h->root.u.def.value = h->plt.offset;
+ }
+
h->needs_plt = 1;
}
while (0);
@@ -1056,9 +1094,20 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
/* Make sure this symbol is output as a dynamic symbol.
Undefined weak syms won't yet be marked as dynamic. */
- if (h->dynindx == -1 && !h->forced_local
- && !bfd_elf_link_record_dynamic_symbol (info, h))
- return false;
+ if (h->dynindx == -1 && !h->forced_local)
+ {
+ if (SYMBOL_REFERENCES_LOCAL (info, h)
+ && (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
+ && h->start_stop)
+ {
+ /* The pr21964-4. do nothing. */
+ }
+ else
+ {
+ if( !bfd_elf_link_record_dynamic_symbol (info, h))
+ return false;
+ }
+ }
s = htab->elf.sgot;
h->got.offset = s->size;
@@ -1091,14 +1140,14 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
else
h->got.offset = MINUS_ONE;
- if (eh->dyn_relocs == NULL)
+ if (h->dyn_relocs == NULL)
return true;
if (SYMBOL_REFERENCES_LOCAL (info, h))
{
struct elf_dyn_relocs **pp;
- for (pp = &eh->dyn_relocs; (p = *pp) != NULL;)
+ for (pp = &h->dyn_relocs; (p = *pp) != NULL;)
{
p->count -= p->pc_count;
p->pc_count = 0;
@@ -1112,7 +1161,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
if (h->root.type == bfd_link_hash_undefweak)
{
if (UNDEFWEAK_NO_DYNAMIC_RELOC (info, h))
- eh->dyn_relocs = NULL;
+ h->dyn_relocs = NULL;
else if (h->dynindx == -1 && !h->forced_local
/* Make sure this symbol is output as a dynamic symbol.
Undefined weak syms won't yet be marked as dynamic. */
@@ -1120,7 +1169,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
return false;
}
- for (p = eh->dyn_relocs; p != NULL; p = p->next)
+ for (p = h->dyn_relocs; p != NULL; p = p->next)
{
asection *sreloc = elf_section_data (p->sec)->sreloc;
sreloc->size += p->count * sizeof (ElfNN_External_Rela);
@@ -1129,16 +1178,60 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
return true;
}
+/* Allocate space in .plt, .got and associated reloc sections for
+ ifunc dynamic relocs. */
+
+static bool
+elfNN_loongarch_allocate_ifunc_dynrelocs (struct elf_link_hash_entry *h,
+ void *inf)
+{
+ struct bfd_link_info *info;
+ /* An example of a bfd_link_hash_indirect symbol is versioned
+ symbol. For example: __gxx_personality_v0(bfd_link_hash_indirect)
+ -> __gxx_personality_v0(bfd_link_hash_defined)
+
+ There is no need to process bfd_link_hash_indirect symbols here
+ because we will also be presented with the concrete instance of
+ the symbol and loongarch_elf_copy_indirect_symbol () will have been
+ called to copy all relevant data from the generic to the concrete
+ symbol instance. */
+ if (h->root.type == bfd_link_hash_indirect)
+ return true;
+
+ if (h->root.type == bfd_link_hash_warning)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+ info = (struct bfd_link_info *) inf;
+
+ /* Since STT_GNU_IFUNC symbol must go through PLT, we handle it
+ here if it is defined and referenced in a non-shared object. */
+ if (h->type == STT_GNU_IFUNC
+ && h->def_regular)
+ return _bfd_elf_allocate_ifunc_dyn_relocs (info, h,
+ &h->dyn_relocs,
+ PLT_ENTRY_SIZE,
+ PLT_HEADER_SIZE,
+ GOT_ENTRY_SIZE,
+ false);
+ return true;
+}
+
+/* Allocate space in .plt, .got and associated reloc sections for
+ ifunc dynamic relocs. */
+
static bool
elfNN_loongarch_allocate_local_dynrelocs (void **slot, void *inf)
{
struct elf_link_hash_entry *h = (struct elf_link_hash_entry *) *slot;
- if (!h->def_regular || !h->ref_regular || !h->forced_local
+ if (h->type != STT_GNU_IFUNC
+ || !h->def_regular
+ || !h->ref_regular
+ || !h->forced_local
|| h->root.type != bfd_link_hash_defined)
abort ();
- return allocate_dynrelocs (h, inf);
+ return elfNN_loongarch_allocate_ifunc_dynrelocs (h, inf);
}
/* Set DF_TEXTREL if we find any dynamic relocs that apply to
@@ -1275,6 +1368,11 @@ loongarch_elf_size_dynamic_sections (bfd *output_bfd,
/* Allocate global sym .plt and .got entries, and space for global
sym dynamic relocs. */
elf_link_hash_traverse (&htab->elf, allocate_dynrelocs, info);
+
+ /* Allocate global ifunc sym .plt and .got entries, and space for global
+ ifunc sym dynamic relocs. */
+ elf_link_hash_traverse (&htab->elf, elfNN_loongarch_allocate_ifunc_dynrelocs, info);
+
/* Allocate .plt and .got entries, and space for local ifunc symbols. */
htab_traverse (htab->loc_hash_table,
(void *) elfNN_loongarch_allocate_local_dynrelocs, info);
@@ -1976,14 +2074,9 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
resolved_to_const = true;
}
- if (h && h->type == STT_GNU_IFUNC)
+ /* The ifunc without reference does not generate plt. */
+ if (h && h->type == STT_GNU_IFUNC && h->plt.offset != MINUS_ONE)
{
- if (h->plt.offset == MINUS_ONE)
- info->callbacks->info ("%X%pB(%pA+0x%v): error: %s against `%s':\n"
- "STT_GNU_IFUNC must have PLT stub"
- "\n",
- input_bfd, input_section,
- (bfd_vma) rel->r_offset, howto->name, name);
defined_local = true;
resolved_local = true;
resolved_dynly = false;
@@ -1995,7 +2088,7 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
BFD_ASSERT (resolved_local + resolved_dynly + resolved_to_const == 1);
- BFD_ASSERT (!resolved_dynly || (h && h->dynindx != -1));
+ /* BFD_ASSERT (!resolved_dynly || (h && h->dynindx != -1));. */
BFD_ASSERT (!resolved_local || defined_local);
@@ -2026,7 +2119,31 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
&& (input_section->flags & SEC_ALLOC));
outrel.r_offset += sec_addr (input_section);
- if (resolved_dynly)
+
+ /* A pointer point to a local ifunc symbol. */
+ if(h
+ && h->type == STT_GNU_IFUNC
+ && (h->dynindx == -1
+ || h->forced_local
+ || bfd_link_executable(info)))
+ {
+ outrel.r_info = ELFNN_R_INFO (0, R_LARCH_IRELATIVE);
+ outrel.r_addend = (h->root.u.def.value
+ + h->root.u.def.section->output_section->vma
+ + h->root.u.def.section->output_offset);
+
+ /* Dynamic relocations are stored in
+ 1. .rela.ifunc section in PIC object.
+ 2. .rela.got section in dynamic executable.
+ 3. .rela.iplt section in static executable. */
+ if (bfd_link_pic (info))
+ sreloc = htab->elf.irelifunc;
+ else if (htab->elf.splt != NULL)
+ sreloc = htab->elf.srelgot;
+ else
+ sreloc = htab->elf.irelplt;
+ }
+ else if (resolved_dynly)
{
outrel.r_info = ELFNN_R_INFO (h->dynindx, r_type);
outrel.r_addend = rel->r_addend;
@@ -2093,6 +2210,25 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
bfd_reloc_notsupported,
is_undefweak, name,
"Internal:");
+ if (resolved_local)
+ {
+ if (!elf_hash_table (info)->tls_sec)
+ {
+ fatal = loongarch_reloc_is_fatal (info, input_bfd,
+ input_section, rel, howto, bfd_reloc_notsupported,
+ is_undefweak, name, "TLS section not be created");
+ }
+ else
+ relocation -= elf_hash_table (info)->tls_sec->vma;
+ }
+ else
+ {
+ fatal = loongarch_reloc_is_fatal (info, input_bfd,
+ input_section, rel, howto, bfd_reloc_undefined,
+ is_undefweak, name,
+ "TLS LE just can be resolved local only.");
+ }
+
break;
case R_LARCH_SOP_PUSH_TLS_TPREL:
@@ -2275,7 +2411,8 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
{
off = h->got.offset;
- if (off == MINUS_ONE)
+ if (off == MINUS_ONE
+ && h->type != STT_GNU_IFUNC)
{
fatal = (loongarch_reloc_is_fatal
(info, input_bfd, input_section, rel, howto,
@@ -2284,6 +2421,33 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
break;
}
+ /* Hidden symbol not has .got entry, only .got.plt entry
+ so gprel is (plt - got). */
+ if (off == MINUS_ONE
+ && h->type == STT_GNU_IFUNC)
+ {
+ if (h->plt.offset == (bfd_vma) -1)
+ {
+ abort();
+ }
+
+ bfd_vma plt_index = h->plt.offset / PLT_ENTRY_SIZE;
+ off = plt_index * GOT_ENTRY_SIZE;
+
+ if (htab->elf.splt != NULL)
+ {
+ /* Section .plt header is 2 times of plt entry. */
+ off = sec_addr(htab->elf.sgotplt) + off
+ - sec_addr(htab->elf.sgot);
+ }
+ else
+ {
+ /* Section iplt not has plt header. */
+ off = sec_addr(htab->elf.igotplt) + off
+ - sec_addr(htab->elf.sgot);
+ }
+ }
+
if (!WILL_CALL_FINISH_DYNAMIC_SYMBOL (is_dyn, is_pic, h)
|| (is_pic && SYMBOL_REFERENCES_LOCAL (info, h)))
{
@@ -2321,6 +2485,28 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
off &= ~1;
else
{
+ /* The pr21964-4. Create relocate entry. */
+ if (is_pic && h->start_stop)
+ {
+ asection *s;
+ Elf_Internal_Rela outrel;
+ /* We need to generate a R_LARCH_RELATIVE reloc
+ for the dynamic linker. */
+ s = htab->elf.srelgot;
+ if (!s)
+ {
+ fatal = loongarch_reloc_is_fatal (info, input_bfd,
+ input_section, rel, howto,
+ bfd_reloc_notsupported, is_undefweak, name,
+ "Internal: '.rel.got' not represent");
+ break;
+ }
+
+ outrel.r_offset = sec_addr (got) + off;
+ outrel.r_info = ELFNN_R_INFO (0, R_LARCH_RELATIVE);
+ outrel.r_addend = relocation; /* Link-time addr. */
+ loongarch_elf_append_rela (output_bfd, s, &outrel);
+ }
bfd_put_NN (output_bfd, relocation, got->contents + off);
h->got.offset |= 1;
}
@@ -2516,9 +2702,19 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
}
else /* if (resolved_dynly) */
{
- outrel.r_info =
- ELFNN_R_INFO (h->dynindx, R_LARCH_TLS_TPRELNN);
- outrel.r_addend = 0;
+ /* Static linking has no .dynsym table. */
+ if (!htab->elf.dynamic_sections_created)
+ {
+ outrel.r_info =
+ ELFNN_R_INFO (0, R_LARCH_TLS_TPRELNN);
+ outrel.r_addend = 0;
+ }
+ else
+ {
+ outrel.r_info =
+ ELFNN_R_INFO (h->dynindx, R_LARCH_TLS_TPRELNN);
+ outrel.r_addend = 0;
+ }
loongarch_elf_append_rela (output_bfd, htab->elf.srelgot,
&outrel);
}
@@ -2628,22 +2824,16 @@ loongarch_elf_finish_dynamic_symbol (bfd *output_bfd,
{
struct loongarch_elf_link_hash_table *htab = loongarch_elf_hash_table (info);
const struct elf_backend_data *bed = get_elf_backend_data (output_bfd);
- asection *plt = NULL;
if (h->plt.offset != MINUS_ONE)
{
size_t i, plt_idx;
- asection *gotplt, *relplt;
+ asection *plt, *gotplt, *relplt;
bfd_vma got_address;
uint32_t plt_entry[PLT_ENTRY_INSNS];
bfd_byte *loc;
Elf_Internal_Rela rela;
- plt_idx = (h->plt.offset - PLT_HEADER_SIZE) / PLT_ENTRY_SIZE;
-
- /* One of '.plt' and '.iplt' represents. */
- BFD_ASSERT (!!htab->elf.splt ^ !!htab->elf.iplt);
-
if (htab->elf.splt)
{
BFD_ASSERT ((h->type == STT_GNU_IFUNC
@@ -2653,6 +2843,7 @@ loongarch_elf_finish_dynamic_symbol (bfd *output_bfd,
plt = htab->elf.splt;
gotplt = htab->elf.sgotplt;
relplt = htab->elf.srelplt;
+ plt_idx = (h->plt.offset - PLT_HEADER_SIZE) / PLT_ENTRY_SIZE;
got_address =
sec_addr (gotplt) + GOTPLT_HEADER_SIZE + plt_idx * GOT_ENTRY_SIZE;
}
@@ -2664,6 +2855,7 @@ loongarch_elf_finish_dynamic_symbol (bfd *output_bfd,
plt = htab->elf.iplt;
gotplt = htab->elf.igotplt;
relplt = htab->elf.irelplt;
+ plt_idx = h->plt.offset / PLT_ENTRY_SIZE;
got_address = sec_addr (gotplt) + plt_idx * GOT_ENTRY_SIZE;
}
@@ -2684,7 +2876,9 @@ loongarch_elf_finish_dynamic_symbol (bfd *output_bfd,
bfd_put_NN (output_bfd, sec_addr (plt), loc);
rela.r_offset = got_address;
- if (h->type == STT_GNU_IFUNC && SYMBOL_REFERENCES_LOCAL (info, h))
+
+ /* TRUE if this is a PLT reference to a local IFUNC. */
+ if (PLT_LOCAL_IFUNC_P(info, h))
{
rela.r_info = ELFNN_R_INFO (0, R_LARCH_IRELATIVE);
rela.r_addend = (h->root.u.def.value
@@ -2733,26 +2927,50 @@ loongarch_elf_finish_dynamic_symbol (bfd *output_bfd,
rela.r_offset = sec_addr (sgot) + off;
- if (h->type == STT_GNU_IFUNC)
+ if (h->def_regular
+ && h->type == STT_GNU_IFUNC)
{
- if (elf_hash_table (info)->dynamic_sections_created
- && SYMBOL_REFERENCES_LOCAL (info, h))
+ if(h->plt.offset == MINUS_ONE)
+ {
+ if (htab->elf.splt == NULL)
+ srela = htab->elf.irelplt;
+
+ if (SYMBOL_REFERENCES_LOCAL (info, h))
+ {
+ asection *sec = h->root.u.def.section;
+ rela.r_info = ELFNN_R_INFO (0, R_LARCH_IRELATIVE);
+ rela.r_addend = h->root.u.def.value + sec->output_section->vma
+ + sec->output_offset;
+ bfd_put_NN (output_bfd, 0, sgot->contents + off);
+ }
+ else
+ {
+ BFD_ASSERT ((h->got.offset & 1) == 0);
+ BFD_ASSERT (h->dynindx != -1);
+ rela.r_info = ELFNN_R_INFO (h->dynindx, R_LARCH_NN);
+ rela.r_addend = 0;
+ bfd_put_NN (output_bfd, (bfd_vma) 0, sgot->contents + off);
+ }
+ }
+ else if(bfd_link_pic (info))
{
- asection *sec = h->root.u.def.section;
- rela.r_info = ELFNN_R_INFO (0, R_LARCH_IRELATIVE);
- rela.r_addend = (h->root.u.def.value + sec->output_section->vma
- + sec->output_offset);
- bfd_put_NN (output_bfd, 0, sgot->contents + off);
+ rela.r_info = ELFNN_R_INFO (h->dynindx, R_LARCH_NN);
+ rela.r_addend = 0;
+ bfd_put_NN (output_bfd, rela.r_addend, sgot->contents + off);
}
else
{
- BFD_ASSERT (plt);
- rela.r_info
- = ELFNN_R_INFO (0, (bfd_link_pic (info)
- ? R_LARCH_RELATIVE : R_LARCH_NONE));
- rela.r_addend =
- plt->output_section->vma + plt->output_offset + h->plt.offset;
- bfd_put_NN (output_bfd, rela.r_addend, sgot->contents + off);
+ asection *plt;
+ /* For non-shared object, we can't use .got.plt, which
+ contains the real function address if we need pointer
+ equality. We load the GOT entry with the PLT entry. */
+ plt = htab->elf.splt ? htab->elf.splt : htab->elf.iplt;
+ bfd_put_NN (output_bfd,
+ (plt->output_section->vma
+ + plt->output_offset
+ + h->plt.offset),
+ sgot->contents + off);
+ return true;
}
}
else if (bfd_link_pic (info) && SYMBOL_REFERENCES_LOCAL (info, h))
@@ -2885,10 +3103,8 @@ loongarch_elf_finish_dynamic_sections (bfd *output_bfd,
return false;
}
- if ((plt = htab->elf.splt))
- gotplt = htab->elf.sgotplt;
- else if ((plt = htab->elf.iplt))
- gotplt = htab->elf.igotplt;
+ plt = htab->elf.splt;
+ gotplt = htab->elf.sgotplt;
if (plt && 0 < plt->size)
{
@@ -3017,10 +3233,10 @@ loongarch_elf_copy_indirect_symbol (struct bfd_link_info *info,
struct elf_link_hash_entry *dir,
struct elf_link_hash_entry *ind)
{
- struct loongarch_elf_link_hash_entry *edir, *eind;
+ struct elf_link_hash_entry *edir, *eind;
- edir = (struct loongarch_elf_link_hash_entry *) dir;
- eind = (struct loongarch_elf_link_hash_entry *) ind;
+ edir = dir;
+ eind = ind;
if (eind->dyn_relocs != NULL)
{
@@ -3055,8 +3271,9 @@ loongarch_elf_copy_indirect_symbol (struct bfd_link_info *info,
if (ind->root.type == bfd_link_hash_indirect && dir->got.refcount < 0)
{
- edir->tls_type = eind->tls_type;
- eind->tls_type = GOT_UNKNOWN;
+ loongarch_elf_hash_entry(edir)->tls_type
+ = loongarch_elf_hash_entry(eind)->tls_type;
+ loongarch_elf_hash_entry(eind)->tls_type = GOT_UNKNOWN;
}
_bfd_elf_link_hash_copy_indirect (info, dir, ind);
}
diff --git a/bfd/elfxx-loongarch.h b/bfd/elfxx-loongarch.h
index 3b5c636..8ea63d0 100644
--- a/bfd/elfxx-loongarch.h
+++ b/bfd/elfxx-loongarch.h
@@ -31,3 +31,11 @@ extern reloc_howto_type *
loongarch_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, const char *r_name);
bool loongarch_adjust_reloc_bitsfield (reloc_howto_type *howto, bfd_vma *fix_val);
+
+/* TRUE if this is a PLT reference to a local IFUNC. */
+#define PLT_LOCAL_IFUNC_P(INFO, H) \
+ ((H)->dynindx == -1 \
+ || ((bfd_link_executable (INFO) \
+ || ELF_ST_VISIBILITY ((H)->other) != STV_DEFAULT) \
+ && (H)->def_regular \
+ && (H)->type == STT_GNU_IFUNC))