]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
s390: Represent FP without RA saved in SFrame
authorJens Remus <jremus@linux.ibm.com>
Fri, 11 Jul 2025 08:29:40 +0000 (10:29 +0200)
committerJens Remus <jremus@linux.ibm.com>
Fri, 11 Jul 2025 08:29:40 +0000 (10:29 +0200)
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 s390x 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.
* sframe-api.h (sframe_fre_get_ra_offset): Add comment that for
s390x an offset value of SFRAME_FRE_RA_OFFSET_INVALID indicates
that the RA is not saved.

gas/
* gen-sframe.c (get_fre_num_offsets): For s390x account padding
RA offset, if FP without RA saved.
(sframe_get_fre_offset_size): Likewise.
(output_sframe_row_entry): For s390x 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 s390x.

libsframe/
* sframe.c (sframe_fre_get_ra_offset): Add comment that for
s390x an offset value of SFRAME_FRE_RA_OFFSET_INVALID indicates
that the RA is not saved.
* 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 (s390x): Document s390x-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-s390x-fpra-offset-err-1.s: Rename
to ...
* cfi-sframe/cfi-sframe-s390x-fpra-offset-err-1.d: Likewise.
* gas/cfi-sframe/cfi-sframe-s390x-fpra-offset-2.s: This.
* gas/cfi-sframe/cfi-sframe-s390x-fpra-offset-2.d: Likewise.
Update test verification pattern accordingly.
* cfi-sframe/cfi-sframe-s390x-fpra-register-err-1.s: Rename
to ...
* cfi-sframe/cfi-sframe-s390x-fpra-register-err-1.d: Likewise.
* gas/cfi-sframe/cfi-sframe-s390x-fpra-register-2.s: This.
* gas/cfi-sframe/cfi-sframe-s390x-fpra-register-2.d: Likewise.
Update test verification pattern accordingly.

Signed-off-by: Jens Remus <jremus@linux.ibm.com>
13 files changed:
gas/gen-sframe.c
gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-offset-2.d [new file with mode: 0644]
gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-offset-2.s [moved from gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-offset-err-1.s with 100% similarity]
gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-offset-err-1.d [deleted file]
gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-register-2.d [new file with mode: 0644]
gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-register-2.s [moved from gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-register-err-1.s with 100% similarity]
gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-register-err-1.d [deleted file]
gas/testsuite/gas/cfi-sframe/cfi-sframe.exp
include/sframe-api.h
include/sframe.h
libsframe/doc/sframe-spec.texi
libsframe/sframe-dump.c
libsframe/sframe.c

index 2741a8f93affc53b77202cebff1175f66c678c2e..547e693a7faf141c923edbbccbbf8dba65cce718 100644 (file)
@@ -340,7 +340,10 @@ get_fre_num_offsets (struct sframe_row_entry *sframe_fre)
   if (sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK)
     fre_num_offsets++;
   if (sframe_ra_tracking_p ()
-      && sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK)
+      && (sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK
+         /* For s390x account padding RA offset, if FP without RA saved.  */
+         || (sframe_get_abi_arch () == SFRAME_ABI_S390X_ENDIAN_BIG
+             && sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK)))
     fre_num_offsets++;
   return fre_num_offsets;
 }
@@ -362,9 +365,15 @@ sframe_get_fre_offset_size (struct sframe_row_entry *sframe_fre)
   cfa_offset_size = get_offset_size_in_bytes (sframe_fre->cfa_offset);
   if (sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK)
     bp_offset_size = get_offset_size_in_bytes (sframe_fre->bp_offset);
-  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 s390x account padding RA offset, if FP without RA saved.  */
+      else if (sframe_get_abi_arch () == SFRAME_ABI_S390X_ENDIAN_BIG
+              && sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK)
+       ra_offset_size = get_offset_size_in_bytes (SFRAME_FRE_RA_OFFSET_INVALID);
+    }
 
   /* Get the maximum size needed to represent the offsets.  */
   max_offset_size = cfa_offset_size;
@@ -570,11 +579,20 @@ output_sframe_row_entry (symbolS *fde_start_addr,
   fre_offset_func_map[idx].out_func (sframe_fre->cfa_offset);
   fre_write_offsets++;
 
-  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 s390x write padding RA offset, if FP without RA saved.  */
+      else if (sframe_get_abi_arch () == SFRAME_ABI_S390X_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++;
+       }
     }
   if (sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK)
     {
@@ -1804,7 +1822,9 @@ sframe_do_fde (struct sframe_xlate_ctx *xlate_ctx,
        = get_dw_fde_end_addrS (xlate_ctx->dw_fde);
     }
 
-  if (sframe_ra_tracking_p ())
+  /* ABI/arch except s390x cannot represent FP without RA saved.  */
+  if (sframe_ra_tracking_p ()
+      && sframe_get_abi_arch () != SFRAME_ABI_S390X_ENDIAN_BIG)
     {
       struct sframe_row_entry *fre;
 
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-offset-2.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-offset-2.d
new file mode 100644 (file)
index 0000000..305a917
--- /dev/null
@@ -0,0 +1,22 @@
+#name: SFrame generation on s390x - FP and then RA saved on stack
+#objdump: --sframe=.sframe
+#...
+Contents of the SFrame section .sframe:
+
+  Header :
+
+    Version: SFRAME_VERSION_2
+    Flags: SFRAME_F_FDE_FUNC_START_PCREL
+    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-s390x-fpra-offset-err-1.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-offset-err-1.d
deleted file mode 100644 (file)
index 7d71874..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#name: SFrame generation on s390x - FP without RA saved on stack
-#as: --gsframe
-#warning: FP without RA on stack
-#objdump: --sframe=.sframe
-#...
-Contents of the SFrame section .sframe:
-
-  Header :
-
-    Version: SFRAME_VERSION_2
-    Flags: SFRAME_F_FDE_FUNC_START_PCREL
-    Num FDEs: 0
-    Num FREs: 0
-
-#pass
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-register-2.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-register-2.d
new file mode 100644 (file)
index 0000000..7b719fd
--- /dev/null
@@ -0,0 +1,22 @@
+#name: SFrame generation on s390x - FP and then RA saved in FPR registers
+#objdump: --sframe=.sframe
+#...
+Contents of the SFrame section .sframe:
+
+  Header :
+
+    Version: SFRAME_VERSION_2
+    Flags: SFRAME_F_FDE_FUNC_START_PCREL
+    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-s390x-fpra-register-err-1.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-register-err-1.d
deleted file mode 100644 (file)
index f6854c2..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#name: SFrame generation on s390x - FP without RA saved in registers
-#as: --gsframe
-#warning: FP without RA on stack
-#objdump: --sframe=.sframe
-#...
-Contents of the SFrame section .sframe:
-
-  Header :
-
-    Version: SFRAME_VERSION_2
-    Flags: SFRAME_F_FDE_FUNC_START_PCREL
-    Num FDEs: 0
-    Num FREs: 0
-
-#pass
index 1e7fc9e03cad735617d9867e4bd3c307598cc943..b26ce60c6e597e24c77912337b182c46fb422df0 100644 (file)
@@ -119,7 +119,7 @@ if { [istarget "s390x*-*-*"] && [gas_sframe_check] } then {
     run_dump_test "cfi-sframe-s390x-err-2"
     run_dump_test "cfi-sframe-s390x-err-3"
     run_dump_test "cfi-sframe-s390x-fpra-offset-1"
-    run_dump_test "cfi-sframe-s390x-fpra-offset-err-1"
+    run_dump_test "cfi-sframe-s390x-fpra-offset-2"
     run_dump_test "cfi-sframe-s390x-fpra-register-1"
-    run_dump_test "cfi-sframe-s390x-fpra-register-err-1"
+    run_dump_test "cfi-sframe-s390x-fpra-register-2"
 }
index cb94f358b3d71de52dc391a35ae0229cc8f27753..ac4f19a66f078adaca0a9a6da4e9d5385bdc1f20 100644 (file)
@@ -224,6 +224,8 @@ sframe_fre_get_fp_offset (sframe_decoder_ctx *dctx,
 
 /* Get the RA offset from the FRE.  If the offset is invalid, sets errp.
 
+   For s390x an RA offset value of SFRAME_FRE_RA_OFFSET_INVALID indicates
+   that the RA is not saved, which is only valid in the topmost frame.
    For s390x the offset may be an encoded register number, indicated by
    LSB set to one, which is only valid in the topmost frame.  */
 extern int32_t
index 7bbdf04db171134623870fc46ee68d3b09f0fcfe..28b625b3258f70ff0e01cde034eb1927039fd1a8 100644 (file)
@@ -248,6 +248,10 @@ typedef struct sframe_func_desc_entry
    may or may not be tracked.  */
 #define SFRAME_FRE_FP_OFFSET_IDX    2
 
+/* Invalid RA offset.  Currently used for s390x as padding to represent FP
+   without RA saved.  */
+#define SFRAME_FRE_RA_OFFSET_INVALID 0
+
 typedef struct sframe_fre_info
 {
   /* Information about
@@ -310,6 +314,24 @@ typedef struct sframe_fre_info
      fi
      Note that in AAPCS64, a frame record, if created, will save both FP and
      LR on stack.
+
+   s390x:
+     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 s390x, 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.  */
index 1938207233cdf97fa0abd616ec8996c786caa235..6ba6c131edb60daabccf8bf4b7d757f88e4679e5 100644 (file)
@@ -152,6 +152,9 @@ DWARF register number.
 FP/RA offset.
   @item SFRAME_V2_S390X_OFFSET_DECODE_REGNUM: Decode a DWARF register number from
 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.
  @end itemize
 @end itemize
 
@@ -890,12 +893,13 @@ The (64-bit) 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.  RA remains unchanged,
-if the offset is not available.  Stack tracers are recommended to validate that
-the "unchanged RA" pattern, when present, is seen only for the topmost stack
-frame.  The third stack offset is used to locate the FP stack slot, by
-interpreting it as: FP = CFA + offset3.  FP remains unchanged, if the offset is
-not available.
+stack slot, by interpreting it as: RA = CFA + offset2, unless the offset has a
+value of @code{SFRAME_FRE_RA_OFFSET_INVALID}.  RA remains unchanged, if the
+offset is not available or has a value of @code{SFRAME_FRE_RA_OFFSET_INVALID}.
+Stack tracers are recommended to validate that the "unchanged RA" pattern, when
+present, is seen only for the topmost stack frame.  The third stack offset is
+used to locate the FP stack slot, by interpreting it as: FP = CFA + offset3.
+FP remains unchanged, if the offset is not available.
 
 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
@@ -919,6 +923,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
index f17ff64e1224fdc403871affc67e02c4f1dcd7ea..d55d3847194e47227023f91b9a1161d8370d249f 100644 (file)
@@ -211,6 +211,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. s390x, it can be a 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_s390x (sfd_ctx)
index af40e5b4fadfae79dd9a0a27b4506f43da9982ee..d8d370da90dd6f212a1d90a51f6ee535e1d8205a 100644 (file)
@@ -733,6 +733,8 @@ sframe_fre_get_fp_offset (sframe_decoder_ctx *dctx,
 
 /* Get the RA offset from the FRE.  If the offset is invalid, sets errp.
 
+   For s390x an RA offset value of SFRAME_FRE_RA_OFFSET_INVALID indicates
+   that the RA is not saved, which is only valid in the topmost frame.
    For s390x the offset may be an encoded register number, indicated by
    LSB set to one, which is only valid in the topmost frame.  */