/* 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.
#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)
{
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;
&& 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->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
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;
+ return;
- /* 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)
+ /* 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 = "mafdqlcbjtpvn";
+ 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);
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:
}
}
-/* Delete some bytes from a section while relaxing. */
+/* 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)
+_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;
- 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;
+ size_t bytes_to_move = toaddr - addr - count;
/* Actually delete the bytes. */
sec->size -= count;
- memmove (contents + addr, contents + addr + count, toaddr - addr - 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
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 *,
/* 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, pcgp_relocs);
+ 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;
}
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, pcgp_relocs);
+ 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, pcgp_relocs);
+ link_info, pcgp_relocs, rel + 1);
}
return true;
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,
- pcgp_relocs);
+ pcgp_relocs, rel);
default:
abort ();
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,
- NULL);
+ 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 ATTRIBUTE_UNUSED,
- 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, NULL))
- return false;
- rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
- return true;
-}
-
/* Called by after_allocation to set the information of data segment
before relaxing. */
/* Relax a section.
- Pass 0: Shortens code sequences for LUI/CALL/TPREL/PCREL relocs.
- Pass 1: Deletes the bytes that PCREL relaxation in pass 0 made obsolete.
- Pass 2: Which cannot be disabled, handles code alignment directives. */
+ 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->sec_flg0
- || (sec->flags & SEC_RELOC) == 0
|| sec->reloc_count == 0
+ || (sec->flags & SEC_RELOC) == 0
+ || (sec->flags & SEC_HAS_CONTENTS) == 0
|| (info->disable_target_specific_optimizations
&& info->relax_pass == 0)
/* The exp_seg_relro_adjust is enum phase_enum (0x4),
|| *(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)
+ 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;
+ riscv_relax_delete_bytes = _riscv_relax_delete_piecewise;
/* Only relax this reloc if it is paired with R_RISCV_RELAX. */
if (i == sec->reloc_count - 1
/* Skip over the R_RISCV_RELAX. */
i++;
}
- else if (info->relax_pass == 1 && type == R_RISCV_DELETE)
- relax_func = _bfd_riscv_relax_delete;
- else if (info->relax_pass == 2 && type == R_RISCV_ALIGN)
- relax_func = _bfd_riscv_relax_align;
+ 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;
goto fail;
}
+ /* Resolve R_RISCV_DELETE relocations. */
+ if (!riscv_relax_resolve_delete_relocs (abfd, sec, info, relocs))
+ goto fail;
+
ret = true;
fail:
#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