[PATCH] gas: sframe: skip DW_CFA_GNU_args_size when safe to ignore
Sam James
sam@gentoo.org
Mon Sep 8 18:14:54 GMT 2025
Indu Bhagat via Binutils <binutils@sourceware.org> writes:
> 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 virtual 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.
I would personally include a link to that ML post/RFC from you, just for
reference in case anyone digs in future..
Anyway, yes please, but I can't approve it. This is one of two "known
warnings" that came up in the discussion with us and also Jan.
>
> gas/
> * 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/
> * 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.
> ---
> gas/gen-sframe.c | 62 ++++++++++++++++++-
> .../gas/cfi-sframe/cfi-sframe-common-12.d | 22 +++++++
> .../gas/cfi-sframe/cfi-sframe-common-12.s | 8 +++
> .../gas/cfi-sframe/cfi-sframe-x86_64-3.d | 21 +++++++
> .../gas/cfi-sframe/cfi-sframe-x86_64-3.s | 5 ++
> gas/testsuite/gas/cfi-sframe/cfi-sframe.exp | 2 +
> 6 files changed, 119 insertions(+), 1 deletion(-)
> create mode 100644 gas/testsuite/gas/cfi-sframe/cfi-sframe-common-12.d
> create mode 100644 gas/testsuite/gas/cfi-sframe/cfi-sframe-common-12.s
> create mode 100644 gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-3.d
> create mode 100644 gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-3.s
>
> diff --git a/gas/gen-sframe.c b/gas/gen-sframe.c
> index f5cf9bd8594..576c05a26a9 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 virtual
> + 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
> + when 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 00000000000..c91ce7b1011
> --- /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 00000000000..41af4e11b2e
> --- /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 00000000000..0e495159c01
> --- /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 00000000000..e28efefb236
> --- /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 9380c98fc3d..7c93164c4c6 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"
More information about the Binutils
mailing list