Make bfd_byte an int8_t, flagword a uint32_t
[binutils-gdb.git] / bfd / elfnn-riscv.c
index a944b3302274700791a303187ab8083745741a3f..e90e36b58bbbd2da875a37678d609f5fa9d4d189 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-2023 Free Software Foundation, Inc.
 
    Contributed by Andrew Waterman (andrew@sifive.com).
    Based on TILE-Gx and MIPS targets.
 #include "elf/riscv.h"
 #include "opcode/riscv.h"
 #include "objalloc.h"
-#include "cpu-riscv.h"
 
 #include <limits.h>
 #ifndef CHAR_BIT
 #define CHAR_BIT 8
 #endif
 
+/* True if dynamic relocation is needed.  If we are creating a shared library,
+   and this is a reloc against a global symbol, or a non PC relative reloc
+   against a local symbol, then we need to copy the reloc into the shared
+   library.  However, if we are linking with -Bsymbolic, we do not need to
+   copy a reloc against a global symbol which is defined in an object we are
+   including in the link (i.e., DEF_REGULAR is set).
+
+   At this point we have not seen all the input files, so it is possible that
+   DEF_REGULAR is not set now but will be set later (it is never cleared).
+   In case of a weak definition, DEF_REGULAR may be cleared later by a strong
+   definition in a shared library.  We account for that possibility below by
+   storing information in the relocs_copied field of the hash table entry.
+   A similar situation occurs when creating shared libraries and symbol
+   visibility changes render the symbol local.
+
+   If on the other hand, we are creating an executable, we may need to keep
+   relocations for symbols satisfied by a dynamic library if we manage to
+   avoid copy relocs for the symbol.
+
+   Generate dynamic pointer relocation against STT_GNU_IFUNC symbol in the
+   non-code section (R_RISCV_32/R_RISCV_64).  */
+#define RISCV_NEED_DYNAMIC_RELOC(PCREL, INFO, H, SEC) \
+  ((bfd_link_pic (INFO) \
+    && ((SEC)->flags & SEC_ALLOC) != 0 \
+    && (!(PCREL) \
+       || ((H) != NULL \
+           && (!(INFO)->symbolic \
+               || (H)->root.type == bfd_link_hash_defweak \
+               || !(H)->def_regular)))) \
+   || (!bfd_link_pic (INFO) \
+       && ((SEC)->flags & SEC_ALLOC) != 0 \
+       && (H) != NULL \
+       && ((H)->root.type == bfd_link_hash_defweak \
+          || !(H)->def_regular)) \
+   || (!bfd_link_pic (INFO) \
+       && (H) != NULL \
+       && (H)->type == STT_GNU_IFUNC \
+       && ((SEC)->flags & SEC_CODE) == 0))
+
+/* True if dynamic relocation should be generated.  */
+#define RISCV_GENERATE_DYNAMIC_RELOC(PCREL, INFO, H, RESOLVED_TO_ZERO) \
+  ((bfd_link_pic (INFO) \
+    && ((H) == NULL \
+       || (ELF_ST_VISIBILITY ((H)->other) == STV_DEFAULT && !(RESOLVED_TO_ZERO)) \
+       || (H)->root.type != bfd_link_hash_undefweak) \
+    && (!(PCREL) \
+       || !SYMBOL_CALLS_LOCAL ((INFO), (H)))) \
+   || (!bfd_link_pic (INFO) \
+       && (H) != NULL \
+       && (H)->dynindx != -1 \
+       && !(H)->non_got_ref \
+       && (((H)->def_dynamic && !(H)->def_regular) \
+          || (H)->root.type == bfd_link_hash_undefweak \
+          || (H)->root.type == bfd_link_hash_undefined)))
+
+/* True if this input relocation should be copied to output.  H->dynindx
+   may be -1 if this symbol was marked to become local.  */
+#define RISCV_COPY_INPUT_RELOC(INFO, H) \
+  ((H) != NULL \
+   && (H)->dynindx != -1 \
+   && (!bfd_link_pic (INFO) \
+       || !SYMBOLIC_BIND ((INFO), (H)) \
+       || !(H)->def_regular))
+
+/* True if this is actually a static link, or it is a -Bsymbolic link
+   and the symbol is defined locally, or the symbol was forced to be
+   local because of a version file.  */
+#define RISCV_RESOLVED_LOCALLY(INFO, H) \
+  (!WILL_CALL_FINISH_DYNAMIC_SYMBOL (elf_hash_table (INFO)->dynamic_sections_created, \
+                                    bfd_link_pic (INFO), (H)) \
+   || (bfd_link_pic (INFO) \
+       && SYMBOL_REFERENCES_LOCAL ((INFO), (H))))
+
 /* Internal relocations used exclusively by the relaxation pass.  */
 #define R_RISCV_DELETE (R_RISCV_max + 1)
 
 #define ELF_MAXPAGESIZE                        0x1000
 #define ELF_COMMONPAGESIZE             0x1000
 
+#define RISCV_ATTRIBUTES_SECTION_NAME ".riscv.attributes"
+
 /* RISC-V ELF linker hash entry.  */
 
 struct riscv_elf_link_hash_entry
@@ -117,12 +191,18 @@ struct riscv_elf_link_hash_table
 {
   struct elf_link_hash_table elf;
 
+  /* Various options and other info passed from the linker.  */
+  struct riscv_elf_params *params;
+
   /* Short-cuts to get to dynamic linker sections.  */
   asection *sdyntdata;
 
   /* The max alignment of output sections.  */
   bfd_vma max_alignment;
 
+  /* The max alignment of output sections in [gp-2K, gp+2K) range.  */
+  bfd_vma max_alignment_for_gp;
+
   /* Used by local STT_GNU_IFUNC symbols.  */
   htab_t loc_hash_table;
   void * loc_hash_memory;
@@ -130,8 +210,12 @@ struct riscv_elf_link_hash_table
   /* The index of the last unused .rel.iplt slot.  */
   bfd_vma last_iplt_index;
 
-  /* Re-run the relaxations from relax pass 0 if TRUE.  */
-  bool restart_relax;
+  /* The data segment phase, don't relax the section
+     when it is exp_seg_relro_adjust.  */
+  int *data_segment_phase;
+
+  /* Relocations for variant CC symbols may be present.  */
+  int variant_cc;
 };
 
 /* Instruction access functions. */
@@ -152,6 +236,13 @@ 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)
 
+void
+riscv_elfNN_set_options (struct bfd_link_info *link_info,
+                        struct riscv_elf_params *params)
+{
+  riscv_elf_hash_table (link_info)->params = params;
+}
+
 static bool
 riscv_info_to_howto_rela (bfd *abfd,
                          arelent *cache_ptr,
@@ -400,7 +491,7 @@ riscv_elf_link_hash_table_create (bfd *abfd)
     }
 
   ret->max_alignment = (bfd_vma) -1;
-  ret->restart_relax = false;
+  ret->max_alignment_for_gp = (bfd_vma) -1;
 
   /* Create hash table for local ifunc.  */
   ret->loc_hash_table = htab_try_create (1024,
@@ -647,6 +738,7 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
       unsigned int r_type;
       unsigned int r_symndx;
       struct elf_link_hash_entry *h;
+      bool is_abs_symbol = false;
 
       r_symndx = ELFNN_R_SYM (rel->r_info);
       r_type = ELFNN_R_TYPE (rel->r_info);
@@ -666,6 +758,8 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
          if (isym == NULL)
            return false;
 
+         is_abs_symbol = isym->st_shndx == SHN_ABS ? true : false;
+
          /* Check relocation against local STT_GNU_IFUNC symbol.  */
          if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
            {
@@ -691,6 +785,8 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
          while (h->root.type == bfd_link_hash_indirect
                 || h->root.type == bfd_link_hash_warning)
            h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+         is_abs_symbol = bfd_is_abs_symbol (&h->root) ? true : false;
        }
 
       if (h != NULL)
@@ -770,6 +866,47 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                 ifunc symbol.  */
              h->plt.refcount += 1;
            }
