diff --git a/gas/gen-sframe.c b/gas/gen-sframe.c index 3d727b6475d..2741a8f93af 100644 --- a/gas/gen-sframe.c +++ b/gas/gen-sframe.c @@ -1133,6 +1133,36 @@ sframe_xlate_do_val_offset (const struct sframe_xlate_ctx *xlate_ctx ATTRIBUTE_U 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, + SFRAME_V2_S390X_OFFSET_ENCODE_REGNUM (cfi_insn->u.rr.reg2)); + else if (sframe_ra_tracking_p () + && cfi_insn->u.rr.reg1 == SFRAME_CFA_RA_REG) + sframe_fre_set_ra_track (cur_fre, + SFRAME_V2_S390X_OFFSET_ENCODE_REGNUM (cfi_insn->u.rr.reg2)); + + return SFRAME_XLATE_OK; +} + /* Translate DW_CFA_register into SFrame context. Return SFRAME_XLATE_OK if success. */ @@ -1140,6 +1170,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_S390X_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-s390x-fpra-register-1.d b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-register-1.d new file mode 100644 index 00000000000..aa1b19511a9 --- /dev/null +++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-register-1.d @@ -0,0 +1,22 @@ +#name: SFrame generation on s390x - RA and then FP saved in registers +#objdump: --sframe=.sframe +#... +Contents of the SFrame section .sframe: + + Header : + + Version: SFRAME_VERSION_2 + Flags: SFRAME_F_FDE_FUNC_START_PCREL + Num FDEs: 1 + Num FREs: 5 + + Function Index : + + func idx \[0\]: pc = 0x0, size = 26 bytes + STARTPC +CFA +FP +RA + + 0+0000 +sp\+160 +u +u + + 0+0004 +sp\+160 +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-s390x-fpra-register-err-2.s b/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-register-1.s similarity index 100% rename from gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-register-err-2.s rename to gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-register-1.s 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 index ad83f650cd6..f6854c26814 100644 --- 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 @@ -1,6 +1,6 @@ -#name: SFrame generation on s390x - FP and then RA saved in register +#name: SFrame generation on s390x - FP without RA saved in registers #as: --gsframe -#warning: FP register 11 in .cfi_register +#warning: FP without RA on stack #objdump: --sframe=.sframe #... Contents of the SFrame section .sframe: 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 deleted file mode 100644 index 7dde59c8c07..00000000000 --- a/gas/testsuite/gas/cfi-sframe/cfi-sframe-s390x-fpra-register-err-2.d +++ /dev/null @@ -1,15 +0,0 @@ -#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.exp b/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp index d17cd767cc6..1e7fc9e03ca 100644 --- a/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp +++ b/gas/testsuite/gas/cfi-sframe/cfi-sframe.exp @@ -120,6 +120,6 @@ if { [istarget "s390x*-*-*"] && [gas_sframe_check] } then { 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-1" run_dump_test "cfi-sframe-s390x-fpra-register-err-1" - run_dump_test "cfi-sframe-s390x-fpra-register-err-2" } diff --git a/include/sframe-api.h b/include/sframe-api.h index 3dc18b6ad39..cb94f358b3d 100644 --- a/include/sframe-api.h +++ b/include/sframe-api.h @@ -214,12 +214,18 @@ extern int32_t sframe_fre_get_cfa_offset (sframe_decoder_ctx *dtcx, sframe_frame_row_entry *fre, int *errp); -/* Get the FP offset from the FRE. If the offset is invalid, sets errp. */ +/* Get the FP offset from the FRE. If the offset is invalid, sets errp. + + 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_fp_offset (sframe_decoder_ctx *dctx, sframe_frame_row_entry *fre, int *errp); -/* Get the RA offset from the FRE. If the offset is invalid, sets errp. */ +/* Get the RA offset from the FRE. If the offset is invalid, sets errp. + + 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 (sframe_decoder_ctx *dctx, sframe_frame_row_entry *fre, int *errp); diff --git a/include/sframe.h b/include/sframe.h index 0278a8dc47c..7bbdf04db17 100644 --- a/include/sframe.h +++ b/include/sframe.h @@ -358,6 +358,19 @@ typedef struct sframe_frame_row_entry_addr4 SP value offset from CFA is -160. */ #define SFRAME_S390X_SP_VAL_OFFSET (-160) +/* On s390x, the FP and RA registers can be saved either on the stack or, + in case of leaf functions, in registers. Store DWARF register numbers + encoded as offset by using the least-significant bit (LSB) as indicator: + - LSB=0: Stack offset. The s390x ELF ABI mandates that stack register + slots must be 8-byte aligned. + - LSB=1: DWARF register number shifted to the left by one. */ +#define SFRAME_V2_S390X_OFFSET_IS_REGNUM(offset) \ + ((offset) & 1) +#define SFRAME_V2_S390X_OFFSET_ENCODE_REGNUM(regnum) \ + (((regnum) << 1) | 1) +#define SFRAME_V2_S390X_OFFSET_DECODE_REGNUM(offset) \ + ((offset) >> 1) + #ifdef __cplusplus } #endif diff --git a/libsframe/doc/sframe-spec.texi b/libsframe/doc/sframe-spec.texi index 57e116356a1..1938207233c 100644 --- a/libsframe/doc/sframe-spec.texi +++ b/libsframe/doc/sframe-spec.texi @@ -146,6 +146,12 @@ 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. + @item SFRAME_V2_S390X_OFFSET_IS_REGNUM: Test whether FP/RA offset is an encoded +DWARF register number. + @item SFRAME_V2_S390X_OFFSET_ENCODE_REGNUM: Encode a DWARF register number as an +FP/RA offset. + @item SFRAME_V2_S390X_OFFSET_DECODE_REGNUM: Decode a DWARF register number from +an FP/RA offset. @end itemize @end itemize @@ -792,10 +798,11 @@ This section covers the ABI/arch-specific definition of the SFrame file format. Currently, the only part of the SFrame file format definition that is ABI/arch-specific is the interpretation of the variable number of bytes at the -tail end of each SFrame FRE. Currently, these bytes are only used for -representing stack offsets (for all the currently supported ABIs). It is -recommended to peruse this section along with @xref{SFrame Frame Row Entries} -for clarity of context. +tail end of each SFrame FRE. Currently, these bytes are used for representing +stack offsets (for AMD64 and AARCH64 ABIs). For s390x ABI, the interpretation +of these bytes may be stack offsets or even register numbers. It is recommended +to peruse this section along with @xref{SFrame Frame Row Entries} for clarity of +context. Future ABIs must specify the algorithm for identifying the appropriate SFrame FRE stack offsets in this chapter. This should inevitably include the @@ -890,16 +897,30 @@ 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. +In leaf functions the RA and FP may be saved in other registers, such as +floating-point registers (FPRs), instead of on the stack. To represent this +in the SFrame stack trace format the DWARF register number is encoded as +RA/FP offset using the least-significant bit (LSB) as indication: +offset = (regnum << 1) | 1. A LSB of zero indicates a stack slot offset. +A LSB of one indicates a DWARF register number, which is interpreted as: +regnum = offset >> 1. Given the nature of leaf functions, this can only occur +in the topmost frame during stack tracing. It is recommended that a stack +tracer implementation performs the required checks to ensure that restoring +FP and RA from the said register locations is done only for topmost stack +frame in the callchain. + +Given the nature of things, the number of stack offsets and/or register numbers +seen on s390x per SFrame FRE is either 1, 2, or 3. Hence, in summary: -@multitable {Offset ID} {Interpretation in s390x in X} +@multitable @columnfractions .15 .85 @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 +@item 2 @tab RA stack slot = CFA + offset2, if (offset2 & 1 == 0) + @*RA register number = offset2 >> 1, if (offset2 & 1 == 1) +@item 3 @tab FP stack slot = CFA + offset3, if (offset3 & 1 == 0) + @*FP register number = offset3 >> 1, if (offset3 & 1 == 1) @end multitable The s390x ELF ABI defines the CFA as stack pointer (SP) at call site +160. The diff --git a/libsframe/sframe-dump.c b/libsframe/sframe-dump.c index 47ac00e3159..f17ff64e122 100644 --- a/libsframe/sframe-dump.c +++ b/libsframe/sframe-dump.c @@ -38,6 +38,14 @@ is_sframe_abi_arch_aarch64 (sframe_decoder_ctx *sfd_ctx) return aarch64_p; } +/* Return TRUE if the SFrame section is associated with the s390x ABI. */ + +static bool +is_sframe_abi_arch_s390x (sframe_decoder_ctx *sfd_ctx) +{ + return sframe_decoder_get_abi_arch (sfd_ctx) == SFRAME_ABI_S390X_ENDIAN_BIG; +} + static void dump_sframe_header_flags (sframe_decoder_ctx *sfd_ctx) { @@ -186,7 +194,13 @@ 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_s390x (sfd_ctx) + && SFRAME_V2_S390X_OFFSET_IS_REGNUM (fp_offset)) + sprintf (temp, "r%d", SFRAME_V2_S390X_OFFSET_DECODE_REGNUM (fp_offset)); + else + sprintf (temp, "c%+d", fp_offset); + } else strcpy (temp, "u"); printf ("%-10s", temp); @@ -198,7 +212,13 @@ 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_s390x (sfd_ctx) + && SFRAME_V2_S390X_OFFSET_IS_REGNUM (ra_offset)) + sprintf (temp, "r%d", SFRAME_V2_S390X_OFFSET_DECODE_REGNUM (ra_offset)); + else + sprintf (temp, "c%+d", ra_offset); + } else strcpy (temp, "u"); diff --git a/libsframe/sframe.c b/libsframe/sframe.c index ba01f06a09b..af40e5b4fad 100644 --- a/libsframe/sframe.c +++ b/libsframe/sframe.c @@ -701,7 +701,10 @@ sframe_fre_get_cfa_offset (sframe_decoder_ctx *dctx ATTRIBUTE_UNUSED, return sframe_get_fre_offset (fre, SFRAME_FRE_CFA_OFFSET_IDX, errp); } -/* Get the FP offset from the FRE. If the offset is invalid, sets errp. */ +/* Get the FP offset from the FRE. If the offset is invalid, sets errp. + + For s390x the offset may be an encoded register number, indicated by + LSB set to one, which is only valid in the topmost frame. */ int32_t sframe_fre_get_fp_offset (sframe_decoder_ctx *dctx, @@ -728,7 +731,10 @@ sframe_fre_get_fp_offset (sframe_decoder_ctx *dctx, return sframe_get_fre_offset (fre, fp_offset_idx, errp); } -/* Get the RA offset from the FRE. If the offset is invalid, sets errp. */ +/* Get the RA offset from the FRE. If the offset is invalid, sets errp. + + For s390x the offset may be an encoded register number, indicated by + LSB set to one, which is only valid in the topmost frame. */ int32_t sframe_fre_get_ra_offset (sframe_decoder_ctx *dctx,