[PATCH] gas: sframe: partially process DWARF expressions in CFI_escape
Indu Bhagat
indu.bhagat@oracle.com
Tue Jan 28 00:57:22 GMT 2025
CFI_escape is most commonly used to include DWARF expressions in the
unwind information. One may also use CFI_escape to add OS-specific CFI
opcodes. Up until now, SFrame generation process would skip generating
SFrame FDE at the mere sight of a CFI_escape opcode.
Fine tune the handling of CFI_escape for SFrame generation by explicitly
checking for some "harmless" (in context of SFrame generation)
CFI_escape DWARF expressions:
- DW_CFA_expression affecting non SP/FP registers
- DW_CFA_value_expression affecting non SP/FP registers.
Expose the current cfi_escape_data structure in dw2gencfi.c to the
relevant header file to allow SFrame generation APIs to use it too.
In future, SFrame stack trace may support non-SP/FP as base register
(albeit in limited form). Add an explicit check in sframe_xlate_do_expr
(to test against the current CFA register) to ensure the functionality
continues to work.
Also, add a common test with DWARF Reg 12 which is non SP / FP on both
x86_64 and aarch64.
gas/
* dw2gencfi.c (struct cfi_escape_data): Move from ...
* dw2gencfi.h (struct cfi_escape_data): ... to.
* gen-sframe.c (sframe_xlate_do_expr): New definition.
(sframe_do_cfi_insn): Handle CFI_escape explicitly.
gas/testsuite/
* gas/cfi-sframe/cfi-sframe.exp: Add new tests.
* gas/cfi-sframe/cfi-sframe-common-9.d: New test.
* gas/cfi-sframe/cfi-sframe-common-9.s: New test.
* gas/cfi-sframe/cfi-sframe-x86_64-empty-1.d: New test.
* gas/cfi-sframe/cfi-sframe-x86_64-empty-1.s: New test.
---
Testing notes:
- Regression tested on a set of x86_64* and aarch64* targets.
- In a follow up patch, we will target some more of "harmless"
instances of the currently seen warnings in some packages:
skipping SFrame FDE; .cfi_escape with op (0x2e)
(DW_CFA_GNU_args_size = 0x2e).
gas/dw2gencfi.c | 6 --
gas/dw2gencfi.h | 6 ++
gas/gen-sframe.c | 77 ++++++++++++++++++-
.../gas/cfi-sframe/cfi-sframe-common-9.d | 22 ++++++
.../gas/cfi-sframe/cfi-sframe-common-9.s | 12 +++
.../cfi-sframe/cfi-sframe-x86_64-empty-1.d | 17 ++++
.../cfi-sframe/cfi-sframe-x86_64-empty-1.s | 10 +++
gas/testsuite/gas/cfi-sframe/cfi-sframe.exp | 2 +
8 files changed, 144 insertions(+), 8 deletions(-)
create mode 100644 gas/testsuite/gas/cfi-sframe/cfi-sframe-common-9.d
create mode 100644 gas/testsuite/gas/cfi-sframe/cfi-sframe-common-9.s
create mode 100644 gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-empty-1.d
create mode 100644 gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-empty-1.s
diff --git a/gas/dw2gencfi.c b/gas/dw2gencfi.c
index 012af0ff33e..41e15cd1463 100644
--- a/gas/dw2gencfi.c
+++ b/gas/dw2gencfi.c
@@ -368,12 +368,6 @@ generic_dwarf2_emit_offset (symbolS *symbol, unsigned int size)
}
#endif
-struct cfi_escape_data
-{
- struct cfi_escape_data *next;
- expressionS exp;
-};
-
struct cie_entry
{
struct cie_entry *next;
diff --git a/gas/dw2gencfi.h b/gas/dw2gencfi.h
index 42eb3863a0a..9b0e5979c0d 100644
--- a/gas/dw2gencfi.h
+++ b/gas/dw2gencfi.h
@@ -93,6 +93,12 @@ extern void cfi_add_CFA_restore_state (void);
#define MULTIPLE_FRAME_SECTIONS (SUPPORT_FRAME_LINKONCE || SUPPORT_COMPACT_EH \
|| TARGET_MULTIPLE_EH_FRAME_SECTIONS)
+struct cfi_escape_data
+{
+ struct cfi_escape_data *next;
+ expressionS exp;
+};
+
struct cfi_insn_data
{
struct cfi_insn_data *next;
diff --git a/gas/gen-sframe.c b/gas/gen-sframe.c
index 85d2f03a55c..05ecc60adda 100644
--- a/gas/gen-sframe.c
+++ b/gas/gen-sframe.c
@@ -1310,6 +1310,77 @@ sframe_xlate_do_gnu_window_save (struct sframe_xlate_ctx *xlate_ctx,
return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */
}
+/* Translate CFI_escape into SFrame context.
+
+ .cfi_escape CFI directive allows the user to add arbitrary bytes to the
+ unwind info. DWARF expressions commonly follow after CFI_escape (fake CFI)
+ DWARF opcode. One might also use CFI_escape to add OS-specific CFI opcodes
+ even.
+
+ In SFrame stack trace format, complex unwind info cannot be represented. In
+ such cases, SFrame FDE generation is skipped and the user is warned. Recall,
+ however, that SFrame stack trace information is meant to convey information
+ about SP, FP and RA only. Hence, some DWARF expressions, are indeed safe to
+ skip.
+
+ This function partially processes some DWARF expresssions and returns
+ SFRAME_XLATE_OK if OK to skip. */
+
+static int
+sframe_xlate_do_expr (struct sframe_xlate_ctx *xlate_ctx,
+ struct cfi_insn_data *cfi_insn)
+{
+ int op;
+ struct cfi_escape_data *e;
+ unsigned int reg = 0;
+ int err = SFRAME_XLATE_OK;
+ struct sframe_row_entry *cur_fre = NULL;
+
+ e = cfi_insn->u.esc;
+
+ if (e)
+ {
+ op = e->exp.X_add_number;
+ switch (op)
+ {
+ /* Of all the possible opcodes expected here, it is safe to
+ ignore DW_CFA_expression and DW_CFA_val_expression, provided they
+ do not impact the SP / FP register. */
+ case DW_CFA_expression:
+ case DW_CFA_val_expression:
+ /* Both DW_CFA_expression and DW_CFA_val_expression instructions take
+ two operands: an unsigned LEB128 value representing a register
+ number, and a DW_FORM_block value representing a DWARF expression.
+ For the current purpose, we simply need to know the register
+ number. */
+ e = e->next;
+ /* Keep in sync with the behaviour of cfi_parse_reg (). */
+ gas_assert (e->exp.X_op == O_register || e->exp.X_op == O_constant);
+ reg = e->exp.X_add_number;
+ /* Get the scratchpad FRE. */
+ cur_fre = xlate_ctx->cur_fre;
+ if (reg == SFRAME_CFA_SP_REG || reg == SFRAME_CFA_FP_REG
+ || reg == cur_fre->cfa_base_reg)
+ {
+ as_warn (_("skipping SFrame FDE; .cfi_escape with reg (%#x)"), reg);
+ err = SFRAME_XLATE_ERR_NOTREPRESENTED;
+ }
+ break;
+ /* FIXME - Also add processing for DW_CFA_GNU_args_size in future? */
+ default:
+ /* In all other cases (e.g., DW_CFA_def_cfa_expression or other
+ OS-specific CFI opcodes), skip inspecting the DWARF expression.
+ This may impact the asynchronicity due to loss of coverage.
+ Continue to warn the user and bail out. */
+ as_warn (_("skipping SFrame FDE; .cfi_escape with op (%#x)"), op);
+ err = SFRAME_XLATE_ERR_NOTREPRESENTED;
+ break;
+ }
+ }
+
+ return err;
+}
+
/* Returns the DWARF call frame instruction name or fake CFI name for the
specified CFI opcode, or NULL if the value is not recognized. */
@@ -1406,6 +1477,9 @@ sframe_do_cfi_insn (struct sframe_xlate_ctx *xlate_ctx,
case DW_CFA_register:
err = sframe_xlate_do_register (xlate_ctx, cfi_insn);
break;
+ case CFI_escape:
+ err = sframe_xlate_do_expr (xlate_ctx, cfi_insn);
+ break;
/* Following CFI opcodes are not processed at this time.
These do not impact the coverage of the basic stack tracing
information as conveyed in the SFrame format. */
@@ -1413,8 +1487,7 @@ sframe_do_cfi_insn (struct sframe_xlate_ctx *xlate_ctx,
case DW_CFA_same_value:
break;
default:
- /* Following skipped operations do, however, impact the asynchronicity:
- - CFI_escape. */
+ /* Other skipped operations may, however, impact the asynchronicity. */
{
const char *cfi_name = sframe_get_cfi_name (op);
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-9.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-9.d
new file mode 100644
index 00000000000..80c92357073
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-9.d
@@ -0,0 +1,22 @@
+#as: --gsframe
+#objdump: --sframe=.sframe
+#name: SFrame cfi_escape test
+#...
+Contents of the SFrame section .sframe:
+
+ Header :
+
+ Version: SFRAME_VERSION_2
+ Flags: NONE
+#? CFA fixed FP offset: \-?\d+
+#? CFA fixed RA offset: \-?\d+
+ Num FDEs: 1
+ Num FREs: 2
+
+ Function Index :
+ func idx \[0\]: pc = 0x0, size = 8 bytes
+ STARTPC + CFA + FP + RA +
+#...
+ 0+0004 +sp\+16 +u +[uf] +
+
+#pass
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-9.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-9.s
new file mode 100644
index 00000000000..743e774a2b2
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-9.s
@@ -0,0 +1,12 @@
+## CFI_escape may be used to encode DWARF expressions among other things.
+## Depending on the register applicable for the DWARF expression, skipping
+## SFrame FDE may be OK: SFrame stack trace information is relevant for SP, FP
+## and RA only. In this test, CFI_escape is safe to skip (does not affect
+## correctness of SFrame data). The register 0xc is non SP / FP on both
+## aarch64 and x86_64.
+ .cfi_startproc
+ .long 0
+ .cfi_def_cfa_offset 16
+ .cfi_escape 0x10,0xc,0x2,0x76,0x78
+ .long 0
+ .cfi_endproc
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-empty-1.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-empty-1.d
new file mode 100644
index 00000000000..aa21d52a970
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-empty-1.d
@@ -0,0 +1,17 @@
+#as: --gsframe
+#warning: skipping SFrame FDE; \.cfi_escape with reg \(0x7\)
+#objdump: --sframe=.sframe
+#name: CFI_escape with register of significance to SFrame
+#...
+Contents of the SFrame section .sframe:
+
+ Header :
+
+ Version: SFRAME_VERSION_2
+ Flags: NONE
+#? CFA fixed FP offset: \-?\d+
+#? CFA fixed RA offset: \-?\d+
+ Num FDEs: 0
+ Num FREs: 0
+
+#pass
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-empty-1.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-empty-1.s
new file mode 100644
index 00000000000..168ac06bc4f
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-empty-1.s
@@ -0,0 +1,10 @@
+## CFI_escape may be used to encode DWARF expressions among other things.
+## In this test, a DWARF expression involving a register of interest (REG_SP on
+## x86_64, which is also the CFA in the current FRE) is seen. SFrame
+## generation, is skipped and a warning issued to the user.
+ .cfi_startproc
+ .long 0
+ .cfi_def_cfa_offset 16
+ .cfi_escape 0x10,0x7,0x2,0x76,0x78
+ .long 0
+ .cfi_endproc
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp b/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp
index b119b9da73d..803ae1c6cf0 100644
--- a/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp
@@ -78,6 +78,7 @@ if { ([istarget "x86_64-*-*"] || [istarget "aarch64*-*-*"]) \
run_dump_test "cfi-sframe-common-6"
run_dump_test "cfi-sframe-common-7"
run_dump_test "cfi-sframe-common-8"
+ run_dump_test "cfi-sframe-common-9"
run_dump_test "common-empty-1"
run_dump_test "common-empty-2"
@@ -89,6 +90,7 @@ if { [istarget "x86_64-*-*"] && [gas_sframe_check] } then {
if { [gas_x86_64_check] } then {
set ASFLAGS "$ASFLAGS --64"
run_dump_test "cfi-sframe-x86_64-1"
+ run_dump_test "cfi-sframe-x86_64-empty-1"
set ASFLAGS "$old_ASFLAGS"
}
}
--
2.43.0
More information about the Binutils
mailing list