/* RISC-V-specific support for NN-bit ELF.
- Copyright (C) 2011-2018 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.
(elf_hash_table_id ((struct elf_link_hash_table *) ((p)->hash)) \
== RISCV_ELF_DATA ? ((struct riscv_elf_link_hash_table *) ((p)->hash)) : NULL)
-static void
-riscv_info_to_howto_rela (bfd *abfd ATTRIBUTE_UNUSED,
+static bfd_boolean
+riscv_info_to_howto_rela (bfd *abfd,
arelent *cache_ptr,
Elf_Internal_Rela *dst)
{
- cache_ptr->howto = riscv_elf_rtype_to_howto (ELFNN_R_TYPE (dst->r_info));
+ cache_ptr->howto = riscv_elf_rtype_to_howto (abfd, ELFNN_R_TYPE (dst->r_info));
+ return cache_ptr->howto != NULL;
}
static void
/* Generate a PLT header. */
-static void
-riscv_make_plt_header (bfd_vma gotplt_addr, bfd_vma addr, uint32_t *entry)
+static bfd_boolean
+riscv_make_plt_header (bfd *output_bfd, bfd_vma gotplt_addr, bfd_vma addr,
+ uint32_t *entry)
{
bfd_vma gotplt_offset_high = RISCV_PCREL_HIGH_PART (gotplt_addr, addr);
bfd_vma gotplt_offset_low = RISCV_PCREL_LOW_PART (gotplt_addr, addr);
+ /* RVE has no t3 register, so this won't work, and is not supported. */
+ if (elf_elfheader (output_bfd)->e_flags & EF_RISCV_RVE)
+ {
+ _bfd_error_handler (_("%pB: warning: RVE PLT generation not supported"),
+ output_bfd);
+ return FALSE;
+ }
+
/* auipc t2, %hi(.got.plt)
sub t1, t1, t3 # shifted .got.plt offset + hdr size + 12
l[w|d] t3, %lo(.got.plt)(t2) # _dl_runtime_resolve
entry[5] = RISCV_ITYPE (SRLI, X_T1, X_T1, 4 - RISCV_ELF_LOG_WORD_BYTES);
entry[6] = RISCV_ITYPE (LREG, X_T0, X_T0, RISCV_ELF_WORD_BYTES);
entry[7] = RISCV_ITYPE (JALR, 0, X_T3, 0);
+
+ return TRUE;
}
/* Generate a PLT entry. */
-static void
-riscv_make_plt_entry (bfd_vma got, bfd_vma addr, uint32_t *entry)
+static bfd_boolean
+riscv_make_plt_entry (bfd *output_bfd, bfd_vma got, bfd_vma addr,
+ uint32_t *entry)
{
+ /* RVE has no t3 register, so this won't work, and is not supported. */
+ if (elf_elfheader (output_bfd)->e_flags & EF_RISCV_RVE)
+ {
+ _bfd_error_handler (_("%pB: warning: RVE PLT generation not supported"),
+ output_bfd);
+ return FALSE;
+ }
+
/* auipc t3, %hi(.got.plt entry)
l[w|d] t3, %lo(.got.plt entry)(t3)
jalr t1, t3
entry[1] = RISCV_ITYPE (LREG, X_T3, X_T3, RISCV_PCREL_LOW_PART (got, addr));
entry[2] = RISCV_ITYPE (JALR, X_T1, X_T3, 0);
entry[3] = RISCV_NOP;
+
+ return TRUE;
}
/* Create an entry in an RISC-V ELF linker 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)
(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_ALLOC | SEC_THREAD_LOCAL
+ | SEC_LOAD | SEC_DATA
+ | SEC_HAS_CONTENTS
+ | SEC_LINKER_CREATED));
}
if (!htab->elf.splt || !htab->elf.srelplt || !htab->elf.sdynbss
static bfd_boolean
bad_static_reloc (bfd *abfd, unsigned r_type, struct elf_link_hash_entry *h)
{
+ reloc_howto_type * r = riscv_elf_rtype_to_howto (abfd, r_type);
+
(*_bfd_error_handler)
(_("%pB: relocation %s against `%s' can not be used when making a shared "
"object; recompile with -fPIC"),
- abfd, riscv_elf_rtype_to_howto (r_type)->name,
- h != NULL ? h->root.root.string : "a local symbol");
+ abfd, r ? r->name : _("<unknown>"),
+ h != NULL ? h->root.root.string : "a local symbol");
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
may need to keep relocations for symbols satisfied by a
dynamic library if we manage to avoid copy relocs for the
symbol. */
+ reloc_howto_type * r = riscv_elf_rtype_to_howto (abfd, r_type);
+
if ((bfd_link_pic (info)
&& (sec->flags & SEC_ALLOC) != 0
- && (! riscv_elf_rtype_to_howto (r_type)->pc_relative
+ && ((r != NULL && ! r->pc_relative)
|| (h != NULL
&& (! info->symbolic
|| h->root.type == bfd_link_hash_defweak
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)
}
p->count += 1;
- p->pc_count += riscv_elf_rtype_to_howto (r_type)->pc_relative;
+ p->pc_count += r == NULL ? 0 : r->pc_relative;
}
break;
else
{
s->size += RISCV_ELF_WORD_BYTES;
- if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, bfd_link_pic (info), h))
+ if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, bfd_link_pic (info), h)
+ && ! UNDEFWEAK_NO_DYNAMIC_RELOC (info, h))
htab->elf.srelgot->size += sizeof (ElfNN_External_Rela);
}
}
if (eh->dyn_relocs != NULL
&& h->root.type == bfd_link_hash_undefweak)
{
- if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
+ if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
+ || UNDEFWEAK_NO_DYNAMIC_RELOC (info, h))
eh->dyn_relocs = NULL;
/* Make sure undefined weak symbols are output as a dynamic
|| s == htab->elf.sgot
|| s == htab->elf.sgotplt
|| s == htab->elf.sdynbss
- || s == htab->elf.sdynrelro)
+ || s == htab->elf.sdynrelro
+ || s == htab->sdyntdata)
{
/* Strip this section if we don't need it; see the
comment below. */
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:
riscv_pcrel_hi_reloc search = {r->addr, 0};
riscv_pcrel_hi_reloc *entry = htab_find (p->hi_relocs, &search);
- if (entry == NULL)
+ if (entry == NULL
+ /* Check for overflow into bit 11 when adding reloc addend. */
+ || (! (entry->value & 0x800)
+ && ((entry->value + r->reloc->r_addend) & 0x800)))
{
- ((*r->info->callbacks->reloc_overflow)
- (r->info, NULL, r->name, r->howto->name, (bfd_vma) 0,
- input_bfd, r->input_section, r->reloc->r_offset));
+ char *string = (entry == NULL
+ ? "%pcrel_lo missing matching %pcrel_hi"
+ : "%pcrel_lo overflow with an addend");
+ (*r->info->callbacks->reloc_dangerous)
+ (r->info, string, input_bfd, r->input_section, r->reloc->r_offset);
return TRUE;
}
bfd_boolean unresolved_reloc, is_ie = FALSE;
bfd_vma pc = sec_addr (input_section) + rel->r_offset;
int r_type = ELFNN_R_TYPE (rel->r_info), tls_type;
- reloc_howto_type *howto = riscv_elf_rtype_to_howto (r_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 (r_type == R_RISCV_GNU_VTINHERIT || r_type == R_RISCV_GNU_VTENTRY)
+ if (howto == NULL
+ || r_type == R_RISCV_GNU_VTINHERIT || r_type == R_RISCV_GNU_VTENTRY)
continue;
/* This is a final link. */
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
+ && UNDEFWEAK_NO_DYNAMIC_RELOC (info, h));
+
switch (r_type)
{
case R_RISCV_NONE:
howto,
input_bfd);
r_type = ELFNN_R_TYPE (rel->r_info);
- howto = riscv_elf_rtype_to_howto (r_type);
- if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
- relocation, absolute))
+ howto = riscv_elf_rtype_to_howto (input_bfd, r_type);
+ if (howto == NULL)
+ r = bfd_reloc_notsupported;
+ else if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
+ relocation, absolute))
r = bfd_reloc_overflow;
break;
}
break;
- case R_RISCV_CALL_PLT:
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
+ && (!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,
+ contents + rel->r_offset + 4);
+ insn &= ~(OP_MASK_RS1 << OP_SH_RS1);
+ bfd_put_32 (input_bfd, insn, contents + rel->r_offset + 4);
+ /* Set the relocation value so that we get 0 after the pc
+ relative adjustment. */
+ relocation = sec_addr (input_section) + rel->r_offset;
+ }
+ /* Fall through. */
+
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. */
howto,
input_bfd);
r_type = ELFNN_R_TYPE (rel->r_info);
- howto = riscv_elf_rtype_to_howto (r_type);
- if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
- relocation + rel->r_addend,
- absolute))
+ howto = riscv_elf_rtype_to_howto (input_bfd, r_type);
+ if (howto == NULL)
+ r = bfd_reloc_notsupported;
+ else if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
+ relocation + rel->r_addend,
+ absolute))
r = bfd_reloc_overflow;
break;
case R_RISCV_PCREL_LO12_I:
case R_RISCV_PCREL_LO12_S:
- /* Addends are not allowed, because then riscv_relax_delete_bytes
- would have to search through all relocs to update the addends.
- Also, riscv_resolve_pcrel_lo_relocs does not support addends
- when searching for a matching hi reloc. */
- if (rel->r_addend)
+ /* We don't allow section symbols plus addends as the auipc address,
+ because then riscv_relax_delete_bytes would have to search through
+ all relocs to update these addends. This is also ambiguous, as
+ we do allow offsets to be added to the target address, which are
+ not to be used to find the auipc address. */
+ if (((sym != NULL && (ELF_ST_TYPE (sym->st_info) == STT_SECTION))
+ || (h != NULL && h->type == STT_SECTION))
+ && rel->r_addend)
{
+ msg = _("%pcrel_lo section symbol with an addend");
r = bfd_reloc_dangerous;
break;
}
if ((bfd_link_pic (info)
&& (h == NULL
- || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+ || (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+ && !resolved_to_zero)
|| h->root.type != bfd_link_hash_undefweak)
&& (! howto->pc_relative
|| !SYMBOL_CALLS_LOCAL (info, h)))
&& _bfd_elf_section_offset (output_bfd, info, input_section,
rel->r_offset) != (bfd_vma) -1)
{
- (*_bfd_error_handler)
- (_("%pB(%pA+%#Lx): unresolvable %s relocation against symbol `%s'"),
- input_bfd,
- input_section,
- 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 with 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` if needed. */
+ if (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;
}
loc = htab->elf.splt->contents + h->plt.offset;
/* Fill in the PLT entry itself. */
- riscv_make_plt_entry (got_address, header_address + h->plt.offset,
- plt_entry);
+ if (! riscv_make_plt_entry (output_bfd, got_address,
+ header_address + h->plt.offset,
+ plt_entry))
+ return FALSE;
+
for (i = 0; i < PLT_ENTRY_INSNS; i++)
bfd_put_32 (output_bfd, plt_entry[i], loc + 4*i);
}
if (h->got.offset != (bfd_vma) -1
- && !(riscv_elf_hash_entry (h)->tls_type & (GOT_TLS_GD | GOT_TLS_IE)))
+ && !(riscv_elf_hash_entry (h)->tls_type & (GOT_TLS_GD | GOT_TLS_IE))
+ && !UNDEFWEAK_NO_DYNAMIC_RELOC (info, h))
{
asection *sgot;
asection *srela;
rela.r_offset = sec_addr (sgot) + (h->got.offset &~ (bfd_vma) 1);
- /* If this is a -Bsymbolic link, and the symbol is defined
- locally, we just want to emit a RELATIVE reloc. Likewise if
+ /* If this is a local symbol reference, we just want to emit a RELATIVE
+ reloc. This can happen if it is a -Bsymbolic link, or a pie link, or
the symbol was forced to be local because of a version file.
The entry in the global offset table will already have been
initialized in the relocate_section function. */
if (bfd_link_pic (info)
- && (info->symbolic || h->dynindx == -1)
- && h->def_regular)
+ && SYMBOL_REFERENCES_LOCAL (info, h))
{
+ BFD_ASSERT((h->got.offset & 1) != 0);
asection *sec = h->root.u.def.section;
rela.r_info = ELFNN_R_INFO (0, R_RISCV_RELATIVE);
rela.r_addend = (h->root.u.def.value
}
else
{
+ BFD_ASSERT((h->got.offset & 1) == 0);
BFD_ASSERT (h->dynindx != -1);
rela.r_info = ELFNN_R_INFO (h->dynindx, R_RISCV_NN);
rela.r_addend = 0;
{
int i;
uint32_t plt_header[PLT_HEADER_INSNS];
- riscv_make_plt_header (sec_addr (htab->elf.sgotplt),
- sec_addr (splt), plt_header);
+ ret = riscv_make_plt_header (output_bfd,
+ sec_addr (htab->elf.sgotplt),
+ sec_addr (splt), plt_header);
+ if (!ret)
+ return ret;
for (i = 0; i < PLT_HEADER_INSNS; i++)
bfd_put_32 (output_bfd, plt_header[i], splt->contents + 4*i);
}
}
+/* Given the ELF header flags in FLAGS, it returns a string that describes the
+ float ABI. */
+
+static const char *
+riscv_float_abi_string (flagword flags)
+{
+ switch (flags & EF_RISCV_FLOAT_ABI)
+ {
+ case EF_RISCV_FLOAT_ABI_SOFT:
+ return "soft-float";
+ break;
+ case EF_RISCV_FLOAT_ABI_SINGLE:
+ return "single-float";
+ break;
+ case EF_RISCV_FLOAT_ABI_DOUBLE:
+ return "double-float";
+ break;
+ case EF_RISCV_FLOAT_ABI_QUAD:
+ return "quad-float";
+ break;
+ default:
+ abort ();
+ }
+}
+
+/* The information of architecture attribute. */
+static riscv_subset_list_t in_subsets;
+static riscv_subset_list_t out_subsets;
+static riscv_subset_list_t merged_subsets;
+
+/* Predicator for standard extension. */
+
+static bfd_boolean
+riscv_std_ext_p (const char *name)
+{
+ return (strlen (name) == 1) && (name[0] != 'x') && (name[0] != 's');
+}
+
+/* Error handler when version mis-match. */
+
+static void
+riscv_version_mismatch (bfd *ibfd,
+ struct riscv_subset_t *in,
+ struct riscv_subset_t *out)
+{
+ _bfd_error_handler
+ (_("error: %pB: Mis-matched ISA version for '%s' extension. "
+ "%d.%d vs %d.%d"),
+ ibfd, in->name,
+ in->major_version, in->minor_version,
+ out->major_version, out->minor_version);
+}
+
+/* Return true if subset is 'i' or 'e'. */
+
+static bfd_boolean
+riscv_i_or_e_p (bfd *ibfd,
+ const char *arch,
+ struct riscv_subset_t *subset)
+{
+ if ((strcasecmp (subset->name, "e") != 0)
+ && (strcasecmp (subset->name, "i") != 0))
+ {
+ _bfd_error_handler
+ (_("error: %pB: corrupted ISA string '%s'. "
+ "First letter should be 'i' or 'e' but got '%s'."),
+ ibfd, arch, subset->name);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Merge standard extensions.
+
+ Return Value:
+ Return FALSE if failed to merge.
+
+ 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 bfd_boolean
+riscv_merge_std_ext (bfd *ibfd,
+ const char *in_arch,
+ const char *out_arch,
+ struct riscv_subset_t **pin,
+ struct riscv_subset_t **pout)
+{
+ const char *standard_exts = riscv_supported_std_ext ();
+ const char *p;
+ struct riscv_subset_t *in = *pin;
+ struct riscv_subset_t *out = *pout;
+
+ /* First letter should be 'i' or 'e'. */
+ if (!riscv_i_or_e_p (ibfd, in_arch, in))
+ return FALSE;
+
+ if (!riscv_i_or_e_p (ibfd, out_arch, out))
+ return FALSE;
+
+ if (in->name[0] != out->name[0])
+ {
+ /* TODO: We might allow merge 'i' with 'e'. */
+ _bfd_error_handler
+ (_("error: %pB: Mis-matched ISA string to merge '%s' and '%s'."),
+ ibfd, in->name, out->name);
+ return FALSE;
+ }
+ else if ((in->major_version != out->major_version) ||
+ (in->minor_version != out->minor_version))
+ {
+ /* TODO: Allow different merge policy. */
+ riscv_version_mismatch (ibfd, in, out);
+ return FALSE;
+ }
+ else
+ riscv_add_subset (&merged_subsets,
+ in->name, in->major_version, in->minor_version);
+
+ in = in->next;
+ out = out->next;
+
+ /* Handle standard extension first. */
+ for (p = standard_exts; *p; ++p)
+ {
+ char find_ext[2] = {*p, '\0'};
+ struct riscv_subset_t *find_in =
+ riscv_lookup_subset (&in_subsets, find_ext);
+ struct riscv_subset_t *find_out =
+ riscv_lookup_subset (&out_subsets, find_ext);
+
+ if (find_in == NULL && find_out == NULL)
+ continue;
+
+ /* Check version is same or not. */
+ /* TODO: Allow different merge policy. */
+ if ((find_in != NULL && find_out != NULL)
+ && ((find_in->major_version != find_out->major_version)
+ || (find_in->minor_version != find_out->minor_version)))
+ {
+ riscv_version_mismatch (ibfd, in, out);
+ return FALSE;
+ }
+
+ struct riscv_subset_t *merged = find_in ? find_in : find_out;
+ riscv_add_subset (&merged_subsets, merged->name,
+ merged->major_version, merged->minor_version);
+ }
+
+ /* Skip all standard extensions. */
+ while ((in != NULL) && riscv_std_ext_p (in->name)) in = in->next;
+ while ((out != NULL) && riscv_std_ext_p (out->name)) out = out->next;
+
+ *pin = in;
+ *pout = out;
+
+ return TRUE;
+}
+
+/* If C is a prefix class, then return the EXT string without the prefix.
+ Otherwise return the entire EXT string. */
+
+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_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;
+
+ int cmp;
+
+ while (in && out)
+ {
+ 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
+ {
+ /* Both present, check version and increment both. */
+ if ((in->major_version != out->major_version)
+ || (in->minor_version != out->minor_version))
+ {
+ 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;
+ }
+ }
+
+ 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;
+}
+
+/* Merge Tag_RISCV_arch attribute. */
+
+static char *
+riscv_merge_arch_attr_info (bfd *ibfd, char *in_arch, char *out_arch)
+{
+ riscv_subset_t *in, *out;
+ char *merged_arch_str;
+
+ unsigned xlen_in, xlen_out;
+ merged_subsets.head = NULL;
+ merged_subsets.tail = NULL;
+
+ riscv_parse_subset_t rpe_in;
+ riscv_parse_subset_t rpe_out;
+
+ rpe_in.subset_list = &in_subsets;
+ rpe_in.error_handler = _bfd_error_handler;
+ rpe_in.xlen = &xlen_in;
+
+ rpe_out.subset_list = &out_subsets;
+ rpe_out.error_handler = _bfd_error_handler;
+ rpe_out.xlen = &xlen_out;
+
+ if (in_arch == NULL && out_arch == NULL)
+ return NULL;
+
+ if (in_arch == NULL && out_arch != NULL)
+ return out_arch;
+
+ if (in_arch != NULL && out_arch == NULL)
+ return in_arch;
+
+ /* Parse subset from arch string. */
+ if (!riscv_parse_subset (&rpe_in, in_arch))
+ return NULL;
+
+ if (!riscv_parse_subset (&rpe_out, out_arch))
+ return NULL;
+
+ /* Checking XLEN. */
+ if (xlen_out != xlen_in)
+ {
+ _bfd_error_handler
+ (_("error: %pB: ISA string of input (%s) doesn't match "
+ "output (%s)."), ibfd, in_arch, out_arch);
+ return NULL;
+ }
+
+ /* Merge subset list. */
+ in = in_subsets.head;
+ out = out_subsets.head;
+
+ /* Merge standard extension. */
+ if (!riscv_merge_std_ext (ibfd, in_arch, out_arch, &in, &out))
+ return NULL;
+
+ /* 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)
+ {
+ _bfd_error_handler
+ (_("error: %pB: XLEN of input (%u) doesn't match "
+ "output (%u)."), ibfd, xlen_in, xlen_out);
+ return NULL;
+ }
+
+ if (xlen_in != ARCH_SIZE)
+ {
+ _bfd_error_handler
+ (_("error: %pB: Unsupported XLEN (%u), you might be "
+ "using wrong emulation."), ibfd, xlen_in);
+ return NULL;
+ }
+
+ merged_arch_str = riscv_arch_str (ARCH_SIZE, &merged_subsets);
+
+ /* Release the subset lists. */
+ riscv_release_subset_list (&in_subsets);
+ riscv_release_subset_list (&out_subsets);
+ riscv_release_subset_list (&merged_subsets);
+
+ return merged_arch_str;
+}
+
+/* Merge object attributes from IBFD into output_bfd of INFO.
+ Raise an error if there are conflicting attributes. */
+
+static bfd_boolean
+riscv_merge_attributes (bfd *ibfd, struct bfd_link_info *info)
+{
+ bfd *obfd = info->output_bfd;
+ obj_attribute *in_attr;
+ obj_attribute *out_attr;
+ bfd_boolean result = TRUE;
+ const char *sec_name = get_elf_backend_data (ibfd)->obj_attrs_section;
+ unsigned int i;
+
+ /* Skip linker created files. */
+ if (ibfd->flags & BFD_LINKER_CREATED)
+ return TRUE;
+
+ /* Skip any input that doesn't have an attribute section.
+ This enables to link object files without attribute section with
+ any others. */
+ if (bfd_get_section_by_name (ibfd, sec_name) == NULL)
+ return TRUE;
+
+ if (!elf_known_obj_attributes_proc (obfd)[0].i)
+ {
+ /* This is the first object. Copy the attributes. */
+ _bfd_elf_copy_obj_attributes (ibfd, obfd);
+
+ out_attr = elf_known_obj_attributes_proc (obfd);
+
+ /* Use the Tag_null value to indicate the attributes have been
+ initialized. */
+ out_attr[0].i = 1;
+
+ return TRUE;
+ }
+
+ in_attr = elf_known_obj_attributes_proc (ibfd);
+ out_attr = elf_known_obj_attributes_proc (obfd);
+
+ for (i = LEAST_KNOWN_OBJ_ATTRIBUTE; i < NUM_KNOWN_OBJ_ATTRIBUTES; i++)
+ {
+ switch (i)
+ {
+ case Tag_RISCV_arch:
+ if (!out_attr[Tag_RISCV_arch].s)
+ out_attr[Tag_RISCV_arch].s = in_attr[Tag_RISCV_arch].s;
+ else if (in_attr[Tag_RISCV_arch].s
+ && out_attr[Tag_RISCV_arch].s)
+ {
+ /* Check arch compatible. */
+ char *merged_arch =
+ riscv_merge_arch_attr_info (ibfd,
+ in_attr[Tag_RISCV_arch].s,
+ out_attr[Tag_RISCV_arch].s);
+ if (merged_arch == NULL)
+ {
+ result = FALSE;
+ out_attr[Tag_RISCV_arch].s = "";
+ }
+ else
+ out_attr[Tag_RISCV_arch].s = merged_arch;
+ }
+ break;
+ case Tag_RISCV_priv_spec:
+ case Tag_RISCV_priv_spec_minor:
+ case Tag_RISCV_priv_spec_revision:
+ if (out_attr[i].i != in_attr[i].i)
+ {
+ _bfd_error_handler
+ (_("error: %pB: conflicting priv spec version "
+ "(major/minor/revision)."), ibfd);
+ result = FALSE;
+ }
+ break;
+ case Tag_RISCV_unaligned_access:
+ out_attr[i].i |= in_attr[i].i;
+ break;
+ case Tag_RISCV_stack_align:
+ if (out_attr[i].i == 0)
+ out_attr[i].i = in_attr[i].i;
+ else if (in_attr[i].i != 0
+ && out_attr[i].i != 0
+ && out_attr[i].i != in_attr[i].i)
+ {
+ _bfd_error_handler
+ (_("error: %pB use %u-byte stack aligned but the output "
+ "use %u-byte stack aligned."),
+ ibfd, in_attr[i].i, out_attr[i].i);
+ result = FALSE;
+ }
+ break;
+ default:
+ result &= _bfd_elf_merge_unknown_attribute_low (ibfd, obfd, i);
+ }
+
+ /* If out_attr was copied from in_attr then it won't have a type yet. */
+ if (in_attr[i].type && !out_attr[i].type)
+ out_attr[i].type = in_attr[i].type;
+ }
+
+ /* Merge Tag_compatibility attributes and any common GNU ones. */
+ if (!_bfd_elf_merge_object_attributes (ibfd, info))
+ return FALSE;
+
+ /* Check for any attributes not known on RISC-V. */
+ result &= _bfd_elf_merge_unknown_attribute_list (ibfd, obfd);
+
+ return result;
+}
+
/* Merge backend specific data from an object file to the output
object file when linking. */
_bfd_riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
{
bfd *obfd = info->output_bfd;
- flagword new_flags = elf_elfheader (ibfd)->e_flags;
- flagword old_flags = elf_elfheader (obfd)->e_flags;
+ flagword new_flags, old_flags;
if (!is_riscv_elf (ibfd) || !is_riscv_elf (obfd))
return TRUE;
if (!_bfd_elf_merge_object_attributes (ibfd, info))
return FALSE;
+ if (!riscv_merge_attributes (ibfd, info))
+ return FALSE;
+
+ new_flags = elf_elfheader (ibfd)->e_flags;
+ old_flags = elf_elfheader (obfd)->e_flags;
+
if (! elf_flags_init (obfd))
{
elf_flags_init (obfd) = TRUE;
return TRUE;
}
+ /* Check to see if the input BFD actually contains any sections. If not,
+ its flags may not have been initialized either, but it cannot actually
+ cause any incompatibility. Do not short-circuit dynamic objects; their
+ section list may be emptied by elf_link_add_object_symbols.
+
+ Also check to see if there are no code sections in the input. In this
+ case, there is no need to check for code specific flags. */
+ if (!(ibfd->flags & DYNAMIC))
+ {
+ bfd_boolean null_input_bfd = TRUE;
+ bfd_boolean only_data_sections = TRUE;
+ asection *sec;
+
+ for (sec = ibfd->sections; sec != NULL; sec = sec->next)
+ {
+ if ((bfd_section_flags (sec)
+ & (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS))
+ == (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS))
+ only_data_sections = FALSE;
+
+ null_input_bfd = FALSE;
+ break;
+ }
+
+ if (null_input_bfd || only_data_sections)
+ return TRUE;
+ }
+
/* Disallow linking different float ABIs. */
if ((old_flags ^ new_flags) & EF_RISCV_FLOAT_ABI)
{
(*_bfd_error_handler)
- (_("%pB: can't link hard-float modules with soft-float modules"), ibfd);
+ (_("%pB: can't link %s modules with %s modules"), ibfd,
+ riscv_float_abi_string (new_flags),
+ riscv_float_abi_string (old_flags));
+ goto fail;
+ }
+
+ /* Disallow linking RVE and non-RVE. */
+ if ((old_flags ^ new_flags) & EF_RISCV_RVE)
+ {
+ (*_bfd_error_handler)
+ (_("%pB: can't link RVE with other target"), ibfd);
goto fail;
}
return TRUE;
-fail:
+ fail:
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
/* If the symbol *spans* the bytes we just deleted (i.e. its
*end* is in the moved bytes but its *start* isn't), then we
- must adjust its size. */
- if (sym->st_value <= addr
- && sym->st_value + sym->st_size > addr
- && sym->st_value + sym->st_size <= toaddr)
+ must adjust its size.
+
+ This test needs to use the original value of st_value, otherwise
+ we might accidentally decrease size when deleting bytes right
+ before the symbol. But since deleted relocs can't span across
+ symbols, we can't have both a st_value and a st_size decrease,
+ so it is simpler to just use an else. */
+ else if (sym->st_value <= addr
+ && sym->st_value + sym->st_size > addr
+ && sym->st_value + sym->st_size <= toaddr)
sym->st_size -= count;
}
}
call to SYMBOL as well. Since both __wrap_SYMBOL and SYMBOL reference
the same symbol (which is __wrap_SYMBOL), but still exist as two
different symbols in 'sym_hashes', we don't want to adjust
- the global symbol __wrap_SYMBOL twice.
- This check is only relevant when symbols are being wrapped. */
- if (link_info->wrap_hash != NULL)
+ the global symbol __wrap_SYMBOL twice. */
+ /* The same problem occurs with symbols that are versioned_hidden, as
+ foo becomes an alias for foo@BAR, and hence they need the same
+ treatment. */
+ if (link_info->wrap_hash != NULL
+ || sym_hash->versioned == versioned_hidden)
{
struct elf_link_hash_entry **cur_sym_hashes;
sym_hash->root.u.def.value -= count;
/* As above, adjust the size if needed. */
- if (sym_hash->root.u.def.value <= addr
- && sym_hash->root.u.def.value + sym_hash->size > addr
- && sym_hash->root.u.def.value + sym_hash->size <= toaddr)
+ else if (sym_hash->root.u.def.value <= addr
+ && sym_hash->root.u.def.value + sym_hash->size > addr
+ && sym_hash->root.u.def.value + sym_hash->size <= toaddr)
sym_hash->size -= count;
}
}
bfd_vma hi_addr;
unsigned hi_sym;
asection *sym_sec;
+ bfd_boolean undefined_weak;
riscv_pcgp_hi_reloc *next;
};
riscv_pcgp_lo_reloc *lo;
} riscv_pcgp_relocs;
+/* Initialize the pcgp reloc info in P. */
+
static bfd_boolean
riscv_init_pcgp_relocs (riscv_pcgp_relocs *p)
{
return TRUE;
}
+/* Free the pcgp reloc info in P. */
+
static void
riscv_free_pcgp_relocs (riscv_pcgp_relocs *p,
bfd *abfd ATTRIBUTE_UNUSED,
}
}
+/* Record pcgp hi part reloc info in P, using HI_SEC_OFF as the lookup index.
+ The HI_ADDEND, HI_ADDR, HI_SYM, and SYM_SEC args contain info required to
+ relax the corresponding lo part reloc. */
+
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;
}
+/* Look up hi part pcgp reloc info in P, using HI_SEC_OFF as the lookup index.
+ This is used by a lo part reloc to find the corresponding hi part reloc. */
+
static riscv_pcgp_hi_reloc *
riscv_find_pcgp_hi_reloc(riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
{
return NULL;
}
-static bfd_boolean
-riscv_delete_pcgp_hi_reloc(riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
-{
- bfd_boolean out = FALSE;
- riscv_pcgp_hi_reloc *c;
-
- for (c = p->hi; c != NULL; c = c->next)
- if (c->hi_sec_off == hi_sec_off)
- out = TRUE;
-
- return out;
-}
-
-static bfd_boolean
-riscv_use_pcgp_hi_reloc(riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
-{
- bfd_boolean out = FALSE;
- riscv_pcgp_hi_reloc *c;
-
- for (c = p->hi; c != NULL; c = c->next)
- if (c->hi_sec_off == hi_sec_off)
- out = TRUE;
-
- return out;
-}
+/* Record pcgp lo part reloc info in P, using HI_SEC_OFF as the lookup info.
+ This is used to record relocs that can't be relaxed. */
static bfd_boolean
riscv_record_pcgp_lo_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
return TRUE;
}
+/* Look up lo part pcgp reloc info in P, using HI_SEC_OFF as the lookup index.
+ This is used by a hi part reloc to find the corresponding lo part reloc. */
+
static bfd_boolean
riscv_find_pcgp_lo_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
{
return FALSE;
}
-static bfd_boolean
-riscv_delete_pcgp_lo_reloc (riscv_pcgp_relocs *p ATTRIBUTE_UNUSED,
- bfd_vma lo_sec_off ATTRIBUTE_UNUSED,
- size_t bytes ATTRIBUTE_UNUSED)
-{
- return TRUE;
-}
-
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. */
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))
auipc = bfd_get_32 (abfd, contents + rel->r_offset);
jalr = bfd_get_32 (abfd, contents + rel->r_offset + 4);
rd = (jalr >> OP_SH_RD) & OP_MASK_RD;
- rvc = rvc && VALID_RVC_J_IMM (foff) && ARCH_SIZE == 32;
+ rvc = rvc && VALID_RVC_J_IMM (foff);
+
+ /* C.J exists on RV32 and RV64, but C.JAL is RV32-only. */
+ rvc = rvc && (rd == 0 || (rd == X_RA && ARCH_SIZE == 32));
- if (rvc && (rd == 0 || rd == X_RA))
+ if (rvc)
{
/* Relax to C.J[AL] rd, addr. */
r_type = R_RISCV_RVC_JUMP;
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);
int use_rvc = elf_elfheader (abfd)->e_flags & EF_RISCV_RVC;
- /* Mergeable symbols and code might later move out of range. */
- if (sym_sec->flags & (SEC_MERGE | SEC_CODE))
- return TRUE;
-
BFD_ASSERT (rel->r_offset + 4 <= sec->size);
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:
}
/* Can we relax LUI to C.LUI? Alignment might move the section forward;
- account for this assuming page alignment at worst. */
+ account for this assuming page alignment at worst. In the presence of
+ RELRO segment the linker aligns it by one page size, therefore sections
+ after the segment can be moved more than one page. */
+
if (use_rvc
&& ELFNN_R_TYPE (rel->r_info) == R_RISCV_HI20
&& VALID_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (symval))
- && VALID_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (symval + ELF_MAXPAGESIZE)))
+ && VALID_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (symval)
+ + (link_info->relro ? 2 * ELF_MAXPAGESIZE
+ : ELF_MAXPAGESIZE)))
{
/* Replace LUI with C.LUI if legal (i.e., rd != x0 and rd != x2/sp). */
bfd_vma lui = bfd_get_32 (abfd, contents + rel->r_offset);
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;
/* Make sure there are enough NOPs to actually achieve the alignment. */
if (rel->r_addend < nop_bytes)
{
- (*_bfd_error_handler)
- (_("%pB(%pA+0x%lx): %d bytes required for alignment "
- "to %d-byte boundary, but only %d present"),
- abfd, sym_sec, rel->r_offset, nop_bytes, alignment, rel->r_addend);
+ _bfd_error_handler
+ (_("%pB(%pA+%#" PRIx64 "): %" PRId64 " bytes required for alignment "
+ "to %" PRId64 "-byte boundary, but only %" PRId64 " present"),
+ abfd, sym_sec, (uint64_t) rel->r_offset,
+ (int64_t) nop_bytes, (int64_t) alignment, (int64_t) rel->r_addend);
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
/* Relax PC-relative references to GP-relative references. */
static bfd_boolean
-_bfd_riscv_relax_pc (bfd *abfd,
+_bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
asection *sec,
asection *sym_sec,
struct bfd_link_info *link_info,
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);
case R_RISCV_PCREL_LO12_I:
case R_RISCV_PCREL_LO12_S:
{
+ /* If the %lo has an addend, it isn't for the label pointing at the
+ hi part instruction, but rather for the symbol pointed at by the
+ hi part instruction. So we must subtract it here for the lookup.
+ It is still used below in the final symbol address. */
+ bfd_vma hi_sec_off = symval - sec_addr (sym_sec) - rel->r_addend;
riscv_pcgp_hi_reloc *hi = riscv_find_pcgp_hi_reloc (pcgp_relocs,
- symval - sec_addr(sym_sec));
+ hi_sec_off);
if (hi == NULL)
{
- riscv_record_pcgp_lo_reloc (pcgp_relocs, symval - sec_addr(sym_sec));
+ riscv_record_pcgp_lo_reloc (pcgp_relocs, hi_sec_off);
return TRUE;
}
hi_reloc = *hi;
symval = hi_reloc.hi_addr;
sym_sec = hi_reloc.sym_sec;
- if (!riscv_use_pcgp_hi_reloc(pcgp_relocs, hi->hi_sec_off))
- (*_bfd_error_handler)
- (_("%pB(%pA+0x%lx): Unable to clear RISCV_PCREL_HI20 reloc"
- "for cooresponding RISCV_PCREL_LO12 reloc"),
- abfd, sec, rel->r_offset);
+
+ /* 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;
- return riscv_delete_pcgp_lo_reloc (pcgp_relocs, rel->r_offset, 4);
+ 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;
- return riscv_delete_pcgp_lo_reloc (pcgp_relocs, rel->r_offset, 4);
+ 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:
riscv_record_pcgp_hi_reloc (pcgp_relocs,
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;
- return riscv_delete_pcgp_hi_reloc (pcgp_relocs, rel->r_offset);
+ return TRUE;
default:
abort ();
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))
relax_func_t relax_func;
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)
? 0 : isym->st_size - rel->r_addend;
if (isym->st_shndx == SHN_UNDEF)
- sym_sec = sec, symval = sec_addr (sec) + rel->r_offset;
+ sym_sec = sec, symval = rel->r_offset;
else
{
BFD_ASSERT (isym->st_shndx < elf_numsections (abfd));
if (sec_addr (sym_sec) == 0)
continue;
#endif
- symval = sec_addr (sym_sec) + isym->st_value;
+ symval = isym->st_value;
}
+ symtype = ELF_ST_TYPE (isym->st_info);
}
else
{
|| h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
- if (h->plt.offset != MINUS_ONE)
- symval = sec_addr (htab->elf.splt) + h->plt.offset;
+ 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 (undefined_weak)
+ {
+ symval = 0;
+ sym_sec = bfd_und_section_ptr;
+ }
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
- symval = sec_addr (h->root.u.def.section) + h->root.u.def.value;
+ {
+ symval = h->root.u.def.value;
+ sym_sec = h->root.u.def.section;
+ }
if (h->type != STT_FUNC)
reserve_size =
(h->size - rel->r_addend) > h->size ? 0 : h->size - rel->r_addend;
- sym_sec = h->root.u.def.section;
+ symtype = h->type;
+ }
+
+ if (sym_sec->sec_info_type == SEC_INFO_TYPE_MERGE
+ && (sym_sec->flags & SEC_MERGE))
+ {
+ /* At this stage in linking, no SEC_MERGE symbol has been
+ adjusted, so all references to such symbols need to be
+ passed through _bfd_merged_section_offset. (Later, in
+ relocate_section, all SEC_MERGE symbols *except* for
+ section symbols have been adjusted.)
+
+ gas may reduce relocations against symbols in SEC_MERGE
+ sections to a relocation against the section symbol when
+ the original addend was zero. When the reloc is against
+ a section symbol we should include the addend in the
+ offset passed to _bfd_merged_section_offset, since the
+ location of interest is the original symbol. On the
+ other hand, an access to "sym+addend" where "sym" is not
+ a section symbol should not include the addend; Such an
+ access is presumed to be an offset from "sym"; The
+ location of interest is just "sym". */
+ if (symtype == STT_SECTION)
+ symval += rel->r_addend;
+
+ symval = _bfd_merged_section_offset (abfd, &sym_sec,
+ elf_section_data (sym_sec)->sec_info,
+ symval);
+
+ if (symtype != STT_SECTION)
+ symval += rel->r_addend;
}
+ else
+ symval += rel->r_addend;
- symval += rel->r_addend;
+ symval += sec_addr (sym_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);
}
#if ARCH_SIZE == 32
-# define PRSTATUS_SIZE 0 /* FIXME */
+# define PRSTATUS_SIZE 204
# define PRSTATUS_OFFSET_PR_CURSIG 12
# define PRSTATUS_OFFSET_PR_PID 24
# define PRSTATUS_OFFSET_PR_REG 72
return TRUE;
}
+/* Determine whether an object attribute tag takes an integer, a
+ string or both. */
+
+static int
+riscv_elf_obj_attrs_arg_type (int tag)
+{
+ return (tag & 1) != 0 ? ATTR_TYPE_FLAG_STR_VAL : ATTR_TYPE_FLAG_INT_VAL;
+}
#define TARGET_LITTLE_SYM riscv_elfNN_vec
#define TARGET_LITTLE_NAME "elfNN-littleriscv"
#define elf_backend_rela_normal 1
#define elf_backend_default_execstack 0
+#undef elf_backend_obj_attrs_vendor
+#define elf_backend_obj_attrs_vendor "riscv"
+#undef elf_backend_obj_attrs_arg_type
+#define elf_backend_obj_attrs_arg_type riscv_elf_obj_attrs_arg_type
+#undef elf_backend_obj_attrs_section_type
+#define elf_backend_obj_attrs_section_type SHT_RISCV_ATTRIBUTES
+#undef elf_backend_obj_attrs_section
+#define elf_backend_obj_attrs_section ".riscv.attributes"
+
#include "elfNN-target.h"