[binutils-gdb] RISC-V: check offsets when linker relaxation is disabled

Nelson Chu nelsonc1225@sourceware.org
Fri May 16 01:56:49 GMT 2025


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=27f754ee503500d4986bfec4e4daf6fda014e159

commit 27f754ee503500d4986bfec4e4daf6fda014e159
Author: Tsukasa OI <research_trasio@irq.a4lg.com>
Date:   Tue May 13 08:14:01 2025 +0000

    RISC-V: check offsets when linker relaxation is disabled
    
    The assembler partially relied on the linker to check whether the
    offset is valid.  However, some optimization logic (added later)
    removes relocations relative to local symbols without checking offsets.
    
    For instance, it caused following code to silently emit wrong jumps
    (to the jump instruction "." itself) without relocations:
    
    > .option norelax
    > j .+0x200000   # J (or JAL) instruction cannot encode this offset.
    > j .+1          # Jump to odd address is not valid.
    
    This commit adds offset checks where necessary.
    
    gas/ChangeLog:
    
            * config/tc-riscv.c (md_apply_fix): Check offsets when the
            relocation relative to a local symbol is being optimized out.
            * testsuite/gas/riscv/no-relax-branch-offset-fail.s: Failure
            case where the branch offset is invalid.
            * testsuite/gas/riscv/no-relax-branch-offset-fail.d: Ditto.
            * testsuite/gas/riscv/no-relax-branch-offset-fail.l: Ditto.
            * testsuite/gas/riscv/no-relax-branch-offset-ok.s: Border case.
            * testsuite/gas/riscv/no-relax-branch-offset-ok.d: Ditto.
            * testsuite/gas/riscv/no-relax-pcrel-offset-fail-64.s: Failure
            case only on RV64 where the PC-relative offset exceed limits.
            * testsuite/gas/riscv/no-relax-pcrel-offset-fail-64.d: Ditto.
            * testsuite/gas/riscv/no-relax-pcrel-offset-fail-64.l: Ditto.
            * testsuite/gas/riscv/no-relax-pcrel-offset-fail-not-32.d: Test
            case for RV32 so that no errors occur.
            * testsuite/gas/riscv/no-relax-pcrel-offset-ok.s: Border case.
            * testsuite/gas/riscv/no-relax-pcrel-offset-ok.d: Ditto.

Diff:
---
 gas/config/tc-riscv.c                              | 44 +++++++++++++++++++---
 .../gas/riscv/no-relax-branch-offset-fail.d        |  2 +
 .../gas/riscv/no-relax-branch-offset-fail.l        |  8 ++++
 .../gas/riscv/no-relax-branch-offset-fail.s        | 20 ++++++++++
 .../gas/riscv/no-relax-branch-offset-ok.d          | 17 +++++++++
 .../gas/riscv/no-relax-branch-offset-ok.s          | 17 +++++++++
 .../gas/riscv/no-relax-pcrel-offset-fail-64.d      |  2 +
 .../gas/riscv/no-relax-pcrel-offset-fail-64.l      |  7 ++++
 .../gas/riscv/no-relax-pcrel-offset-fail-64.s      | 31 +++++++++++++++
 .../gas/riscv/no-relax-pcrel-offset-fail-not-32.d  | 22 +++++++++++
 gas/testsuite/gas/riscv/no-relax-pcrel-offset-ok.d | 21 +++++++++++
 gas/testsuite/gas/riscv/no-relax-pcrel-offset-ok.s | 29 ++++++++++++++
 12 files changed, 214 insertions(+), 6 deletions(-)

diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c
index ca4030d6a93..a35288e55cb 100644
--- a/gas/config/tc-riscv.c
+++ b/gas/config/tc-riscv.c
@@ -4848,7 +4848,13 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
 	  bfd_vma delta = target - md_pcrel_from (fixP);
 	  bfd_putl32 (bfd_getl32 (buf) | ENCODE_JTYPE_IMM (delta), buf);
 	  if (!riscv_opts.relax && S_IS_LOCAL (fixP->fx_addsy))
