]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
s390: Initial support to generate .sframe from CFI directives in assembler
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)
This introduces initial support to generate .sframe from CFI directives
in assembler on s390 64-bit (s390x).  Due to SFrame V2 format
limitations it has the following limitations, some of them getting
addressed by subsequent patches, which cause generation of SFrame FDE
to be skipped:

- SFrame FP/RA tracking only supports register contents being saved on
  the stack (i.e. .cfi_offset).  It does not support FP/RA register
  contents being saved in other registers (i.e. .cfi_register).  GCC on
  s390x can be observed to save the FP/RA register contents in floating-
  point registers, but only in leaf functions.
  This issue is detailed further and resolved in the subsequent commit
  "s390: Represent FP/RA saved in register in SFrame".

- SFrame FP/RA tracking cannot represent FP without RA saved.  This is
  because the format assumes SFrame FDE offset2 to be the RA offset, if
  there are two offsets, and offset3 to be the FP offset, if there are
  three offsets.  There is no mean to distinguish whether offset2 is the
  RA or FP offset, if there are only two offsets.
  This issue is detailed further and resolved in the subsequent commit
  "s390: Represent FP without RA saved in SFrame".

- SFrame assumes a dedicated FP register number.  The s390x ELF ABI [1]
  does only designate register 11 as preferred FP register number.  In
  general GCC and Clang on s390x use register 11 as frame pointer.
  GCC on s390x can be observed to use register 14 as frame pointer in
  the stack clash protector in the function prologue.
  glibc on s390x contains hand-written assembler code that uses
  register 12 as frame pointer.

This s390x support is largely based on the AArch64 support from commit
b52c4ee46657 ("gas: generate .sframe from CFI directives").

The SFrame ABI/arch identifier SFRAME_ABI_S390X_ENDIAN_BIG is introduced
for s390x and added to the SFrame format specification.

The s390x ELF ABI [1] specifies the following C calling conventions for
s390x architecture:
- Register 15 is the stack pointer (SP).
- Register 14 contains the return address (RA) at function entry.
- There is no dedicated frame pointer register.  Register 11 is the
  preferred frame pointer (FP). [2]  GCC and Clang in general use
  register 11 as frame pointer.
- The CFA is defined as SP at call site +160. [3]  The SP at call site
  can therefore be derived from the CFA using a SP value offset from CFA
  of -160.

The s390x ELF ABI [1] does not assign any standard save slot to each
register in the register save area of a stack frame.  Neither the
return address (RA, r14) nor preferred frame pointer (FP, r11)
necessarily need to be saved.  Therefore SFrame RA and FP tracking is
used.

Support for SFrame on s390 is only enabled for the 64-bit s390x ELF ABI
(z/Architecture with 64-bit addressing mode).  It is disabled for the
32-bit s390 ELF ABI (ESA/390 or z/Architecture with 32-bit addressing
mode).

s390x-specific SFrame assembler and linker tests are added, including
error tests for use of a non-preferred frame pointer (FP) register and
specification of a non-default return address (RA) register.

