aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIndu Bhagat <indu.bhagat@oracle.com>2025-09-05 15:01:52 -0700
committerIndu Bhagat <indu.bhagat@oracle.com>2025-09-10 11:13:21 -0700
commit84c1e5cec0ec7d80d460db2b823d329e9759c642 (patch)
treedff0997defbb124f32173c803f56b1014415ea41
parent2fe19b88c905d521b9e9bcd4171817b860a1cc4c (diff)
downloadbinutils-84c1e5cec0ec7d80d460db2b823d329e9759c642.zip
binutils-84c1e5cec0ec7d80d460db2b823d329e9759c642.tar.gz
binutils-84c1e5cec0ec7d80d460db2b823d329e9759c642.tar.bz2
gas: sframe: skip DW_CFA_GNU_args_size when safe to ignore
Currently, gas warns and skips generating SFrame FDE when it sees: .cfi_escape 0x2e,XX From the documentation of DW_CFA_GNU_args_size: "The DW_CFA_GNU_args_size instruction takes an unsigned LEB128 operand representing an argument size. This instruction specifies the total of the size of the arguments which have been pushed onto the stack." With origins seemingly for VAX architecture, the usage of DW_CFA_GNU_args_size seems to have evolved. The purpose of DW_CFA_GNU_args_size is to adjust SP when performing stack unwinding for exception handling. For the purpose of stack tracing using SFrame, DW_CFA_GNU_args_size is safe to skip, especially when the CFA restoration is known to be FP based. A previous summary of the reasoning and intent was indicated here [1]. [1] https://sourceware.org/pipermail/binutils/2025-August/143829.html This fixes PR gas/33414 - sframe: handle DW_CFA_GNU_args_size in gas better gas/ PR gas/33414 * gen-sframe.c (sframe_xlate_do_escape_gnu_args_size): New definition. (sframe_xlate_do_cfi_escape): Handle DW_CFA_GNU_args_size. gas/testsuite/ PR gas/33414 * gas/cfi-sframe/cfi-sframe.exp: New test. * gas/cfi-sframe/cfi-sframe-common-12.d: New test. * gas/cfi-sframe/cfi-sframe-common-12.s: New test. * gas/cfi-sframe/cfi-sframe-x86_64-3.d: New test. * gas/cfi-sframe/cfi-sframe-x86_64-3.s: New test.
-rw-r--r--gas/gen-sframe.c62
-rw-r--r--gas/testsuite/gas/cfi-sframe/cfi-sframe-common-12.d22
-rw-r--r--gas/testsuite/gas/cfi-sframe/cfi-sframe-common-12.s8
-rw-r--r--gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-3.d21
-rw-r--r--gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-3.s5
-rw-r--r--gas/testsuite/gas/cfi-sframe/cfi-sframe.exp2
6 files changed, 119 insertions, 1 deletions
diff --git a/gas/gen-sframe.c b/gas/gen-sframe.c
index f5cf9bd..ac14278 100644
--- a/gas/gen-sframe.c
+++ b/gas/gen-sframe.c
@@ -1510,6 +1510,64 @@ warn_and_exit:
return err;
}
+/* Handle DW_CFA_GNU_args_size in .cfi_escape.
+
+ The purpose of DW_CFA_GNU_args_size is to adjust SP when performing stack
+ unwinding for exception handling. For stack tracing needs,
+ DW_CFA_GNU_args_size can be ignored, when CFA is FP-based. This is because
+ if the topmost frame is that of the catch block, the SP has been restored to
+ correct value by exception handling logic. From this point of interest in
+ the catch block now, stack tracing intends to go backwards to the caller
+ frame. If CFA restoration does not need SP, DW_CFA_GNU_args_size can be
+ ignored for stack tracing.
+
+ Continue to warn and not emit SFrame FDE if CFA is SP based. The pattern
+ where the CFA is SP based and there is a DW_CFA_GNU_args_size for
+ SP-adjustment is not entirely clear.
+
+ Sets CALLER_WARN_P for skipped cases (and returns SFRAME_XLATE_OK) where the
+ caller must warn. The caller then must also set
+ SFRAME_XLATE_ERR_NOTREPRESENTED for their callers. */
+
+static int
+sframe_xlate_do_escape_gnu_args_size (const struct sframe_xlate_ctx *xlate_ctx,
+ const struct cfi_insn_data *cfi_insn,
+ bool *caller_warn_p)
+{
+ const struct cfi_escape_data *e = cfi_insn->u.esc;
+ unsigned int i = 0;
+
+ /* Check for (DW_CFA_GNU_args_size offset) sequence. */
+#define CFI_ESC_NUM_EXP 1
+ offsetT items[CFI_ESC_NUM_EXP] = {0};
+ while (e->next)
+ {
+ e = e->next;
+ if (i >= CFI_ESC_NUM_EXP || e->exp.X_op != O_constant
+ || e->type != CFI_ESC_byte
+ || e->reloc != TC_PARSE_CONS_RETURN_NONE)
+ goto warn_and_exit;
+ items[i] = e->exp.X_add_number;
+ i++;
+ }
+ if (i == 0)
+ goto warn_and_exit;
+
+#undef CFI_ESC_NUM_EXP
+
+ offsetT offset = items[0];
+
+ struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre;
+ gas_assert (cur_fre);
+ /* If CFA is FP based, safe to skip. */
+ if (offset == 0 || cur_fre->cfa_base_reg == SFRAME_CFA_FP_REG)
+ return SFRAME_XLATE_OK;
+
+warn_and_exit:
+ *caller_warn_p = true;
+ return SFRAME_XLATE_OK;
+}
+
/* Handle CFI_escape in SFrame context.
.cfi_escape CFI directive allows the user to add arbitrary data to the
@@ -1573,7 +1631,9 @@ sframe_xlate_do_cfi_escape (const struct sframe_xlate_ctx *xlate_ctx,
err = sframe_xlate_do_escape_val_offset (xlate_ctx, cfi_insn, &warn_p);
break;
- /* FIXME - Also add processing for DW_CFA_GNU_args_size in future? */
+ case DW_CFA_GNU_args_size:
+ err = sframe_xlate_do_escape_gnu_args_size (xlate_ctx, cfi_insn, &warn_p);
+ break;
default:
warn_p = true;
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-12.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-12.d
new file mode 100644
index 0000000..c91ce7b
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-12.d
@@ -0,0 +1,22 @@
+#as: --gsframe
+#objdump: --sframe=.sframe
+#name: SFrame DW_CFA_GNU_args_size test
+#...
+Contents of the SFrame section .sframe:
+
+ Header :
+
+ Version: SFRAME_VERSION_2
+ Flags: SFRAME_F_FDE_FUNC_START_PCREL
+#? 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-12.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-12.s
new file mode 100644
index 0000000..41af4e1
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-12.s
@@ -0,0 +1,8 @@
+## DW_CFA_GNU_args_size 0 can be ignored
+ .cfi_startproc
+ .long 0
+ .cfi_def_cfa_offset 16
+ .long
+ .cfi_escape 0x2e, 0x0
+ .long 0
+ .cfi_endproc
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-3.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-3.d
new file mode 100644
index 0000000..0e49515
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-3.d
@@ -0,0 +1,21 @@
+#as: --gsframe
+#objdump: --sframe=.sframe
+#name: CFI_escape DW_CFA_GNU_args_size with FP-based CFA
+#...
+Contents of the SFrame section .sframe:
+
+ Header :
+
+ Version: SFRAME_VERSION_2
+ Flags: SFRAME_F_FDE_FUNC_START_PCREL
+#? CFA fixed FP offset: \-?\d+
+#? CFA fixed RA offset: \-?\d+
+ Num FDEs: 1
+ Num FREs: 1
+
+ Function Index :
+
+ func idx \[0\]: pc = 0x0, size = 0 bytes
+ STARTPC +CFA +FP +RA +
+ 0+0000 +fp\+8 +u +f +
+#pass
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-3.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-3.s
new file mode 100644
index 0000000..e28efef
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-3.s
@@ -0,0 +1,5 @@
+# DW_CFA_GNU_args_size is safe to skip, if CFA is FP based.
+ .cfi_startproc
+ .cfi_def_cfa_register 6
+ .cfi_escape 0x2e, 0x20
+ .cfi_endproc
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp b/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp
index 9380c98..7c93164 100644
--- a/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp
@@ -47,6 +47,7 @@ if { ([istarget "x86_64-*-*"] || [istarget "aarch64*-*-*"]
run_dump_test "cfi-sframe-common-9"
run_dump_test "cfi-sframe-common-10"
run_dump_test "cfi-sframe-common-11"
+ run_dump_test "cfi-sframe-common-12"
run_dump_test "common-empty-1"
run_dump_test "common-empty-2"
@@ -59,6 +60,7 @@ if { [istarget "x86_64-*-*"] && [gas_sframe_check] } then {
set ASFLAGS "$ASFLAGS --64"
run_dump_test "cfi-sframe-x86_64-1"
run_dump_test "cfi-sframe-x86_64-2"
+ run_dump_test "cfi-sframe-x86_64-3"
run_dump_test "cfi-sframe-x86_64-pr33170"
run_dump_test "cfi-sframe-x86_64-empty-1"
run_dump_test "cfi-sframe-x86_64-empty-2"