aboutsummaryrefslogtreecommitdiff
path: root/libsframe
diff options
context:
space:
mode:
Diffstat (limited to 'libsframe')
-rw-r--r--libsframe/Makefile.in42
-rw-r--r--libsframe/doc/sframe-spec.texi123
-rw-r--r--libsframe/libsframe.ver14
-rw-r--r--libsframe/libtool-version2
-rw-r--r--libsframe/sframe-dump.c28
-rw-r--r--libsframe/sframe.c49
-rw-r--r--libsframe/testsuite/libsframe.find/find.exp5
-rw-r--r--libsframe/testsuite/libsframe.find/local.mk5
-rw-r--r--libsframe/testsuite/libsframe.find/plt-findfre-2.c201
9 files changed, 422 insertions, 47 deletions
diff --git a/libsframe/Makefile.in b/libsframe/Makefile.in
index 13f4c26..fa705dd 100644
--- a/libsframe/Makefile.in
+++ b/libsframe/Makefile.in
@@ -118,7 +118,8 @@ check_PROGRAMS = $(am__EXEEXT_1)
@HAVE_COMPAT_DEJAGNU_TRUE@ testsuite/libsframe.encode/encode-1 \
@HAVE_COMPAT_DEJAGNU_TRUE@ testsuite/libsframe.find/findfre-1 \
@HAVE_COMPAT_DEJAGNU_TRUE@ testsuite/libsframe.find/findfunc-1 \
-@HAVE_COMPAT_DEJAGNU_TRUE@ testsuite/libsframe.find/plt-findfre-1
+@HAVE_COMPAT_DEJAGNU_TRUE@ testsuite/libsframe.find/plt-findfre-1 \
+@HAVE_COMPAT_DEJAGNU_TRUE@ testsuite/libsframe.find/plt-findfre-2
subdir = .
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/../bfd/acinclude.m4 \
@@ -193,7 +194,8 @@ libsframe_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
@HAVE_COMPAT_DEJAGNU_TRUE@ testsuite/libsframe.encode/encode-1$(EXEEXT) \
@HAVE_COMPAT_DEJAGNU_TRUE@ testsuite/libsframe.find/findfre-1$(EXEEXT) \
@HAVE_COMPAT_DEJAGNU_TRUE@ testsuite/libsframe.find/findfunc-1$(EXEEXT) \
-@HAVE_COMPAT_DEJAGNU_TRUE@ testsuite/libsframe.find/plt-findfre-1$(EXEEXT)
+@HAVE_COMPAT_DEJAGNU_TRUE@ testsuite/libsframe.find/plt-findfre-1$(EXEEXT) \
+@HAVE_COMPAT_DEJAGNU_TRUE@ testsuite/libsframe.find/plt-findfre-2$(EXEEXT)
am__dirstamp = $(am__leading_dot)dirstamp
am_testsuite_libsframe_decode_be_flipping_OBJECTS = testsuite/libsframe.decode/testsuite_libsframe_decode_be_flipping-be-flipping.$(OBJEXT)
testsuite_libsframe_decode_be_flipping_OBJECTS = \
@@ -230,6 +232,11 @@ testsuite_libsframe_find_plt_findfre_1_OBJECTS = \
$(am_testsuite_libsframe_find_plt_findfre_1_OBJECTS)
testsuite_libsframe_find_plt_findfre_1_DEPENDENCIES = \
${top_builddir}/libsframe.la
+am_testsuite_libsframe_find_plt_findfre_2_OBJECTS = testsuite/libsframe.find/testsuite_libsframe_find_plt_findfre_2-plt-findfre-2.$(OBJEXT)
+testsuite_libsframe_find_plt_findfre_2_OBJECTS = \
+ $(am_testsuite_libsframe_find_plt_findfre_2_OBJECTS)
+testsuite_libsframe_find_plt_findfre_2_DEPENDENCIES = \
+ ${top_builddir}/libsframe.la
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
am__v_P_0 = false
@@ -271,7 +278,8 @@ SOURCES = $(libsframe_la_SOURCES) \
$(testsuite_libsframe_encode_encode_1_SOURCES) \
$(testsuite_libsframe_find_findfre_1_SOURCES) \
$(testsuite_libsframe_find_findfunc_1_SOURCES) \
- $(testsuite_libsframe_find_plt_findfre_1_SOURCES)
+ $(testsuite_libsframe_find_plt_findfre_1_SOURCES) \
+ $(testsuite_libsframe_find_plt_findfre_2_SOURCES)
DIST_SOURCES = $(libsframe_la_SOURCES) \
$(testsuite_libsframe_decode_be_flipping_SOURCES) \
$(testsuite_libsframe_decode_frecnt_1_SOURCES) \
@@ -279,7 +287,8 @@ DIST_SOURCES = $(libsframe_la_SOURCES) \
$(testsuite_libsframe_encode_encode_1_SOURCES) \
$(testsuite_libsframe_find_findfre_1_SOURCES) \
$(testsuite_libsframe_find_findfunc_1_SOURCES) \
- $(testsuite_libsframe_find_plt_findfre_1_SOURCES)
+ $(testsuite_libsframe_find_plt_findfre_1_SOURCES) \
+ $(testsuite_libsframe_find_plt_findfre_2_SOURCES)
AM_V_DVIPS = $(am__v_DVIPS_@AM_V@)
am__v_DVIPS_ = $(am__v_DVIPS_@AM_DEFAULT_V@)
am__v_DVIPS_0 = @echo " DVIPS " $@;
@@ -569,6 +578,9 @@ testsuite_libsframe_find_findfunc_1_CPPFLAGS = -I${top_srcdir}/../include -Wall
testsuite_libsframe_find_plt_findfre_1_SOURCES = testsuite/libsframe.find/plt-findfre-1.c
testsuite_libsframe_find_plt_findfre_1_LDADD = ${top_builddir}/libsframe.la
testsuite_libsframe_find_plt_findfre_1_CPPFLAGS = -I${top_srcdir}/../include -Wall
+testsuite_libsframe_find_plt_findfre_2_SOURCES = testsuite/libsframe.find/plt-findfre-2.c
+testsuite_libsframe_find_plt_findfre_2_LDADD = ${top_builddir}/libsframe.la
+testsuite_libsframe_find_plt_findfre_2_CPPFLAGS = -I${top_srcdir}/../include -Wall
all: config.h
$(MAKE) $(AM_MAKEFLAGS) all-am
@@ -748,6 +760,13 @@ testsuite/libsframe.find/testsuite_libsframe_find_plt_findfre_1-plt-findfre-1.$(
testsuite/libsframe.find/plt-findfre-1$(EXEEXT): $(testsuite_libsframe_find_plt_findfre_1_OBJECTS) $(testsuite_libsframe_find_plt_findfre_1_DEPENDENCIES) $(EXTRA_testsuite_libsframe_find_plt_findfre_1_DEPENDENCIES) testsuite/libsframe.find/$(am__dirstamp)
@rm -f testsuite/libsframe.find/plt-findfre-1$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(testsuite_libsframe_find_plt_findfre_1_OBJECTS) $(testsuite_libsframe_find_plt_findfre_1_LDADD) $(LIBS)
+testsuite/libsframe.find/testsuite_libsframe_find_plt_findfre_2-plt-findfre-2.$(OBJEXT): \
+ testsuite/libsframe.find/$(am__dirstamp) \
+ testsuite/libsframe.find/$(DEPDIR)/$(am__dirstamp)
+
+testsuite/libsframe.find/plt-findfre-2$(EXEEXT): $(testsuite_libsframe_find_plt_findfre_2_OBJECTS) $(testsuite_libsframe_find_plt_findfre_2_DEPENDENCIES) $(EXTRA_testsuite_libsframe_find_plt_findfre_2_DEPENDENCIES) testsuite/libsframe.find/$(am__dirstamp)
+ @rm -f testsuite/libsframe.find/plt-findfre-2$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(testsuite_libsframe_find_plt_findfre_2_OBJECTS) $(testsuite_libsframe_find_plt_findfre_2_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@@ -768,6 +787,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@testsuite/libsframe.find/$(DEPDIR)/testsuite_libsframe_find_findfre_1-findfre-1.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@testsuite/libsframe.find/$(DEPDIR)/testsuite_libsframe_find_findfunc_1-findfunc-1.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@testsuite/libsframe.find/$(DEPDIR)/testsuite_libsframe_find_plt_findfre_1-plt-findfre-1.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@testsuite/libsframe.find/$(DEPDIR)/testsuite_libsframe_find_plt_findfre_2-plt-findfre-2.Po@am__quote@
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
@@ -912,6 +932,20 @@ testsuite/libsframe.find/testsuite_libsframe_find_plt_findfre_1-plt-findfre-1.ob
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testsuite_libsframe_find_plt_findfre_1_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o testsuite/libsframe.find/testsuite_libsframe_find_plt_findfre_1-plt-findfre-1.obj `if test -f 'testsuite/libsframe.find/plt-findfre-1.c'; then $(CYGPATH_W) 'testsuite/libsframe.find/plt-findfre-1.c'; else $(CYGPATH_W) '$(srcdir)/testsuite/libsframe.find/plt-findfre-1.c'; fi`
+testsuite/libsframe.find/testsuite_libsframe_find_plt_findfre_2-plt-findfre-2.o: testsuite/libsframe.find/plt-findfre-2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testsuite_libsframe_find_plt_findfre_2_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT testsuite/libsframe.find/testsuite_libsframe_find_plt_findfre_2-plt-findfre-2.o -MD -MP -MF testsuite/libsframe.find/$(DEPDIR)/testsuite_libsframe_find_plt_findfre_2-plt-findfre-2.Tpo -c -o testsuite/libsframe.find/testsuite_libsframe_find_plt_findfre_2-plt-findfre-2.o `test -f 'testsuite/libsframe.find/plt-findfre-2.c' || echo '$(srcdir)/'`testsuite/libsframe.find/plt-findfre-2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) testsuite/libsframe.find/$(DEPDIR)/testsuite_libsframe_find_plt_findfre_2-plt-findfre-2.Tpo testsuite/libsframe.find/$(DEPDIR)/testsuite_libsframe_find_plt_findfre_2-plt-findfre-2.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='testsuite/libsframe.find/plt-findfre-2.c' object='testsuite/libsframe.find/testsuite_libsframe_find_plt_findfre_2-plt-findfre-2.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testsuite_libsframe_find_plt_findfre_2_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o testsuite/libsframe.find/testsuite_libsframe_find_plt_findfre_2-plt-findfre-2.o `test -f 'testsuite/libsframe.find/plt-findfre-2.c' || echo '$(srcdir)/'`testsuite/libsframe.find/plt-findfre-2.c
+
+testsuite/libsframe.find/testsuite_libsframe_find_plt_findfre_2-plt-findfre-2.obj: testsuite/libsframe.find/plt-findfre-2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testsuite_libsframe_find_plt_findfre_2_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT testsuite/libsframe.find/testsuite_libsframe_find_plt_findfre_2-plt-findfre-2.obj -MD -MP -MF testsuite/libsframe.find/$(DEPDIR)/testsuite_libsframe_find_plt_findfre_2-plt-findfre-2.Tpo -c -o testsuite/libsframe.find/testsuite_libsframe_find_plt_findfre_2-plt-findfre-2.obj `if test -f 'testsuite/libsframe.find/plt-findfre-2.c'; then $(CYGPATH_W) 'testsuite/libsframe.find/plt-findfre-2.c'; else $(CYGPATH_W) '$(srcdir)/testsuite/libsframe.find/plt-findfre-2.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) testsuite/libsframe.find/$(DEPDIR)/testsuite_libsframe_find_plt_findfre_2-plt-findfre-2.Tpo testsuite/libsframe.find/$(DEPDIR)/testsuite_libsframe_find_plt_findfre_2-plt-findfre-2.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='testsuite/libsframe.find/plt-findfre-2.c' object='testsuite/libsframe.find/testsuite_libsframe_find_plt_findfre_2-plt-findfre-2.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testsuite_libsframe_find_plt_findfre_2_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o testsuite/libsframe.find/testsuite_libsframe_find_plt_findfre_2-plt-findfre-2.obj `if test -f 'testsuite/libsframe.find/plt-findfre-2.c'; then $(CYGPATH_W) 'testsuite/libsframe.find/plt-findfre-2.c'; else $(CYGPATH_W) '$(srcdir)/testsuite/libsframe.find/plt-findfre-2.c'; fi`
+
mostlyclean-libtool:
-rm -f *.lo
diff --git a/libsframe/doc/sframe-spec.texi b/libsframe/doc/sframe-spec.texi
index 69fe873..7307789 100644
--- a/libsframe/doc/sframe-spec.texi
+++ b/libsframe/doc/sframe-spec.texi
@@ -77,12 +77,13 @@ Appendices
@section Overview
@cindex Overview
-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 stack trace information is provided in a loaded section, known as
+the @code{.sframe} section. When available, the @code{.sframe} section appears
+in segment of type PT_GNU_SFRAME. An ELF SFrame section will have the type
+SHT_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 +140,31 @@ 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.
+ @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.
+ @item SFRAME_FRE_RA_OFFSET_INVALID: Invalid RA offset value (like
+SFRAME_CFA_FIXED_RA_INVALID). Used on s390x as padding offset to represent
+FP without RA saved.
+ @item SFRAME_S390X_CFA_OFFSET_ADJUSTMENT: CFA offset (from CFA base register)
+adjustment value. Used to enable use of 8-bit SFrame offsets on s390x.
+ @item SFRAME_S390X_CFA_OFFSET_ALIGNMENT_FACTOR: CFA offset alignment factor.
+Used to scale down the CFA offset to improve the use of 8-bit SFrame offsets.
+ @item SFRAME_V2_S390X_CFA_OFFSET_ENCODE: Encode CFA offset (i.e., apply
+CFA offset adjustment and then scale down by CFA offset alignment factor).
+ @item SFRAME_V2_S390X_CFA_OFFSET_DECODE: Decode CFA offset (i.e., scale up
+by CFA offset alignment factor and then revert CFA offset adjustment).
+ @end itemize
@end itemize
SFrame version 1 is now obsolete and should not be used.
@@ -421,6 +447,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
@@ -780,10 +810,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
@@ -794,6 +825,7 @@ auxiliary SFrame header, etc., if used, must also be outlined here.
@menu
* AMD64::
* AArch64::
+* s390x::
@end menu
@node AMD64
@@ -850,6 +882,77 @@ 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. On s390x the value of the offset is stored adjusted by the s390x-specific
+@code{SFRAME_S390X_CFA_OFFSET_ADJUSTMENT} and scaled down by the s390x-specific
+@code{SFRAME_S390X_CFA_OFFSET_ALIGNMENT_FACTOR}, to enable and improve the use
+of signed 8-bit offsets on s390x.
+s390x-specific helpers @code{SFRAME_V2_S390X_CFA_OFFSET_ENCODE} and
+@code{SFRAME_V2_S390X_CFA_OFFSET_DECODE} are provided to perform or undo
+the adjustment and scaling. The CFA offset can therefore be interpreted as:
+CFA = @code{BASE_REG} + offset1 - @code{SFRAME_S390X_CFA_OFFSET_ADJUSTMENT}
+or
+CFA = @code{BASE_REG}
+ + (offset1 * @code{SFRAME_S390X_CFA_OFFSET_ALIGNMENT_FACTOR})
+ - @code{SFRAME_S390X_CFA_OFFSET_ADJUSTMENT}.
+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, unless the offset has a
+value of @code{SFRAME_FRE_RA_OFFSET_INVALID}. RA remains unchanged, if the
+offset is not available or has a value of @code{SFRAME_FRE_RA_OFFSET_INVALID}.
+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.
+
+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 @columnfractions .15 .85
+@headitem Offset ID @tab Interpretation in s390x
+@item 1 @tab CFA = @code{BASE_REG} + offset1
+@item 2 @tab RA stack slot = CFA + offset2, if (offset2 & 1 == 0)
+ @*RA register number = offset2 >> 1, if (offset2 & 1 == 1)
+ @*RA not saved if (offset2 == @code{SFRAME_FRE_RA_OFFSET_INVALID})
+@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
+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 +1016,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.
diff --git a/libsframe/libsframe.ver b/libsframe/libsframe.ver
index 06324ee..8cc80da 100644
--- a/libsframe/libsframe.ver
+++ b/libsframe/libsframe.ver
@@ -1,6 +1,6 @@
LIBSFRAME_0.0 { };
-LIBSFRAME_1.0 {
+LIBSFRAME_2.0 {
global:
sframe_decoder_free;
sframe_fde_create_func_info;
@@ -11,12 +11,13 @@ LIBSFRAME_1.0 {
sframe_fre_get_ra_offset;
sframe_fre_get_ra_mangled_p;
sframe_decode;
+ sframe_decoder_get_flags;
sframe_decoder_get_hdr_size;
sframe_decoder_get_abi_arch;
sframe_decoder_get_version;
+ sframe_decoder_get_offsetof_fde_start_addr;
sframe_decoder_get_fixed_fp_offset;
sframe_decoder_get_fixed_ra_offset;
- sframe_get_funcdesc_with_addr;
sframe_find_fre;
sframe_decoder_get_num_fidx;
sframe_decoder_get_funcdesc;
@@ -24,9 +25,11 @@ LIBSFRAME_1.0 {
sframe_decoder_get_fre;
sframe_encode;
sframe_encoder_free;
+ sframe_encoder_get_flags;
sframe_encoder_get_hdr_size;
sframe_encoder_get_abi_arch;
sframe_encoder_get_version;
+ sframe_encoder_get_offsetof_fde_start_addr;
sframe_encoder_get_num_fidx;
sframe_encoder_add_fre;
sframe_encoder_add_funcdesc;
@@ -38,10 +41,3 @@ LIBSFRAME_1.0 {
local:
*;
} LIBSFRAME_0.0;
-
-LIBSFRAME_1.1 {
- sframe_decoder_get_flags;
- sframe_decoder_get_offsetof_fde_start_addr;
- sframe_encoder_get_flags;
- sframe_encoder_get_offsetof_fde_start_addr;
-} LIBSFRAME_1.0;
diff --git a/libsframe/libtool-version b/libsframe/libtool-version
index 9dcbe48..e06835d 100644
--- a/libsframe/libtool-version
+++ b/libsframe/libtool-version
@@ -27,4 +27,4 @@
# then set age to 0.
#
# CURRENT:REVISION:AGE
-1:0:0
+2:0:0
diff --git a/libsframe/sframe-dump.c b/libsframe/sframe-dump.c
index 47ac00e..d55d384 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);
@@ -197,8 +211,18 @@ dump_sframe_func_with_fres (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. */
+ else if (err[2] == 0 && ra_offset == SFRAME_FRE_RA_OFFSET_INVALID)
+ sprintf (temp, "U");
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 ea0e1c7..7357fc1 100644
--- a/libsframe/sframe.c
+++ b/libsframe/sframe.c
@@ -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;
@@ -520,7 +521,7 @@ flip_sframe (char *frame_buf, size_t buf_size, uint32_t to_foreign)
fre_offset = fdep->sfde_func_start_fre_off;
}
- fp = frame_buf + sframe_get_hdr_size (ihp) + ihp->sfh_freoff;
+ fp = frame_buf + hdrsz + ihp->sfh_freoff;
fp += fre_offset;
for (; j < prev_frep_index + num_fres; j++)
{
@@ -535,8 +536,12 @@ flip_sframe (char *frame_buf, size_t buf_size, uint32_t to_foreign)
prev_frep_index = j;
}
/* All FDEs and FREs must have been endian flipped by now. */
- if ((j != ihp->sfh_num_fres) || (bytes_flipped != (buf_size - hdrsz)))
+ if ((j != ihp->sfh_num_fres) || (bytes_flipped > (buf_size - hdrsz)))
goto bad;
+ /* Optional trailing section padding. */
+ for (fp = frame_buf + hdrsz + bytes_flipped; fp < frame_buf + buf_size; fp++)
+ if (*fp != '\0')
+ goto bad;
/* Success. */
return 0;
@@ -690,13 +695,22 @@ sframe_fre_get_base_reg_id (sframe_frame_row_entry *fre, int *errp)
/* Get the CFA offset from the FRE. If the offset is invalid, sets errp. */
int32_t
-sframe_fre_get_cfa_offset (sframe_decoder_ctx *dctx ATTRIBUTE_UNUSED,
+sframe_fre_get_cfa_offset (sframe_decoder_ctx *dctx,
sframe_frame_row_entry *fre, int *errp)
{
- return sframe_get_fre_offset (fre, SFRAME_FRE_CFA_OFFSET_IDX, errp);
+ int32_t offset = sframe_get_fre_offset (fre, SFRAME_FRE_CFA_OFFSET_IDX, errp);
+
+ /* For s390x undo adjustment of CFA offset (to enable 8-bit offsets). */
+ if (sframe_decoder_get_abi_arch (dctx) == SFRAME_ABI_S390X_ENDIAN_BIG)
+ offset = SFRAME_V2_S390X_CFA_OFFSET_DECODE (offset);
+
+ return offset;
}
-/* 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,
@@ -723,7 +737,12 @@ 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 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. */
int32_t
sframe_fre_get_ra_offset (sframe_decoder_ctx *dctx,
@@ -857,7 +876,7 @@ sframe_decode_fre (const char *fre_buf, sframe_frame_row_entry *fre,
return 0;
}
-/* Decode the specified SFrame buffer CF_BUF of size CF_SIZE and return the
+/* Decode the specified SFrame buffer SF_BUF of size SF_SIZE and return the
new SFrame decoder context.
Sets ERRP for the caller if any error. Frees up the allocated memory in
@@ -1060,18 +1079,6 @@ sframe_decoder_get_offsetof_fde_start_addr (sframe_decoder_ctx *dctx,
+ offsetof (sframe_func_desc_entry, sfde_func_start_address));
}
-/* Find the function descriptor entry which contains the specified address
- ADDR.
- This function is deprecated and will be removed from libsframe.so.2. */
-
-void *
-sframe_get_funcdesc_with_addr (sframe_decoder_ctx *ctx __attribute__ ((unused)),
- int32_t addr __attribute__ ((unused)),
- int *errp)
-{
- return sframe_ret_set_errno (errp, SFRAME_ERR_INVAL);
-}
-
/* Find the function descriptor entry starting which contains the specified
address ADDR. */
@@ -1620,7 +1627,7 @@ sframe_encoder_add_funcdesc (sframe_encoder_ctx *encoder,
int32_t start_addr,
uint32_t func_size,
unsigned char func_info,
- uint32_t num_fres __attribute__ ((unused)))
+ uint32_t num_fres ATTRIBUTE_UNUSED)
{
sframe_header *ehp;
sf_fde_tbl *fd_info;
@@ -1702,7 +1709,7 @@ sframe_encoder_add_funcdesc_v2 (sframe_encoder_ctx *encoder,
uint32_t func_size,
unsigned char func_info,
uint8_t rep_block_size,
- uint32_t num_fres __attribute__ ((unused)))
+ uint32_t num_fres ATTRIBUTE_UNUSED)
{
sf_fde_tbl *fd_info;
int err;
diff --git a/libsframe/testsuite/libsframe.find/find.exp b/libsframe/testsuite/libsframe.find/find.exp
index d1be070..62471a3 100644
--- a/libsframe/testsuite/libsframe.find/find.exp
+++ b/libsframe/testsuite/libsframe.find/find.exp
@@ -26,6 +26,7 @@ if [string equal $COMPAT_DEJAGNU "no"] {
unsupported findfre-1
unsupported findfunc-1
unsupported plt-findfre-1
+ unsupported plt-findfre-2
return;
}
@@ -40,3 +41,7 @@ if { [host_execute "testsuite/libsframe.find/findfunc-1"] ne "" } {
if { [host_execute "testsuite/libsframe.find/plt-findfre-1"] ne "" } {
fail "plt-findfre-1"
}
+
+if { [host_execute "testsuite/libsframe.find/plt-findfre-2"] ne "" } {
+ fail "plt-findfre-2"
+}
diff --git a/libsframe/testsuite/libsframe.find/local.mk b/libsframe/testsuite/libsframe.find/local.mk
index 03206b1..52741e8 100644
--- a/libsframe/testsuite/libsframe.find/local.mk
+++ b/libsframe/testsuite/libsframe.find/local.mk
@@ -1,5 +1,6 @@
if HAVE_COMPAT_DEJAGNU
check_PROGRAMS += %D%/findfre-1 %D%/findfunc-1 %D%/plt-findfre-1
+ check_PROGRAMS += %D%/plt-findfre-2
endif
%C%_findfre_1_SOURCES = %D%/findfre-1.c
@@ -13,3 +14,7 @@ endif
%C%_plt_findfre_1_SOURCES = %D%/plt-findfre-1.c
%C%_plt_findfre_1_LDADD = ${top_builddir}/libsframe.la
%C%_plt_findfre_1_CPPFLAGS = -I${top_srcdir}/../include -Wall
+
+%C%_plt_findfre_2_SOURCES = %D%/plt-findfre-2.c
+%C%_plt_findfre_2_LDADD = ${top_builddir}/libsframe.la
+%C%_plt_findfre_2_CPPFLAGS = -I${top_srcdir}/../include -Wall
diff --git a/libsframe/testsuite/libsframe.find/plt-findfre-2.c b/libsframe/testsuite/libsframe.find/plt-findfre-2.c
new file mode 100644
index 0000000..00a5b2a
--- /dev/null
+++ b/libsframe/testsuite/libsframe.find/plt-findfre-2.c
@@ -0,0 +1,201 @@
+/* plt-findfre-2.c -- Test for sframe_find_fre for SFrame FDE of type
+ PCMASK with with one SFrame FRE only.
+
+ Copyright (C) 2025 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "sframe-api.h"
+
+/* DejaGnu should not use gnulib's vsnprintf replacement here. */
+#undef vsnprintf
+#include <dejagnu.h>
+
+/* s390x-specific size in bytes of plt0 and pltN. */
+#define PLT_SIZE 32
+
+/* Magic values added to CFA offsets to make them distingishable. Must
+ be multiple of 8 due to s390x-specific CFA alignment factor. */
+#define PLT0_CFA_OFFSET_MAGIC 0
+#define PLTN_CFA_OFFSET_MAGIC 8
+
+static int
+add_plt0_fde (sframe_encoder_ctx *ectx, uint32_t plt_vaddr,
+ uint32_t sframe_vaddr, int idx)
+{
+ /* 1 single FRE. */
+ sframe_frame_row_entry fre
+ = { 0x0,
+ { SFRAME_V2_S390X_CFA_OFFSET_ENCODE (160 + PLT0_CFA_OFFSET_MAGIC) },
+ SFRAME_V1_FRE_INFO (SFRAME_BASE_REG_SP, 1, SFRAME_FRE_OFFSET_1B) };
+
+ unsigned char finfo = sframe_fde_create_func_info (SFRAME_FRE_TYPE_ADDR1,
+ SFRAME_FDE_TYPE_PCINC);
+ uint32_t offsetof_fde_in_sec
+ = sframe_encoder_get_offsetof_fde_start_addr (ectx, idx, NULL);
+
+ int32_t func_start_addr = (plt_vaddr
+ - (sframe_vaddr + offsetof_fde_in_sec));
+
+ /* 1 PCINC-type FDE for 1 plt0 entry of 32 bytes. */
+ int err = sframe_encoder_add_funcdesc_v2 (ectx, func_start_addr,
+ PLT_SIZE /* func size. */,
+ finfo,
+ 0 /* rep block size. */,
+ 1 /* num FREs. */);
+ if (err == -1)
+ return err;
+
+ if (sframe_encoder_add_fre (ectx, idx, &fre) == SFRAME_ERR)
+ return -1;
+
+ return 0;
+}
+
+static int
+add_pltn_fde (sframe_encoder_ctx *ectx, uint32_t plt_vaddr,
+ uint32_t sframe_vaddr, int idx)
+{
+ /* 1 single FRE. */
+ sframe_frame_row_entry fre
+ = { 0x0,
+ { SFRAME_V2_S390X_CFA_OFFSET_ENCODE (160 + PLTN_CFA_OFFSET_MAGIC) },
+ SFRAME_V1_FRE_INFO (SFRAME_BASE_REG_SP, 1, SFRAME_FRE_OFFSET_1B) };
+
+ unsigned char finfo = sframe_fde_create_func_info (SFRAME_FRE_TYPE_ADDR1,
+ SFRAME_FDE_TYPE_PCMASK);
+ uint32_t offsetof_fde_in_sec
+ = sframe_encoder_get_offsetof_fde_start_addr (ectx, idx, NULL);
+
+ int32_t func_start_addr = (plt_vaddr
+ - (sframe_vaddr + offsetof_fde_in_sec));
+
+ /* 1 PCMASK-type FDE for 5 pltN entries of 32 bytes each. */
+ int err = sframe_encoder_add_funcdesc_v2 (ectx, func_start_addr,
+ 5 * PLT_SIZE /* func size. */,
+ finfo,
+ PLT_SIZE /* rep block size. */,
+ 1 /* num FREs. */);
+ if (err == -1)
+ return err;
+
+ if (sframe_encoder_add_fre (ectx, idx, &fre) == SFRAME_ERR)
+ return -1;
+
+ return 0;
+}
+
+static
+void test_plt_findfre (const char suffix, const uint32_t plt_vaddr,
+ const uint32_t sframe_vaddr)
+{
+ sframe_encoder_ctx *ectx;
+ sframe_decoder_ctx *dctx;
+ sframe_frame_row_entry frep;
+ char *sframe_buf;
+ size_t sf_size;
+ int err = 0;
+ unsigned int fde_cnt = 0;
+ int i;
+
+#define TEST(cond, ...) \
+ do \
+ { \
+ if (cond) \
+ pass (__VA_ARGS__); \
+ else \
+ fail (__VA_ARGS__); \
+ } \
+ while (0)
+
+ ectx = sframe_encode (SFRAME_VERSION, SFRAME_F_FDE_FUNC_START_PCREL,
+ SFRAME_ABI_S390X_ENDIAN_BIG,
+ SFRAME_CFA_FIXED_FP_INVALID,
+ SFRAME_CFA_FIXED_RA_INVALID,
+ &err);
+ TEST (ectx != NULL && err == 0, "plt-findfre-2%c: Creating SFrame encoder", suffix);
+
+ err = add_plt0_fde (ectx, plt_vaddr, sframe_vaddr, 0);
+ TEST (err == 0, "plt-findfre-2%c: Adding FDE for plt0", suffix);
+
+ fde_cnt = sframe_encoder_get_num_fidx (ectx);
+ TEST (fde_cnt == 1, "plt-findfre-2%c: Test FDE count after adding FDE for plt0", suffix);
+
+ err = add_pltn_fde (ectx, plt_vaddr + PLT_SIZE, sframe_vaddr, 1);
+ TEST (err == 0, "plt-findfre-2%c: Adding FDE for pltN", suffix);
+
+ fde_cnt = sframe_encoder_get_num_fidx (ectx);
+ TEST (fde_cnt == 2, "plt-findfre-2%c: Test FDE count after adding FDE for pltN", suffix);
+
+ sframe_buf = sframe_encoder_write (ectx, &sf_size, &err);
+ TEST (err == 0, "plt-findfre-2%c: Encoder write", suffix);
+
+ dctx = sframe_decode (sframe_buf, sf_size, &err);
+ TEST (dctx != NULL, "plt-findfre-2%c: Decoder setup", suffix);
+
+ /* Find the only FRE in PLT0 at offset 0. */
+ err = sframe_find_fre (dctx, (plt_vaddr + 0 - sframe_vaddr), &frep);
+ TEST (err == 0 && sframe_fre_get_cfa_offset (dctx, &frep, &err) == 160 + PLT0_CFA_OFFSET_MAGIC,
+ "plt-findfre-2%c: Find only FRE in PLT0 at offset 0", suffix);
+
+ /* Find the only FRE in PLT0 at offset PLT_SIZE-1. */
+ err = sframe_find_fre (dctx, (plt_vaddr + (PLT_SIZE-1) - sframe_vaddr), &frep);
+ TEST (err == 0 && sframe_fre_get_cfa_offset (dctx, &frep, &err) == 160 + PLT0_CFA_OFFSET_MAGIC,
+ "plt-findfre-2%c: Find only FRE in PLT0 at offset PLT_SIZE-1", suffix);
+
+ /* Find the only FRE in PLT1-5 at offset 0 and PLT_SIZE-1. */
+ for (i = 1; i < 5; i++)
+ {
+ /* Find the only FRE in PLTN at offset 0. */
+ err = sframe_find_fre (dctx, (plt_vaddr + i * PLT_SIZE + 0 - sframe_vaddr), &frep);
+ TEST (err == 0 && sframe_fre_get_cfa_offset (dctx, &frep, &err) == 160 + PLTN_CFA_OFFSET_MAGIC,
+ "plt-findfre-2%c: Find only FRE in PLT%d at offset 0", suffix, i);
+
+ /* Find the only FRE in PLTN at offset 31. */
+ err = sframe_find_fre (dctx, (plt_vaddr + i * PLT_SIZE + (PLT_SIZE-1) - sframe_vaddr), &frep);
+ TEST (err == 0 && sframe_fre_get_cfa_offset (dctx, &frep, &err) == 160 + PLTN_CFA_OFFSET_MAGIC,
+ "plt-findfre-2%c: Find only FRE in PLT%d at offset PLT_SIZE-1", suffix, i);
+ }
+
+ /* Find no FRE in non-existing PLT6 at offset 0. */
+ err = sframe_find_fre (dctx, (plt_vaddr + 6 * PLT_SIZE + 0 - sframe_vaddr), &frep);
+ TEST (err != 0, "plt-findfre-2%c: Find no FRE in out of range PLT6 at offset 0", suffix);
+
+ sframe_encoder_free (&ectx);
+ sframe_decoder_free (&dctx);
+}
+
+int
+main (void)
+{
+ uint32_t sframe_vaddr = 0x402220;
+ uint32_t plt_vaddr = 0x401020;
+ printf ("plt-findfre-2a: Testing with plt_vaddr = %#x; sframe_vaddr = %#x\n",
+ plt_vaddr, sframe_vaddr);
+ test_plt_findfre ('a', plt_vaddr, sframe_vaddr);
+
+ sframe_vaddr = 0x401020;
+ plt_vaddr = 0x402220;
+ printf ("plt-findfre-2b: Testing with plt_vaddr = %#x; sframe_vaddr = %#x\n",
+ plt_vaddr, sframe_vaddr);
+ test_plt_findfre ('b', plt_vaddr, sframe_vaddr);
+
+ return 0;
+}