Automatic date update in version.in
[binutils-gdb.git] / bfd / elfnn-riscv.c
index ec8a3e73c8fb60dfe0f0c017cfaa17da7245206e..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.
 #include "elf/riscv.h"
 #include "opcode/riscv.h"
 #include "objalloc.h"
-#include "cpu-riscv.h"
 
-#ifdef HAVE_LIMITS_H
 #include <limits.h>
-#endif
 #ifndef CHAR_BIT
 #define CHAR_BIT 8
 #endif
@@ -64,6 +61,8 @@
 #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
@@ -104,7 +103,7 @@ struct _bfd_riscv_elf_obj_tdata
    && elf_tdata (bfd) != NULL                          \
    && elf_object_id (bfd) == RISCV_ELF_DATA)
 
-static bfd_boolean
+static bool
 elfNN_riscv_mkobject (bfd *abfd)
 {
   return bfd_elf_allocate_object (abfd,
@@ -131,6 +130,13 @@ struct riscv_elf_link_hash_table
 
   /* The index of the last unused .rel.iplt slot.  */
   bfd_vma last_iplt_index;
+
+  /* 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. */
@@ -151,7 +157,7 @@ struct riscv_elf_link_hash_table
     && elf_hash_table_id (elf_hash_table (p)) == RISCV_ELF_DATA)       \
    ? (struct riscv_elf_link_hash_table *) (p)->hash : NULL)
 
-static bfd_boolean
+static bool
 riscv_info_to_howto_rela (bfd *abfd,
                          arelent *cache_ptr,
                          Elf_Internal_Rela *dst)
@@ -173,7 +179,7 @@ riscv_elf_append_rela (bfd *abfd, asection *s, Elf_Internal_Rela *rel)
 
 /* Return true if a relocation is modifying an instruction. */
 
-static bfd_boolean
+static bool
 riscv_is_insn_reloc (const reloc_howto_type *howto)
 {
   /* Heuristic: A multibyte destination with a nontrivial mask
@@ -205,7 +211,7 @@ riscv_is_insn_reloc (const reloc_howto_type *howto)
 
 /* Generate a PLT header.  */
 
-static bfd_boolean
+static bool
 riscv_make_plt_header (bfd *output_bfd, bfd_vma gotplt_addr, bfd_vma addr,
                       uint32_t *entry)
 {
@@ -217,7 +223,7 @@ riscv_make_plt_header (bfd *output_bfd, bfd_vma gotplt_addr, bfd_vma addr,
     {
       _bfd_error_handler (_("%pB: warning: RVE PLT generation not supported"),
                          output_bfd);
-      return FALSE;
+      return false;
     }
 
   /* auipc  t2, %hi(.got.plt)
@@ -238,12 +244,12 @@ riscv_make_plt_header (bfd *output_bfd, bfd_vma gotplt_addr, bfd_vma addr,
   entry[6] = RISCV_ITYPE (LREG, X_T0, X_T0, RISCV_ELF_WORD_BYTES);
   entry[7] = RISCV_ITYPE (JALR, 0, X_T3, 0);
 
-  return TRUE;
+  return true;
 }
 
 /* Generate a PLT entry.  */
 
-static bfd_boolean
+static bool
 riscv_make_plt_entry (bfd *output_bfd, bfd_vma got, bfd_vma addr,
                      uint32_t *entry)
 {
@@ -252,7 +258,7 @@ riscv_make_plt_entry (bfd *output_bfd, bfd_vma got, bfd_vma addr,
     {
       _bfd_error_handler (_("%pB: warning: RVE PLT generation not supported"),
                          output_bfd);
-      return FALSE;
+      return false;
     }
 
   /* auipc  t3, %hi(.got.plt entry)
@@ -265,7 +271,7 @@ riscv_make_plt_entry (bfd *output_bfd, bfd_vma got, bfd_vma addr,
   entry[2] = RISCV_ITYPE (JALR, X_T1, X_T3, 0);
   entry[3] = RISCV_NOP;
 
-  return TRUE;
+  return true;
 }
 
 /* Create an entry in an RISC-V ELF linker hash table.  */
@@ -326,7 +332,7 @@ riscv_elf_local_htab_eq (const void *ptr1, const void *ptr2)
 static struct elf_link_hash_entry *
 riscv_elf_get_local_sym_hash (struct riscv_elf_link_hash_table *htab,
                              bfd *abfd, const Elf_Internal_Rela *rel,
-                             bfd_boolean create)
+                             bool create)
 {
   struct riscv_elf_link_hash_entry eh, *ret;
   asection *sec = abfd->sections;
@@ -418,7 +424,7 @@ riscv_elf_link_hash_table_create (bfd *abfd)
 
 /* Create the .got section.  */
 
-static bfd_boolean
+static bool
 riscv_elf_create_got_section (bfd *abfd, struct bfd_link_info *info)
 {
   flagword flags;
@@ -429,7 +435,7 @@ riscv_elf_create_got_section (bfd *abfd, struct bfd_link_info *info)
 
   /* This function may be called more than once.  */
   if (htab->sgot != NULL)
-    return TRUE;
+    return true;
 
   flags = bed->dynamic_sec_flags;
 
@@ -440,13 +446,13 @@ riscv_elf_create_got_section (bfd *abfd, struct bfd_link_info *info)
                                           | SEC_READONLY));
   if (s == NULL
       || !bfd_set_section_alignment (s, bed->s->log_file_align))
-    return FALSE;
+    return false;
   htab->srelgot = s;
 
   s = s_got = bfd_make_section_anyway_with_flags (abfd, ".got", flags);
   if (s == NULL
       || !bfd_set_section_alignment (s, bed->s->log_file_align))
-    return FALSE;
+    return false;
   htab->sgot = s;
 
   /* The first bit of the global offset table is the header.  */
@@ -457,7 +463,7 @@ riscv_elf_create_got_section (bfd *abfd, struct bfd_link_info *info)
       s = bfd_make_section_anyway_with_flags (abfd, ".got.plt", flags);
       if (s == NULL
          || !bfd_set_section_alignment (s, bed->s->log_file_align))
-       return FALSE;
+       return false;
       htab->sgotplt = s;
 
       /* Reserve room for the header.  */
@@ -474,17 +480,17 @@ riscv_elf_create_got_section (bfd *abfd, struct bfd_link_info *info)
                                       "_GLOBAL_OFFSET_TABLE_");
       elf_hash_table (info)->hgot = h;
       if (h == NULL)
-       return FALSE;
+       return false;
     }
 
-  return TRUE;
+  return true;
 }
 
 /* Create .plt, .rela.plt, .got, .got.plt, .rela.got, .dynbss, and
    .rela.bss sections in DYNOBJ, and set up shortcuts to them in our
    hash table.  */
 
-static bfd_boolean
+static bool
 riscv_elf_create_dynamic_sections (bfd *dynobj,
                                   struct bfd_link_info *info)
 {
@@ -494,10 +500,10 @@ riscv_elf_create_dynamic_sections (bfd *dynobj,
   BFD_ASSERT (htab != NULL);
 
   if (!riscv_elf_create_got_section (dynobj, info))
-    return FALSE;
+    return false;
 
   if (!_bfd_elf_create_dynamic_sections (dynobj, info))
-    return FALSE;
+    return false;
 
   if (!bfd_link_pic (info))
     {
@@ -525,7 +531,7 @@ riscv_elf_create_dynamic_sections (bfd *dynobj,
       || (!bfd_link_pic (info) && (!htab->elf.srelbss || !htab->sdyntdata)))
     abort ();
 
-  return TRUE;
+  return true;
 }
 
 /* Copy the extra info we tack onto an elf_link_hash_entry.  */
@@ -549,7 +555,7 @@ riscv_elf_copy_indirect_symbol (struct bfd_link_info *info,
   _bfd_elf_link_hash_copy_indirect (info, dir, ind);
 }
 
-static bfd_boolean
+static bool
 riscv_elf_record_tls_type (bfd *abfd, struct elf_link_hash_entry *h,
                           unsigned long symndx, char tls_type)
 {
@@ -561,12 +567,12 @@ riscv_elf_record_tls_type (bfd *abfd, struct elf_link_hash_entry *h,
       (*_bfd_error_handler)
        (_("%pB: `%s' accessed both as normal and thread local symbol"),
         abfd, h ? h->root.root.string : "<local>");
-      return FALSE;
+      return false;
     }
-  return TRUE;
+  return true;
 }
 
-static bfd_boolean
+static bool
 riscv_elf_record_got_reference (bfd *abfd, struct bfd_link_info *info,
                                struct elf_link_hash_entry *h, long symndx)
 {
@@ -576,13 +582,13 @@ riscv_elf_record_got_reference (bfd *abfd, struct bfd_link_info *info,
   if (htab->elf.sgot == NULL)
     {
       if (!riscv_elf_create_got_section (htab->elf.dynobj, info))
-       return FALSE;
+       return false;
     }
 
   if (h != NULL)
     {
       h->got.refcount += 1;
-      return TRUE;
+      return true;
     }
 
   /* This is a global offset table entry for a local symbol.  */
@@ -590,16 +596,16 @@ riscv_elf_record_got_reference (bfd *abfd, struct bfd_link_info *info,
     {
       bfd_size_type size = symtab_hdr->sh_info * (sizeof (bfd_vma) + 1);
       if (!(elf_local_got_refcounts (abfd) = bfd_zalloc (abfd, size)))
-       return FALSE;
+       return false;
       _bfd_riscv_elf_local_got_tls_type (abfd)
        = (char *) (elf_local_got_refcounts (abfd) + symtab_hdr->sh_info);
     }
   elf_local_got_refcounts (abfd) [symndx] += 1;
 
-  return TRUE;
+  return true;
 }
 
-static bfd_boolean
+static bool
 bad_static_reloc (bfd *abfd, unsigned r_type, struct elf_link_hash_entry *h)
 {
   reloc_howto_type * r = riscv_elf_rtype_to_howto (abfd, r_type);
@@ -613,14 +619,14 @@ bad_static_reloc (bfd *abfd, unsigned r_type, struct elf_link_hash_entry *h)
      abfd, r ? r->name : _("<unknown>"),
      h != NULL ? h->root.root.string : "a local symbol");
   bfd_set_error (bfd_error_bad_value);
-  return FALSE;
+  return false;
 }
 
 /* Look through the relocs for a section during the first phase, and
    allocate space in the global offset table or procedure linkage
    table.  */
 
-static bfd_boolean
+static bool
 riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                        asection *sec, const Elf_Internal_Rela *relocs)
 {
@@ -631,7 +637,7 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
   asection *sreloc = NULL;
 
   if (bfd_link_relocatable (info))
-    return TRUE;
+    return true;
 
   htab = riscv_elf_hash_table (info);
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
@@ -653,7 +659,7 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        {
          (*_bfd_error_handler) (_("%pB: bad symbol index: %d"),
                                 abfd, r_symndx);
-         return FALSE;
+         return false;
        }
 
       if (r_symndx < symtab_hdr->sh_info)
@@ -662,14 +668,14 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
          Elf_Internal_Sym *isym = bfd_sym_from_r_symndx (&htab->elf.sym_cache,
                                                          abfd, r_symndx);
          if (isym == NULL)
-           return FALSE;
+           return false;
 
          /* Check relocation against local STT_GNU_IFUNC symbol.  */
          if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
            {
-             h = riscv_elf_get_local_sym_hash (htab, abfd, rel, TRUE);
+             h = riscv_elf_get_local_sym_hash (htab, abfd, rel, true);
              if (h == NULL)
-               return FALSE;
+               return false;
 
              /* Fake STT_GNU_IFUNC global symbol.  */
              h->root.root.string = bfd_elf_sym_name (abfd, symtab_hdr,
@@ -706,7 +712,7 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                 executables.  */
              if (h->type == STT_GNU_IFUNC
                  && !_bfd_elf_create_ifunc_sections (htab->elf.dynobj, info))
-               return FALSE;
+               return false;
              break;
 
            default:
@@ -722,7 +728,7 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_RISCV_TLS_GD_HI20:
          if (!riscv_elf_record_got_reference (abfd, info, h, r_symndx)
              || !riscv_elf_record_tls_type (abfd, h, r_symndx, GOT_TLS_GD))
-           return FALSE;
+           return false;
          break;
 
        case R_RISCV_TLS_GOT_HI20:
@@ -730,13 +736,13 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
            info->flags |= DF_STATIC_TLS;
          if (!riscv_elf_record_got_reference (abfd, info, h, r_symndx)
              || !riscv_elf_record_tls_type (abfd, h, r_symndx, GOT_TLS_IE))
-           return FALSE;
+           return false;
          break;
 
        case R_RISCV_GOT_HI20:
          if (!riscv_elf_record_got_reference (abfd, info, h, r_symndx)
              || !riscv_elf_record_tls_type (abfd, h, r_symndx, GOT_NORMAL))
-           return FALSE;
+           return false;
          break;
 
        case R_RISCV_CALL:
@@ -872,10 +878,10 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                {
                  sreloc = _bfd_elf_make_dynamic_reloc_section
                    (sec, htab->elf.dynobj, RISCV_ELF_LOG_WORD_BYTES,
-                   abfd, /*rela?*/ TRUE);
+                   abfd, /*rela?*/ true);
 
                  if (sreloc == NULL)
-                   return FALSE;
+                   return false;
                }
 
              /* If this is a global symbol, we count the number of
@@ -895,7 +901,7 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                  isym = bfd_sym_from_r_symndx (&htab->elf.sym_cache,
                                                abfd, r_symndx);
                  if (isym == NULL)
-                   return FALSE;
+                   return false;
 
                  s = bfd_section_from_elf_index (abfd, isym->st_shndx);
                  if (s == NULL)
@@ -912,7 +918,7 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                  p = ((struct elf_dyn_relocs *)
                       bfd_alloc (htab->elf.dynobj, amt));
                  if (p == NULL)
-                   return FALSE;
+                   return false;
                  p->next = *head;
                  *head = p;
                  p->sec = sec;
@@ -928,12 +934,12 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
        case R_RISCV_GNU_VTINHERIT:
          if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
-           return FALSE;
+           return false;
          break;
 
        case R_RISCV_GNU_VTENTRY:
          if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
-           return FALSE;
+           return false;
          break;
 
        default:
@@ -941,7 +947,7 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        }
     }
 
-  return TRUE;
+  return true;
 }
 
 static asection *
@@ -968,7 +974,7 @@ riscv_elf_gc_mark_hook (asection *sec,
    change the definition to something the rest of the link can
    understand.  */
 
-static bfd_boolean
+static bool
 riscv_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
                                 struct elf_link_hash_entry *h)
 {
@@ -1010,7 +1016,7 @@ riscv_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
          h->needs_plt = 0;
        }
 
-      return TRUE;
+      return true;
     }
   else
     h->plt.offset = (bfd_vma) -1;
@@ -1024,7 +1030,7 @@ riscv_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
       BFD_ASSERT (def->root.type == bfd_link_hash_defined);
       h->root.u.def.section = def->root.u.def.section;
       h->root.u.def.value = def->root.u.def.value;
-      return TRUE;
+      return true;
     }
 
   /* This is a reference to a symbol defined by a dynamic object which
@@ -1035,18 +1041,18 @@ riscv_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
      For such cases we need not do anything here; the relocations will
      be handled correctly by relocate_section.  */
   if (bfd_link_pic (info))
-    return TRUE;
+    return true;
 
   /* If there are no references to this symbol that do not use the
      GOT, we don't need to generate a copy reloc.  */
   if (!h->non_got_ref)
-    return TRUE;
+    return true;
 
   /* If -z nocopyreloc was given, we won't generate them either.  */
   if (info->nocopyreloc)
     {
       h->non_got_ref = 0;
-      return TRUE;
+      return true;
     }
 
   /* If we don't find any dynamic relocs in read-only sections, then
@@ -1054,7 +1060,7 @@ riscv_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
   if (!_bfd_elf_readonly_dynrelocs (h))
     {
       h->non_got_ref = 0;
-      return TRUE;
+      return true;
     }
 
   /* We must allocate the symbol in our .dynbss section, which will
@@ -1099,7 +1105,7 @@ riscv_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
 /* Allocate space in .plt, .got and associated reloc sections for
    dynamic relocs.  */
 
-static bfd_boolean
+static bool
 allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
 {
   struct bfd_link_info *info;
@@ -1107,7 +1113,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   struct elf_dyn_relocs *p;
 
   if (h->root.type == bfd_link_hash_indirect)
-    return TRUE;
+    return true;
 
   info = (struct bfd_link_info *) inf;
   htab = riscv_elf_hash_table (info);
@@ -1120,14 +1126,14 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
       && htab->elf.dynamic_sections_created
       && strcmp (h->root.root.string, RISCV_GP_SYMBOL) == 0
       && !bfd_elf_link_record_dynamic_symbol (info, h))
-    return FALSE;
+    return false;
 
   /* Since STT_GNU_IFUNC symbols must go through PLT, we handle them
      in the allocate_ifunc_dynrelocs and allocate_local_ifunc_dynrelocs,
      if they are defined and referenced in a non-shared object.  */
   if (h->type == STT_GNU_IFUNC
       && h->def_regular)
-    return TRUE;
+    return true;
   else if (htab->elf.dynamic_sections_created
           && h->plt.refcount > 0)
     {
@@ -1137,7 +1143,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
          && !h->forced_local)
        {
          if (! bfd_elf_link_record_dynamic_symbol (info, h))
-           return FALSE;
+           return false;
        }
 
       if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, bfd_link_pic (info), h))
