[PATCH 2/2] RISC-V: Optimize relaxations without estimating size.

Nelson Chu nelson@rivosinc.com
Fri May 31 07:57:09 GMT 2024


Currently, we try to estimate and reserve enough space when relaxing, to
prevent these factors from causing the relaxed code to not shrink as expected,
thus causing truncated errors.  The reserved spaces include input/output
section alignments, data segment alignment, and the array size which elements
shared high part instructions.  However, there are currently two main problems,

1. May reserve too many size since large alignments.
The following commits tried to improve it, but has limited effect on some cases.
Commit d0f744f, bfd: RISC-V: relax to gp in more cases.
Commit c6261a0, RISC-V: Fix ld relax failure with calls and align directives.
Commit 9d1da81, RISC-V: Optimize lui and auipc relaxations for undefweak symbol.
Commit 0699f2d, RISC-V: Optimize relaxation of gp with max_alignment.

2. Still have truncated errors.
No matter how hard we estimate, we still cannot cover all possibilities.  For
example, the alignments between output sections, and the fixed address
assignments in the linker scripts.  Even in the default linker script there
are constantly new failure cases.  What was recently reported was this,
https://sourceware.org/pipermail/binutils/2023-May/127413.html

Therefore, this patch proposed a new experimental feature that not only try to
resolve the truncated errors, which casued by relaxations, but also can get
more chance to do relaxation since we don't need to reserve too much of size.

The idea is that - we backup all the information that we need when relaxaing,
and then check if overflows occur or not after finishing all relaxations,
including alignments.  If overflows occur, then we disable the relaxation for
those patterns by marking R_RISCV_RELAX to R_RISCV_NONE, recover the backup
information and re-run the whole relaxations again, until there is no overlfow.

So the flow of relaxations are as follow,
pass 0: Recover symbols, relocations and codes according to backups.
pass 1: Shortens code sequences for LUI/CALL/TPREL/PCREL relocs and
deletes the obsolete bytes.  Backup before doing any relaxation.
pass 2: Which cannot be disabled, handles code alignment directives.
pass 3: Check if any relaxation overflows.  If so, re-run from pass 0.

Btw, the reason that we recover the stuff at pass 0 rather than pass 3 is in
the commit 9abcdc1, for the ld/testsuite/ld-riscv/align-small-region.

The backup information includes codes, symbol tables, and relocation tables.
Backup and recover codes and relocations is quite easy to do by malloc and
memcpy.  But backup the whole symbol tables, especially the global hash tables,
is a bit impractical.  Therefore, we just backup the delta address/size after
relaxing, and only for the affected symbols.  Please see the details in the
_riscv_relax_delete_bytes.

As for which relocations need to be checked for overlfow, the simplest way is
to convert these relaxed relocations into corresponding internal relocations
with _RELAX_ prefix, to distinguish them from the versions without relaxation.
After the checkings, we also need to convert these internal relocations back
to the relocations which defined in the psabi, as possible as we can.
Otherwise, it will looks weird if --emit-relocs is set.  Please see the
mappings in the riscv_internal_reloc_map.

Original                  Relaxed and checked overflow         Emit-relocs
                          (_bfd_riscv_relax_check_overflow)
R_RISCV_HI20           -> R_RISCV_RELAX_HI20                -> R_RISCV_NONE
R_RISCV_RELAX          -> R_RISCV_RELAX_DELETE(4)           -> R_RISCV_NONE
R_RISCV_LO12_I/S       -> R_RISCV_RELAX_LUI_GPREL_I/S       -> R_RISCV_LO12_I/S
R_RISCV_RELAX          -> R_RISCV_RELAX                     -> R_RISCV_RELAX

R_RISCV_HI20           -> R_RISCV_RELAX_RVC_LUI             -> R_RISCV_HI20
R_RISCV_RELAX          -> R_RISCV_RELAX_DELETE(2)           -> R_RISCV_NONE
R_RISCV_LO12_I/S       -> R_RISCV_LO12_I/S                  -> R_RISCV_LO12_I/S
R_RISCV_RELAX          -> R_RISCV_RELAX                     -> R_RISCV_RELAX

R_RISCV_PCREL_HI20     -> R_RISCV_RELAX_PCREL_HI20          -> R_RISCV_NONE
R_RISCV_RELAX          -> R_RISCV_RELAX_DELETE(4)           -> R_RISCV_NONE
R_RISCV_PCREL_LO12_I/S -> R_RISCV_RELAX_AUIPC_GPREL_I/S     -> R_RISCV_PCREL_LO12_I/S
R_RISCV_RELAX          -> R_RISCV_RELAX                     -> R_RISCV_RELAX

R_RISCV_CALL           -> R_RISCV_RELAX_JAL/RVC_JUMP/LO12_I -> R_RISCV_JAL/RVC_JUMP/LO12_I
R_RISCV_RELAX          -> R_RISCV_RELAX_DELETE(4)           -> R_RISCV_NONE

(TODO... tls relaxations)

For example, we convert R_RISCV_PCREL_HI20 to R_RISCV_RELAX_PCREL_HI20, and
R_RISCV_PCREL_LO12_I/S to R_RISCV_PCREL_LO12_I/S when they are relaxed.  Even
that the auipc will be deleted, we still keep the R_RISCV_RELAX_PCREL_HI20 is
because it is more easy to check overflow in _bfd_riscv_relax_check_overflow,
and disable the corresponding R_RISCV_RELAX to R_RISCV_NONE without chaining
pcrel_hi and pcrel_lo again.  However, it is probably easy to chain the pcrel
patterns, but it is hard to do the same thing for lui patterns.

There is one more thing that I didn't expect at first - it seems that we still
need to reserve `reserve_size', otherwise, I get the following gcc testcases
failed.  The `reserve_size' is usually the size of array, the elements of array
may share the high lui instruction.  Not sure the details, but at least reserve
the `reserve_size' for the array when relaxing is needed.  And probably we also
need to reserve at least the instruction alignment, even though I passed the
regressions of riscv-gnu-toolchain without any reservations in this patch.

FAIL: tmpdir-gcc.dg-struct-layout-1/t010 c_compat_x_tst.o-c_compat_y_tst.o execute
FAIL: gcc.dg/torture/fp-int-convert-timode.c   -O0  execution test

I suppose this patch should resolve all of the correctness issues about the
relaxation truncated errors, and also give more chance to relax since we don't
need to reserve so much spaces in case code doesn't shrinked.  We only need to
disable relaxations for those patterns which may be truncated, and then re-run
the relaxations until there is no overlfow.  Because of this, I think this
feature has an obvious shortcoming - May increase the link-time since repeatly
re-run the relaxations.  Therefore, I add two option, --relax-with-reserve and
--relax-without-reserve, to disable/enable this new experimental feature.
Default is --relax-with-reserve, to keep the old behaviors.

bfd/
	* elfnn-riscv.c (riscv_elf_link_hash_table): Added restart_relax to
	restart the whole relaxation passes, and added relax_backups to
	backup all relax information.
	(riscv_elf_link_hash_table_create): Init restart_relax and relax_backups.
	(perform_relocation): Handle internal relocations for relaxation.
	(riscv_elf_relocate_section): Likewise.
	(riscv_relax_backup, riscv_relax_backup_sec, riscv_relax_backup_sym):
	Backup informations for relaxation.
	(riscv_relax_search_and_create_backup): New function to search and
	create backups.
	(riscv_relax_recover_and_free_backup): New function to recover backups
	when relax pass 0.
	(bfd_elfNN_riscv_relax_free_backup): New function called from
	ld/emultempl/riscvelf.em, to free the backups at the end.
	(_bfd_riscv_relax_check_overflow): New function to check if relaxation
	overflow or not.  If so, then disable the relaxation at next round for
	this pattern by marking corresponding R_RISCV_RELAX to R_RISCV_NONE.
	(_riscv_relax_delete_bytes): Backup section deleted size, delta address
	and size of global and local symbols.
	(_riscv_relax_delete_piecewise): Updated to handle backups.
	(_riscv_relax_delete_immediate): Likewise.
	(riscv_relax_resolve_delete_relocs): Likewise.
	(_bfd_riscv_relax_tls_le, _bfd_riscv_relax_align): Likewise.
	(_bfd_riscv_relax_call, _bfd_riscv_relax_lui, _bfd_riscv_relax_pc):
	Likewise, and also don't need to reserve spaces for alignments when
	--relax-without-reserve is set.
	(_bfd_riscv_relax_section): Added two more extra relax passes at the
	begining and end, to recover backups and check relaxation overflows.
	Backup information before doing any relaxation.  Set `restart_relax'
	to true, to re-run the whole relaxation when any overflow occurs.
	* elfxx-riscv.c (howto_table_internal): Added more internal relocation,
	to check overflows and disable relaxations easier.
	(riscv_internal_reloc_map): Updated.
	(riscv_emit_internal_reloc_type_lookup): Likewise.
	(riscv_reloc_name_lookup): Likewise.
	* elfxx-riscv.h: Updated.
include/
	* elf/riscv.h: Added new internal relocation for relaxation.  Like
	ppc/ppc64/hppa, use #ifndef RELOC_MACROS_GEN_FUNC to let these internal
	relocation never appear in object files.
ld/
	* emultempl/riscvelf.em: Added new ld options, --relax-with-reserve and
	--relax-without-reserve.  Default is --relax-with-reserve, to keep the
	old behaviors.
	(riscv_elf_before_allocation): Updated link_info.relax_pass from 2 to 4.
	(after_allocation): Called riscv_restart_relax_sections to restart the
	relaxations if any relaxation overflow occurs.  Called
	riscv_relax_free_backup to free all relaxation backups.
	* ldlex.h: Updated since new ld options.
	* testsuite/ld-riscv-elf/ld-riscv-elf.exp: Updated.
	* testsuite/ld-riscv-elf/pcgp-relax-01-emitrelocs.d: Likewise.
	* testsuite/ld-riscv-elf/pcrel-lo-addend.d: Likewise.
	* testsuite/ld-riscv-elf/relax-truncated-*: New testcases.
