[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