@@ -1169,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
        {
@@ -1185,7 +1196,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   if (h->got.refcount > 0)
     {
       asection *s;
-      bfd_boolean dyn;
+      bool dyn;
       int tls_type = riscv_elf_hash_entry (h)->tls_type;
 
       /* Make sure this symbol is output as a dynamic symbol.
@@ -1194,7 +1205,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
          && !h->forced_local)
        {
          if (! bfd_elf_link_record_dynamic_symbol (info, h))
-           return FALSE;
+           return false;
        }
 
       s = htab->elf.sgot;
@@ -1228,7 +1239,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
     h->got.offset = (bfd_vma) -1;
 
   if (h->dyn_relocs == NULL)
-    return TRUE;
+    return true;
 
   /* In the shared -Bsymbolic case, discard space allocated for
      dynamic pc-relative relocs against symbols which turn out to be
@@ -1268,7 +1279,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
                   && !h->forced_local)
            {
              if (! bfd_elf_link_record_dynamic_symbol (info, h))
-               return FALSE;
+               return false;
            }
        }
     }
@@ -1291,7 +1302,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
              && !h->forced_local)
            {
              if (! bfd_elf_link_record_dynamic_symbol (info, h))
-               return FALSE;
+               return false;
            }
 
          /* If that succeeded, we know we'll be keeping all the
@@ -1312,20 +1323,20 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
       sreloc->size += p->count * sizeof (ElfNN_External_Rela);
     }
 
-  return TRUE;
+  return true;
 }
 
 /* Allocate space in .plt, .got and associated reloc sections for
    ifunc dynamic relocs.  */
 
-static bfd_boolean
+static bool
 allocate_ifunc_dynrelocs (struct elf_link_hash_entry *h,
                          void *inf)
 {
   struct bfd_link_info *info;
 
   if (h->root.type == bfd_link_hash_indirect)
-    return TRUE;
+    return true;
 
   if (h->root.type == bfd_link_hash_warning)
     h = (struct elf_link_hash_entry *) h->root.u.i.link;
@@ -1341,14 +1352,14 @@ allocate_ifunc_dynrelocs (struct elf_link_hash_entry *h,
                                               PLT_ENTRY_SIZE,
                                               PLT_HEADER_SIZE,
                                               GOT_ENTRY_SIZE,
-                                              TRUE);
-  return TRUE;
+                                              true);
+  return true;
 }
 
 /* Allocate space in .plt, .got and associated reloc sections for
    local ifunc dynamic relocs.  */
 
-static bfd_boolean
+static int
 allocate_local_ifunc_dynrelocs (void **slot, void *inf)
 {
   struct elf_link_hash_entry *h
@@ -1364,7 +1375,7 @@ allocate_local_ifunc_dynrelocs (void **slot, void *inf)
   return allocate_ifunc_dynrelocs (h, inf);
 }
 
-static bfd_boolean
+static bool
 riscv_elf_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
 {
   struct riscv_elf_link_hash_table *htab;
@@ -1476,7 +1487,7 @@ riscv_elf_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
       struct elf_link_hash_entry *got;
       got = elf_link_hash_lookup (elf_hash_table (info),
                                  "_GLOBAL_OFFSET_TABLE_",
-                                 FALSE, FALSE, FALSE);
+                                 false, false, false);
 
       /* Don't allocate .got.plt section if there are no GOT nor PLT
         entries and there is no refeence to _GLOBAL_OFFSET_TABLE_.  */
@@ -1511,7 +1522,7 @@ riscv_elf_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
          /* Strip this section if we don't need it; see the
             comment below.  */
        }