---
 bfd/elfnn-riscv.c                             | 561 ++++++++++++++----
 bfd/elfxx-riscv.c                             | 143 +++--
 bfd/elfxx-riscv.h                             |  11 +
 include/elf/riscv.h                           |  30 +-
 ld/emultempl/riscvelf.em                      |  23 +-
 ld/ldlex.h                                    |   2 +
 ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp    |  10 +-
 .../ld-riscv-elf/pcgp-relax-01-emitrelocs.d   |   4 +-
 ld/testsuite/ld-riscv-elf/pcrel-lo-addend.d   |   2 +-
 ...lax-truncated-align-addr-without-reserve.d |  26 +
 .../ld-riscv-elf/relax-truncated-align-addr.d |  27 +
 .../relax-truncated-align-addr.ld             |   5 +
 ...align-between-output-sec-without-reserve.d |  26 +
 ...relax-truncated-align-between-output-sec.d |   3 +
 ...elax-truncated-align-between-output-sec.ld |   6 +
 ...cated-data-segment-align-without-reserve.d |  18 +
 .../relax-truncated-data-segment-align.d      |   3 +
 .../relax-truncated-data-segment-align.s      |  21 +
 ...lax-truncated-fixed-addr-without-reserve.d |  26 +
 .../ld-riscv-elf/relax-truncated-fixed-addr.d |   3 +
 .../relax-truncated-fixed-addr.ld             |   5 +
 ld/testsuite/ld-riscv-elf/relax-truncated.s   |  20 +
 22 files changed, 813 insertions(+), 162 deletions(-)
 create mode 100644 ld/testsuite/ld-riscv-elf/relax-truncated-align-addr-without-reserve.d
 create mode 100644 ld/testsuite/ld-riscv-elf/relax-truncated-align-addr.d
 create mode 100644 ld/testsuite/ld-riscv-elf/relax-truncated-align-addr.ld
 create mode 100644 ld/testsuite/ld-riscv-elf/relax-truncated-align-between-output-sec-without-reserve.d
 create mode 100644 ld/testsuite/ld-riscv-elf/relax-truncated-align-between-output-sec.d
 create mode 100644 ld/testsuite/ld-riscv-elf/relax-truncated-align-between-output-sec.ld
 create mode 100644 ld/testsuite/ld-riscv-elf/relax-truncated-data-segment-align-without-reserve.d
 create mode 100644 ld/testsuite/ld-riscv-elf/relax-truncated-data-segment-align.d
 create mode 100644 ld/testsuite/ld-riscv-elf/relax-truncated-data-segment-align.s
 create mode 100644 ld/testsuite/ld-riscv-elf/relax-truncated-fixed-addr-without-reserve.d
 create mode 100644 ld/testsuite/ld-riscv-elf/relax-truncated-fixed-addr.d
 create mode 100644 ld/testsuite/ld-riscv-elf/relax-truncated-fixed-addr.ld
 create mode 100644 ld/testsuite/ld-riscv-elf/relax-truncated.s

diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
index ace6d70ef1b..6eb5c5b4fdc 100644
--- a/bfd/elfnn-riscv.c
+++ b/bfd/elfnn-riscv.c
@@ -204,6 +204,8 @@ elfNN_riscv_mkobject (bfd *abfd)
 #include "elf/common.h"
 #include "elf/internal.h"
 
+typedef struct riscv_relax_backup riscv_relax_backup;
+
 struct riscv_elf_link_hash_table
 {
   struct elf_link_hash_table elf;
@@ -233,6 +235,11 @@ struct riscv_elf_link_hash_table
 
   /* Relocations for variant CC symbols may be present.  */
   int variant_cc;
+
+  /* Re-run the relaxations from relax pass 0 if TRUE.  */
+  bool restart_relax;
+  /* Backup relaxation information.  */
+  riscv_relax_backup *relax_backups;
 };
 
 /* Instruction access functions. */
@@ -512,6 +519,8 @@ riscv_elf_link_hash_table_create (bfd *abfd)
 
   ret->max_alignment = (bfd_vma) -1;
   ret->max_alignment_for_gp = (bfd_vma) -1;