[1]: s390x ELF ABI, https://github.com/IBM/s390x-abi/releases
[2]: s390x ELF ABI, commit f00421825979 ("Add information about the frame
     pointer register"),
     https://github.com/IBM/s390x-abi/commit/f00421825979
[3]: s390x ELF ABI, commit 4e38ad9c8a88 ("Document the CFA"),
     https://github.com/IBM/s390x-abi/commit/4e38ad9c8a88

include/
* sframe.h: Add reference to s390x architecture in comments.
(SFRAME_ABI_S390X_ENDIAN_BIG): Define SFrame ABI/arch identifier
for s390x.
(SFRAME_S390X_SP_VAL_OFFSET): Define s390x-specific SP value
offset from CFA.

libsframe/
* sframe.c (need_swapping): Add SFRAME_ABI_S390X_ENDIAN_BIG.
* doc/sframe-spec.texi (SFRAME_ABI_S390X_ENDIAN_BIG, s390x,
SFRAME_S390X_SP_VAL_OFFSET): Document SFrame ABI/arch identifier
for s390x, add references to s390x architecture, and document
s390x-specifics, such as the SP value offset from CFA of -160.

gas/
* config/tc-s390.h: s390x support to generate .sframe from CFI
directives in assembler.
(support_sframe_p): Define.
(SFRAME_CFA_SP_REG, SFRAME_CFA_FP_REG, SFRAME_CFA_RA_REG):
Define.
(sframe_ra_tracking_p): Define.
(sframe_cfa_ra_offset): Define.
(sframe_get_abi_arch): Define.
* config/tc-s390.c: s390x support to generate .sframe from CFI
directives in assembler.
(s390_sframe_cfa_sp_reg, s390_sframe_cfa_fp_reg,
s390_sframe_cfa_ra_reg): New.  Initialize to DWARF register
numbers of stack pointer (SP, r15), preferred frame pointer
(FP, r11), and return address (RA, r14) registers.
(s390_support_sframe_p): New function.  Return true if s390x.
(s390_sframe_ra_tracking_p): New function.  Return true.
(s390_sframe_cfa_ra_offset): New function.  Return
SFRAME_CFA_FIXED_RA_INVALID.
(s390_sframe_get_abi_arch): New function.  Return
SFRAME_ABI_S390X_ENDIAN_BIG if s390x, otherwise zero.
* gen-sframe.c: Add reference to s390x architecture in comments.
(sframe_xlate_do_val_offset): Add support for s390x-specific
SFRAME_S390X_SP_VAL_OFFSET.
* NEWS: Add news entry.

gas/testsuite/
* gas/cfi-sframe/cfi-sframe.exp: Enable common SFrame tests for
s390x.  Add s390x-specific SFrame (error) tests.
* gas/cfi-sframe/cfi-sframe-s390x-1.d: New s390x-specific SFrame
test.
* gas/cfi-sframe/cfi-sframe-s390x-1.s: Likewise.
* gas/cfi-sframe/cfi-sframe-s390x-2.d: Likewise.
* gas/cfi-sframe/cfi-sframe-s390x-2.s: Likewise.
* gas/cfi-sframe/cfi-sframe-s390x-err-1.d: New s390x-specific
SFrame error test that uses a non-default frame-pointer register
as CFA base register.
* gas/cfi-sframe/cfi-sframe-s390x-err-1.s: Likewise.
* gas/cfi-sframe/cfi-sframe-s390x-err-2.d: Likewise.
* gas/cfi-sframe/cfi-sframe-s390x-err-2.s: Likewise.
* gas/cfi-sframe/cfi-sframe-s390x-err-3.d: New s390x-specific
SFrame error test that uses a non-default return address
register.
* gas/cfi-sframe/cfi-sframe-s390x-err-3.s: Likewise.
* gas/cfi-sframe/cfi-sframe-s390x-fpra-offset-1.d: New s390x-
specific SFrame test that saves RA and FP individually on the
stack.
* gas/cfi-sframe/cfi-sframe-s390x-fpra-offset-1.s: Likewise.
* gas/cfi-sframe/cfi-sframe-s390x-fpra-offset-err-1.d: New
s390x-specific SFrame error test that saves FP and RA
individually, to trigger FP without RA saved.
* gas/cfi-sframe/cfi-sframe-s390x-fpra-offset-err-1.s: Likewise.
* gas/cfi-sframe/cfi-sframe-s390x-fpra-register-err-1.d: New
s390x-specific SFrame error test that saves FP and RA
individually in registers.
* gas/cfi-sframe/cfi-sframe-s390x-fpra-register-err-1.s:
Likewise.
* gas/cfi-sframe/cfi-sframe-s390x-fpra-register-err-2.d: New
s390x-specific SFrame error test that saves RA and FP
individually in registers.
* gas/cfi-sframe/cfi-sframe-s390x-fpra-register-err-2.s:
Likewise.

ld/testsuite/
* ld-s390/s390.exp: Add simple SFrame test.
* ld-s390/sframe-simple-1.d: New simple SFrame test.
* ld-s390/sframe-bar.s: Likewise.
* ld-s390/sframe-foo.s: Likewise.

Signed-off-by: Jens Remus <jremus@linux.ibm.com>
30 files changed:
gas/NEWS
gas/config/tc-s390.c
gas/config/tc-s390.h
gas/gen-sframe.c
gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-1.d [new file with mode: 0644]
gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-1.s [new file with mode: 0644]
gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-2.d [new file with mode: 0644]
gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-2.s [new file with mode: 0644]
gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-err-1.d [new file with mode: 0644]
gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-err-1.s [new file with mode: 0644]
gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-err-2.d [new file with mode: 0644]
gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-err-2.s [new file with mode: 0644]
gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-err-3.d [new file with mode: 0644]
gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-err-3.s [new file with mode: 0644]
gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-offset-1.d [new file with mode: 0644]
gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-offset-1.s [new file with mode: 0644]
gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-offset-err-1.d [new file with mode: 0644]
gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-offset-err-1.s [new file with mode: 0644]
gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-register-err-1.d [new file with mode: 0644]
gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-register-err-1.s [new file with mode: 0644]
gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-register-err-2.d [new file with mode: 0644]
gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-register-err-2.s [new file with mode: 0644]
gas/testsuite/gas/cfi-sframe/cfi-sframe.exp
include/sframe.h
ld/testsuite/ld-s390/s390.exp
ld/testsuite/ld-s390/sframe-bar.s [new file with mode: 0644]
ld/testsuite/ld-s390/sframe-foo.s [new file with mode: 0644]
ld/testsuite/ld-s390/sframe-simple-1.d [new file with mode: 0644]
libsframe/doc/sframe-spec.texi
libsframe/sframe.c

index cbca8fe8656281901c5c80acd18a9d7c92ff3866..71d9c9cdd76d69aabbba0ac65fb0224c6b3c6df6 100644 (file)
--- a/gas/NEWS
+++ b/gas/NEWS
@@ -1,5 +1,8 @@
 -*- text -*-
 
+* Add initial support to generate SFrame stack trace information (.sframe)
+  from CFI directives on s390 64-bit (s390x).
+
 * All SFrame sections generated by gas have the header flag
   SFRAME_F_FDE_FUNC_START_PCREL set.  gas was already emitting SFrame sections
   with the applicable encoding.  Setting the flag ensures compliance with the
index d5d3976d5fce2004c53e87feee13d84fb28cb8a3..b073d8edbd2535143399230e61309dcf42039766 100644 (file)
@@ -24,6 +24,8 @@
 #include "subsegs.h"
 #include "dwarf2dbg.h"
 #include "dw2gencfi.h"
+#include "sframe.h"
+#include "gen-sframe.h"
 
 #include "opcode/s390.h"
 #include "elf/s390.h"
@@ -97,6 +99,17 @@ const char FLT_CHARS[] = "dD";
 /* The dwarf2 data alignment, adjusted for 32 or 64 bit.  */
 int s390_cie_data_alignment;
 
+/* Register numbers used for SFrame stack trace info.  */
+
+/* Designated stack pointer DWARF register number according to s390x ELF ABI.  */
+const unsigned int s390_sframe_cfa_sp_reg = 15;
+
+/* Preferred frame pointer DWARF register number according to s390x ELF ABI.  */
+const unsigned int s390_sframe_cfa_fp_reg = 11;
+
+/* Designated return address DWARF register number according to s390x ELF ABI.  */
+const unsigned int s390_sframe_cfa_ra_reg = DWARF2_DEFAULT_RETURN_COLUMN;
+
 /* The target specific pseudo-ops which we support.  */
 
 /* Define the prototypes for the pseudo-ops */
@@ -2865,6 +2878,48 @@ tc_s390_regname_to_dw2regnum (char *regname)
   return regnum;
 }
 