-      else if (strncmp (s->name, ".rela", 5) == 0)
+      else if (startswith (s->name, ".rela"))
        {
          if (s->size != 0)
            {
@@ -1549,10 +1560,21 @@ riscv_elf_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
         at the beginning, and we don't want garbage.  */
       s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->size);
       if (s->contents == NULL)
-       return FALSE;
+       return false;
+    }
+
+  /* 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 _bfd_elf_add_dynamic_tags (output_bfd, info, TRUE);
+  return true;
 }
 
 #define TP_OFFSET 0
@@ -1587,7 +1609,7 @@ riscv_global_pointer_value (struct bfd_link_info *info)
 {
   struct bfd_link_hash_entry *h;
 
-  h = bfd_link_hash_lookup (info->hash, RISCV_GP_SYMBOL, FALSE, FALSE, TRUE);
+  h = bfd_link_hash_lookup (info->hash, RISCV_GP_SYMBOL, false, false, true);
   if (h == NULL || h->type != bfd_link_hash_defined)
     return 0;
 
@@ -1733,25 +1755,41 @@ perform_relocation (const reloc_howto_type *howto,
 
 typedef struct
 {
+  /* PC value.  */
   bfd_vma address;
+  /* Relocation value with addend.  */
   bfd_vma value;
+  /* Original reloc type.  */
+  int type;
 } riscv_pcrel_hi_reloc;
 
 typedef struct riscv_pcrel_lo_reloc
 {
+  /* PC value of auipc.  */
+  bfd_vma address;
+  /* Internal relocation.  */
+  const Elf_Internal_Rela *reloc;
+  /* Record the following information helps to resolve the %pcrel
+     which cross different input section.  For now we build a hash
+     for pcrel at the start of riscv_elf_relocate_section, and then
+     free the hash at the end.  But riscv_elf_relocate_section only
+     handles an input section at a time, so that means we can only
+     resolve the %pcrel_hi and %pcrel_lo which are in the same input
+     section.  Otherwise, we will report dangerous relocation errors
+     for those %pcrel which are not in the same input section.  */
   asection *input_section;
   struct bfd_link_info *info;
   reloc_howto_type *howto;
-  const Elf_Internal_Rela *reloc;
-  bfd_vma addr;
-  const char *name;
   bfd_byte *contents;
+  /* The next riscv_pcrel_lo_reloc.  */
   struct riscv_pcrel_lo_reloc *next;
 } riscv_pcrel_lo_reloc;
 
 typedef struct
 {
+  /* Hash table for riscv_pcrel_hi_reloc.  */
   htab_t hi_relocs;
+  /* Linked list for riscv_pcrel_lo_reloc.  */
   riscv_pcrel_lo_reloc *lo_relocs;
 } riscv_pcrel_relocs;
 
@@ -1762,14 +1800,14 @@ riscv_pcrel_reloc_hash (const void *entry)
   return (hashval_t)(e->address >> 2);
 }
 
-static bfd_boolean
+static int
 riscv_pcrel_reloc_eq (const void *entry1, const void *entry2)
 {
   const riscv_pcrel_hi_reloc *e1 = entry1, *e2 = entry2;
   return e1->address == e2->address;
 }
 
-static bfd_boolean
+static bool
 riscv_init_pcrel_relocs (riscv_pcrel_relocs *p)
 {
   p->lo_relocs = NULL;
@@ -1793,14 +1831,13 @@ riscv_free_pcrel_relocs (riscv_pcrel_relocs *p)
   htab_delete (p->hi_relocs);
 }
 
-static bfd_boolean
+static bool
 riscv_zero_pcrel_hi_reloc (Elf_Internal_Rela *rel,
                           struct bfd_link_info *info,
                           bfd_vma pc,
                           bfd_vma addr,
                           bfd_byte *contents,
-                          const reloc_howto_type *howto,
-                          bfd *input_bfd ATTRIBUTE_UNUSED)
+                          const reloc_howto_type *howto)
 {
   /* We may need to reference low addreses in PC-relative modes even when the
      PC is far away from these addresses.  For example, undefweak references
@@ -1810,66 +1847,68 @@ riscv_zero_pcrel_hi_reloc (Elf_Internal_Rela *rel,
      programs to work we simply convert the PC-relative auipc sequences to
      0-relative lui sequences.  */
   if (bfd_link_pic (info))
-    return FALSE;
+    return false;
 
   /* If it's possible to reference the symbol using auipc we do so, as that's
      more in the spirit of the PC-relative relocations we're processing.  */
   bfd_vma offset = addr - pc;
   if (ARCH_SIZE == 32 || VALID_UTYPE_IMM (RISCV_CONST_HIGH_PART (offset)))
-    return FALSE;
+    return false;
 
   /* If it's impossible to reference this with a LUI-based offset then don't
      bother to convert it at all so users still see the PC-relative relocation
      in the truncation message.  */
   if (ARCH_SIZE > 32 && !VALID_UTYPE_IMM (RISCV_CONST_HIGH_PART (addr)))
-    return FALSE;
+    return false;
 
   rel->r_info = ELFNN_R_INFO (addr, R_RISCV_HI20);
 
   bfd_vma insn = riscv_get_insn (howto->bitsize, contents + rel->r_offset);
   insn = (insn & ~MASK_AUIPC) | MATCH_LUI;
   riscv_put_insn (howto->bitsize, insn, contents + rel->r_offset);
-  return TRUE;
+  return true;
 }
 
-static bfd_boolean
-riscv_record_pcrel_hi_reloc (riscv_pcrel_relocs *p, bfd_vma addr,
-                            bfd_vma value, bfd_boolean absolute)
+static bool
+riscv_record_pcrel_hi_reloc (riscv_pcrel_relocs *p,
+                            bfd_vma addr,
+                            bfd_vma value,
+                            int type,
+                            bool absolute)
 {
   bfd_vma offset = absolute ? value : value - addr;
-  riscv_pcrel_hi_reloc entry = {addr, offset};
+  riscv_pcrel_hi_reloc entry = {addr, offset, type};
   riscv_pcrel_hi_reloc **slot =
     (riscv_pcrel_hi_reloc **) htab_find_slot (p->hi_relocs, &entry, INSERT);
 
   BFD_ASSERT (*slot == NULL);
   *slot = (riscv_pcrel_hi_reloc *) bfd_malloc (sizeof (riscv_pcrel_hi_reloc));
   if (*slot == NULL)
-    return FALSE;
+    return false;
   **slot = entry;
-  return TRUE;
+  return true;
 }
 
-static bfd_boolean
+static bool
 riscv_record_pcrel_lo_reloc (riscv_pcrel_relocs *p,
+                            bfd_vma addr,
+                            const Elf_Internal_Rela *reloc,
                             asection *input_section,
                             struct bfd_link_info *info,
                             reloc_howto_type *howto,
-                            const Elf_Internal_Rela *reloc,
-                            bfd_vma addr,
-                            const char *name,
                             bfd_byte *contents)
 {
   riscv_pcrel_lo_reloc *entry;
   entry = (riscv_pcrel_lo_reloc *) bfd_malloc (sizeof (riscv_pcrel_lo_reloc));
   if (entry == NULL)
-    return FALSE;
-  *entry = (riscv_pcrel_lo_reloc) {input_section, info, howto, reloc, addr,
-                                  name, contents, p->lo_relocs};
+    return false;
+  *entry = (riscv_pcrel_lo_reloc) {addr, reloc, input_section, info,
+                                  howto, contents, p->lo_relocs};
   p->lo_relocs = entry;
-  return TRUE;
+  return true;
 }
 
-static bfd_boolean
+static bool
 riscv_resolve_pcrel_lo_relocs (riscv_pcrel_relocs *p)
 {
   riscv_pcrel_lo_reloc *r;
@@ -1878,26 +1917,44 @@ riscv_resolve_pcrel_lo_relocs (riscv_pcrel_relocs *p)
     {
       bfd *input_bfd = r->input_section->owner;
 
-      riscv_pcrel_hi_reloc search = {r->addr, 0};
+      riscv_pcrel_hi_reloc search = {r->address, 0, 0};
       riscv_pcrel_hi_reloc *entry = htab_find (p->hi_relocs, &search);
-      if (entry == NULL
-         /* Check for overflow into bit 11 when adding reloc addend.  */
-         || (!(entry->value & 0x800)
-             && ((entry->value + r->reloc->r_addend) & 0x800)))
+      /* There may be a risk if the %pcrel_lo with addend refers to
+        an IFUNC symbol.  The %pcrel_hi has been relocated to plt,
+        so the corresponding %pcrel_lo with addend looks wrong.  */
+      char *string = NULL;
+      if (entry == NULL)
+       string = _("%pcrel_lo missing matching %pcrel_hi");
+      else if (entry->type == R_RISCV_GOT_HI20
+              && r->reloc->r_addend != 0)
+       string = _("%pcrel_lo with addend isn't allowed for R_RISCV_GOT_HI20");
+      else if (RISCV_CONST_HIGH_PART (entry->value)
+              != RISCV_CONST_HIGH_PART (entry->value + r->reloc->r_addend))
+       {
+         /* Check the overflow when adding reloc addend.  */
+         if (asprintf (&string,
+                       _("%%pcrel_lo overflow with an addend, the "
+                         "value of %%pcrel_hi is 0x%" PRIx64 " without "
+                         "any addend, but may be 0x%" PRIx64 " after "
+                         "adding the %%pcrel_lo addend"),
+                       (int64_t) RISCV_CONST_HIGH_PART (entry->value),
+                       (int64_t) RISCV_CONST_HIGH_PART
+                               (entry->value + r->reloc->r_addend)) == -1)
+           string = _("%pcrel_lo overflow with an addend");
+       }
+
+      if (string != NULL)
        {
-         char *string = (entry == NULL
-                         ? "%pcrel_lo missing matching %pcrel_hi"
-                         : "%pcrel_lo overflow with an addend");
          (*r->info->callbacks->reloc_dangerous)
            (r->info, string, input_bfd, r->input_section, r->reloc->r_offset);
-         return TRUE;
+         return true;
        }
 
       perform_relocation (r->howto, r->reloc, entry->value, r->input_section,
                          input_bfd, r->contents);
     }
 
-  return TRUE;
+  return true;
 }
 
 /* Relocate a RISC-V ELF section.
@@ -1928,7 +1985,7 @@ riscv_resolve_pcrel_lo_relocs (riscv_pcrel_relocs *p)
    section, which means that the addend must be adjusted
    accordingly.  */
 