+  ret->restart_relax = false;
+  ret->relax_backups = NULL;
 
   /* Create hash table for local ifunc.  */
   ret->loc_hash_table = htab_try_create (1024,
@@ -1804,22 +1813,23 @@ perform_relocation (const reloc_howto_type *howto,
       break;
 
     case R_RISCV_LO12_I:
-    case R_RISCV_LUI_GPREL_I:
-    case R_RISCV_AUIPC_GPREL_I:
     case R_RISCV_TPREL_LO12_I:
-    case R_RISCV_TPREL_I:
     case R_RISCV_PCREL_LO12_I:
     case R_RISCV_TLSDESC_LOAD_LO12:
     case R_RISCV_TLSDESC_ADD_LO12:
+    case R_RISCV_RELAX_LUI_GPREL_I:
+    case R_RISCV_RELAX_AUIPC_GPREL_I:
+    case R_RISCV_RELAX_TPREL_I:
+    case R_RISCV_RELAX_LO12_I:
       value = ENCODE_ITYPE_IMM (value);
       break;
 
     case R_RISCV_LO12_S:
-    case R_RISCV_LUI_GPREL_S:
-    case R_RISCV_AUIPC_GPREL_S:
     case R_RISCV_TPREL_LO12_S:
-    case R_RISCV_TPREL_S:
     case R_RISCV_PCREL_LO12_S:
+    case R_RISCV_RELAX_LUI_GPREL_S:
+    case R_RISCV_RELAX_AUIPC_GPREL_S:
+    case R_RISCV_RELAX_TPREL_S:
       value = ENCODE_STYPE_IMM (value);
       break;
 
@@ -1832,6 +1842,7 @@ perform_relocation (const reloc_howto_type *howto,
       break;
 
     case R_RISCV_JAL:
+    case R_RISCV_RELAX_JAL:
       if (!VALID_JTYPE_IMM (value))
 	return bfd_reloc_overflow;
       value = ENCODE_JTYPE_IMM (value);
@@ -1850,12 +1861,13 @@ perform_relocation (const reloc_howto_type *howto,
       break;
 
     case R_RISCV_RVC_JUMP:
+    case R_RISCV_RELAX_RVC_JUMP:
       if (!VALID_CJTYPE_IMM (value))
 	return bfd_reloc_overflow;
       value = ENCODE_CJTYPE_IMM (value);
       break;
 
-    case R_RISCV_RVC_LUI:
+    case R_RISCV_RELAX_RVC_LUI:
       if (RISCV_CONST_HIGH_PART (value) == 0)
 	{
 	  /* Linker relaxation can convert an address equal to or greater than
@@ -1939,7 +1951,9 @@ perform_relocation (const reloc_howto_type *howto,
     case R_RISCV_TLS_DTPREL64:
       break;
 
-    case R_RISCV_DELETE:
+    case R_RISCV_RELAX_DELETE:
+    case R_RISCV_RELAX_HI20:
+    case R_RISCV_RELAX_PCREL_HI20:
       return bfd_reloc_ok;
 
     default:
@@ -2554,7 +2568,6 @@ riscv_elf_relocate_section (bfd *output_bfd,
 	case R_RISCV_HI20:
 	case R_RISCV_BRANCH:
 	case R_RISCV_RVC_BRANCH:
-	case R_RISCV_RVC_LUI:
 	case R_RISCV_LO12_I:
 	case R_RISCV_LO12_S:
 	case R_RISCV_SET6:
@@ -2562,10 +2575,18 @@ riscv_elf_relocate_section (bfd *output_bfd,
 	case R_RISCV_SET16:
 	case R_RISCV_SET32:
 	case R_RISCV_32_PCREL:
-	case R_RISCV_DELETE:
 	  /* These require no special handling beyond perform_relocation.  */
 	  break;
 
+	case R_RISCV_RELAX_DELETE:
+	case R_RISCV_RELAX_RVC_LUI:
+	case R_RISCV_RELAX_HI20:
+	case R_RISCV_RELAX_PCREL_HI20:
+	case R_RISCV_RELAX_LO12_I:
+	  /* These mean instructions have relaxed, and equire no special
+	     handling.  */
+	  break;
+
 	case R_RISCV_SET_ULEB128:
 	  if (uleb128_set_rel == NULL)
 	    {
@@ -2765,6 +2786,8 @@ riscv_elf_relocate_section (bfd *output_bfd,
 
 	case R_RISCV_JAL:
 	case R_RISCV_RVC_JUMP:
+	case R_RISCV_RELAX_JAL:
+	case R_RISCV_RELAX_RVC_JUMP:
 	  if (bfd_link_pic (info) && h != NULL)
 	    {
 	      if (h->plt.offset != MINUS_ONE)
@@ -2813,8 +2836,8 @@ riscv_elf_relocate_section (bfd *output_bfd,
 	  relocation = tpoff (info, relocation);
 	  break;
 
-	case R_RISCV_TPREL_I:
-	case R_RISCV_TPREL_S:
+	case R_RISCV_RELAX_TPREL_I:
+	case R_RISCV_RELAX_TPREL_S:
 	  relocation = tpoff (info, relocation);
 	  if (VALID_ITYPE_IMM (relocation + rel->r_addend))
 	    {
@@ -2828,10 +2851,10 @@ riscv_elf_relocate_section (bfd *output_bfd,
 	    r = bfd_reloc_overflow;
 	  break;
 
-	case R_RISCV_LUI_GPREL_I:
-	case R_RISCV_LUI_GPREL_S:
-	case R_RISCV_AUIPC_GPREL_I:
-	case R_RISCV_AUIPC_GPREL_S:
+	case R_RISCV_RELAX_LUI_GPREL_I:
+	case R_RISCV_RELAX_LUI_GPREL_S:
+	case R_RISCV_RELAX_AUIPC_GPREL_I:
+	case R_RISCV_RELAX_AUIPC_GPREL_S:
 	  {
 	    bfd_vma gp = riscv_global_pointer_value (info);
 	    bool x0_base = VALID_ITYPE_IMM (relocation + rel->r_addend);
@@ -4398,6 +4421,227 @@ riscv_update_pcgp_relocs (riscv_pcgp_relocs *p, asection *deleted_sec,
     }
 }
 
+typedef struct riscv_relax_backup_sec riscv_relax_backup_sec;
+struct riscv_relax_backup_sec
+{
+  riscv_relax_backup *root;
+  asection *sec;
+  bfd_vma size;
+  bfd_byte *contents;		/* Code.  */
+  Elf_Internal_Rela *relocs;	/* Relocations.  */
+  riscv_relax_backup_sec *next;
+};
+
+typedef struct
+{
+  /* May point to Elf_Internal_Sym or struct elf_link_hash_entry.  */
+  void *symbol;
+  bfd_vma delta_val;
+  bfd_vma delta_size;
+} riscv_relax_backup_sym;
+
+struct riscv_relax_backup
+{
+  bfd *bfd;
+  riscv_relax_backup_sec *sections;
+  riscv_relax_backup_sym *local_symbols;
+  riscv_relax_backup_sym *global_symbols;
+  unsigned int local_symcount;
+  unsigned int global_symcount;
+  riscv_relax_backup *next;
+};
+
+static riscv_relax_backup_sec *
+riscv_relax_search_and_create_backup (struct bfd_link_info *info, bfd *abfd,
+				      asection *sec)
+{
+  struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
+  riscv_relax_backup *backup = htab->relax_backups;
+  while (backup && backup->bfd != abfd)
+    backup = backup->next;
+  if (backup == NULL)
+    {
+      Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+      backup = bfd_zmalloc (sizeof (*backup));
+      backup->bfd = abfd;
+      backup->local_symcount = symtab_hdr->sh_info;
+      backup->global_symcount =
+	((symtab_hdr->sh_size / sizeof (ElfNN_External_Sym))
+	 - symtab_hdr->sh_info);
+      backup->local_symbols =
+	bfd_zmalloc (sizeof (*backup) * backup->local_symcount);
+      backup->global_symbols =
+	bfd_zmalloc (sizeof (*backup) * backup->global_symcount);
+      backup->next = htab->relax_backups;
+      htab->relax_backups = backup;
+    }
+  riscv_relax_backup_sec *backup_sec = backup->sections;
+  while (backup_sec && backup_sec->sec != sec)
+    backup_sec = backup_sec->next;
+  if (backup_sec == NULL)
+    {
+      struct bfd_elf_section_data *data = elf_section_data (sec);
+      backup_sec = bfd_zmalloc (sizeof (*backup_sec));
+      backup_sec->sec = sec;
+      /* Relocs.  */
+      bfd_size_type size = (bfd_size_type) sec->reloc_count * sizeof (Elf_Internal_Rela);
+      backup_sec->relocs = (Elf_Internal_Rela *) bfd_zmalloc (size);
+      memcpy (backup_sec->relocs, data->relocs, size);
+      /* Section size.  */
+      backup_sec->size = sec->size;
+      /* Code.  */
+      backup_sec->contents = bfd_zmalloc (sec->size);
+      memcpy (backup_sec->contents, data->this_hdr.contents, sec->size);
+      backup_sec->root = backup;
+      backup_sec->next = backup->sections;
+      backup->sections = backup_sec;
+    }
+  return backup_sec;
+}
+
+static void
+riscv_relax_recover_and_free_backup (struct bfd_link_info *info, bool recover)
+{
+  struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
+  riscv_relax_backup *p = htab->relax_backups;
+  unsigned int i;
+  while (p != NULL)
+    {
+      riscv_relax_backup *next_p = p->next;
+      /* Local symbols.  */
+      if (p->local_symbols && p->local_symcount != 0)
+	{
+	  for (i = 0; i < p->local_symcount && recover; i++)
+	    {
+	      riscv_relax_backup_sym *sym = &p->local_symbols[i];
+	      Elf_Internal_Sym *isym = (Elf_Internal_Sym *) sym->symbol;
+	      if (isym != NULL)
+		{
+		  isym->st_value += sym->delta_val;
+		  isym->st_size += sym->delta_size;
+		}
+	    }
+	  free (p->local_symbols);
+	}
+      /* Global symbols.  */
+      if (p->global_symbols && p->global_symcount != 0)
+	{
+	  for (i = 0; i < p->global_symcount && recover; i++)
+	    {
+	      riscv_relax_backup_sym *sym = &p->global_symbols[i];
+	      struct elf_link_hash_entry *h =
+		(struct elf_link_hash_entry *) sym->symbol;
+	      if (h != NULL)
+		{
+		  h->root.u.def.value += sym->delta_val;
+		  h->size += sym->delta_size;
+		}
+	    }
+	  free (p->global_symbols);
+	}
+      /* Sections.  */
+      riscv_relax_backup_sec *s = p->sections;
+      while (s != NULL)
+	{
+	  riscv_relax_backup_sec *next_s = s->next;
+	  asection *sec = s->sec;
+	  if (sec != NULL && s->contents != NULL && s->relocs != NULL)
+	    {
+	      if (recover)
+		{
+		  struct bfd_elf_section_data *data = elf_section_data (sec);
+		  /* Relocs.  */
+		  bfd_size_type size = (bfd_size_type)
+			(sec->reloc_count * sizeof (Elf_Internal_Rela));
+		  memcpy (data->relocs, s->relocs, size);
+		  /* Code.  */
+		  memcpy (data->this_hdr.contents, s->contents, s->size);
+		  sec->size = s->size;
+		}
+	      free (s->relocs);
+	      free (s->contents);
+	    }
+	  free (s);
+	  s = next_s;
+	}
+      free (p);
+      p = next_p;
+    }
+  htab->relax_backups = NULL;
+}
+
+void
+bfd_elfNN_riscv_relax_free_backup (struct bfd_link_info *info)
+{
+  riscv_relax_recover_and_free_backup (info, false/* recover */);
+}
+
+static bool
+_bfd_riscv_relax_check_overflow (bfd *abfd ATTRIBUTE_UNUSED,
+				 asection *sec,
+				 asection *sym_sec ATTRIBUTE_UNUSED,
+				 struct bfd_link_info *link_info,
+				 Elf_Internal_Rela *rel,
+				 bfd_vma symval,
+				 bfd_vma max_alignment ATTRIBUTE_UNUSED,
+				 bfd_vma reserve_size ATTRIBUTE_UNUSED,
+				 bool *again,
+				 riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED,
+				 bool undefined_weak,
+				 riscv_relax_backup_sec *backup_sec)
+{
+  struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (link_info);
+  bfd_vma gp = htab->params->relax_gp
+	       ? riscv_global_pointer_value (link_info)
+	       : 0;
+  struct bfd_elf_section_data *data = elf_section_data (sec);
+  Elf_Internal_Rela *backup_relocs = backup_sec->relocs;
+  Elf_Internal_Rela *backup_rel = &backup_relocs[rel - data->relocs];
+  Elf_Internal_Rela *backup_relax_rel = backup_rel + 1;
+
+  switch (ELFNN_R_TYPE (rel->r_info))
+    {
+      case R_RISCV_RELAX_HI20:
+      case R_RISCV_RELAX_PCREL_HI20:
+      case R_RISCV_RELAX_LUI_GPREL_I:
+      case R_RISCV_RELAX_LUI_GPREL_S:
+      case R_RISCV_RELAX_AUIPC_GPREL_I:
+      case R_RISCV_RELAX_AUIPC_GPREL_S:
+	if (undefined_weak
+	    /* The x0 base.  */
+	    || VALID_ITYPE_IMM (symval)
+	    /* The gp base.  */
+	    || VALID_ITYPE_IMM (symval - gp))
+	  return true;
+	goto disable_relax_reloc;
+      case R_RISCV_RELAX_RVC_LUI:
+	if (VALID_CITYPE_LUI_IMM (RISCV_CONST_HIGH_PART (symval))
+	    /* The perform_relocation will convert it to c.li.  */
+	    || RISCV_CONST_HIGH_PART (symval) == 0)
+	  return true;
+	goto disable_relax_reloc;
+      case R_RISCV_RELAX_JAL:
+	if (VALID_JTYPE_IMM (symval - (sec_addr (sec) + rel->r_offset)))
+	  return true;
+	goto disable_relax_reloc;
+      case R_RISCV_RELAX_RVC_JUMP:
+	if (VALID_CJTYPE_IMM (symval - (sec_addr (sec) + rel->r_offset)))
+	  return true;
+	goto disable_relax_reloc;
+      case R_RISCV_RELAX_LO12_I:
+	/* Near zero.  */
+	if ((symval + RISCV_IMM_REACH / 2) < RISCV_IMM_REACH)
+	  return true;
+ disable_relax_reloc:
+	/* Disable R_RISCV_RELAX if overflow.  */
+	backup_relax_rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE);
+	*again = true;
+	break;
+    }
+
+  return true;
+}
+
 /* Delete some bytes, adjust relcocations and symbol table from a section.  */
 
 static bool
@@ -4408,7 +4652,8 @@ _riscv_relax_delete_bytes (bfd *abfd,
 			   struct bfd_link_info *link_info,
 			   riscv_pcgp_relocs *p,
 			   bfd_vma delete_total,
-			   bfd_vma toaddr)
+			   bfd_vma toaddr,
+			   riscv_relax_backup_sec *backup_sec)
 {
   unsigned int i, symcount;
   struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (abfd);
@@ -4417,6 +4662,7 @@ _riscv_relax_delete_bytes (bfd *abfd,
   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;
+  riscv_relax_backup_sym *backup_symbol = NULL;
 
   /* Actually delete the bytes.  */
   sec->size -= count;
@@ -4443,10 +4689,20 @@ _riscv_relax_delete_bytes (bfd *abfd,
       Elf_Internal_Sym *sym = (Elf_Internal_Sym *) symtab_hdr->contents + i;
       if (sym->st_shndx == sec_shndx)
 	{
+	  if (backup_sec && backup_sec->root)
+	    {
+	      backup_symbol = &backup_sec->root->local_symbols[i];
+	      backup_symbol->symbol = (void *) sym;
+	    }
+
 	  /* 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;
+	    {
+	      sym->st_value -= count;
+	      if (backup_symbol)
+		backup_symbol->delta_val += 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
@@ -4460,7 +4716,11 @@ _riscv_relax_delete_bytes (bfd *abfd,
 	  else if (sym->st_value <= addr
 		   && sym->st_value + sym->st_size > addr
 		   && sym->st_value + sym->st_size <= toaddr)
-	    sym->st_size -= count;
+	    {
+	      sym->st_size -= count;
+	      if (backup_symbol)
+		backup_symbol->delta_size += count;
+	    }
 	}
     }
 
@@ -4505,16 +4765,30 @@ _riscv_relax_delete_bytes (bfd *abfd,
 	   || sym_hash->root.type == bfd_link_hash_defweak)
 	  && sym_hash->root.u.def.section == sec)
 	{
+	  if (backup_sec && backup_sec->root)
+	    {
+	      backup_symbol = &backup_sec->root->global_symbols[i];
+	      backup_symbol->symbol = (void *) sym_hash;
+	    }
+
 	  /* 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;
+	    {
+	      sym_hash->root.u.def.value -= count;
+	      if (backup_symbol)
+		backup_symbol->delta_val += 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;
+	    {
+	      sym_hash->size -= count;
+	      if (backup_symbol)
+		backup_symbol->delta_size += count;
+	    }
 	}
     }
 
@@ -4525,12 +4799,13 @@ typedef bool (*relax_delete_t) (bfd *, asection *,
 				bfd_vma, size_t,
 				struct bfd_link_info *,
 				riscv_pcgp_relocs *,
-				Elf_Internal_Rela *);
+				Elf_Internal_Rela *,
+				riscv_relax_backup_sec *);
 
 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.  */
+   Just mark the deleted bytes as R_RISCV_RELAX_DELETE.  */
 
 static bool
 _riscv_relax_delete_piecewise (bfd *abfd ATTRIBUTE_UNUSED,
@@ -4539,11 +4814,12 @@ _riscv_relax_delete_piecewise (bfd *abfd ATTRIBUTE_UNUSED,
 			       size_t count,
 			       struct bfd_link_info *link_info ATTRIBUTE_UNUSED,
 			       riscv_pcgp_relocs *p ATTRIBUTE_UNUSED,
-			       Elf_Internal_Rela *rel)
+			       Elf_Internal_Rela *rel,
+			       riscv_relax_backup_sec *backup_sec ATTRIBUTE_UNUSED)
 {
   if (rel == NULL)
     return false;
-  rel->r_info = ELFNN_R_INFO (0, R_RISCV_DELETE);
+  rel->r_info = ELFNN_R_INFO (0, R_RISCV_RELAX_DELETE);
   rel->r_offset = addr;
   rel->r_addend = count;
   return true;
@@ -4558,21 +4834,23 @@ _riscv_relax_delete_immediate (bfd *abfd,
 			       size_t count,
 			       struct bfd_link_info *link_info,
 			       riscv_pcgp_relocs *p,
-			       Elf_Internal_Rela *rel)
+			       Elf_Internal_Rela *rel,
+			       riscv_relax_backup_sec *backup_sec)
 {
   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);
+  return _riscv_relax_delete_bytes (abfd, sec, addr, count, link_info, p, 0,
+				    sec->size, backup_sec);
 }
 
-/* Delete the bytes for R_RISCV_DELETE relocs.  */
+/* Delete the bytes for R_RISCV_RELAX_DELETE relocs.  */
 
 static bool
 riscv_relax_resolve_delete_relocs (bfd *abfd,
 				   asection *sec,
 				   struct bfd_link_info *link_info,
-				   Elf_Internal_Rela *relocs)
+				   Elf_Internal_Rela *relocs,
+				   riscv_relax_backup_sec *backup_sec)
 {
   bfd_vma delete_total = 0;
   unsigned int i;
@@ -4580,10 +4858,10 @@ riscv_relax_resolve_delete_relocs (bfd *abfd,
   for (i = 0; i < sec->reloc_count; i++)
     {
       Elf_Internal_Rela *rel = relocs + i;
-      if (ELFNN_R_TYPE (rel->r_info) != R_RISCV_DELETE)
+      if (ELFNN_R_TYPE (rel->r_info) != R_RISCV_RELAX_DELETE)
 	continue;
 
-      /* Find the next R_RISCV_DELETE reloc if possible.  */
+      /* Find the next R_RISCV_RELAX_DELETE reloc if possible.  */
       Elf_Internal_Rela *rel_next = NULL;
       unsigned int start = rel - relocs;
       for (i = start; i < sec->reloc_count; i++)
@@ -4592,7 +4870,7 @@ riscv_relax_resolve_delete_relocs (bfd *abfd,
 	     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
+	  if (ELFNN_R_TYPE ((rel_next)->r_info) == R_RISCV_RELAX_DELETE
 	      && (rel_next)->r_offset > rel->r_offset)
 	    {
 	      BFD_ASSERT (rel_next - rel > 0);
@@ -4604,7 +4882,8 @@ riscv_relax_resolve_delete_relocs (bfd *abfd,
 
       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))
+				      link_info, NULL, delete_total, toaddr,
+				      backup_sec))
 	return false;
 
       delete_total += rel->r_addend;
@@ -4623,7 +4902,8 @@ typedef bool (*relax_func_t) (bfd *, asection *, asection *,
 			      Elf_Internal_Rela *,
 			      bfd_vma, bfd_vma, bfd_vma, bool *,
 			      riscv_pcgp_relocs *,
-			      bool undefined_weak);
+			      bool undefined_weak,
+			      riscv_relax_backup_sec *);
 
 /* Relax AUIPC + JALR into JAL.  */
 
@@ -4636,8 +4916,10 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
 		       bfd_vma reserve_size ATTRIBUTE_UNUSED,
 		       bool *again,
 		       riscv_pcgp_relocs *pcgp_relocs,
-		       bool undefined_weak ATTRIBUTE_UNUSED)
+		       bool undefined_weak ATTRIBUTE_UNUSED,
+		       riscv_relax_backup_sec *backup_sec)
 {
+  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 foff = symval - (sec_addr (sec) + rel->r_offset);
   bool near_zero = (symval + RISCV_IMM_REACH / 2) < RISCV_IMM_REACH;
@@ -4648,7 +4930,7 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
      cause the PC-relative offset to later increase, so we need to add in the
      max alignment of any section inclusive from the call to the target.
      Otherwise, we only need to use the alignment of the current section.  */
-  if (VALID_JTYPE_IMM (foff))
+  if (htab->params->relax_reserve && VALID_JTYPE_IMM (foff))
     {
       if (sym_sec->output_section == sec->output_section
 	  && sym_sec->output_section != bfd_abs_section_ptr)
@@ -4674,20 +4956,20 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
   if (rvc)
     {
       /* Relax to C.J[AL] rd, addr.  */
-      r_type = R_RISCV_RVC_JUMP;
+      r_type = R_RISCV_RELAX_RVC_JUMP;
       auipc = rd == 0 ? MATCH_C_J : MATCH_C_JAL;
       len = 2;
     }
   else if (VALID_JTYPE_IMM (foff))
     {
       /* Relax to JAL rd, addr.  */
-      r_type = R_RISCV_JAL;
+      r_type = R_RISCV_RELAX_JAL;
       auipc = MATCH_JAL | (rd << OP_SH_RD);
     }
   else
     {
       /* Near zero, relax to JALR rd, x0, addr.  */
-      r_type = R_RISCV_LO12_I;
+      r_type = R_RISCV_RELAX_LO12_I;
       auipc = MATCH_JALR | (rd << OP_SH_RD);
     }
 
@@ -4699,7 +4981,8 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
   /* Delete unnecessary JALR and reuse the R_RISCV_RELAX reloc.  */
   *again = true;
   return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + len, 8 - len,
-				   link_info, pcgp_relocs, rel + 1);
+				   link_info, pcgp_relocs, rel + 1,
+				   backup_sec);
 }
 
 /* Traverse all output sections and return the max alignment.
@@ -4742,7 +5025,8 @@ _bfd_riscv_relax_lui (bfd *abfd,
 		      bfd_vma reserve_size,
 		      bool *again,
 		      riscv_pcgp_relocs *pcgp_relocs,
-		      bool undefined_weak)
+		      bool undefined_weak,
+		      riscv_relax_backup_sec *backup_sec)
 {
   struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (link_info);
   bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
@@ -4754,7 +5038,7 @@ _bfd_riscv_relax_lui (bfd *abfd,
 
   BFD_ASSERT (rel->r_offset + 4 <= sec->size);
 
-  if (!undefined_weak && gp)
+  if (htab->params->relax_reserve && !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.  */
@@ -4791,18 +5075,20 @@ _bfd_riscv_relax_lui (bfd *abfd,
       switch (ELFNN_R_TYPE (rel->r_info))
 	{
 	case R_RISCV_LO12_I:
-	  rel->r_info = ELFNN_R_INFO (sym, R_RISCV_LUI_GPREL_I);
+	  rel->r_info = ELFNN_R_INFO (sym, R_RISCV_RELAX_LUI_GPREL_I);
 	  return true;
 
 	case R_RISCV_LO12_S:
-	  rel->r_info = ELFNN_R_INFO (sym, R_RISCV_LUI_GPREL_S);
+	  rel->r_info = ELFNN_R_INFO (sym, R_RISCV_RELAX_LUI_GPREL_S);
 	  return true;
 
 	case R_RISCV_HI20:
-	  /* Delete unnecessary LUI and reuse the reloc.  */
+	  rel->r_info = ELFNN_R_INFO (sym, R_RISCV_RELAX_HI20);
+	  /* Delete unnecessary LUI and reuse the R_RISCV_RELAX reloc.  */
 	  *again = true;
 	  return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4,
-					   link_info, pcgp_relocs, rel);
+					   link_info, pcgp_relocs, rel + 1,
+					   backup_sec);
 
 	default:
 	  abort ();
@@ -4814,12 +5100,18 @@ _bfd_riscv_relax_lui (bfd *abfd,
      RELRO segment the linker aligns it by one page size, therefore sections
      after the segment can be moved more than one page. */
 
+  bfd_vma maxpage_reserve_size;
+  if (htab->params->relax_reserve)
+    maxpage_reserve_size = link_info->relro ? 2 * ELF_MAXPAGESIZE
+					    : ELF_MAXPAGESIZE;
+  else
+    maxpage_reserve_size = 0;
+
   if (use_rvc
       && ELFNN_R_TYPE (rel->r_info) == R_RISCV_HI20
       && VALID_CITYPE_LUI_IMM (RISCV_CONST_HIGH_PART (symval))
       && VALID_CITYPE_LUI_IMM (RISCV_CONST_HIGH_PART (symval)
-			    + (link_info->relro ? 2 * ELF_MAXPAGESIZE
-			       : ELF_MAXPAGESIZE)))
+			       + maxpage_reserve_size))
     {
       /* Replace LUI with C.LUI if legal (i.e., rd != x0 and rd != x2/sp).  */
       bfd_vma lui = bfd_getl32 (contents + rel->r_offset);
@@ -4831,12 +5123,13 @@ _bfd_riscv_relax_lui (bfd *abfd,
       bfd_putl32 (lui, contents + rel->r_offset);
 
       /* Replace the R_RISCV_HI20 reloc.  */
-      rel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel->r_info), R_RISCV_RVC_LUI);
+      rel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel->r_info), R_RISCV_RELAX_RVC_LUI);
 
       /* Delete extra bytes and reuse the R_RISCV_RELAX reloc.  */
       *again = true;
       return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + 2, 2,
-				       link_info, pcgp_relocs, rel + 1);
+				       link_info, pcgp_relocs, rel + 1,
+				       backup_sec);
     }
 
   return true;
@@ -4855,7 +5148,8 @@ _bfd_riscv_relax_tls_le (bfd *abfd,
 			 bfd_vma reserve_size ATTRIBUTE_UNUSED,
 			 bool *again,
 			 riscv_pcgp_relocs *pcgp_relocs,
-			 bool undefined_weak ATTRIBUTE_UNUSED)
+			 bool undefined_weak ATTRIBUTE_UNUSED,
+			 riscv_relax_backup_sec *backup_sec)
 {
   /* See if this symbol is in range of tp.  */
   if (RISCV_CONST_HIGH_PART (tpoff (link_info, symval)) != 0)
@@ -4865,11 +5159,11 @@ _bfd_riscv_relax_tls_le (bfd *abfd,
   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);
+      rel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel->r_info), R_RISCV_RELAX_TPREL_I);
       return true;
 
     case R_RISCV_TPREL_LO12_S:
-      rel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel->r_info), R_RISCV_TPREL_S);
+      rel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel->r_info), R_RISCV_RELAX_TPREL_S);
       return true;
 
     case R_RISCV_TPREL_HI20:
@@ -4877,7 +5171,7 @@ _bfd_riscv_relax_tls_le (bfd *abfd,
       /* Delete unnecessary instruction and reuse the reloc.  */
       *again = true;
       return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4, link_info,
-				       pcgp_relocs, rel);
+				       pcgp_relocs, rel, backup_sec);
 
     default:
       abort ();
@@ -4897,7 +5191,8 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec,
 			bfd_vma reserve_size ATTRIBUTE_UNUSED,
 			bool *again ATTRIBUTE_UNUSED,
 			riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED,
-			bool undefined_weak ATTRIBUTE_UNUSED)
+			bool undefined_weak ATTRIBUTE_UNUSED,
+			riscv_relax_backup_sec *backup_sec)
 {
   bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
   bfd_vma alignment = 1, pos;
@@ -4908,9 +5203,6 @@ _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)
     {
@@ -4941,7 +5233,7 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec,
   /* Delete excess bytes.  */
   return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + nop_bytes,
 				   rel->r_addend - nop_bytes, link_info,
-				   NULL, NULL);
+				   NULL, NULL, backup_sec);
 }
 
 /* Relax PC-relative references to GP-relative references.  */
@@ -4957,7 +5249,8 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
 		     bfd_vma reserve_size,
 		     bool *again,
 		     riscv_pcgp_relocs *pcgp_relocs,
-		     bool undefined_weak)
+		     bool undefined_weak,
+		     riscv_relax_backup_sec *backup_sec)
 {
   struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (link_info);
   /* Can relax to x0 even when gp relaxation is disabled.  */
@@ -5018,7 +5311,7 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
       abort ();
     }
 
-  if (!undefined_weak && gp)
+  if (htab->params->relax_reserve && !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.  */
@@ -5055,12 +5348,12 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
       switch (ELFNN_R_TYPE (rel->r_info))
 	{
 	case R_RISCV_PCREL_LO12_I:
-	  rel->r_info = ELFNN_R_INFO (sym, R_RISCV_AUIPC_GPREL_I);
+	  rel->r_info = ELFNN_R_INFO (sym, R_RISCV_RELAX_AUIPC_GPREL_I);
 	  rel->r_addend += hi_reloc.hi_addend;
 	  return true;
 
 	case R_RISCV_PCREL_LO12_S:
-	  rel->r_info = ELFNN_R_INFO (sym, R_RISCV_AUIPC_GPREL_S);
+	  rel->r_info = ELFNN_R_INFO (sym, R_RISCV_RELAX_AUIPC_GPREL_S);
 	  rel->r_addend += hi_reloc.hi_addend;
 	  return true;
 
@@ -5072,10 +5365,12 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
 				      ELFNN_R_SYM(rel->r_info),
 				      sym_sec,
 				      undefined_weak);
-	  /* Delete unnecessary AUIPC and reuse the reloc.  */
+	  rel->r_info = ELFNN_R_INFO (ELFNN_R_SYM(rel->r_info),
+				      R_RISCV_RELAX_PCREL_HI20);
+	  /* Delete unnecessary AUIPC and reuse the R_RISCV_RELAX reloc.  */
 	  *again = true;
 	  riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4, link_info,
-				    pcgp_relocs, rel);
+				    pcgp_relocs, rel + 1, backup_sec);
 	  return true;
 
 	default:
