LoongArch: Fix LD check fails.

Some test cases about ifunc.

  bfd/
    * elfnn-loongarch.c
    * elfxx-loongarch.h

   === ld Summary ===
  of expected passes             1430
  of expected failures           11
  of untested testcases          1
  of unsupported tests           154
This commit is contained in:
liuzhensong 2022-02-21 14:34:07 +08:00
parent 3b14682a43
commit d218dba3f4
2 changed files with 287 additions and 62 deletions

View File

@ -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)
{
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);
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))
{
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);
}

View File

@ -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))