-static bfd_boolean
+static int
 riscv_elf_relocate_section (bfd *output_bfd,
                            struct bfd_link_info *info,
                            bfd *input_bfd,
@@ -1941,15 +1998,15 @@ riscv_elf_relocate_section (bfd *output_bfd,
   Elf_Internal_Rela *rel;
   Elf_Internal_Rela *relend;
   riscv_pcrel_relocs pcrel_relocs;
-  bfd_boolean ret = FALSE;
+  bool ret = false;
   struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
   Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (input_bfd);
   struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (input_bfd);
   bfd_vma *local_got_offsets = elf_local_got_offsets (input_bfd);
-  bfd_boolean absolute;
+  bool absolute;
 
   if (!riscv_init_pcrel_relocs (&pcrel_relocs))
-    return FALSE;
+    return false;
 
   relend = relocs + input_section->reloc_count;
   for (rel = relocs; rel < relend; rel++)
@@ -1962,13 +2019,13 @@ riscv_elf_relocate_section (bfd *output_bfd,
       bfd_reloc_status_type r = bfd_reloc_ok;
       const char *name = NULL;
       bfd_vma off, ie_off;
-      bfd_boolean unresolved_reloc, is_ie = FALSE;
+      bool unresolved_reloc, is_ie = false;
       bfd_vma pc = sec_addr (input_section) + rel->r_offset;
       int r_type = ELFNN_R_TYPE (rel->r_info), tls_type;
       reloc_howto_type *howto = riscv_elf_rtype_to_howto (input_bfd, r_type);
       const char *msg = NULL;
       char *msg_buf = NULL;
-      bfd_boolean resolved_to_zero;
+      bool resolved_to_zero;
 
       if (howto == NULL
          || r_type == R_RISCV_GNU_VTINHERIT || r_type == R_RISCV_GNU_VTENTRY)
@@ -1979,7 +2036,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
       h = NULL;
       sym = NULL;
       sec = NULL;
-      unresolved_reloc = FALSE;
+      unresolved_reloc = false;
       if (r_symndx < symtab_hdr->sh_info)
        {
          sym = local_syms + r_symndx;
@@ -1990,7 +2047,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
          if (!bfd_link_relocatable (info)
              && ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
            {
-             h = riscv_elf_get_local_sym_hash (htab, input_bfd, rel, FALSE);
+             h = riscv_elf_get_local_sym_hash (htab, input_bfd, rel, false);
              if (h == NULL)
                abort ();
 
@@ -2001,7 +2058,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
        }
       else
        {
-         bfd_boolean warned, ignored;
+         bool warned, ignored;
 
          RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
                                   r_symndx, symtab_hdr, sym_hashes,
@@ -2084,7 +2141,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
                       "symbol `%s' has non-zero addend: %" PRId64),
                     input_bfd, howto->name, name, (int64_t) rel->r_addend);
                  bfd_set_error (bfd_error_bad_value);
-                 return FALSE;
+                 return false;
                }
 
                /* Generate dynamic relocation only when there is a non-GOT
@@ -2209,12 +2266,9 @@ riscv_elf_relocate_section (bfd *output_bfd,
                relocation = base_got->output_section->vma
                             + base_got->output_offset + off;
 
-               r_type = ELFNN_R_TYPE (rel->r_info);
-               howto = riscv_elf_rtype_to_howto (input_bfd, r_type);
-               if (howto == NULL)
-                 r = bfd_reloc_notsupported;
-               else if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
-                                                      relocation, FALSE))
+               if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
+                                                 relocation, r_type,
+                                                 false))
                  r = bfd_reloc_overflow;
                goto do_relocation;
 
@@ -2226,12 +2280,9 @@ riscv_elf_relocate_section (bfd *output_bfd,
                goto do_relocation;
 
              case R_RISCV_PCREL_HI20:
-               r_type = ELFNN_R_TYPE (rel->r_info);
-               howto = riscv_elf_rtype_to_howto (input_bfd, r_type);
-               if (howto == NULL)
-                 r = bfd_reloc_notsupported;
-               else if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
-                                                      relocation, FALSE))
+               if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
+                                                 relocation, r_type,
+                                                 false))
                  r = bfd_reloc_overflow;
                goto do_relocation;
 
@@ -2250,7 +2301,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
                 "symbol `%s' isn't supported"), input_bfd,
               howto->name, name);
              bfd_set_error (bfd_error_bad_value);
-             return FALSE;
+             return false;
            }
        }
 
@@ -2297,7 +2348,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
        case R_RISCV_GOT_HI20:
          if (h != NULL)
            {
-             bfd_boolean dyn, pic;
+             bool dyn, pic;
 
              off = h->got.offset;
              BFD_ASSERT (off != (bfd_vma) -1);
@@ -2329,7 +2380,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
                    }
                }
              else
-               unresolved_reloc = FALSE;
+               unresolved_reloc = false;
            }
          else
            {
@@ -2368,21 +2419,29 @@ riscv_elf_relocate_section (bfd *output_bfd,
                  local_got_offsets[r_symndx] |= 1;
                }
            }
-         relocation = sec_addr (htab->elf.sgot) + off;
-         absolute = riscv_zero_pcrel_hi_reloc (rel,
-                                               info,
-                                               pc,
-                                               relocation,
-                                               contents,
-                                               howto,
-                                               input_bfd);
-         r_type = ELFNN_R_TYPE (rel->r_info);
-         howto = riscv_elf_rtype_to_howto (input_bfd, r_type);
-         if (howto == NULL)
-           r = bfd_reloc_notsupported;
-         else if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
-                                                relocation, absolute))
-           r = bfd_reloc_overflow;
+
+         if (rel->r_addend != 0)
+           {
+             msg = _("The addend isn't allowed for R_RISCV_GOT_HI20");
+             r = bfd_reloc_dangerous;
+           }
+         else
+           {
+             /* Address of got entry.  */
+             relocation = sec_addr (htab->elf.sgot) + off;
+             absolute = riscv_zero_pcrel_hi_reloc (rel, info, pc,
+                                                   relocation, contents,
+                                                   howto);
+             /* Update howto if relocation is changed.  */
+             howto = riscv_elf_rtype_to_howto (input_bfd,
+                                               ELFNN_R_TYPE (rel->r_info));
+             if (howto == NULL)
+               r = bfd_reloc_notsupported;
+             else if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
+                                                    relocation, r_type,
+                                                    absolute))
+               r = bfd_reloc_overflow;
+           }
          break;
 
        case R_RISCV_ADD8:
@@ -2432,7 +2491,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
            {
              /* Refer to the PLT entry.  */
              relocation = sec_addr (htab->elf.splt) + h->plt.offset;
-             unresolved_reloc = FALSE;
+             unresolved_reloc = false;
            }
          break;
 
@@ -2464,7 +2523,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
        case R_RISCV_GPREL_S:
          {
            bfd_vma gp = riscv_global_pointer_value (info);
-           bfd_boolean x0_base = VALID_ITYPE_IMM (relocation + rel->r_addend);
+           bool x0_base = VALID_ITYPE_IMM (relocation + rel->r_addend);
            if (x0_base || VALID_ITYPE_IMM (relocation + rel->r_addend - gp))
              {
                /* We can use x0 or gp as the base register.  */
@@ -2483,20 +2542,16 @@ riscv_elf_relocate_section (bfd *output_bfd,
          }
 
        case R_RISCV_PCREL_HI20:
-         absolute = riscv_zero_pcrel_hi_reloc (rel,
-                                               info,
-                                               pc,
-                                               relocation,
-                                               contents,
-                                               howto,
-                                               input_bfd);
-         r_type = ELFNN_R_TYPE (rel->r_info);
-         howto = riscv_elf_rtype_to_howto (input_bfd, r_type);
+         absolute = riscv_zero_pcrel_hi_reloc (rel, info, pc, relocation,
+                                               contents, howto);
+         /* Update howto if relocation is changed.  */
+         howto = riscv_elf_rtype_to_howto (input_bfd,
+                                           ELFNN_R_TYPE (rel->r_info));
          if (howto == NULL)
            r = bfd_reloc_notsupported;
          else if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
                                                 relocation + rel->r_addend,
-                                                absolute))
+                                                r_type, absolute))
            r = bfd_reloc_overflow;
          break;
 
@@ -2516,8 +2571,8 @@ riscv_elf_relocate_section (bfd *output_bfd,
              break;
            }
 
