[PATCH v4 08/11] s390: Store SFrame CFA offset adjusted
Jens Remus
jremus@linux.ibm.com
Mon Jul 7 16:21:49 GMT 2025
In SFrame V2 the size of the offsets following an SFrame FRE can be
either signed 8-bit, 16-bit, or 32-bit integer, with 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.
Suggested-by: Indu Bhagat <indu.bhagat@oracle.com>
Signed-off-by: Jens Remus <jremus@linux.ibm.com>
---
Notes (jremus):
Changes in V4:
- Prefix internal helper sframe_fre_get_cfa_offset with "__" to resolve
clash with external SFrame API function. Issue surfaced, due to
recent fix of relocatable SFrame links including "sframe-api.h".
- Indent list of s390x-specific changes in SFrame spec.
- Fix typo in commit message ("with"). (Indu)
- Use comma after "i.e." in SFrame spec. (Indu)
- Use "revert" instead of "unapply" in SFrame spec. (Indu)
Changes in V3:
- Update "Changes from Version 1 to Version 2" in SFrame spec. (Indu)
- Regenerate statistics in patch notes below. Differences to V2 due
to change in min/max N-bit offset accounting (was unusual in V1/V2)
and use in newer GCC.
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,697 bytes
VALUE TOTAL MIN MAX AVG
FDEs: 3651 - - -
FREs/FDE: 15237 1 20 4
Offsets/FDE: 29795 1 38 8
8-bit: 0 0 0 0
16-bit: 29795 1 38 8
32-bit: 0 0 0 0
Offsets/FRE: 29795 1 3 1
8-bit: - 0 0 0
16-bit: - 1 3 1
32-bit: - 0 0 0
O_Padd/FDE: 345 - - 0
8-bit: 0
16-bit: 345
32-bit: 0
Statistics for libc.so - CFA offset adjustment:
.sframe size: 154,698 bytes
VALUE TOTAL MIN MAX AVG
FDEs: 3651 - - -
FREs/FDE: 15237 1 20 4
Offsets/FDE: 29795 1 38 8
8-bit: 14999 1 38 4
16-bit: 14796 0 21 4
32-bit: 0 0 0 0
Offsets/FRE: 29795 1 3 1
8-bit: - 1 3 0
16-bit: - 1 3 0
32-bit: - 0 0 0
O_Padd/FDE: 345 - - 0
8-bit: 286
16-bit: 59
32-bit: 0
bfd/elf64-s390.c | 4 ++--
gas/gen-sframe.c | 18 +++++++++++++++++-
include/sframe.h | 8 ++++++++
libsframe/doc/sframe-spec.texi | 16 +++++++++++++++-
libsframe/sframe.c | 10 ++++++++--
.../testsuite/libsframe.find/plt-findfre-2.c | 8 ++++----
6 files changed, 54 insertions(+), 10 deletions(-)
diff --git a/bfd/elf64-s390.c b/bfd/elf64-s390.c
index 491070da803b..eb850e932c9e 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 ab1dfcb49dd1..034dd2680b67 100644
--- a/gas/gen-sframe.c
+++ b/gas/gen-sframe.c
@@ -135,10 +135,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;
}
@@ -1043,7 +1059,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 28b625b3258f..db137393532e 100644
--- a/include/sframe.h
+++ b/include/sframe.h
@@ -376,6 +376,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 6ba6c131edb6..9814c975147d 100644
--- a/libsframe/doc/sframe-spec.texi
+++ b/libsframe/doc/sframe-spec.texi
@@ -155,6 +155,12 @@ an FP/RA offset.
@item SFRAME_FRE_RA_OFFSET_INVALID: Invalid RA offset value (like
SFRAME_CFA_FIXED_RA_INVALID). Used on s390x as padding offset to represent
FP without RA saved.
+ @item SFRAME_S390X_CFA_OFFSET_ADJUSTMENT: CFA offset (from CFA base register)
+adjustment value. Used to enable use of 8-bit SFrame offsets on s390x.
+ @item SFRAME_V2_FRE_S390X_CFA_OFFSET_ENCODE: Encode CFA offset (i.e., apply
+CFA offset adjustment).
+ @item SFRAME_V2_FRE_S390X_CFA_OFFSET_DECODE: Decode CFA offset (i.e., revert
+CFA offset adjustment).
@end itemize
@end itemize
@@ -885,7 +891,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 d8d370da90dd..cedfd0d59580 100644
--- a/libsframe/sframe.c
+++ b/libsframe/sframe.c
@@ -695,10 +695,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 3ccc259a4d1c..5049e221ca5d 100644
--- a/libsframe/testsuite/libsframe.find/plt-findfre-2.c
+++ b/libsframe/testsuite/libsframe.find/plt-findfre-2.c
@@ -43,8 +43,8 @@ add_plt0_fde (sframe_encoder_ctx *ectx, uint32_t plt_vaddr,
/* 1 single FRE. */
sframe_frame_row_entry fre
= { 0x0,
- { 0, 160 + PLT0_CFA_OFFSET_MAGIC },
- SFRAME_V1_FRE_INFO (SFRAME_BASE_REG_SP, 1, SFRAME_FRE_OFFSET_2B) };
+ { SFRAME_V2_FRE_S390X_CFA_OFFSET_ENCODE (160 + PLT0_CFA_OFFSET_MAGIC) },
+ 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);
@@ -72,8 +72,8 @@ add_pltn_fde (sframe_encoder_ctx *ectx, uint32_t plt_vaddr,
/* 1 single FRE. */
sframe_frame_row_entry fre
= { 0x0,
- { 0, 160 + PLTN_CFA_OFFSET_MAGIC },
- SFRAME_V1_FRE_INFO (SFRAME_BASE_REG_SP, 1, SFRAME_FRE_OFFSET_2B) };
+ { SFRAME_V2_FRE_S390X_CFA_OFFSET_ENCODE (160 + PLTN_CFA_OFFSET_MAGIC) },
+ 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);
--
2.48.1
More information about the Binutils
mailing list