+
+         /* The non-preemptible absolute symbol shouldn't be referneced with
+            pc-relative relocation when generating shared object.  However,
+            PCREL_HI20/LO12 relocs are always bind locally when generating
+            shared object, so all absolute symbol referenced need to be
+            disallowed, except they are defined in linker script.
+
+            Maybe we should add this check for all pc-relative relocations,
+            please see pr28789 and pr25749 for details.  */
+         if (bfd_link_pic (info)
+             /* (h == NULL || SYMBOL_REFERENCES_LOCAL (info, h))  */
+             && is_abs_symbol)
+           {
+             if (h != NULL && (h)->root.ldscript_def)
+               /* Disallow the absolute symbol defined in linker script here
+                  will cause the glibc-linux toolchain build failed, so regard
+                  them as pc-relative symbols, just like what x86 did.  */
+               ;
+             else
+               {
+                 const char *name;
+                 if (h->root.root.string)
+                   name = h->root.root.string;
+                 else
+                   {
+                     Elf_Internal_Sym *sym;
+                     sym = bfd_sym_from_r_symndx (&htab->elf.sym_cache, abfd,
+                                                  r_symndx);
+                     name = bfd_elf_sym_name (abfd, symtab_hdr, sym, NULL);
+                   }
+
+                 reloc_howto_type *r_t =
+                       riscv_elf_rtype_to_howto (abfd, r_type);
+                 _bfd_error_handler
+                   (_("%pB: relocation %s against absolute symbol `%s' can "
+                      "not be used when making a shared object"),
+                    abfd, r_t ? r_t->name : _("<unknown>"), name);
+                 bfd_set_error (bfd_error_bad_value);
+                 return false;
+               }
+           }
          /* Fall through.  */
 
        case R_RISCV_JAL:
@@ -792,13 +929,31 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_RISCV_HI20:
          if (bfd_link_pic (info))
            return bad_static_reloc (abfd, r_type, h);
-         /* Fall through.  */
+         goto static_reloc;
+
+       case R_RISCV_32:
+         if (ARCH_SIZE > 32
+             && bfd_link_pic (info)
+             && (sec->flags & SEC_ALLOC) != 0)
+           {
+             if (is_abs_symbol)
+               break;
+
+             reloc_howto_type *r_t = riscv_elf_rtype_to_howto (abfd, r_type);
+             _bfd_error_handler
+               (_("%pB: relocation %s against non-absolute symbol `%s' can "
+                  "not be used in RVNN when making a shared object"),
+                abfd, r_t ? r_t->name : _("<unknown>"),
+                h != NULL ? h->root.root.string : "a local symbol");
+             bfd_set_error (bfd_error_bad_value);
+             return false;
+           }
+         goto static_reloc;
 
        case R_RISCV_COPY:
        case R_RISCV_JUMP_SLOT:
        case R_RISCV_RELATIVE:
        case R_RISCV_64:
-       case R_RISCV_32:
          /* Fall through.  */
 
        static_reloc:
@@ -821,48 +976,8 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                }
            }
 
-         /* If we are creating a shared library, and this is a reloc
-            against a global symbol, or a non PC relative reloc
-            against a local symbol, then we need to copy the reloc
-            into the shared library.  However, if we are linking with
-            -Bsymbolic, we do not need to copy a reloc against a
-            global symbol which is defined in an object we are
-            including in the link (i.e., DEF_REGULAR is set).  At
-            this point we have not seen all the input files, so it is
-            possible that DEF_REGULAR is not set now but will be set
-            later (it is never cleared).  In case of a weak definition,
-            DEF_REGULAR may be cleared later by a strong definition in
-            a shared library.  We account for that possibility below by
-            storing information in the relocs_copied field of the hash
-            table entry.  A similar situation occurs when creating
-            shared libraries and symbol visibility changes render the
-            symbol local.
-
-            If on the other hand, we are creating an executable, we
-            may need to keep relocations for symbols satisfied by a
-            dynamic library if we manage to avoid copy relocs for the
-            symbol.
-
-            Generate dynamic pointer relocation against STT_GNU_IFUNC
-            symbol in the non-code section (R_RISCV_32/R_RISCV_64).  */
-         reloc_howto_type * r = riscv_elf_rtype_to_howto (abfd, r_type);
-
-         if ((bfd_link_pic (info)
-              && (sec->flags & SEC_ALLOC) != 0
-              && ((r != NULL && !r->pc_relative)
-                  || (h != NULL
-                      && (!info->symbolic
-                          || h->root.type == bfd_link_hash_defweak
-                          || !h->def_regular))))
-             || (!bfd_link_pic (info)
-                 && (sec->flags & SEC_ALLOC) != 0
-                 && h != NULL
-                 && (h->root.type == bfd_link_hash_defweak
-                     || !h->def_regular))
-             || (!bfd_link_pic (info)
-                 && h != NULL
-                 && h->type == STT_GNU_IFUNC
-                 && (sec->flags & SEC_CODE) == 0))
+         reloc_howto_type *r = riscv_elf_rtype_to_howto (abfd, r_type);
+         if (RISCV_NEED_DYNAMIC_RELOC (r->pc_relative, info, h, sec))
            {
              struct elf_dyn_relocs *p;
              struct elf_dyn_relocs **head;
@@ -928,16 +1043,6 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
          break;
 
-       case R_RISCV_GNU_VTINHERIT:
-         if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
-           return false;
-         break;
-
-       case R_RISCV_GNU_VTENTRY:
-         if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
-           return false;
-         break;
-
        default:
          break;
        }
@@ -946,24 +1051,6 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
   return true;
 }
 
-static asection *
-riscv_elf_gc_mark_hook (asection *sec,
-                       struct bfd_link_info *info,
-                       Elf_Internal_Rela *rel,
-                       struct elf_link_hash_entry *h,
-                       Elf_Internal_Sym *sym)
-{
-  if (h != NULL)
-    switch (ELFNN_R_TYPE (rel->r_info))
-      {
-      case R_RISCV_GNU_VTINHERIT:
-      case R_RISCV_GNU_VTENTRY:
-       return NULL;
-      }
-
-  return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym);
-}
-
 /* Adjust a symbol defined by a dynamic object and referenced by a
    regular object.  The current definition is in some section of the
    dynamic object, but we're not including those sections.  We have to
@@ -1171,6 +1258,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
        {
@@ -1554,7 +1646,18 @@ riscv_elf_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
        return false;
     }
 
-  return _bfd_elf_add_dynamic_tags (output_bfd, info, true);
+  /* Add dynamic entries.  */
+  if (elf_hash_table (info)->dynamic_sections_created)
+    {
+      if (!_bfd_elf_add_dynamic_tags (output_bfd, info, true))
+       return false;
+
+      if (htab->variant_cc
+         && !_bfd_elf_add_dynamic_entry (info, DT_RISCV_VARIANT_CC, 0))
+       return false;
+    }
+
+  return true;
 }
 
 #define TP_OFFSET 0
@@ -1735,25 +1838,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;
 
@@ -1801,8 +1920,7 @@ riscv_zero_pcrel_hi_reloc (Elf_Internal_Rela *rel,
                           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
@@ -1835,11 +1953,14 @@ riscv_zero_pcrel_hi_reloc (Elf_Internal_Rela *rel,
 }
 
 static bool
-riscv_record_pcrel_hi_reloc (riscv_pcrel_relocs *p, bfd_vma addr,
-                            bfd_vma value, bool absolute)
+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);
 
@@ -1853,20 +1974,19 @@ riscv_record_pcrel_hi_reloc (riscv_pcrel_relocs *p, bfd_vma addr,
 
 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};
+  *entry = (riscv_pcrel_lo_reloc) {addr, reloc, input_section, info,
+                                  howto, contents, p->lo_relocs};
   p->lo_relocs = entry;
   return true;
 }
@@ -1880,16 +2000,34 @@ 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;
@@ -1972,8 +2110,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
       char *msg_buf = NULL;
       bool resolved_to_zero;
 
-      if (howto == NULL
-         || r_type == R_RISCV_GNU_VTINHERIT || r_type == R_RISCV_GNU_VTENTRY)
+      if (howto == NULL)
        continue;
 
       /* This is a final link.  */
@@ -2211,12 +2348,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;
 
@@ -2228,12 +2362,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;
 
@@ -2299,23 +2430,14 @@ riscv_elf_relocate_section (bfd *output_bfd,
        case R_RISCV_GOT_HI20:
          if (h != NULL)
            {
-             bool dyn, pic;
-
              off = h->got.offset;
              BFD_ASSERT (off != (bfd_vma) -1);
-             dyn = elf_hash_table (info)->dynamic_sections_created;
-             pic = bfd_link_pic (info);
 
-             if (! WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, pic, h)
-                 || (pic && SYMBOL_REFERENCES_LOCAL (info, h)))
+             if (RISCV_RESOLVED_LOCALLY (info, h))
                {
-                 /* This is actually a static link, or it is a
-                    -Bsymbolic link and the symbol is defined
-                    locally, or the symbol was forced to be local
-                    because of a version file.  We must initialize
-                    this entry in the global offset table.  Since the
-                    offset must always be a multiple of the word size,
-                    we use the least significant bit to record whether
+                 /* We must initialize this entry in the global offset table.
+                    Since the offset must always be a multiple of the word
+                    size, we use the least significant bit to record whether
                     we have initialized it already.
 
                     When doing a dynamic link, we create a .rela.got
@@ -2370,21 +2492,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:
@@ -2399,6 +2529,15 @@ riscv_elf_relocate_section (bfd *output_bfd,
          break;
 
        case R_RISCV_SUB6:
+         {
+           bfd_vma old_value = bfd_get (howto->bitsize, input_bfd,
+                                        contents + rel->r_offset);
+           relocation = (old_value & ~howto->dst_mask)
+                        | (((old_value & howto->dst_mask) - relocation)
+                           & howto->dst_mask);
+         }
+         break;
+
        case R_RISCV_SUB8:
        case R_RISCV_SUB16:
        case R_RISCV_SUB32:
@@ -2429,12 +2568,44 @@ riscv_elf_relocate_section (bfd *output_bfd,
 
        case R_RISCV_JAL:
        case R_RISCV_RVC_JUMP:
-         /* This line has to match the check in _bfd_riscv_relax_section.  */
-         if (bfd_link_pic (info) && h != NULL && h->plt.offset != MINUS_ONE)
+         if (bfd_link_pic (info) && h != NULL)
            {
-             /* Refer to the PLT entry.  */
-             relocation = sec_addr (htab->elf.splt) + h->plt.offset;
-             unresolved_reloc = false;
+             if (h->plt.offset != MINUS_ONE)
+               {
+                 /* Refer to the PLT entry.  This check has to match the
+                    check in _bfd_riscv_relax_section.  */
+                 relocation = sec_addr (htab->elf.splt) + h->plt.offset;
+                 unresolved_reloc = false;
+               }
+             else if (!SYMBOL_REFERENCES_LOCAL (info, h)
+                      && (input_section->flags & SEC_ALLOC) != 0
+                      && (input_section->flags & SEC_READONLY) != 0
+                      && ELF_ST_VISIBILITY (h->other) == STV_DEFAULT)
+               {
+                 /* PR 28509, when generating the shared object, these
+                    referenced symbols may bind externally, which means
+                    they will be exported to the dynamic symbol table,
+                    and are preemptible by default.  These symbols cannot
+                    be referenced by the non-pic relocations, like
+                    R_RISCV_JAL and R_RISCV_RVC_JUMP relocations.
+
+                    However, consider that linker may relax the R_RISCV_CALL
+                    relocations to R_RISCV_JAL or R_RISCV_RVC_JUMP, if
+                    these relocations are relocated to the plt entries,
+                    then we won't report error for them.
+
+                    Perhaps we also need the similar checks for the
+                    R_RISCV_BRANCH and R_RISCV_RVC_BRANCH relocations.  */
+                 if (asprintf (&msg_buf,
+                               _("%%X%%P: relocation %s against `%s' which "
+                                 "may bind externally can not be used when "
+                                 "making a shared object; recompile "
+                                 "with -fPIC\n"),
+                               howto->name, h->root.root.string) == -1)
+                   msg_buf = NULL;
+                 msg = msg_buf;
+                 r = bfd_reloc_notsupported;
+               }
            }
          break;
 
@@ -2485,20 +2656,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;
 
@@ -2518,8 +2685,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;
@@ -2531,29 +2698,20 @@ riscv_elf_relocate_section (bfd *output_bfd,
          break;
 
        case R_RISCV_32:
+         /* Non ABS symbol should be blocked in check_relocs.  */
+         if (ARCH_SIZE > 32)
+           break;
+         /* Fall through.  */
+
        case R_RISCV_64:
          if ((input_section->flags & SEC_ALLOC) == 0)
            break;
 
-         if ((bfd_link_pic (info)
-              && (h == NULL
-                  || (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
-                      && !resolved_to_zero)
-                  || h->root.type != bfd_link_hash_undefweak)
-              && (!howto->pc_relative
-                  || !SYMBOL_CALLS_LOCAL (info, h)))
-             || (!bfd_link_pic (info)
-                 && h != NULL
-                 && h->dynindx != -1
-                 && !h->non_got_ref
-                 && ((h->def_dynamic
-                      && !h->def_regular)
-                     || h->root.type == bfd_link_hash_undefweak
-                     || h->root.type == bfd_link_hash_undefined)))
+         if (RISCV_GENERATE_DYNAMIC_RELOC (howto->pc_relative, info, h,
+                                           resolved_to_zero))
            {
              Elf_Internal_Rela outrel;
              asection *sreloc;
-             bool skip_static_relocation, skip_dynamic_relocation;
 
              /* When generating a shared object, these relocations
                 are copied into the output file to be resolved at run
@@ -2562,29 +2720,44 @@ riscv_elf_relocate_section (bfd *output_bfd,
              outrel.r_offset =
                _bfd_elf_section_offset (output_bfd, info, input_section,
                                         rel->r_offset);
-             skip_static_relocation = outrel.r_offset != (bfd_vma) -2;
-             skip_dynamic_relocation = outrel.r_offset >= (bfd_vma) -2;
+             bool skip = false;
+             bool relocate = false;
+             if (outrel.r_offset == (bfd_vma) -1)
+               skip = true;
+             else if (outrel.r_offset == (bfd_vma) -2)
+               {
+                 skip = true;
+                 relocate = true;
+               }
+             else if (h != NULL && bfd_is_abs_symbol (&h->root))
+               {
+                 /* Don't need dynamic reloc when the ABS symbol is
+                    non-dynamic or forced to local.  Maybe just use
+                    SYMBOL_REFERENCES_LOCAL to check?  */
+                 skip = (h->forced_local || (h->dynindx == -1));
+                 relocate = skip;
+               }
+
              outrel.r_offset += sec_addr (input_section);
 
-             if (skip_dynamic_relocation)
-               memset (&outrel, 0, sizeof outrel);
-             else if (h != NULL && h->dynindx != -1
-                      && !(bfd_link_pic (info)
-                           && SYMBOLIC_BIND (info, h)
-                           && h->def_regular))
+             if (skip)
+               memset (&outrel, 0, sizeof outrel);     /* R_RISCV_NONE.  */
+             else if (RISCV_COPY_INPUT_RELOC (info, h))
                {
+                 /* Maybe just use !SYMBOL_REFERENCES_LOCAL to check?  */
                  outrel.r_info = ELFNN_R_INFO (h->dynindx, r_type);
                  outrel.r_addend = rel->r_addend;
                }
              else
                {
+                 /* This symbol is local, or marked to become local.  */
                  outrel.r_info = ELFNN_R_INFO (0, R_RISCV_RELATIVE);
                  outrel.r_addend = relocation + rel->r_addend;
                }
 
              sreloc = elf_section_data (input_section)->sreloc;
              riscv_elf_append_rela (output_bfd, sreloc, &outrel);
-             if (skip_static_relocation)
+             if (!relocate)
                continue;
            }
          break;
@@ -2712,7 +2885,8 @@ 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;
          break;
@@ -2730,29 +2904,12 @@ riscv_elf_relocate_section (bfd *output_bfd,
          && _bfd_elf_section_offset (output_bfd, info, input_section,
                                      rel->r_offset) != (bfd_vma) -1)
        {
-         switch (r_type)
-           {
-           case R_RISCV_JAL:
-           case R_RISCV_RVC_JUMP:
-             if (asprintf (&msg_buf,
-                           _("%%X%%P: relocation %s against `%s' can "
-                             "not be used when making a shared object; "
-                             "recompile with -fPIC\n"),
-                           howto->name,
-                           h->root.root.string) == -1)
-               msg_buf = NULL;
-             break;
-
-           default:
-             if (asprintf (&msg_buf,
-                           _("%%X%%P: unresolvable %s relocation against "
-                             "symbol `%s'\n"),
-                           howto->name,
-                           h->root.root.string) == -1)
-               msg_buf = NULL;
-             break;
-           }
-
+         if (asprintf (&msg_buf,
+                       _("%%X%%P: unresolvable %s relocation against "
+                         "symbol `%s'\n"),
+                       howto->name,
+                       h->root.root.string) == -1)
+           msg_buf = NULL;
          msg = msg_buf;
          r = bfd_reloc_notsupported;
        }
@@ -3317,52 +3474,27 @@ riscv_std_ext_p (const char *name)
   return (strlen (name) == 1) && (name[0] != 'x') && (name[0] != 's');
 }
 
-/* Check if the versions are compatible.  */
+/* Update the output subset's version to match the input when the input
+   subset's version is newer.  */
 
-static bool
-riscv_version_mismatch (bfd *ibfd,
-                       struct riscv_subset_t *in,
-                       struct riscv_subset_t *out)
+static void
+riscv_update_subset_version (struct riscv_subset_t *in,
+                            struct riscv_subset_t *out)
 {
   if (in == NULL || out == NULL)
-    return true;
-
-  /* Since there are no version conflicts for now, we just report
-     warning when the versions are mis-matched.  */
-  if (in->major_version != out->major_version
-      || in->minor_version != out->minor_version)
+    return;
+
+  /* Update the output ISA versions to the newest ones, but otherwise don't
+     provide any errors or warnings about mis-matched ISA versions as it's
+     generally too tricky to check for these at link time. */
+  if ((in->major_version > out->major_version)
+      || (in->major_version == out->major_version
+         && in->minor_version > out->minor_version)
+      || (out->major_version == RISCV_UNKNOWN_VERSION))
     {
-      if ((in->major_version == RISCV_UNKNOWN_VERSION
-          && in->minor_version == RISCV_UNKNOWN_VERSION)
-         || (out->major_version == RISCV_UNKNOWN_VERSION
-             && out->minor_version == RISCV_UNKNOWN_VERSION))
-       {
-         /* Do not report the warning when the version of input
-            or output is RISCV_UNKNOWN_VERSION, since the extension
-            is added implicitly.  */
-       }
-      else
-       _bfd_error_handler
-         (_("warning: %pB: mis-matched ISA version %d.%d for '%s' "
-            "extension, the output version is %d.%d"),
-          ibfd,
-          in->major_version,
-          in->minor_version,
-          in->name,
-          out->major_version,
-          out->minor_version);
-
-      /* Update the output ISA versions to the newest ones.  */
-      if ((in->major_version > out->major_version)
-         || (in->major_version == out->major_version
-             && in->minor_version > out->minor_version))
-       {
-         out->major_version = in->major_version;
-         out->minor_version = in->minor_version;
-       }
+      out->major_version = in->major_version;
+      out->minor_version = in->minor_version;
     }
-
-  return true;
 }
 
 /* Return true if subset is 'i' or 'e'.  */
@@ -3403,7 +3535,7 @@ riscv_merge_std_ext (bfd *ibfd,
                     struct riscv_subset_t **pin,
                     struct riscv_subset_t **pout)
 {
-  const char *standard_exts = riscv_supported_std_ext ();
+  const char *standard_exts = "mafdqlcbjtpvnh";
   const char *p;
   struct riscv_subset_t *in = *pin;
   struct riscv_subset_t *out = *pout;
@@ -3423,11 +3555,10 @@ riscv_merge_std_ext (bfd *ibfd,
         ibfd, in->name, out->name);
       return false;
     }
-  else if (!riscv_version_mismatch (ibfd, in, out))
-    return false;
-  else
-    riscv_add_subset (&merged_subsets,
-                     out->name, out->major_version, out->minor_version);
+
+  riscv_update_subset_version(in, out);
+  riscv_add_subset (&merged_subsets,
+                   out->name, out->major_version, out->minor_version);
 
   in = in->next;
   out = out->next;
@@ -3445,10 +3576,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,
@@ -3470,8 +3599,7 @@ riscv_merge_std_ext (bfd *ibfd,
    on success and FALSE when a conflict is found.  */
 
 static bool
-riscv_merge_multi_letter_ext (bfd *ibfd,
-                             riscv_subset_t **pin,
+riscv_merge_multi_letter_ext (riscv_subset_t **pin,
                              riscv_subset_t **pout)
 {
   riscv_subset_t *in = *pin;
@@ -3501,8 +3629,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);
@@ -3539,35 +3666,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.  */
@@ -3588,7 +3702,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)
@@ -3875,6 +3989,9 @@ _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;
 
+  /* Allow linking TSO and non-TSO, and keep the TSO flag.  */
+  elf_elfheader (obfd)->e_flags |= new_flags & EF_RISCV_TSO;
+
   return true;
 
  fail:
@@ -3882,115 +3999,6 @@ _bfd_riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
   return false;
 }
 
-/* Delete some bytes from a section while relaxing.  */
-
-static bool
-riscv_relax_delete_bytes (bfd *abfd, asection *sec, bfd_vma addr, size_t count,
-                         struct bfd_link_info *link_info)
-{
-  unsigned int i, symcount;
-  bfd_vma toaddr = sec->size;
-  struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (abfd);
-  Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
-  unsigned int sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec);
-  struct bfd_elf_section_data *data = elf_section_data (sec);
-  bfd_byte *contents = data->this_hdr.contents;
-
-  /* Actually delete the bytes.  */
-  sec->size -= count;
-  memmove (contents + addr, contents + addr + count, toaddr - addr - count);
-
-  /* Adjust the location of all of the relocs.  Note that we need not
-     adjust the addends, since all PC-relative references must be against
-     symbols, which we will adjust below.  */
-  for (i = 0; i < sec->reloc_count; i++)
-    if (data->relocs[i].r_offset > addr && data->relocs[i].r_offset < toaddr)
-      data->relocs[i].r_offset -= count;
-
-  /* Adjust the local symbols defined in this section.  */
-  for (i = 0; i < symtab_hdr->sh_info; i++)
-    {
-      Elf_Internal_Sym *sym = (Elf_Internal_Sym *) symtab_hdr->contents + i;
-      if (sym->st_shndx == sec_shndx)
-       {
-         /* If the symbol is in the range of memory we just moved, we
-            have to adjust its value.  */
-         if (sym->st_value > addr && sym->st_value <= toaddr)
-           sym->st_value -= count;
-
-         /* If the symbol *spans* the bytes we just deleted (i.e. its
-            *end* is in the moved bytes but its *start* isn't), then we
-            must adjust its size.
-
-            This test needs to use the original value of st_value, otherwise
-            we might accidentally decrease size when deleting bytes right
-            before the symbol.  But since deleted relocs can't span across
-            symbols, we can't have both a st_value and a st_size decrease,
-            so it is simpler to just use an else.  */
-         else if (sym->st_value <= addr
-                  && sym->st_value + sym->st_size > addr
-                  && sym->st_value + sym->st_size <= toaddr)
-           sym->st_size -= count;
-       }
-    }
-
-  /* Now adjust the global symbols defined in this section.  */
-  symcount = ((symtab_hdr->sh_size / sizeof (ElfNN_External_Sym))
-             - symtab_hdr->sh_info);
-
-  for (i = 0; i < symcount; i++)
-    {
-      struct elf_link_hash_entry *sym_hash = sym_hashes[i];
-
-      /* The '--wrap SYMBOL' option is causing a pain when the object file,
-        containing the definition of __wrap_SYMBOL, includes a direct
-        call to SYMBOL as well. Since both __wrap_SYMBOL and SYMBOL reference
-        the same symbol (which is __wrap_SYMBOL), but still exist as two
-        different symbols in 'sym_hashes', we don't want to adjust
-        the global symbol __wrap_SYMBOL twice.
-
-        The same problem occurs with symbols that are versioned_hidden, as
-        foo becomes an alias for foo@BAR, and hence they need the same
-        treatment.  */
-      if (link_info->wrap_hash != NULL
-         || sym_hash->versioned == versioned_hidden)
-       {
-         struct elf_link_hash_entry **cur_sym_hashes;
-
-         /* Loop only over the symbols which have already been checked.  */
-         for (cur_sym_hashes = sym_hashes; cur_sym_hashes < &sym_hashes[i];
-              cur_sym_hashes++)
-           {
-             /* If the current symbol is identical to 'sym_hash', that means
-                the symbol was already adjusted (or at least checked).  */
-             if (*cur_sym_hashes == sym_hash)
-               break;
-           }
-         /* Don't adjust the symbol again.  */
-         if (cur_sym_hashes < &sym_hashes[i])
-           continue;
-       }
-
-      if ((sym_hash->root.type == bfd_link_hash_defined
-          || sym_hash->root.type == bfd_link_hash_defweak)
-         && sym_hash->root.u.def.section == sec)
-       {
-         /* As above, adjust the value if needed.  */
-         if (sym_hash->root.u.def.value > addr
-             && sym_hash->root.u.def.value <= toaddr)
-           sym_hash->root.u.def.value -= count;
-
-         /* As above, adjust the size if needed.  */
-         else if (sym_hash->root.u.def.value <= addr
-                  && sym_hash->root.u.def.value + sym_hash->size > addr
-                  && sym_hash->root.u.def.value + sym_hash->size <= toaddr)
-           sym_hash->size -= count;
-       }
-    }
-
-  return true;
-}
-
 /* A second format for recording PC-relative hi relocations.  This stores the
    information required to relax them to GP-relative addresses.  */
 
@@ -4121,6 +4129,257 @@ riscv_find_pcgp_lo_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off)
   return false;
 }
 
+static void
+riscv_update_pcgp_relocs (riscv_pcgp_relocs *p, asection *deleted_sec,
+                         bfd_vma deleted_addr, size_t deleted_count)
+{
+  /* Bytes have already been deleted and toaddr should match the old section
+     size for our checks, so adjust it here.  */
+  bfd_vma toaddr = deleted_sec->size + deleted_count;
+  riscv_pcgp_lo_reloc *l;
+  riscv_pcgp_hi_reloc *h;
+
+  /* Update section offsets of corresponding pcrel_hi relocs for the pcrel_lo
+     entries where they occur after the deleted bytes.  */
+  for (l = p->lo; l != NULL; l = l->next)
+    if (l->hi_sec_off > deleted_addr
+       && l->hi_sec_off < toaddr)
+      l->hi_sec_off -= deleted_count;
+
+  /* Update both section offsets, and symbol values of pcrel_hi relocs where
+     these values occur after the deleted bytes.  */
+  for (h = p->hi; h != NULL; h = h->next)
+    {
+      if (h->hi_sec_off > deleted_addr
+         && h->hi_sec_off < toaddr)
+       h->hi_sec_off -= deleted_count;
+      if (h->sym_sec == deleted_sec
+         && h->hi_addr > deleted_addr
+         && h->hi_addr < toaddr)
+      h->hi_addr -= deleted_count;
+    }
+}
+
+/* Delete some bytes, adjust relcocations and symbol table from a section.  */
+
+static bool
+_riscv_relax_delete_bytes (bfd *abfd,
+                          asection *sec,
+                          bfd_vma addr,
+                          size_t count,
+                          struct bfd_link_info *link_info,
+                          riscv_pcgp_relocs *p,
+                          bfd_vma delete_total,
+                          bfd_vma toaddr)
+{
+  unsigned int i, symcount;
+  struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (abfd);
+  Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+  unsigned int sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec);
+  struct bfd_elf_section_data *data = elf_section_data (sec);
+  bfd_byte *contents = data->this_hdr.contents;
+  size_t bytes_to_move = toaddr - addr - count;
+
+  /* Actually delete the bytes.  */
+  sec->size -= count;
+  memmove (contents + addr, contents + addr + count + delete_total, bytes_to_move);
+
+  /* Still adjust relocations and symbols in non-linear times.  */
+  toaddr = sec->size + count;
+
+  /* Adjust the location of all of the relocs.  Note that we need not
+     adjust the addends, since all PC-relative references must be against
+     symbols, which we will adjust below.  */
+  for (i = 0; i < sec->reloc_count; i++)
+    if (data->relocs[i].r_offset > addr && data->relocs[i].r_offset < toaddr)
+      data->relocs[i].r_offset -= count;
+
+  /* Adjust the hi_sec_off, and the hi_addr of any entries in the pcgp relocs
+     table for which these values occur after the deleted bytes.  */
+  if (p)
+    riscv_update_pcgp_relocs (p, sec, addr, count);
+
+  /* Adjust the local symbols defined in this section.  */
+  for (i = 0; i < symtab_hdr->sh_info; i++)
+    {
+      Elf_Internal_Sym *sym = (Elf_Internal_Sym *) symtab_hdr->contents + i;
+      if (sym->st_shndx == sec_shndx)
+       {
+         /* If the symbol is in the range of memory we just moved, we
+            have to adjust its value.  */
+         if (sym->st_value > addr && sym->st_value <= toaddr)
+           sym->st_value -= count;
+
+         /* If the symbol *spans* the bytes we just deleted (i.e. its
+            *end* is in the moved bytes but its *start* isn't), then we
+            must adjust its size.
+
+            This test needs to use the original value of st_value, otherwise
+            we might accidentally decrease size when deleting bytes right
+            before the symbol.  But since deleted relocs can't span across
+            symbols, we can't have both a st_value and a st_size decrease,
+            so it is simpler to just use an else.  */
+         else if (sym->st_value <= addr
+                  && sym->st_value + sym->st_size > addr
+                  && sym->st_value + sym->st_size <= toaddr)
+           sym->st_size -= count;
+       }
+    }
+
+  /* Now adjust the global symbols defined in this section.  */
+  symcount = ((symtab_hdr->sh_size / sizeof (ElfNN_External_Sym))
+             - symtab_hdr->sh_info);
+
+  for (i = 0; i < symcount; i++)
+    {
+      struct elf_link_hash_entry *sym_hash = sym_hashes[i];
+
+      /* The '--wrap SYMBOL' option is causing a pain when the object file,
+        containing the definition of __wrap_SYMBOL, includes a direct
+        call to SYMBOL as well. Since both __wrap_SYMBOL and SYMBOL reference
+        the same symbol (which is __wrap_SYMBOL), but still exist as two
+        different symbols in 'sym_hashes', we don't want to adjust
+        the global symbol __wrap_SYMBOL twice.
+
+        The same problem occurs with symbols that are versioned_hidden, as
+        foo becomes an alias for foo@BAR, and hence they need the same
+        treatment.  */
+      if (link_info->wrap_hash != NULL
+         || sym_hash->versioned != unversioned)
+       {
+         struct elf_link_hash_entry **cur_sym_hashes;
+
+         /* Loop only over the symbols which have already been checked.  */
+         for (cur_sym_hashes = sym_hashes; cur_sym_hashes < &sym_hashes[i];
+              cur_sym_hashes++)
+           {
+             /* If the current symbol is identical to 'sym_hash', that means
+                the symbol was already adjusted (or at least checked).  */
+             if (*cur_sym_hashes == sym_hash)
+               break;
+           }
+         /* Don't adjust the symbol again.  */
+         if (cur_sym_hashes < &sym_hashes[i])
+           continue;
+       }
+
+      if ((sym_hash->root.type == bfd_link_hash_defined
+          || sym_hash->root.type == bfd_link_hash_defweak)
+         && sym_hash->root.u.def.section == sec)
+       {
+         /* As above, adjust the value if needed.  */
+         if (sym_hash->root.u.def.value > addr
+             && sym_hash->root.u.def.value <= toaddr)
+           sym_hash->root.u.def.value -= count;
+
+         /* As above, adjust the size if needed.  */
+         else if (sym_hash->root.u.def.value <= addr
+                  && sym_hash->root.u.def.value + sym_hash->size > addr
+                  && sym_hash->root.u.def.value + sym_hash->size <= toaddr)
+           sym_hash->size -= count;
+       }
+    }
+
+  return true;
+}
+
+typedef bool (*relax_delete_t) (bfd *, asection *,
+                               bfd_vma, size_t,
+                               struct bfd_link_info *,
+                               riscv_pcgp_relocs *,
+                               Elf_Internal_Rela *);
+
+static relax_delete_t riscv_relax_delete_bytes;
+
+/* Do not delete some bytes from a section while relaxing.
+   Just mark the deleted bytes as R_RISCV_DELETE.  */
+
+static bool
+_riscv_relax_delete_piecewise (bfd *abfd ATTRIBUTE_UNUSED,
+                              asection *sec ATTRIBUTE_UNUSED,
+                              bfd_vma addr,
+                              size_t count,
+                              struct bfd_link_info *link_info ATTRIBUTE_UNUSED,
+                              riscv_pcgp_relocs *p ATTRIBUTE_UNUSED,
+                              Elf_Internal_Rela *rel)
+{
+  if (rel == NULL)
+    return false;
+  rel->r_info = ELFNN_R_INFO (0, R_RISCV_DELETE);
+  rel->r_offset = addr;
+  rel->r_addend = count;
+  return true;
+}
+
+/* Delete some bytes from a section while relaxing.  */
+
+static bool
+_riscv_relax_delete_immediate (bfd *abfd,
+                              asection *sec,
+                              bfd_vma addr,
+                              size_t count,
+                              struct bfd_link_info *link_info,
+                              riscv_pcgp_relocs *p,
+                              Elf_Internal_Rela *rel)
+{
+  if (rel != NULL)
+    rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
+  return _riscv_relax_delete_bytes (abfd, sec, addr, count,
+                                   link_info, p, 0, sec->size);
+}
+
+/* Delete the bytes for R_RISCV_DELETE relocs.  */
+
+static bool
+riscv_relax_resolve_delete_relocs (bfd *abfd,
+                                  asection *sec,
+                                  struct bfd_link_info *link_info,
+                                  Elf_Internal_Rela *relocs)
+{
+  bfd_vma delete_total = 0;
+  unsigned int i;
+
+  for (i = 0; i < sec->reloc_count; i++)
+    {
+      Elf_Internal_Rela *rel = relocs + i;
+      if (ELFNN_R_TYPE (rel->r_info) != R_RISCV_DELETE)
+       continue;
+
+      /* Find the next R_RISCV_DELETE reloc if possible.  */
+      Elf_Internal_Rela *rel_next = NULL;
+      unsigned int start = rel - relocs;
+      for (i = start; i < sec->reloc_count; i++)
+       {
+         /* Since we only replace existing relocs and don't add new relocs, the
+            relocs are in sequential order. We can skip the relocs prior to this
+            one, making this search linear time.  */
+         rel_next = relocs + i;
+         if (ELFNN_R_TYPE ((rel_next)->r_info) == R_RISCV_DELETE
+             && (rel_next)->r_offset > rel->r_offset)
+           {
+             BFD_ASSERT (rel_next - rel > 0);
+             break;
+           }
+         else
+           rel_next = NULL;
+       }
+
+      bfd_vma toaddr = rel_next == NULL ? sec->size : rel_next->r_offset;
+      if (!_riscv_relax_delete_bytes (abfd, sec, rel->r_offset, rel->r_addend,
+                                     link_info, NULL, delete_total, toaddr))
+       return false;
+
+      delete_total += rel->r_addend;
+      rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
+
+      /* Skip ahead to the next delete reloc.  */
+      i = rel_next != NULL ? (unsigned int) (rel_next - relocs - 1)
+                          : sec->reloc_count;
+    }
+
+  return true;
+}
+
 typedef bool (*relax_func_t) (bfd *, asection *, asection *,
                              struct bfd_link_info *,
                              Elf_Internal_Rela *,
@@ -4138,7 +4397,7 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
                       bfd_vma max_alignment,
                       bfd_vma reserve_size ATTRIBUTE_UNUSED,
                       bool *again,
-                      riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED,
+                      riscv_pcgp_relocs *pcgp_relocs,
                       bool undefined_weak ATTRIBUTE_UNUSED)
 {
   bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
@@ -4199,23 +4458,33 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
   /* Replace the AUIPC.  */
   riscv_put_insn (8 * len, auipc, contents + rel->r_offset);
 
-  /* Delete unnecessary JALR.  */
+  /* Delete unnecessary JALR and reuse the R_RISCV_RELAX reloc.  */
   *again = true;
   return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + len, 8 - len,
-                                  link_info);
+                                  link_info, pcgp_relocs, rel + 1);
 }
 