-         if (riscv_record_pcrel_lo_reloc (&pcrel_relocs, input_section, info,
-                                          howto, rel, relocation, name,
+         if (riscv_record_pcrel_lo_reloc (&pcrel_relocs, relocation, rel,
+                                          input_section, info, howto,
                                           contents))
            continue;
          r = bfd_reloc_overflow;
@@ -2551,7 +2606,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
            {
              Elf_Internal_Rela outrel;
              asection *sreloc;
-             bfd_boolean skip_static_relocation, skip_dynamic_relocation;
+             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
@@ -2588,7 +2643,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
          break;
 
        case R_RISCV_TLS_GOT_HI20:
-         is_ie = TRUE;
+         is_ie = true;
          /* Fall through.  */
 
        case R_RISCV_TLS_GD_HI20:
@@ -2617,14 +2672,14 @@ riscv_elf_relocate_section (bfd *output_bfd,
            {
              Elf_Internal_Rela outrel;
              int indx = 0;
-             bfd_boolean need_relocs = FALSE;
+             bool need_relocs = false;
 
              if (htab->elf.srelgot == NULL)
                abort ();
 
              if (h != NULL)
                {
-                 bfd_boolean dyn, pic;
+                 bool dyn, pic;
                  dyn = htab->elf.dynamic_sections_created;
                  pic = bfd_link_pic (info);
 
@@ -2639,7 +2694,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
                  && (h == NULL
                      || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
                      || h->root.type != bfd_link_hash_undefweak))
-                   need_relocs = TRUE;
+                   need_relocs = true;
 
              if (tls_type & GOT_TLS_GD)
                {
@@ -2710,9 +2765,10 @@ riscv_elf_relocate_section (bfd *output_bfd,
          BFD_ASSERT (off < (bfd_vma) -2);
          relocation = sec_addr (htab->elf.sgot) + off + (is_ie ? ie_off : 0);
          if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
-                                           relocation, FALSE))
+                                           relocation, r_type,
+                                           false))
            r = bfd_reloc_overflow;
-         unresolved_reloc = FALSE;
+         unresolved_reloc = false;
          break;
 
        default:
@@ -2778,7 +2834,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
        case bfd_reloc_undefined:
          info->callbacks->undefined_symbol
            (info, name, input_bfd, input_section, rel->r_offset,
-            TRUE);
+            true);
          break;
 
        case bfd_reloc_outofrange:
@@ -2813,7 +2869,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
 
       /* We already reported the error via a callback, so don't try to report
         it again by returning false.  That leads to spurious errors.  */
-      ret = TRUE;
+      ret = true;
       goto out;
     }
 
@@ -2826,7 +2882,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
 /* Finish up dynamic symbol handling.  We set the contents of various
    dynamic sections here.  */
 
-static bfd_boolean
+static bool
 riscv_elf_finish_dynamic_symbol (bfd *output_bfd,
                                 struct bfd_link_info *info,
                                 struct elf_link_hash_entry *h,
@@ -2868,7 +2924,7 @@ riscv_elf_finish_dynamic_symbol (bfd *output_bfd,
          || plt == NULL
          || gotplt == NULL
          || relplt == NULL)
-       return FALSE;
+       return false;
 
       /* Calculate the address of the PLT header.  */
       header_address = sec_addr (plt);
@@ -2896,7 +2952,7 @@ riscv_elf_finish_dynamic_symbol (bfd *output_bfd,
       if (! riscv_make_plt_entry (output_bfd, got_address,
                                  header_address + h->plt.offset,
                                  plt_entry))
-       return FALSE;
+       return false;
 
       for (i = 0; i < PLT_ENTRY_INSNS; i++)
        bfd_putl32 (plt_entry[i], loc + 4*i);
@@ -2956,7 +3012,7 @@ riscv_elf_finish_dynamic_symbol (bfd *output_bfd,
       asection *sgot;
       asection *srela;
       Elf_Internal_Rela rela;
-      bfd_boolean use_elf_append_rela = TRUE;
+      bool use_elf_append_rela = true;
 
       /* This symbol has an entry in the GOT.  Set it up.  */
 
@@ -2982,7 +3038,7 @@ riscv_elf_finish_dynamic_symbol (bfd *output_bfd,
 
                  /* Do not use riscv_elf_append_rela to add dynamic
                     relocs.  */
-                 use_elf_append_rela = FALSE;
+                 use_elf_append_rela = false;
                }
 
              if (SYMBOL_REFERENCES_LOCAL (info, h))
@@ -3029,7 +3085,7 @@ riscv_elf_finish_dynamic_symbol (bfd *output_bfd,
                                       + h->plt.offset),
                          htab->elf.sgot->contents
                          + (h->got.offset & ~(bfd_vma) 1));
-             return TRUE;
+             return true;
            }
        }
       else if (bfd_link_pic (info)
@@ -3100,13 +3156,13 @@ riscv_elf_finish_dynamic_symbol (bfd *output_bfd,
       || (h == htab->elf.hgot || h == htab->elf.hplt))
     sym->st_shndx = SHN_ABS;
 
-  return TRUE;
+  return true;
 }
 
 /* Finish up local dynamic symbol handling.  We set the contents of
    various dynamic sections here.  */
 
-static bfd_boolean
+static int
 riscv_elf_finish_local_dynamic_symbol (void **slot, void *inf)
 {
   struct elf_link_hash_entry *h = (struct elf_link_hash_entry *) *slot;
@@ -3117,7 +3173,7 @@ riscv_elf_finish_local_dynamic_symbol (void **slot, void *inf)
 
 /* Finish up the dynamic sections.  */
 
-static bfd_boolean
+static bool
 riscv_finish_dyn (bfd *output_bfd, struct bfd_link_info *info,
                  bfd *dynobj, asection *sdyn)
 {
@@ -3154,10 +3210,10 @@ riscv_finish_dyn (bfd *output_bfd, struct bfd_link_info *info,
 
       bed->s->swap_dyn_out (output_bfd, &dyn, dyncon);
     }
-  return TRUE;
+  return true;
 }
 
-static bfd_boolean
+static bool
 riscv_elf_finish_dynamic_sections (bfd *output_bfd,
                                   struct bfd_link_info *info)
 {
@@ -3174,7 +3230,7 @@ riscv_elf_finish_dynamic_sections (bfd *output_bfd,
   if (elf_hash_table (info)->dynamic_sections_created)
     {
       asection *splt;
-      bfd_boolean ret;
+      bool ret;
 
       splt = htab->elf.splt;
       BFD_ASSERT (splt != NULL && sdyn != NULL);
@@ -3211,7 +3267,7 @@ riscv_elf_finish_dynamic_sections (bfd *output_bfd,
        {
          (*_bfd_error_handler)
            (_("discarded output section: `%pA'"), htab->elf.sgotplt);
-         return FALSE;
+         return false;
        }
 
       if (htab->elf.sgotplt->size > 0)
@@ -3246,7 +3302,7 @@ riscv_elf_finish_dynamic_sections (bfd *output_bfd,
                 riscv_elf_finish_local_dynamic_symbol,
                 info);
 
-  return TRUE;
+  return true;
 }
 
 /* Return address for Ith PLT stub in section PLT, for relocation REL
@@ -3309,53 +3365,38 @@ static riscv_subset_list_t merged_subsets;
 
 /* Predicator for standard extension.  */
 
-static bfd_boolean
+static bool
 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 bfd_boolean
-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))
     {
-      _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'.  */
 
-static bfd_boolean
+static bool
 riscv_i_or_e_p (bfd *ibfd,
                const char *arch,
                struct riscv_subset_t *subset)
@@ -3367,9 +3408,9 @@ riscv_i_or_e_p (bfd *ibfd,
        (_("error: %pB: corrupted ISA string '%s'.  "
           "First letter should be 'i' or 'e' but got '%s'"),
           ibfd, arch, subset->name);
-      return FALSE;
+      return false;
     }
-  return TRUE;
+  return true;
 }
 
 /* Merge standard extensions.
@@ -3384,24 +3425,24 @@ riscv_i_or_e_p (bfd *ibfd,
      `pin`: Subset list for input object.
      `pout`: Subset list for output object.  */
 
-static bfd_boolean
+static bool
 riscv_merge_std_ext (bfd *ibfd,
                     const char *in_arch,
                     const char *out_arch,
                     struct riscv_subset_t **pin,
                     struct riscv_subset_t **pout)
 {
-  const char *standard_exts = riscv_supported_std_ext ();
+  const char *standard_exts = "mafdqlcbjtpvn";
   const char *p;
   struct riscv_subset_t *in = *pin;
   struct riscv_subset_t *out = *pout;
 
   /* First letter should be 'i' or 'e'.  */
   if (!riscv_i_or_e_p (ibfd, in_arch, in))
-    return FALSE;
+    return false;
 
   if (!riscv_i_or_e_p (ibfd, out_arch, out))
-    return FALSE;
+    return false;
 
   if (strcasecmp (in->name, out->name) != 0)
     {
@@ -3409,13 +3450,12 @@ riscv_merge_std_ext (bfd *ibfd,
       _bfd_error_handler
        (_("error: %pB: mis-matched ISA string to merge '%s' and '%s'"),
         ibfd, in->name, out->name);
-      return FALSE;
+      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;
@@ -3425,7 +3465,7 @@ riscv_merge_std_ext (bfd *ibfd,
     {
       struct riscv_subset_t *ext_in, *ext_out, *ext_merged;
       char find_ext[2] = {*p, '\0'};
-      bfd_boolean find_in, find_out;
+      bool find_in, find_out;
 
       find_in = riscv_lookup_subset (&in_subsets, find_ext, &ext_in);
       find_out = riscv_lookup_subset (&out_subsets, find_ext, &ext_out);
@@ -3433,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,
@@ -3450,16 +3488,15 @@ riscv_merge_std_ext (bfd *ibfd,
   *pin = in;
   *pout = out;
 
-  return TRUE;
+  return true;
 }
 
 /* Merge multi letter extensions.  PIN is a pointer to the head of the input
    object subset list.  Likewise for POUT and the output object.  Return TRUE
    on success and FALSE when a conflict is found.  */
 
-static bfd_boolean
-riscv_merge_multi_letter_ext (bfd *ibfd,
-                             riscv_subset_t **pin,
+static bool
+riscv_merge_multi_letter_ext (riscv_subset_t **pin,
                              riscv_subset_t **pout)
 {
   riscv_subset_t *in = *pin;
@@ -3489,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);
@@ -3512,7 +3548,7 @@ riscv_merge_multi_letter_ext (bfd *ibfd,
        }
     }
 
-  return TRUE;
+  return true;
 }
 
 /* Merge Tag_RISCV_arch attribute.  */
@@ -3527,35 +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_out.subset_list = &out_subsets;
-  rpe_out.error_handler = _bfd_error_handler;
-  rpe_out.xlen = &xlen_out;
-  rpe_out.get_default_version = NULL;
+  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.  */
@@ -3576,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)
@@ -3608,26 +3631,26 @@ riscv_merge_arch_attr_info (bfd *ibfd, char *in_arch, char *out_arch)
 /* Merge object attributes from IBFD into output_bfd of INFO.
    Raise an error if there are conflicting attributes.  */
 
-static bfd_boolean
+static bool
 riscv_merge_attributes (bfd *ibfd, struct bfd_link_info *info)
 {
   bfd *obfd = info->output_bfd;
   obj_attribute *in_attr;
   obj_attribute *out_attr;
-  bfd_boolean result = TRUE;
-  bfd_boolean priv_attrs_merged = FALSE;
+  bool result = true;
+  bool priv_attrs_merged = false;
   const char *sec_name = get_elf_backend_data (ibfd)->obj_attrs_section;
   unsigned int i;
 
   /* Skip linker created files.  */
   if (ibfd->flags & BFD_LINKER_CREATED)
-    return TRUE;
+    return true;
 
   /* Skip any input that doesn't have an attribute section.
      This enables to link object files without attribute section with
      any others.  */
   if (bfd_get_section_by_name (ibfd, sec_name) == NULL)
-    return TRUE;
+    return true;
 
   if (!elf_known_obj_attributes_proc (obfd)[0].i)
     {
@@ -3640,7 +3663,7 @@ riscv_merge_attributes (bfd *ibfd, struct bfd_link_info *info)
         initialized.  */
       out_attr[0].i = 1;
 
-      return TRUE;
+      return true;
     }
 
   in_attr = elf_known_obj_attributes_proc (ibfd);
@@ -3663,7 +3686,7 @@ riscv_merge_attributes (bfd *ibfd, struct bfd_link_info *info)
                                            out_attr[Tag_RISCV_arch].s);
            if (merged_arch == NULL)
              {
-               result = FALSE;
+               result = false;
                out_attr[Tag_RISCV_arch].s = "";
              }
            else
@@ -3733,7 +3756,7 @@ riscv_merge_attributes (bfd *ibfd, struct bfd_link_info *info)
                    out_attr[Tag_c].i = in_attr[Tag_c].i;
                  }
              }
-           priv_attrs_merged = TRUE;
+           priv_attrs_merged = true;
          }
        break;
 
@@ -3752,7 +3775,7 @@ riscv_merge_attributes (bfd *ibfd, struct bfd_link_info *info)
              (_("error: %pB use %u-byte stack aligned but the output "
                 "use %u-byte stack aligned"),
               ibfd, in_attr[i].i, out_attr[i].i);
-           result = FALSE;
+           result = false;
          }
        break;
 
@@ -3767,7 +3790,7 @@ riscv_merge_attributes (bfd *ibfd, struct bfd_link_info *info)
 
   /* Merge Tag_compatibility attributes and any common GNU ones.  */
   if (!_bfd_elf_merge_object_attributes (ibfd, info))
-    return FALSE;
+    return false;
 
   /* Check for any attributes not known on RISC-V.  */
   result &= _bfd_elf_merge_unknown_attribute_list (ibfd, obfd);
@@ -3778,14 +3801,14 @@ riscv_merge_attributes (bfd *ibfd, struct bfd_link_info *info)
 /* Merge backend specific data from an object file to the output
    object file when linking.  */
 
-static bfd_boolean
+static bool
 _bfd_riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
 {
   bfd *obfd = info->output_bfd;
   flagword new_flags, old_flags;
 
   if (!is_riscv_elf (ibfd) || !is_riscv_elf (obfd))
-    return TRUE;
+    return true;
 
   if (strcmp (bfd_get_target (ibfd), bfd_get_target (obfd)) != 0)
     {
@@ -3793,14 +3816,14 @@ _bfd_riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
        (_("%pB: ABI is incompatible with that of the selected emulation:\n"
           "  target emulation `%s' does not match `%s'"),
         ibfd, bfd_get_target (ibfd), bfd_get_target (obfd));
-      return FALSE;
+      return false;
     }
 
   if (!_bfd_elf_merge_object_attributes (ibfd, info))
-    return FALSE;
+    return false;
 
   if (!riscv_merge_attributes (ibfd, info))
-    return FALSE;
+    return false;
 
   /* Check to see if the input BFD actually contains any sections.  If not,
      its flags may not have been initialized either, but it cannot actually
@@ -3811,25 +3834,25 @@ _bfd_riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
      case, there is no need to check for code specific flags.  */
   if (!(ibfd->flags & DYNAMIC))
     {
-      bfd_boolean null_input_bfd = TRUE;
-      bfd_boolean only_data_sections = TRUE;
+      bool null_input_bfd = true;
+      bool only_data_sections = true;
       asection *sec;
 
       for (sec = ibfd->sections; sec != NULL; sec = sec->next)
        {
-         null_input_bfd = FALSE;
+         null_input_bfd = false;
 
          if ((bfd_section_flags (sec)
               & (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS))
              == (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS))
            {
-             only_data_sections = FALSE;
+             only_data_sections = false;
              break;
            }
        }
 
       if (null_input_bfd || only_data_sections)
-       return TRUE;
+       return true;
     }
 
   new_flags = elf_elfheader (ibfd)->e_flags;
@@ -3837,9 +3860,9 @@ _bfd_riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
 
   if (!elf_flags_init (obfd))
     {
-      elf_flags_init (obfd) = TRUE;
+      elf_flags_init (obfd) = true;
       elf_elfheader (obfd)->e_flags = new_flags;
-      return TRUE;
+      return true;
     }
 
   /* Disallow linking different float ABIs.  */
@@ -3863,120 +3886,11 @@ _bfd_riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
   /* Allow linking RVC and non-RVC, and keep the RVC flag.  */
   elf_elfheader (obfd)->e_flags |= new_flags & EF_RISCV_RVC;
 
-  return TRUE;
+  return true;
 
  fail:
   bfd_set_error (bfd_error_bad_value);
-  return FALSE;
-}
-
-/* Delete some bytes from a section while relaxing.  */
-
-static bfd_boolean
-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;
+  return false;
 }
 
 /* A second format for recording PC-relative hi relocations.  This stores the
@@ -3990,7 +3904,7 @@ struct riscv_pcgp_hi_reloc
   bfd_vma hi_addr;
   unsigned hi_sym;
   asection *sym_sec;
-  bfd_boolean undefined_weak;
+  bool undefined_weak;
   riscv_pcgp_hi_reloc *next;
 };
 
@@ -4009,12 +3923,12 @@ typedef struct
 
 /* Initialize the pcgp reloc info in P.  */
 
-static bfd_boolean
+static bool
 riscv_init_pcgp_relocs (riscv_pcgp_relocs *p)
 {
   p->hi = NULL;
   p->lo = NULL;
-  return TRUE;
+  return true;
 }
 
 /* Free the pcgp reloc info in P.  */
@@ -4046,15 +3960,15 @@ riscv_free_pcgp_relocs (riscv_pcgp_relocs *p,
    The HI_ADDEND, HI_ADDR, HI_SYM, and SYM_SEC args contain info required to
    relax the corresponding lo part reloc.  */
 
-static bfd_boolean
+static bool
 riscv_record_pcgp_hi_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off,
                            bfd_vma hi_addend, bfd_vma hi_addr,
                            unsigned hi_sym, asection *sym_sec,
-                           bfd_boolean undefined_weak)
+                           bool undefined_weak)
 {
   riscv_pcgp_hi_reloc *new = bfd_malloc (sizeof (*new));
   if (!new)
-    return FALSE;
+    return false;
   new->hi_sec_off = hi_sec_off;
   new->hi_addend = hi_addend;
   new->hi_addr = hi_addr;
@@ -4063,7 +3977,7 @@ riscv_record_pcgp_hi_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off,
   new->undefined_weak = undefined_weak;
   new->next = p->hi;
   p->hi = new;
-  return TRUE;
+  return true;
 }
 
 /* Look up hi part pcgp reloc info in P, using HI_SEC_OFF as the lookup index.
@@ -4083,55 +3997,204 @@ riscv_find_pcgp_hi_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
 /* Record pcgp lo part reloc info in P, using HI_SEC_OFF as the lookup info.
    This is used to record relocs that can't be relaxed.  */
 
