[PATCH v4 3/4] gas: sframe: Represent .cfi_undefined RA as FRE without offsets

Jens Remus jremus@linux.ibm.com
Fri Jul 25 16:23:32 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.

Represent DW_CFA_undefined as SFrame FRE without any offsets, 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 FRE a so far invalid
offset count of zero and stop the trace.

include/
	* sframe.h (SFRAME_V2_FRE_RA_UNDEFINED_P): New macro to test
	FRE info word for RA undefined.

gas/
	* gen-sframe.h (struct sframe_row_entry): Add ra_undefined flag.
	* gen-sframe.c (sframe_row_entry_new): Initialize ra_undefined
	flag to not set.
	(sframe_row_entry_initialize): Treat ra_undefined flag as
	sticky.
	(sframe_fre_set_ra_track): Reset ra_undefined flag.
	(sframe_xlate_do_restore): Reset ra_undefined flag to saved
	state.
	(sframe_xlate_do_same_value): Reset ra_undefined flag.
	(sframe_xlate_do_cfi_undefined): For RA set ra_undefined flag.
	(get_fre_base_reg_id, get_fre_num_offsets,
	sframe_get_fre_offset_size): Return 0 if ra_undefined flag set.
	(output_sframe_row_entry): Represent RA undefined as SFrame FRE
	without any offsets (e.g. no CFA, RA, nor FP offsets).

libsframe/
	* doc/sframe-spec.texi (Changes from Version 1 to Version 2):
	Mention that a SFrame FRE without any offsets flag indicates an
	outermost frame with an undefined RA.
	(fre_offset_count): Document that a FRE offset count of zero
	indicates an outermost frame with an undefined RA.
	* sframe-dump.c (dump_sframe_func_with_fres): Show FRE without
	any offsets as "RA undefined".

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 V4:
    - Represent RA undefined as SFrame FRE without any offsets. (Indu)
      This aligns to DWARF and saves from adding a SFrame FDE flag.
    
    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.

 gas/gen-sframe.c                              | 52 +++++++++++++++++--
 gas/gen-sframe.h                              |  3 ++
 .../cfi-sframe-aarch64-ra-undefined-1.d       | 20 +++++++
 .../cfi-sframe-aarch64-ra-undefined-1.s       | 13 +++++
 .../cfi-sframe-s390x-ra-undefined-1.d         | 21 ++++++++
 .../cfi-sframe-s390x-ra-undefined-1.s         | 11 ++++
 .../cfi-sframe-x86_64-ra-undefined-1.d        | 22 ++++++++
 .../cfi-sframe-x86_64-ra-undefined-1.s        | 11 ++++
 gas/testsuite/gas/cfi-sframe/cfi-sframe.exp   |  3 ++
 include/sframe.h                              |  1 +
 libsframe/doc/sframe-spec.texi                | 10 +++-
 libsframe/sframe-dump.c                       | 15 +++++-
 12 files changed, 174 insertions(+), 8 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 acea1dc7dbb7..eeb1607105f7 100644
--- a/gas/gen-sframe.c
+++ b/gas/gen-sframe.c
@@ -164,6 +164,7 @@ sframe_fre_set_ra_track (struct sframe_row_entry *fre, offsetT ra_offset)
 {
   fre->ra_loc = SFRAME_FRE_ELEM_LOC_STACK;
   fre->ra_offset = ra_offset;
+  fre->ra_undefined = false;
   fre->merge_candidate = false;
 }
 
@@ -335,6 +336,9 @@ get_fre_base_reg_id (struct sframe_row_entry *sframe_fre)
   unsigned int cfi_insn_cfa_base_reg = sframe_fre->cfa_base_reg;
   unsigned fre_base_reg = SFRAME_BASE_REG_SP;
 
+  if (sframe_fre->ra_undefined)
+    return 0;
+
   if (cfi_insn_cfa_base_reg == SFRAME_CFA_FP_REG)
     fre_base_reg = SFRAME_BASE_REG_FP;
 
@@ -353,6 +357,10 @@ get_fre_num_offsets (struct sframe_row_entry *sframe_fre)
   /* Atleast 1 must always be present (to recover CFA).  */
   unsigned int fre_num_offsets = 1;
 
+  /* Represent RA undefined as FRE without any offsets.  */
+  if (sframe_fre->ra_undefined)
+    return 0;
+
   if (sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK)
     fre_num_offsets++;
   if (sframe_ra_tracking_p ()
@@ -377,6 +385,10 @@ sframe_get_fre_offset_size (struct sframe_row_entry *sframe_fre)
 
   unsigned int fre_offset_size = 0;
 
+  /* Represent RA undefined as FRE without any offsets.  */
+  if (sframe_fre->ra_undefined)
+    return 0;
+
   /* What size of offsets appear in this frame row entry.  */
   cfa_offset_size = get_offset_size_in_bytes (sframe_fre->cfa_offset);
   if (sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK)
@@ -514,6 +526,8 @@ sframe_row_entry_new (void)
      initialize it in sframe_row_entry_initialize () with the sticky
      bit if set.  */
   fre->mangled_ra_p = false;
+  /* Reset the RA undefined status by to zero by default.  */
+  fre->ra_undefined = false;
 
   return fre;
 }
