diff --git a/gas/gen-sframe.c b/gas/gen-sframe.c
index 93a5eebcc9d8..4cc86eb6c815 100644
--- a/gas/gen-sframe.c
+++ b/gas/gen-sframe.c
@@ -1150,6 +1150,37 @@ sframe_xlate_do_val_offset (struct sframe_xlate_ctx *xlate_ctx ATTRIBUTE_UNUSED,
   return SFRAME_XLATE_OK;
 }
 
+/* S390-specific translate DW_CFA_register into SFrame context.
+   Return SFRAME_XLATE_OK if success.  */
+
+static int
+s390_sframe_xlate_do_register (struct sframe_xlate_ctx *xlate_ctx,
+			       struct cfi_insn_data *cfi_insn)
+{
+  /* The scratchpad FRE currently being updated with each cfi_insn
+     being interpreted.  This FRE eventually gets linked in into the
+     list of FREs for the specific function.  */
+  struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre;
+
+  gas_assert (cur_fre);
+
+  /* Change the rule for the register indicated by the register number to
+     be the specified register.  Encode the register number as offset by
+     shifting it to the left by one and setting the least-significant bit
+     (LSB).  The LSB can be used to differentiate offsets from register
+     numbers, as offsets from CFA are always a multiple of -8 on s390x.  */
+  if (cfi_insn->u.rr.reg1 == SFRAME_CFA_FP_REG)
+    sframe_fre_set_bp_track (cur_fre, cfi_insn->u.rr.reg2 << 1 | 1);
+#ifdef SFRAME_FRE_RA_TRACKING
+  else if (sframe_ra_tracking_p ()
+	   && cfi_insn->u.rr.reg1 == SFRAME_CFA_RA_REG)
+    sframe_fre_set_ra_track (cur_fre, cfi_insn->u.rr.reg2 << 1 | 1);
+#endif
+
+  /* Safe to skip.  */
+  return SFRAME_XLATE_OK;
+}
+
 /* Translate DW_CFA_register into SFrame context.
    Return SFRAME_XLATE_OK if success.  */
 
@@ -1157,6 +1188,10 @@ static int
 sframe_xlate_do_register (struct sframe_xlate_ctx *xlate_ctx ATTRIBUTE_UNUSED,
 			  struct cfi_insn_data *cfi_insn)
 {
+  /* Conditionally invoke S390-specific implementation.  */
+  if (sframe_get_abi_arch () == SFRAME_ABI_S390_ENDIAN_BIG)
+    return s390_sframe_xlate_do_register (xlate_ctx, cfi_insn);
+
   /* Previous value of register1 is register2.  However, if the specified
      register1 is not interesting (FP or RA reg), the current DW_CFA_register
      instruction can be safely skipped without sacrificing the asynchronicity of
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-3.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-3.d
new file mode 100644
index 000000000000..722874113225
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-3.d
@@ -0,0 +1,22 @@
+#objdump: --sframe=.sframe
+#name: SFrame generation on s390
+#...
+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 +u +r16 +
+    0+0008 +sp\+160 +r17 +r16 +
+    0+0014 +sp\+160 +u +r16 +
+    0+0018 +sp\+160 +u +u +
+#pass
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-3.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-3.s
new file mode 100644
index 000000000000..1d4497178ada
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-3.s
@@ -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
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-4.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-4.d
new file mode 100644
index 000000000000..3c013ac2b6fe
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-4.d
@@ -0,0 +1,21 @@
+#name: SFrame generation on s390
+#as: --gsframe
+#objdump: --sframe=.sframe
+#...
+Contents of the SFrame section .sframe:
+
+  Header :
+
+    Version: SFRAME_VERSION_2
+    Flags: NONE
+    Num FDEs: 1
+    Num FREs: 2
+
+  Function Index :
+
+    func idx \[0\]: pc = 0x0, size = 6 bytes
+    STARTPC +CFA +FP +RA +
+    0+0000 +sp\+160 +u +u +
+    0+0004 +sp\+160 +u +r7 +
+
+#pass:
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-err-3.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-4.s
similarity index 100%
rename from gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-err-3.s
rename to gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-4.s
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-err-3.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-err-6.d
similarity index 74%
rename from gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-err-3.d
rename to gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-err-6.d
index 4b3262f02f02..57af27d26419 100644
--- a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-err-3.d
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-err-6.d
@@ -1,6 +1,6 @@
 #name: SFrame generation on s390
 #as: --gsframe
-#warning: skipping SFrame FDE due to .cfi_register specifying RA register
+#warning: skipping SFrame FDE due to FP without RA on stack
 #objdump: --sframe=.sframe
 #...
 Contents of the SFrame section .sframe:
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-err-6.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-err-6.s
new file mode 100644
index 000000000000..48b01ac5f57b
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390-err-6.s
@@ -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.exp b/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp
index f97fa9e170a2..8b97b02222b3 100644
--- a/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp
@@ -105,9 +105,11 @@ if { [istarget "aarch64*-*-*"] && [gas_sframe_check] } then {
 if { [istarget "s390x*-*-*"] && [gas_sframe_check] } then {
     run_dump_test "cfi-sframe-s390-1"
     run_dump_test "cfi-sframe-s390-2"
+    run_dump_test "cfi-sframe-s390-3"
+    run_dump_test "cfi-sframe-s390-4"
     run_dump_test "cfi-sframe-s390-err-1"
     run_dump_test "cfi-sframe-s390-err-2"
-    run_dump_test "cfi-sframe-s390-err-3"
     run_dump_test "cfi-sframe-s390-err-4"
     run_dump_test "cfi-sframe-s390-err-5"
+    run_dump_test "cfi-sframe-s390-err-6"
 }
diff --git a/libsframe/sframe-dump.c b/libsframe/sframe-dump.c
index 69633d53a33a..40ea531314ba 100644
--- a/libsframe/sframe-dump.c
+++ b/libsframe/sframe-dump.c
@@ -40,6 +40,14 @@ is_sframe_abi_arch_aarch64 (sframe_decoder_ctx *sfd_ctx)
   return aarch64_p;
 }
 
+/* Return TRUE if the SFrame section is associated with the s390 ABIs.  */
+
+static bool
+is_sframe_abi_arch_s390 (sframe_decoder_ctx *sfd_ctx)
+{
+  return sframe_decoder_get_abi_arch (sfd_ctx) == SFRAME_ABI_S390_ENDIAN_BIG;
+}
+
 static void
 dump_sframe_header (sframe_decoder_ctx *sfd_ctx)
 {
@@ -175,7 +183,12 @@ dump_sframe_func_with_fres (sframe_decoder_ctx *sfd_ctx,
 
       /* Dump SP/FP info.  */
       if (err[1] == 0)
-	sprintf (temp, "c%+d", fp_offset);
+	{
+	  if (is_sframe_abi_arch_s390 (sfd_ctx) && (fp_offset & 1))
+	    sprintf (temp, "r%d", fp_offset >> 1);
+	  else
+	    sprintf (temp, "c%+d", fp_offset);
+	}
       else
 	strcpy (temp, "u");
       printf ("%-10s", temp);
@@ -187,7 +200,12 @@ dump_sframe_func_with_fres (sframe_decoder_ctx *sfd_ctx,
 	  != SFRAME_CFA_FIXED_RA_INVALID)
 	strcpy (temp, "f");
       else if (err[2] == 0)
-	sprintf (temp, "c%+d", ra_offset);
+	{
+	  if (is_sframe_abi_arch_s390 (sfd_ctx) && (ra_offset & 1))
+	    sprintf (temp, "r%d", ra_offset >> 1);
+	  else
+	    sprintf (temp, "c%+d", ra_offset);
+	}
       else
 	strcpy (temp, "u");
 