-/* Traverse all output sections and return the max alignment.  */
+/* Traverse all output sections and return the max alignment.
+
+   If gp is zero, then all the output section alignments are
+   possible candidates;  Otherwise, only the output sections
+   which are in the [gp-2K, gp+2K) range need to be considered.  */
 
 static bfd_vma
-_bfd_riscv_get_max_alignment (asection *sec)
+_bfd_riscv_get_max_alignment (asection *sec, bfd_vma gp)
 {
   unsigned int max_alignment_power = 0;
   asection *o;
 
   for (o = sec->output_section->owner->sections; o != NULL; o = o->next)
     {
-      if (o->alignment_power > max_alignment_power)
+      bool valid = true;
+      if (gp
+         && !(VALID_ITYPE_IMM (sec_addr (o) - gp)
+              || VALID_ITYPE_IMM (sec_addr (o) + o->size - gp)))
+       valid = false;
+
+      if (valid && o->alignment_power > max_alignment_power)
        max_alignment_power = o->alignment_power;
     }
 
@@ -4234,16 +4503,19 @@ _bfd_riscv_relax_lui (bfd *abfd,
                      bfd_vma max_alignment,
                      bfd_vma reserve_size,
                      bool *again,
-                     riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED,
+                     riscv_pcgp_relocs *pcgp_relocs,
                      bool undefined_weak)
 {
+  struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (link_info);
   bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
-  bfd_vma gp = riscv_global_pointer_value (link_info);
+  bfd_vma gp = htab->params->relax_gp
+              ? riscv_global_pointer_value (link_info)
+              : 0;
   int use_rvc = elf_elfheader (abfd)->e_flags & EF_RISCV_RVC;
 
   BFD_ASSERT (rel->r_offset + 4 <= sec->size);
 
-  if (gp)
+  if (!undefined_weak && gp)
     {
       /* If gp and the symbol are in the same output section, which is not the
         abs section, then consider only that output section's alignment.  */
@@ -4253,16 +4525,28 @@ _bfd_riscv_relax_lui (bfd *abfd,
       if (h->u.def.section->output_section == sym_sec->output_section
          && sym_sec->output_section != bfd_abs_section_ptr)
        max_alignment = (bfd_vma) 1 << sym_sec->output_section->alignment_power;
+      else
+       {
+         /* Consider output section alignments which are in [gp-2K, gp+2K). */
+         max_alignment = htab->max_alignment_for_gp;
+         if (max_alignment == (bfd_vma) -1)
+           {
+             max_alignment = _bfd_riscv_get_max_alignment (sec, gp);
+             htab->max_alignment_for_gp = max_alignment;
+           }
+       }
     }
 
   /* Is the reference in range of x0 or gp?
-     Valid gp range conservatively because of alignment issue.  */
+     Valid gp range conservatively because of alignment issue.
+
+     Should we also consider the alignment issue for x0 base?  */
   if (undefined_weak
-      || (VALID_ITYPE_IMM (symval)
-         || (symval >= gp
-             && VALID_ITYPE_IMM (symval - gp + max_alignment + reserve_size))
-         || (symval < gp
-             && VALID_ITYPE_IMM (symval - gp - max_alignment - reserve_size))))
+      || VALID_ITYPE_IMM (symval)
+      || (symval >= gp
+         && VALID_ITYPE_IMM (symval - gp + max_alignment + reserve_size))
+      || (symval < gp
+         && VALID_ITYPE_IMM (symval - gp - max_alignment - reserve_size)))
     {
       unsigned sym = ELFNN_R_SYM (rel->r_info);
       switch (ELFNN_R_TYPE (rel->r_info))
@@ -4292,11 +4576,10 @@ _bfd_riscv_relax_lui (bfd *abfd,
          return true;
 
        case R_RISCV_HI20:
-         /* We can delete the unnecessary LUI and reloc.  */
-         rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
+         /* Delete unnecessary LUI and reuse the reloc.  */
          *again = true;
          return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4,
-                                          link_info);
+                                          link_info, pcgp_relocs, rel);
 
        default:
          abort ();
@@ -4327,9 +4610,10 @@ _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);
 
+      /* Delete extra bytes and reuse the R_RISCV_RELAX reloc.  */
       *again = true;
       return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + 2, 2,
-                                      link_info);
+                                      link_info, pcgp_relocs, rel + 1);
     }
 
   return true;