@@ -588,6 +602,13 @@ output_sframe_row_entry (symbolS *fde_start_addr,
 				  fre_offset_size, sframe_fre->mangled_ra_p);
   out_one (fre_info);
 
+  /* Represent RA undefined as FRE without any offsets.  */
+  if (sframe_fre->ra_undefined)
+    {
+      gas_assert (fre_num_offsets == 0);
+      return;
+    }
+
   idx = sframe_fre_offset_func_map_index (fre_offset_size);
   gas_assert (idx < SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_MAX);
 
@@ -942,6 +963,10 @@ sframe_row_entry_initialize (struct sframe_row_entry *cur_fre,
   /* Treat RA mangling as a sticky bit.  It retains its value until another
      .cfi_negate_ra_state is seen.  */
   cur_fre->mangled_ra_p = prev_fre->mangled_ra_p;
+  /* Treat RA undefined as a sticky bit.  It retains its value until a
+     .cfi_offset RA, .cfi_register RA, .cfi_restore RA, or .cfi_same_value RA
+     is seen.  */
+  cur_fre->ra_undefined = prev_fre->ra_undefined;
 }
 
 /* Return SFrame register name for SP, FP, and RA, or NULL if other.  */
@@ -1317,6 +1342,7 @@ sframe_xlate_do_restore (struct sframe_xlate_ctx *xlate_ctx,
       gas_assert (cur_fre);
       cur_fre->ra_loc = cie_fre->ra_loc;
       cur_fre->ra_offset = cie_fre->ra_offset;
+      cur_fre->ra_undefined = cie_fre->ra_undefined;
       cur_fre->merge_candidate = false;
     }
   return SFRAME_XLATE_OK;
@@ -1603,9 +1629,15 @@ 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 FRE without any offsets.  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.  */
 
@@ -1614,15 +1646,24 @@ sframe_xlate_do_cfi_undefined (const struct sframe_xlate_ctx *xlate_ctx ATTRIBUT
 			       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 FRE without any
+	 offsets.  */
+      struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre;
+
+      gas_assert (cur_fre);
+      /* Set RA undefined status bit.  */
+      cur_fre->ra_undefined = true;
+      cur_fre->merge_candidate = false;
+    }
 
-  /* Safe to skip.  */
   return SFRAME_XLATE_OK;
 }
 
@@ -1670,6 +1711,7 @@ sframe_xlate_do_same_value (const struct sframe_xlate_ctx *xlate_ctx,
     {
       cur_fre->ra_loc = SFRAME_FRE_ELEM_LOC_REG;
       cur_fre->ra_offset = 0;
+      cur_fre->ra_undefined = false;
       cur_fre->merge_candidate = false;
     }
   else if (cfi_insn->u.r == SFRAME_CFA_FP_REG)
diff --git a/gas/gen-sframe.h b/gas/gen-sframe.h
index e4a4c6ea0ceb..13956489b09a 100644
--- a/gas/gen-sframe.h
+++ b/gas/gen-sframe.h
@@ -55,6 +55,9 @@ struct sframe_row_entry
   /* Whether the return address is mangled with pauth code.  */
   bool mangled_ra_p;
 
+  /* Whether RA is undefined.  */
+  bool ra_undefined;
+
   /* Track CFA base (architectural) register ID.  */
   unsigned int cfa_base_reg;
   /* Offset from the CFA base register for recovering CFA.  */
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..f6bd6d71008b
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-aarch64-ra-undefined-1.d
@@ -0,0 +1,20 @@
+#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: 4
+
+  Function Index :
+
+    func idx \[0\]: pc = 0x0, size = 16 bytes
+    STARTPC +CFA +FP +RA +
+    0+0000 +sp\+0 +u +u +
+    0+0004 +sp\+16 +c\-16 +c\-8 +
+    0+0008 +RA undefined
+    0+000c +RA undefined
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..dfd47e30bbe9
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-ra-undefined-1.d
@@ -0,0 +1,21 @@
+#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: 4
+
+  Function Index :
+
+    func idx \[0\]: pc = 0x0, size = 18 bytes
+    STARTPC +CFA +FP +RA +
+    0+0000 +sp\+160 +u +u +
+    0+0006 +sp\+160 +u +c\-48 +
+    0+000a +RA undefined
+    0+0010 +sp\+160 +u +u +
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..a635c3cd9cbf
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-ra-undefined-1.d
@@ -0,0 +1,22 @@
+#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: 4
+
+  Function Index :
+
+    func idx \[0\]: pc = 0x0, size = 6 bytes
+    STARTPC +CFA +FP +RA +
+    0+0000 +sp\+8 +u +f +
+    0+0001 +sp\+16 +c\-16 +f +
+    0+0004 +fp\+16 +c\-16 +f +
+    0+0005 +RA undefined
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 9380c98fc3db..41b927fd7196 100644
--- a/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp
@@ -64,6 +64,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"
     }
 }