-static bfd_boolean
+static bool
 riscv_record_pcgp_lo_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
 {
   riscv_pcgp_lo_reloc *new = bfd_malloc (sizeof (*new));
   if (!new)
-    return FALSE;
+    return false;
   new->hi_sec_off = hi_sec_off;
   new->next = p->lo;
   p->lo = new;
-  return TRUE;
+  return true;
 }
 
 /* Look up lo part pcgp reloc info in P, using HI_SEC_OFF as the lookup index.
    This is used by a hi part reloc to find the corresponding lo part reloc.  */
 
-static bfd_boolean
+static bool
 riscv_find_pcgp_lo_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
 {
   riscv_pcgp_lo_reloc *c;
 
   for (c = p->lo; c != NULL; c = c->next)
     if (c->hi_sec_off == hi_sec_off)
-      return TRUE;
-  return FALSE;
+      return true;
+  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 bfd_boolean (*relax_func_t) (bfd *, asection *, asection *,
-                                    struct bfd_link_info *,
-                                    Elf_Internal_Rela *,
-                                    bfd_vma, bfd_vma, bfd_vma, bfd_boolean *,
-                                    riscv_pcgp_relocs *,
-                                    bfd_boolean undefined_weak);
+typedef bool (*relax_func_t) (bfd *, asection *, asection *,
+                             struct bfd_link_info *,
+                             Elf_Internal_Rela *,
+                             bfd_vma, bfd_vma, bfd_vma, bool *,
+                             riscv_pcgp_relocs *,
+                             bool undefined_weak);
 
 /* Relax AUIPC + JALR into JAL.  */
 
-static bfd_boolean
+static bool
 _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
                       struct bfd_link_info *link_info,
                       Elf_Internal_Rela *rel,
                       bfd_vma symval,
                       bfd_vma max_alignment,
                       bfd_vma reserve_size ATTRIBUTE_UNUSED,
-                      bfd_boolean *again,
-                      riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED,
-                      bfd_boolean undefined_weak ATTRIBUTE_UNUSED)
+                      bool *again,
+                      riscv_pcgp_relocs *pcgp_relocs,
+                      bool undefined_weak ATTRIBUTE_UNUSED)
 {
   bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
   bfd_vma foff = symval - (sec_addr (sec) + rel->r_offset);
-  bfd_boolean near_zero = (symval + RISCV_IMM_REACH / 2) < RISCV_IMM_REACH;
+  bool near_zero = (symval + RISCV_IMM_REACH / 2) < RISCV_IMM_REACH;
   bfd_vma auipc, jalr;
   int rd, r_type, len = 4, rvc = elf_elfheader (abfd)->e_flags & EF_RISCV_RVC;
 
@@ -4149,7 +4212,7 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
 
   /* See if this function call can be shortened.  */
   if (!VALID_JTYPE_IMM (foff) && !(!bfd_link_pic (link_info) && near_zero))
-    return TRUE;
+    return true;
 
   /* Shorten the function call.  */
   BFD_ASSERT (rel->r_offset + 8 <= sec->size);
@@ -4188,9 +4251,9 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
   riscv_put_insn (8 * len, auipc, contents + rel->r_offset);
 
   /* Delete unnecessary JALR.  */
-  *again = TRUE;
+  *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.  */
@@ -4212,7 +4275,7 @@ _bfd_riscv_get_max_alignment (asection *sec)
 
 /* Relax non-PIC global variable references to GP-relative references.  */
 
-static bfd_boolean
+static bool
 _bfd_riscv_relax_lui (bfd *abfd,
                      asection *sec,
                      asection *sym_sec,
@@ -4221,9 +4284,9 @@ _bfd_riscv_relax_lui (bfd *abfd,
                      bfd_vma symval,
                      bfd_vma max_alignment,
                      bfd_vma reserve_size,
-                     bfd_boolean *again,
-                     riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED,
-                     bfd_boolean undefined_weak)
+                     bool *again,
+                     riscv_pcgp_relocs *pcgp_relocs,
+                     bool undefined_weak)
 {
   bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
   bfd_vma gp = riscv_global_pointer_value (link_info);
@@ -4236,8 +4299,8 @@ _bfd_riscv_relax_lui (bfd *abfd,
       /* If gp and the symbol are in the same output section, which is not the
         abs section, then consider only that output section's alignment.  */
       struct bfd_link_hash_entry *h =
-       bfd_link_hash_lookup (link_info->hash, RISCV_GP_SYMBOL, FALSE, FALSE,
-                             TRUE);
+       bfd_link_hash_lookup (link_info->hash, RISCV_GP_SYMBOL, false, false,
+                             true);
       if (h->u.def.section->output_section == sym_sec->output_section
          && sym_sec->output_section != bfd_abs_section_ptr)
        max_alignment = (bfd_vma) 1 << sym_sec->output_section->alignment_power;
@@ -4265,7 +4328,7 @@ _bfd_riscv_relax_lui (bfd *abfd,
            }
          else
            rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_I);
-         return TRUE;
+         return true;
 
        case R_RISCV_LO12_S:
          if (undefined_weak)
@@ -4277,14 +4340,14 @@ _bfd_riscv_relax_lui (bfd *abfd,
            }
          else
            rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_S);
-         return TRUE;
+         return true;
 
        case R_RISCV_HI20:
          /* We can delete the unnecessary LUI and reloc.  */
          rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
-         *again = TRUE;
+         *again = true;
          return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4,
-                                          link_info);
+                                          link_info, pcgp_relocs);
 
        default:
          abort ();