@@ -5086,6 +5381,19 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED,
   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)
+{
+  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;
+}
+
 /* Called by after_allocation to set the information of data segment
    before relaxing.  */
 
@@ -5099,9 +5407,11 @@ bfd_elfNN_riscv_set_data_segment_info (struct bfd_link_info *info,
 
 /* Relax a section.
 
-   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.  */
+   Pass 0: Recover symbols, relocations and codes according to backups.
+   Pass 1: Shortens code sequences for LUI/CALL/TPREL/PCREL relocs and
+	   deletes the obsolete bytes.  Backup before doing any relaxation.
+   Pass 2: Which cannot be disabled, handles code alignment directives.
+   Pass 3: Check if any relaxation overflows.  If so, re-run from Pass 0.  */
 
 static bool
 _bfd_riscv_relax_section (bfd *abfd, asection *sec,
@@ -5114,31 +5424,25 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
   Elf_Internal_Rela *relocs;
   bool ret = false;
   unsigned int i;
-  bfd_vma max_alignment, reserve_size = 0;
+  bfd_vma max_alignment = 0, reserve_size = 0;
   riscv_pcgp_relocs pcgp_relocs;
   static asection *first_section = NULL;
 
   *again = false;
 
   if (bfd_link_relocatable (info)
-      || 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 == 0)
+	  && info->relax_pass == 1)
+      || (htab->params->relax_reserve
+	  && info->relax_pass == 3)
       /* 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.  */
@@ -5147,14 +5451,46 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
   else if (!(relocs = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL,
 						 info->keep_memory)))
     goto fail;
+  data->relocs = relocs;
+
+  /* Read this BFD's contents if we haven't done so already.  */
+  if (!data->this_hdr.contents
+      && !bfd_malloc_and_get_section (abfd, sec, &data->this_hdr.contents))
+    goto fail;
+
+  /* Read this BFD's symbols if we haven't done so already.  */
+  if (symtab_hdr->sh_info != 0
+      && !symtab_hdr->contents
+      && !(symtab_hdr->contents =
+	(unsigned char *) bfd_elf_get_elf_syms (abfd, symtab_hdr,
+						symtab_hdr->sh_info,
+						0, NULL, NULL, NULL)))
+    goto fail;
+
+  riscv_relax_backup_sec *backup_sec = NULL;
+  if (info->relax_pass == 0)
+    riscv_relax_recover_and_free_backup (info, true/* recover */);
+  else if (!htab->params->relax_reserve)
+    /* Backup information before doing any relaxations.  */
+    backup_sec = riscv_relax_search_and_create_backup (info, abfd, sec);
 
-  /* Estimate the maximum alignment for all output sections once time
-     should be enough.  */
-  max_alignment = htab->max_alignment;
-  if (max_alignment == (bfd_vma) -1)
+  if (htab->params->relax_reserve)
     {
-      max_alignment = _bfd_riscv_get_max_alignment (sec, 0/* gp */);
-      htab->max_alignment = max_alignment;
+      /* 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;
+
+      /* 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 = _bfd_riscv_get_max_alignment (sec, 0/* gp */);
+	  htab->max_alignment = max_alignment;
+	}
     }
 
   /* Examine and consider relaxing each reloc.  */
@@ -5170,7 +5506,7 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
 
       relax_func = NULL;
       riscv_relax_delete_bytes = NULL;
-      if (info->relax_pass == 0)
+      if (info->relax_pass == 1)
 	{
 	  if (type == R_RISCV_CALL
 	      || type == R_RISCV_CALL_PLT)
@@ -5202,38 +5538,38 @@ _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)
+      else if (info->relax_pass == 2 && type == R_RISCV_ALIGN)
 	{
 	  relax_func = _bfd_riscv_relax_align;
 	  riscv_relax_delete_bytes = _riscv_relax_delete_immediate;
 	}
+      else if (info->relax_pass == 3
+	       && (type == R_RISCV_RELAX_HI20
+		   || type == R_RISCV_RELAX_RVC_LUI
+		   || type == R_RISCV_RELAX_PCREL_HI20
+		   || type == R_RISCV_RELAX_LUI_GPREL_I
+		   || type == R_RISCV_RELAX_LUI_GPREL_S
+		   || type == R_RISCV_RELAX_AUIPC_GPREL_I
+		   || type == R_RISCV_RELAX_AUIPC_GPREL_S
+		   || type == R_RISCV_RELAX_JAL
+		   || type == R_RISCV_RELAX_RVC_JUMP
+		   || type == R_RISCV_RELAX_LO12_I))
+	{
+	  relax_func = _bfd_riscv_relax_check_overflow;
+	  riscv_relax_delete_bytes = NULL;
+	}
       else
 	continue;
 
-      data->relocs = relocs;
-
-      /* Read this BFD's contents if we haven't done so already.  */
-      if (!data->this_hdr.contents
-	  && !bfd_malloc_and_get_section (abfd, sec, &data->this_hdr.contents))
-	goto fail;
-
-      /* Read this BFD's symbols if we haven't done so already.  */
-      if (symtab_hdr->sh_info != 0
-	  && !symtab_hdr->contents
-	  && !(symtab_hdr->contents =
-	       (unsigned char *) bfd_elf_get_elf_syms (abfd, symtab_hdr,
-						       symtab_hdr->sh_info,
-						       0, NULL, NULL, NULL)))
-	goto fail;
-
       /* Get the value of the symbol referred to by the reloc.  */
       if (ELFNN_R_SYM (rel->r_info) < symtab_hdr->sh_info)
 	{
 	  /* A local symbol.  */
 	  Elf_Internal_Sym *isym = ((Elf_Internal_Sym *) symtab_hdr->contents
 				    + ELFNN_R_SYM (rel->r_info));
-	  reserve_size = (isym->st_size - rel->r_addend) > isym->st_size
-	    ? 0 : isym->st_size - rel->r_addend;
+	  if (htab->params->relax_reserve)
+	    reserve_size = (isym->st_size - rel->r_addend) > isym->st_size
+			   ? 0 : isym->st_size - rel->r_addend;
 
 	  /* Relocate against local STT_GNU_IFUNC symbol.  we have created
 	     a fake global symbol entry for this, so deal with the local ifunc
@@ -5324,7 +5660,7 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
 	  else
 	    continue;
 
-	  if (h->type != STT_FUNC)
+	  if (htab->params->relax_reserve && h->type != STT_FUNC)
 	    reserve_size =
 	      (h->size - rel->r_addend) > h->size ? 0 : h->size - rel->r_addend;
 	  symtype = h->type;
@@ -5366,12 +5702,13 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
 
       if (!relax_func (abfd, sec, sym_sec, info, rel, symval,
 		       max_alignment, reserve_size, again,
-		       &pcgp_relocs, undefined_weak))
+		       &pcgp_relocs, undefined_weak, backup_sec))
 	goto fail;
     }
 
-  /* Resolve R_RISCV_DELETE relocations.  */
-  if (!riscv_relax_resolve_delete_relocs (abfd, sec, info, relocs))
+  /* Resolve R_RISCV_RELAX_DELETE relocations.  */
+  if (!riscv_relax_resolve_delete_relocs (abfd, sec, info, data->relocs,
+					  backup_sec))
     goto fail;
 
   ret = true;
@@ -5381,6 +5718,12 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
     free (relocs);
   riscv_free_pcgp_relocs (&pcgp_relocs, abfd, sec);
 
+  if (info->relax_pass == 3 && *again)
+    {
+      htab->restart_relax = true;
+      *again = false;
+    }
+
   return ret;
 }
 
diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c
index c1b8b9254f2..8d1b8247953 100644
--- a/bfd/elfxx-riscv.c
+++ b/bfd/elfxx-riscv.c
@@ -881,11 +881,11 @@ static reloc_howto_type howto_table[] =
 
 static reloc_howto_type howto_table_internal[] =
 {
-  /* R_RISCV_DELETE.  */
-  EMPTY_HOWTO (0),
+  /* R_RISCV_RELAX_DELETE.  */
+  EMPTY_HOWTO (R_RISCV_RELAX_DELETE),
 
   /* For lui relaxation, high 6 bits of 18-bit absolute address.  */
-  HOWTO (R_RISCV_RVC_LUI,		/* type */
+  HOWTO (R_RISCV_RELAX_RVC_LUI,		/* type */
 	 0,				/* rightshift */
 	 2,				/* size */
 	 16,				/* bitsize */
@@ -893,14 +893,14 @@ static reloc_howto_type howto_table_internal[] =
 	 0,				/* bitpos */
 	 complain_overflow_dont,	/* complain_on_overflow */
 	 bfd_elf_generic_reloc,		/* special_function */
-	 "R_RISCV_RVC_LUI",		/* name */
+	 "R_RISCV_RELAX_RVC_LUI",	/* name */
 	 false,				/* partial_inplace */
 	 0,				/* src_mask */
 	 ENCODE_CITYPE_IMM (-1U),	/* dst_mask */
 	 false),			/* pcrel_offset */
 
   /* For lui relaxation, GP-relative load.  */
-  HOWTO (R_RISCV_LUI_GPREL_I,		/* type */
+  HOWTO (R_RISCV_RELAX_LUI_GPREL_I,	/* type */
 	 0,				/* rightshift */
 	 4,				/* size */
 	 32,				/* bitsize */
@@ -908,14 +908,14 @@ static reloc_howto_type howto_table_internal[] =
 	 0,				/* bitpos */
 	 complain_overflow_dont,	/* complain_on_overflow */
 	 bfd_elf_generic_reloc,		/* special_function */
-	 "R_RISCV_LUI_GPREL_I",		/* name */
+	 "R_RISCV_RELAX_LUI_GPREL_I",	/* name */
 	 false,				/* partial_inplace */
 	 0,				/* src_mask */
 	 ENCODE_ITYPE_IMM (-1U),	/* dst_mask */
 	 false),			/* pcrel_offset */
 
   /* For lui relaxation, GP-relative store.  */
-  HOWTO (R_RISCV_LUI_GPREL_S,		/* type */
+  HOWTO (R_RISCV_RELAX_LUI_GPREL_S,	/* type */
 	 0,				/* rightshift */
 	 4,				/* size */
 	 32,				/* bitsize */
@@ -923,14 +923,14 @@ static reloc_howto_type howto_table_internal[] =
 	 0,				/* bitpos */
 	 complain_overflow_dont,	/* complain_on_overflow */
 	 bfd_elf_generic_reloc,		/* special_function */
-	 "R_RISCV_LUI_GPREL_S",		/* name */
+	 "R_RISCV_RELAX_LUI_GPREL_S",	/* name */
 	 false,				/* partial_inplace */
 	 0,				/* src_mask */
 	 ENCODE_STYPE_IMM (-1U),	/* dst_mask */
 	 false),			/* pcrel_offset */
 
   /* For auipc relaxation, GP-relative load.  */
-  HOWTO (R_RISCV_AUIPC_GPREL_I,		/* type */
+  HOWTO (R_RISCV_RELAX_AUIPC_GPREL_I,	/* type */
 	 0,				/* rightshift */
 	 4,				/* size */
 	 32,				/* bitsize */
@@ -938,14 +938,14 @@ static reloc_howto_type howto_table_internal[] =
 	 0,				/* bitpos */
 	 complain_overflow_dont,	/* complain_on_overflow */
 	 bfd_elf_generic_reloc,		/* special_function */
-	 "R_RISCV_AUIPC_GPREL_I",	/* name */
+	 "R_RISCV_RELAX_AUIPC_GPREL_I",	/* name */
 	 false,				/* partial_inplace */
 	 0,				/* src_mask */
 	 ENCODE_ITYPE_IMM (-1U),	/* dst_mask */
 	 false),			/* pcrel_offset */
 
   /* For auipc relaxation, GP-relative store.  */
-  HOWTO (R_RISCV_AUIPC_GPREL_S,		/* type */
+  HOWTO (R_RISCV_RELAX_AUIPC_GPREL_S,	/* type */
 	 0,				/* rightshift */
 	 4,				/* size */
 	 32,				/* bitsize */
@@ -953,14 +953,14 @@ static reloc_howto_type howto_table_internal[] =
 	 0,				/* bitpos */
 	 complain_overflow_dont,	/* complain_on_overflow */
 	 bfd_elf_generic_reloc,		/* special_function */
-	 "R_RISCV_AUIPC_GPREL_S",	/* name */
+	 "R_RISCV_RELAX_AUIPC_GPREL_S",	/* name */
 	 false,				/* partial_inplace */
 	 0,				/* src_mask */
 	 ENCODE_STYPE_IMM (-1U),	/* dst_mask */
 	 false),			/* pcrel_offset */
 
   /* For tprel relaxation, TP-relative TLS LE load.  */
-  HOWTO (R_RISCV_TPREL_I,		/* type */
+  HOWTO (R_RISCV_RELAX_TPREL_I,		/* type */
 	 0,				/* rightshift */
 	 4,				/* size */
 	 32,				/* bitsize */
@@ -968,14 +968,14 @@ static reloc_howto_type howto_table_internal[] =
 	 0,				/* bitpos */
 	 complain_overflow_signed,	/* complain_on_overflow */
 	 bfd_elf_generic_reloc,		/* special_function */
-	 "R_RISCV_TPREL_I",		/* name */
+	 "R_RISCV_RELAX_TPREL_I",	/* name */
 	 false,				/* partial_inplace */
 	 0,				/* src_mask */
 	 ENCODE_ITYPE_IMM (-1U),	/* dst_mask */
 	 false),			/* pcrel_offset */
 
   /* For tprel relaxation, TP-relative TLS LE store.  */
-  HOWTO (R_RISCV_TPREL_S,		/* type */
+  HOWTO (R_RISCV_RELAX_TPREL_S,		/* type */
 	 0,				/* rightshift */
 	 4,				/* size */
 	 32,				/* bitsize */
@@ -983,11 +983,84 @@ static reloc_howto_type howto_table_internal[] =
 	 0,				/* bitpos */
 	 complain_overflow_signed,	/* complain_on_overflow */
 	 bfd_elf_generic_reloc,		/* special_function */
-	 "R_RISCV_TPREL_S",		/* name */
+	 "R_RISCV_RELAX_TPREL_S",	/* name */
 	 false,				/* partial_inplace */
 	 0,				/* src_mask */
 	 ENCODE_STYPE_IMM (-1U),	/* dst_mask */
 	 false),			/* pcrel_offset */
+
+  /* High 20 bits of 32-bit absolute address.  */
+  HOWTO (R_RISCV_RELAX_HI20,		/* type */
+	 0,				/* rightshift */
+	 4,				/* size */
+	 32,				/* bitsize */
+	 false,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_dont,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_HI20",		/* name */
+	 false,				/* partial_inplace */
+	 0,				/* src_mask */
+	 ENCODE_UTYPE_IMM (-1U),	/* dst_mask */
+	 false),			/* pcrel_offset */
+
+  /* High 20 bits of 32-bit PC-relative reference.  */
+  HOWTO (R_RISCV_RELAX_PCREL_HI20,	/* type */
+	 0,				/* rightshift */
+	 4,				/* size */
+	 32,				/* bitsize */
+	 true,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_dont,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_RELAX_PCREL_HI20",	/* name */
+	 false,				/* partial_inplace */
+	 0,				/* src_mask */
+	 ENCODE_UTYPE_IMM (-1U),	/* dst_mask */
+	 true),				/* pcrel_offset */
+
+  /* For relax call.  */
+  HOWTO (R_RISCV_RELAX_JAL,		/* type */
+	 0,				/* rightshift */
+	 4,				/* size */
+	 32,				/* bitsize */
+	 true,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_dont,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_RELAX_JAL",		/* name */
+	 false,				/* partial_inplace */
+	 0,				/* src_mask */
+	 ENCODE_JTYPE_IMM (-1U),	/* dst_mask */
+	 true),				/* pcrel_offset */
+
+  HOWTO (R_RISCV_RELAX_RVC_JUMP,	/* type */
+	 0,				/* rightshift */
+	 2,				/* size */
+	 16,				/* bitsize */
+	 true,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_dont,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_RELAX_RVC_JUMP",	/* name */
+	 false,				/* partial_inplace */
+	 0,				/* src_mask */
+	 ENCODE_CJTYPE_IMM (-1U),	/* dst_mask */
+	 true),				/* pcrel_offset */
+
+  HOWTO (R_RISCV_RELAX_LO12_I,		/* type */
+	 0,				/* rightshift */
+	 4,				/* size */
+	 32,				/* bitsize */
+	 false,				/* pc_relative */
+	 0,				/* bitpos */
+	 complain_overflow_dont,	/* complain_on_overflow */
+	 bfd_elf_generic_reloc,		/* special_function */
+	 "R_RISCV_RELAX_LO12_I",	/* name */
+	 false,				/* partial_inplace */
+	 0,				/* src_mask */
+	 ENCODE_ITYPE_IMM (-1U),	/* dst_mask */
+	 false),			/* pcrel_offset */
 };
 
 /* A mapping from BFD reloc types to RISC-V ELF reloc types.  */
@@ -1071,32 +1144,34 @@ riscv_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
 /* A mapping from RISC-V internal reloc types to standard reloc types.  */
 struct elf_internal_reloc_map
 {
-  enum elf_riscv_reloc_type internal;
+  enum elf_riscv_internal_reloc_type internal;
   enum elf_riscv_reloc_type standard;
 };
 
 static const struct elf_internal_reloc_map riscv_internal_reloc_map[] =
 {
-  { R_RISCV_DELETE, R_RISCV_NONE },
-  { R_RISCV_RVC_LUI, R_RISCV_HI20 },
-  { R_RISCV_LUI_GPREL_I, R_RISCV_LO12_I},
-  { R_RISCV_LUI_GPREL_S, R_RISCV_LO12_S},
-  { R_RISCV_AUIPC_GPREL_I, R_RISCV_PCREL_LO12_I},
-  { R_RISCV_AUIPC_GPREL_S, R_RISCV_PCREL_LO12_S},
-  { R_RISCV_TPREL_I, R_RISCV_TPREL_LO12_I },
-  { R_RISCV_TPREL_S, R_RISCV_TPREL_LO12_S },
+  { R_RISCV_RELAX_DELETE, R_RISCV_NONE },
+  { R_RISCV_RELAX_RVC_LUI, R_RISCV_HI20 },
+  { R_RISCV_RELAX_LUI_GPREL_I, R_RISCV_LO12_I},
+  { R_RISCV_RELAX_LUI_GPREL_S, R_RISCV_LO12_S},
+  { R_RISCV_RELAX_AUIPC_GPREL_I, R_RISCV_PCREL_LO12_I},
+  { R_RISCV_RELAX_AUIPC_GPREL_S, R_RISCV_PCREL_LO12_S},
+  { R_RISCV_RELAX_TPREL_I, R_RISCV_TPREL_LO12_I },
+  { R_RISCV_RELAX_TPREL_S, R_RISCV_TPREL_LO12_S },
+  { R_RISCV_RELAX_HI20, R_RISCV_NONE },
+  { R_RISCV_RELAX_PCREL_HI20, R_RISCV_NONE },
+  { R_RISCV_RELAX_JAL, R_RISCV_JAL },
+  { R_RISCV_RELAX_RVC_JUMP, R_RISCV_RVC_JUMP },
+  { R_RISCV_RELAX_LO12_I, R_RISCV_LO12_I },
 };
 
 reloc_howto_type *
 riscv_emit_internal_reloc_type_lookup (bfd *abfd, unsigned int r_type)
 {
-  if (r_type < ARRAY_SIZE (howto_table))
+  if (r_type < R_RISCV_max)
     return NULL;
-
-  unsigned int i;
-  for (i = 0; i < ARRAY_SIZE (riscv_internal_reloc_map); i++)
-    if (riscv_internal_reloc_map[i].internal == r_type)
-      return &howto_table[riscv_internal_reloc_map[i].standard];
+  else if (r_type < R_RISCV_internal_max)
+    return &howto_table[riscv_internal_reloc_map[r_type - R_RISCV_max].standard];
 
   (*_bfd_error_handler)
 	(_("%pB: warning: mismatched internal relocation type %#x"),
@@ -1109,7 +1184,7 @@ riscv_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, const char *r_name)
 {
   unsigned int i;
 
-  for (i = 0; i < ARRAY_SIZE (howto_table); i++)
+  for (i = 0; i < R_RISCV_max; i++)
     if (howto_table[i].name && strcasecmp (howto_table[i].name, r_name) == 0)
       return &howto_table[i];
 
@@ -1119,9 +1194,9 @@ riscv_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, const char *r_name)
 reloc_howto_type *
 riscv_elf_rtype_to_howto (bfd *abfd, unsigned int r_type)
 {
-  if (r_type < ARRAY_SIZE (howto_table))
+  if (r_type < R_RISCV_max)
     return &howto_table[r_type];
-  else if (r_type < R_RISCV_max + ARRAY_SIZE (howto_table_internal))
+  else if (r_type < R_RISCV_internal_max)
     return &howto_table_internal[r_type - R_RISCV_max];
   else
     {
diff --git a/bfd/elfxx-riscv.h b/bfd/elfxx-riscv.h
index 9c747fdd540..3d6ffd02fc0 100644
--- a/bfd/elfxx-riscv.h
+++ b/bfd/elfxx-riscv.h
@@ -31,6 +31,7 @@ struct riscv_elf_params
 {
   /* Whether to relax code sequences to GP-relative addressing.  */
   bool relax_gp;
+  bool relax_reserve;
   /* Whether to check if SUB_ULEB128 relocation has non-zero addend.  */
   bool check_uleb128;
 };
@@ -131,3 +132,13 @@ extern void
 bfd_elf32_riscv_set_data_segment_info (struct bfd_link_info *, int *);
 extern void
 bfd_elf64_riscv_set_data_segment_info (struct bfd_link_info *, int *);
+
+extern bool
+bfd_elf32_riscv_restart_relax_sections (struct bfd_link_info *);
+extern bool
+bfd_elf64_riscv_restart_relax_sections (struct bfd_link_info *);
+
+extern void
+bfd_elf32_riscv_relax_free_backup (struct bfd_link_info *);
+extern void
+bfd_elf64_riscv_relax_free_backup (struct bfd_link_info *);
diff --git a/include/elf/riscv.h b/include/elf/riscv.h
index 4c42ecb3373..825e31e8589 100644
--- a/include/elf/riscv.h
+++ b/include/elf/riscv.h
@@ -75,11 +75,6 @@ START_RELOC_NUMBERS (elf_riscv_reloc_type)
   RELOC_NUMBER (R_RISCV_ALIGN, 43)
   RELOC_NUMBER (R_RISCV_RVC_BRANCH, 44)
   RELOC_NUMBER (R_RISCV_RVC_JUMP, 45)
-  RELOC_NUMBER (R_RISCV_RVC_LUI, 46)
-  RELOC_NUMBER (R_RISCV_GPREL_I, 47)
-  RELOC_NUMBER (R_RISCV_GPREL_S, 48)
-  RELOC_NUMBER (R_RISCV_TPREL_I, 49)
-  RELOC_NUMBER (R_RISCV_TPREL_S, 50)
   RELOC_NUMBER (R_RISCV_RELAX, 51)
   RELOC_NUMBER (R_RISCV_SUB6, 52)
   RELOC_NUMBER (R_RISCV_SET6, 53)
@@ -98,14 +93,23 @@ START_RELOC_NUMBERS (elf_riscv_reloc_type)
 END_RELOC_NUMBERS (R_RISCV_max)
 
 /* Internal relocations used exclusively by the relaxation pass.  */
-#define R_RISCV_DELETE  (R_RISCV_max)
-#define R_RISCV_RVC_LUI (R_RISCV_max + 1)
-#define R_RISCV_LUI_GPREL_I (R_RISCV_max + 2)
-#define R_RISCV_LUI_GPREL_S (R_RISCV_max + 3)
-#define R_RISCV_AUIPC_GPREL_I (R_RISCV_max + 4)
-#define R_RISCV_AUIPC_GPREL_S (R_RISCV_max + 5)
-#define R_RISCV_TPREL_I (R_RISCV_max + 6)
-#define R_RISCV_TPREL_S (R_RISCV_max + 7)
+#ifndef RELOC_MACROS_GEN_FUNC
+START_RELOC_NUMBERS (elf_riscv_internal_reloc_type)
+  FAKE_RELOC (R_RISCV_RELAX_DELETE, R_RISCV_max)
+  EMPTY_RELOC (R_RISCV_RELAX_RVC_LUI)
+  EMPTY_RELOC (R_RISCV_RELAX_LUI_GPREL_I)
+  EMPTY_RELOC (R_RISCV_RELAX_LUI_GPREL_S)
+  EMPTY_RELOC (R_RISCV_RELAX_AUIPC_GPREL_I)
+  EMPTY_RELOC (R_RISCV_RELAX_AUIPC_GPREL_S)
+  EMPTY_RELOC (R_RISCV_RELAX_TPREL_I)
+  EMPTY_RELOC (R_RISCV_RELAX_TPREL_S)
+  EMPTY_RELOC (R_RISCV_RELAX_HI20)
+  EMPTY_RELOC (R_RISCV_RELAX_PCREL_HI20)
+  EMPTY_RELOC (R_RISCV_RELAX_JAL)
+  EMPTY_RELOC (R_RISCV_RELAX_RVC_JUMP)
+  EMPTY_RELOC (R_RISCV_RELAX_LO12_I)
+END_RELOC_NUMBERS (R_RISCV_internal_max)
+#endif
 
 /* Processor specific flags for the ELF header e_flags field.  */
 
diff --git a/ld/emultempl/riscvelf.em b/ld/emultempl/riscvelf.em
index afc43ed55dc..a54526e9fec 100644
--- a/ld/emultempl/riscvelf.em
+++ b/ld/emultempl/riscvelf.em
@@ -26,6 +26,7 @@ fragment <<EOF
 #include "elfxx-riscv.h"
 
 static struct riscv_elf_params params = { .relax_gp = 1,
+					  .relax_reserve = 0,
 					  .check_uleb128 = 0};
 EOF
 
@@ -34,6 +35,9 @@ EOF
 PARSE_AND_LIST_LONGOPTS=${PARSE_AND_LIST_LONGOPTS}'
     { "relax-gp", no_argument, NULL, OPTION_RELAX_GP },
     { "no-relax-gp", no_argument, NULL, OPTION_NO_RELAX_GP },
+    { "relax-with-reserve", no_argument, NULL, OPTION_RELAX_WITH_RESERVE },
+    { "relax-without-reserve", no_argument, NULL, OPTION_RELAX_WITHOUT_RESERVE },
+    { "no-relax-gp", no_argument, NULL, OPTION_NO_RELAX_GP },
     { "check-uleb128", no_argument, NULL, OPTION_CHECK_ULEB128 },
     { "no-check-uleb128", no_argument, NULL, OPTION_NO_CHECK_ULEB128 },
 '
@@ -41,6 +45,8 @@ PARSE_AND_LIST_LONGOPTS=${PARSE_AND_LIST_LONGOPTS}'
 PARSE_AND_LIST_OPTIONS=${PARSE_AND_LIST_OPTIONS}'
   fprintf (file, _("  --relax-gp                  Perform GP relaxation\n"));
   fprintf (file, _("  --no-relax-gp               Don'\''t perform GP relaxation\n"));
+  fprintf (file, _("  --relax-with-reserve        ...\n"));
+  fprintf (file, _("  --relax-without-reserve     ...\n"));
   fprintf (file, _("  --check-uleb128             Check if SUB_ULEB128 has non-zero addend\n"));
   fprintf (file, _("  --no-check-uleb128          Don'\''t check if SUB_ULEB128 has non-zero addend\n"));
 '
@@ -54,6 +60,14 @@ PARSE_AND_LIST_ARGS_CASES=${PARSE_AND_LIST_ARGS_CASES}'
       params.relax_gp = 0;
       break;
 
+    case OPTION_RELAX_WITH_RESERVE:
+      params.relax_reserve = 1;
+      break;
+
+    case OPTION_RELAX_WITHOUT_RESERVE:
+      params.relax_reserve = 0;
+      break;
+
     case OPTION_CHECK_ULEB128:
       params.check_uleb128 = 1;
       break;
@@ -81,7 +95,7 @@ riscv_elf_before_allocation (void)
 	ENABLE_RELAXATION;
     }
 
-  link_info.relax_pass = 2;
+  link_info.relax_pass = 4;
 }
 
 static void
@@ -115,7 +129,12 @@ gld${EMULATION_NAME}_after_allocation (void)
   enum phase_enum *phase = &(expld.dataseg.phase);
   bfd_elf${ELFSIZE}_riscv_set_data_segment_info (&link_info, (int *) phase);
 
-  ldelf_map_segments (need_layout);
+  do
+    {
+      ldelf_map_segments (need_layout);
+    }
+  while (bfd_elf${ELFSIZE}_riscv_restart_relax_sections (&link_info));
+  bfd_elf${ELFSIZE}_riscv_relax_free_backup (&link_info);
 }
 
 /* This is a convenient point to tell BFD about target specific flags.
diff --git a/ld/ldlex.h b/ld/ldlex.h
index 7a0c3b4be94..3ebd45a1719 100644
--- a/ld/ldlex.h
+++ b/ld/ldlex.h
@@ -423,6 +423,8 @@ enum option_values
   /* Used by emultempl/riscvelf.em.  */
   OPTION_RELAX_GP,
   OPTION_NO_RELAX_GP,
+  OPTION_RELAX_WITH_RESERVE,
+  OPTION_RELAX_WITHOUT_RESERVE,
   OPTION_CHECK_ULEB128,
   OPTION_NO_CHECK_ULEB128,
   /* Used by emultempl/rxelf.em.  */
diff --git a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
index 9056402b093..42a3bdd97d0 100644
--- a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
+++ b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
@@ -70,7 +70,7 @@ proc run_dump_test_ifunc { name target output} {
 
     run_ld_link_tests [list \
 	[list "$name ($target-$output)" \
-	      "$ldflags" "" \
+	      "$ldflags --no-relax-gp" "" \
 	      "$asflags" \
 	      [list "$name.s"] \
 	      [concat [list "readelf -rW $name-$output.rd"] \
@@ -174,6 +174,14 @@ if [istarget "riscv*-*-*"] {
     run_dump_test "attr-merge-priv-spec-failed-06"
     run_dump_test "attr-phdr"
     run_dump_test "relax-max-align-gp"
+    run_dump_test "relax-truncated-fixed-addr"
+    run_dump_test "relax-truncated-align-addr"
+    run_dump_test "relax-truncated-align-between-output-sec"
+    run_dump_test "relax-truncated-data-segment-align"
+    run_dump_test "relax-truncated-fixed-addr-without-reserve"
+    run_dump_test "relax-truncated-align-addr-without-reserve"
+    run_dump_test "relax-truncated-align-between-output-sec-without-reserve"
+    run_dump_test "relax-truncated-data-segment-align-without-reserve"
     run_dump_test "uleb128"
     run_dump_test "pr31179"
     run_dump_test "pr31179-r"
diff --git a/ld/testsuite/ld-riscv-elf/pcgp-relax-01-emitrelocs.d b/ld/testsuite/ld-riscv-elf/pcgp-relax-01-emitrelocs.d
index 9aa05d5be6a..3b8065ac3b8 100644
--- a/ld/testsuite/ld-riscv-elf/pcgp-relax-01-emitrelocs.d
+++ b/ld/testsuite/ld-riscv-elf/pcgp-relax-01-emitrelocs.d
@@ -15,13 +15,13 @@ Disassembly of section .text:
 [ 	]+[0-9a-f]+:[ 	]+R_RISCV_JAL[ 	]+_start
 [ 	]+[0-9a-f]+:[ 	]+[0-9a-f]+[ 	]+addi[ 	]+a1,gp,\-[0-9]+ # [0-9a-f]+ <data_g>
 [ 	]+[0-9a-f]+:[ 	]+R_RISCV_NONE[ 	]+\*ABS\*\+0x4
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_NONE[ 	]+data_g
 [ 	]+[0-9a-f]+:[ 	]+R_RISCV_NONE[ 	]+\*ABS\*\+0x4
-[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
 [ 	]+[0-9a-f]+:[ 	]+R_RISCV_PCREL_LO12_I[ 	]+data_g\-0x[0-9a-f]+
 [ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
 [ 	]+[0-9a-f]+:[ 	]+[0-9a-f]+[ 	]+addi[ 	]+a2,gp,\-[0-9]+ # [0-9a-f]+ <data_g>
+[ 	]+[0-9a-f]+:[ 	]+R_RISCV_NONE[ 	]+data_g
 [ 	]+[0-9a-f]+:[ 	]+R_RISCV_NONE[ 	]+\*ABS\*\+0x4
-[ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
 [ 	]+[0-9a-f]+:[ 	]+R_RISCV_LO12_I[ 	]+data_g\-0x[0-9a-f]+
 [ 	]+[0-9a-f]+:[ 	]+R_RISCV_RELAX[ 	]+\*ABS\*
 [ 	]+[0-9a-f]+:[ 	]+[0-9a-f]+[ 	]+addi[ 	]+a3,tp,0 # 0 <data_t>
diff --git a/ld/testsuite/ld-riscv-elf/pcrel-lo-addend.d b/ld/testsuite/ld-riscv-elf/pcrel-lo-addend.d
index 92d41528a4a..44946e7c77e 100644
--- a/ld/testsuite/ld-riscv-elf/pcrel-lo-addend.d
+++ b/ld/testsuite/ld-riscv-elf/pcrel-lo-addend.d
@@ -1,5 +1,5 @@
 #name: %pcrel_lo section symbol with an addend
 #source: pcrel-lo-addend.s
 #as: -march=rv32ic
-#ld: -m[riscv_choose_ilp32_emul]
+#ld: -m[riscv_choose_ilp32_emul] --no-relax-gp
 #error: .*dangerous relocation: %pcrel_lo section symbol with an addend
diff --git a/ld/testsuite/ld-riscv-elf/relax-truncated-align-addr-without-reserve.d b/ld/testsuite/ld-riscv-elf/relax-truncated-align-addr-without-reserve.d
new file mode 100644
index 00000000000..c6836b8a0f6
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relax-truncated-align-addr-without-reserve.d
@@ -0,0 +1,26 @@
+#source: relax-truncated.s
+#ld: --relax-without-reserve -Trelax-truncated-align-addr.ld
+#objdump: -d
+
+.*:[ 	]+file format .*
+
+
+Disassembly of section .text.a:
+
+0+[0-9a-f]+ <_start>:
+.*:[ 	]+[0-9a-f]+[ 	]+jal[ 	]+.*<bar>
+#...
+
+0+[0-9a-f]+ <foo>:
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
+.*:[ 	]+[0-9a-f]+[ 	]+jalr[ 	]+.*<baz>
+
+Disassembly of section .text.b:
+
+0+[0-9a-f]+ <bar>:
+.*:[ 	]+[0-9a-f]+[ 	]+nop
+
+Disassembly of section .text.c:
+
+0+[0-9a-f]+ <baz>:
+.*:[ 	]+[0-9a-f]+[ 	]+nop
diff --git a/ld/testsuite/ld-riscv-elf/relax-truncated-align-addr.d b/ld/testsuite/ld-riscv-elf/relax-truncated-align-addr.d
new file mode 100644
index 00000000000..4ff5e0eb1c9
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relax-truncated-align-addr.d
@@ -0,0 +1,27 @@
+#source: relax-truncated.s
+#ld: --relax-with-reserve -Trelax-truncated-align-addr.ld
+#objdump: -d
+
+.*:[ 	]+file format .*
+
+
+Disassembly of section .text.a:
+
+0+[0-9a-f]+ <_start>:
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
+.*:[ 	]+[0-9a-f]+[ 	]+jalr[ 	]+.*<bar>
+#...
+
+0+[0-9a-f]+ <foo>:
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
+.*:[ 	]+[0-9a-f]+[ 	]+jalr[ 	]+.*<baz>
+
+Disassembly of section .text.b:
+
+0+[0-9a-f]+ <bar>:
+.*:[ 	]+[0-9a-f]+[ 	]+nop
+
+Disassembly of section .text.c:
+
+0+[0-9a-f]+ <baz>:
+.*:[ 	]+[0-9a-f]+[ 	]+nop
diff --git a/ld/testsuite/ld-riscv-elf/relax-truncated-align-addr.ld b/ld/testsuite/ld-riscv-elf/relax-truncated-align-addr.ld
new file mode 100644
index 00000000000..af9ea425fd3
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relax-truncated-align-addr.ld
@@ -0,0 +1,5 @@
+SECTIONS {
+	.text.a 0x0 : { *(.text.a) }
+	.text.b : { *(.text.b) }
+	.text.c : ALIGN(0x100400) { *(.text.c) }
+}
diff --git a/ld/testsuite/ld-riscv-elf/relax-truncated-align-between-output-sec-without-reserve.d b/ld/testsuite/ld-riscv-elf/relax-truncated-align-between-output-sec-without-reserve.d
new file mode 100644
index 00000000000..3b79521f3b2
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relax-truncated-align-between-output-sec-without-reserve.d
@@ -0,0 +1,26 @@
+#source: relax-truncated.s
+#ld: --relax-without-reserve -Trelax-truncated-align-between-output-sec.ld
+#objdump: -d
+
+.*:[ 	]+file format .*
+
+
+Disassembly of section .text.a:
+
+0+[0-9a-f]+ <_start>:
+.*:[ 	]+[0-9a-f]+[ 	]+jal[ 	]+[0-9a-f]+ <bar>
+#...
+
+0+[0-9a-f]+ <foo>:
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+ra,.*
+.*:[ 	]+[0-9a-f]+[ 	]+jalr[ 	]+ra # [0-9a-f]+ <baz>
+
+Disassembly of section .text.b:
+
+0+[0-9a-f]+ <bar>:
+.*:[ 	]+[0-9a-f]+[ 	]+nop
+
+Disassembly of section .text.c:
+
+0+[0-9a-f]+ <baz>:
+.*:[ 	]+[0-9a-f]+[ 	]+nop
diff --git a/ld/testsuite/ld-riscv-elf/relax-truncated-align-between-output-sec.d b/ld/testsuite/ld-riscv-elf/relax-truncated-align-between-output-sec.d
new file mode 100644
index 00000000000..1e2ceb396de
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relax-truncated-align-between-output-sec.d
@@ -0,0 +1,3 @@
+#source: relax-truncated.s
+#ld: --relax-with-reserve -Trelax-truncated-align-between-output-sec.ld
+#error: .*relocation truncated to fit: R_RISCV_RELAX_JAL against `baz'.*
diff --git a/ld/testsuite/ld-riscv-elf/relax-truncated-align-between-output-sec.ld b/ld/testsuite/ld-riscv-elf/relax-truncated-align-between-output-sec.ld
new file mode 100644
index 00000000000..eec27f23d4a
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relax-truncated-align-between-output-sec.ld
@@ -0,0 +1,6 @@
+SECTIONS {
+	.text.a 0x0 : { *(.text.a) }
+	.text.b : { *(.text.b) }
+	. = ALIGN (0x100400);
+	.text.c : { *(.text.c) }
+}
diff --git a/ld/testsuite/ld-riscv-elf/relax-truncated-data-segment-align-without-reserve.d b/ld/testsuite/ld-riscv-elf/relax-truncated-data-segment-align-without-reserve.d
new file mode 100644
index 00000000000..95e85ee4220
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relax-truncated-data-segment-align-without-reserve.d
@@ -0,0 +1,18 @@
+#source: relax-truncated-data-segment-align.s
+#ld: --relax-without-reserve -Ttext 0x10094
+#objdump: -d
+
+.*:[ 	]+file format .*
+
+
+Disassembly of section .text:
+
+0+[0-9a-f]+ <_start>:
+.*:[ 	]+[0-9a-f]+[ 	]+jal[ 	]+.*
+#...
+
+0+[0-9a-f]+ <foo>:
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+a0,.*
+.*:[ 	]+[0-9a-f]+[ 	]+addi[ 	]+a0,a0,.*
+.*:[ 	]+[0-9a-f]+[ 	]+lui[ 	]+a0,.*
+.*:[ 	]+[0-9a-f]+[ 	]+addi[ 	]+a0,a0,.*
diff --git a/ld/testsuite/ld-riscv-elf/relax-truncated-data-segment-align.d b/ld/testsuite/ld-riscv-elf/relax-truncated-data-segment-align.d
new file mode 100644
index 00000000000..d1ec22920d4
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relax-truncated-data-segment-align.d
@@ -0,0 +1,3 @@
+#source: relax-truncated-data-segment-align.s
+#ld: --relax-with-reserve -Ttext 0x10094
+#error: .*relocation truncated to fit: R_RISCV_RELAX_AUIPC_GPREL_I against `symbol'.*
diff --git a/ld/testsuite/ld-riscv-elf/relax-truncated-data-segment-align.s b/ld/testsuite/ld-riscv-elf/relax-truncated-data-segment-align.s
new file mode 100644
index 00000000000..c483c5ddf9d
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relax-truncated-data-segment-align.s
@@ -0,0 +1,21 @@
+	.text
+	.globl _start
+_start:
+	.rept 6000
+	call _start
+	.endr
+	.globl foo
+foo:
+	lla a0, symbol
+	lui a0, %hi(symbol)
+	addi a0, a0, %lo(symbol)
+
+	.section .rodata
+	.set symbol, . + 4598
+	.fill 100, 4, 1
+
+	.data
+	.align 3
+	.rept 860
+	.long 0x1000
+	.endr
diff --git a/ld/testsuite/ld-riscv-elf/relax-truncated-fixed-addr-without-reserve.d b/ld/testsuite/ld-riscv-elf/relax-truncated-fixed-addr-without-reserve.d
new file mode 100644
index 00000000000..96868bccd3c
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relax-truncated-fixed-addr-without-reserve.d
@@ -0,0 +1,26 @@
+#source: relax-truncated.s
+#ld: --relax-without-reserve -Trelax-truncated-fixed-addr.ld
+#objdump: -d
+
+.*:[ 	]+file format .*
+
+
+Disassembly of section .text.a:
+
+0+[0-9a-f]+ <_start>:
+.*:[ 	]+[0-9a-f]+[ 	]+jal[ 	]+.*<bar>
+#...
+
+0+[0-9a-f]+ <foo>:
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
+.*:[ 	]+[0-9a-f]+[ 	]+jalr[ 	]+.*<baz>
+
+Disassembly of section .text.b:
+
+0+[0-9a-f]+ <bar>:
+.*:[ 	]+[0-9a-f]+[ 	]+nop
+
+Disassembly of section .text.c:
+
+0+[0-9a-f]+ <baz>:
+.*:[ 	]+[0-9a-f]+[ 	]+nop
diff --git a/ld/testsuite/ld-riscv-elf/relax-truncated-fixed-addr.d b/ld/testsuite/ld-riscv-elf/relax-truncated-fixed-addr.d
new file mode 100644
index 00000000000..e1cc8ccef91
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relax-truncated-fixed-addr.d
@@ -0,0 +1,3 @@
+#source: relax-truncated.s
+#ld: --relax-with-reserve -Trelax-truncated-fixed-addr.ld
+#error: .*relocation truncated to fit: R_RISCV_RELAX_JAL against `baz'.*
diff --git a/ld/testsuite/ld-riscv-elf/relax-truncated-fixed-addr.ld b/ld/testsuite/ld-riscv-elf/relax-truncated-fixed-addr.ld
new file mode 100644
index 00000000000..ad93798e76d
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relax-truncated-fixed-addr.ld
@@ -0,0 +1,5 @@
+SECTIONS {
+	.text.a 0x0 : { *(.text.a) }
+	.text.b : { *(.text.b) }
+	.text.c 0x100400 : { *(.text.c) }
+}
diff --git a/ld/testsuite/ld-riscv-elf/relax-truncated.s b/ld/testsuite/ld-riscv-elf/relax-truncated.s
new file mode 100644
index 00000000000..fc1382618a6
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/relax-truncated.s
@@ -0,0 +1,20 @@
+	.section .text.a, "ax"
+	.align 2
+	.globl _start
+_start:
+	.rept 0x100
+	call bar
+	.endr
+	.globl foo
+foo:
+	call baz
+
+	.section .text.b, "ax"
+	.align 2
+bar:
+	nop
+
+	.section .text.c, "ax"
+	.align 2
+baz:
+	nop
-- 
2.39.3 (Apple Git-146)



More information about the Binutils mailing list