[PATCH v2 08/11] s390: Store SFrame CFA offset adjusted
Indu Bhagat
indu.bhagat@oracle.com
Mon Jun 2 20:46:29 GMT 2025
On 5/27/25 4:07 AM, Jens Remus wrote:
> In SFrame V2 the size of the offsets following an SFrame FRE can be
> either signed 8-bit, 16-bit, or 32-bit integer, which the largest offset
> determining their size:
> 1. CFA offset from CFA base register
> 2. RA (stack save slot) offset from CFA, usually -48 on s390x if saved
> 3. FP (stack save slot) offset from CFA, usually -72 on s390x if saved
> The FP and RA offsets from CFA, when FP/RA saved on the stack, usually
> have fixed values that fit into signed 8-bit SFrame offsets. Likewise
> the DWARF register numbers on s390x of general registers (GR; 0-15) and
> floating-point registers (FPR; 16-31), when FP/RA saved in registers.
> With that the CFA offset from CFA base register has the greatest impact
> on the signed SFrame offset size.
>
> The s390x ELF ABI [1] defines the CFA as stack pointer (SP) at call
> site +160. [2] Therefore the minimum CFA offset from CFA base register
> on s390x is 160. This does not fit into a signed 8-bit integer and
> therefore effectively prevents any use of signed 8-bit SFrame offsets
> on s390x.
>
> For s390x store the CFA offset from CFA base register adjusted by -160
> to enable the use of signed 8-bit SFrame offsets.
>
> [1]: s390x ELF ABI, https://github.com/IBM/s390x-abi/releases
> [2]: s390x ELF ABI, commit 4e38ad9c8a88 ("Document the CFA"),
> https://github.com/IBM/s390x-abi/commit/4e38ad9c8a88
>
> include/
> * sframe.h (SFRAME_S390X_CFA_OFFSET_ADJUSTMENT): Define
> s390x-specific CFA offset adjustment.
> (SFRAME_V2_FRE_S390X_CFA_OFFSET_ENCODE,
> SFRAME_V2_FRE_S390X_CFA_OFFSET_DECODE): New s390x-specific
> macros. Use SFRAME_S390X_CFA_OFFSET_ADJUSTMENT to en-/decode
> CFA offset.
>
> bfd/
> * elf64-s390.c (elf_s390x_sframe_plt_fre): Use
> SFRAME_V2_FRE_S390X_CFA_OFFSET_ENCODE on CFA offset to store it
> adjusted and switch to 8-bit offsets.
>
> gas/
> * gen-sframe.c (sframe_fre_set_cfa_offset): For s390x use
> SFRAME_V2_FRE_S390X_CFA_OFFSET_ENCODE on CFA offset to store it
> adjusted.
> (sframe_fre_get_cfa_offset): New helper. For s390x use
> SFRAME_V2_FRE_S390X_CFA_OFFSET_DECODE on CFA offset to undo its
> adjustment.
> (sframe_xlate_do_def_cfa_register): Use new helper
> sframe_fre_get_cfa_offset.
>
> libsframe/
> * sframe.c (sframe_fre_get_cfa_offset): For s390x use
> SFRAME_V2_FRE_S390X_CFA_OFFSET_DECODE on CFA offset to undo its
> adjustment.
> * doc/sframe-spec.texi (s390x,
> SFRAME_S390X_CFA_OFFSET_ADJUSTMENT,
> SFRAME_V2_FRE_S390X_CFA_OFFSET_ENCODE,
> SFRAME_V2_FRE_S390X_CFA_OFFSET_DECODE): Document s390x-specific
> adjustment of CFA offset.
>
> libsframe/testsuite/
> * libsframe.find/plt-findfre-2.c (add_plt0_fde, add_pltn_fde):
> Use SFRAME_V2_FRE_S390X_CFA_OFFSET_ENCODE to enable use of
> 1-byte SFrame offsets.
>
SFrame specific implementation looks good to me. One question below.
It will be good to double check in case we have overlooked something.
> Suggested-by: Indu Bhagat <indu.bhagat@oracle.com>
> Signed-off-by: Jens Remus <jremus@linux.ibm.com>
Reviewed-by: Indu Bhagat <indu.bhagat@oracle.com>
> ---
>
> Notes (jremus):
> Changes in V2:
> - Reword commit message (s390 to s390x, offsets, FDE to FRE). (Indu)
> - Rename s390x-specific macros from *_S390_* to *_S390X_*. (Indu)
> - Use "s390x" instead of "S390" in comments and SFrame spec. (Indu)
> - Change order of CFA restore instructions in SFrame spec. (Indu)
> - Update new libsframe test plt-findfre-2.
>
> A test build of Glibc tag 2.41 on s390x libc.so shows a ~8.8% reduction in
> .sframe section size due to storing the SFrame CFA offsets adjusted by
> -160.
>
> Statistics for libc.so - base (no CFA offset adjustment):
>
> .sframe size: 169,749 bytes
>
> VALUE TOTAL MIN MAX AVG
> FDEs: 3652 - - -
> FREs/FDE: 15236 1 20 4
> Offsets/FDE: 29792 1 38 8
> 8-bit: 0 0 0 0
> 16-bit: 29792 1 38 8
> 32-bit: 0 0 0 0
> Offsets/FRE: 29792 1 3 1
> 8-bit: - 0 0 0
> 16-bit: - 1 3 1
> 32-bit: - 0 0 0
> O_Padd/FDE: 342 - - 0
> 8-bit: 0
> 16-bit: 342
> 32-bit: 0
>
> Statistics for libc.so - CFA offset adjustment:
>
> .sframe size: 154,757 bytes
>
> VALUE TOTAL MIN MAX AVG
> FDEs: 3654 - - -
Hmm, why would the number of FDEs shift (3652 above vs 3654 now) with
this sort of experiment.
> FREs/FDE: 15238 1 20 4
> Offsets/FDE: 29794 1 38 8
> 8-bit: 14992 1 38 4
> 16-bit: 14802 0 0 4
> 32-bit: 0 0 0 0
> Offsets/FRE: 29794 2 6 1
> 8-bit: - 1 3 0
> 16-bit: - 1 3 0
> 32-bit: - 0 0 0
> O_Padd/FDE: 342 - - 0
> 8-bit: 283
> 16-bit: 59
> 32-bit: 0
>
> Changes in V2:
> - New patch. (Indu)
>
> Kept this as a separate patch, as it does not depend on nor test the
> linker changes introduced with "s390: Add SFrame stack trace information
> for .plt section", although closely related.
>
> bfd/elf64-s390.c | 4 ++--
> gas/gen-sframe.c | 18 +++++++++++++++++-
> include/sframe.h | 8 ++++++++
> libsframe/doc/sframe-spec.texi | 10 +++++++++-
> libsframe/sframe.c | 10 ++++++++--
> .../testsuite/libsframe.find/plt-findfre-2.c | 8 ++++----
> 6 files changed, 48 insertions(+), 10 deletions(-)
>
> diff --git a/bfd/elf64-s390.c b/bfd/elf64-s390.c
> index bd4ffcc9d13a..7e9dc24f1c6c 100644
> --- a/bfd/elf64-s390.c
> +++ b/bfd/elf64-s390.c
> @@ -621,8 +621,8 @@ struct elf_s390x_sframe_plt
> static const sframe_frame_row_entry elf_s390x_sframe_plt_fre =
> {
> 0, /* SFrame FRE start address. */
> - {0, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* 12 bytes. */
> - SFRAME_V1_FRE_INFO (SFRAME_BASE_REG_SP, 1, SFRAME_FRE_OFFSET_2B) /* FRE info. */
> + { SFRAME_V2_FRE_S390X_CFA_OFFSET_ENCODE (160), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* Offset bytes. */
> + SFRAME_V1_FRE_INFO (SFRAME_BASE_REG_SP, 1, SFRAME_FRE_OFFSET_1B) /* FRE info. */
> };
>
> /* SFrame helper object for PLT. */
> diff --git a/gas/gen-sframe.c b/gas/gen-sframe.c
> index 3c7db6cf458e..1d61471176a4 100644
> --- a/gas/gen-sframe.c
> +++ b/gas/gen-sframe.c
> @@ -134,10 +134,26 @@ sframe_fre_set_cfa_base_reg (struct sframe_row_entry *fre,
> fre->merge_candidate = false;
> }
>
> +static offsetT
> +sframe_fre_get_cfa_offset (const struct sframe_row_entry * fre)
> +{
> + offsetT offset = fre->cfa_offset;
> +
> + /* For s390x undo adjustment of CFA offset (to enable 8-bit offsets). */
> + if (sframe_get_abi_arch () == SFRAME_ABI_S390X_ENDIAN_BIG)
> + offset = SFRAME_V2_FRE_S390X_CFA_OFFSET_DECODE (offset);
> +
> + return offset;
> +}
> +
> static void
> sframe_fre_set_cfa_offset (struct sframe_row_entry *fre,
> offsetT cfa_offset)
> {
> + /* For s390x adjust CFA offset to enable 8-bit offsets. */
> + if (sframe_get_abi_arch () == SFRAME_ABI_S390X_ENDIAN_BIG)
> + cfa_offset = SFRAME_V2_FRE_S390X_CFA_OFFSET_ENCODE (cfa_offset);
> +
> fre->cfa_offset = cfa_offset;
> fre->merge_candidate = false;
> }
> @@ -1036,7 +1052,7 @@ sframe_xlate_do_def_cfa_register (struct sframe_xlate_ctx *xlate_ctx,
> }
> sframe_fre_set_cfa_base_reg (cur_fre, cfi_insn->u.r);
> if (last_fre)
> - sframe_fre_set_cfa_offset (cur_fre, last_fre->cfa_offset);
> + sframe_fre_set_cfa_offset (cur_fre, sframe_fre_get_cfa_offset (last_fre));
>
> cur_fre->merge_candidate = false;
>
> diff --git a/include/sframe.h b/include/sframe.h
> index 5ad007f6ccb0..43cebc1b49cc 100644
> --- a/include/sframe.h
> +++ b/include/sframe.h
> @@ -364,6 +364,14 @@ typedef struct sframe_frame_row_entry_addr4
> #define SFRAME_FRE_TYPE_ADDR4_LIMIT \
> (1ULL << ((SFRAME_FRE_TYPE_ADDR4 * 2) * 8))
>
> +/* On s390x, the CFA offset from CFA base register is by definition a minimum
> + of 160. Store it adjusted by -160 to enable use of 8-bit SFrame offsets. */
> +#define SFRAME_S390X_CFA_OFFSET_ADJUSTMENT SFRAME_S390X_SP_VAL_OFFSET
> +#define SFRAME_V2_FRE_S390X_CFA_OFFSET_ENCODE(offset) \
> + ((offset) + SFRAME_S390X_CFA_OFFSET_ADJUSTMENT)
> +#define SFRAME_V2_FRE_S390X_CFA_OFFSET_DECODE(offset) \
> + ((offset) - SFRAME_S390X_CFA_OFFSET_ADJUSTMENT)
> +
> /* On s390x, the CFA is defined as SP at call site + 160. Therefore the
> SP value offset from CFA is -160. */
> #define SFRAME_S390X_SP_VAL_OFFSET (-160)
> diff --git a/libsframe/doc/sframe-spec.texi b/libsframe/doc/sframe-spec.texi
> index f42a9136b76e..8497619de427 100644
> --- a/libsframe/doc/sframe-spec.texi
> +++ b/libsframe/doc/sframe-spec.texi
> @@ -845,7 +845,15 @@ case in the topmost stack frame of the callchain. For FP this may be the case
> in any stack frame.
>
> Irrespective of the ABI, the first stack offset is always used to locate the
> -CFA, by interpreting it as: CFA = @code{BASE_REG} + offset1.
> +CFA. On s390x the value of the offset is stored adjusted by the s390x-specific
> +@code{SFRAME_S390X_CFA_OFFSET_ADJUSTMENT} to enable the use of signed 8-bit
> +offsets on s390x.
> +s390x-specific helpers @code{SFRAME_V2_FRE_S390X_CFA_OFFSET_ENCODE} and
> +@code{SFRAME_V2_FRE_S390X_CFA_OFFSET_DECODE} are provided to perform and undo
> +the adjustment. The CFA offset can therefore be interpreted as:
> +CFA = @code{BASE_REG} + @code{SFRAME_V2_FRE_S390X_CFA_OFFSET_DECODE(offset1)}
> +or
> +CFA = @code{BASE_REG} + offset1 - @code{SFRAME_S390X_CFA_OFFSET_ADJUSTMENT}.
> The identification of the @code{BASE_REG} is done by using the
> @code{fre_cfa_base_reg_id} field in the SFrame FRE info word.
>
> diff --git a/libsframe/sframe.c b/libsframe/sframe.c
> index 712551b0ed1a..2e0397e87b67 100644
> --- a/libsframe/sframe.c
> +++ b/libsframe/sframe.c
> @@ -673,10 +673,16 @@ sframe_fre_get_base_reg_id (sframe_frame_row_entry *fre, int *errp)
> /* Get the CFA offset from the FRE. If the offset is invalid, sets errp. */
>
> int32_t
> -sframe_fre_get_cfa_offset (sframe_decoder_ctx *dctx ATTRIBUTE_UNUSED,
> +sframe_fre_get_cfa_offset (sframe_decoder_ctx *dctx,
> sframe_frame_row_entry *fre, int *errp)
> {
> - return sframe_get_fre_offset (fre, SFRAME_FRE_CFA_OFFSET_IDX, errp);
> + int32_t offset = sframe_get_fre_offset (fre, SFRAME_FRE_CFA_OFFSET_IDX, errp);
> +
> + /* For s390x undo adjustment of CFA offset (to enable 8-bit offsets). */
> + if (sframe_decoder_get_abi_arch (dctx) == SFRAME_ABI_S390X_ENDIAN_BIG)
> + offset = SFRAME_V2_FRE_S390X_CFA_OFFSET_DECODE (offset);
> +
> + return offset;
> }
>
> /* Get the FP offset from the FRE. If the offset is invalid, sets errp.
> diff --git a/libsframe/testsuite/libsframe.find/plt-findfre-2.c b/libsframe/testsuite/libsframe.find/plt-findfre-2.c
> index ef9cc6bc7ed7..df5a1f84cb3c 100644
> --- a/libsframe/testsuite/libsframe.find/plt-findfre-2.c
> +++ b/libsframe/testsuite/libsframe.find/plt-findfre-2.c
> @@ -35,8 +35,8 @@ add_plt0_fde (sframe_encoder_ctx *ectx, int idx)
> /* 1 FRE. The start_ip_offset must remain less than 32 bytes. */
> sframe_frame_row_entry fre
> = { 0x0,
> - { 0, 160 },
> - SFRAME_V1_FRE_INFO (SFRAME_BASE_REG_SP, 1, SFRAME_FRE_OFFSET_2B) };
> + { SFRAME_V2_FRE_S390X_CFA_OFFSET_ENCODE (160) },
> + SFRAME_V1_FRE_INFO (SFRAME_BASE_REG_SP, 1, SFRAME_FRE_OFFSET_1B) };
>
> unsigned char finfo = sframe_fde_create_func_info (SFRAME_FRE_TYPE_ADDR1,
> SFRAME_FDE_TYPE_PCINC);
> @@ -58,8 +58,8 @@ add_pltn_fde (sframe_encoder_ctx *ectx, int idx)
> /* 1 FRE. The start_ip_offset must remain less than 32 bytes. */
> sframe_frame_row_entry fre
> = { 0x0,
> - { 0, 160+8 },
> - SFRAME_V1_FRE_INFO (SFRAME_BASE_REG_SP, 1, SFRAME_FRE_OFFSET_2B) };
> + { SFRAME_V2_FRE_S390X_CFA_OFFSET_ENCODE (160+8) },
> + SFRAME_V1_FRE_INFO (SFRAME_BASE_REG_SP, 1, SFRAME_FRE_OFFSET_1B) };
>
> unsigned char finfo = sframe_fde_create_func_info (SFRAME_FRE_TYPE_ADDR1,
> SFRAME_FDE_TYPE_PCMASK);
More information about the Binutils
mailing list