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.
This commit is contained in:
Indu Bhagat
2025-09-05 15:01:52 -07:00
parent 2fe19b88c9
commit 84c1e5cec0
6 changed files with 119 additions and 1 deletions

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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"