[6/7] aarch64: add PAuth_LR instructions with imm16 operand

Message ID 20260827151014.9719-7-muhammad.kamran@arm.com
State New
Headers
Series aarch64: Add FEAT_PAuth_LR asm/disasm support |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm warning Skipped because of CI-tag:skip
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 warning Skipped because of CI-tag:skip

Commit Message

Muhammad Kamran Aug. 27, 2026, 3:10 p.m. UTC
  From: Matthieu Longo <matthieu.longo@arm.com>

Add the immediate PC-relative forms of the FEAT_PAuth_LR
instructions:

	* AUTIA<k>SPPC
	* RETA<k>SPPC

These instructions use an unsigned 16-bit offset field, scaled by 4,
to identify a PC-relative instruction address.  Add ADDR_UPCREL16 for
this operand form and handle it as an assembler-internal fixup, since
the offset must be fully resolved by gas and no object-file relocation
is emitted.

Also update operand printing so disassembly computes the target address
using the unsigned backwards PC-relative offset semantics, and add gas
tests for valid encodings and diagnostics.

Co-authored-by: Muhammad Kamran <muhammad.kamran@arm.com>

CI-tag:skip
---
 gas/config/tc-aarch64.c                  | 41 ++++++++++++++++++++++++
 gas/testsuite/gas/aarch64/pauth_lr-bad.l |  8 +++++
 gas/testsuite/gas/aarch64/pauth_lr-bad.s | 15 +++++++++
 gas/testsuite/gas/aarch64/pauth_lr.d     | 13 ++++++++
 gas/testsuite/gas/aarch64/pauth_lr.s     | 17 ++++++++++
 include/opcode/aarch64.h                 |  1 +
 opcodes/aarch64-opc.c                    |  7 +++-
 opcodes/aarch64-tbl.h                    | 12 +++++++
 8 files changed, 113 insertions(+), 1 deletion(-)
  

Patch

diff --git a/gas/config/tc-aarch64.c b/gas/config/tc-aarch64.c
index f5d57b1d094..d7963b66817 100644
--- a/gas/config/tc-aarch64.c
+++ b/gas/config/tc-aarch64.c
@@ -5241,6 +5241,12 @@  encode_addsub_imm_shift_amount (uint32_t cnt)
   return cnt << 22;
 }
 
+/* Encode the imm16 field of RETA<k>SPPC instruction.  */
+static inline uint32_t
+encode_pauthauti_imm16 (uint32_t imm)
+{
+  return imm << 5;
+}
 
 /* encode the imm field of Adr instruction */
 static inline uint32_t
@@ -7626,6 +7632,7 @@  parse_operands (char *str, const aarch64_opcode *opcode)
 
 	case AARCH64_OPND_ADDR_PCREL9:
 	case AARCH64_OPND_ADDR_PCREL14:
+	case AARCH64_OPND_ADDR_UPCREL16:
 	case AARCH64_OPND_ADDR_PCREL19:
 	case AARCH64_OPND_ADDR_PCREL21:
 	case AARCH64_OPND_ADDR_PCREL26:
@@ -7680,6 +7687,11 @@  parse_operands (char *str, const aarch64_opcode *opcode)
 		      (opcode->op == OP_BL) ? BFD_RELOC_AARCH64_CALL26
 			 : BFD_RELOC_AARCH64_JUMP26;
 		    break;
+		  case pauth_lr:
+		    /* e.g. AUTI<k>SPPC or RETA<k>SPPC  */
+		    gas_assert (operands[i] == AARCH64_OPND_ADDR_UPCREL16);
+		    aarch64_set_gas_internal_fixup (&inst.reloc, info, 0);
+		    break;
 		  case loadlit:
 		    gas_assert (operands[i] == AARCH64_OPND_ADDR_PCREL19);
 		    inst.reloc.type = BFD_RELOC_AARCH64_LD_LO19_PCREL;
@@ -9725,6 +9737,7 @@  fix_insn (fixS *fixP, uint32_t flags, offsetT value)
 {
   int idx;
   uint32_t insn;
+  offsetT offset;
   char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
   enum aarch64_opnd opnd = fixP->tc_fix_data.opnd;
   aarch64_inst *new_inst = fixP->tc_fix_data.inst;
@@ -9877,6 +9890,34 @@  fix_insn (fixS *fixP, uint32_t flags, offsetT value)
 		    _("immediate offset out of range"));
       break;
 
+    case AARCH64_OPND_ADDR_UPCREL16:
+      /* immediate offset from a label.  */
+      if (value > 0)
+	{
+	  as_bad_where (fixP->fx_file, fixP->fx_line,
+			 _("negative pc-relative address offset not allowed"\
+			   " in this context: %ld"), -value);
+	  break;
+	}
+      offset = -value;
+      if (unsigned_overflow (offset, 18))
+	{
+	  as_bad_where (fixP->fx_file, fixP->fx_line,
+			  _("pc-relative address offset out of range: %#lx"),
+			  offset);
+	  break;
+	}
+      if (offset & 3)
+	{
+	  as_bad_where (fixP->fx_file, fixP->fx_line,
+			 _("pc-relative address offset not word aligned"));
+	  break;
+	}
+      insn = get_aarch64_insn (buf);
+      insn |= encode_pauthauti_imm16 (offset >> 2);
+      put_aarch64_insn (buf, insn);
+      break;
+
     default:
       gas_assert (0);
       as_fatal (_("unhandled operand code %d"), opnd);
diff --git a/gas/testsuite/gas/aarch64/pauth_lr-bad.l b/gas/testsuite/gas/aarch64/pauth_lr-bad.l
index befd2c566d1..3afad8e4327 100644
--- a/gas/testsuite/gas/aarch64/pauth_lr-bad.l
+++ b/gas/testsuite/gas/aarch64/pauth_lr-bad.l
@@ -8,3 +8,11 @@ 
 .*: Info:    	retaasppcr x0
 .*: Error: unexpected register type at operand 1 -- `retaasppcr sp'
 .*: Error: unexpected register type at operand 1 -- `retaasppcr xzr'
+.*: Error: immediate value must be a multiple of 4 at operand 1 -- `retaasppc 0xff'
+.*: Error: immediate value must be a multiple of 4 at operand 1 -- `retaasppc 0x3fffd'
+.*: Error: immediate value out of range 0 to 65535 at operand 1 -- `retaasppc 0x4fffc'
+.*: Error: immediate value out of range 0 to 65535 at operand 1 -- `retaasppc -0x4'
+.*: Error: negative pc-relative address offset not allowed in this context: -12
+.*: Error: undefined symbol external_symbol used as an immediate value
+.*: Error: pc-relative address offset out of range: 0x40000
+.*: Error: negative pc-relative address offset not allowed in this context: -12
diff --git a/gas/testsuite/gas/aarch64/pauth_lr-bad.s b/gas/testsuite/gas/aarch64/pauth_lr-bad.s
index 4b4539a9354..25661e4a1d2 100644
--- a/gas/testsuite/gas/aarch64/pauth_lr-bad.s
+++ b/gas/testsuite/gas/aarch64/pauth_lr-bad.s
@@ -9,3 +9,18 @@  lr_signing:
 	retaasppcr w0 // 32-bit registers not allowed
 	retaasppcr sp // SP not allowed
 	retaasppcr xzr // zero register not allowed
+
+	// Invalid values
+	retaasppc 0xff // not multiple of 4
+	retaasppc 0x3fffd // max value overflowed but might not be detected due to bit shift
+	retaasppc 0x4fffc // aligned overflow: not possible to represent the value on 16 bits
+
+	retaasppc -0x4  // unsupported negative offset
+	retaasppc .+0xc // future PC target is invalid: encoded offset would be negative
+	retaasppc external_symbol
+
+	.balign 0x40000 // NOP filling
+
+	retaasppc lr_signing // the distance between the label and the reta<k>sppc instruction
+			     // is above the limit that can be encoded (=0x3fffc).
+	retaasppc .+0xc // future PC target is invalid: encoded offset would be negative
diff --git a/gas/testsuite/gas/aarch64/pauth_lr.d b/gas/testsuite/gas/aarch64/pauth_lr.d
index 93ccfd7ee26..cd224169ea4 100644
--- a/gas/testsuite/gas/aarch64/pauth_lr.d
+++ b/gas/testsuite/gas/aarch64/pauth_lr.d
@@ -23,3 +23,16 @@  Disassembly of section \.text:
 .*:	d65f0be0 	retaasppcr	x0
 .*:	d65f0bfe 	retaasppcr	x30
 .*:	d65f0fe0 	retabsppcr	x0
+[^\s]+\s<lr_signing>:
+      40:	f380001f 	autiasppc	40 <lr_signing>
+      44:	f3a0003f 	autibsppc	40 <lr_signing>
+      48:	5500005f 	retaasppc	40 <lr_signing>
+      4c:	5520007f 	retabsppc	40 <lr_signing>
+      50:	5500001f 	retaasppc	50 <lr_signing\+0x10>
+      54:	5500001f 	retaasppc	54 <lr_signing\+0x14>
+      58:	5500003f 	retaasppc	54 <lr_signing\+0x14>
+      5c:	5500011f 	retaasppc	3c <lr_signing-0x4>
+      60:	550002df 	retaasppc	8 <lr_signing-0x38>
+#...
+   40000:	55091a7f 	retaasppc	2dcb4 <lr_signing\+0x2dc74>
+   40004:	551fffff 	retaasppc	8 <lr_signing-0x38>
diff --git a/gas/testsuite/gas/aarch64/pauth_lr.s b/gas/testsuite/gas/aarch64/pauth_lr.s
index 49585e50e6a..495446b52fb 100644
--- a/gas/testsuite/gas/aarch64/pauth_lr.s
+++ b/gas/testsuite/gas/aarch64/pauth_lr.s
@@ -29,3 +29,20 @@ 
 	retaasppcr x0
 	retaasppcr x30
 	retabsppcr x0
+
+lr_signing:
+	autiasppc lr_signing
+	autibsppc lr_signing
+	retaasppc lr_signing
+	retabsppc lr_signing
+
+	retaasppc 0x0 // min: reference to itself
+	retaasppc .+0x0 // min: current PC + 0
+	retaasppc 0x4 // first valid value after min: point to previous instruction
+	retaasppc 0x20
+	retaasppc .-0x58 // point to pacm
+
+	.balign 0x40000 // NOP filling
+
+	retaasppc 0x1234c // a random value in the valid range
+	retaasppc 0x3fffc // max
diff --git a/include/opcode/aarch64.h b/include/opcode/aarch64.h
index 0248d86f3e4..f61af00aae7 100644
--- a/include/opcode/aarch64.h
+++ b/include/opcode/aarch64.h
@@ -732,6 +732,7 @@  enum aarch64_opnd
   AARCH64_OPND_ADDR_ADRP,	/* Memory address for ADRP */
   AARCH64_OPND_ADDR_PCREL9,	/* 9-bit PC-relative address for e.g. CB<cc>.  */
   AARCH64_OPND_ADDR_PCREL14,	/* 14-bit PC-relative address for e.g. TBZ.  */
+  AARCH64_OPND_ADDR_UPCREL16,	/* unsigned 16-bit PC-relative offset (imm16:'00') for e.g. RETA<k>... */
   AARCH64_OPND_ADDR_PCREL19,	/* 19-bit PC-relative address for e.g. LDR.  */
   AARCH64_OPND_ADDR_PCREL21,	/* 21-bit PC-relative address for e.g. ADR.  */
   AARCH64_OPND_ADDR_PCREL26,	/* 26-bit PC-relative address for e.g. BL.  */
diff --git a/opcodes/aarch64-opc.c b/opcodes/aarch64-opc.c
index 41f38053275..8c6bfb98e6b 100644
--- a/opcodes/aarch64-opc.c
+++ b/opcodes/aarch64-opc.c
@@ -2132,6 +2132,7 @@  operand_general_constraint_met_p (const aarch64_opnd_info *opnds, int idx,
 
 	case AARCH64_OPND_ADDR_PCREL9:
 	case AARCH64_OPND_ADDR_PCREL14:
+	case AARCH64_OPND_ADDR_UPCREL16:
 	case AARCH64_OPND_ADDR_PCREL19:
 	case AARCH64_OPND_ADDR_PCREL21:
 	case AARCH64_OPND_ADDR_PCREL26:
@@ -4532,10 +4533,14 @@  aarch64_print_operand (char *buf, size_t size, bfd_vma pc,
 
     case AARCH64_OPND_ADDR_PCREL9:
     case AARCH64_OPND_ADDR_PCREL14:
+    case AARCH64_OPND_ADDR_UPCREL16:
     case AARCH64_OPND_ADDR_PCREL19:
     case AARCH64_OPND_ADDR_PCREL21:
     case AARCH64_OPND_ADDR_PCREL26:
-      addr = pc + AARCH64_PCREL_OFFSET + opnd->imm.value;
+      if (opnd->type == AARCH64_OPND_ADDR_UPCREL16)
+	addr = pc + AARCH64_PCREL_OFFSET - opnd->imm.value;
+      else
+	addr = pc + AARCH64_PCREL_OFFSET + opnd->imm.value;
       if (pcrel_p)
 	*pcrel_p = 1;
       if (address)
diff --git a/opcodes/aarch64-tbl.h b/opcodes/aarch64-tbl.h
index 78c4c4e60f7..d6e709f5817 100644
--- a/opcodes/aarch64-tbl.h
+++ b/opcodes/aarch64-tbl.h
@@ -1103,6 +1103,12 @@ 
   QLF4(V_16B, V_16B, V_16B, imm_0_15),	\
 }
 
+/* e.g. AUTI<k>SPPC <label_imm16:00>  */
+#define QL_PAUTH_IMM16		\
+{				\
+  QLF1(NIL),		\
+}
+
 /* e.g. AUTI<k>SPPCR Xn  */
 #define QL_PAUTH_REG		\
 {				\
@@ -4436,6 +4442,8 @@  const struct aarch64_opcode aarch64_opcode_table[] =
   PAUTH_INSN ("eretab", 0xd69f0fff, 0xffffffff, branch_reg, OP0 (), QL_0, F_BRANCH_RET),
   PAUTH_LR_INSN ("retaasppcr", 0xd65f0be0, 0xffffffe0, pauth_lr, OP1 (Rd), QL_PAUTH_REG, F_BRANCH_RET),
   PAUTH_LR_INSN ("retabsppcr", 0xd65f0fe0, 0xffffffe0, pauth_lr, OP1 (Rd), QL_PAUTH_REG, F_BRANCH_RET),
+  PAUTH_LR_INSN ("retaasppc", 0x5500001f, 0xffe0001f, pauth_lr, OP1 (ADDR_UPCREL16), QL_PAUTH_IMM16, F_BRANCH_RET),
+  PAUTH_LR_INSN ("retabsppc", 0x5520001f, 0xffe0001f, pauth_lr, OP1 (ADDR_UPCREL16), QL_PAUTH_IMM16, F_BRANCH_RET),
   /* Compare & branch (immediate).  */
   CORE_INSN ("cbz", 0x34000000, 0x7f000000, compbranch, 0, OP2 (Rt, ADDR_PCREL19), QL_R_PCREL, F_SF),
   CORE_INSN ("cbnz", 0x35000000, 0x7f000000, compbranch, 0, OP2 (Rt, ADDR_PCREL19), QL_R_PCREL, F_SF),
@@ -4543,6 +4551,8 @@  const struct aarch64_opcode aarch64_opcode_table[] =
   PAUTH_INSN ("autdb", 0xdac11c00, 0xfffffc00, dp_1src, OP2 (Rd, Rn_SP), QL_I2SAMEX, 0),
   PAUTH_LR_INSN ("autiasppcr", 0xdac1901e, 0xfffffc1f, pauth_lr, OP1 (Rn), QL_PAUTH_REG, F_SUBCLASS_OTHER),
   PAUTH_LR_INSN ("autibsppcr", 0xdac1941e, 0xfffffc1f, pauth_lr, OP1 (Rn), QL_PAUTH_REG, F_SUBCLASS_OTHER),
+  PAUTH_LR_INSN ("autiasppc", 0xf380001f, 0xffe0001f, pauth_lr, OP1 (ADDR_UPCREL16), QL_PAUTH_IMM16, F_SUBCLASS_OTHER),
+  PAUTH_LR_INSN ("autibsppc", 0xf3a0001f, 0xffe0001f, pauth_lr, OP1 (ADDR_UPCREL16), QL_PAUTH_IMM16, F_SUBCLASS_OTHER),
   PAUTH_LR_INSN ("autia171615", 0xdac1bbfe, 0xffffffff, pauth_lr, OP0 (), {}, F_SUBCLASS_OTHER),
   PAUTH_LR_INSN ("autib171615", 0xdac1bffe, 0xffffffff, pauth_lr, OP0 (), {}, F_SUBCLASS_OTHER),
   PAUTH_INSN ("paciza", 0xdac123e0, 0xffffffe0, dp_1src, OP1 (Rd), QL_I1X, 0),
@@ -8184,6 +8194,8 @@  const struct aarch64_opcode aarch64_opcode_table[] =
       F(FLD(5, 9)), "9-bit PC-relative address")			\
     Y(ADDRESS, imm, "ADDR_PCREL14", OPD_F_SEXT | OPD_F_SHIFT_BY_2,	\
       F(FLD(5, 14)), "14-bit PC-relative address")			\
+    Y(ADDRESS, imm, "ADDR_UPCREL16", OPD_F_SHIFT_BY_2 | OPD_F_UNSIGNED,	\
+      F(FLD(5, 16)), "unsigned 16-bit PC-relative offset (imm16:'00')")	\
     Y(ADDRESS, imm, "ADDR_PCREL19", OPD_F_SEXT | OPD_F_SHIFT_BY_2,	\
       F(FLD(5, 19)), "19-bit PC-relative address")			\
     Y(ADDRESS, imm, "ADDR_PCREL21", OPD_F_SEXT, F(FLD(5, 19), FLD(29, 2)),	\