+/* Whether SFrame stack trace info is supported.  */
+
+bool
+s390_support_sframe_p (void)
+{
+  /* At this time, SFrame is supported for s390x (64-bit) only.  */
+  return (s390_arch_size == 64);
+}
+
+/* Specify if RA tracking is needed.  */
+
+bool
+s390_sframe_ra_tracking_p (void)
+{
+  return true;
+}
+
+/* Specify the fixed offset to recover RA from CFA.
+   (useful only when RA tracking is not needed).  */
+
+offsetT
+s390_sframe_cfa_ra_offset (void)
+{
+  return (offsetT) SFRAME_CFA_FIXED_RA_INVALID;
+}
+
+/* Get the abi/arch identifier for SFrame.  */
+
+unsigned char
+s390_sframe_get_abi_arch (void)
+{
+  unsigned char sframe_abi_arch = 0;
+
+  if (s390_support_sframe_p ())
+    {
+      gas_assert (target_big_endian);
+      sframe_abi_arch = SFRAME_ABI_S390X_ENDIAN_BIG;
+    }
+
+  return sframe_abi_arch;
+}
+
 void
 s390_elf_final_processing (void)
 {
index 9206c029a1c159dca29ce006b381c7405e333ca2..c92769e507b324fd2b4b4efc818062d6dc3a1239 100644 (file)
@@ -98,3 +98,34 @@ extern int s390_cie_data_alignment;
 extern void s390_elf_final_processing (void);
 
 #define elf_tc_final_processing s390_elf_final_processing
+
+/* SFrame.  */
+
+/* Whether SFrame stack trace info is supported.  */
+extern bool s390_support_sframe_p (void);
+#define support_sframe_p s390_support_sframe_p
+
+/* The stack pointer DWARF register number for SFrame CFA tracking.  */
+extern const unsigned int s390_sframe_cfa_sp_reg;
+#define SFRAME_CFA_SP_REG s390_sframe_cfa_sp_reg
+
+/* The frame pointer DWARF register number for SFrame CFA and FP tracking.  */
+extern const unsigned int s390_sframe_cfa_fp_reg;
+#define SFRAME_CFA_FP_REG s390_sframe_cfa_fp_reg
+
+/* The return address DWARF register number for SFrame RA tracking.  */
+extern const unsigned int s390_sframe_cfa_ra_reg;
+#define SFRAME_CFA_RA_REG s390_sframe_cfa_ra_reg
+
+/* Whether SFrame return address tracking is needed.  */
+extern bool s390_sframe_ra_tracking_p (void);
+#define sframe_ra_tracking_p s390_sframe_ra_tracking_p
+
+/* The fixed offset from CFA for SFrame to recover the return address.
+   (useful only when SFrame RA tracking is not needed).  */
+extern offsetT s390_sframe_cfa_ra_offset (void);
+#define sframe_cfa_ra_offset s390_sframe_cfa_ra_offset
+
+/* The abi/arch identifier for SFrame.  */
+unsigned char s390_sframe_get_abi_arch (void);
+#define sframe_get_abi_arch s390_sframe_get_abi_arch
index 3cba5085e1ce5b9d278cd1c5c27aaddaadb35d43..3d727b6475d88e64924e7a69fff80d5a5bd8b7d6 100644 (file)
@@ -714,7 +714,7 @@ output_sframe_internal (void)
     }
   out_one (fixed_ra_offset);
 
