[PATCH 4/4] gas: sframe: handle .cfi_same_value

Jens Remus jremus@linux.ibm.com
Mon May 12 10:10:34 GMT 2025
Hello Indu!

On 11.05.2025 09:35, Indu Bhagat wrote:
> Fix PR gas/32953 - sframe: incorrect handling of .cfi_same_value in gas
> 
> As per documentation, .cfi_same_value indicates that: Current value of
> register is the same like in the previous frame, i.e. no restoration
> needed.  SFrame has no means to encode this information.

If no restoration is needed, then that would be as if the tracked FP/RA
register was not saved on the stack.  This is currently represented in
SFrame by FREs without stack offsets from CFA for the respective
register.  The SP register is special, as it is restored using the
implied default CFA rule from the CFA.

For instance on S390 the FP and RA registers are not saved on function
entry and may not be be saved by the prologue at all if in a leaf
function.  This is represented in SFrame by FREs without the respective
FP/RA offsets.  The SP at function entry is equal to the SP at call
site.  The s390x ELF ABI defines all call saved and reserved registers
as well as the RA register to have the "same value" rule by default on
function entry. [1]


>From DWARF V5 [2]:

  "DW_CFA_same_value
  ... The required action is to set the rule for the specified
  register to "same value."

  DW_CFA_restore
  ... The required action is to change the rule for the indicated
  register to the rule assigned it by the initial_instructions in
  the CIE.

  The register rules are:

  same value
  This register has not been modified from the previous frame. (By
  convention, it is preserved by the callee, but the callee has not
  modified it.)"


Shouldn't SFrame therefore represent .cfi_same_value and .cfa_restore as
follows?

.cfi_same_value <FP/RA>
Reset the FP/RA save information to "unsaved".  That is
{bp|ra}_loc = SFRAME_FRE_ELEM_LOC_REG and bp_offset = 0.

.cfi_same_value <SP>
May require architecture/ABI dependent handling.  On x86-64 ARM64 this
should probably cause the FDE to be skipped, as SP at function entry is
not equal to SP at call site (due to the call instruction).  On S390 it
should be safe to ignore, as the SP at function entry is equal to the
SP at call site.

.cfi_restore <FP/RA>
Reset the FP/RA save information to the one of the FDE FRE[0], as it is
already done by sframe_xlate_do_restore.

.cfi_restore <SP>
Should be safe to ignore, as it is already done by
sframe_xlate_do_restore.


[1]: s390x ELF ABI, subsection "Call Frame Information", commit 963953c
     ("Add information about DWARF CFI default rules"),
     https://github.com/IBM/s390x-abi/commit/963953c

[2]: DWARF V5, https://dwarfstd.org/dwarf5std.html