-	    fixP->fx_done = 1;
+	    {
+	      if (!VALID_JTYPE_IMM (delta))
+		as_bad_where (fixP->fx_file, fixP->fx_line,
+			      _("invalid J-type offset (%+lld)"),
+			      (long long) delta);
+	      fixP->fx_done = 1;
+	    }
 	}
       break;
 
@@ -4860,7 +4866,13 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
 	  bfd_vma delta = target - md_pcrel_from (fixP);
 	  bfd_putl32 (bfd_getl32 (buf) | ENCODE_BTYPE_IMM (delta), buf);
 	  if (!riscv_opts.relax && S_IS_LOCAL (fixP->fx_addsy))
-	    fixP->fx_done = 1;
+	    {
+	      if (!VALID_BTYPE_IMM (delta))
+		as_bad_where (fixP->fx_file, fixP->fx_line,
+			      _("invalid B-type offset (%+lld)"),
+			      (long long) delta);
+	      fixP->fx_done = 1;
+	    }
 	}
       break;
 
@@ -4872,7 +4884,13 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
 	  bfd_vma delta = target - md_pcrel_from (fixP);
 	  bfd_putl16 (bfd_getl16 (buf) | ENCODE_CBTYPE_IMM (delta), buf);
 	  if (!riscv_opts.relax && S_IS_LOCAL (fixP->fx_addsy))
-	    fixP->fx_done = 1;
+	    {
+	      if (!VALID_CBTYPE_IMM (delta))
+		as_bad_where (fixP->fx_file, fixP->fx_line,
+			      _("invalid CB-type offset (%+lld)"),
+			      (long long) delta);
+	      fixP->fx_done = 1;
+	    }
 	}
       break;
 
@@ -4884,7 +4902,13 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
 	  bfd_vma delta = target - md_pcrel_from (fixP);
 	  bfd_putl16 (bfd_getl16 (buf) | ENCODE_CJTYPE_IMM (delta), buf);
 	  if (!riscv_opts.relax && S_IS_LOCAL (fixP->fx_addsy))
-	    fixP->fx_done = 1;
+	    {
+	      if (!VALID_CJTYPE_IMM (delta))
+		as_bad_where (fixP->fx_file, fixP->fx_line,
+			      _("invalid CJ-type offset (%+lld)"),
+			      (long long) delta);
+	      fixP->fx_done = 1;
+	    }
 	}
       break;
 
@@ -4919,7 +4943,14 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
 		      | ENCODE_UTYPE_IMM (RISCV_CONST_HIGH_PART (value)),
 		      buf);
 	  if (!riscv_opts.relax)
-	    fixP->fx_done = 1;
+	    {
+	      if (xlen > 32
+		  && !VALID_UTYPE_IMM (RISCV_CONST_HIGH_PART (value)))
+		as_bad_where (fixP->fx_file, fixP->fx_line,
+			      _("invalid pcrel_hi offset (%+lld)"),
+			      (long long) value);
+	      fixP->fx_done = 1;
+	    }
 	}
       relaxable = true;
       break;
@@ -4945,7 +4976,8 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
 	      bfd_putl32 (bfd_getl32 (buf) | ENCODE_STYPE_IMM (value), buf);
 	    else
 	      bfd_putl32 (bfd_getl32 (buf) | ENCODE_ITYPE_IMM (value), buf);
-	    /* Relaxations should never be enabled by `.option relax'.  */
+	    /* Relaxations should never be enabled by `.option relax'.
+	       The offset is checked by corresponding %pcrel_hi entry.  */
 	    if (!riscv_opts.relax)
 	      fixP->fx_done = 1;
 	  }
