[PATCH 5/8] s390: Represent FP without RA saved in SFrame
Jens Remus
jremus@linux.ibm.com
Wed Apr 2 16:12:00 GMT 2025
If an architecture uses both SFrame RA and FP tracking SFrame assumes
that the RA offset is the 2nd offset and the FP offset is the 3rd offset
following a SFrame FRE. An architecture does not necessarily need to
save both on the stack (or in register) at the same time or even at all.
SFrame cannot represent FP without RA saved on stack (or in a register),
since it cannot distinguish whether the 2nd offset is the RA or FP
offset.
For S390 use an invalid SFrame RA offset from CFA value of zero as
padding to represent the FP being saved when the RA is not saved. This
aligns with the existing invalid SFrame fixed RA offset from CFA value
of zero. In a stack tracer this then also naturally falls into place,
as it can skip restoring the RA in the topmost frame, if both the fixed
RA offset (from SFrame header) and the RA offset (from FDE) are zero,
without any need to test architecture-specific flags.
include/
* sframe.h (SFRAME_FRE_RA_OFFSET_INVALID): New define. Used as
padding offset.
gas/
* gen-sframe.c (get_fre_num_offsets): For S390 account padding
RA offset, if FP without RA saved.
(sframe_get_fre_offset_size): Likewise.
(output_sframe_row_entry): For S390 write a padding RA offset,
if FP without RA needs to be represented.
(sframe_do_fde): Enable FP without RA saved to be represented
on S390.
libsframe/
* sframe-dump.c (dump_sframe_func_with_fres): Treat invalid
RA offsets as if they were undefined. Display them as "u*"
to distinguish them.
* doc/sframe-spec.texi (S390): Document S390-specific use of
SFRAME_FRE_RA_OFFSET_INVALID to represent FP without RA saved.
gas/testsuite/
* gas/cfi-sframe/cfi-sframe.exp: Rename s390x-specific tests.
* gas/cfi-sframe/cfi-sframe-s390-fpra-offset-err-1.s: Rename
to ...
* cfi-sframe/cfi-sframe-s390-fpra-offset-err-1.d: Likewise.
* gas/cfi-sframe/cfi-sframe-s390-fpra-offset-2.s: This.
* gas/cfi-sframe/cfi-sframe-s390-fpra-offset-2.d: Likewise.
Update test verification pattern accordingly.
* cfi-sframe/cfi-sframe-s390-fpra-register-err-1.s: Rename
to ...
* cfi-sframe/cfi-sframe-s390-fpra-register-err-1.d: Likewise.
* gas/cfi-sframe/cfi-sframe-s390-fpra-register-2.s: This.
* gas/cfi-sframe/cfi-sframe-s390-fpra-register-2.d: Likewise.
Update test verification pattern accordingly.
Signed-off-by: Jens Remus <jremus@linux.ibm.com>
---
Notes (jremus):
An alternative approach would have been to redefine the 4-bit offset
count field in the FDE info word into a bit field as proposed with
"[RFC PATCH 0/1] sframe: Represent FP without RA on stack (bitmap)" [1].
With the current 4-bit count up to 15+1 offsets can be represented (+1
if the CFA offset would be considered mandatory, so that it does not
need to be explicitly accounted). With a 4 bit bit field only up to
4+1 offsets could be represented.
[1]: Path series "[RFC PATCH 0/1] sframe: Represent FP without RA on
stack (bitmap)",
https://inbox.sourceware.org/binutils/20240422155905.2497883-1-jremus@linux.ibm.com/
A test build of Glibc tag 2.41 on s390x libc.so shows 342 padding
RA offsets. With a total of 29792 offsets the padding RA offsets
account for only ~1.15% of the offsets, which seems acceptable.
Statistics for libc.so:
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
Note that on s390x the offsets are at minimum 16-bits in size, due to
the mandatory CFA offset being at least 160, which does not fit into
an 8-bit signed integer. This is addressed by a following patch.
gas/gen-sframe.c | 38 ++++++++++++++-----
.../cfi-sframe-s390-fpra-offset-2.d | 22 +++++++++++
...rr-1.s => cfi-sframe-s390-fpra-offset-2.s} | 0
.../cfi-sframe-s390-fpra-offset-err-1.d | 15 --------
.../cfi-sframe-s390-fpra-register-2.d | 22 +++++++++++
...-1.s => cfi-sframe-s390-fpra-register-2.s} | 0
.../cfi-sframe-s390-fpra-register-err-1.d | 15 --------
gas/testsuite/gas/cfi-sframe/cfi-sframe.exp | 4 +-
include/sframe.h | 21 ++++++++++
libsframe/doc/sframe-spec.texi | 8 +++-
libsframe/sframe-dump.c | 4 ++
11 files changed, 106 insertions(+), 43 deletions(-)
create mode 100644 gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-fpra-offset-2.d
rename gas/testsuite/gas/cfi-sframe/{cfi-sframe-s390-fpra-offset-err-1.s => cfi-sframe-s390-fpra-offset-2.s} (100%)
delete mode 100644 gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-fpra-offset-err-1.d
create mode 100644 gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-fpra-register-2.d
rename gas/testsuite/gas/cfi-sframe/{cfi-sframe-s390-fpra-register-err-1.s => cfi-sframe-s390-fpra-register-2.s} (100%)
delete mode 100644 gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-fpra-register-err-1.d
diff --git a/gas/gen-sframe.c b/gas/gen-sframe.c
index fb3a0342b1ee..a1823ec15738 100644
--- a/gas/gen-sframe.c
+++ b/gas/gen-sframe.c
@@ -354,7 +354,10 @@ get_fre_num_offsets (struct sframe_row_entry *sframe_fre)
fre_num_offsets++;
#ifdef SFRAME_FRE_RA_TRACKING
if (sframe_ra_tracking_p ()
- && sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK)
+ && (sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK
+ /* For S390 account padding RA offset, if FP without RA saved. */
+ || (sframe_get_abi_arch () == SFRAME_ABI_S390_ENDIAN_BIG
+ && sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK)))
fre_num_offsets++;
#endif
return fre_num_offsets;
@@ -378,9 +381,15 @@ sframe_get_fre_offset_size (struct sframe_row_entry *sframe_fre)
if (sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK)
bp_offset_size = get_offset_size_in_bytes (sframe_fre->bp_offset);
#ifdef SFRAME_FRE_RA_TRACKING
- if (sframe_ra_tracking_p ()
- && sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK)
- ra_offset_size = get_offset_size_in_bytes (sframe_fre->ra_offset);
+ if (sframe_ra_tracking_p ())
+ {
+ if (sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK)
+ ra_offset_size = get_offset_size_in_bytes (sframe_fre->ra_offset);
+ /* For S390 account padding RA offset, if FP without RA saved. */
+ else if (sframe_get_abi_arch () == SFRAME_ABI_S390_ENDIAN_BIG
+ && sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK)
+ ra_offset_size = get_offset_size_in_bytes (SFRAME_FRE_RA_OFFSET_INVALID);
+ }
#endif
/* Get the maximum size needed to represent the offsets. */
@@ -588,11 +597,20 @@ output_sframe_row_entry (symbolS *fde_start_addr,
fre_write_offsets++;
#ifdef SFRAME_FRE_RA_TRACKING
- if (sframe_ra_tracking_p ()
- && sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK)
+ if (sframe_ra_tracking_p ())
{
- fre_offset_func_map[idx].out_func (sframe_fre->ra_offset);
- fre_write_offsets++;
+ if (sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK)
+ {
+ fre_offset_func_map[idx].out_func (sframe_fre->ra_offset);
+ fre_write_offsets++;
+ }
+ /* For S390 write padding RA offset, if FP without RA saved. */
+ else if (sframe_get_abi_arch () == SFRAME_ABI_S390_ENDIAN_BIG
+ && sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK)
+ {
+ fre_offset_func_map[idx].out_func (SFRAME_FRE_RA_OFFSET_INVALID);
+ fre_write_offsets++;
+ }
}
#endif
if (sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK)
@@ -1747,7 +1765,9 @@ sframe_do_fde (struct sframe_xlate_ctx *xlate_ctx,
}
#ifdef SFRAME_FRE_RA_TRACKING
- if (sframe_ra_tracking_p ())
+ /* ABI/arch except S390 cannot represent FP without RA saved. */
+ if (sframe_ra_tracking_p ()
+ && sframe_get_abi_arch () != SFRAME_ABI_S390_ENDIAN_BIG)
{
struct sframe_row_entry *fre;
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-fpra-offset-2.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-fpra-offset-2.d
new file mode 100644
index 000000000000..4a1462984cca
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-fpra-offset-2.d
@@ -0,0 +1,22 @@
+#objdump: --sframe=.sframe
+#name: SFrame generation on s390 - FP and RA saved on stack
+#...
+Contents of the SFrame section .sframe:
+
+ Header :
+
+ Version: SFRAME_VERSION_2
+ Flags: NONE
+ Num FDEs: 1
+ Num FREs: 5
+
+ Function Index :
+
+ func idx \[0\]: pc = 0x0, size = 34 bytes
+ STARTPC +CFA +FP +RA +
+ 0+0000 +sp\+160 +u +u +
+ 0+0006 +sp\+160 +c\-72 +u\* +
+ 0+000c +sp\+160 +c\-72 +c\-48 +
+ 0+001a +sp\+160 +c-72 +u\* +
+ 0+0020 +sp\+160 +u +u +
+#pass
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-fpra-offset-err-1.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-fpra-offset-2.s
similarity index 100%
rename from gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-fpra-offset-err-1.s
rename to gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-fpra-offset-2.s
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-fpra-offset-err-1.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-fpra-offset-err-1.d
deleted file mode 100644
index 468a150bad26..000000000000
--- a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-fpra-offset-err-1.d
+++ /dev/null
@@ -1,15 +0,0 @@
-#name: SFrame generation on s390 - FP without RA saved on stack
-#as: --gsframe
-#warning: skipping SFrame FDE; FP without RA on stack
-#objdump: --sframe=.sframe
-#...
-Contents of the SFrame section .sframe:
-
- Header :
-
- Version: SFRAME_VERSION_2
- Flags: NONE
- Num FDEs: 0
- Num FREs: 0
-
-#pass
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-fpra-register-2.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-fpra-register-2.d
new file mode 100644
index 000000000000..ee9f25c9aa84
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-fpra-register-2.d
@@ -0,0 +1,22 @@
+#objdump: --sframe=.sframe
+#name: SFrame generation on s390 - FP and RA registers saved in FPR registers
+#...
+Contents of the SFrame section .sframe:
+
+ Header :
+
+ Version: SFRAME_VERSION_2
+ Flags: NONE
+ Num FDEs: 1
+ Num FREs: 5
+
+ Function Index :
+
+ func idx \[0\]: pc = 0x0, size = 26 bytes
+ STARTPC +CFA +FP +RA +
+ 0+0000 +sp\+160 +u +u +
+ 0+0004 +sp\+160 +r17 +u\* +
+ 0+0008 +sp\+160 +r17 +r16 +
+ 0+0014 +sp\+160 +r17 +u\* +
+ 0+0018 +sp\+160 +u +u +
+#pass
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-fpra-register-err-1.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-fpra-register-2.s
similarity index 100%
rename from gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-fpra-register-err-1.s
rename to gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-fpra-register-2.s
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-fpra-register-err-1.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-fpra-register-err-1.d
deleted file mode 100644
index 6baaf59271b1..000000000000
--- a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-fpra-register-err-1.d
+++ /dev/null
@@ -1,15 +0,0 @@
-#name: SFrame generation on s390 - FP without RA saved in register
-#as: --gsframe
-#warning: skipping SFrame FDE; FP without RA on stack
-#objdump: --sframe=.sframe
-#...
-Contents of the SFrame section .sframe:
-
- Header :
-
- Version: SFRAME_VERSION_2
- Flags: NONE
- Num FDEs: 0
- Num FREs: 0
-
-#pass
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp b/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp
index 13b2e236bb46..2d1b6dcc889f 100644
--- a/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp
@@ -115,7 +115,7 @@ if { [istarget "s390x*-*-*"] && [gas_sframe_check] } then {
run_dump_test "cfi-sframe-s390-err-2"
run_dump_test "cfi-sframe-s390-err-3"
run_dump_test "cfi-sframe-s390-fpra-offset-1"
- run_dump_test "cfi-sframe-s390-fpra-offset-err-1"
+ run_dump_test "cfi-sframe-s390-fpra-offset-2"
run_dump_test "cfi-sframe-s390-fpra-register-1"
- run_dump_test "cfi-sframe-s390-fpra-register-err-1"
+ run_dump_test "cfi-sframe-s390-fpra-register-2"
}
diff --git a/include/sframe.h b/include/sframe.h
index a790f4078121..fb0b6100fa9e 100644
--- a/include/sframe.h
+++ b/include/sframe.h
@@ -237,6 +237,9 @@ typedef struct sframe_func_desc_entry
may or may not be tracked. */
#define SFRAME_FRE_FP_OFFSET_IDX 2
+/* Invalid RA offset. Used as padding to represent FP without RA on stack. */
+#define SFRAME_FRE_RA_OFFSET_INVALID 0
+
typedef struct sframe_fre_info
{
/* Information about
@@ -299,6 +302,24 @@ typedef struct sframe_fre_info
fi
Note that in AAPCS64, a frame record, if created, will save both FP and
LR on stack.
+
+ S390:
+ offset1 (interpreted as CFA = BASE_REG + offset1)
+ if RA is being tracked
+ offset2 (interpreted as RA = CFA + offset2; an offset value of
+ SFRAME_FRE_RA_OFFSET_INVALID indicates a dummy padding RA offset
+ to represent FP without RA saved on stack)
+ if FP is being tracked
+ offset3 (intrepreted as FP = CFA + offset3)
+ fi
+ else
+ if FP is being tracked
+ offset2 (intrepreted as FP = CFA + offset2)
+ fi
+ fi
+ Note that in S390, if a FP/RA offset2/offset3 value has the least-
+ significant bit set it represents a DWARF register number shifted to the
+ left by 1 to restore the FP/RA value from.
*/
/* Used when SFRAME_FRE_TYPE_ADDR1 is specified as FRE type. */
diff --git a/libsframe/doc/sframe-spec.texi b/libsframe/doc/sframe-spec.texi
index 5bd027828e3f..bf5c94722d0a 100644
--- a/libsframe/doc/sframe-spec.texi
+++ b/libsframe/doc/sframe-spec.texi
@@ -845,8 +845,11 @@ On S390, the s390x ELF ABI does not mandate the precise location in a function
where the return address (RA) and frame pointer (FP) are saved, if at all.
Hence the need to track RA in the SFrame stack trace format. As RA is being
tracked in this ABI, the second stack offset is always used to locate the RA
-stack slot, by interpreting it as: RA = CFA + offset2. The third stack offset
-is used to locate the FP stack slot, by interpreting it as: FP = CFA + offset3.
+stack slot, by interpreting it as: RA = CFA + offset2, unless the offset has a
+value of @code{SFRAME_FRE_RA_OFFSET_INVALID}. The third stack offset is used
+to locate the FP stack slot, by interpreting it as: FP = CFA + offset3. A value
+of @code{SFRAME_FRE_RA_OFFSET_INVALID} is used to represent FP without RA saved
+on the stack or in a register.
In leaf functions the RA and FP may be saved in other registers, such as
floating-point registers (FPRs), instead of on the stack. To represent this
@@ -867,6 +870,7 @@ Hence, in summary:
@item 1 @tab CFA = @code{BASE_REG} + offset1
@item 2 @tab RA stack slot = CFA + offset2, if (offset2 & 1 == 0)
@*RA register number = offset2 >> 1, if (offset2 & 1 == 1)
+ @*RA not saved if (offset2 == @code{SFRAME_FRE_RA_OFFSET_INVALID})
@item 3 @tab FP stack slot = CFA + offset3, if (offset3 & 1 == 0)
@*FP register number = offset3 >> 1, if (offset3 & 1 == 1)
@end multitable
diff --git a/libsframe/sframe-dump.c b/libsframe/sframe-dump.c
index f86b5c09ddc5..0d56604640bd 100644
--- a/libsframe/sframe-dump.c
+++ b/libsframe/sframe-dump.c
@@ -200,6 +200,10 @@ dump_sframe_func_with_fres (sframe_decoder_ctx *sfd_ctx,
if (sframe_decoder_get_fixed_ra_offset (sfd_ctx)
!= SFRAME_CFA_FIXED_RA_INVALID)
strcpy (temp, "f");
+ /* If an ABI does track RA offset, e.g. AArch64 and S390, it can be a
+ dummy as padding to represent FP without RA being saved on stack. */
+ else if (err[2] == 0 && ra_offset == SFRAME_FRE_RA_OFFSET_INVALID)
+ sprintf (temp, "u*");
else if (err[2] == 0)
{
if (is_sframe_abi_arch_s390 (sfd_ctx)
--
2.45.2
More information about the Binutils
mailing list