> 
> Skip generating FDE, but warn if it is for a register of interest.
> 
> gas/
> 	* gen-sframe.c (sframe_xlate_do_same_value): New definition.
> 	(sframe_do_cfi_insn): Handle DW_CFA_same_value.
> gas/testsuite/
> 	* gas/cfi-sframe/cfi-sframe.exp: Add new tests.
> 	* gas/cfi-sframe/cfi-sframe-common-11.d: New test.
> 	* gas/cfi-sframe/cfi-sframe-common-11.s: New test.
> 	* gas/cfi-sframe/cfi-sframe-x86_64-empty-5.d: New test.
> 	* gas/cfi-sframe/cfi-sframe-x86_64-empty-5.s: New test.
> ---
>  gas/gen-sframe.c                              | 30 +++++++++++++++++--
>  .../gas/cfi-sframe/cfi-sframe-common-11.d     | 22 ++++++++++++++
>  .../gas/cfi-sframe/cfi-sframe-common-11.s     | 12 ++++++++
>  .../cfi-sframe/cfi-sframe-x86_64-empty-5.d    | 17 +++++++++++
>  .../cfi-sframe/cfi-sframe-x86_64-empty-5.s    |  6 ++++
>  gas/testsuite/gas/cfi-sframe/cfi-sframe.exp   |  2 ++
>  6 files changed, 86 insertions(+), 3 deletions(-)
>  create mode 100644 gas/testsuite/gas/cfi-sframe/cfi-sframe-common-11.d
>  create mode 100644 gas/testsuite/gas/cfi-sframe/cfi-sframe-common-11.s
>  create mode 100644 gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-empty-5.d
>  create mode 100644 gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-empty-5.s
> 
> diff --git a/gas/gen-sframe.c b/gas/gen-sframe.c
> index a87d464fd5f..f989627193f 100644
> --- a/gas/gen-sframe.c
> +++ b/gas/gen-sframe.c
> @@ -1533,6 +1533,32 @@ sframe_xlate_do_cfi_undefined (const struct sframe_xlate_ctx *xlate_ctx ATTRIBUT
>    return SFRAME_XLATE_OK;
>  }
>  
> +/* Translate DW_CFA_same_value into SFrame context.
> +
> +   DW_CFA_undefined op implies that current value of register is the same like
> +   in the previous frame, i.e. no restoration needed.  In SFrame stack trace,
> +   we cannot represent such a semantic.  So, we skip generating an SFrame FDE
> +   for this, when a register of interest is used with DW_CFA_same_value.
> +
> +   Return SFRAME_XLATE_OK if success.  */
> +
> +static int
> +sframe_xlate_do_same_value (const struct sframe_xlate_ctx *xlate_ctx ATTRIBUTE_UNUSED,
> +			    const struct cfi_insn_data *cfi_insn)
> +{
> +  if (cfi_insn->u.ri.reg == SFRAME_CFA_FP_REG
> +      || (sframe_ra_tracking_p () && cfi_insn->u.ri.reg == SFRAME_CFA_RA_REG)
> +      || cfi_insn->u.ri.reg == SFRAME_CFA_SP_REG)
> +    {
> +      as_warn (_("skipping SFrame FDE; %s reg %u in .cfi_same_value"),
> +	       sframe_register_name (cfi_insn->u.ri.reg), cfi_insn->u.ri.reg);

Same as in previous patch: cfi_insn->u.r

> +      return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented.  */
> +    }
> +
> +  /* Safe to skip.  */
> +  return SFRAME_XLATE_OK;
> +}
> +
>  /* Returns the DWARF call frame instruction name or fake CFI name for the
>     specified CFI opcode, or NULL if the value is not recognized.  */
>  
> @@ -1632,13 +1658,11 @@ sframe_do_cfi_insn (struct sframe_xlate_ctx *xlate_ctx,
>      case CFI_escape:
>        err = sframe_xlate_do_cfi_escape (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.  */
>      case DW_CFA_undefined:
>        err = sframe_xlate_do_cfi_undefined (xlate_ctx, cfi_insn);
>        break;
>      case DW_CFA_same_value:
> +      err = sframe_xlate_do_same_value (xlate_ctx, cfi_insn);
>        break;
>      default:
>        /* Other skipped operations may, however, impact the asynchronicity.  */
> diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-11.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-11.d
> new file mode 100644
> index 00000000000..25848150135
> --- /dev/null
> +++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-11.d
> @@ -0,0 +1,22 @@
> +#as: --gsframe
> +#objdump: --sframe=.sframe
> +#name: SFrame cfi_same_value 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-11.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-11.s
> new file mode 100644
> index 00000000000..e299f586cf2
> --- /dev/null
> +++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-11.s
> @@ -0,0 +1,12 @@
> +## cfi_same_value when used with "not interesting" registers (from the
> +## perspective of SFrame section, non SP/FP/RA registers are not
> +## interesting) does not affect the asynchronicity of the SFrame stack
> +## trace information.  Such CFI directives can be skipped for SFrame
> +## stack trace info generation.
> +	.cfi_startproc
> +	.long 0
> +	.cfi_def_cfa_offset 16
> +	.cfi_same_value 1
> +	.cfi_same_value 2
> +	.long 0
> +	.cfi_endproc
> diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-empty-5.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-empty-5.d
> new file mode 100644
> index 00000000000..24d944a8757
> --- /dev/null
> +++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-empty-5.d
> @@ -0,0 +1,17 @@
> +#as: --gsframe
> +#warning: skipping SFrame FDE; FP reg 6 in \.cfi\_same\_value
> +#objdump: --sframe=.sframe
> +#name: CFI_escape with multiple DWARF expr

You missed to update the test name. :-)

> +#...
> +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-5.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-empty-5.s
> new file mode 100644
> index 00000000000..89b13b2028c
> --- /dev/null
> +++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-x86_64-empty-5.s
> @@ -0,0 +1,6 @@
> +	.cfi_startproc
> +	.long 0
> +	.cfi_def_cfa_offset 16
> +	.cfi_same_value 0x6
> +	.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 ad5602fc373..8ca22d3c652 100644
> --- a/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp
> +++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp
> @@ -80,6 +80,7 @@ if  { ([istarget "x86_64-*-*"] || [istarget "aarch64*-*-*"]) \
>      run_dump_test "cfi-sframe-common-8"
>      run_dump_test "cfi-sframe-common-9"
>      run_dump_test "cfi-sframe-common-10"
> +    run_dump_test "cfi-sframe-common-11"
>  
>      run_dump_test "common-empty-1"
>      run_dump_test "common-empty-2"
> @@ -96,6 +97,7 @@ if { [istarget "x86_64-*-*"] && [gas_sframe_check] } then {
>  	run_dump_test "cfi-sframe-x86_64-empty-2"
>  	run_dump_test "cfi-sframe-x86_64-empty-3"
>  	run_dump_test "cfi-sframe-x86_64-empty-4"
> +	run_dump_test "cfi-sframe-x86_64-empty-5"
>  	set ASFLAGS "$old_ASFLAGS"
>      }
>  }

Thanks and regards,
Jens
-- 
Jens Remus
Linux on Z Development (D3303)
+49-7031-16-1128 Office
jremus@de.ibm.com

IBM

IBM Deutschland Research & Development GmbH; Vorsitzender des
Aufsichtsrats: Wolfgang Wendt; Geschäftsführung: David Faller; Sitz der
Gesellschaft: Böblingen; Registergericht: Amtsgericht Stuttgart, HRB 243294
IBM Data Privacy Statement: https://www.ibm.com/privacy/



More information about the Binutils mailing list