diff --git a/gas/testsuite/gas/riscv/no-relax-branch-offset-fail.d b/gas/testsuite/gas/riscv/no-relax-branch-offset-fail.d
new file mode 100644
index 00000000000..0e847714680
--- /dev/null
+++ b/gas/testsuite/gas/riscv/no-relax-branch-offset-fail.d
@@ -0,0 +1,2 @@
+#as: -march=rv32ic
+#error_output: no-relax-branch-offset-fail.l
diff --git a/gas/testsuite/gas/riscv/no-relax-branch-offset-fail.l b/gas/testsuite/gas/riscv/no-relax-branch-offset-fail.l
new file mode 100644
index 00000000000..8f6b5c95591
--- /dev/null
+++ b/gas/testsuite/gas/riscv/no-relax-branch-offset-fail.l
@@ -0,0 +1,8 @@
+.*Assembler messages:
+.*:4: Error: invalid J-type offset \(\+1048576\)
+.*:5: Error: invalid J-type offset \(-1048578\)
+.*:8: Error: invalid J-type offset \(\+1048576\)
+.*:10: Error: invalid J-type offset \(-1048578\)
+.*:14: Error: invalid J-type offset \(\+1048576\)
+.*:17: Error: invalid J-type offset \(-1048578\)
+.*:20: Error: invalid CJ-type offset \(\+1\)
diff --git a/gas/testsuite/gas/riscv/no-relax-branch-offset-fail.s b/gas/testsuite/gas/riscv/no-relax-branch-offset-fail.s
new file mode 100644
index 00000000000..98a0978e7fc
--- /dev/null
+++ b/gas/testsuite/gas/riscv/no-relax-branch-offset-fail.s
@@ -0,0 +1,20 @@
+	.option	norelax
+
+	# Relative to the current instruction.
+	j	.+0x0ffffe+2
+	j	.-0x100000-2
+
+	# Relative to local labels (make sure that all instructions except "c.j" occupy 4-bytes).
+	j	1f+0x0ffffe-4+2
+1:
+	j	2f-0x100000-4-2
+2:
+3:
+	lui	t0, 0x12345
+	j	3b+0x0ffffe+4+2
+4:
+	lui	t0, 0x2abcd
+	j	4b-0x100000+4-2
+
+	# Jump to odd address (violates instruction alignment).
+	c.j	.+1
diff --git a/gas/testsuite/gas/riscv/no-relax-branch-offset-ok.d b/gas/testsuite/gas/riscv/no-relax-branch-offset-ok.d
new file mode 100644
index 00000000000..6ef31e2df41
--- /dev/null
+++ b/gas/testsuite/gas/riscv/no-relax-branch-offset-ok.d
@@ -0,0 +1,17 @@
+#as: -march=rv32ic
+#objdump: -dr
+
+.*:     file format .*
+
+
+Disassembly of section \.text:
+
+0+ <\.text>:
+[ 	]+0:[ 	]+7ffff06f[ 	]+j[ 	][0-9a-f]+.*
+[ 	]+4:[ 	]+8000006f[ 	]+j[ 	][0-9a-f]+.*
+[ 	]+8:[ 	]+7ffff06f[ 	]+j[ 	][0-9a-f]+.*
+[ 	]+c:[ 	]+8000006f[ 	]+j[ 	][0-9a-f]+.*
+[ 	]+10:[ 	]+123452b7[ 	]+lui[ 	]t0,0x12345
+[ 	]+14:[ 	]+7ffff06f[ 	]+j[ 	][0-9a-f]+.*
+[ 	]+18:[ 	]+2abcd2b7[ 	]+lui[ 	]t0,0x2abcd
+[ 	]+1c:[ 	]+8000006f[ 	]+j[ 	][0-9a-f]+.*
diff --git a/gas/testsuite/gas/riscv/no-relax-branch-offset-ok.s b/gas/testsuite/gas/riscv/no-relax-branch-offset-ok.s
new file mode 100644
index 00000000000..4711c90c023
--- /dev/null
+++ b/gas/testsuite/gas/riscv/no-relax-branch-offset-ok.s
@@ -0,0 +1,17 @@
+	.option	norelax
+
+	# Relative to the current instruction.
+	j	.+0x0ffffe
+	j	.-0x100000
+
+	# Relative to local labels (make sure that all instructions occupy 4-bytes).
+	j	1f+0x0ffffe-4
+1:
+	j	2f-0x100000-4
+2:
+3:
+	lui	t0, 0x12345
+	j	3b+0x0ffffe+4
+4:
+	lui	t0, 0x2abcd
+	j	4b-0x100000+4
diff --git a/gas/testsuite/gas/riscv/no-relax-pcrel-offset-fail-64.d b/gas/testsuite/gas/riscv/no-relax-pcrel-offset-fail-64.d
new file mode 100644
index 00000000000..79d59deb095
--- /dev/null
+++ b/gas/testsuite/gas/riscv/no-relax-pcrel-offset-fail-64.d
@@ -0,0 +1,2 @@
+#as: -march=rv64i
+#error_output: no-relax-pcrel-offset-fail-64.l
diff --git a/gas/testsuite/gas/riscv/no-relax-pcrel-offset-fail-64.l b/gas/testsuite/gas/riscv/no-relax-pcrel-offset-fail-64.l
new file mode 100644
index 00000000000..bcc09252b75
--- /dev/null
+++ b/gas/testsuite/gas/riscv/no-relax-pcrel-offset-fail-64.l
@@ -0,0 +1,7 @@
+.*Assembler messages:
+.*:8: Error: invalid pcrel_hi offset \(\+2147481600\)
+.*:11: Error: invalid pcrel_hi offset \(-2147485697\)
+.*:17: Error: invalid pcrel_hi offset \(\+2147481600\)
+.*:21: Error: invalid pcrel_hi offset \(-2147485697\)
+.*:26: Error: invalid pcrel_hi offset \(\+2147481600\)
+.*:30: Error: invalid pcrel_hi offset \(-2147485697\)
diff --git a/gas/testsuite/gas/riscv/no-relax-pcrel-offset-fail-64.s b/gas/testsuite/gas/riscv/no-relax-pcrel-offset-fail-64.s
new file mode 100644
index 00000000000..d01ffe965ed
--- /dev/null
+++ b/gas/testsuite/gas/riscv/no-relax-pcrel-offset-fail-64.s
@@ -0,0 +1,31 @@
+	.option	norelax
+
+	## Fail on RV64, wrap without errors on RV32.
+
+	# Relative to the current instruction.
+
+1:
+	auipc	a0, %pcrel_hi(.+0x7ffff7ff+1)
+	addi	a0, a0, %pcrel_lo(1b)
+2:
+	auipc	a0, %pcrel_hi(.-0x80000800-1)
+	addi	a0, a0, %pcrel_lo(2b)
+
+	# Relative to local labels (all instructions occupy 4-bytes).
+
+3:
+	auipc	a0, %pcrel_hi(4f+0x7ffff7ff-4+1)
+4:
+	addi	a0, a0, %pcrel_lo(3b)
+5:
+	auipc	a0, %pcrel_hi(6f-0x80000800-4-1)
+6:
+	addi	a0, a0, %pcrel_lo(5b)
+
+7:
+	auipc	a0, %pcrel_hi(6b+0x7ffff7ff+4+1)
+8:
+	addi	a0, a0, %pcrel_lo(7b)
+9:
+	auipc	a0, %pcrel_hi(8b-0x80000800+4-1)
+	addi	a0, a0, %pcrel_lo(9b)
diff --git a/gas/testsuite/gas/riscv/no-relax-pcrel-offset-fail-not-32.d b/gas/testsuite/gas/riscv/no-relax-pcrel-offset-fail-not-32.d
new file mode 100644
index 00000000000..0d7cb7c650d
--- /dev/null
+++ b/gas/testsuite/gas/riscv/no-relax-pcrel-offset-fail-not-32.d
@@ -0,0 +1,22 @@
+#as: -march=rv32i
+#source: no-relax-pcrel-offset-fail-64.s
+#objdump: -dr
+
+.*:     file format .*
+
+
+Disassembly of section \.text:
+
+0+ <\.text>:
+[ 	]+0:[ 	]+80000517[ 	]+auipc[ 	]a0,0x80000
+[ 	]+4:[ 	]+80050513[ 	]+addi[ 	]a0,a0,-2048([^0-9].*)?
+[ 	]+8:[ 	]+7ffff517[ 	]+auipc[ 	]a0,0x7ffff
+[ 	]+c:[ 	]+7ff50513[ 	]+addi[ 	]a0,a0,2047([^0-9].*)?
+[ 	]+10:[ 	]+80000517[ 	]+auipc[ 	]a0,0x80000
+[ 	]+14:[ 	]+80050513[ 	]+addi[ 	]a0,a0,-2048([^0-9].*)?
+[ 	]+18:[ 	]+7ffff517[ 	]+auipc[ 	]a0,0x7ffff
+[ 	]+1c:[ 	]+7ff50513[ 	]+addi[ 	]a0,a0,2047([^0-9].*)?
+[ 	]+20:[ 	]+80000517[ 	]+auipc[ 	]a0,0x80000
+[ 	]+24:[ 	]+80050513[ 	]+addi[ 	]a0,a0,-2048([^0-9].*)?
+[ 	]+28:[ 	]+7ffff517[ 	]+auipc[ 	]a0,0x7ffff
+[ 	]+2c:[ 	]+7ff50513[ 	]+addi[ 	]a0,a0,2047([^0-9].*)?
diff --git a/gas/testsuite/gas/riscv/no-relax-pcrel-offset-ok.d b/gas/testsuite/gas/riscv/no-relax-pcrel-offset-ok.d
new file mode 100644
index 00000000000..e585032f862
--- /dev/null
+++ b/gas/testsuite/gas/riscv/no-relax-pcrel-offset-ok.d
@@ -0,0 +1,21 @@
+#as: -march=rv64i
+#objdump: -dr
+
+.*:     file format .*
+
+
+Disassembly of section \.text:
+
+0+ <\.text>:
+[ 	]+0:[ 	]+7ffff517[ 	]+auipc[ 	]a0,0x7ffff
+[ 	]+4:[ 	]+7ff50513[ 	]+addi[ 	]a0,a0,2047([^0-9].*)?
+[ 	]+8:[ 	]+80000517[ 	]+auipc[ 	]a0,0x80000
+[ 	]+c:[ 	]+80050513[ 	]+addi[ 	]a0,a0,-2048([^0-9].*)?
+[ 	]+10:[ 	]+7ffff517[ 	]+auipc[ 	]a0,0x7ffff
+[ 	]+14:[ 	]+7ff50513[ 	]+addi[ 	]a0,a0,2047([^0-9].*)?
+[ 	]+18:[ 	]+80000517[ 	]+auipc[ 	]a0,0x80000
+[ 	]+1c:[ 	]+80050513[ 	]+addi[ 	]a0,a0,-2048([^0-9].*)?
+[ 	]+20:[ 	]+7ffff517[ 	]+auipc[ 	]a0,0x7ffff
+[ 	]+24:[ 	]+7ff50513[ 	]+addi[ 	]a0,a0,2047([^0-9].*)?
+[ 	]+28:[ 	]+80000517[ 	]+auipc[ 	]a0,0x80000
+[ 	]+2c:[ 	]+80050513[ 	]+addi[ 	]a0,a0,-2048([^0-9].*)?
diff --git a/gas/testsuite/gas/riscv/no-relax-pcrel-offset-ok.s b/gas/testsuite/gas/riscv/no-relax-pcrel-offset-ok.s
new file mode 100644
index 00000000000..ededcca02b1
--- /dev/null
+++ b/gas/testsuite/gas/riscv/no-relax-pcrel-offset-ok.s
@@ -0,0 +1,29 @@
+	.option	norelax
+
+	# Relative to the current instruction.
+
+1:
+	auipc	a0, %pcrel_hi(.+0x7ffff7ff)
+	addi	a0, a0, %pcrel_lo(1b)
+2:
+	auipc	a0, %pcrel_hi(.-0x80000800)
+	addi	a0, a0, %pcrel_lo(2b)
+
+	# Relative to local labels (all instructions occupy 4-bytes).
+
+3:
+	auipc	a0, %pcrel_hi(4f+0x7ffff7ff-4)
+4:
+	addi	a0, a0, %pcrel_lo(3b)
+5:
+	auipc	a0, %pcrel_hi(6f-0x80000800-4)
+6:
+	addi	a0, a0, %pcrel_lo(5b)
+
+7:
+	auipc	a0, %pcrel_hi(6b+0x7ffff7ff+4)
+8:
+	addi	a0, a0, %pcrel_lo(7b)
+9:
+	auipc	a0, %pcrel_hi(8b-0x80000800+4)
+	addi	a0, a0, %pcrel_lo(9b)


More information about the Binutils-cvs mailing list