public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] gas: sframe: partially process DWARF expressions in CFI_escape
@ 2025-01-28  0:57 Indu Bhagat
  2025-01-28  8:03 ` Jan Beulich
                   ` (2 more replies)
  0 siblings, 3 replies; 13+ messages in thread
From: Indu Bhagat @ 2025-01-28  0:57 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

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


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2025-02-04 14:20 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-01-28  0:57 [PATCH] gas: sframe: partially process DWARF expressions in CFI_escape Indu Bhagat
2025-01-28  8:03 ` Jan Beulich
2025-01-28 15:24   ` Indu Bhagat
2025-01-28 15:31     ` Jan Beulich
2025-01-28 18:09       ` Indu Bhagat
2025-01-29  7:06         ` Jan Beulich
2025-01-29  7:22 ` Jan Beulich
2025-01-29 20:23   ` Indu Bhagat
2025-01-30  7:40     ` Jan Beulich
2025-02-04 14:20       ` Indu Bhagat
2025-01-29 17:11 ` Jens Remus
2025-02-01  1:15   ` Indu Bhagat
2025-02-03 10:23     ` Jens Remus

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).