@@ -4347,7 +4631,7 @@ _bfd_riscv_relax_tls_le (bfd *abfd,
                         bfd_vma max_alignment ATTRIBUTE_UNUSED,
                         bfd_vma reserve_size ATTRIBUTE_UNUSED,
                         bool *again,
-                        riscv_pcgp_relocs *prcel_relocs ATTRIBUTE_UNUSED,
+                        riscv_pcgp_relocs *pcgp_relocs,
                         bool undefined_weak ATTRIBUTE_UNUSED)
 {
   /* See if this symbol is in range of tp.  */
@@ -4367,10 +4651,10 @@ _bfd_riscv_relax_tls_le (bfd *abfd,
 
     case R_RISCV_TPREL_HI20:
     case R_RISCV_TPREL_ADD:
-      /* We can delete the unnecessary instruction and reloc.  */
-      rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
+      /* Delete unnecessary instruction and reuse the reloc.  */
       *again = true;
-      return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4, link_info);
+      return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4, link_info,
+                                      pcgp_relocs, rel);
 
     default:
       abort ();
@@ -4389,7 +4673,7 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec,
                        bfd_vma max_alignment ATTRIBUTE_UNUSED,
                        bfd_vma reserve_size ATTRIBUTE_UNUSED,
                        bool *again ATTRIBUTE_UNUSED,
