X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=bfd%2Felfnn-riscv.c;h=163c4d9f7450f85d128966ba81668e99c8809095;hb=refs%2Fheads%2Fconcurrent-displaced-stepping-2020-04-01;hp=706dcb923f113bb9553ad7877afc09c1015c908a;hpb=0f52d45acd6c54aa47082778ffcbafddd423a5b4;p=deliverable%2Fbinutils-gdb.git diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c index 706dcb923f..163c4d9f74 100644 --- a/bfd/elfnn-riscv.c +++ b/bfd/elfnn-riscv.c @@ -1,5 +1,5 @@ /* RISC-V-specific support for NN-bit ELF. - Copyright (C) 2011-2019 Free Software Foundation, Inc. + Copyright (C) 2011-2020 Free Software Foundation, Inc. Contributed by Andrew Waterman (andrew@sifive.com). Based on TILE-Gx and MIPS targets. @@ -61,9 +61,6 @@ struct riscv_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 @@ -98,6 +95,14 @@ struct _bfd_riscv_elf_obj_tdata && elf_tdata (bfd) != NULL \ && elf_object_id (bfd) == RISCV_ELF_DATA) +static bfd_boolean +elfNN_riscv_mkobject (bfd *abfd) +{ + return bfd_elf_allocate_object (abfd, + sizeof (struct _bfd_riscv_elf_obj_tdata), + RISCV_ELF_DATA); +} + #include "elf/common.h" #include "elf/internal.h" @@ -256,7 +261,6 @@ link_hash_newfunc (struct bfd_hash_entry *entry, struct riscv_elf_link_hash_entry *eh; eh = (struct riscv_elf_link_hash_entry *) entry; - eh->dyn_relocs = NULL; eh->tls_type = GOT_UNKNOWN; } @@ -269,7 +273,7 @@ static struct bfd_link_hash_table * riscv_elf_link_hash_table_create (bfd *abfd) { struct riscv_elf_link_hash_table *ret; - bfd_size_type amt = sizeof (struct riscv_elf_link_hash_table); + size_t amt = sizeof (struct riscv_elf_link_hash_table); ret = (struct riscv_elf_link_hash_table *) bfd_zmalloc (amt); if (ret == NULL) @@ -310,13 +314,13 @@ riscv_elf_create_got_section (bfd *abfd, struct bfd_link_info *info) (bed->dynamic_sec_flags | SEC_READONLY)); if (s == NULL - || ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align)) + || !bfd_set_section_alignment (s, bed->s->log_file_align)) return FALSE; htab->srelgot = s; s = s_got = bfd_make_section_anyway_with_flags (abfd, ".got", flags); if (s == NULL - || !bfd_set_section_alignment (abfd, s, bed->s->log_file_align)) + || !bfd_set_section_alignment (s, bed->s->log_file_align)) return FALSE; htab->sgot = s; @@ -327,8 +331,7 @@ riscv_elf_create_got_section (bfd *abfd, struct bfd_link_info *info) { s = bfd_make_section_anyway_with_flags (abfd, ".got.plt", flags); if (s == NULL - || !bfd_set_section_alignment (abfd, s, - bed->s->log_file_align)) + || !bfd_set_section_alignment (s, bed->s->log_file_align)) return FALSE; htab->sgotplt = s; @@ -373,9 +376,23 @@ riscv_elf_create_dynamic_sections (bfd *dynobj, if (!bfd_link_pic (info)) { + /* Technically, this section doesn't have contents. It is used as the + target of TLS copy relocs, to copy TLS data from shared libraries into + the executable. However, if we don't mark it as loadable, then it + matches the IS_TBSS test in ldlang.c, and there is no run-time address + space allocated for it even though it has SEC_ALLOC. That test is + correct for .tbss, but not correct for this section. There is also + a second problem that having a section with no contents can only work + if it comes after all sections with contents in the same segment, + but the linker script does not guarantee that. This is just mixed in + with other .tdata.* sections. We can fix both problems by lying and + saying that there are contents. This section is expected to be small + so this should not cause a significant extra program startup cost. */ htab->sdyntdata = bfd_make_section_anyway_with_flags (dynobj, ".tdata.dyn", (SEC_ALLOC | SEC_THREAD_LOCAL + | SEC_LOAD | SEC_DATA + | SEC_HAS_CONTENTS | SEC_LINKER_CREATED)); } @@ -398,37 +415,6 @@ riscv_elf_copy_indirect_symbol (struct bfd_link_info *info, edir = (struct riscv_elf_link_hash_entry *) dir; eind = (struct riscv_elf_link_hash_entry *) ind; - if (eind->dyn_relocs != NULL) - { - if (edir->dyn_relocs != NULL) - { - struct elf_dyn_relocs **pp; - struct elf_dyn_relocs *p; - - /* Add reloc counts against the indirect sym to the direct sym - list. Merge any entries against the same section. */ - for (pp = &eind->dyn_relocs; (p = *pp) != NULL; ) - { - struct elf_dyn_relocs *q; - - for (q = edir->dyn_relocs; q != NULL; q = q->next) - if (q->sec == p->sec) - { - q->pc_count += p->pc_count; - q->count += p->count; - *pp = p->next; - break; - } - if (q == NULL) - pp = &p->next; - } - *pp = edir->dyn_relocs; - } - - edir->dyn_relocs = eind->dyn_relocs; - eind->dyn_relocs = NULL; - } - if (ind->root.type == bfd_link_hash_indirect && dir->got.refcount <= 0) { @@ -684,7 +670,7 @@ riscv_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 riscv_elf_link_hash_entry *) h)->dyn_relocs; + head = &h->dyn_relocs; else { /* Track dynamic relocs needed for local syms too. @@ -711,7 +697,7 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, p = *head; if (p == NULL || p->sec != sec) { - bfd_size_type amt = sizeof *p; + size_t amt = sizeof *p; p = ((struct elf_dyn_relocs *) bfd_alloc (htab->elf.dynobj, amt)); if (p == NULL) @@ -765,23 +751,6 @@ riscv_elf_gc_mark_hook (asection *sec, return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym); } -/* Find dynamic relocs for H that apply to read-only sections. */ - -static asection * -readonly_dynrelocs (struct elf_link_hash_entry *h) -{ - struct elf_dyn_relocs *p; - - for (p = riscv_elf_hash_entry (h)->dyn_relocs; p != NULL; p = p->next) - { - asection *s = p->sec->output_section; - - if (s != NULL && (s->flags & SEC_READONLY) != 0) - return p->sec; - } - return NULL; -} - /* Adjust a symbol defined by a dynamic object and referenced by a regular object. The current definition is in some section of the dynamic object, but we're not including those sections. We have to @@ -870,7 +839,7 @@ riscv_elf_adjust_dynamic_symbol (struct bfd_link_info *info, /* If we don't find any dynamic relocs in read-only sections, then we'll be keeping the dynamic relocs and avoiding the copy reloc. */ - if (!readonly_dynrelocs (h)) + if (!_bfd_elf_readonly_dynrelocs (h)) { h->non_got_ref = 0; return TRUE; @@ -923,7 +892,6 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) { struct bfd_link_info *info; struct riscv_elf_link_hash_table *htab; - struct riscv_elf_link_hash_entry *eh; struct elf_dyn_relocs *p; if (h->root.type == bfd_link_hash_indirect) @@ -1032,8 +1000,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) else h->got.offset = (bfd_vma) -1; - eh = (struct riscv_elf_link_hash_entry *) h; - if (eh->dyn_relocs == NULL) + if (h->dyn_relocs == NULL) return TRUE; /* In the shared -Bsymbolic case, discard space allocated for @@ -1048,7 +1015,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) { 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; @@ -1061,12 +1028,12 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) /* Also discard relocs on undefined weak syms with non-default visibility. */ - if (eh->dyn_relocs != NULL + if (h->dyn_relocs != NULL && h->root.type == bfd_link_hash_undefweak) { if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT || UNDEFWEAK_NO_DYNAMIC_RELOC (info, h)) - eh->dyn_relocs = NULL; + h->dyn_relocs = NULL; /* Make sure undefined weak symbols are output as a dynamic symbol in PIEs. */ @@ -1106,13 +1073,13 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) goto keep; } - eh->dyn_relocs = NULL; + h->dyn_relocs = NULL; keep: ; } /* Finally, allocate space. */ - 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); @@ -1121,33 +1088,6 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) return TRUE; } -/* Set DF_TEXTREL if we find any dynamic relocs that apply to - read-only sections. */ - -static bfd_boolean -maybe_set_textrel (struct elf_link_hash_entry *h, void *info_p) -{ - asection *sec; - - if (h->root.type == bfd_link_hash_indirect) - return TRUE; - - sec = readonly_dynrelocs (h); - if (sec != NULL) - { - struct bfd_link_info *info = (struct bfd_link_info *) info_p; - - info->flags |= DF_TEXTREL; - info->callbacks->minfo - (_("%pB: dynamic relocation against `%pT' in read-only section `%pA'\n"), - sec->owner, h->root.root.string, sec); - - /* Not an error, just cut short the traversal. */ - return FALSE; - } - return TRUE; -} - static bfd_boolean riscv_elf_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info) { @@ -1354,7 +1294,8 @@ riscv_elf_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info) /* If any dynamic relocs apply to a read-only section, then we need a DT_TEXTREL entry. */ if ((info->flags & DF_TEXTREL) == 0) - elf_link_hash_traverse (&htab->elf, maybe_set_textrel, info); + elf_link_hash_traverse (&htab->elf, + _bfd_elf_maybe_set_textrel, info); if (info->flags & DF_TEXTREL) { @@ -1482,9 +1423,21 @@ perform_relocation (const reloc_howto_type *howto, break; case R_RISCV_RVC_LUI: - if (!VALID_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (value))) + if (RISCV_CONST_HIGH_PART (value) == 0) + { + /* Linker relaxation can convert an address equal to or greater than + 0x800 to slightly below 0x800. C.LUI does not accept zero as a + valid immediate. We can fix this by converting it to a C.LI. */ + bfd_vma insn = bfd_get (howto->bitsize, input_bfd, + contents + rel->r_offset); + insn = (insn & ~MATCH_C_LUI) | MATCH_C_LI; + bfd_put (howto->bitsize, input_bfd, insn, contents + rel->r_offset); + value = ENCODE_RVC_IMM (0); + } + else if (!VALID_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (value))) return bfd_reloc_overflow; - value = ENCODE_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (value)); + else + value = ENCODE_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (value)); break; case R_RISCV_32: @@ -1762,6 +1715,7 @@ riscv_elf_relocate_section (bfd *output_bfd, int r_type = ELFNN_R_TYPE (rel->r_info), tls_type; reloc_howto_type *howto = riscv_elf_rtype_to_howto (input_bfd, r_type); const char *msg = NULL; + char *msg_buf = NULL; bfd_boolean resolved_to_zero; if (howto == NULL @@ -1814,7 +1768,7 @@ riscv_elf_relocate_section (bfd *output_bfd, name = (bfd_elf_string_from_elf_section (input_bfd, symtab_hdr->sh_link, sym->st_name)); if (name == NULL || *name == '\0') - name = bfd_section_name (input_bfd, sec); + name = bfd_section_name (sec); } resolved_to_zero = (h != NULL @@ -1961,10 +1915,11 @@ riscv_elf_relocate_section (bfd *output_bfd, break; case R_RISCV_CALL: + case R_RISCV_CALL_PLT: /* Handle a call to an undefined weak function. This won't be relaxed, so we have to handle it here. */ if (h != NULL && h->root.type == bfd_link_hash_undefweak - && h->plt.offset == MINUS_ONE) + && (!bfd_link_pic (info) || h->plt.offset == MINUS_ONE)) { /* We can use x0 as the base register. */ bfd_vma insn = bfd_get_32 (input_bfd, @@ -1977,9 +1932,9 @@ riscv_elf_relocate_section (bfd *output_bfd, } /* Fall through. */ - case R_RISCV_CALL_PLT: case R_RISCV_JAL: case R_RISCV_RVC_JUMP: + /* This line has to match the check in _bfd_riscv_relax_section. */ if (bfd_link_pic (info) && h != NULL && h->plt.offset != MINUS_ONE) { /* Refer to the PLT entry. */ @@ -2063,6 +2018,7 @@ riscv_elf_relocate_section (bfd *output_bfd, || (h != NULL && h->type == STT_SECTION)) && rel->r_addend) { + msg = _("%pcrel_lo section symbol with an addend"); r = bfd_reloc_dangerous; break; } @@ -2277,21 +2233,42 @@ riscv_elf_relocate_section (bfd *output_bfd, && _bfd_elf_section_offset (output_bfd, info, input_section, rel->r_offset) != (bfd_vma) -1) { - (*_bfd_error_handler) - (_("%pB(%pA+%#" PRIx64 "): " - "unresolvable %s relocation against symbol `%s'"), - input_bfd, - input_section, - (uint64_t) rel->r_offset, - howto->name, - h->root.root.string); - continue; + switch (r_type) + { + case R_RISCV_CALL: + case R_RISCV_JAL: + case R_RISCV_RVC_JUMP: + if (asprintf (&msg_buf, + _("%%X%%P: relocation %s against `%s' can " + "not be used when making a shared object; " + "recompile with -fPIC\n"), + howto->name, + h->root.root.string) == -1) + msg_buf = NULL; + break; + + default: + if (asprintf (&msg_buf, + _("%%X%%P: unresolvable %s relocation against " + "symbol `%s'\n"), + howto->name, + h->root.root.string) == -1) + msg_buf = NULL; + break; + } + + msg = msg_buf; + r = bfd_reloc_notsupported; } if (r == bfd_reloc_ok) r = perform_relocation (howto, rel, relocation, input_section, input_bfd, contents); + /* We should have already detected the error and set message before. + If the error message isn't set since the linker runs out of memory + or we don't set it before, then we should set the default message + with the "internal error" string here. */ switch (r) { case bfd_reloc_ok: @@ -2310,17 +2287,21 @@ riscv_elf_relocate_section (bfd *output_bfd, break; case bfd_reloc_outofrange: - msg = _("%X%P: internal error: out of range error\n"); + if (msg == NULL) + msg = _("%X%P: internal error: out of range error\n"); break; case bfd_reloc_notsupported: - msg = _("%X%P: internal error: unsupported relocation error\n"); + if (msg == NULL) + msg = _("%X%P: internal error: unsupported relocation error\n"); break; case bfd_reloc_dangerous: + /* The error message should already be set. */ + if (msg == NULL) + msg = _("dangerous relocation error"); info->callbacks->reloc_dangerous - (info, "%pcrel_lo section symbol with an addend", input_bfd, - input_section, rel->r_offset); + (info, msg, input_bfd, input_section, rel->r_offset); break; default: @@ -2328,9 +2309,13 @@ riscv_elf_relocate_section (bfd *output_bfd, break; } - if (msg) + /* Do not report error message for the dangerous relocation again. */ + if (msg && r != bfd_reloc_dangerous) info->callbacks->einfo (msg); + /* Free the unused `msg_buf`. */ + free (msg_buf); + /* We already reported the error via a callback, so don't try to report it again by returning false. That leads to spurious errors. */ ret = TRUE; @@ -2338,7 +2323,7 @@ riscv_elf_relocate_section (bfd *output_bfd, } ret = riscv_resolve_pcrel_lo_relocs (&pcrel_relocs); -out: + out: riscv_free_pcrel_relocs (&pcrel_relocs); return ret; } @@ -2678,30 +2663,6 @@ riscv_std_ext_p (const char *name) return (strlen (name) == 1) && (name[0] != 'x') && (name[0] != 's'); } -/* Predicator for non-standard extension. */ - -static bfd_boolean -riscv_non_std_ext_p (const char *name) -{ - return (strlen (name) >= 2) && (name[0] == 'x'); -} - -/* Predicator for standard supervisor extension. */ - -static bfd_boolean -riscv_std_sv_ext_p (const char *name) -{ - return (strlen (name) >= 2) && (name[0] == 's') && (name[1] != 'x'); -} - -/* Predicator for non-standard supervisor extension. */ - -static bfd_boolean -riscv_non_std_sv_ext_p (const char *name) -{ - return (strlen (name) >= 3) && (name[0] == 's') && (name[1] == 'x'); -} - /* Error handler when version mis-match. */ static void @@ -2768,7 +2729,7 @@ riscv_merge_std_ext (bfd *ibfd, if (!riscv_i_or_e_p (ibfd, out_arch, out)) return FALSE; - if (in->name[0] != out->name[0]) + if (strcasecmp (in->name, out->name) != 0) { /* TODO: We might allow merge 'i' with 'e'. */ _bfd_error_handler @@ -2827,53 +2788,102 @@ riscv_merge_std_ext (bfd *ibfd, return TRUE; } -/* Merge non-standard and supervisor extensions. - Return Value: - Return FALSE if failed to merge. +/* If C is a prefix class, then return the EXT string without the prefix. + Otherwise return the entire EXT string. */ - Arguments: - `bfd`: bfd handler. - `in_arch`: Raw arch string for input object. - `out_arch`: Raw arch string for output object. - `pin`: subset list for input object, and it'll skip all merged subset after - merge. - `pout`: Like `pin`, but for output object. */ +static const char * +riscv_skip_prefix (const char *ext, riscv_isa_ext_class_t c) +{ + switch (c) + { + case RV_ISA_CLASS_X: return &ext[1]; + case RV_ISA_CLASS_S: return &ext[1]; + case RV_ISA_CLASS_Z: return &ext[1]; + default: return ext; + } +} + +/* Compare prefixed extension names canonically. */ + +static int +riscv_prefix_cmp (const char *a, const char *b) +{ + riscv_isa_ext_class_t ca = riscv_get_prefix_class (a); + riscv_isa_ext_class_t cb = riscv_get_prefix_class (b); + + /* Extension name without prefix */ + const char *anp = riscv_skip_prefix (a, ca); + const char *bnp = riscv_skip_prefix (b, cb); + + if (ca == cb) + return strcasecmp (anp, bnp); + + return (int)ca - (int)cb; +} + +/* Merge multi letter extensions. PIN is a pointer to the head of the input + object subset list. Likewise for POUT and the output object. Return TRUE + on success and FALSE when a conflict is found. */ static bfd_boolean -riscv_merge_non_std_and_sv_ext (bfd *ibfd, - riscv_subset_t **pin, - riscv_subset_t **pout, - bfd_boolean (*predicate_func) (const char *)) +riscv_merge_multi_letter_ext (bfd *ibfd, + riscv_subset_t **pin, + riscv_subset_t **pout) { riscv_subset_t *in = *pin; riscv_subset_t *out = *pout; + riscv_subset_t *tail; - for (in = *pin; in != NULL && predicate_func (in->name); in = in->next) - riscv_add_subset (&merged_subsets, in->name, in->major_version, - in->minor_version); + int cmp; - for (out = *pout; out != NULL && predicate_func (out->name); out = out->next) + while (in && out) { - riscv_subset_t *find_ext = - riscv_lookup_subset (&merged_subsets, out->name); - if (find_ext != NULL) + cmp = riscv_prefix_cmp (in->name, out->name); + + if (cmp < 0) + { + /* `in' comes before `out', append `in' and increment. */ + riscv_add_subset (&merged_subsets, in->name, in->major_version, + in->minor_version); + in = in->next; + } + else if (cmp > 0) { - /* Check version is same or not. */ - /* TODO: Allow different merge policy. */ - if ((find_ext->major_version != out->major_version) - || (find_ext->minor_version != out->minor_version)) + /* `out' comes before `in', append `out' and increment. */ + riscv_add_subset (&merged_subsets, out->name, out->major_version, + out->minor_version); + out = out->next; + } + else + { + /* Both present, check version and increment both. */ + if ((in->major_version != out->major_version) + || (in->minor_version != out->minor_version)) { - riscv_version_mismatch (ibfd, find_ext, out); + riscv_version_mismatch (ibfd, in, out); return FALSE; } + + riscv_add_subset (&merged_subsets, out->name, out->major_version, + out->minor_version); + out = out->next; + in = in->next; } - else - riscv_add_subset (&merged_subsets, out->name, - out->major_version, out->minor_version); } - *pin = in; - *pout = out; + if (in || out) { + /* If we're here, either `in' or `out' is running longer than + the other. So, we need to append the corresponding tail. */ + tail = in ? in : out; + + while (tail) + { + riscv_add_subset (&merged_subsets, tail->name, tail->major_version, + tail->minor_version); + tail = tail->next; + } + } + return TRUE; } @@ -2892,13 +2902,17 @@ riscv_merge_arch_attr_info (bfd *ibfd, char *in_arch, char *out_arch) riscv_parse_subset_t rpe_in; riscv_parse_subset_t rpe_out; + /* Only assembler needs to check the default version of ISA, so just set + the rpe_in.get_default_version and rpe_out.get_default_version to NULL. */ rpe_in.subset_list = &in_subsets; rpe_in.error_handler = _bfd_error_handler; rpe_in.xlen = &xlen_in; + rpe_in.get_default_version = NULL; rpe_out.subset_list = &out_subsets; rpe_out.error_handler = _bfd_error_handler; rpe_out.xlen = &xlen_out; + rpe_out.get_default_version = NULL; if (in_arch == NULL && out_arch == NULL) return NULL; @@ -2932,14 +2946,9 @@ riscv_merge_arch_attr_info (bfd *ibfd, char *in_arch, char *out_arch) /* Merge standard extension. */ if (!riscv_merge_std_ext (ibfd, in_arch, out_arch, &in, &out)) return NULL; - /* Merge non-standard extension. */ - if (!riscv_merge_non_std_and_sv_ext (ibfd, &in, &out, riscv_non_std_ext_p)) - return NULL; - /* Merge standard supervisor extension. */ - if (!riscv_merge_non_std_and_sv_ext (ibfd, &in, &out, riscv_std_sv_ext_p)) - return NULL; - /* Merge non-standard supervisor extension. */ - if (!riscv_merge_non_std_and_sv_ext (ibfd, &in, &out, riscv_non_std_sv_ext_p)) + + /* Merge all non-single letter extensions with single call. */ + if (!riscv_merge_multi_letter_ext (ibfd, &in, &out)) return NULL; if (xlen_in != xlen_out) @@ -3131,7 +3140,7 @@ _bfd_riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info) for (sec = ibfd->sections; sec != NULL; sec = sec->next) { - if ((bfd_get_section_flags (ibfd, sec) + if ((bfd_section_flags (sec) & (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS)) == (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS)) only_data_sections = FALSE; @@ -3167,7 +3176,7 @@ _bfd_riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info) return TRUE; -fail: + fail: bfd_set_error (bfd_error_bad_value); return FALSE; } @@ -3291,6 +3300,7 @@ struct riscv_pcgp_hi_reloc bfd_vma hi_addr; unsigned hi_sym; asection *sym_sec; + bfd_boolean undefined_weak; riscv_pcgp_hi_reloc *next; }; @@ -3349,7 +3359,8 @@ riscv_free_pcgp_relocs (riscv_pcgp_relocs *p, static bfd_boolean riscv_record_pcgp_hi_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off, bfd_vma hi_addend, bfd_vma hi_addr, - unsigned hi_sym, asection *sym_sec) + unsigned hi_sym, asection *sym_sec, + bfd_boolean undefined_weak) { riscv_pcgp_hi_reloc *new = bfd_malloc (sizeof(*new)); if (!new) @@ -3359,6 +3370,7 @@ riscv_record_pcgp_hi_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off, new->hi_addr = hi_addr; new->hi_sym = hi_sym; new->sym_sec = sym_sec; + new->undefined_weak = undefined_weak; new->next = p->hi; p->hi = new; return TRUE; @@ -3411,7 +3423,8 @@ typedef bfd_boolean (*relax_func_t) (bfd *, asection *, asection *, struct bfd_link_info *, Elf_Internal_Rela *, bfd_vma, bfd_vma, bfd_vma, bfd_boolean *, - riscv_pcgp_relocs *); + riscv_pcgp_relocs *, + bfd_boolean undefined_weak); /* Relax AUIPC + JALR into JAL. */ @@ -3423,7 +3436,8 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec, bfd_vma max_alignment, bfd_vma reserve_size ATTRIBUTE_UNUSED, bfd_boolean *again, - riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED) + riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED, + bfd_boolean undefined_weak ATTRIBUTE_UNUSED) { bfd_byte *contents = elf_section_data (sec)->this_hdr.contents; bfd_signed_vma foff = symval - (sec_addr (sec) + rel->r_offset); @@ -3432,9 +3446,16 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec, int rd, r_type, len = 4, rvc = elf_elfheader (abfd)->e_flags & EF_RISCV_RVC; /* If the call crosses section boundaries, an alignment directive could - cause the PC-relative offset to later increase. */ - if (VALID_UJTYPE_IMM (foff) && sym_sec->output_section != sec->output_section) - foff += (foff < 0 ? -max_alignment : max_alignment); + cause the PC-relative offset to later increase, so we need to add in the + max alignment of any section inclusive from the call to the target. + Otherwise, we only need to use the alignment of the current section. */ + if (VALID_UJTYPE_IMM (foff)) + { + if (sym_sec->output_section == sec->output_section + && sym_sec->output_section != bfd_abs_section_ptr) + max_alignment = (bfd_vma) 1 << sym_sec->output_section->alignment_power; + foff += (foff < 0 ? -max_alignment : max_alignment); + } /* See if this function call can be shortened. */ if (!VALID_UJTYPE_IMM (foff) && !(!bfd_link_pic (link_info) && near_zero)) @@ -3511,7 +3532,8 @@ _bfd_riscv_relax_lui (bfd *abfd, bfd_vma max_alignment, bfd_vma reserve_size, bfd_boolean *again, - riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED) + riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED, + bfd_boolean undefined_weak) { bfd_byte *contents = elf_section_data (sec)->this_hdr.contents; bfd_vma gp = riscv_global_pointer_value (link_info); @@ -3521,32 +3543,50 @@ _bfd_riscv_relax_lui (bfd *abfd, if (gp) { - /* If gp and the symbol are in the same output section, then - consider only that section's alignment. */ + /* If gp and the symbol are in the same output section, which is not the + abs section, then consider only that output section's alignment. */ struct bfd_link_hash_entry *h = bfd_link_hash_lookup (link_info->hash, RISCV_GP_SYMBOL, FALSE, FALSE, TRUE); - if (h->u.def.section->output_section == sym_sec->output_section) + if (h->u.def.section->output_section == sym_sec->output_section + && sym_sec->output_section != bfd_abs_section_ptr) max_alignment = (bfd_vma) 1 << sym_sec->output_section->alignment_power; } /* Is the reference in range of x0 or gp? Valid gp range conservatively because of alignment issue. */ - if (VALID_ITYPE_IMM (symval) - || (symval >= gp - && VALID_ITYPE_IMM (symval - gp + max_alignment + reserve_size)) - || (symval < gp - && VALID_ITYPE_IMM (symval - gp - max_alignment - reserve_size))) + if (undefined_weak + || (VALID_ITYPE_IMM (symval) + || (symval >= gp + && VALID_ITYPE_IMM (symval - gp + max_alignment + reserve_size)) + || (symval < gp + && VALID_ITYPE_IMM (symval - gp - max_alignment - reserve_size)))) { unsigned sym = ELFNN_R_SYM (rel->r_info); switch (ELFNN_R_TYPE (rel->r_info)) { case R_RISCV_LO12_I: - rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_I); + if (undefined_weak) + { + /* Change the RS1 to zero. */ + bfd_vma insn = bfd_get_32 (abfd, contents + rel->r_offset); + insn &= ~(OP_MASK_RS1 << OP_SH_RS1); + bfd_put_32 (abfd, insn, contents + rel->r_offset); + } + else + rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_I); return TRUE; case R_RISCV_LO12_S: - rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_S); + if (undefined_weak) + { + /* Change the RS1 to zero. */ + bfd_vma insn = bfd_get_32 (abfd, contents + rel->r_offset); + insn &= ~(OP_MASK_RS1 << OP_SH_RS1); + bfd_put_32 (abfd, insn, contents + rel->r_offset); + } + else + rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_S); return TRUE; case R_RISCV_HI20: @@ -3605,7 +3645,8 @@ _bfd_riscv_relax_tls_le (bfd *abfd, bfd_vma max_alignment ATTRIBUTE_UNUSED, bfd_vma reserve_size ATTRIBUTE_UNUSED, bfd_boolean *again, - riscv_pcgp_relocs *prcel_relocs ATTRIBUTE_UNUSED) + riscv_pcgp_relocs *prcel_relocs ATTRIBUTE_UNUSED, + bfd_boolean undefined_weak ATTRIBUTE_UNUSED) { /* See if this symbol is in range of tp. */ if (RISCV_CONST_HIGH_PART (tpoff (link_info, symval)) != 0) @@ -3645,7 +3686,8 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec, bfd_vma max_alignment ATTRIBUTE_UNUSED, bfd_vma reserve_size ATTRIBUTE_UNUSED, bfd_boolean *again ATTRIBUTE_UNUSED, - riscv_pcgp_relocs *pcrel_relocs ATTRIBUTE_UNUSED) + riscv_pcgp_relocs *pcrel_relocs ATTRIBUTE_UNUSED, + bfd_boolean undefined_weak ATTRIBUTE_UNUSED) { bfd_byte *contents = elf_section_data (sec)->this_hdr.contents; bfd_vma alignment = 1, pos; @@ -3703,8 +3745,10 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED, bfd_vma max_alignment, bfd_vma reserve_size, bfd_boolean *again ATTRIBUTE_UNUSED, - riscv_pcgp_relocs *pcgp_relocs) + riscv_pcgp_relocs *pcgp_relocs, + bfd_boolean undefined_weak) { + bfd_byte *contents = elf_section_data (sec)->this_hdr.contents; bfd_vma gp = riscv_global_pointer_value (link_info); BFD_ASSERT (rel->r_offset + 4 <= sec->size); @@ -3734,12 +3778,19 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED, hi_reloc = *hi; symval = hi_reloc.hi_addr; sym_sec = hi_reloc.sym_sec; + + /* We can not know whether the undefined weak symbol is referenced + according to the information of R_RISCV_PCREL_LO12_I/S. Therefore, + we have to record the 'undefined_weak' flag when handling the + corresponding R_RISCV_HI20 reloc in riscv_record_pcgp_hi_reloc. */ + undefined_weak = hi_reloc.undefined_weak; } break; case R_RISCV_PCREL_HI20: /* Mergeable symbols and code might later move out of range. */ - if (sym_sec->flags & (SEC_MERGE | SEC_CODE)) + if (! undefined_weak + && sym_sec->flags & (SEC_MERGE | SEC_CODE)) return TRUE; /* If the cooresponding lo relocation has already been seen then it's not @@ -3755,33 +3806,62 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED, if (gp) { - /* If gp and the symbol are in the same output section, then - consider only that section's alignment. */ + /* If gp and the symbol are in the same output section, which is not the + abs section, then consider only that output section's alignment. */ struct bfd_link_hash_entry *h = - bfd_link_hash_lookup (link_info->hash, RISCV_GP_SYMBOL, FALSE, FALSE, TRUE); - if (h->u.def.section->output_section == sym_sec->output_section) + bfd_link_hash_lookup (link_info->hash, RISCV_GP_SYMBOL, FALSE, FALSE, + TRUE); + if (h->u.def.section->output_section == sym_sec->output_section + && sym_sec->output_section != bfd_abs_section_ptr) max_alignment = (bfd_vma) 1 << sym_sec->output_section->alignment_power; } /* Is the reference in range of x0 or gp? Valid gp range conservatively because of alignment issue. */ - if (VALID_ITYPE_IMM (symval) - || (symval >= gp - && VALID_ITYPE_IMM (symval - gp + max_alignment + reserve_size)) - || (symval < gp - && VALID_ITYPE_IMM (symval - gp - max_alignment - reserve_size))) + if (undefined_weak + || (VALID_ITYPE_IMM (symval) + || (symval >= gp + && VALID_ITYPE_IMM (symval - gp + max_alignment + reserve_size)) + || (symval < gp + && VALID_ITYPE_IMM (symval - gp - max_alignment - reserve_size)))) { unsigned sym = hi_reloc.hi_sym; switch (ELFNN_R_TYPE (rel->r_info)) { case R_RISCV_PCREL_LO12_I: - rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_I); - rel->r_addend += hi_reloc.hi_addend; + if (undefined_weak) + { + /* Change the RS1 to zero, and then modify the relocation + type to R_RISCV_LO12_I. */ + bfd_vma insn = bfd_get_32 (abfd, contents + rel->r_offset); + insn &= ~(OP_MASK_RS1 << OP_SH_RS1); + bfd_put_32 (abfd, insn, contents + rel->r_offset); + rel->r_info = ELFNN_R_INFO (sym, R_RISCV_LO12_I); + rel->r_addend = hi_reloc.hi_addend; + } + else + { + rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_I); + rel->r_addend += hi_reloc.hi_addend; + } return TRUE; case R_RISCV_PCREL_LO12_S: - rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_S); - rel->r_addend += hi_reloc.hi_addend; + if (undefined_weak) + { + /* Change the RS1 to zero, and then modify the relocation + type to R_RISCV_LO12_S. */ + bfd_vma insn = bfd_get_32 (abfd, contents + rel->r_offset); + insn &= ~(OP_MASK_RS1 << OP_SH_RS1); + bfd_put_32 (abfd, insn, contents + rel->r_offset); + rel->r_info = ELFNN_R_INFO (sym, R_RISCV_LO12_S); + rel->r_addend = hi_reloc.hi_addend; + } + else + { + rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_S); + rel->r_addend += hi_reloc.hi_addend; + } return TRUE; case R_RISCV_PCREL_HI20: @@ -3790,7 +3870,8 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED, rel->r_addend, symval, ELFNN_R_SYM(rel->r_info), - sym_sec); + sym_sec, + undefined_weak); /* We can delete the unnecessary AUIPC and reloc. */ rel->r_info = ELFNN_R_INFO (0, R_RISCV_DELETE); rel->r_addend = 4; @@ -3816,7 +3897,8 @@ _bfd_riscv_relax_delete (bfd *abfd, bfd_vma max_alignment ATTRIBUTE_UNUSED, bfd_vma reserve_size ATTRIBUTE_UNUSED, bfd_boolean *again ATTRIBUTE_UNUSED, - riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED) + riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED, + bfd_boolean undefined_weak ATTRIBUTE_UNUSED) { if (!riscv_relax_delete_bytes(abfd, sec, rel->r_offset, rel->r_addend, link_info)) @@ -3883,6 +3965,7 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec, int type = ELFNN_R_TYPE (rel->r_info); bfd_vma symval; char symtype; + bfd_boolean undefined_weak = FALSE; relax_func = NULL; if (info->relax_pass == 0) @@ -3977,20 +4060,48 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec, || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; - if (h->plt.offset != MINUS_ONE) + if (h->root.type == bfd_link_hash_undefweak + && (relax_func == _bfd_riscv_relax_lui + || relax_func == _bfd_riscv_relax_pc)) + { + /* For the lui and auipc relaxations, since the symbol + value of an undefined weak symbol is always be zero, + we can optimize the patterns into a single LI/MV/ADDI + instruction. + + Note that, creating shared libraries and pie output may + break the rule above. Fortunately, since we do not relax + pc relocs when creating shared libraries and pie output, + and the absolute address access for R_RISCV_HI20 isn't + allowed when "-fPIC" is set, the problem of creating shared + libraries can not happen currently. Once we support the + auipc relaxations when creating shared libraries, then we will + need the more rigorous checking for this optimization. */ + undefined_weak = TRUE; + } + + /* This line has to match the check in riscv_elf_relocate_section + in the R_RISCV_CALL[_PLT] case. */ + if (bfd_link_pic (info) && h->plt.offset != MINUS_ONE) { sym_sec = htab->elf.splt; symval = h->plt.offset; } - else if (h->root.u.def.section->output_section == NULL - || (h->root.type != bfd_link_hash_defined - && h->root.type != bfd_link_hash_defweak)) - continue; - else + else if (undefined_weak) + { + symval = 0; + sym_sec = bfd_und_section_ptr; + } + else if ((h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + && h->root.u.def.section != NULL + && h->root.u.def.section->output_section != NULL) { symval = h->root.u.def.value; sym_sec = h->root.u.def.section; } + else + continue; if (h->type != STT_FUNC) reserve_size = @@ -4034,13 +4145,13 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec, if (!relax_func (abfd, sec, sym_sec, info, rel, symval, max_alignment, reserve_size, again, - &pcgp_relocs)) + &pcgp_relocs, undefined_weak)) goto fail; } ret = TRUE; -fail: + fail: if (relocs != data->relocs) free (relocs); riscv_free_pcgp_relocs(&pcgp_relocs, abfd, sec); @@ -4183,6 +4294,7 @@ riscv_elf_obj_attrs_arg_type (int tag) #define elf_info_to_howto_rel NULL #define elf_info_to_howto riscv_info_to_howto_rela #define bfd_elfNN_bfd_relax_section _bfd_riscv_relax_section +#define bfd_elfNN_mkobject elfNN_riscv_mkobject #define elf_backend_init_index_section _bfd_elf_init_1_index_section