-  /* None of the AMD64, or AARCH64 ABIs need the auxiliary header.
+  /* None of the AMD64, AARCH64, or s390x ABIs need the auxiliary header.
      When the need does arise to use this field, the appropriate backend
      must provide this information.  */
   out_one (0); /* Auxiliary SFrame header length.  */
@@ -1117,7 +1117,11 @@ sframe_xlate_do_val_offset (const struct sframe_xlate_ctx *xlate_ctx ATTRIBUTE_U
   if (cfi_insn->u.ri.reg == SFRAME_CFA_FP_REG
       || (sframe_ra_tracking_p () && cfi_insn->u.ri.reg == SFRAME_CFA_RA_REG)
       /* Ignore SP reg, if offset matches assumed default rule.  */
-      || (cfi_insn->u.ri.reg == SFRAME_CFA_SP_REG && cfi_insn->u.ri.offset != 0))
+      || (cfi_insn->u.ri.reg == SFRAME_CFA_SP_REG
+         && ((sframe_get_abi_arch () != SFRAME_ABI_S390X_ENDIAN_BIG
+              && cfi_insn->u.ri.offset != 0)
+             || (sframe_get_abi_arch () == SFRAME_ABI_S390X_ENDIAN_BIG
+                 && cfi_insn->u.ri.offset != SFRAME_S390X_SP_VAL_OFFSET))))
     {
       as_warn (_("no SFrame FDE emitted; %s with %s reg %u"),
               cfi_esc_p ? ".cfi_escape DW_CFA_val_offset" : ".cfi_val_offset",
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-1.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-1.d
new file mode 100644 (file)
index 0000000..df315df
--- /dev/null
@@ -0,0 +1,23 @@
+#name: SFrame generation on s390x - .cfi_offset and .cfi_def_cfa_{offset,register}
+#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: 6
+
+  Function Index :
+
+    func idx \[0\]: pc = 0x0, size = 40 bytes
+    STARTPC +CFA +FP +RA +
+    0+0000 +sp\+160 +u +u +
+    0+0006 +sp\+160 +c\-72 +c\-48 +
+    0+000c +sp\+320 +c\-72 +c\-48 +
+    0+0010 +fp\+320 +c\-72 +c\-48 +
+    0+001c +sp\+160 +u +u +
+    0+001e +fp\+320 +c\-72 +c\-48 +
+#pass
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-1.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-1.s
new file mode 100644 (file)
index 0000000..7d132a4
--- /dev/null
@@ -0,0 +1,37 @@
+       .cfi_sections .sframe
+       .cfi_startproc
+       stmg    %r6,%r15,48(%r15)
+       .cfi_offset 6, -112
+       .cfi_offset 7, -104
+       .cfi_offset 8, -96
+       .cfi_offset 9, -88
+       .cfi_offset 10, -80
+       .cfi_offset 11, -72
+       .cfi_offset 12, -64
+       .cfi_offset 13, -56
+       .cfi_offset 14, -48
+       .cfi_offset 15, -40
+       lay     %r15,-160(%r15)
+       .cfi_def_cfa_offset 320
+       lgr     %r11,%r15
+       .cfi_def_cfa_register 11
+       lay     %r15,-128(%r15)
+.Lreturn:
+       lmg     %r6,%r15,160+48(%r11)
+       .cfi_remember_state
+       .cfi_restore 15
+       .cfi_restore 14
+       .cfi_restore 13
+       .cfi_restore 12
+       .cfi_restore 11
+       .cfi_restore 10
+       .cfi_restore 9
+       .cfi_restore 8
+       .cfi_restore 7
+       .cfi_restore 6
+       .cfi_def_cfa 15, 160
+       br      %r14
+       .cfi_restore_state
+       lay     %r15,-128(%r15)
+       j       .Lreturn
+       .cfi_endproc
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-2.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-2.d
new file mode 100644 (file)
index 0000000..a772c2b
--- /dev/null
@@ -0,0 +1,23 @@
+#name: SFrame generation on s390x - .cfi_rel_offset and .cfi_def_cfa_{offset,register}
+#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: 6
+
+  Function Index :
+
+    func idx \[0\]: pc = 0x0, size = 40 bytes
+    STARTPC +CFA +FP +RA +
+    0+0000 +sp\+160 +u +u +
+    0+0006 +sp\+160 +c\-72 +c\-48 +
+    0+000c +sp\+320 +c\-72 +c\-48 +
+    0+0010 +fp\+320 +c\-72 +c\-48 +
+    0+001c +sp\+160 +u +u +
+    0+001e +fp\+320 +c\-72 +c\-48 +
+#pass
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-2.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-2.s
new file mode 100644 (file)
index 0000000..6d52037
--- /dev/null
@@ -0,0 +1,37 @@
+       .cfi_sections .sframe
+       .cfi_startproc
+       stmg    %r6,%r15,48(%r15)
+       .cfi_rel_offset 6, 48
+       .cfi_rel_offset 7, 56
+       .cfi_rel_offset 8, 64
+       .cfi_rel_offset 9, 72
+       .cfi_rel_offset 10, 80
+       .cfi_rel_offset 11, 88
+       .cfi_rel_offset 12, 96
+       .cfi_rel_offset 13, 104
+       .cfi_rel_offset 14, 112
+       .cfi_rel_offset 15, 120
+       lay     %r15,-160(%r15)
+       .cfi_def_cfa_offset 320
+       lgr     %r11,%r15
+       .cfi_def_cfa_register 11
+       lay     %r15,-128(%r15)
+.Lreturn:
+       lmg     %r6,%r15,160+48(%r11)
+       .cfi_remember_state
+       .cfi_restore 15
+       .cfi_restore 14
+       .cfi_restore 13
+       .cfi_restore 12
+       .cfi_restore 11
+       .cfi_restore 10
+       .cfi_restore 9
+       .cfi_restore 8
+       .cfi_restore 7
+       .cfi_restore 6
+       .cfi_def_cfa 15, 160
+       br      %r14
+       .cfi_restore_state
+       lay     %r15,-128(%r15)
+       j       .Lreturn
+       .cfi_endproc
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-err-1.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-err-1.d
new file mode 100644 (file)
index 0000000..14f2382
--- /dev/null
@@ -0,0 +1,15 @@
+#name: SFrame generation on s390x - .cfi_def_cfa_register with non-SP/FP register
+#as: --gsframe
+#warning: non-SP/FP register 10 in \.cfi_def_cfa_register
+#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-err-1.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-err-1.s
new file mode 100644 (file)
index 0000000..8b724df
--- /dev/null
@@ -0,0 +1,37 @@
+       .cfi_sections .sframe
+       .cfi_startproc
+       stmg    %r6,%r15,48(%r15)
+       .cfi_offset 6, -112
+       .cfi_offset 7, -104
+       .cfi_offset 8, -96
+       .cfi_offset 9, -88
+       .cfi_offset 10, -80
+       .cfi_offset 11, -72
+       .cfi_offset 12, -64
+       .cfi_offset 13, -56
+       .cfi_offset 14, -48
+       .cfi_offset 15, -40
+       lay     %r15,-160(%r15)
+       .cfi_def_cfa_offset 320
+       lgr     %r10,%r15
+       .cfi_def_cfa_register 10        # non-default frame pointer register
+       lay     %r15,-128(%r15)
+.Lreturn:
+       lmg     %r6,%r15,160+48(%r10)
+       .cfi_remember_state
+       .cfi_restore 15
+       .cfi_restore 14
+       .cfi_restore 13
+       .cfi_restore 12
+       .cfi_restore 11
+       .cfi_restore 10
+       .cfi_restore 9
+       .cfi_restore 8
+       .cfi_restore 7
+       .cfi_restore 6
+       .cfi_def_cfa 15, 160
+       br      %r14
+       .cfi_restore_state
+       lay     %r15,-128(%r15)
+       j       .Lreturn
+       .cfi_endproc
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-err-2.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-err-2.d
new file mode 100644 (file)
index 0000000..0d3b475
--- /dev/null
@@ -0,0 +1,15 @@
+#name: SFrame generation on s390x - .cfi_def_cfa with non-SP/FP register
+#as: --gsframe
+#warning: non-SP/FP register 10 in \.cfi_def_cfa
+#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-err-2.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-err-2.s
new file mode 100644 (file)
index 0000000..dbcf439
--- /dev/null
@@ -0,0 +1,37 @@
+       .cfi_sections .sframe
+       .cfi_startproc
+       stmg    %r6,%r15,48(%r15)
+       .cfi_offset 6, -112
+       .cfi_offset 7, -104
+       .cfi_offset 8, -96
+       .cfi_offset 9, -88
+       .cfi_offset 10, -80
+       .cfi_offset 11, -72
+       .cfi_offset 12, -64
+       .cfi_offset 13, -56
+       .cfi_offset 14, -48
+       .cfi_offset 15, -40
+       lay     %r15,-160(%r15)
+       .cfi_def_cfa_offset 320
+       lgr     %r10,%r15
+       .cfi_def_cfa 10, 320            # non-default frame pointer register
+       lay     %r15,-128(%r15)
+.Lreturn:
+       lmg     %r6,%r15,160+48(%r10)
+       .cfi_remember_state
+       .cfi_restore 15
+       .cfi_restore 14
+       .cfi_restore 13
+       .cfi_restore 12
+       .cfi_restore 11
+       .cfi_restore 10
+       .cfi_restore 9
+       .cfi_restore 8
+       .cfi_restore 7
+       .cfi_restore 6
+       .cfi_def_cfa 15, 160
+       br      %r14
+       .cfi_restore_state
+       lay     %r15,-128(%r15)
+       j       .Lreturn
+       .cfi_endproc
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-err-3.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-err-3.d
new file mode 100644 (file)
index 0000000..519c235
--- /dev/null
@@ -0,0 +1,15 @@
+#name: SFrame generation on s390x - non-default RA register
+#as: --gsframe
+#warning: non-default RA register 7
+#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-err-3.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-err-3.s
new file mode 100644 (file)
index 0000000..7cea165
--- /dev/null
@@ -0,0 +1,5 @@
+       .cfi_sections .sframe
+       .cfi_startproc
+       .cfi_return_column 7            # non-default return address register
+       br      %r7
+       .cfi_endproc
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-offset-1.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-offset-1.d
new file mode 100644 (file)
index 0000000..e5eac75
--- /dev/null
@@ -0,0 +1,22 @@
+#name: SFrame generation on s390x - RA and then FP 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 +u +c\-48 +
+    0+000c +sp\+160 +c\-72 +c\-48 +
+    0+001a +sp\+160 +u +c\-48 +
+    0+0020 +sp\+160 +u +u +
+#pass
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-offset-1.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-offset-1.s
new file mode 100644 (file)
index 0000000..3d9c8cb
--- /dev/null
@@ -0,0 +1,15 @@
+       .cfi_sections .sframe
+       .cfi_startproc
+       stg     %r14,112(%r15)
+       .cfi_rel_offset 14, 112
+       stg     %r11,88(%r15)
+       .cfi_rel_offset 11, 88
+       la      %r11,0
+       la      %r14,0
+.Lreturn:
+       lg      %r11,88(%r15)
+       .cfi_restore 11
+       lg      %r14,112(%r15)
+       .cfi_restore 14
+       br      %r14
+       .cfi_endproc
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
new file mode 100644 (file)
index 0000000..7d71874
--- /dev/null
@@ -0,0 +1,15 @@
+#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-offset-err-1.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-offset-err-1.s
new file mode 100644 (file)
index 0000000..a84c391
--- /dev/null
@@ -0,0 +1,15 @@
+       .cfi_sections .sframe
+       .cfi_startproc
+       stg     %r11,88(%r15)
+       .cfi_rel_offset 11, 88
+       stg     %r14,112(%r15)
+       .cfi_rel_offset 14, 112
+       la      %r11,0
+       la      %r14,0
+.Lreturn:
+       lg      %r14,112(%r15)
+       .cfi_restore 14
+       lg      %r11,88(%r15)
+       .cfi_restore 11
+       br      %r14
+       .cfi_endproc
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
new file mode 100644 (file)
index 0000000..ad83f65
--- /dev/null
@@ -0,0 +1,15 @@
+#name: SFrame generation on s390x - FP and then RA saved in register
+#as: --gsframe
+#warning: FP register 11 in .cfi_register
+#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-err-1.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-register-err-1.s
new file mode 100644 (file)
index 0000000..48b01ac
--- /dev/null
@@ -0,0 +1,15 @@
+       .cfi_sections .sframe
+       .cfi_startproc
+       ldgr    %f2,%r11
+       .cfi_register 11, 17
+       ldgr    %f0,%r14
+       .cfi_register 14, 16
+       la      %r11,0
+       la      %r14,0
+.Lreturn:
+       lgdr    %r14,%f0
+       .cfi_restore 14
+       lgdr    %r11,%f2
+       .cfi_restore 11
+       br      %r14
+       .cfi_endproc
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-register-err-2.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-register-err-2.d
new file mode 100644 (file)
index 0000000..7dde59c
--- /dev/null
@@ -0,0 +1,15 @@
+#name: SFrame generation on s390x - RA and then FP saved in register
+#as: --gsframe
+#warning: RA register 14 in .cfi_register
+#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-err-2.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-register-err-2.s
new file mode 100644 (file)
index 0000000..1d44971
--- /dev/null
@@ -0,0 +1,15 @@
+       .cfi_sections .sframe
+       .cfi_startproc
+       ldgr    %f0,%r14
+       .cfi_register 14, 16
+       ldgr    %f2,%r11
+       .cfi_register 11, 17
+       la      %r11,0
+       la      %r14,0
+.Lreturn:
+       lgdr    %r11,%f2
+       .cfi_restore 11
+       lgdr    %r14,%f0
+       .cfi_restore 14
+       br      %r14
+       .cfi_endproc
index 341a56a9eab290558408389d347c0ffd64c1b5ed..d17cd767cc6e00790e75e85314b89f053c41633a 100644 (file)
@@ -64,8 +64,9 @@ proc gas_x86_64_check { } {
 }
 
 # common tests
-if  { ([istarget "x86_64-*-*"] || [istarget "aarch64*-*-*"]) \
-       && [gas_sframe_check] } then {
+if  { ([istarget "x86_64-*-*"] || [istarget "aarch64*-*-*"]
+       || [istarget "s390x-*-*"]) \
+      && [gas_sframe_check] } then {
 
     global ASFLAGS
     set old_ASFLAGS "$ASFLAGS"
@@ -109,3 +110,16 @@ if { [istarget "aarch64*-*-*"] && [gas_sframe_check] } then {
     run_dump_test "cfi-sframe-aarch64-4"
     run_dump_test "cfi-sframe-aarch64-pac-ab-key-1"
 }
+
+# s390x specific tests
+if { [istarget "s390x*-*-*"] && [gas_sframe_check] } then {
+    run_dump_test "cfi-sframe-s390x-1"
+    run_dump_test "cfi-sframe-s390x-2"
+    run_dump_test "cfi-sframe-s390x-err-1"
+    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-register-err-1"
+    run_dump_test "cfi-sframe-s390x-fpra-register-err-2"
+}
index c0375ba6b7cbc4959bbb9d36bfd0996441d5130d..0278a8dc47c8a9c85b4c483879ce0d7bc90e085c 100644 (file)
@@ -104,6 +104,7 @@ extern "C"
 #define SFRAME_ABI_AARCH64_ENDIAN_BIG      1 /* AARCH64 big endian.  */
 #define SFRAME_ABI_AARCH64_ENDIAN_LITTLE   2 /* AARCH64 little endian.  */
 #define SFRAME_ABI_AMD64_ENDIAN_LITTLE     3 /* AMD64 little endian.  */
+#define SFRAME_ABI_S390X_ENDIAN_BIG        4 /* s390x big endian.  */
 
 /* SFrame FRE types.  */
 #define SFRAME_FRE_TYPE_ADDR1  0
@@ -201,7 +202,7 @@ typedef struct sframe_func_desc_entry
      - 2-bits: Unused.
      ------------------------------------------------------------------------
      |     Unused    |  PAC auth A/B key (aarch64) |  FDE type |   FRE type   |
-     |               |        Unused (amd64)       |           |              |
+     |               |     Unused (amd64, s390x)   |           |              |
      ------------------------------------------------------------------------
      8               6                             5           4              0     */
   uint8_t sfde_func_info;
@@ -259,7 +260,7 @@ typedef struct sframe_fre_info
      - 1 bit: Mangled RA state bit (aarch64 only).
      ----------------------------------------------------------------------------------
      | Mangled-RA (aarch64) |  Size of offsets   |   Number of offsets    |   base_reg |
-     |  Unused (amd64)      |                    |                        |            |
+     | Unused (amd64, s390x)|                    |                        |            |
      ----------------------------------------------------------------------------------
      8                     7                    5                        1            0
 
@@ -285,7 +286,7 @@ typedef struct sframe_fre_info
 
 /* SFrame Frame Row Entry definitions.
 
-   Used for both AMD64 and AARCH64.
+   Used for AMD64, AARCH64, and s390x.
 
    An SFrame Frame Row Entry is a self-sufficient record which contains
    information on how to generate the stack trace for the specified range of
@@ -353,6 +354,10 @@ typedef struct sframe_frame_row_entry_addr4
 #define SFRAME_FRE_TYPE_ADDR4_LIMIT   \
   (1ULL << ((SFRAME_FRE_TYPE_ADDR4 * 2) * 8))
 
+/* 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)
+
 #ifdef __cplusplus
 }
 #endif
index e5bfb511ff6158e73323a16b27c44d3daa145baa..c41acf10ca1c55ed6bea38a3ddea311312e88e3c 100644 (file)
@@ -146,4 +146,8 @@ if { [istarget "s390-*-*"] || [istarget "s390x-*-*"] } {
 
 if [istarget "s390x-*-*"] {
     run_ld_link_tests $s390xtests
+
+    if { ![skip_sframe_tests] } {
+       run_dump_test "sframe-simple-1"
+    }
 }
diff --git a/ld/testsuite/ld-s390/sframe-bar.s b/ld/testsuite/ld-s390/sframe-bar.s
new file mode 100644 (file)
index 0000000..f707569
--- /dev/null
@@ -0,0 +1,21 @@
+       .text
+       .globl  bar
+       .type   bar, @function
+bar:
+       .cfi_startproc
+       stmg    %r14,%r15,112(%r15)
+       .cfi_rel_offset %r14, 112
+       .cfi_rel_offset %r15, 120
+       lay     %r15,-160(%r15)
+       .cfi_adjust_cfa_offset 160
+       clgfi   %r2,1000
+       jle     .Lreturn
+       brasl   %r14,foo@PLT
+.Lreturn:
+       lay     %r15,160(%r15)
+       .cfi_adjust_cfa_offset -160
+       lg      %r14,112(%r15)
+       .cfi_restore %r14
+       br      %r14
+       .cfi_endproc
+       .size   bar, .-bar
diff --git a/ld/testsuite/ld-s390/sframe-foo.s b/ld/testsuite/ld-s390/sframe-foo.s
new file mode 100644 (file)
index 0000000..75657e9
--- /dev/null
@@ -0,0 +1,9 @@
+       .text
+       .globl  foo
+       .type   foo, @function
+foo:
+       .cfi_startproc
+       msgfi   %r2,42
+       br      %r14
+       .cfi_endproc
+       .size   foo, .-foo
diff --git a/ld/testsuite/ld-s390/sframe-simple-1.d b/ld/testsuite/ld-s390/sframe-simple-1.d
new file mode 100644 (file)
index 0000000..353dbce
--- /dev/null
@@ -0,0 +1,31 @@
+#as: --gsframe
+#source: sframe-foo.s
+#source: sframe-bar.s
+#objdump: --sframe=.sframe
+#ld: -shared --no-rosegment
+#name: SFrame simple link
+
+.*: +file format .*
+
+Contents of the SFrame section .sframe:
+  Header :
+
+    Version: SFRAME_VERSION_2
+    Flags: SFRAME_F_FDE_SORTED,
+           SFRAME_F_FDE_FUNC_START_PCREL
+    Num FDEs: 2
+    Num FREs: 6
+
+  Function Index :
+
+    func idx \[0\]: pc = 0x228, size = 8 bytes
+    STARTPC +CFA +FP +RA +
+    0+228 +sp\+160 +u +u +
+
+    func idx \[1\]: pc = 0x230, size = 42 bytes
+    STARTPC +CFA +FP +RA +
+    0+230 +sp\+160 +u +u +
+    0+236 +sp\+160 +u +c-48 +
+    0+23c +sp\+320 +u +c-48 +
+    0+252 +sp\+160 +u +c-48 +
+    0+258 +sp\+160 +u +u +
index 69fe873b9eccf053cfbefb64b491d901cf4aaaa4..57e116356a18a0b2e84126b9b37ea96da3bb3398 100644 (file)
@@ -81,8 +81,8 @@ The SFrame stack trace information is provided in a loaded section, known as the
 @code{.sframe} section.  When available, the @code{.sframe} section appears in
 a new segment of its own, PT_GNU_SFRAME.
 
-The SFrame format is currently supported only for select ABIs, namely, AMD64
-and AAPCS64.
+The SFrame format is currently supported only for select ABIs, namely, AMD64,
+AAPCS64, and s390x.
 
 A portion of the SFrame format follows an unaligned on-disk representation.
 Some data structures, however, (namely the SFrame header and the SFrame
@@ -139,6 +139,14 @@ bytes to the start PC of the associated function from the field itself.
 bytes to the start PC of the associated function from the start of the SFrame
 section.
  @end itemize
+@item
+Add a new ABI/arch identifier SFRAME_ABI_S390X_ENDIAN_BIG for the s390
+architecture (64-bit) s390x ABI.  Other s390x-specific backward compatible
+changes including the following helper definitions have been incrementally
+added to SFrame version 2 only:
+ @itemize @minus
+  @item SFRAME_S390X_SP_VAL_OFFSET: SP value offset from CFA.
+ @end itemize
 @end itemize
 
 SFrame version 1 is now obsolete and should not be used.
@@ -421,6 +429,10 @@ in the format.
 @item @code{SFRAME_ABI_AMD64_ENDIAN_LITTLE}
 @tab 3 @tab AMD64 little-endian
 
+@tindex SFRAME_ABI_S390X_ENDIAN_BIG
+@item @code{SFRAME_ABI_S390X_ENDIAN_BIG}
+@tab 4 @tab s390x big-endian
+
 @end multitable
 
 The presence of an explicit identification of ABI/arch in SFrame may allow
@@ -794,6 +806,7 @@ auxiliary SFrame header, etc., if used, must also be outlined here.
 @menu
 * AMD64::
 * AArch64::
+* s390x::
 @end menu
 
 @node AMD64
@@ -850,6 +863,50 @@ Hence, in summary:
 @item 3 @tab FP = CFA + offset3
 @end multitable
 
+@node s390x
+@section s390x
+
+A stack tracer implementation must initialize the SP to the designated SP
+register value, the FP to the preferred FP register value, and the RA to the
+designated RA register value in the topmost stack frame of the callchain.  This
+is required, as either the SP or FP is used as CFA base register and as the FP
+and/or RA are not necessarily saved on the stack.  For RA this may only be the
+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.
+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.
+
+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.
+
+Given the nature of things, the number of stack offsets seen on s390x per
+SFrame FRE is either 1, 2, or 3.
+
+Hence, in summary:
+
+@multitable {Offset ID} {Interpretation in s390x in X}
+@headitem Offset ID @tab Interpretation in s390x
+@item 1 @tab CFA = @code{BASE_REG} + offset1
+@item 2 @tab RA = CFA + offset2
+@item 3 @tab FP = CFA + offset3
+@end multitable
+
+The s390x ELF ABI defines the CFA as stack pointer (SP) at call site +160.  The
+SP can therefore be obtained using the SP value offset from CFA
+@code{SFRAME_S390X_SP_VAL_OFFSET} of -160 as follows:
+SP = CFA + @code{SFRAME_S390X_SP_VAL_OFFSET}
+
 @node Generating Stack Traces using SFrame
 @appendix Generating Stack Traces using SFrame
 
@@ -913,7 +970,7 @@ SFrame section.
         fp_offset = sframe_fre_get_fp_offset (fre);
 
         cfa = base_reg_val + cfa_offset;
-        next_frame->sp = cfa;
+        next_frame->sp = cfa [+ SFRAME_S390X_SP_VAL_OFFSET on s390x];
 
         ra_stack_loc = cfa + ra_offset;
         // Get the address stored in the stack location.
index 977abf5ef0a1e29667d8390ebc952d811e7d0b84..ba01f06a09b4a3876812170f3d0fd11b753e1730 100644 (file)
@@ -166,6 +166,7 @@ need_swapping (int endian)
       case SFRAME_ABI_AMD64_ENDIAN_LITTLE:
        return !is_little;
       case SFRAME_ABI_AARCH64_ENDIAN_BIG:
+      case SFRAME_ABI_S390X_ENDIAN_BIG:
        return is_little;
       default:
        break;