-                       riscv_pcgp_relocs *pcrel_relocs ATTRIBUTE_UNUSED,
+                       riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED,
                        bool undefined_weak ATTRIBUTE_UNUSED)
 {
   bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
@@ -4401,6 +4685,9 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec,
   bfd_vma aligned_addr = ((symval - 1) & ~(alignment - 1)) + alignment;
   bfd_vma nop_bytes = aligned_addr - symval;
 
+  /* Once we've handled an R_RISCV_ALIGN, we can't relax anything else.  */
+  sec->sec_flg0 = true;
+
   /* Make sure there are enough NOPs to actually achieve the alignment.  */
   if (rel->r_addend < nop_bytes)
     {
@@ -4428,9 +4715,10 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec,
   if (nop_bytes % 4 != 0)
     bfd_putl16 (RVC_NOP, contents + rel->r_offset + pos);
 
-  /* Delete the excess bytes.  */
+  /* Delete excess bytes.  */
   return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + nop_bytes,
-                                  rel->r_addend - nop_bytes, link_info);
+                                  rel->r_addend - nop_bytes, link_info,
+                                  NULL, NULL);
 }
 
 /* Relax PC-relative references to GP-relative references.  */
@@ -4444,10 +4732,11 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
                     bfd_vma symval,
                     bfd_vma max_alignment,
                     bfd_vma reserve_size,
-                    bool *again ATTRIBUTE_UNUSED,
+                    bool *again,
                     riscv_pcgp_relocs *pcgp_relocs,
                     bool undefined_weak)
 {
+  struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (link_info);
   bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
   bfd_vma gp = riscv_global_pointer_value (link_info);
 
@@ -4504,7 +4793,7 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
       abort ();
     }
 
-  if (gp)
+  if (!undefined_weak && gp)
     {
       /* If gp and the symbol are in the same output section, which is not the
         abs section, then consider only that output section's alignment.  */
@@ -4514,16 +4803,28 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
       if (h->u.def.section->output_section == sym_sec->output_section
          && sym_sec->output_section != bfd_abs_section_ptr)
        max_alignment = (bfd_vma) 1 << sym_sec->output_section->alignment_power;
+      else
+       {
+         /* Consider output section alignments which are in [gp-2K, gp+2K). */
+         max_alignment = htab->max_alignment_for_gp;
+         if (max_alignment == (bfd_vma) -1)
+           {
+             max_alignment = _bfd_riscv_get_max_alignment (sec, gp);
+             htab->max_alignment_for_gp = max_alignment;
+           }
+       }
     }
 
   /* Is the reference in range of x0 or gp?
-     Valid gp range conservatively because of alignment issue.  */
+     Valid gp range conservatively because of alignment issue.
+
+     Should we also consider the alignment issue for x0 base?  */
   if (undefined_weak
-      || (VALID_ITYPE_IMM (symval)
-         || (symval >= gp
-             && VALID_ITYPE_IMM (symval - gp + max_alignment + reserve_size))
-         || (symval < gp
-             && VALID_ITYPE_IMM (symval - gp - max_alignment - reserve_size))))
+      || VALID_ITYPE_IMM (symval)
+      || (symval >= gp
+         && VALID_ITYPE_IMM (symval - gp + max_alignment + reserve_size))
+      || (symval < gp
+         && VALID_ITYPE_IMM (symval - gp - max_alignment - reserve_size)))
     {
       unsigned sym = hi_reloc.hi_sym;
       switch (ELFNN_R_TYPE (rel->r_info))
@@ -4572,9 +4873,10 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
                                      ELFNN_R_SYM(rel->r_info),
                                      sym_sec,
                                      undefined_weak);
-         /* We can delete the unnecessary AUIPC and reloc.  */
-         rel->r_info = ELFNN_R_INFO (0, R_RISCV_DELETE);
-         rel->r_addend = 4;
+         /* Delete unnecessary AUIPC and reuse the reloc.  */
+         *again = true;
+         riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4, link_info,
+                                   pcgp_relocs, rel);
          return true;
 
        default:
@@ -4585,58 +4887,22 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
   return true;
 }
 
-/* Delete the bytes for R_RISCV_DELETE.  */
+/* Called by after_allocation to set the information of data segment
+   before relaxing.  */
 
-static bool
-_bfd_riscv_relax_delete (bfd *abfd,
-                        asection *sec,
-                        asection *sym_sec ATTRIBUTE_UNUSED,
-                        struct bfd_link_info *link_info,
-                        Elf_Internal_Rela *rel,
-                        bfd_vma symval ATTRIBUTE_UNUSED,
-                        bfd_vma max_alignment ATTRIBUTE_UNUSED,
-                        bfd_vma reserve_size ATTRIBUTE_UNUSED,
-                        bool *again,
-                        riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED,
-                        bool undefined_weak ATTRIBUTE_UNUSED)
-{
-  if (!riscv_relax_delete_bytes (abfd, sec, rel->r_offset, rel->r_addend,
-                                link_info))
-    return false;
-  rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
-  *again = true;
-  return true;
-}
-
-/* Called by after_allocation to check if we need to run the whole
-   relaxations again.  */
-
-bool
-bfd_elfNN_riscv_restart_relax_sections (struct bfd_link_info *info)
+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);
-  bool restart = htab->restart_relax;
-  /* Reset the flag.  */
-  htab->restart_relax = false;
-  return restart;
+  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 obsolete.
-   Pass 3: Which cannot be disabled, handles code alignment directives.
-
-   The `again` is used to determine whether the relax pass itself needs to
-   run again.  And the `restart_relax` is used to determine if we need to
-   run the whole relax passes again from 0 to 2.  Once we have deleted the
-   code between relax pass 0 to 2, the restart_relax will be set to TRUE,
-   and we should run the whole relaxations again to give them more chances
-   to shorten the code.
-
-   Since we can't relax anything else once we start to handle the alignments,
-   we will only enter into the relax pass 3 when the restart_relax is FALSE.  */
+   Pass 0: Shortens code sequences for LUI/CALL/TPREL/PCREL relocs and
+          deletes the obsolete bytes.
+   Pass 1: Which cannot be disabled, handles code alignment directives.  */
 
 static bool
 _bfd_riscv_relax_section (bfd *abfd, asection *sec,
@@ -4651,18 +4917,29 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
   unsigned int i;
   bfd_vma max_alignment, reserve_size = 0;
   riscv_pcgp_relocs pcgp_relocs;
+  static asection *first_section = NULL;
 
   *again = false;
 
   if (bfd_link_relocatable (info)
-      || (sec->flags & SEC_RELOC) == 0
+      || sec->sec_flg0
       || sec->reloc_count == 0
+      || (sec->flags & SEC_RELOC) == 0
+      || (sec->flags & SEC_HAS_CONTENTS) == 0
       || (info->disable_target_specific_optimizations
-         && info->relax_pass < 2)
-      || (htab->restart_relax
-         && info->relax_pass == 3))
+         && info->relax_pass == 0)
+      /* The exp_seg_relro_adjust is enum phase_enum (0x4),
+        and defined in ld/ldexp.h.  */
+      || *(htab->data_segment_phase) == 4)
     return true;
 
