[PATCH] gas: sframe: Represent .cfi_undefined RA as SFrame FDE without FREs
Jens Remus
jremus@linux.ibm.com
Fri Jul 18 16:33:59 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 FDE without any FREs,
so that a stack tracer implementation can use this as indication that
an outermost frame has been reached and the trace is complete.
This representation is backwards 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.
gas/
* gen-sframe.h (enum sframe_xlate_err): Add
SFRAME_XLATE_RA_UNDEFINED.
* gen-sframe.c (sframe_xlate_do_cfi_undefined): Represent
DW_CFA_undefined RA as SFrame FDE without and SFrame FREs and
return SFRAME_XLATE_RA_UNDEFINED to stop any further processing
of DWARF FREs.
(sframe_do_fde): Add comment that RA undefined may stop
processing of DWARF FREs.
(create_sframe_all): Handle SFRAME_XLATE_RA_UNDEFINED with
zero FREs as good case.
libsframe/
* doc/sframe-spec.texi (Changes from Version 1 to Version 2):
Mention that a SFrame FDE without any FREs indicates an
outermost frame with an "undefined" RA register rule.
(sfde_func_num_fres): Document that a FRE count of zero
indicates an outermost frame with an "undefined" RA register
rule.
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):
Stack tracer implementations using SFrame, such as Glibc backtrace(),
benefit from the ability to detect whether an outermost frame has been
reached, which indicates that the trace is complete. In DWARF CFI this
is indicated by an "undefined" register rule for the return address
register.
This would enable to implement s390 64-bit (s390x) support for
backtracing using SFrame in Glibc [1], which requires the stack tracer
implementation to reliably detect, that an outermost frame has been
reached.
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.
[1]: [RFC PATCH v2 0/2] s390x: Add support for SFrame stack
backtracing,
https://inbox.sourceware.org/libc-alpha/20250627133813.2198784-1-jremus@linux.ibm.com/
gas/gen-sframe.c | 24 +++++++++++++++----
gas/gen-sframe.h | 2 ++
.../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 +++
libsframe/doc/sframe-spec.texi | 8 ++++++-
10 files changed, 120 insertions(+), 6 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 806ef5f9b5be..72f84fcca16e 100644
--- a/gas/gen-sframe.c
+++ b/gas/gen-sframe.c
@@ -1597,17 +1597,29 @@ sframe_xlate_do_cfi_escape (const struct sframe_xlate_ctx *xlate_ctx,
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. */
+ sframe_xlate_ctx_cleanup (xlate_ctx);
+ xlate_ctx->first_fre = NULL;
+ xlate_ctx->last_fre = NULL;
+ xlate_ctx->cur_fre = NULL;
+ xlate_ctx->remember_fre = NULL;
+ xlate_ctx->num_xlate_fres = 0;
+
+ return SFRAME_XLATE_RA_UNDEFINED;
+ }
/* Safe to skip. */
return SFRAME_XLATE_OK;
@@ -1819,8 +1831,9 @@ 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 () or if
+ RA is undefined (i.e. outermost frame).
+ Warning message already printed by sframe_do_cfi_insn (). */
return err; /* Return the error code. */
}
}
@@ -1885,7 +1898,8 @@ create_sframe_all (void)
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..a46c9a0caa71 100644
--- a/gas/gen-sframe.h
+++ b/gas/gen-sframe.h
@@ -124,6 +124,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,
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..a4d4885df58f
--- /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
+ 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..18e24ef207de
--- /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
+ 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..cd24ce667ed1
--- /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
+ 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..073755b5716b
--- /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 00a3ecc067ce..42f77103d184 100644
--- a/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp
@@ -62,6 +62,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"
}
}
@@ -73,6 +74,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
@@ -87,4 +89,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/libsframe/doc/sframe-spec.texi b/libsframe/doc/sframe-spec.texi
index 7307789df264..29a822373928 100644
--- a/libsframe/doc/sframe-spec.texi
+++ b/libsframe/doc/sframe-spec.texi
@@ -165,6 +165,10 @@ 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
+Represent the "undefined" register rule for the RA register as an SFrame FDE
+without any 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 +535,9 @@ 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 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}
--
2.48.1
More information about the Binutils
mailing list