/* 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.
{
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
&& 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"
struct riscv_elf_link_hash_entry *eh;
eh = (struct riscv_elf_link_hash_entry *) entry;
- eh->dyn_relocs = NULL;
eh->tls_type = GOT_UNKNOWN;
}
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)
(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;
{
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;
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));
}
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)
{
/* 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.
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)
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
/* 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;
{
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)
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
{
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;
/* 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. */
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);
if (h->root.type == bfd_link_hash_indirect)
return TRUE;
- sec = readonly_dynrelocs (h);
+ sec = _bfd_elf_readonly_dynrelocs (h);
if (sec != NULL)
{
struct bfd_link_info *info = (struct bfd_link_info *) info_p;
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
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
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,
}
/* 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. */
|| (h != NULL && h->type == STT_SECTION))
&& rel->r_addend)
{
+ msg = _("%pcrel_lo section symbol with an addend");
r = bfd_reloc_dangerous;
break;
}
&& _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:
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:
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;
}
ret = riscv_resolve_pcrel_lo_relocs (&pcrel_relocs);
-out:
+ out:
riscv_free_pcrel_relocs (&pcrel_relocs);
return ret;
}
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
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
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)
+ {
+ /* `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
{
- /* 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))
+ /* 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;
}
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;
/* 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)
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;
return TRUE;
-fail:
+ fail:
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
bfd_vma hi_addr;
unsigned hi_sym;
asection *sym_sec;
+ bfd_boolean undefined_weak;
riscv_pcgp_hi_reloc *next;
};
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)
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;
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. */
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);
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))
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);
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:
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)
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;
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);
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
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:
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;
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))
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)
|| 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 =
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);
#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