+  /* Record the first relax section, so that we can reset the
+     max_alignment_for_gp for the repeated relax passes.  */
+  if (first_section == NULL)
+    first_section = sec;
+  else if (first_section == sec)
+    htab->max_alignment_for_gp = -1;
+
   riscv_init_pcgp_relocs (&pcgp_relocs);
 
   /* Read this BFD's relocs if we haven't done so already.  */
@@ -4672,17 +4949,14 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
                                                 info->keep_memory)))
     goto fail;
 
-  if (htab)
+  /* Estimate the maximum alignment for all output sections once time
+     should be enough.  */
+  max_alignment = htab->max_alignment;
+  if (max_alignment == (bfd_vma) -1)
     {
-      max_alignment = htab->max_alignment;
-      if (max_alignment == (bfd_vma) -1)
-       {
-         max_alignment = _bfd_riscv_get_max_alignment (sec);
-         htab->max_alignment = max_alignment;
-       }
+      max_alignment = _bfd_riscv_get_max_alignment (sec, 0/* gp */);
+      htab->max_alignment = max_alignment;
     }
-  else
-    max_alignment = _bfd_riscv_get_max_alignment (sec);
 
   /* Examine and consider relaxing each reloc.  */
   for (i = 0; i < sec->reloc_count; i++)
@@ -4696,6 +4970,7 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
       bool undefined_weak = false;
 
       relax_func = NULL;
+      riscv_relax_delete_bytes = NULL;
       if (info->relax_pass == 0)
        {
          if (type == R_RISCV_CALL
@@ -4710,24 +4985,15 @@ _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) && htab->params->relax_gp
+                  && (type == R_RISCV_PCREL_HI20
+                      || type == R_RISCV_PCREL_LO12_I
+                      || type == R_RISCV_PCREL_LO12_S))
+           relax_func = _bfd_riscv_relax_pc;
          else
            continue;
-       }
-      else if (info->relax_pass == 1
-              && !bfd_link_pic (info)
-              && (type == R_RISCV_PCREL_HI20
-                  || type == R_RISCV_PCREL_LO12_I
-                  || type == R_RISCV_PCREL_LO12_S))
-       relax_func = _bfd_riscv_relax_pc;
-      else if (info->relax_pass == 2 && type == R_RISCV_DELETE)
-       relax_func = _bfd_riscv_relax_delete;
-      else if (info->relax_pass == 3 && type == R_RISCV_ALIGN)
-       relax_func = _bfd_riscv_relax_align;
-      else
-       continue;
+         riscv_relax_delete_bytes = _riscv_relax_delete_piecewise;
 
-      if (info->relax_pass < 2)
-       {
          /* Only relax this reloc if it is paired with R_RISCV_RELAX.  */
          if (i == sec->reloc_count - 1
              || ELFNN_R_TYPE ((rel + 1)->r_info) != R_RISCV_RELAX
@@ -4737,6 +5003,13 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
          /* Skip over the R_RISCV_RELAX.  */
          i++;
        }
+      else if (info->relax_pass == 1 && type == R_RISCV_ALIGN)
+       {
+         relax_func = _bfd_riscv_relax_align;
+         riscv_relax_delete_bytes = _riscv_relax_delete_immediate;
+       }
+      else
+       continue;
 
       data->relocs = relocs;
 
@@ -4892,6 +5165,10 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
        goto fail;
     }
 
+  /* Resolve R_RISCV_DELETE relocations.  */
+  if (!riscv_relax_resolve_delete_relocs (abfd, sec, info, relocs))
+    goto fail;
+
   ret = true;
 
  fail:
@@ -4899,9 +5176,6 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
     free (relocs);
   riscv_free_pcgp_relocs (&pcgp_relocs, abfd, sec);
 
-  if (*again)
-    htab->restart_relax = true;
-
   return ret;
 }
 
@@ -5094,14 +5368,111 @@ riscv_elf_obj_attrs_arg_type (int tag)
   return (tag & 1) != 0 ? ATTR_TYPE_FLAG_STR_VAL : ATTR_TYPE_FLAG_INT_VAL;
 }
 
-/* PR27584, Omit local and empty symbols since they usually generated
-   for pcrel relocations.  */
+/* Do not choose mapping symbols as a function name.  */
+
+static bfd_size_type
+riscv_maybe_function_sym (const asymbol *sym,
+                         asection *sec,
+                         bfd_vma *code_off)
+{
+  if (sym->flags & BSF_LOCAL
+      && riscv_elf_is_mapping_symbols (sym->name))
+    return 0;
+
+  return _bfd_elf_maybe_function_sym (sym, sec, code_off);
+}
+
+/* Treat the following cases as target special symbols, they are
+   usually omitted.  */
 
 static bool
 riscv_elf_is_target_special_symbol (bfd *abfd, asymbol *sym)
 {
+  /* PR27584, local and empty symbols.  Since they are usually
+     generated for pcrel relocations.  */
   return (!strcmp (sym->name, "")
-         || _bfd_elf_is_local_label_name (abfd, sym->name));
+         || _bfd_elf_is_local_label_name (abfd, sym->name)
+         /* PR27916, mapping symbols.  */
+         || riscv_elf_is_mapping_symbols (sym->name));
+}
+
+static int
+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
@@ -5126,16 +5497,20 @@ riscv_elf_is_target_special_symbol (bfd *abfd, asymbol *sym)
 #define elf_backend_relocate_section           riscv_elf_relocate_section
 #define elf_backend_finish_dynamic_symbol      riscv_elf_finish_dynamic_symbol
 #define elf_backend_finish_dynamic_sections    riscv_elf_finish_dynamic_sections
-#define elf_backend_gc_mark_hook               riscv_elf_gc_mark_hook
 #define elf_backend_plt_sym_val                        riscv_elf_plt_sym_val
 #define elf_backend_grok_prstatus              riscv_elf_grok_prstatus
 #define elf_backend_grok_psinfo                        riscv_elf_grok_psinfo
 #define elf_backend_object_p                   riscv_elf_object_p
 #define elf_backend_write_core_note            riscv_write_core_note
+#define elf_backend_maybe_function_sym         riscv_maybe_function_sym
 #define elf_info_to_howto_rel                  NULL
 #define elf_info_to_howto                      riscv_info_to_howto_rela
 #define bfd_elfNN_bfd_relax_section            _bfd_riscv_relax_section
 #define bfd_elfNN_mkobject                     elfNN_riscv_mkobject
+#define elf_backend_additional_program_headers \
+  riscv_elf_additional_program_headers
+#define elf_backend_modify_segment_map         riscv_elf_modify_segment_map
+#define elf_backend_merge_symbol_attribute     riscv_elf_merge_symbol_attribute
 
 #define elf_backend_init_index_section         _bfd_elf_init_1_index_section
 
@@ -5157,6 +5532,6 @@ riscv_elf_is_target_special_symbol (bfd *abfd, asymbol *sym)
 #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"