On 25/03/26 14:17, Jens Remus wrote:
> On 3/24/2026 6:02 PM, Abhay Kandpal wrote:
>> Inspired by s390 commit d27d82f560a8 ("s390: Initial support to generate
>> .sframe from CFI directives in assembler").
>>
>> Support for SFrame on PowerPC 64 is only enabled for the 64-bit PowerPC
>> ELF ABI.
>>
>> The SFrame ABI/arch identifiers SFRAME_ABI_PPC64_ENDIAN_BIG and
>> SFRAME_ABI_PPC64_ENDIAN_LITTLE are introduced for PowerPC 64 big/little
>> endian and added to the SFrame format specification.
>>
>> Port x86-64 commit d7f343eaad3f ("x86-64: Remove sframe relocs against
>> discarded sections").
>>
>> Based on s390 64-bit (s390x) commit 955570f0973d ("s390: Represent FP
>> without RA saved in SFrame").
>>
>> 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 PPC64 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_ABI_PPC64_ENDIAN_BIG,
>> SFRAME_ABI_PPC64_ENDIAN_LITTLE): Define.
>> * sframe-api.h (sframe_fre_get_ra_offset): Add PPC64 to comment
>> for RA offset value of SFRAME_FRE_RA_OFFSET_INVALID indicating
>> that the RA is not saved.
>>
>> libsframe/
>> * sframe.c (need_swapping): Handle SFRAME_ABI_PPC64_ENDIAN_BIG
>> and SFRAME_ABI_PPC64_ENDIAN_LITTLE.
>> * doc/sframe-spec.texi (SFRAME_ABI_PPC64_ENDIAN_BIG,
>> SFRAME_ABI_PPC64_ENDIAN_LITTLE, PPC64): Document SFrame ABI/arch
>> identifier for PPC64, add references to 64-bit PowerPC
>> architecture, and document PPC64-specifics.
>> * sframe-dump.c (ppc64_sframe_abi_reg_map): Define register map
>> for PPC64.
>> (sframe_get_reg_name): Use PPC64 register map for PPC64.
>> (dump_sframe_func_fres_simple): Update comment to mention PPC64
>> for RA offset padding.
>>
>> gas/
>> * NEWS: Mention powerpc64 support to generate SFrame from CFI
>> directives.
>> * config/tc-ppc.h (support_sframe_p, SFRAME_CFA_SP_REG,
>> SFRAME_CFA_FP_REG, SFRAME_CFA_RA_REG, sframe_ra_tracking_p,
>> sframe_cfa_ra_offset, sframe_get_abi_arch,
>> sframe_support_flex_fde_p): Define.
>> * config/tc-ppc.c (ppc_support_sframe_p,
>> ppc_sframe_get_abi_arch): New functions. Return whether SFrame
>> is supported and the SFrame ABI/arch identifier.
>> gen-sframe.c (get_fre_dataword_count): For PPC64 account padding
>> RA offset, if FP without RA saved.
>> (sframe_get_fre_dataword_size): Likewise.
>> (output_sframe_row_entry_datawords): For PPC64 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 PPC64.
>>
>> gas/testsuite/
>> * gas/cfi-sframe/cfi-sframe.exp: Enable common SFrame tests
>> on PPC64.
>> * gas/cfi-sframe/cfi-sframe-common-8.s: Use registers 2 and 3,
>> as register 1 is SP on PPC64.
>> * gas/cfi-sframe/cfi-sframe-common-10.s: Likewise.
>> * gas/cfi-sframe/cfi-sframe-ppc64-1.s: New test.
>> * gas/cfi-sframe/cfi-sframe-ppc64-1.d: Likewise.
>> * gas/cfi-sframe/cfi-sframe-ppc64-3.s: New test.
>> * gas/cfi-sframe/cfi-sframe-ppc64-3.d: Likewise.
>> * gas/cfi-sframe/cfi-sframe-ppc64-err-3.s: New test.
>> * gas/cfi-sframe/cfi-sframe-ppc64-err-3.d: Likewise.
> * gas/cfi-sframe/cfi-sframe-ppc64-fpra-offset-1.s: New test.
> * gas/cfi-sframe/cfi-sframe-ppc64-fpra-offset-1.d: Likewise.
>
>> * gas/cfi-sframe/cfi-sframe-ppc64-fpra-offset-2.s: New test.
>> * gas/cfi-sframe/cfi-sframe-ppc64-fpra-offset-2.d: Likewise.
>> * gas/cfi-sframe/cfi-sframe-ppc64-fpra-register-2.s: New test.
>> * gas/cfi-sframe/cfi-sframe-ppc64-fpra-register-2.d: Likewise.
>> * gas/cfi-sframe/cfi-sframe-ppc64-non-spfp-cfa-2.s: New test.
>> * gas/cfi-sframe/cfi-sframe-ppc64-non-spfp-cfa-2.d: Likewise.
>> * gas/cfi-sframe/cfi-sframe-ppc64-pr33756.s: New test.
>> * gas/cfi-sframe/cfi-sframe-ppc64-pr33756.d: Likewise.
>> * gas/cfi-sframe/cfi-sframe-ppc64-ra-undefined-1.s: New test.
>> * gas/cfi-sframe/cfi-sframe-ppc64-ra-undefined-1.d: Likewise.
>>
>> bfd/
>> * elf64-ppc.c (ppc64_elf_relocate_section): Remove sframe relocs
>> against discarded sections.
>>
>> Co-authored-by: Jens Remus<jremus@linux.ibm.com>
>> Signed-off-by: Abhay Kandpal<abhay@linux.ibm.com>
> LGTM. Please see my minor comments.
>
> Let's see what Indu has to say. :-)
>
>> 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:
>> +
> Nit: IIUC above line is added by the binutils maintainer before a new
> release, so please drop that.
sure
>
>> +* 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/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
> Is it intentional that RA (= LR) and FP are saved at non-default
> offsets? Are these within the red zone on PPC64?
The test intention is to verify 'U' padding mechanism.
Although the offsets 112, 88 are not in red zone, they are above SP in the caller's frame area, I will modify
these offset for the both the test cases to use standard PPC64 ABI offset to make test more realistic and consistent across the PPC test cases.
>
>> + 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.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
> Likewise (non-default offsets intentional?).
>
>> + 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
> Thanks and regards,
> Jens
@@ -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;
@@ -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.
@@ -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 */
@@ -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 */
@@ -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;
@@ -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
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -0,0 +1,4 @@
+# Although not a useful construct by itself, ensure graceful handling.
+ .cfi_startproc
+ .cfi_def_cfa_register 31
+ .cfi_endproc
new file mode 100644
@@ -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
new file mode 100644
@@ -0,0 +1,5 @@
+ .cfi_sections .sframe
+ .cfi_startproc
+ .cfi_return_column 3
+ blr
+ .cfi_endproc
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
@@ -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"
+}
@@ -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,
@@ -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
@@ -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
@@ -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)
@@ -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: