diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
index 7e41085ed91..41aa3b82e41 100644
--- a/bfd/elf64-ppc.c
+++ b/bfd/elf64-ppc.c
@@ -15698,11 +15698,15 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 	  wrel->r_addend = 0;
 
 	  /* For ld -r, remove relocations in debug sections against
-	     symbols defined in discarded sections.  Not done for
-	     non-debug to preserve relocs in .eh_frame which the
-	     eh_frame editing code expects to be present.  */
+	     sections defined in discarded sections, including sframe
+	     sections.  Not done for non-debug to preserve relocs in
+	     .eh_frame which the eh_frame editing code expects to be
+	     present.  NB: Since sframe code keeps R_PPC64_NONE reloc
+	     as is, its r_offset is wrong, we must not generate
+	     R_PPC64_NONE reloc in sframe section.  */
 	  if (bfd_link_relocatable (info)
-	      && (input_section->flags & SEC_DEBUGGING))
+	      && ((input_section->flags & SEC_DEBUGGING)
+		  || elf_section_type (input_section) == SHT_GNU_SFRAME))
 	    wrel--;
 
 	  continue;
diff --git a/gas/NEWS b/gas/NEWS
index e384d1135c0..fbc755861ce 100644
--- a/gas/NEWS
+++ b/gas/NEWS
@@ -1,5 +1,10 @@
 -*- text -*-
 
+Changes in 2.47:
+
+* Add support to generate SFrame stack trace information (.sframe)
+  from CFI directives on PowerPC 64-bit (powerpc64).
+
 Changes in 2.46:
 
 * Add support for AMD Zen6 processor.
diff --git a/gas/config/tc-ppc.c b/gas/config/tc-ppc.c
index 78e5941484c..625d1a490ee 100644
--- a/gas/config/tc-ppc.c
+++ b/gas/config/tc-ppc.c
@@ -29,6 +29,7 @@
 #include "elf/ppc.h"
 #include "elf/ppc64.h"
 #include "dwarf2dbg.h"
+#include "sframe.h"
 #endif
 
 #ifdef OBJ_XCOFF
@@ -7795,3 +7796,35 @@ tc_ppc_regname_to_dw2regnum (char *regname)
     }
   return regnum;
 }
+
+#ifdef OBJ_ELF
+
+/* Whether SFrame stack trace info is supported.  */
+
+bool
+ppc_support_sframe_p (void)
+{
+  /* At this time, SFrame is supported for PPC64 only.  */
+  return ppc_obj64;
+}
+
+/* Get the abi/arch identifier for SFrame.  */
+
+unsigned char
+ppc_sframe_get_abi_arch (void)
+{
+  unsigned char sframe_abi_arch = 0;
+
+  if (ppc_support_sframe_p ())
+    {
+      gas_assert (set_target_endian != 0);
+      if (target_big_endian)
+	sframe_abi_arch = SFRAME_ABI_PPC64_ENDIAN_BIG;
+      else
+	sframe_abi_arch = SFRAME_ABI_PPC64_ENDIAN_LITTLE;
+    }
+
+  return sframe_abi_arch;
+}
+
+#endif /* OBJ_ELF  */
diff --git a/gas/config/tc-ppc.h b/gas/config/tc-ppc.h
index fd1ea410213..200fab8af3c 100644
--- a/gas/config/tc-ppc.h
+++ b/gas/config/tc-ppc.h
@@ -370,8 +370,39 @@ extern int ppc_dwarf2_line_min_insn_length;
 #define EH_FRAME_ALIGNMENT		2
 
 #ifdef OBJ_ELF
+
 /* The target supports Object Attributes v1.  */
 #define TC_OBJ_ATTR_v1 1
+
+/* SFrame.  */
+
+/* Whether SFrame stack trace info is supported.  */
+extern bool ppc_support_sframe_p (void);
+#define support_sframe_p ppc_support_sframe_p
+
+/* The stack pointer DWARF register number for SFrame CFA tracking.  */
+#define SFRAME_CFA_SP_REG 1	/* ABI-designated SP register.  */
+
+/* The frame pointer DWARF register number for SFrame CFA and FP tracking.  */
+#define SFRAME_CFA_FP_REG 31	/* Conventionally used FP register.  */
+
+/* The return address DWARF register number for SFrame RA tracking.  */
+#define SFRAME_CFA_RA_REG DWARF2_DEFAULT_RETURN_COLUMN
+
+/* Whether SFrame return address tracking is needed.  */
+#define sframe_ra_tracking_p() true
+
+/* The fixed offset from CFA for SFrame to recover the return address.
+   (useful only when SFrame RA tracking is not needed).  */
+#define sframe_cfa_ra_offset() ((offsetT) SFRAME_CFA_FIXED_RA_INVALID)
+
+/* The abi/arch identifier for SFrame.  */
+unsigned char ppc_sframe_get_abi_arch (void);
+#define sframe_get_abi_arch ppc_sframe_get_abi_arch
+
+/* Whether SFrame FDE of type SFRAME_FDE_TYPE_FLEX be generated.  */
+#define sframe_support_flex_fde_p() true
+
 #endif /* OBJ_ELF */
 
 #endif /* TC_PPC */
diff --git a/gas/gen-sframe.c b/gas/gen-sframe.c
index 7c1b7be93bb..1315d2f5f6c 100644
--- a/gas/gen-sframe.c
+++ b/gas/gen-sframe.c
@@ -409,9 +409,11 @@ get_fre_dataword_count (const struct sframe_row_entry *sframe_fre, bool flex_p)
     }
   else if (sframe_ra_tracking_p ()
 	   && (sframe_fre->ra_loc != SFRAME_FRE_ELEM_LOC_NONE
-	       /* For s390x account padding RA data word, if FP without RA
-		  saved.  */
-	       || (sframe_get_abi_arch () == SFRAME_ABI_S390X_ENDIAN_BIG
+	       /* For PPC64 and s390x account padding RA data word,
+		  if FP without RA saved.  */
+	       || ((sframe_get_abi_arch () == SFRAME_ABI_PPC64_ENDIAN_BIG
+		    || sframe_get_abi_arch () == SFRAME_ABI_PPC64_ENDIAN_LITTLE
+		    || sframe_get_abi_arch () == SFRAME_ABI_S390X_ENDIAN_BIG)
 		   && sframe_fre->fp_loc != SFRAME_FRE_ELEM_LOC_NONE)))
     fre_dataword_count++;
 
@@ -443,8 +445,10 @@ sframe_get_fre_dataword_size (const struct sframe_row_entry *sframe_fre,
     {
       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
+      /* For PPC64 and s390x account padding RA offset, if FP without RA saved.  */
+      else if ((sframe_get_abi_arch () == SFRAME_ABI_PPC64_ENDIAN_BIG
+		|| sframe_get_abi_arch () == SFRAME_ABI_PPC64_ENDIAN_LITTLE
+		|| sframe_get_abi_arch () == SFRAME_ABI_S390X_ENDIAN_BIG)
 	       && sframe_fre->fp_loc == SFRAME_FRE_ELEM_LOC_STACK)
 	ra_offset_size = get_offset_size_in_bytes (SFRAME_FRE_RA_OFFSET_INVALID);
     }
@@ -720,8 +724,10 @@ output_sframe_row_entry_datawords (const struct sframe_func_entry *sframe_fde,
 	      dataword_func_map[idx].out_func (sframe_fre->ra_offset);
 	      fre_write_datawords++;
 	    }
-	  /* For s390x write padding RA offset, if FP without RA saved.  */
-	  else if (sframe_get_abi_arch () == SFRAME_ABI_S390X_ENDIAN_BIG
+	  /* For PPC64 and s390x write padding RA offset, if FP without RA saved.  */
+	  else if ((sframe_get_abi_arch () == SFRAME_ABI_PPC64_ENDIAN_BIG
+		    || sframe_get_abi_arch () == SFRAME_ABI_PPC64_ENDIAN_LITTLE
+		    || sframe_get_abi_arch () == SFRAME_ABI_S390X_ENDIAN_BIG)
 		   && sframe_fre->fp_loc == SFRAME_FRE_ELEM_LOC_STACK)
 	    {
 	      dataword_func_map[idx].out_func (SFRAME_FRE_RA_OFFSET_INVALID);
@@ -2436,9 +2442,11 @@ sframe_do_fde (struct sframe_xlate_ctx *xlate_ctx,
       return SFRAME_XLATE_ERR_NOTREPRESENTED;
     }
 
-  /* ABI/arch except s390x cannot represent FP without RA saved.  */
+  /* ABI/arch except PPC64 and s390x cannot represent FP without RA saved.  */
   if (sframe_ra_tracking_p ()
-      && sframe_get_abi_arch () != SFRAME_ABI_S390X_ENDIAN_BIG)
+      && (sframe_get_abi_arch () != SFRAME_ABI_PPC64_ENDIAN_BIG
+	  && sframe_get_abi_arch () != SFRAME_ABI_PPC64_ENDIAN_LITTLE
+	  && sframe_get_abi_arch () != SFRAME_ABI_S390X_ENDIAN_BIG))
     {
       struct sframe_row_entry *fre;
 
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-10.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-10.s
index 7761edaf2b7..8af0997be72 100644
--- a/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-10.s
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-10.s
@@ -6,7 +6,7 @@
 	.cfi_startproc
 	.long 0
 	.cfi_def_cfa_offset 16
-	.cfi_undefined 1
 	.cfi_undefined 2
+	.cfi_undefined 3
 	.long 0
 	.cfi_endproc
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-8.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-8.s
index 50c57c9611e..f7fea786d4c 100644
--- a/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-8.s
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-common-8.s
@@ -6,7 +6,7 @@
 	.cfi_startproc
 	.long 0
 	.cfi_def_cfa_offset 16
-	.cfi_val_offset 1, 8
-	.cfi_val_offset 2, -32
+	.cfi_val_offset 2, 8
+	.cfi_val_offset 3, -32
 	.long 0
 	.cfi_endproc
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-1.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-1.d
new file mode 100644
index 00000000000..6845063aba7
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-1.d
@@ -0,0 +1,24 @@
+#name: SFrame generation on ppc64 - basic prologue/epilogue
+#as: --gsframe
+#objdump: --sframe=.sframe
+#...
+Contents of the SFrame section .sframe:
+
+  Header :
+
+    Version: SFRAME_VERSION_3
+    Flags: SFRAME_F_FDE_FUNC_START_PCREL
+    Num FDEs: 1
+    Num FREs: 6
+
+  Function Index :
+
+    func idx \[0\]: pc = 0x0, size = 44 bytes
+    STARTPC +CFA +FP +RA +
+    0+0000 +sp\+0 +u +u +
+    0+000c +sp\+0 +c\-8 +c\+16 +
+    0+0010 +sp\+176 +c\-8 +c\+16 +
+    0+0014 +fp\+176 +c\-8 +c\+16 +
+    0+001c +sp\+0 +c\-8 +c\+16 +
+    0+0028 +sp\+0 +u +u +
+#pass
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-1.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-1.s
new file mode 100644
index 00000000000..012465b47eb
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-1.s
@@ -0,0 +1,27 @@
+	.cfi_sections .sframe
+	.cfi_startproc
+	# Save all registers FIRST
+	mflr 0
+	std 0,16(1)
+	std 31,-8(1)
+	# All .cfi_offset directives together
+	.cfi_offset 65, 16
+	.cfi_offset 31, -8
+	# THEN adjust stack
+	stdu 1,-176(1)
+	.cfi_def_cfa_offset 176
+	# Set frame pointer
+	mr 31,1
+	.cfi_def_cfa_register 31
+	nop
+	# Epilogue
+	addi 1,31,176
+	.cfi_def_cfa 1, 0
+	ld 0,16(1)
+	ld 31,-8(1)
+	mtlr 0
+	# All .cfi_restore directives together
+	.cfi_restore 65
+	.cfi_restore 31
+	blr
+	.cfi_endproc
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-3.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-3.d
new file mode 100644
index 00000000000..eabd828bb0a
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-3.d
@@ -0,0 +1,19 @@
+#name: SFrame generation on ppc64 - .cfi_def_cfa_register with no previous offset
+#as: --gsframe
+#objdump: --sframe=.sframe
+#...
+Contents of the SFrame section .sframe:
+
+  Header :
+
+    Version: SFRAME_VERSION_3
+    Flags: SFRAME_F_FDE_FUNC_START_PCREL
+    Num FDEs: 1
+    Num FREs: 1
+
+  Function Index :
+
+    func idx \[0\]: pc = 0x0, size = 0 bytes
+    STARTPC +CFA +FP +RA +
+    0+0000 +fp\+0 +u +u +
+#pass
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-3.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-3.s
new file mode 100644
index 00000000000..eecf4b8fcfd
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-3.s
@@ -0,0 +1,4 @@
+# Although not a useful construct by itself, ensure graceful handling.
+	.cfi_startproc
+	.cfi_def_cfa_register 31
+	.cfi_endproc
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-err-3.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-err-3.d
new file mode 100644
index 00000000000..804714b07be
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-err-3.d
@@ -0,0 +1,15 @@
+#name: SFrame generation on ppc64 - non-default RA register
+#as: --gsframe
+#warning: no SFrame FDE emitted; non-default RA register 3
+#objdump: --sframe=.sframe
+#...
+Contents of the SFrame section .sframe:
+
+  Header :
+
+    Version: SFRAME_VERSION_3
+    Flags: SFRAME_F_FDE_FUNC_START_PCREL
+    Num FDEs: 0
+    Num FREs: 0
+
+#pass
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-err-3.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-err-3.s
new file mode 100644
index 00000000000..6dfdfcbe92b
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-err-3.s
@@ -0,0 +1,5 @@
+	.cfi_sections .sframe
+	.cfi_startproc
+	.cfi_return_column 3
+	blr
+	.cfi_endproc
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-fpra-offset-1.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-fpra-offset-1.d
new file mode 100644
index 00000000000..eb8fd87c775
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-fpra-offset-1.d
@@ -0,0 +1,23 @@
+#name: SFrame generation on ppc64 - RA and then FP saved on stack
+#as: --gsframe
+#objdump: --sframe=.sframe
+#...
+Contents of the SFrame section .sframe:
+
+  Header :
+
+    Version: SFRAME_VERSION_3
+    Flags: SFRAME_F_FDE_FUNC_START_PCREL
+    Num FDEs: 1
+    Num FREs: 5
+
+  Function Index :
+
+    func idx \[0\]: pc = 0x0, size = [0-9]+ bytes
+    STARTPC +CFA +FP +RA +
+    0+0000 +sp\+0 +u +u +
+    0+0008 +sp\+0 +u +c\+112 +
+    0+000c +sp\+0 +c\+88 +c\+112 +
+    0+0018 +sp\+0 +u +c\+112 +
+    0+0020 +sp\+0 +u +u +
+#pass
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-fpra-offset-1.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-fpra-offset-1.s
new file mode 100644
index 00000000000..6053b54534a
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-fpra-offset-1.s
@@ -0,0 +1,17 @@
+	.cfi_sections .sframe
+	.cfi_startproc
+	mflr 0
+	std 0,112(1)
+	.cfi_offset 65, 112
+	std 31,88(1)
+	.cfi_offset 31, 88
+	li 31,0
+	li 0,0
+.Lreturn:
+	ld 31,88(1)
+	.cfi_restore 31
+	ld 0,112(1)
+	mtlr 0
+	.cfi_restore 65
+	blr
+	.cfi_endproc
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-fpra-offset-2.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-fpra-offset-2.d
new file mode 100644
index 00000000000..e23bcec3a77
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-fpra-offset-2.d
@@ -0,0 +1,23 @@
+#name: SFrame generation on ppc64 - FP and then RA saved on stack
+#as: --gsframe
+#objdump: --sframe=.sframe
+#...
+Contents of the SFrame section .sframe:
+
+  Header :
+
+    Version: SFRAME_VERSION_3
+    Flags: SFRAME_F_FDE_FUNC_START_PCREL
+    Num FDEs: 1
+    Num FREs: 5
+
+  Function Index :
+
+    func idx \[0\]: pc = 0x0, size = [0-9]+ bytes
+    STARTPC +CFA +FP +RA +
+    0+0000 +sp\+0 +u +u +
+    0+0004 +sp\+0 +c\+88 +U +
+    0+000c +sp\+0 +c\+88 +c\+112 +
+    0+001c +sp\+0 +c\+88 +U +
+    0+0020 +sp\+0 +u +u +
+#pass
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-fpra-offset-2.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-fpra-offset-2.s
new file mode 100644
index 00000000000..67798c24191
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-fpra-offset-2.s
@@ -0,0 +1,18 @@
+	.cfi_sections .sframe
+	.cfi_startproc
+	# No stack allocation, CFA = SP+0 implicitly
+	std 31,88(1)            # Save FP at SP+88
+	.cfi_offset 31, 88      # FP at CFA+88
+	mflr 0                  # Get LR
+	std 0,112(1)            # Save RA at SP+112
+	.cfi_offset 65, 112     # RA at CFA+112
+	li 31,0                 # Clear registers
+	li 0,0
+.Lreturn:
+	ld 0,112(1)             # Restore RA
+	mtlr 0
+	.cfi_restore 65
+	ld 31,88(1)             # Restore FP
+	.cfi_restore 31
+	blr
+	.cfi_endproc
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-fpra-register-2.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-fpra-register-2.d
new file mode 100644
index 00000000000..1b1aa874eea
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-fpra-register-2.d
@@ -0,0 +1,23 @@
+#name: SFrame generation on ppc64 - FP and then RA saved in FPR registers
+#as:
+#objdump: --sframe=.sframe
+#...
+Contents of the SFrame section .sframe:
+
+  Header :
+
+    Version: SFRAME_VERSION_3
+    Flags: SFRAME_F_FDE_FUNC_START_PCREL
+    Num FDEs: 1
+    Num FREs: 5
+
+  Function Index :
+
+    func idx \[0\]: pc = 0x0, size = 28 bytes, attr = "F"
+    STARTPC +CFA +FP +RA +
+    0+0000 +sp\+0 +u +u +
+    0+0004 +sp\+0 +r2\+0 +U +
+    0+0008 +sp\+0 +r2\+0 +r0\+0 +
+    0+0014 +sp\+0 +r2\+0 +U +
+    0+0018 +sp\+0 +u +u +
+#pass
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-fpra-register-2.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-fpra-register-2.s
new file mode 100644
index 00000000000..6ebe90befc9
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-fpra-register-2.s
@@ -0,0 +1,17 @@
+	.cfi_sections .sframe
+	.cfi_startproc
+	mr 2,31              # Move r31 (FP) to r2
+	.cfi_register 31, 2  # FP is now in r2
+	mflr 0               # Get LR into r0
+	.cfi_register 65, 0  # RA is now in r0
+	li 31,0
+	li 0,0
+.Lreturn:
+	# Restore RA from r0
+	mtlr 0
+	.cfi_restore 65
+	# Restore FP from r2
+	mr 31,2
+	.cfi_restore 31
+	blr
+	.cfi_endproc
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-non-spfp-cfa-2.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-non-spfp-cfa-2.d
new file mode 100644
index 00000000000..5718986badd
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-non-spfp-cfa-2.d
@@ -0,0 +1,26 @@
+#name: SFrame generation on ppc64 - .cfi_def_cfa with non-SP/FP register
+#as: --gsframe
+#objdump: --sframe=.sframe
+#...
+Contents of the SFrame section .sframe:
+
+  Header :
+
+    Version: SFRAME_VERSION_3
+    Flags: SFRAME_F_FDE_FUNC_START_PCREL
+    Num FDEs: 1
+    Num FREs: 8
+
+  Function Index :
+
+    func idx \[0\]: pc = 0x0, size = 188 bytes, attr = "F"
+    STARTPC +CFA +FP +RA +
+    0+0000 +sp\+0 +u +u +
+    0+0008 +sp\+0 +u +c\+16 +
+    0+0050 +sp\+0 +c\-8 +c\+16 +
+    0+0054 +sp\+288 +c\-8 +c\+16 +
+    0+0058 +r30\+288 +c\-8 +c\+16 +
+    0+0060 +sp\+288 +c\-8 +c\+16 +
+    0+00b0 +sp\+0 +u +u +
+    0+00b4 +r30\+288 +c\-8 +c\+16 +
+#pass
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-non-spfp-cfa-2.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-non-spfp-cfa-2.s
new file mode 100644
index 00000000000..70ec5031021
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-non-spfp-cfa-2.s
@@ -0,0 +1,100 @@
+# Test SFrame generation with non-FP CFA base register
+#
+# This test uses r30 (instead of conventional FP r31) as the CFA
+# base register to verify that SFrame flexible FDE correctly handles
+# arbitrary CFA base registers for topmost frames.
+	.cfi_sections .sframe
+	.cfi_startproc
+	mflr 0
+	std 0,16(1)
+	.cfi_offset 65, 16
+	std 14,-144(1)
+	std 15,-136(1)
+	std 16,-128(1)
+	std 17,-120(1)
+	std 18,-112(1)
+	std 19,-104(1)
+	std 20,-96(1)
+	std 21,-88(1)
+	std 22,-80(1)
+	std 23,-72(1)
+	std 24,-64(1)
+	std 25,-56(1)
+	std 26,-48(1)
+	std 27,-40(1)
+	std 28,-32(1)
+	std 29,-24(1)
+	std 30,-16(1)
+	std 31,-8(1)
+	.cfi_offset 14, -144
+	.cfi_offset 15, -136
+	.cfi_offset 16, -128
+	.cfi_offset 17, -120
+	.cfi_offset 18, -112
+	.cfi_offset 19, -104
+	.cfi_offset 20, -96
+	.cfi_offset 21, -88
+	.cfi_offset 22, -80
+	.cfi_offset 23, -72
+	.cfi_offset 24, -64
+	.cfi_offset 25, -56
+	.cfi_offset 26, -48
+	.cfi_offset 27, -40
+	.cfi_offset 28, -32
+	.cfi_offset 29, -24
+	.cfi_offset 30, -16
+	.cfi_offset 31, -8
+	stdu 1,-288(1)
+	.cfi_def_cfa_offset 288
+	mr 30,1
+	.cfi_def_cfa 30, 288
+	addi 1,1,-64
+.Lreturn:
+	addi 1,30,288
+	.cfi_remember_state
+	.cfi_def_cfa 1, 288
+	ld 0,16(1)
+	ld 14,-144(1)
+	ld 15,-136(1)
+	ld 16,-128(1)
+	ld 17,-120(1)
+	ld 18,-112(1)
+	ld 19,-104(1)
+	ld 20,-96(1)
+	ld 21,-88(1)
+	ld 22,-80(1)
+	ld 23,-72(1)
+	ld 24,-64(1)
+	ld 25,-56(1)
+	ld 26,-48(1)
+	ld 27,-40(1)
+	ld 28,-32(1)
+	ld 29,-24(1)
+	ld 30,-16(1)
+	ld 31,-8(1)
+	mtlr 0
+	.cfi_restore 65
+	.cfi_restore 14
+	.cfi_restore 15
+	.cfi_restore 16
+	.cfi_restore 17
+	.cfi_restore 18
+	.cfi_restore 19
+	.cfi_restore 20
+	.cfi_restore 21
+	.cfi_restore 22
+	.cfi_restore 23
+	.cfi_restore 24
+	.cfi_restore 25
+	.cfi_restore 26
+	.cfi_restore 27
+	.cfi_restore 28
+	.cfi_restore 29
+	.cfi_restore 30
+	.cfi_restore 31
+	.cfi_def_cfa_offset 0
+	blr
+	.cfi_restore_state
+	addi 1,1,-64
+	b .Lreturn
+	.cfi_endproc
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-pr33756.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-pr33756.d
new file mode 100644
index 00000000000..359f963aab4
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-pr33756.d
@@ -0,0 +1,24 @@
+#as: --gsframe
+#objdump: --sframe=.sframe
+#name: SFrame generation on ppc64 - .cfi_remember_state after .cfi_startproc PR gas/33756
+#...
+Contents of the SFrame section .sframe:
+
+  Header :
+
+    Version: SFRAME_VERSION_3
+    Flags: SFRAME_F_FDE_FUNC_START_PCREL
+    Num FDEs: 1
+    Num FREs: 5
+
+  Function Index :
+
+    func idx \[0\]: pc = 0x0, size = 36 bytes
+    STARTPC +CFA +FP +RA +
+    0+0000 +sp\+0 +u +u +
+    0+0008 +sp\+0 +u +c\+16 +
+    0+000c +sp\+0 +c\-8 +c\+16 +
+    0+0010 +sp\+176 +c\-8 +c\+16 +
+    0+0020 +sp\+0 +u +u +
+
+#pass
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-pr33756.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-pr33756.s
new file mode 100644
index 00000000000..e4852b1c7c2
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-pr33756.s
@@ -0,0 +1,20 @@
+	.text
+	.type foo, @function
+foo:
+	.cfi_startproc
+	.cfi_remember_state
+	mflr 0                 # Move LR to r0
+	std 0,16(1)            # Save RA (LR)
+	.cfi_offset 65, 16     # Track RA sp+16
+	std 31,-8(1)           # Save FP
+	.cfi_offset 31, -8     # Track RA at sp-8
+	addi 1,1,-176
+	.cfi_adjust_cfa_offset 176
+	addi 1,1,176
+	ld 31,-8(1)            # Restore FP
+	ld 0,16(1)             # Restore RA
+	mtlr 0
+	.cfi_restore_state
+	blr
+	.cfi_endproc
+	.size foo, .-foo
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-ra-undefined-1.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-ra-undefined-1.d
new file mode 100644
index 00000000000..0807ab48128
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-ra-undefined-1.d
@@ -0,0 +1,24 @@
+#name: SFrame generation on ppc64 - .cfi_undefined RA
+#as: --gsframe
+#objdump: --sframe=.sframe
+#...
+Contents of the SFrame section .sframe:
+
+  Header :
+
+    Version: SFRAME_VERSION_3
+    Flags: SFRAME_F_FDE_FUNC_START_PCREL
+    Num FDEs: 1
+    Num FREs: 5
+
+  Function Index :
+
+    func idx \[0\]: pc = 0x0, size = [0-9]+ bytes
+    STARTPC +CFA +FP +RA +
+    0+0000 +sp\+0 +u +u +
+    0+0008 +sp\+0 +u +c\+16 +
+    0+000c +sp\+0 +c\-8 +c\+16 +
+    0+0010 +RA undefined
+    0+001c +sp\+0 +u +u +
+
+#pass
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-ra-undefined-1.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-ra-undefined-1.s
new file mode 100644
index 00000000000..bffbf3658c9
--- /dev/null
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-ppc64-ra-undefined-1.s
@@ -0,0 +1,15 @@
+	.cfi_startproc
+	mflr	0
+	std	0,16(1)          # Save LR at SP+16
+	.cfi_offset 65, 16
+	std	31,-8(1)         # Save FP at SP-8
+	.cfi_offset 31, -8
+	nop
+	.cfi_undefined 65
+	ld	31,-8(1)
+	ld	0,16(1)
+	mtlr	0
+	.cfi_restore 65
+	.cfi_restore 31
+	blr
+	.cfi_endproc
diff --git a/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp b/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp
index a73e5c84033..d1758ab8214 100644
--- a/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp
+++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp
@@ -28,7 +28,7 @@ proc gas_x86_64_check { } {
 
 # common tests
 if  { ([istarget "x86_64-*-*"] || [istarget "aarch64*-*-*"]
-       || [istarget "s390x-*-*"]) \
+       || [istarget "s390x-*-*"] || [istarget "powerpc64*-*-*"]) \
       && [gas_sframe_check] } then {
 
     global ASFLAGS
@@ -112,3 +112,16 @@ if { [istarget "s390x*-*-*"] && [gas_sframe_check] } then {
     run_dump_test "cfi-sframe-s390x-non-spfp-cfa-1"
     run_dump_test "cfi-sframe-s390x-non-spfp-cfa-2"
 }
+
+# ppc64 specific tests
+if { [istarget "powerpc64*-*-*"] && [gas_sframe_check] } then {
+    run_dump_test "cfi-sframe-ppc64-1"
+    run_dump_test "cfi-sframe-ppc64-ra-undefined-1"
+    run_dump_test "cfi-sframe-ppc64-fpra-offset-1"
+    run_dump_test "cfi-sframe-ppc64-fpra-offset-2"
+    run_dump_test "cfi-sframe-ppc64-fpra-register-2"
+    run_dump_test "cfi-sframe-ppc64-non-spfp-cfa-2"
+    run_dump_test "cfi-sframe-ppc64-3"
+    run_dump_test "cfi-sframe-ppc64-err-3"
+    run_dump_test "cfi-sframe-ppc64-pr33756"
+}
diff --git a/include/sframe-api.h b/include/sframe-api.h
index bd297a65395..0f45bb811ae 100644
--- a/include/sframe-api.h
+++ b/include/sframe-api.h
@@ -239,10 +239,10 @@ sframe_fre_get_fp_offset (const 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.  */
+   For PPC64 and 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
 sframe_fre_get_ra_offset (const sframe_decoder_ctx *dctx,
 			  const sframe_frame_row_entry *fre,
diff --git a/include/sframe.h b/include/sframe.h
index 2c9019cb64d..89211e1099e 100644
--- a/include/sframe.h
+++ b/include/sframe.h
@@ -110,6 +110,8 @@ extern "C"
 #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.  */
+#define SFRAME_ABI_PPC64_ENDIAN_BIG        5 /* PPC64 big endian.  */
+#define SFRAME_ABI_PPC64_ENDIAN_LITTLE     6 /* PPC64 little endian.  */
 
 /* SFrame FRE types.  */
 #define SFRAME_FRE_TYPE_ADDR1	0
diff --git a/libsframe/doc/sframe-spec.texi b/libsframe/doc/sframe-spec.texi
index cd76bf9f8e3..3c5fa021927 100644
--- a/libsframe/doc/sframe-spec.texi
+++ b/libsframe/doc/sframe-spec.texi
@@ -88,7 +88,7 @@ of type @code{PT_GNU_SFRAME}.  An ELF SFrame section will have the type
 @code{SHT_GNU_SFRAME}.
 
 The SFrame format is currently supported only for select ABIs, namely, AMD64,
-AAPCS64, and s390x.
+AAPCS64, PPC64, 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
@@ -169,6 +169,9 @@ indicate an outermost frame.
 @item On s390x, use FDE type @code{SFRAME_FDE_TYPE_FLEX} to encode FP/RA
 recovery from REG, instead of encoding DWARF register number in the SFrame FRE
 variable-length data of FDE type @code{SFRAME_FDE_TYPE_DEFAULT}.
+@item
+[Errata 1] Add new ABI/arch identifiers SFRAME_ABI_PPC64_ENDIAN_BIG and
+SFRAME_ABI_PPC64_ENDIAN_LITTLE for the 64-bit PowerPC ELF ABI.
 @end itemize
 
 @node Changes from Version 1 to Version 2
@@ -522,6 +525,14 @@ in the format.
 @item @code{SFRAME_ABI_S390X_ENDIAN_BIG}
 @tab 4 @tab s390x big-endian
 
+@tindex SFRAME_ABI_PPC64_ENDIAN_BIG
+@item @code{SFRAME_ABI_PPC64_ENDIAN_BIG}
+@tab 5 @tab PPC64 big-endian
+
+@tindex SFRAME_ABI_PPC64_ENDIAN_LITTLE
+@item @code{SFRAME_ABI_PPC64_ENDIAN_LITTLE}
+@tab 6 @tab PPC64 little-endian
+
 @end multitable
 
 The presence of an explicit identification of ABI/arch in SFrame may allow
@@ -690,7 +701,7 @@ From MSB to LSB:
 in the SFrame FDE.  Two possible values: @*
 @code{SFRAME_AARCH64_PAUTH_KEY_A} (0), or @*
 @code{SFRAME_AARCH64_PAUTH_KEY_B} (1). @*
-Unsed in AMD64, s390x
+Unsed in AMD64, PPC64, s390x
 
 @item 4
 @tab @code{fde_pctype}
@@ -1026,6 +1037,7 @@ supported architectures.
 @menu
 * AMD64::
 * AArch64::
+* PPC64::
 * s390x::
 @end menu
 
@@ -1083,6 +1095,33 @@ Hence, in summary:
 @item 3 @tab FP = CFA + offset3
 @end multitable
 
+@node PPC64
+@subsection PPC64
+
+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 byte.
+
+In PPC64, the 64-bit PowerPC 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, by interpreting it as: RA = CFA + offset2.  The third stack offset is
+used to locate the FP, by interpreting it as: FP = CFA + offset3.
+
+Given the nature of things, the number of stack offsets seen on PPC64 per
+SFrame FRE is either 1 or 3.
+
+Hence, in summary:
+
+@multitable {Offset ID} {Interpretation in PPC64 in X}
+@headitem Offset ID @tab Interpretation in PPC64
+@item 1 @tab CFA = @code{BASE_REG} + offset1
+@item 2 @tab RA = CFA + offset2
+@item 3 @tab FP = CFA + offset3
+@end multitable
+
 @node s390x
 @subsection s390x
 
diff --git a/libsframe/sframe-dump.c b/libsframe/sframe-dump.c
index 56cf963566e..3fe74008ac6 100644
--- a/libsframe/sframe-dump.c
+++ b/libsframe/sframe-dump.c
@@ -52,6 +52,7 @@ typedef struct
    stack pointer and frame pointer.  */
 SFRAME_ABI_REG_MAP (amd64, SFRAME_SP (7), SFRAME_FP (6));
 SFRAME_ABI_REG_MAP (aarch64, SFRAME_SP (31), SFRAME_FP (29));
+SFRAME_ABI_REG_MAP (ppc64, SFRAME_SP (1), SFRAME_FP (31));
 SFRAME_ABI_REG_MAP (s390x, SFRAME_SP (15), SFRAME_FP (11));
 
 static const char *
@@ -70,6 +71,10 @@ sframe_get_reg_name (uint8_t abi_arch, unsigned int reg_num, char *buf,
       case SFRAME_ABI_AMD64_ENDIAN_LITTLE:
 	abi_reg_map = &amd64_sframe_abi_reg_map;
 	break;
+      case SFRAME_ABI_PPC64_ENDIAN_BIG:
+      case SFRAME_ABI_PPC64_ENDIAN_LITTLE:
+	abi_reg_map = &ppc64_sframe_abi_reg_map;
+	break;
       case SFRAME_ABI_S390X_ENDIAN_BIG:
 	abi_reg_map = &s390x_sframe_abi_reg_map;
 	break;
@@ -287,8 +292,8 @@ dump_sframe_func_fres_simple (const 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.  */
+      /* If an ABI does track RA offset, e.g. PPC64 and 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)
diff --git a/libsframe/sframe.c b/libsframe/sframe.c
index cd6bb3022db..d275ef3d02e 100644
--- a/libsframe/sframe.c
+++ b/libsframe/sframe.c
@@ -262,8 +262,10 @@ need_swapping (int endian)
     {
       case SFRAME_ABI_AARCH64_ENDIAN_LITTLE:
       case SFRAME_ABI_AMD64_ENDIAN_LITTLE:
+      case SFRAME_ABI_PPC64_ENDIAN_LITTLE:
 	return !is_little;
       case SFRAME_ABI_AARCH64_ENDIAN_BIG:
+      case SFRAME_ABI_PPC64_ENDIAN_BIG:
       case SFRAME_ABI_S390X_ENDIAN_BIG:
 	return is_little;
       default:
