[PATCH v3 3/3] gas: sframe: Represent .cfi_undefined RA

Jens Remus jremus@linux.ibm.com
Wed Jul 23 13:17:58 GMT 2025


In DWARF CFI an "undefined" register rule for the return address (RA)
register indicates that there is no return address and the stack trace
is complete.

Introduce a SFrame FDE info word flag "RA undefined".  Represent
DW_CFA_undefined as SFrame FDE without any FREs and FDE info word flag
"RA undefined" set, so that a stack tracer implementation can use this
as indication that an outermost frame has been reached and the stack
trace is complete.

This representation is backward compatible, as existing stack tracers
should already deal with the case, that an SFrame FDE has a FRE count of
zero and stop the trace.  Furthermore the new FDE info word flag
"RA undefined" was previously unused and therefore not set.  Stack
tracers can therefore rely on FDE without FREs and RA undefined set
as indication for an outermost frame.

include/
	* sframe.h (FDE info word): New RA undefined flag.
	(SFRAME_V2_FUNC_RA_UNDEF, SFRAME_V2_FUNC_INFO_UPDATE_RA_UNDEF):
	New macros to get and set the RA undefined flag.

gas/
	* gen-sframe.h (struct sframe_func_entry): Add ra_undefined
	flag.
	(struct sframe_xlate_ctx): Likewise.
	(enum sframe_xlate_err): Add SFRAME_XLATE_RA_UNDEFINED.
	(struct sframe_version_ops): Add parameter for ra_undefined to
	set_func_info.
	* gen-sframe.c (sframe_xlate_ctx_init): Initialize ra_undefined
	flag to not set.
	(sframe_xlate_ctx_finalize): Copy ra_undefined flag from
	translation context to SFrame FDE.
	(sframe_v1_set_func_info): Rename to ...
	(sframe_v2_set_func_info): ... this, require new parameter
	ra_undefined, and use its value to set SFrame FDE info word flag
	"RA undefined".
	(sframe_set_version): Wire sframe_v2_set_func_info as versioned
	operation set_func_info
	(sframe_set_func_info): Require new parameter ra_undefined and
	pass it to versioned operation set_func_info.
	(output_sframe_funcdesc): Pass RA undefined flag to
	sframe_set_func_info.
	(sframe_xlate_do_cfi_undefined): Represent
	DW_CFA_undefined RA as SFrame FDE without and SFrame FREs and
	RA undefined flag set, and return SFRAME_XLATE_RA_UNDEFINED to
	stop any further processing of DWARF CFI.
	(sframe_do_fde): Add comment that RA undefined stops processing
	of further DWARF FREs.
	(create_sframe_all): Handle SFRAME_XLATE_RA_UNDEFINED with
	zero FREs as good case.
	(output_sframe_funcdesc): For FDEs without any FREs set the
	start FRE offset to zero.

libsframe/
	* doc/sframe-spec.texi (Changes from Version 1 to Version 2):
	Mention that a SFrame FDE without any FREs and RA undefined
	flag indicates an outermost frame with an "undefined" RA
	register rule.
	(sfde_func_num_fres): Document that a FRE count of zero in
	combination with the RA undefined flag indicates an outermost
	frame with an "undefined" RA register rule.
	(The SFrame FDE Info Word): Document RA undefined flag.
	* sframe-dump.c (dump_sframe_func_with_fres): Dump RA undefined
	flag.

gas/testsuite/
	* gas/cfi-sframe/cfi-sframe.exp: Run tests for .cfi_undefined RA
	on AArch64, s390x, and x86-64.
	* gas/cfi-sframe/cfi-sframe-aarch64-ra-undefined-1.d: Add test
	for .cfi_undefined RA on AArch64.
	* gas/cfi-sframe/cfi-sframe-aarch64-ra-undefined-1.s: Likewise.
	* as/cfi-sframe/cfi-sframe-s390x-ra-undefined-1.d: Add test
	for .cfi_undefined RA on s390x.
	* gas/cfi-sframe/cfi-sframe-s390x-ra-undefined-1.s: Likewise.
	* gas/cfi-sframe/cfi-sframe-x86_64-ra-undefined-1.d: Add test
	for .cfi_undefined RA on x86-64.
	* gas/cfi-sframe/cfi-sframe-x86_64-ra-undefined-1.s: Likewise.

Signed-off-by: Jens Remus <jremus@linux.ibm.com>
---

Notes (jremus):
    Changes in V3:
    - Introduce a FDE info word flag ra_undefined.  Represent RA undefined
      as FDE without any FREs and the ra_undefined flag set. (Indu)
    - Set sfde_func_start_fre_off to zero, if FDE without any FREs.
    - In x86-64 tests use spaces to separate mnemonic from operands. (Indu)
    - Reword commit subject, commit message, and GNU ChangeLog.
    
    An alternative to introducing a FDE info word flag ra_undefined would
    be to repurpose the FRE offset in the FDE to carry indications, if
    the FRE count is zero.  While that would be easy in the assembler to
    generate and easy for a stack tracer to process it would require
    a few modifications in libsframe to preserve those special FRE offset
    values.
    
    Wouldn't it make sense to remove the half baked versioned operations
    (see struct sframe_version_ops) with a preparatory cleanup patch?  I
    don't see how the current implementation would work in the future.
    The operations would otherwise probably need to be split up into
    smaller ones: fde_info_init(fde_type, fre_type), fde_info_set_pauth_key,
    fde_info_set_ra_undefined, ... .
    
    Changes in V2:
    - Use sframe_xlate_ctx_init to re-initialize translation context.
    - Reword comment on sframe_xlate_do_cfi_escape.
    - Reword comment in sframe_do_fde for SFRAME_XLATE_RA_UNDEFINED.
    - Reword comment in create_sframe_all for SFRAME_XLATE_RA_UNDEFINED.
    
    Representing .cfi_undefined RA as SFrame FDE without any FREs is a
    simplification compared to DWARF CFI, as the latter can represent
    RA undefined for a range of PCs within a DWARF FDE.  An alternative
    would have been to represent .cfi_undefined RA as SFrame FRE without
    any offsets, which would have provided the same versatility.  Note
    that the current choice to represent this at a SFrame FDE level
    does not prevent to represent it at a SFrame FRE level in addition
    in the future.

 gas/gen-sframe.c                              | 88 +++++++++++++------
 gas/gen-sframe.h                              | 11 ++-
 gas/sframe-opt.c                              |  3 +
 .../cfi-sframe-aarch64-ra-undefined-1.d       | 17 ++++
 .../cfi-sframe-aarch64-ra-undefined-1.s       | 13 +++
 .../cfi-sframe-s390x-ra-undefined-1.d         | 18 ++++
 .../cfi-sframe-s390x-ra-undefined-1.s         | 11 +++
 .../cfi-sframe-x86_64-ra-undefined-1.d        | 19 ++++
 .../cfi-sframe-x86_64-ra-undefined-1.s        | 11 +++
 gas/testsuite/gas/cfi-sframe/cfi-sframe.exp   |  3 +
 include/sframe.h                              | 17 ++--
 libsframe/doc/sframe-spec.texi                | 17 +++-
 libsframe/sframe-dump.c                       |  3 +
 13 files changed, 198 insertions(+), 33 deletions(-)
 create mode 100644 gas/testsuite/gas/cfi-sframe/cfi-sframe-aarch64-ra-undefined-1.d
 create mode 100644 gas/testsuite/gas/cfi-sframe/cfi-sframe-aarch64-ra-undefined-1.s
 create mode 100644 gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-ra-undefined-1.d
 create mode 100644 gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-ra-undefined-1.s
 create mode 100644 gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-ra-undefined-1.d
 create mode 100644 gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-ra-undefined-1.s

diff --git a/gas/gen-sframe.c b/gas/gen-sframe.c
index d082b974be3f..8476063e7a1f 100644
--- a/gas/gen-sframe.c
+++ b/gas/gen-sframe.c
@@ -272,14 +272,16 @@ sframe_v1_set_fre_info (unsigned int base_reg, unsigned int num_offsets,
   return fre_info;
 }
 
-/* SFrame (SFRAME_VERSION_1) set function info.  */
+/* SFrame (SFRAME_VERSION_2) set function info.  */
+
 static unsigned char
-sframe_v1_set_func_info (unsigned int fde_type, unsigned int fre_type,
-			 unsigned int pauth_key)
+sframe_v2_set_func_info (unsigned int fde_type, unsigned int fre_type,
+			 unsigned int pauth_key, bool ra_undefined)
 {
   unsigned char func_info;
   func_info = SFRAME_V1_FUNC_INFO (fde_type, fre_type);
   func_info = SFRAME_V1_FUNC_INFO_UPDATE_PAUTH_KEY (pauth_key, func_info);
+  func_info = SFRAME_V2_FUNC_INFO_UPDATE_RA_UNDEF (ra_undefined, func_info);
   return func_info;
 }
 
@@ -290,12 +292,12 @@ sframe_set_version (uint32_t sframe_version ATTRIBUTE_UNUSED)
 {
   sframe_ver_ops.format_version = SFRAME_VERSION_2;
 
+  sframe_ver_ops.set_func_info = sframe_v2_set_func_info;
+
   /* These operations remain the same for SFRAME_VERSION_2 as fre_info and
      func_info have not changed from SFRAME_VERSION_1.  */
 
   sframe_ver_ops.set_fre_info = sframe_v1_set_fre_info;
-
-  sframe_ver_ops.set_func_info = sframe_v1_set_func_info;
 }
 
 /* SFrame set FRE info.  */
@@ -312,9 +314,10 @@ sframe_set_fre_info (unsigned int base_reg, unsigned int num_offsets,
 
 static unsigned char
 sframe_set_func_info (unsigned int fde_type, unsigned int fre_type,
-		      unsigned int pauth_key)
+		      unsigned int pauth_key, bool ra_undefined)
 {
-  return sframe_ver_ops.set_func_info (fde_type, fre_type, pauth_key);
+  return sframe_ver_ops.set_func_info (fde_type, fre_type, pauth_key,
+				       ra_undefined);
 }
 
 /* Get the number of SFrame FDEs for the current file.  */
@@ -651,12 +654,18 @@ output_sframe_funcdesc (symbolS *start_of_fre_section,
 				  sfde_func_size));
 
   /* Offset to the first frame row entry.  */
-  exp.X_op = O_subtract;
-  exp.X_add_symbol = fre_symbol; /* Minuend.  */
-  exp.X_op_symbol = start_of_fre_section; /* Subtrahend.  */
-  exp.X_add_number = 0;
-  emit_expr (&exp, sizeof_member (sframe_func_desc_entry,
-				  sfde_func_start_fre_off));
+  if (sframe_fde->num_fres == 0)
+    /* SFrame FDEs without any FREs have a FRE offset of zero.  */
+    out_four (0);
+  else
+    {
+      exp.X_op = O_subtract;
+      exp.X_add_symbol = fre_symbol; /* Minuend.  */
+      exp.X_op_symbol = start_of_fre_section; /* Subtrahend.  */
+      exp.X_add_number = 0;
+      emit_expr (&exp, sizeof_member (sframe_func_desc_entry,
+				      sfde_func_start_fre_off));
+    }
 
   /* Number of FREs.  */
   out_four (sframe_fde->num_fres);
@@ -667,7 +676,8 @@ output_sframe_funcdesc (symbolS *start_of_fre_section,
 	       ? SFRAME_AARCH64_PAUTH_KEY_B : SFRAME_AARCH64_PAUTH_KEY_A);
   func_info = sframe_set_func_info (SFRAME_FDE_TYPE_PCINC,
 				    SFRAME_FRE_TYPE_ADDR4,
-				    pauth_key);
+				    pauth_key,
+				    sframe_fde->ra_undefined);
 #if SFRAME_FRE_TYPE_SELECTION_OPT
   expressionS cexp;
   create_func_info_exp (&cexp, dw_fde_end_addrS, dw_fde_start_addrS,
@@ -869,6 +879,7 @@ sframe_xlate_ctx_init (struct sframe_xlate_ctx *xlate_ctx)
   xlate_ctx->cur_fre = NULL;
   xlate_ctx->remember_fre = NULL;
   xlate_ctx->num_xlate_fres = 0;
+  xlate_ctx->ra_undefined = false;
 }
 
 /* Cleanup the given SFrame translation context.  */
@@ -890,6 +901,7 @@ sframe_xlate_ctx_finalize (struct sframe_xlate_ctx *xlate_ctx,
   sframe_fde->dw_fde = xlate_ctx->dw_fde;
   sframe_fde->sframe_fres = xlate_ctx->first_fre;
   sframe_fde->num_fres = xlate_ctx->num_xlate_fres;
+  sframe_fde->ra_undefined = xlate_ctx->ra_undefined;
 }
 
 /* Add the given FRE in the list of frame row entries in the given FDE
@@ -1597,24 +1609,46 @@ sframe_xlate_do_cfi_escape (const struct sframe_xlate_ctx *xlate_ctx,
 /* Translate DW_CFA_undefined into SFrame context.
 
    DW_CFA_undefined op indicates that from now on, the previous value of
-   register can’t be restored anymore.  In SFrame stack trace, we cannot
-   represent such a semantic.  So, we skip generating an SFrame FDE for this,
-   when a register of interest is used with DW_CFA_undefined.
+   register can’t be restored anymore.  In DWARF, for the return address (RA)
+   register, this indicates to an unwinder that there is no return address
+   and the unwind is complete.
+
+   In SFrame, represent the use of the RA register with DW_CFA_undefined as
+   SFrame FDE without any FREs and the RA undefined flag set.  Stack tracers
+   can use this as indication  that an outermost frame has been reached and
+   the stack trace is complete.
+   The use of other registers of interest with  DW_CFA_undefined cannot be
+   represented in SFrame.  Therefore skip generating an SFrame FDE.
 
    Return SFRAME_XLATE_OK if success.  */
 
 static int
-sframe_xlate_do_cfi_undefined (const struct sframe_xlate_ctx *xlate_ctx ATTRIBUTE_UNUSED,
+sframe_xlate_do_cfi_undefined (struct sframe_xlate_ctx *xlate_ctx,
 			       const struct cfi_insn_data *cfi_insn)
 {
   if (cfi_insn->u.r == SFRAME_CFA_FP_REG
-      || cfi_insn->u.r == SFRAME_CFA_RA_REG
       || cfi_insn->u.r == SFRAME_CFA_SP_REG)
     {
       as_warn (_("no SFrame FDE emitted; %s reg %u in .cfi_undefined"),
 	       sframe_register_name (cfi_insn->u.r), cfi_insn->u.r);
       return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented.  */
     }
+  else if (cfi_insn->u.r == SFRAME_CFA_RA_REG)
+    {
+      /* Represent RA undefined (i.e. outermost frame) as FDE without any
+	 FREs and the RA undefined flag set.  */
+      const struct fde_entry *dw_fde = xlate_ctx->dw_fde;
+
+      /* Re-initialize translation context while preserving the associated
+	 DWARF FDE.  */
+      sframe_xlate_ctx_cleanup (xlate_ctx);
+      sframe_xlate_ctx_init (xlate_ctx);
+      xlate_ctx->dw_fde = dw_fde;
+      xlate_ctx->ra_undefined = true;
+
+      /* Stop processing of this FDE.  */
+      return SFRAME_XLATE_RA_UNDEFINED;
+    }
 
   /* Safe to skip.  */
   return SFRAME_XLATE_OK;
@@ -1826,8 +1860,10 @@ sframe_do_fde (struct sframe_xlate_ctx *xlate_ctx,
       if (err != SFRAME_XLATE_OK)
 	{
 	  /* Skip generating SFrame stack trace info for the function if any
-	     offending CFI is encountered by sframe_do_cfi_insn ().  Warning
-	     message already printed by sframe_do_cfi_insn ().  */
+	     offending CFI is encountered by sframe_do_cfi_insn ().  Likewise
+	     stop translating any further CFI, if RA undefined is encountered
+	     (SFRAME_XLATE_RA_UNDEFINED).
+	     Warning message already printed by sframe_do_cfi_insn ().  */
 	  return err; /* Return the error code.  */
 	}
     }
@@ -1888,11 +1924,13 @@ create_sframe_all (void)
       sframe_xlate_ctx_init (xlate_ctx);
 
       /* Process and link SFrame FDEs if no error.  Also skip adding an SFrame
-	 FDE if it does not contain any SFrame FREs.  There is little use of an
-	 SFrame FDE if there is no stack tracing information for the
-	 function.  */
+	 FDE if it does not contain any SFrame FREs, except if it is to
+	 represent RA undefined (SFRAME_XLATE_RA_UNDEFINED).  Otherwise there
+	 is little use of an SFrame FDE if there is no stack tracing
+	 information for the function.  */
       int err = sframe_do_fde (xlate_ctx, dw_fde);
-      if (err || xlate_ctx->num_xlate_fres == 0)
+      if ((err != SFRAME_XLATE_OK && err != SFRAME_XLATE_RA_UNDEFINED)
+	  || (err != SFRAME_XLATE_RA_UNDEFINED && xlate_ctx->num_xlate_fres == 0))
 	{
 	  sframe_xlate_ctx_cleanup (xlate_ctx);
 	  sframe_fde_free (sframe_fde);
diff --git a/gas/gen-sframe.h b/gas/gen-sframe.h
index e4a4c6ea0ceb..3fdd7d34a673 100644
--- a/gas/gen-sframe.h
+++ b/gas/gen-sframe.h
@@ -89,6 +89,9 @@ struct sframe_func_entry
   struct sframe_row_entry *sframe_fres;
 
   unsigned int num_fres;
+
+  /* Whether RA is undefined.  If true, num_fres should be zero.  */
+  bool ra_undefined;
 };
 
 /* SFrame Function Description Entry Translation Context.  */
@@ -115,6 +118,10 @@ struct sframe_xlate_ctx
   struct sframe_row_entry *remember_fre;
 
   unsigned num_xlate_fres;
+
+  /* Whether RA is undefined.  If true, all fields except for dw_fde should
+     be initialized.  */
+  bool ra_undefined;
 };
 
 /* Error codes for SFrame translation context.  */
@@ -124,6 +131,8 @@ enum sframe_xlate_err
   SFRAME_XLATE_OK = 0,
   /* Error.  */
   SFRAME_XLATE_ERROR = 1,
+  /* RA undefined (i.e. outermost frame).  */
+  SFRAME_XLATE_RA_UNDEFINED = 2,
   /* Detailed error codes.  */
   SFRAME_XLATE_ERR_INVAL = -1,
   SFRAME_XLATE_ERR_NOTREPRESENTED = -2,
@@ -144,7 +153,7 @@ struct sframe_version_ops
   unsigned char (*set_fre_info) (unsigned int, unsigned int, unsigned int,
 				 bool);
   /* set SFrame Func info.  */
-  unsigned char (*set_func_info) (unsigned int, unsigned int, unsigned int);
+  unsigned char (*set_func_info) (unsigned int, unsigned int, unsigned int, bool);
 };
 
 /* Generate SFrame stack trace info and prepare contents for the output.
diff --git a/gas/sframe-opt.c b/gas/sframe-opt.c
index 655207348282..72087e6f2649 100644
--- a/gas/sframe-opt.c
+++ b/gas/sframe-opt.c
@@ -96,6 +96,7 @@ sframe_convert_frag (fragS *frag)
   offsetT rest_of_data;
   uint8_t fde_type, fre_type;
   uint8_t pauth_key;
+  bool ra_undefined;
 
   expressionS *exp;
   symbolS *dataS;
@@ -118,6 +119,7 @@ sframe_convert_frag (fragS *frag)
       rest_of_data = (symbol_get_value_expression(dataS))->X_add_number;
       fde_type = SFRAME_V1_FUNC_FDE_TYPE (rest_of_data);
       pauth_key = SFRAME_V1_FUNC_PAUTH_KEY (rest_of_data);
+      ra_undefined = SFRAME_V2_FUNC_RA_UNDEF (rest_of_data);
       gas_assert (fde_type == SFRAME_FDE_TYPE_PCINC);
 
       /* Calculate the applicable fre_type.  */
@@ -133,6 +135,7 @@ sframe_convert_frag (fragS *frag)
       /* Create the new function info.  */
       value = SFRAME_V1_FUNC_INFO (fde_type, fre_type);
       value = SFRAME_V1_FUNC_INFO_UPDATE_PAUTH_KEY (pauth_key, value);
+      value = SFRAME_V2_FUNC_INFO_UPDATE_RA_UNDEF (ra_undefined, value);
 
       frag->fr_literal[frag->fr_fix] = value;
     }
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-aarch64-ra-undefined-1.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-aarch64-ra-undefined-1.d
new file mode 100644
index 000000000000..74c5f440e4dc
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-aarch64-ra-undefined-1.d
@@ -0,0 +1,17 @@
+#as: --gsframe
+#objdump: --sframe=.sframe
+#name: SFrame generation on aarch64 - .cfi_undefined RA
+#...
+Contents of the SFrame section .sframe:
+  Header :
+
+    Version: SFRAME_VERSION_2
+    Flags: SFRAME_F_FDE_FUNC_START_PCREL
+    Num FDEs: 1
+    Num FREs: 0
+
+  Function Index :
+
+    func idx \[0\]: pc = 0x0, size = 16 bytes, RA undefined
+    STARTPC +CFA +FP +RA +
+#pass
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-aarch64-ra-undefined-1.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-aarch64-ra-undefined-1.s
new file mode 100644
index 000000000000..7b28ab8f6528
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-aarch64-ra-undefined-1.s
@@ -0,0 +1,13 @@
+	.cfi_startproc
+	stp	fp, lr, [sp, #-16]!
+	.cfi_def_cfa_offset 16
+	.cfi_offset 29, -16
+	.cfi_offset 30, -8
+	nop
+	.cfi_undefined 30
+	ldp	fp, lr, [sp], #16
+	.cfi_restore 20
+	.cfi_restore 19
+	.cfi_def_cfa_offset 0
+	ret	lr
+	.cfi_endproc
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-ra-undefined-1.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-ra-undefined-1.d
new file mode 100644
index 000000000000..00170b2aaa62
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-ra-undefined-1.d
@@ -0,0 +1,18 @@
+#name: SFrame generation on s390x - .cfi_undefined RA
+#as: --gsframe
+#objdump: --sframe=.sframe
+#...
+Contents of the SFrame section .sframe:
+
+  Header :
+
+    Version: SFRAME_VERSION_2
+    Flags: SFRAME_F_FDE_FUNC_START_PCREL
+    Num FDEs: 1
+    Num FREs: 0
+
+  Function Index :
+
+    func idx \[0\]: pc = 0x0, size = 18 bytes, RA undefined
+    STARTPC +CFA +FP +RA +
+#pass
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-ra-undefined-1.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-ra-undefined-1.s
new file mode 100644
index 000000000000..dda7abcdf4d7
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-ra-undefined-1.s
@@ -0,0 +1,11 @@
+	.cfi_startproc
+	stmg	%r11,%r15,48(%r15)
+	.cfi_offset 14, -48
+	.cfi_offset 15, -40
+	nop
+	.cfi_undefined 14
+	lmg	%r14,%r15,160+48(%r11)
+	.cfi_restore 15
+	.cfi_restore 14
+	br	%r14
+	.cfi_endproc
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-ra-undefined-1.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-ra-undefined-1.d
new file mode 100644
index 000000000000..03cd26cea9e6
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-ra-undefined-1.d
@@ -0,0 +1,19 @@
+#as: --gsframe -O0
+#objdump: --sframe=.sframe
+#name: SFrame generation on x86_64 - .cfi_undefined RA
+#...
+Contents of the SFrame section .sframe:
+
+  Header :
+
+    Version: SFRAME_VERSION_2
+    Flags: SFRAME_F_FDE_FUNC_START_PCREL
+    CFA fixed RA offset: \-8
+    Num FDEs: 1
+    Num FREs: 0
+
+  Function Index :
+
+    func idx \[0\]: pc = 0x0, size = 6 bytes, RA undefined
+    STARTPC +CFA +FP +RA +
+#pass
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-ra-undefined-1.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-ra-undefined-1.s
new file mode 100644
index 000000000000..0f5b9c71715f
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-ra-undefined-1.s
@@ -0,0 +1,11 @@
+	.cfi_startproc
+	pushq   %rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset 6, -16
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register 6
+	nop
+	.cfi_undefined 16
+	.cfi_def_cfa 7, 8
+	ret
+	.cfi_endproc
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp b/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp
index 82669cd04751..fe26abcfd1fd 100644
--- a/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp
@@ -63,6 +63,7 @@ if { [istarget "x86_64-*-*"] && [gas_sframe_check] } then {
 	run_dump_test "cfi-sframe-x86_64-empty-2"
 	run_dump_test "cfi-sframe-x86_64-empty-3"
 	run_dump_test "cfi-sframe-x86_64-empty-4"
+	run_dump_test "cfi-sframe-x86_64-ra-undefined-1"
 	set ASFLAGS "$old_ASFLAGS"
     }
 }
@@ -74,6 +75,7 @@ if { [istarget "aarch64*-*-*"] && [gas_sframe_check] } then {
     run_dump_test "cfi-sframe-aarch64-3"
     run_dump_test "cfi-sframe-aarch64-4"
     run_dump_test "cfi-sframe-aarch64-pac-ab-key-1"
+    run_dump_test "cfi-sframe-aarch64-ra-undefined-1"
 }
 
 # s390x specific tests
@@ -88,4 +90,5 @@ if { [istarget "s390x*-*-*"] && [gas_sframe_check] } then {
     run_dump_test "cfi-sframe-s390x-fpra-offset-2"
     run_dump_test "cfi-sframe-s390x-fpra-register-1"
     run_dump_test "cfi-sframe-s390x-fpra-register-2"
+    run_dump_test "cfi-sframe-s390x-ra-undefined-1"
 }
diff --git a/include/sframe.h b/include/sframe.h
index 7523adbef3a8..221b70c39caf 100644
--- a/include/sframe.h
+++ b/include/sframe.h
@@ -199,12 +199,13 @@ typedef struct sframe_func_desc_entry
      - 4-bits: Identify the FRE type used for the function.
      - 1-bit: Identify the FDE type of the function - mask or inc.
      - 1-bit: PAC authorization A/B key (aarch64).
-     - 2-bits: Unused.
+     - 1-bit: Identify whether the RA is undefined (i.e. outermost frame).
+     - 1-bit: Unused.
      ------------------------------------------------------------------------
-     |     Unused    |  PAC auth A/B key (aarch64) |  FDE type |   FRE type   |
-     |               |     Unused (amd64, s390x)   |           |              |
+     | Unused|  RA   |  PAC auth A/B key (aarch64) |  FDE type |   FRE type   |
+     |       | undef.|     Unused (amd64, s390x)   |           |              |
      ------------------------------------------------------------------------
-     8               6                             5           4              0     */
+     8       7       6                             5           4              0     */
   uint8_t sfde_func_info;
   /* Size of the block of repeating insns.  Used for SFrame FDEs of type
      SFRAME_FDE_TYPE_PCMASK.  */
@@ -214,7 +215,8 @@ typedef struct sframe_func_desc_entry
 
 /* Macros to compose and decompose function info in FDE.  */
 
-/* Note: Set PAC auth key to SFRAME_AARCH64_PAUTH_KEY_A by default.  */
+/* Note: Set PAC auth key to SFRAME_AARCH64_PAUTH_KEY_A and RA undefined to
+	 0 by default.  */
 #define SFRAME_V1_FUNC_INFO(fde_type, fre_enc_type) \
   (((SFRAME_AARCH64_PAUTH_KEY_A & 0x1) << 5) | \
    (((fde_type) & 0x1) << 4) | ((fre_enc_type) & 0xf))
@@ -222,11 +224,16 @@ typedef struct sframe_func_desc_entry
 #define SFRAME_V1_FUNC_FRE_TYPE(data)	  ((data) & 0xf)
 #define SFRAME_V1_FUNC_FDE_TYPE(data)	  (((data) >> 4) & 0x1)
 #define SFRAME_V1_FUNC_PAUTH_KEY(data)	  (((data) >> 5) & 0x1)
+#define SFRAME_V2_FUNC_RA_UNDEF(data)     (((data) >> 6) & 0x1)
 
 /* Set the pauth key as indicated.  */
 #define SFRAME_V1_FUNC_INFO_UPDATE_PAUTH_KEY(pauth_key, fde_info) \
   ((((pauth_key) & 0x1) << 5) | ((fde_info) & 0xdf))
 
+/* Set the RA undefined flag as indicated.  */
+#define SFRAME_V2_FUNC_INFO_UPDATE_RA_UNDEF(ra_undef, fde_info) \
+  ((((ra_undef) & 0x1) << 6) | ((fde_info) & 0xbf))
+
 /* Size of stack frame offsets in an SFrame Frame Row Entry.  A single
    SFrame FRE has all offsets of the same size.  Offset size may vary
    across frame row entries.  */
diff --git a/libsframe/doc/sframe-spec.texi b/libsframe/doc/sframe-spec.texi
index 7307789df264..3e440cac5591 100644
--- a/libsframe/doc/sframe-spec.texi
+++ b/libsframe/doc/sframe-spec.texi
@@ -165,6 +165,12 @@ CFA offset adjustment and then scale down by CFA offset alignment factor).
   @item SFRAME_V2_S390X_CFA_OFFSET_DECODE: Decode CFA offset (i.e., scale up
 by CFA offset alignment factor and then revert CFA offset adjustment).
  @end itemize
+@item
+Add a new SFrame FDE info word flag @code{ra_undefined}, as an erratum to
+SFrame Version 2, to indicate whether the return address (RA) is undefined
+for the whole range of PCs covered by the FDE.  An SFrame FDE with the
+@code{ra_undefined} flag set has no FREs.  A stack tracer may use this as
+indication that an outermost frame has been reached and the trace is complete.
 @end itemize
 
 SFrame version 1 is now obsolete and should not be used.
@@ -531,7 +537,10 @@ function's first SFrame FRE in the SFrame section.
 @tab @code{uint32_t}
 @tab @code{sfde_func_num_fres}
 @tab Unsigned 32-bit integral field specifying the total number of SFrame FREs
-used for the function.
+used for the function.  A value of zero in combination with the SFrame FDE info
+word flag @code{ra_undefined} set indicates that an outermost frame with
+an "undefined" register rule for the return address has been reached.  For a
+stack tracer this indicates the trace is complete.
 
 @item 0x10
 @tab @code{uint8_t}
@@ -567,10 +576,14 @@ The info word is a bitfield split into three parts.  From MSB to LSB:
 
 @multitable {Bit offset} {@code{pauth_key}} {Specify which key is used for signing the return addresses}
 @headitem Bit offset @tab Name @tab Description
-@item 7--6
+@item 7
 @tab @code{unused}
 @tab Unused bits.
 
+@item 6
+@tab @code{ra_undefined}
+@tab Specify whether the return address is undefined for the SFrame FDE.
+
 @item 5
 @tab @code{pauth_key}
 @tab (For AARCH64) Specify which key is used for signing the return addresses
diff --git a/libsframe/sframe-dump.c b/libsframe/sframe-dump.c
index d55d3847194e..d42860bb017a 100644
--- a/libsframe/sframe-dump.c
+++ b/libsframe/sframe-dump.c
@@ -167,6 +167,9 @@ dump_sframe_func_with_fres (sframe_decoder_ctx *sfd_ctx,
       && (SFRAME_V1_FUNC_PAUTH_KEY (func_info) == SFRAME_AARCH64_PAUTH_KEY_B))
     printf (", pauth = B key");
 
+  if (SFRAME_V2_FUNC_RA_UNDEF (func_info))
+    printf (", RA undefined");
+
   char temp[100];
 
   printf ("\n    %-7s%-8s %-10s%-10s%-13s",
-- 
2.48.1



More information about the Binutils mailing list