@@ -4307,7 +4370,7 @@ _bfd_riscv_relax_lui (bfd *abfd,
       bfd_vma lui = bfd_getl32 (contents + rel->r_offset);
       unsigned rd = ((unsigned)lui >> OP_SH_RD) & OP_MASK_RD;
       if (rd == 0 || rd == X_SP)
-       return TRUE;
+       return true;
 
       lui = (lui & (OP_MASK_RD << OP_SH_RD)) | MATCH_C_LUI;
       bfd_putl32 (lui, contents + rel->r_offset);
@@ -4315,17 +4378,17 @@ _bfd_riscv_relax_lui (bfd *abfd,
       /* Replace the R_RISCV_HI20 reloc.  */
       rel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel->r_info), R_RISCV_RVC_LUI);
 
-      *again = TRUE;
+      *again = true;
       return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + 2, 2,
-                                      link_info);
+                                      link_info, pcgp_relocs);
     }
 
-  return TRUE;
+  return true;
 }
 
 /* Relax non-PIC TLS references to TP-relative references.  */
 
-static bfd_boolean
+static bool
 _bfd_riscv_relax_tls_le (bfd *abfd,
                         asection *sec,
                         asection *sym_sec ATTRIBUTE_UNUSED,
@@ -4334,40 +4397,42 @@ _bfd_riscv_relax_tls_le (bfd *abfd,
                         bfd_vma symval,
                         bfd_vma max_alignment ATTRIBUTE_UNUSED,
                         bfd_vma reserve_size ATTRIBUTE_UNUSED,
-                        bfd_boolean *again,
-                        riscv_pcgp_relocs *prcel_relocs ATTRIBUTE_UNUSED,
-                        bfd_boolean undefined_weak ATTRIBUTE_UNUSED)
+                        bool *again,
+                        riscv_pcgp_relocs *pcgp_relocs,
+                        bool undefined_weak ATTRIBUTE_UNUSED)
 {
   /* See if this symbol is in range of tp.  */
   if (RISCV_CONST_HIGH_PART (tpoff (link_info, symval)) != 0)
-    return TRUE;
+    return true;
 
   BFD_ASSERT (rel->r_offset + 4 <= sec->size);
   switch (ELFNN_R_TYPE (rel->r_info))
     {
     case R_RISCV_TPREL_LO12_I:
       rel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel->r_info), R_RISCV_TPREL_I);
-      return TRUE;
+      return true;
 
     case R_RISCV_TPREL_LO12_S:
       rel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel->r_info), R_RISCV_TPREL_S);
-      return TRUE;
+      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);
-      *again = TRUE;
-      return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4, link_info);
+      *again = true;
+      return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4, link_info,
+                                      pcgp_relocs);
 
     default:
       abort ();
     }
 }
 
-/* Implement R_RISCV_ALIGN by deleting excess alignment NOPs.  */
+/* Implement R_RISCV_ALIGN by deleting excess alignment NOPs.
+   Once we've handled an R_RISCV_ALIGN, we can't relax anything else.  */
 
-static bfd_boolean
+static bool
 _bfd_riscv_relax_align (bfd *abfd, asection *sec,
                        asection *sym_sec,
                        struct bfd_link_info *link_info,
@@ -4375,9 +4440,9 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec,
                        bfd_vma symval,
                        bfd_vma max_alignment ATTRIBUTE_UNUSED,
                        bfd_vma reserve_size ATTRIBUTE_UNUSED,
-                       bfd_boolean *again ATTRIBUTE_UNUSED,
-                       riscv_pcgp_relocs *pcrel_relocs ATTRIBUTE_UNUSED,
-                       bfd_boolean undefined_weak ATTRIBUTE_UNUSED)
+                       bool *again 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 alignment = 1, pos;
@@ -4389,7 +4454,7 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec,
   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;
+  sec->sec_flg0 = true;
 
   /* Make sure there are enough NOPs to actually achieve the alignment.  */
   if (rel->r_addend < nop_bytes)
@@ -4400,7 +4465,7 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec,
         abfd, sym_sec, (uint64_t) rel->r_offset,
         (int64_t) nop_bytes, (int64_t) alignment, (int64_t) rel->r_addend);
       bfd_set_error (bfd_error_bad_value);
-      return FALSE;
+      return false;
     }
 
   /* Delete the reloc.  */
@@ -4408,7 +4473,7 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec,
 
   /* If the number of NOPs is already correct, there's nothing to do.  */
   if (nop_bytes == rel->r_addend)
-    return TRUE;
+    return true;
 
   /* Write as many RISC-V NOPs as we need.  */
   for (pos = 0; pos < (nop_bytes & -4); pos += 4)
@@ -4420,12 +4485,13 @@ _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.  */
 
-static bfd_boolean
+static bool
 _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
                     asection *sec,
                     asection *sym_sec,
@@ -4434,9 +4500,9 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
                     bfd_vma symval,
                     bfd_vma max_alignment,
                     bfd_vma reserve_size,
-                    bfd_boolean *again ATTRIBUTE_UNUSED,
+                    bool *again ATTRIBUTE_UNUSED,
                     riscv_pcgp_relocs *pcgp_relocs,
-                    bfd_boolean undefined_weak)
+                    bool undefined_weak)
 {
   bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
   bfd_vma gp = riscv_global_pointer_value (link_info);
@@ -4462,7 +4528,7 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
        if (hi == NULL)
          {
            riscv_record_pcgp_lo_reloc (pcgp_relocs, hi_sec_off);
-           return TRUE;
+           return true;
          }
 
        hi_reloc = *hi;
@@ -4481,12 +4547,12 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
       /* Mergeable symbols and code might later move out of range.  */
       if (! undefined_weak
          && sym_sec->flags & (SEC_MERGE | SEC_CODE))
-       return TRUE;
+       return true;
 
       /* If the cooresponding lo relocation has already been seen then it's not
          safe to relax this relocation.  */
       if (riscv_find_pcgp_lo_reloc (pcgp_relocs, rel->r_offset))
-       return TRUE;
+       return true;
 
       break;
 
@@ -4499,8 +4565,8 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
       /* If gp and the symbol are in the same output section, which is not the
         abs section, then consider only that output section's alignment.  */
       struct bfd_link_hash_entry *h =
-       bfd_link_hash_lookup (link_info->hash, RISCV_GP_SYMBOL, FALSE, FALSE,
-                             TRUE);
+       bfd_link_hash_lookup (link_info->hash, RISCV_GP_SYMBOL, false, false,
+                             true);
       if (h->u.def.section->output_section == sym_sec->output_section
          && sym_sec->output_section != bfd_abs_section_ptr)
        max_alignment = (bfd_vma) 1 << sym_sec->output_section->alignment_power;
@@ -4534,7 +4600,7 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
              rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_I);
              rel->r_addend += hi_reloc.hi_addend;
            }
-         return TRUE;
+         return true;
 
        case R_RISCV_PCREL_LO12_S:
          if (undefined_weak)
@@ -4552,7 +4618,7 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
              rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_S);
              rel->r_addend += hi_reloc.hi_addend;
            }
-         return TRUE;
+         return true;
 
        case R_RISCV_PCREL_HI20:
          riscv_record_pcgp_hi_reloc (pcgp_relocs,
@@ -4565,19 +4631,19 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
          /* We can delete the unnecessary AUIPC and reloc.  */
          rel->r_info = ELFNN_R_INFO (0, R_RISCV_DELETE);
          rel->r_addend = 4;
-         return TRUE;
+         return true;
 
        default:
          abort ();
        }
     }
 
-  return TRUE;
+  return true;
 }
 
 /* Delete the bytes for R_RISCV_DELETE.  */
 
-static bfd_boolean
+static bool
 _bfd_riscv_relax_delete (bfd *abfd,
                         asection *sec,
                         asection *sym_sec ATTRIBUTE_UNUSED,
@@ -4586,47 +4652,60 @@ _bfd_riscv_relax_delete (bfd *abfd,
                         bfd_vma symval ATTRIBUTE_UNUSED,
                         bfd_vma max_alignment ATTRIBUTE_UNUSED,
                         bfd_vma reserve_size ATTRIBUTE_UNUSED,
-                        bfd_boolean *again ATTRIBUTE_UNUSED,
+                        bool *again ATTRIBUTE_UNUSED,
                         riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED,
-                        bfd_boolean undefined_weak ATTRIBUTE_UNUSED)
+                        bool undefined_weak ATTRIBUTE_UNUSED)
 {
   if (!riscv_relax_delete_bytes (abfd, sec, rel->r_offset, rel->r_addend,
-                                link_info))
-    return FALSE;
+                                link_info, NULL))
+    return false;
   rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
-  return TRUE;
+  return true;
+}
+
+/* Called by after_allocation to set the information of data segment
+   before relaxing.  */
+
+void
+bfd_elfNN_riscv_set_data_segment_info (struct bfd_link_info *info,
+                                       int *data_segment_phase)
+{
+  struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
+  htab->data_segment_phase = data_segment_phase;
 }
 
 /* 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 obselete.
-   Pass 3: Which cannot be disabled, handles code alignment directives.  */
+   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 bfd_boolean
+static bool
 _bfd_riscv_relax_section (bfd *abfd, asection *sec,
                          struct bfd_link_info *info,
-                         bfd_boolean *again)
+                         bool *again)
 {
   Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (abfd);
   struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
   struct bfd_elf_section_data *data = elf_section_data (sec);
   Elf_Internal_Rela *relocs;
-  bfd_boolean ret = FALSE;
+  bool ret = false;
   unsigned int i;
   bfd_vma max_alignment, reserve_size = 0;
   riscv_pcgp_relocs pcgp_relocs;
 
-  *again = FALSE;
+  *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))
-    return TRUE;
+         && info->relax_pass == 0)
+      /* The exp_seg_relro_adjust is enum phase_enum (0x4),
+        and defined in ld/ldexp.h.  */
+      || *(htab->data_segment_phase) == 4)
+    return true;
 
   riscv_init_pcgp_relocs (&pcgp_relocs);
 
@@ -4658,7 +4737,7 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
       int type = ELFNN_R_TYPE (rel->r_info);
       bfd_vma symval;
       char symtype;
-      bfd_boolean undefined_weak = FALSE;
+      bool undefined_weak = false;
 
       relax_func = NULL;
       if (info->relax_pass == 0)
@@ -4675,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
@@ -4702,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;
 
@@ -4785,7 +4860,7 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
                 libraries can not happen currently.  Once we support the
                 auipc relaxations when creating shared libraries, then we will
                 need the more rigorous checking for this optimization.  */
-             undefined_weak = TRUE;
+             undefined_weak = true;
            }
 
          /* This line has to match the check in riscv_elf_relocate_section
@@ -4857,7 +4932,7 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
        goto fail;
     }
 
-  ret = TRUE;
+  ret = true;
 
  fail:
   if (relocs != data->relocs)
@@ -4877,6 +4952,8 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
 # define PRPSINFO_OFFSET_PR_PID                16
 # define PRPSINFO_OFFSET_PR_FNAME      32
 # define PRPSINFO_OFFSET_PR_PSARGS     48
+# define PRPSINFO_PR_FNAME_LENGTH      16
+# define PRPSINFO_PR_PSARGS_LENGTH     80
 #else
 # define PRSTATUS_SIZE                 376
 # define PRSTATUS_OFFSET_PR_CURSIG     12
@@ -4887,17 +4964,92 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
 # define PRPSINFO_OFFSET_PR_PID                24
 # define PRPSINFO_OFFSET_PR_FNAME      40
 # define PRPSINFO_OFFSET_PR_PSARGS     56
+# define PRPSINFO_PR_FNAME_LENGTH      16
+# define PRPSINFO_PR_PSARGS_LENGTH     80
 #endif
 
+/* Write PRSTATUS and PRPSINFO note into core file.  This will be called
+   before the generic code in elf.c.  By checking the compiler defines we
+   only perform any action here if the generic code would otherwise not be
+   able to help us.  The intention is that bare metal core dumps (where the
+   prstatus_t and/or prpsinfo_t might not be available) will use this code,
+   while non bare metal tools will use the generic elf code.  */
+
+static char *
+riscv_write_core_note (bfd *abfd ATTRIBUTE_UNUSED,
+                       char *buf ATTRIBUTE_UNUSED,
+                       int *bufsiz ATTRIBUTE_UNUSED,
+                       int note_type ATTRIBUTE_UNUSED, ...)
+{
+  switch (note_type)
+    {
+    default:
+      return NULL;
+
+#if !defined (HAVE_PRPSINFO_T)
+    case NT_PRPSINFO:
+      {
+       char data[PRPSINFO_SIZE] ATTRIBUTE_NONSTRING;
+       va_list ap;
+
+       va_start (ap, note_type);
+       memset (data, 0, sizeof (data));
+       strncpy (data + PRPSINFO_OFFSET_PR_FNAME, va_arg (ap, const char *),
+                 PRPSINFO_PR_FNAME_LENGTH);
+#if GCC_VERSION == 8000 || GCC_VERSION == 8001
+       DIAGNOSTIC_PUSH;
+       /* GCC 8.0 and 8.1 warn about 80 equals destination size with
+          -Wstringop-truncation:
+          https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85643
+        */
+       DIAGNOSTIC_IGNORE_STRINGOP_TRUNCATION;
+#endif
+       strncpy (data + PRPSINFO_OFFSET_PR_PSARGS, va_arg (ap, const char *),
+                 PRPSINFO_PR_PSARGS_LENGTH);
+#if GCC_VERSION == 8000 || GCC_VERSION == 8001
+       DIAGNOSTIC_POP;
+#endif
+       va_end (ap);
+       return elfcore_write_note (abfd, buf, bufsiz,
+                                  "CORE", note_type, data, sizeof (data));
+      }
+#endif /* !HAVE_PRPSINFO_T */
+
+#if !defined (HAVE_PRSTATUS_T)
+    case NT_PRSTATUS:
+      {
+        char data[PRSTATUS_SIZE];
+        va_list ap;
+        long pid;
+        int cursig;
+        const void *greg;
+
+        va_start (ap, note_type);
+        memset (data, 0, sizeof(data));
+        pid = va_arg (ap, long);
+        bfd_put_32 (abfd, pid, data + PRSTATUS_OFFSET_PR_PID);
+        cursig = va_arg (ap, int);
+        bfd_put_16 (abfd, cursig, data + PRSTATUS_OFFSET_PR_CURSIG);
+        greg = va_arg (ap, const void *);
+        memcpy (data + PRSTATUS_OFFSET_PR_REG, greg,
+                PRSTATUS_SIZE - PRSTATUS_OFFSET_PR_REG - ARCH_SIZE / 8);
+        va_end (ap);
+        return elfcore_write_note (abfd, buf, bufsiz,
+                                   "CORE", note_type, data, sizeof (data));
+      }
+#endif /* !HAVE_PRSTATUS_T */
+    }
+}
+
 /* Support for core dump NOTE sections.  */
 
-static bfd_boolean
+static bool
 riscv_elf_grok_prstatus (bfd *abfd, Elf_Internal_Note *note)
 {
   switch (note->descsz)
     {
       default:
-       return FALSE;
+       return false;
 
       case PRSTATUS_SIZE: /* sizeof(struct elf_prstatus) on Linux/RISC-V.  */
        /* pr_cursig */
@@ -4915,13 +5067,13 @@ riscv_elf_grok_prstatus (bfd *abfd, Elf_Internal_Note *note)
                                          note->descpos + PRSTATUS_OFFSET_PR_REG);
 }
 
-static bfd_boolean
+static bool
 riscv_elf_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
 {
   switch (note->descsz)
     {
       default:
-       return FALSE;
+       return false;
 
       case PRPSINFO_SIZE: /* sizeof(struct elf_prpsinfo) on Linux/RISC-V.  */
        /* pr_pid */
@@ -4930,11 +5082,13 @@ riscv_elf_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
 
        /* pr_fname */
        elf_tdata (abfd)->core->program = _bfd_elfcore_strndup
-         (abfd, note->descdata + PRPSINFO_OFFSET_PR_FNAME, 16);
+         (abfd, note->descdata + PRPSINFO_OFFSET_PR_FNAME,
+           PRPSINFO_PR_FNAME_LENGTH);
 
        /* pr_psargs */
        elf_tdata (abfd)->core->command = _bfd_elfcore_strndup
-         (abfd, note->descdata + PRPSINFO_OFFSET_PR_PSARGS, 80);
+         (abfd, note->descdata + PRPSINFO_OFFSET_PR_PSARGS,
+           PRPSINFO_PR_PSARGS_LENGTH);
        break;
     }
 
@@ -4950,12 +5104,12 @@ riscv_elf_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
       command[n - 1] = '\0';
   }
 
-  return TRUE;
+  return true;
 }
 
 /* Set the right mach type.  */
 
-static bfd_boolean
+static bool
 riscv_elf_object_p (bfd *abfd)
 {
   /* There are only two mach types in RISCV currently.  */
@@ -4965,7 +5119,7 @@ riscv_elf_object_p (bfd *abfd)
   else
     bfd_default_set_arch_mach (abfd, bfd_arch_riscv, bfd_mach_riscv64);
 
-  return TRUE;
+  return true;
 }
 
 /* Determine whether an object attribute tag takes an integer, a
@@ -4977,6 +5131,113 @@ riscv_elf_obj_attrs_arg_type (int tag)
   return (tag & 1) != 0 ? ATTR_TYPE_FLAG_STR_VAL : ATTR_TYPE_FLAG_INT_VAL;
 }
 
+/* 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)
+         /* 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 TARGET_LITTLE_NAME                     "elfNN-littleriscv"
 #define TARGET_BIG_SYM                         riscv_elfNN_be_vec
@@ -4989,6 +5250,7 @@ riscv_elf_obj_attrs_arg_type (int tag)
 #define bfd_elfNN_bfd_reloc_type_lookup                riscv_reloc_type_lookup
 #define bfd_elfNN_bfd_merge_private_bfd_data \
   _bfd_riscv_elf_merge_private_bfd_data
+#define bfd_elfNN_bfd_is_target_special_symbol riscv_elf_is_target_special_symbol
 
 #define elf_backend_copy_indirect_symbol       riscv_elf_copy_indirect_symbol
 #define elf_backend_create_dynamic_sections    riscv_elf_create_dynamic_sections
@@ -5003,10 +5265,16 @@ riscv_elf_obj_attrs_arg_type (int tag)
 #define elf_backend_grok_prstatus              riscv_elf_grok_prstatus
 #define elf_backend_grok_psinfo                        riscv_elf_grok_psinfo
 #define elf_backend_object_p                   riscv_elf_object_p
+#define elf_backend_write_core_note            riscv_write_core_note
+#define elf_backend_maybe_function_sym         riscv_maybe_function_sym
 #define elf_info_to_howto_rel                  NULL
 #define elf_info_to_howto                      riscv_info_to_howto_rela
 #define bfd_elfNN_bfd_relax_section            _bfd_riscv_relax_section
 #define bfd_elfNN_mkobject                     elfNN_riscv_mkobject
+#define elf_backend_additional_program_headers \
+  riscv_elf_additional_program_headers
+#define elf_backend_modify_segment_map         riscv_elf_modify_segment_map
+#define elf_backend_merge_symbol_attribute     riscv_elf_merge_symbol_attribute
 
 #define elf_backend_init_index_section         _bfd_elf_init_1_index_section
 
@@ -5028,6 +5296,6 @@ riscv_elf_obj_attrs_arg_type (int tag)
 #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"