x86/Intel: split certain AVX512-FP16 VCVT*2PH templates
[binutils-gdb.git] / bfd / elfnn-riscv.c
index 2dfa63513370327f7f9452a3589deb242a0c2bbe..8f9f0d8a86a3a793b627b0de653b83a5df3338db 100644 (file)
@@ -1,5 +1,5 @@
 /* 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.
@@ -32,7 +32,6 @@
 #include "elf/riscv.h"
 #include "opcode/riscv.h"
 #include "objalloc.h"
-#include "cpu-riscv.h"
 
 #include <limits.h>
 #ifndef CHAR_BIT
@@ -132,12 +131,12 @@ struct riscv_elf_link_hash_table
   /* 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. */
@@ -406,7 +405,6 @@ riscv_elf_link_hash_table_create (bfd *abfd)
     }
 
   ret->max_alignment = (bfd_vma) -1;
-  ret->restart_relax = false;
 
   /* Create hash table for local ifunc.  */
   ret->loc_hash_table = htab_try_create (1024,
@@ -1177,6 +1175,11 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
              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
        {
@@ -1560,7 +1563,18 @@ riscv_elf_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
        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
@@ -3357,52 +3371,27 @@ riscv_std_ext_p (const char *name)
   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'.  */
@@ -3443,7 +3432,7 @@ riscv_merge_std_ext (bfd *ibfd,
                     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;
@@ -3463,11 +3452,10 @@ riscv_merge_std_ext (bfd *ibfd,
         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;
@@ -3485,10 +3473,8 @@ riscv_merge_std_ext (bfd *ibfd,
       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,
@@ -3510,8 +3496,7 @@ riscv_merge_std_ext (bfd *ibfd,
    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;
@@ -3541,8 +3526,7 @@ riscv_merge_multi_letter_ext (bfd *ibfd,
       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);
@@ -3579,37 +3563,22 @@ riscv_merge_arch_attr_info (bfd *ibfd, char *in_arch, char *out_arch)
   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.  */
@@ -3630,7 +3599,7 @@ riscv_merge_arch_attr_info (bfd *ibfd, char *in_arch, char *out_arch)
     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)
@@ -3924,115 +3893,6 @@ _bfd_riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
   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.  */
 
@@ -4163,6 +4023,155 @@ riscv_find_pcgp_lo_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
   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 *,
@@ -4180,7 +4189,7 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
                       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;
@@ -4244,7 +4253,7 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
   /* 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.  */
@@ -4276,7 +4285,7 @@ _bfd_riscv_relax_lui (bfd *abfd,
                      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;
@@ -4338,7 +4347,7 @@ _bfd_riscv_relax_lui (bfd *abfd,
          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 ();
@@ -4371,7 +4380,7 @@ _bfd_riscv_relax_lui (bfd *abfd,
 
       *again = true;
       return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + 2, 2,
-                                      link_info);
+                                      link_info, pcgp_relocs);
     }
 
   return true;
@@ -4389,7 +4398,7 @@ _bfd_riscv_relax_tls_le (bfd *abfd,
                         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.  */
@@ -4412,7 +4421,8 @@ _bfd_riscv_relax_tls_le (bfd *abfd,
       /* 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 ();
@@ -4431,7 +4441,7 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec,
                        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;
@@ -4443,6 +4453,9 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec,
   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)
     {
@@ -4472,7 +4485,8 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec,
 
   /* 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.  */
@@ -4638,15 +4652,14 @@ _bfd_riscv_relax_delete (bfd *abfd,
                         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;
 }
 
@@ -4661,35 +4674,11 @@ bfd_elfNN_riscv_set_data_segment_info (struct bfd_link_info *info,
   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,
@@ -4708,12 +4697,11 @@ _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)
@@ -4766,24 +4754,14 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
                   || 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
@@ -4793,6 +4771,12 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
          /* 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;
 
@@ -4955,9 +4939,6 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
     free (relocs);
   riscv_free_pcgp_relocs (&pcgp_relocs, abfd, sec);
 
-  if (*again)
-    htab->restart_relax = true;
-
   return ret;
 }
 
@@ -5150,14 +5131,32 @@ riscv_elf_obj_attrs_arg_type (int tag)
   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
@@ -5217,6 +5216,28 @@ riscv_elf_modify_segment_map (bfd *abfd,
   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 TARGET_LITTLE_NAME                     "elfNN-littleriscv"
 #define TARGET_BIG_SYM                         riscv_elfNN_be_vec
@@ -5245,6 +5266,7 @@ riscv_elf_modify_segment_map (bfd *abfd,
 #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
@@ -5252,6 +5274,7 @@ riscv_elf_modify_segment_map (bfd *abfd,
 #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