/* RISC-V-specific support for NN-bit ELF.
- Copyright (C) 2011-2021 Free Software Foundation, Inc.
+ Copyright (C) 2011-2022 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 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
/* 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. */
}
ret->max_alignment = (bfd_vma) -1;
- ret->restart_relax = false;
/* Create hash table for local ifunc. */
ret->loc_hash_table = htab_try_create (1024,
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
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 = "mafdqlcbjtpvn";
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)
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 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,
+ riscv_pcgp_relocs *p)
+{
+ 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 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_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;
/* Delete unnecessary JALR. */
*again = true;
return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + len, 8 - len,
- link_info);
+ link_info, pcgp_relocs);
}
/* Traverse all output sections and return the max alignment. */
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)
{
bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
*again = true;
return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4,
- link_info);
+ link_info, pcgp_relocs);
default:
abort ();
*again = true;
return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + 2, 2,
- link_info);
+ link_info, pcgp_relocs);
}
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. */
/* We can delete the unnecessary instruction and reloc. */
rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
*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);
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)
{
/* Delete the 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);
}
/* Relax PC-relative references to GP-relative references. */
bfd_vma symval ATTRIBUTE_UNUSED,
bfd_vma max_alignment ATTRIBUTE_UNUSED,
bfd_vma reserve_size ATTRIBUTE_UNUSED,
- bool *again,
+ 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))
+ link_info, NULL))
return false;
rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
- *again = true;
return true;
}
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.
+ Pass 1: Deletes the bytes that PCREL relaxation in pass 0 made obsolete.
+ Pass 2: Which cannot be disabled, handles code alignment directives. */
static bool
_bfd_riscv_relax_section (bfd *abfd, asection *sec,
*again = false;
if (bfd_link_relocatable (info)
+ || sec->sec_flg0
|| (sec->flags & SEC_RELOC) == 0
|| sec->reloc_count == 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)
|| 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)
+ && (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;
- 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_DELETE)
+ relax_func = _bfd_riscv_relax_delete;
+ else if (info->relax_pass == 2 && type == R_RISCV_ALIGN)
+ relax_func = _bfd_riscv_relax_align;
+ else
+ continue;
data->relocs = relocs;
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_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"