/* RISC-V-specific support for NN-bit ELF.
- Copyright (C) 2011-2021 Free Software Foundation, Inc.
+ Copyright (C) 2011-2023 Free Software Foundation, Inc.
Contributed by Andrew Waterman (andrew@sifive.com).
Based on TILE-Gx and MIPS targets.
#include "elf/riscv.h"
#include "opcode/riscv.h"
#include "objalloc.h"
-#include "cpu-riscv.h"
#include <limits.h>
#ifndef CHAR_BIT
#define CHAR_BIT 8
#endif
+/* True if dynamic relocation is needed. If we are creating a shared library,
+ and this is a reloc against a global symbol, or a non PC relative reloc
+ against a local symbol, then we need to copy the reloc into the shared
+ library. However, if we are linking with -Bsymbolic, we do not need to
+ copy a reloc against a global symbol which is defined in an object we are
+ including in the link (i.e., DEF_REGULAR is set).
+
+ At this point we have not seen all the input files, so it is possible that
+ DEF_REGULAR is not set now but will be set later (it is never cleared).
+ In case of a weak definition, DEF_REGULAR may be cleared later by a strong
+ definition in a shared library. We account for that possibility below by
+ storing information in the relocs_copied field of the hash table entry.
+ A similar situation occurs when creating shared libraries and symbol
+ visibility changes render the symbol local.
+
+ If on the other hand, we are creating an executable, we may need to keep
+ relocations for symbols satisfied by a dynamic library if we manage to
+ avoid copy relocs for the symbol.
+
+ Generate dynamic pointer relocation against STT_GNU_IFUNC symbol in the
+ non-code section (R_RISCV_32/R_RISCV_64). */
+#define RISCV_NEED_DYNAMIC_RELOC(PCREL, INFO, H, SEC) \
+ ((bfd_link_pic (INFO) \
+ && ((SEC)->flags & SEC_ALLOC) != 0 \
+ && (!(PCREL) \
+ || ((H) != NULL \
+ && (!(INFO)->symbolic \
+ || (H)->root.type == bfd_link_hash_defweak \
+ || !(H)->def_regular)))) \
+ || (!bfd_link_pic (INFO) \
+ && ((SEC)->flags & SEC_ALLOC) != 0 \
+ && (H) != NULL \
+ && ((H)->root.type == bfd_link_hash_defweak \
+ || !(H)->def_regular)) \
+ || (!bfd_link_pic (INFO) \
+ && (H) != NULL \
+ && (H)->type == STT_GNU_IFUNC \
+ && ((SEC)->flags & SEC_CODE) == 0))
+
+/* True if dynamic relocation should be generated. */
+#define RISCV_GENERATE_DYNAMIC_RELOC(PCREL, INFO, H, RESOLVED_TO_ZERO) \
+ ((bfd_link_pic (INFO) \
+ && ((H) == NULL \
+ || (ELF_ST_VISIBILITY ((H)->other) == STV_DEFAULT && !(RESOLVED_TO_ZERO)) \
+ || (H)->root.type != bfd_link_hash_undefweak) \
+ && (!(PCREL) \
+ || !SYMBOL_CALLS_LOCAL ((INFO), (H)))) \
+ || (!bfd_link_pic (INFO) \
+ && (H) != NULL \
+ && (H)->dynindx != -1 \
+ && !(H)->non_got_ref \
+ && (((H)->def_dynamic && !(H)->def_regular) \
+ || (H)->root.type == bfd_link_hash_undefweak \
+ || (H)->root.type == bfd_link_hash_undefined)))
+
+/* True if this input relocation should be copied to output. H->dynindx
+ may be -1 if this symbol was marked to become local. */
+#define RISCV_COPY_INPUT_RELOC(INFO, H) \
+ ((H) != NULL \
+ && (H)->dynindx != -1 \
+ && (!bfd_link_pic (INFO) \
+ || !SYMBOLIC_BIND ((INFO), (H)) \
+ || !(H)->def_regular))
+
+/* True if this is actually a static link, or it is a -Bsymbolic link
+ and the symbol is defined locally, or the symbol was forced to be
+ local because of a version file. */
+#define RISCV_RESOLVED_LOCALLY(INFO, H) \
+ (!WILL_CALL_FINISH_DYNAMIC_SYMBOL (elf_hash_table (INFO)->dynamic_sections_created, \
+ bfd_link_pic (INFO), (H)) \
+ || (bfd_link_pic (INFO) \
+ && SYMBOL_REFERENCES_LOCAL ((INFO), (H))))
+
/* Internal relocations used exclusively by the relaxation pass. */
#define R_RISCV_DELETE (R_RISCV_max + 1)
#define ELF_MAXPAGESIZE 0x1000
#define ELF_COMMONPAGESIZE 0x1000
+#define RISCV_ATTRIBUTES_SECTION_NAME ".riscv.attributes"
+
/* RISC-V ELF linker hash entry. */
struct riscv_elf_link_hash_entry
{
struct elf_link_hash_table elf;
+ /* Various options and other info passed from the linker. */
+ struct riscv_elf_params *params;
+
/* Short-cuts to get to dynamic linker sections. */
asection *sdyntdata;
/* The max alignment of output sections. */
bfd_vma max_alignment;
+ /* The max alignment of output sections in [gp-2K, gp+2K) range. */
+ bfd_vma max_alignment_for_gp;
+
/* Used by local STT_GNU_IFUNC symbols. */
htab_t loc_hash_table;
void * loc_hash_memory;
/* The index of the last unused .rel.iplt slot. */
bfd_vma last_iplt_index;
- /* Re-run the relaxations from relax pass 0 if TRUE. */
- bool restart_relax;
-
/* The data segment phase, don't relax the section
when it is exp_seg_relro_adjust. */
int *data_segment_phase;
+
+ /* Relocations for variant CC symbols may be present. */
+ int variant_cc;
};
/* Instruction access functions. */
&& elf_hash_table_id (elf_hash_table (p)) == RISCV_ELF_DATA) \
? (struct riscv_elf_link_hash_table *) (p)->hash : NULL)
+void
+riscv_elfNN_set_options (struct bfd_link_info *link_info,
+ struct riscv_elf_params *params)
+{
+ riscv_elf_hash_table (link_info)->params = params;
+}
+
static bool
riscv_info_to_howto_rela (bfd *abfd,
arelent *cache_ptr,
}
ret->max_alignment = (bfd_vma) -1;
- ret->restart_relax = false;
+ ret->max_alignment_for_gp = (bfd_vma) -1;
/* Create hash table for local ifunc. */
ret->loc_hash_table = htab_try_create (1024,
unsigned int r_type;
unsigned int r_symndx;
struct elf_link_hash_entry *h;
+ bool is_abs_symbol = false;
r_symndx = ELFNN_R_SYM (rel->r_info);
r_type = ELFNN_R_TYPE (rel->r_info);
if (isym == NULL)
return false;
+ is_abs_symbol = isym->st_shndx == SHN_ABS ? true : false;
+
/* Check relocation against local STT_GNU_IFUNC symbol. */
if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
{
while (h->root.type == bfd_link_hash_indirect
|| h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+ is_abs_symbol = bfd_is_abs_symbol (&h->root) ? true : false;
}
if (h != NULL)
ifunc symbol. */
h->plt.refcount += 1;
}
+
+ /* The non-preemptible absolute symbol shouldn't be referneced with
+ pc-relative relocation when generating shared object. However,
+ PCREL_HI20/LO12 relocs are always bind locally when generating
+ shared object, so all absolute symbol referenced need to be
+ disallowed, except they are defined in linker script.
+
+ Maybe we should add this check for all pc-relative relocations,
+ please see pr28789 and pr25749 for details. */
+ if (bfd_link_pic (info)
+ /* (h == NULL || SYMBOL_REFERENCES_LOCAL (info, h)) */
+ && is_abs_symbol)
+ {
+ if (h != NULL && (h)->root.ldscript_def)
+ /* Disallow the absolute symbol defined in linker script here
+ will cause the glibc-linux toolchain build failed, so regard
+ them as pc-relative symbols, just like what x86 did. */
+ ;
+ else
+ {
+ const char *name;
+ if (h->root.root.string)
+ name = h->root.root.string;
+ else
+ {
+ Elf_Internal_Sym *sym;
+ sym = bfd_sym_from_r_symndx (&htab->elf.sym_cache, abfd,
+ r_symndx);
+ name = bfd_elf_sym_name (abfd, symtab_hdr, sym, NULL);
+ }
+
+ reloc_howto_type *r_t =
+ riscv_elf_rtype_to_howto (abfd, r_type);
+ _bfd_error_handler
+ (_("%pB: relocation %s against absolute symbol `%s' can "
+ "not be used when making a shared object"),
+ abfd, r_t ? r_t->name : _("<unknown>"), name);
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+ }
/* Fall through. */
case R_RISCV_JAL:
case R_RISCV_HI20:
if (bfd_link_pic (info))
return bad_static_reloc (abfd, r_type, h);
- /* Fall through. */
+ goto static_reloc;
+
+ case R_RISCV_32:
+ if (ARCH_SIZE > 32
+ && bfd_link_pic (info)
+ && (sec->flags & SEC_ALLOC) != 0)
+ {
+ if (is_abs_symbol)
+ break;
+
+ reloc_howto_type *r_t = riscv_elf_rtype_to_howto (abfd, r_type);
+ _bfd_error_handler
+ (_("%pB: relocation %s against non-absolute symbol `%s' can "
+ "not be used in RVNN when making a shared object"),
+ abfd, r_t ? r_t->name : _("<unknown>"),
+ h != NULL ? h->root.root.string : "a local symbol");
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+ goto static_reloc;
case R_RISCV_COPY:
case R_RISCV_JUMP_SLOT:
case R_RISCV_RELATIVE:
case R_RISCV_64:
- case R_RISCV_32:
/* Fall through. */
static_reloc:
}
}
- /* If we are creating a shared library, and this is a reloc
- against a global symbol, or a non PC relative reloc
- against a local symbol, then we need to copy the reloc
- into the shared library. However, if we are linking with
- -Bsymbolic, we do not need to copy a reloc against a
- global symbol which is defined in an object we are
- including in the link (i.e., DEF_REGULAR is set). At
- this point we have not seen all the input files, so it is
- possible that DEF_REGULAR is not set now but will be set
- later (it is never cleared). In case of a weak definition,
- DEF_REGULAR may be cleared later by a strong definition in
- a shared library. We account for that possibility below by
- storing information in the relocs_copied field of the hash
- table entry. A similar situation occurs when creating
- shared libraries and symbol visibility changes render the
- symbol local.
-
- If on the other hand, we are creating an executable, we
- may need to keep relocations for symbols satisfied by a
- dynamic library if we manage to avoid copy relocs for the
- symbol.
-
- Generate dynamic pointer relocation against STT_GNU_IFUNC
- symbol in the non-code section (R_RISCV_32/R_RISCV_64). */
- reloc_howto_type * r = riscv_elf_rtype_to_howto (abfd, r_type);
-
- if ((bfd_link_pic (info)
- && (sec->flags & SEC_ALLOC) != 0
- && ((r != NULL && !r->pc_relative)
- || (h != NULL
- && (!info->symbolic
- || h->root.type == bfd_link_hash_defweak
- || !h->def_regular))))
- || (!bfd_link_pic (info)
- && (sec->flags & SEC_ALLOC) != 0
- && h != NULL
- && (h->root.type == bfd_link_hash_defweak
- || !h->def_regular))
- || (!bfd_link_pic (info)
- && h != NULL
- && h->type == STT_GNU_IFUNC
- && (sec->flags & SEC_CODE) == 0))
+ reloc_howto_type *r = riscv_elf_rtype_to_howto (abfd, r_type);
+ if (RISCV_NEED_DYNAMIC_RELOC (r->pc_relative, info, h, sec))
{
struct elf_dyn_relocs *p;
struct elf_dyn_relocs **head;
break;
- case R_RISCV_GNU_VTINHERIT:
- if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
- return false;
- break;
-
- case R_RISCV_GNU_VTENTRY:
- if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
- return false;
- break;
-
default:
break;
}
return true;
}
-static asection *
-riscv_elf_gc_mark_hook (asection *sec,
- struct bfd_link_info *info,
- Elf_Internal_Rela *rel,
- struct elf_link_hash_entry *h,
- Elf_Internal_Sym *sym)
-{
- if (h != NULL)
- switch (ELFNN_R_TYPE (rel->r_info))
- {
- case R_RISCV_GNU_VTINHERIT:
- case R_RISCV_GNU_VTENTRY:
- return NULL;
- }
-
- return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym);
-}
-
/* 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
h->root.u.def.section = s;
h->root.u.def.value = h->plt.offset;
}
+
+ /* If the symbol has STO_RISCV_VARIANT_CC flag, then raise the
+ variant_cc flag of riscv_elf_link_hash_table. */
+ if (h->other & STO_RISCV_VARIANT_CC)
+ htab->variant_cc = 1;
}
else
{
return false;
}
- return _bfd_elf_add_dynamic_tags (output_bfd, info, true);
+ /* Add dynamic entries. */
+ if (elf_hash_table (info)->dynamic_sections_created)
+ {
+ if (!_bfd_elf_add_dynamic_tags (output_bfd, info, true))
+ return false;
+
+ if (htab->variant_cc
+ && !_bfd_elf_add_dynamic_entry (info, DT_RISCV_VARIANT_CC, 0))
+ return false;
+ }
+
+ return true;
}
#define TP_OFFSET 0
char *msg_buf = NULL;
bool resolved_to_zero;
- if (howto == NULL
- || r_type == R_RISCV_GNU_VTINHERIT || r_type == R_RISCV_GNU_VTENTRY)
+ if (howto == NULL)
continue;
/* This is a final link. */
case R_RISCV_GOT_HI20:
if (h != NULL)
{
- bool dyn, pic;
-
off = h->got.offset;
BFD_ASSERT (off != (bfd_vma) -1);
- dyn = elf_hash_table (info)->dynamic_sections_created;
- pic = bfd_link_pic (info);
- if (! WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, pic, h)
- || (pic && SYMBOL_REFERENCES_LOCAL (info, h)))
+ if (RISCV_RESOLVED_LOCALLY (info, h))
{
- /* This is actually a static link, or it is a
- -Bsymbolic link and the symbol is defined
- locally, or the symbol was forced to be local
- because of a version file. We must initialize
- this entry in the global offset table. Since the
- offset must always be a multiple of the word size,
- we use the least significant bit to record whether
+ /* We must initialize this entry in the global offset table.
+ Since the offset must always be a multiple of the word
+ size, we use the least significant bit to record whether
we have initialized it already.
When doing a dynamic link, we create a .rela.got
break;
case R_RISCV_SUB6:
+ {
+ bfd_vma old_value = bfd_get (howto->bitsize, input_bfd,
+ contents + rel->r_offset);
+ relocation = (old_value & ~howto->dst_mask)
+ | (((old_value & howto->dst_mask) - relocation)
+ & howto->dst_mask);
+ }
+ break;
+
case R_RISCV_SUB8:
case R_RISCV_SUB16:
case R_RISCV_SUB32:
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)
+ if (bfd_link_pic (info) && h != NULL)
{
- /* Refer to the PLT entry. */
- relocation = sec_addr (htab->elf.splt) + h->plt.offset;
- unresolved_reloc = false;
+ if (h->plt.offset != MINUS_ONE)
+ {
+ /* Refer to the PLT entry. This check has to match the
+ check in _bfd_riscv_relax_section. */
+ relocation = sec_addr (htab->elf.splt) + h->plt.offset;
+ unresolved_reloc = false;
+ }
+ else if (!SYMBOL_REFERENCES_LOCAL (info, h)
+ && (input_section->flags & SEC_ALLOC) != 0
+ && (input_section->flags & SEC_READONLY) != 0
+ && ELF_ST_VISIBILITY (h->other) == STV_DEFAULT)
+ {
+ /* PR 28509, when generating the shared object, these
+ referenced symbols may bind externally, which means
+ they will be exported to the dynamic symbol table,
+ and are preemptible by default. These symbols cannot
+ be referenced by the non-pic relocations, like
+ R_RISCV_JAL and R_RISCV_RVC_JUMP relocations.
+
+ However, consider that linker may relax the R_RISCV_CALL
+ relocations to R_RISCV_JAL or R_RISCV_RVC_JUMP, if
+ these relocations are relocated to the plt entries,
+ then we won't report error for them.
+
+ Perhaps we also need the similar checks for the
+ R_RISCV_BRANCH and R_RISCV_RVC_BRANCH relocations. */
+ if (asprintf (&msg_buf,
+ _("%%X%%P: relocation %s against `%s' which "
+ "may bind externally can not be used when "
+ "making a shared object; recompile "
+ "with -fPIC\n"),
+ howto->name, h->root.root.string) == -1)
+ msg_buf = NULL;
+ msg = msg_buf;
+ r = bfd_reloc_notsupported;
+ }
}
break;
break;
case R_RISCV_32:
+ /* Non ABS symbol should be blocked in check_relocs. */
+ if (ARCH_SIZE > 32)
+ break;
+ /* Fall through. */
+
case R_RISCV_64:
if ((input_section->flags & SEC_ALLOC) == 0)
break;
- if ((bfd_link_pic (info)
- && (h == NULL
- || (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_link_pic (info)
- && h != NULL
- && h->dynindx != -1
- && !h->non_got_ref
- && ((h->def_dynamic
- && !h->def_regular)
- || h->root.type == bfd_link_hash_undefweak
- || h->root.type == bfd_link_hash_undefined)))
+ if (RISCV_GENERATE_DYNAMIC_RELOC (howto->pc_relative, info, h,
+ resolved_to_zero))
{
Elf_Internal_Rela outrel;
asection *sreloc;
- bool skip_static_relocation, skip_dynamic_relocation;
/* When generating a shared object, these relocations
are copied into the output file to be resolved at run
outrel.r_offset =
_bfd_elf_section_offset (output_bfd, info, input_section,
rel->r_offset);
- skip_static_relocation = outrel.r_offset != (bfd_vma) -2;
- skip_dynamic_relocation = outrel.r_offset >= (bfd_vma) -2;
+ bool skip = false;
+ bool relocate = false;
+ if (outrel.r_offset == (bfd_vma) -1)
+ skip = true;
+ else if (outrel.r_offset == (bfd_vma) -2)
+ {
+ skip = true;
+ relocate = true;
+ }
+ else if (h != NULL && bfd_is_abs_symbol (&h->root))
+ {
+ /* Don't need dynamic reloc when the ABS symbol is
+ non-dynamic or forced to local. Maybe just use
+ SYMBOL_REFERENCES_LOCAL to check? */
+ skip = (h->forced_local || (h->dynindx == -1));
+ relocate = skip;
+ }
+
outrel.r_offset += sec_addr (input_section);
- if (skip_dynamic_relocation)
- memset (&outrel, 0, sizeof outrel);
- else if (h != NULL && h->dynindx != -1
- && !(bfd_link_pic (info)
- && SYMBOLIC_BIND (info, h)
- && h->def_regular))
+ if (skip)
+ memset (&outrel, 0, sizeof outrel); /* R_RISCV_NONE. */
+ else if (RISCV_COPY_INPUT_RELOC (info, h))
{
+ /* Maybe just use !SYMBOL_REFERENCES_LOCAL to check? */
outrel.r_info = ELFNN_R_INFO (h->dynindx, r_type);
outrel.r_addend = rel->r_addend;
}
else
{
+ /* This symbol is local, or marked to become local. */
outrel.r_info = ELFNN_R_INFO (0, R_RISCV_RELATIVE);
outrel.r_addend = relocation + rel->r_addend;
}
sreloc = elf_section_data (input_section)->sreloc;
riscv_elf_append_rela (output_bfd, sreloc, &outrel);
- if (skip_static_relocation)
+ if (!relocate)
continue;
}
break;
&& _bfd_elf_section_offset (output_bfd, info, input_section,
rel->r_offset) != (bfd_vma) -1)
{
- switch (r_type)
- {
- 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;
- }
-
+ if (asprintf (&msg_buf,
+ _("%%X%%P: unresolvable %s relocation against "
+ "symbol `%s'\n"),
+ howto->name,
+ h->root.root.string) == -1)
+ msg_buf = NULL;
msg = msg_buf;
r = bfd_reloc_notsupported;
}
return (strlen (name) == 1) && (name[0] != 'x') && (name[0] != 's');
}
-/* Check if the versions are compatible. */
+/* Update the output subset's version to match the input when the input
+ subset's version is newer. */
-static bool
-riscv_version_mismatch (bfd *ibfd,
- struct riscv_subset_t *in,
- struct riscv_subset_t *out)
+static void
+riscv_update_subset_version (struct riscv_subset_t *in,
+ struct riscv_subset_t *out)
{
if (in == NULL || out == NULL)
- return true;
-
- /* Since there are no version conflicts for now, we just report
- warning when the versions are mis-matched. */
- if (in->major_version != out->major_version
- || in->minor_version != out->minor_version)
+ return;
+
+ /* Update the output ISA versions to the newest ones, but otherwise don't
+ provide any errors or warnings about mis-matched ISA versions as it's
+ generally too tricky to check for these at link time. */
+ if ((in->major_version > out->major_version)
+ || (in->major_version == out->major_version
+ && in->minor_version > out->minor_version)
+ || (out->major_version == RISCV_UNKNOWN_VERSION))
{
- if ((in->major_version == RISCV_UNKNOWN_VERSION
- && in->minor_version == RISCV_UNKNOWN_VERSION)
- || (out->major_version == RISCV_UNKNOWN_VERSION
- && out->minor_version == RISCV_UNKNOWN_VERSION))
- {
- /* Do not report the warning when the version of input
- or output is RISCV_UNKNOWN_VERSION, since the extension
- is added implicitly. */
- }
- else
- _bfd_error_handler
- (_("warning: %pB: mis-matched ISA version %d.%d for '%s' "
- "extension, the output version is %d.%d"),
- ibfd,
- in->major_version,
- in->minor_version,
- in->name,
- out->major_version,
- out->minor_version);
-
- /* Update the output ISA versions to the newest ones. */
- if ((in->major_version > out->major_version)
- || (in->major_version == out->major_version
- && in->minor_version > out->minor_version))
- {
- out->major_version = in->major_version;
- out->minor_version = in->minor_version;
- }
+ out->major_version = in->major_version;
+ out->minor_version = in->minor_version;
}
-
- return true;
}
/* Return true if subset is 'i' or 'e'. */
struct riscv_subset_t **pin,
struct riscv_subset_t **pout)
{
- const char *standard_exts = riscv_supported_std_ext ();
+ const char *standard_exts = "mafdqlcbjtpvnh";
const char *p;
struct riscv_subset_t *in = *pin;
struct riscv_subset_t *out = *pout;
ibfd, in->name, out->name);
return false;
}
- else if (!riscv_version_mismatch (ibfd, in, out))
- return false;
- else
- riscv_add_subset (&merged_subsets,
- out->name, out->major_version, out->minor_version);
+
+ riscv_update_subset_version(in, out);
+ riscv_add_subset (&merged_subsets,
+ out->name, out->major_version, out->minor_version);
in = in->next;
out = out->next;
if (!find_in && !find_out)
continue;
- if (find_in
- && find_out
- && !riscv_version_mismatch (ibfd, ext_in, ext_out))
- return false;
+ if (find_in && find_out)
+ riscv_update_subset_version(ext_in, ext_out);
ext_merged = find_out ? ext_out : ext_in;
riscv_add_subset (&merged_subsets, ext_merged->name,
on success and FALSE when a conflict is found. */
static bool
-riscv_merge_multi_letter_ext (bfd *ibfd,
- riscv_subset_t **pin,
+riscv_merge_multi_letter_ext (riscv_subset_t **pin,
riscv_subset_t **pout)
{
riscv_subset_t *in = *pin;
else
{
/* Both present, check version and increment both. */
- if (!riscv_version_mismatch (ibfd, in, out))
- return false;
+ riscv_update_subset_version (in, out);
riscv_add_subset (&merged_subsets, out->name, out->major_version,
out->minor_version);
merged_subsets.head = NULL;
merged_subsets.tail = NULL;
- 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_in.check_unknown_prefixed_ext = false;
-
- rpe_out.subset_list = &out_subsets;
- rpe_out.error_handler = _bfd_error_handler;
- rpe_out.xlen = &xlen_out;
- rpe_out.get_default_version = NULL;
- rpe_out.check_unknown_prefixed_ext = false;
+ riscv_parse_subset_t riscv_rps_ld_in =
+ {&in_subsets, _bfd_error_handler, &xlen_in, NULL, false};
+ riscv_parse_subset_t riscv_rps_ld_out =
+ {&out_subsets, _bfd_error_handler, &xlen_out, NULL, false};
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 ISA string. */
- if (!riscv_parse_subset (&rpe_in, in_arch))
+ if (!riscv_parse_subset (&riscv_rps_ld_in, in_arch))
return NULL;
-
- if (!riscv_parse_subset (&rpe_out, out_arch))
+ if (!riscv_parse_subset (&riscv_rps_ld_out, out_arch))
return NULL;
/* Checking XLEN. */
return NULL;
/* Merge all non-single letter extensions with single call. */
- if (!riscv_merge_multi_letter_ext (ibfd, &in, &out))
+ if (!riscv_merge_multi_letter_ext (&in, &out))
return NULL;
if (xlen_in != xlen_out)
/* Allow linking RVC and non-RVC, and keep the RVC flag. */
elf_elfheader (obfd)->e_flags |= new_flags & EF_RISCV_RVC;
+ /* Allow linking TSO and non-TSO, and keep the TSO flag. */
+ elf_elfheader (obfd)->e_flags |= new_flags & EF_RISCV_TSO;
+
return true;
fail:
return false;
}
-/* Delete some bytes from a section while relaxing. */
-
-static bool
-riscv_relax_delete_bytes (bfd *abfd, asection *sec, bfd_vma addr, size_t count,
- struct bfd_link_info *link_info)
-{
- unsigned int i, symcount;
- bfd_vma toaddr = sec->size;
- struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (abfd);
- Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
- unsigned int sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec);
- struct bfd_elf_section_data *data = elf_section_data (sec);
- bfd_byte *contents = data->this_hdr.contents;
-
- /* Actually delete the bytes. */
- sec->size -= count;
- memmove (contents + addr, contents + addr + count, toaddr - addr - count);
-
- /* Adjust the location of all of the relocs. Note that we need not
- adjust the addends, since all PC-relative references must be against
- symbols, which we will adjust below. */
- for (i = 0; i < sec->reloc_count; i++)
- if (data->relocs[i].r_offset > addr && data->relocs[i].r_offset < toaddr)
- data->relocs[i].r_offset -= count;
-
- /* Adjust the local symbols defined in this section. */
- for (i = 0; i < symtab_hdr->sh_info; i++)
- {
- Elf_Internal_Sym *sym = (Elf_Internal_Sym *) symtab_hdr->contents + i;
- if (sym->st_shndx == sec_shndx)
- {
- /* If the symbol is in the range of memory we just moved, we
- have to adjust its value. */
- if (sym->st_value > addr && sym->st_value <= toaddr)
- sym->st_value -= count;
-
- /* 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.
-
- 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;
- }
- }
-
- /* Now adjust the global symbols defined in this section. */
- symcount = ((symtab_hdr->sh_size / sizeof (ElfNN_External_Sym))
- - symtab_hdr->sh_info);
-
- for (i = 0; i < symcount; i++)
- {
- struct elf_link_hash_entry *sym_hash = sym_hashes[i];
-
- /* The '--wrap SYMBOL' option is causing a pain when the object file,
- containing the definition of __wrap_SYMBOL, includes a direct
- 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.
-
- 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;
-
- /* Loop only over the symbols which have already been checked. */
- for (cur_sym_hashes = sym_hashes; cur_sym_hashes < &sym_hashes[i];
- cur_sym_hashes++)
- {
- /* If the current symbol is identical to 'sym_hash', that means
- the symbol was already adjusted (or at least checked). */
- if (*cur_sym_hashes == sym_hash)
- break;
- }
- /* Don't adjust the symbol again. */
- if (cur_sym_hashes < &sym_hashes[i])
- continue;
- }
-
- if ((sym_hash->root.type == bfd_link_hash_defined
- || sym_hash->root.type == bfd_link_hash_defweak)
- && sym_hash->root.u.def.section == sec)
- {
- /* As above, adjust the value if needed. */
- if (sym_hash->root.u.def.value > addr
- && sym_hash->root.u.def.value <= toaddr)
- sym_hash->root.u.def.value -= count;
-
- /* As above, adjust the size if needed. */
- 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;
- }
- }
-
- return true;
-}
-
/* A second format for recording PC-relative hi relocations. This stores the
information required to relax them to GP-relative addresses. */
return false;
}
+static void
+riscv_update_pcgp_relocs (riscv_pcgp_relocs *p, asection *deleted_sec,
+ bfd_vma deleted_addr, size_t deleted_count)
+{
+ /* Bytes have already been deleted and toaddr should match the old section
+ size for our checks, so adjust it here. */
+ bfd_vma toaddr = deleted_sec->size + deleted_count;
+ riscv_pcgp_lo_reloc *l;
+ riscv_pcgp_hi_reloc *h;
+
+ /* Update section offsets of corresponding pcrel_hi relocs for the pcrel_lo
+ entries where they occur after the deleted bytes. */
+ for (l = p->lo; l != NULL; l = l->next)
+ if (l->hi_sec_off > deleted_addr
+ && l->hi_sec_off < toaddr)
+ l->hi_sec_off -= deleted_count;
+
+ /* Update both section offsets, and symbol values of pcrel_hi relocs where
+ these values occur after the deleted bytes. */
+ for (h = p->hi; h != NULL; h = h->next)
+ {
+ if (h->hi_sec_off > deleted_addr
+ && h->hi_sec_off < toaddr)
+ h->hi_sec_off -= deleted_count;
+ if (h->sym_sec == deleted_sec
+ && h->hi_addr > deleted_addr
+ && h->hi_addr < toaddr)
+ h->hi_addr -= deleted_count;
+ }
+}
+
+/* Delete some bytes, adjust relcocations and symbol table from a section. */
+
+static bool
+_riscv_relax_delete_bytes (bfd *abfd,
+ asection *sec,
+ bfd_vma addr,
+ size_t count,
+ struct bfd_link_info *link_info,
+ riscv_pcgp_relocs *p,
+ bfd_vma delete_total,
+ bfd_vma toaddr)
+{
+ unsigned int i, symcount;
+ struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (abfd);
+ Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+ unsigned int sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec);
+ struct bfd_elf_section_data *data = elf_section_data (sec);
+ bfd_byte *contents = data->this_hdr.contents;
+ size_t bytes_to_move = toaddr - addr - count;
+
+ /* Actually delete the bytes. */
+ sec->size -= count;
+ memmove (contents + addr, contents + addr + count + delete_total, bytes_to_move);
+
+ /* Still adjust relocations and symbols in non-linear times. */
+ toaddr = sec->size + count;
+
+ /* Adjust the location of all of the relocs. Note that we need not
+ adjust the addends, since all PC-relative references must be against
+ symbols, which we will adjust below. */
+ for (i = 0; i < sec->reloc_count; i++)
+ if (data->relocs[i].r_offset > addr && data->relocs[i].r_offset < toaddr)
+ data->relocs[i].r_offset -= count;
+
+ /* Adjust the hi_sec_off, and the hi_addr of any entries in the pcgp relocs
+ table for which these values occur after the deleted bytes. */
+ if (p)
+ riscv_update_pcgp_relocs (p, sec, addr, count);
+
+ /* Adjust the local symbols defined in this section. */
+ for (i = 0; i < symtab_hdr->sh_info; i++)
+ {
+ Elf_Internal_Sym *sym = (Elf_Internal_Sym *) symtab_hdr->contents + i;
+ if (sym->st_shndx == sec_shndx)
+ {
+ /* If the symbol is in the range of memory we just moved, we
+ have to adjust its value. */
+ if (sym->st_value > addr && sym->st_value <= toaddr)
+ sym->st_value -= count;
+
+ /* 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.
+
+ 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;
+ }
+ }
+
+ /* Now adjust the global symbols defined in this section. */
+ symcount = ((symtab_hdr->sh_size / sizeof (ElfNN_External_Sym))
+ - symtab_hdr->sh_info);
+
+ for (i = 0; i < symcount; i++)
+ {
+ struct elf_link_hash_entry *sym_hash = sym_hashes[i];
+
+ /* The '--wrap SYMBOL' option is causing a pain when the object file,
+ containing the definition of __wrap_SYMBOL, includes a direct
+ 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.
+
+ 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 != unversioned)
+ {
+ struct elf_link_hash_entry **cur_sym_hashes;
+
+ /* Loop only over the symbols which have already been checked. */
+ for (cur_sym_hashes = sym_hashes; cur_sym_hashes < &sym_hashes[i];
+ cur_sym_hashes++)
+ {
+ /* If the current symbol is identical to 'sym_hash', that means
+ the symbol was already adjusted (or at least checked). */
+ if (*cur_sym_hashes == sym_hash)
+ break;
+ }
+ /* Don't adjust the symbol again. */
+ if (cur_sym_hashes < &sym_hashes[i])
+ continue;
+ }
+
+ if ((sym_hash->root.type == bfd_link_hash_defined
+ || sym_hash->root.type == bfd_link_hash_defweak)
+ && sym_hash->root.u.def.section == sec)
+ {
+ /* As above, adjust the value if needed. */
+ if (sym_hash->root.u.def.value > addr
+ && sym_hash->root.u.def.value <= toaddr)
+ sym_hash->root.u.def.value -= count;
+
+ /* As above, adjust the size if needed. */
+ 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;
+ }
+ }
+
+ return true;
+}
+
+typedef bool (*relax_delete_t) (bfd *, asection *,
+ bfd_vma, size_t,
+ struct bfd_link_info *,
+ riscv_pcgp_relocs *,
+ Elf_Internal_Rela *);
+
+static relax_delete_t riscv_relax_delete_bytes;
+
+/* Do not delete some bytes from a section while relaxing.
+ Just mark the deleted bytes as R_RISCV_DELETE. */
+
+static bool
+_riscv_relax_delete_piecewise (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec ATTRIBUTE_UNUSED,
+ bfd_vma addr,
+ size_t count,
+ struct bfd_link_info *link_info ATTRIBUTE_UNUSED,
+ riscv_pcgp_relocs *p ATTRIBUTE_UNUSED,
+ Elf_Internal_Rela *rel)
+{
+ if (rel == NULL)
+ return false;
+ rel->r_info = ELFNN_R_INFO (0, R_RISCV_DELETE);
+ rel->r_offset = addr;
+ rel->r_addend = count;
+ return true;
+}
+
+/* Delete some bytes from a section while relaxing. */
+
+static bool
+_riscv_relax_delete_immediate (bfd *abfd,
+ asection *sec,
+ bfd_vma addr,
+ size_t count,
+ struct bfd_link_info *link_info,
+ riscv_pcgp_relocs *p,
+ Elf_Internal_Rela *rel)
+{
+ if (rel != NULL)
+ rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
+ return _riscv_relax_delete_bytes (abfd, sec, addr, count,
+ link_info, p, 0, sec->size);
+}
+
+/* Delete the bytes for R_RISCV_DELETE relocs. */
+
+static bool
+riscv_relax_resolve_delete_relocs (bfd *abfd,
+ asection *sec,
+ struct bfd_link_info *link_info,
+ Elf_Internal_Rela *relocs)
+{
+ bfd_vma delete_total = 0;
+ unsigned int i;
+
+ for (i = 0; i < sec->reloc_count; i++)
+ {
+ Elf_Internal_Rela *rel = relocs + i;
+ if (ELFNN_R_TYPE (rel->r_info) != R_RISCV_DELETE)
+ continue;
+
+ /* Find the next R_RISCV_DELETE reloc if possible. */
+ Elf_Internal_Rela *rel_next = NULL;
+ unsigned int start = rel - relocs;
+ for (i = start; i < sec->reloc_count; i++)
+ {
+ /* Since we only replace existing relocs and don't add new relocs, the
+ relocs are in sequential order. We can skip the relocs prior to this
+ one, making this search linear time. */
+ rel_next = relocs + i;
+ if (ELFNN_R_TYPE ((rel_next)->r_info) == R_RISCV_DELETE
+ && (rel_next)->r_offset > rel->r_offset)
+ {
+ BFD_ASSERT (rel_next - rel > 0);
+ break;
+ }
+ else
+ rel_next = NULL;
+ }
+
+ bfd_vma toaddr = rel_next == NULL ? sec->size : rel_next->r_offset;
+ if (!_riscv_relax_delete_bytes (abfd, sec, rel->r_offset, rel->r_addend,
+ link_info, NULL, delete_total, toaddr))
+ return false;
+
+ delete_total += rel->r_addend;
+ rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
+
+ /* Skip ahead to the next delete reloc. */
+ i = rel_next != NULL ? (unsigned int) (rel_next - relocs - 1)
+ : sec->reloc_count;
+ }
+
+ return true;
+}
+
typedef bool (*relax_func_t) (bfd *, asection *, asection *,
struct bfd_link_info *,
Elf_Internal_Rela *,
bfd_vma max_alignment,
bfd_vma reserve_size ATTRIBUTE_UNUSED,
bool *again,
- riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED,
+ riscv_pcgp_relocs *pcgp_relocs,
bool undefined_weak ATTRIBUTE_UNUSED)
{
bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
/* Replace the AUIPC. */
riscv_put_insn (8 * len, auipc, contents + rel->r_offset);
- /* Delete unnecessary JALR. */
+ /* Delete unnecessary JALR and reuse the R_RISCV_RELAX reloc. */
*again = true;
return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + len, 8 - len,
- link_info);
+ link_info, pcgp_relocs, rel + 1);
}
-/* Traverse all output sections and return the max alignment. */
+/* Traverse all output sections and return the max alignment.
+
+ If gp is zero, then all the output section alignments are
+ possible candidates; Otherwise, only the output sections
+ which are in the [gp-2K, gp+2K) range need to be considered. */
static bfd_vma
-_bfd_riscv_get_max_alignment (asection *sec)
+_bfd_riscv_get_max_alignment (asection *sec, bfd_vma gp)
{
unsigned int max_alignment_power = 0;
asection *o;
for (o = sec->output_section->owner->sections; o != NULL; o = o->next)
{
- if (o->alignment_power > max_alignment_power)
+ bool valid = true;
+ if (gp
+ && !(VALID_ITYPE_IMM (sec_addr (o) - gp)
+ || VALID_ITYPE_IMM (sec_addr (o) + o->size - gp)))
+ valid = false;
+
+ if (valid && o->alignment_power > max_alignment_power)
max_alignment_power = o->alignment_power;
}
bfd_vma max_alignment,
bfd_vma reserve_size,
bool *again,
- riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED,
+ riscv_pcgp_relocs *pcgp_relocs,
bool undefined_weak)
{
+ struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (link_info);
bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
- bfd_vma gp = riscv_global_pointer_value (link_info);
+ bfd_vma gp = htab->params->relax_gp
+ ? riscv_global_pointer_value (link_info)
+ : 0;
int use_rvc = elf_elfheader (abfd)->e_flags & EF_RISCV_RVC;
BFD_ASSERT (rel->r_offset + 4 <= sec->size);
- if (gp)
+ if (!undefined_weak && gp)
{
/* 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. */
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;
+ else
+ {
+ /* Consider output section alignments which are in [gp-2K, gp+2K). */
+ max_alignment = htab->max_alignment_for_gp;
+ if (max_alignment == (bfd_vma) -1)
+ {
+ max_alignment = _bfd_riscv_get_max_alignment (sec, gp);
+ htab->max_alignment_for_gp = max_alignment;
+ }
+ }
}
/* Is the reference in range of x0 or gp?
- Valid gp range conservatively because of alignment issue. */
+ Valid gp range conservatively because of alignment issue.
+
+ Should we also consider the alignment issue for x0 base? */
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))))
+ || 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))
return true;
case R_RISCV_HI20:
- /* We can delete the unnecessary LUI and reloc. */
- rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
+ /* Delete unnecessary LUI and reuse the reloc. */
*again = true;
return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4,
- link_info);
+ link_info, pcgp_relocs, rel);
default:
abort ();
/* Replace the R_RISCV_HI20 reloc. */
rel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel->r_info), R_RISCV_RVC_LUI);
+ /* Delete extra bytes and reuse the R_RISCV_RELAX reloc. */
*again = true;
return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + 2, 2,
- link_info);
+ link_info, pcgp_relocs, rel + 1);
}
return true;
bfd_vma max_alignment ATTRIBUTE_UNUSED,
bfd_vma reserve_size ATTRIBUTE_UNUSED,
bool *again,
- riscv_pcgp_relocs *prcel_relocs ATTRIBUTE_UNUSED,
+ riscv_pcgp_relocs *pcgp_relocs,
bool undefined_weak ATTRIBUTE_UNUSED)
{
/* See if this symbol is in range of tp. */
case R_RISCV_TPREL_HI20:
case R_RISCV_TPREL_ADD:
- /* We can delete the unnecessary instruction and reloc. */
- rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
+ /* Delete unnecessary instruction and reuse the reloc. */
*again = true;
- return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4, link_info);
+ return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4, link_info,
+ pcgp_relocs, rel);
default:
abort ();
bfd_vma max_alignment ATTRIBUTE_UNUSED,
bfd_vma reserve_size ATTRIBUTE_UNUSED,
bool *again ATTRIBUTE_UNUSED,
- riscv_pcgp_relocs *pcrel_relocs ATTRIBUTE_UNUSED,
+ riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED,
bool undefined_weak ATTRIBUTE_UNUSED)
{
bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
bfd_vma aligned_addr = ((symval - 1) & ~(alignment - 1)) + alignment;
bfd_vma nop_bytes = aligned_addr - symval;
+ /* Once we've handled an R_RISCV_ALIGN, we can't relax anything else. */
+ sec->sec_flg0 = true;
+
/* Make sure there are enough NOPs to actually achieve the alignment. */
if (rel->r_addend < nop_bytes)
{
if (nop_bytes % 4 != 0)
bfd_putl16 (RVC_NOP, contents + rel->r_offset + pos);
- /* Delete the excess bytes. */
+ /* Delete excess bytes. */
return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + nop_bytes,
- rel->r_addend - nop_bytes, link_info);
+ rel->r_addend - nop_bytes, link_info,
+ NULL, NULL);
}
/* Relax PC-relative references to GP-relative references. */
bfd_vma symval,
bfd_vma max_alignment,
bfd_vma reserve_size,
- bool *again ATTRIBUTE_UNUSED,
+ bool *again,
riscv_pcgp_relocs *pcgp_relocs,
bool undefined_weak)
{
+ struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (link_info);
bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
bfd_vma gp = riscv_global_pointer_value (link_info);
abort ();
}
- if (gp)
+ if (!undefined_weak && gp)
{
/* 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. */
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;
+ else
+ {
+ /* Consider output section alignments which are in [gp-2K, gp+2K). */
+ max_alignment = htab->max_alignment_for_gp;
+ if (max_alignment == (bfd_vma) -1)
+ {
+ max_alignment = _bfd_riscv_get_max_alignment (sec, gp);
+ htab->max_alignment_for_gp = max_alignment;
+ }
+ }
}
/* Is the reference in range of x0 or gp?
- Valid gp range conservatively because of alignment issue. */
+ Valid gp range conservatively because of alignment issue.
+
+ Should we also consider the alignment issue for x0 base? */
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))))
+ || 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))
ELFNN_R_SYM(rel->r_info),
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;
+ /* Delete unnecessary AUIPC and reuse the reloc. */
+ *again = true;
+ riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4, link_info,
+ pcgp_relocs, rel);
return true;
default:
return true;
}
-/* Delete the bytes for R_RISCV_DELETE. */
-
-static bool
-_bfd_riscv_relax_delete (bfd *abfd,
- asection *sec,
- asection *sym_sec ATTRIBUTE_UNUSED,
- struct bfd_link_info *link_info,
- Elf_Internal_Rela *rel,
- bfd_vma symval ATTRIBUTE_UNUSED,
- bfd_vma max_alignment ATTRIBUTE_UNUSED,
- bfd_vma reserve_size ATTRIBUTE_UNUSED,
- bool *again,
- riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED,
- bool undefined_weak ATTRIBUTE_UNUSED)
-{
- if (!riscv_relax_delete_bytes (abfd, sec, rel->r_offset, rel->r_addend,
- link_info))
- return false;
- rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
- *again = true;
- return true;
-}
-
/* Called by after_allocation to set the information of data segment
before relaxing. */
htab->data_segment_phase = data_segment_phase;
}
-/* Called by after_allocation to check if we need to run the whole
- relaxations again. */
-
-bool
-bfd_elfNN_riscv_restart_relax_sections (struct bfd_link_info *info)
-{
- struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
- bool restart = htab->restart_relax;
- /* Reset the flag. */
- htab->restart_relax = false;
- return restart;
-}
-
/* Relax a section.
- Pass 0: Shortens code sequences for LUI/CALL/TPREL relocs.
- Pass 1: Shortens code sequences for PCREL relocs.
- Pass 2: Deletes the bytes that pass 1 made obsolete.
- Pass 3: Which cannot be disabled, handles code alignment directives.
-
- The `again` is used to determine whether the relax pass itself needs to
- run again. And the `restart_relax` is used to determine if we need to
- run the whole relax passes again from 0 to 2. Once we have deleted the
- code between relax pass 0 to 2, the restart_relax will be set to TRUE,
- and we should run the whole relaxations again to give them more chances
- to shorten the code.
-
- Since we can't relax anything else once we start to handle the alignments,
- we will only enter into the relax pass 3 when the restart_relax is FALSE. */
+ Pass 0: Shortens code sequences for LUI/CALL/TPREL/PCREL relocs and
+ deletes the obsolete bytes.
+ Pass 1: Which cannot be disabled, handles code alignment directives. */
static bool
_bfd_riscv_relax_section (bfd *abfd, asection *sec,
unsigned int i;
bfd_vma max_alignment, reserve_size = 0;
riscv_pcgp_relocs pcgp_relocs;
+ static asection *first_section = NULL;
*again = false;
if (bfd_link_relocatable (info)
- || (sec->flags & SEC_RELOC) == 0
+ || sec->sec_flg0
|| sec->reloc_count == 0
+ || (sec->flags & SEC_RELOC) == 0
+ || (sec->flags & SEC_HAS_CONTENTS) == 0
|| (info->disable_target_specific_optimizations
- && info->relax_pass < 2)
- || (htab->restart_relax
- && info->relax_pass == 3)
+ && info->relax_pass == 0)
/* The exp_seg_relro_adjust is enum phase_enum (0x4),
and defined in ld/ldexp.h. */
|| *(htab->data_segment_phase) == 4)
return true;
+ /* Record the first relax section, so that we can reset the
+ max_alignment_for_gp for the repeated relax passes. */
+ if (first_section == NULL)
+ first_section = sec;
+ else if (first_section == sec)
+ htab->max_alignment_for_gp = -1;
+
riscv_init_pcgp_relocs (&pcgp_relocs);
/* Read this BFD's relocs if we haven't done so already. */
info->keep_memory)))
goto fail;
- if (htab)
+ /* Estimate the maximum alignment for all output sections once time
+ should be enough. */
+ max_alignment = htab->max_alignment;
+ if (max_alignment == (bfd_vma) -1)
{
- max_alignment = htab->max_alignment;
- if (max_alignment == (bfd_vma) -1)
- {
- max_alignment = _bfd_riscv_get_max_alignment (sec);
- htab->max_alignment = max_alignment;
- }
+ max_alignment = _bfd_riscv_get_max_alignment (sec, 0/* gp */);
+ htab->max_alignment = max_alignment;
}
- else
- max_alignment = _bfd_riscv_get_max_alignment (sec);
/* Examine and consider relaxing each reloc. */
for (i = 0; i < sec->reloc_count; i++)
bool undefined_weak = false;
relax_func = NULL;
+ riscv_relax_delete_bytes = NULL;
if (info->relax_pass == 0)
{
if (type == R_RISCV_CALL
|| type == R_RISCV_TPREL_LO12_I
|| type == R_RISCV_TPREL_LO12_S)
relax_func = _bfd_riscv_relax_tls_le;
+ else if (!bfd_link_pic (info) && htab->params->relax_gp
+ && (type == R_RISCV_PCREL_HI20
+ || type == R_RISCV_PCREL_LO12_I
+ || type == R_RISCV_PCREL_LO12_S))
+ relax_func = _bfd_riscv_relax_pc;
else
continue;
- }
- else if (info->relax_pass == 1
- && !bfd_link_pic (info)
- && (type == R_RISCV_PCREL_HI20
- || type == R_RISCV_PCREL_LO12_I
- || type == R_RISCV_PCREL_LO12_S))
- relax_func = _bfd_riscv_relax_pc;
- else if (info->relax_pass == 2 && type == R_RISCV_DELETE)
- relax_func = _bfd_riscv_relax_delete;
- else if (info->relax_pass == 3 && type == R_RISCV_ALIGN)
- relax_func = _bfd_riscv_relax_align;
- else
- continue;
+ riscv_relax_delete_bytes = _riscv_relax_delete_piecewise;
- if (info->relax_pass < 2)
- {
/* Only relax this reloc if it is paired with R_RISCV_RELAX. */
if (i == sec->reloc_count - 1
|| ELFNN_R_TYPE ((rel + 1)->r_info) != R_RISCV_RELAX
/* Skip over the R_RISCV_RELAX. */
i++;
}
+ else if (info->relax_pass == 1 && type == R_RISCV_ALIGN)
+ {
+ relax_func = _bfd_riscv_relax_align;
+ riscv_relax_delete_bytes = _riscv_relax_delete_immediate;
+ }
+ else
+ continue;
data->relocs = relocs;
goto fail;
}
+ /* Resolve R_RISCV_DELETE relocations. */
+ if (!riscv_relax_resolve_delete_relocs (abfd, sec, info, relocs))
+ goto fail;
+
ret = true;
fail:
free (relocs);
riscv_free_pcgp_relocs (&pcgp_relocs, abfd, sec);
- if (*again)
- htab->restart_relax = true;
-
return ret;
}
return (tag & 1) != 0 ? ATTR_TYPE_FLAG_STR_VAL : ATTR_TYPE_FLAG_INT_VAL;
}
-/* PR27584, Omit local and empty symbols since they usually generated
- for pcrel relocations. */
+/* Do not choose mapping symbols as a function name. */
+
+static bfd_size_type
+riscv_maybe_function_sym (const asymbol *sym,
+ asection *sec,
+ bfd_vma *code_off)
+{
+ if (sym->flags & BSF_LOCAL
+ && riscv_elf_is_mapping_symbols (sym->name))
+ return 0;
+
+ return _bfd_elf_maybe_function_sym (sym, sec, code_off);
+}
+
+/* Treat the following cases as target special symbols, they are
+ usually omitted. */
static bool
riscv_elf_is_target_special_symbol (bfd *abfd, asymbol *sym)
{
+ /* PR27584, local and empty symbols. Since they are usually
+ generated for pcrel relocations. */
return (!strcmp (sym->name, "")
- || _bfd_elf_is_local_label_name (abfd, sym->name));
+ || _bfd_elf_is_local_label_name (abfd, sym->name)
+ /* PR27916, mapping symbols. */
+ || riscv_elf_is_mapping_symbols (sym->name));
+}
+
+static int
+riscv_elf_additional_program_headers (bfd *abfd,
+ struct bfd_link_info *info ATTRIBUTE_UNUSED)
+{
+ int ret = 0;
+
+ /* See if we need a PT_RISCV_ATTRIBUTES segment. */
+ if (bfd_get_section_by_name (abfd, RISCV_ATTRIBUTES_SECTION_NAME))
+ ++ret;
+
+ return ret;
+}
+
+static bool
+riscv_elf_modify_segment_map (bfd *abfd,
+ struct bfd_link_info *info ATTRIBUTE_UNUSED)
+{
+ asection *s;
+ struct elf_segment_map *m, **pm;
+ size_t amt;
+
+ /* If there is a .riscv.attributes section, we need a PT_RISCV_ATTRIBUTES
+ segment. */
+ s = bfd_get_section_by_name (abfd, RISCV_ATTRIBUTES_SECTION_NAME);
+ if (s != NULL)
+ {
+ for (m = elf_seg_map (abfd); m != NULL; m = m->next)
+ if (m->p_type == PT_RISCV_ATTRIBUTES)
+ break;
+ /* If there is already a PT_RISCV_ATTRIBUTES header, avoid adding
+ another. */
+ if (m == NULL)
+ {
+ amt = sizeof (*m);
+ m = bfd_zalloc (abfd, amt);
+ if (m == NULL)
+ return false;
+
+ m->p_type = PT_RISCV_ATTRIBUTES;
+ m->count = 1;
+ m->sections[0] = s;
+
+ /* We want to put it after the PHDR and INTERP segments. */
+ pm = &elf_seg_map (abfd);
+ while (*pm != NULL
+ && ((*pm)->p_type == PT_PHDR
+ || (*pm)->p_type == PT_INTERP))
+ pm = &(*pm)->next;
+
+ m->next = *pm;
+ *pm = m;
+ }
+ }
+
+ return true;
+}
+
+/* Merge non-visibility st_other attributes. */
+
+static void
+riscv_elf_merge_symbol_attribute (struct elf_link_hash_entry *h,
+ unsigned int st_other,
+ bool definition ATTRIBUTE_UNUSED,
+ bool dynamic ATTRIBUTE_UNUSED)
+{
+ unsigned int isym_sto = st_other & ~ELF_ST_VISIBILITY (-1);
+ unsigned int h_sto = h->other & ~ELF_ST_VISIBILITY (-1);
+
+ if (isym_sto == h_sto)
+ return;
+
+ if (isym_sto & ~STO_RISCV_VARIANT_CC)
+ _bfd_error_handler (_("unknown attribute for symbol `%s': 0x%02x"),
+ h->root.root.string, isym_sto);
+
+ if (isym_sto & STO_RISCV_VARIANT_CC)
+ h->other |= STO_RISCV_VARIANT_CC;
}
#define TARGET_LITTLE_SYM riscv_elfNN_vec
#define elf_backend_relocate_section riscv_elf_relocate_section
#define elf_backend_finish_dynamic_symbol riscv_elf_finish_dynamic_symbol
#define elf_backend_finish_dynamic_sections riscv_elf_finish_dynamic_sections
-#define elf_backend_gc_mark_hook riscv_elf_gc_mark_hook
#define elf_backend_plt_sym_val riscv_elf_plt_sym_val
#define elf_backend_grok_prstatus riscv_elf_grok_prstatus
#define elf_backend_grok_psinfo riscv_elf_grok_psinfo
#define elf_backend_object_p riscv_elf_object_p
#define elf_backend_write_core_note riscv_write_core_note
+#define elf_backend_maybe_function_sym riscv_maybe_function_sym
#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_additional_program_headers \
+ riscv_elf_additional_program_headers
+#define elf_backend_modify_segment_map riscv_elf_modify_segment_map
+#define elf_backend_merge_symbol_attribute riscv_elf_merge_symbol_attribute
#define elf_backend_init_index_section _bfd_elf_init_1_index_section
#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"
+#define elf_backend_obj_attrs_section RISCV_ATTRIBUTES_SECTION_NAME
#include "elfNN-target.h"