From d218dba3f491191322e50df74376faa11c4a9f6b Mon Sep 17 00:00:00 2001 From: liuzhensong Date: Mon, 21 Feb 2022 14:34:07 +0800 Subject: [PATCH] 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 --- bfd/elfnn-loongarch.c | 341 ++++++++++++++++++++++++++++++++++-------- bfd/elfxx-loongarch.h | 8 + 2 files changed, 287 insertions(+), 62 deletions(-) diff --git a/bfd/elfnn-loongarch.c b/bfd/elfnn-loongarch.c index ed5d2641783..8aaf157b303 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 3b5c6361e06..8ea63d03fa5 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)) -- 2.30.2