@@ -75,6 +76,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
@@ -89,4 +91,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..127bf3750577 100644
--- a/include/sframe.h
+++ b/include/sframe.h
@@ -287,6 +287,7 @@ typedef struct sframe_fre_info
 #define SFRAME_V1_FRE_OFFSET_COUNT(data)	  (((data) >> 1) & 0xf)
 #define SFRAME_V1_FRE_OFFSET_SIZE(data)		  (((data) >> 5) & 0x3)
 #define SFRAME_V1_FRE_MANGLED_RA_P(data)	  (((data) >> 7) & 0x1)
+#define SFRAME_V2_FRE_RA_UNDEFINED_P(data)	  (SFRAME_V1_FRE_OFFSET_COUNT (data) == 0)
 
 /* SFrame Frame Row Entry definitions.
 
diff --git a/libsframe/doc/sframe-spec.texi b/libsframe/doc/sframe-spec.texi
index 7307789df264..346d29a51ac0 100644
--- a/libsframe/doc/sframe-spec.texi
+++ b/libsframe/doc/sframe-spec.texi
@@ -165,6 +165,11 @@ 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
+An SFrame FRE info word offset count of zero indicates that the return
+address (RA) is undefined for the range of PCs covered by the SFrame FRE.
+A stack tracer may use this as indication that an outermost frame has been
+reached and the stack trace is complete.
 @end itemize
 
 SFrame version 1 is now obsolete and should not be used.
@@ -771,7 +776,10 @@ SFRAME_FRE_OFFSET_4B.
 @item 1-4
 @tab @code{fre_offset_count}
 @tab A max value of 15 is allowed.  Typically, a value of upto 3 is sufficient
-for most ABIs to track all three of CFA, FP and RA.
+for most ABIs to track all three of CFA, FP and RA.  A value of zero indicates
+that the return address (RA) is undefined.  A stack tracer may use this as
+indication that an outermost frame has been reached and the stack trace is
+complete.
 
 @item 0
 @tab @code{fre_cfa_base_reg_id}
diff --git a/libsframe/sframe-dump.c b/libsframe/sframe-dump.c
index d55d3847194e..bada1ee6c684 100644
--- a/libsframe/sframe-dump.c
+++ b/libsframe/sframe-dump.c
@@ -130,6 +130,7 @@ dump_sframe_func_with_fres (sframe_decoder_ctx *sfd_ctx,
   uint64_t func_start_pc_vma = 0;
   uint64_t fre_start_pc_vma = 0;
   const char *base_reg_str[] = {"fp", "sp"};
+  int offset_count = 0;
   int32_t cfa_offset = 0;
   int32_t fp_offset = 0;
   int32_t ra_offset = 0;
@@ -180,15 +181,25 @@ dump_sframe_func_with_fres (sframe_decoder_ctx *sfd_ctx,
 			  : func_start_pc_vma + fre.fre_start_addr);
 
       /* FIXME - fixup the err caching in array.
-	 assert no error for base reg id.  */
+	 assert no error for base reg id and offset count.  */
       base_reg_id = sframe_fre_get_base_reg_id (&fre, &err[0]);
+      offset_count = sframe_fre_get_offset_count (sfd_ctx, &fre, &err[0]);
       cfa_offset = sframe_fre_get_cfa_offset (sfd_ctx, &fre, &err[0]);
       fp_offset = sframe_fre_get_fp_offset (sfd_ctx, &fre, &err[1]);
       ra_offset = sframe_fre_get_ra_offset (sfd_ctx, &fre, &err[2]);
 
-      /* Dump CFA info.  */
+      /* Dump VMA.  */
       printf ("\n");
       printf ("    %016"PRIx64, fre_start_pc_vma);
+
+      /* Dump RA undefined (FRE without any offsets).  */
+      if (offset_count == 0)
+	{
+	  printf ("  RA undefined");
+	  continue;
+	}
+
+      /* Dump CFA info.  */
       sprintf (temp, "%s+%d", base_reg_str[base_reg_id], cfa_offset);
       printf ("  %-10s", temp);
 
-- 
2.48.1



More information about the Binutils mailing list