aboutsummaryrefslogtreecommitdiff
path: root/libsframe/doc/sframe-spec.texi
diff options
context:
space:
mode:
Diffstat (limited to 'libsframe/doc/sframe-spec.texi')
-rw-r--r--libsframe/doc/sframe-spec.texi375
1 files changed, 334 insertions, 41 deletions
diff --git a/libsframe/doc/sframe-spec.texi b/libsframe/doc/sframe-spec.texi
index bb2b35a..7307789 100644
--- a/libsframe/doc/sframe-spec.texi
+++ b/libsframe/doc/sframe-spec.texi
@@ -3,7 +3,7 @@
@settitle The SFrame Format
@copying
-Copyright @copyright{} 2021-2024 Free Software Foundation, Inc.
+Copyright @copyright{} 2021-2025 Free Software Foundation, Inc.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU General Public License, Version 3 or any
@@ -21,6 +21,8 @@ License''.
@titlepage
@title The SFrame Format
@subtitle Version 2
+@sp 15
+@center @today{}
@author Indu Bhagat
@page
@@ -52,6 +54,11 @@ low-overhead mechanism to generate stack traces.
@menu
* Introduction::
* SFrame Section::
+* ABI/arch-specific Definition::
+
+Appendices
+* Generating Stack Traces using SFrame::
+
* Index::
@end menu
@@ -70,12 +77,13 @@ low-overhead mechanism to generate stack traces.
@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
@@ -121,6 +129,42 @@ the data structure.
@item
The above two imply that each SFrame function descriptor entry has a fixed size
of 20 bytes instead of its size of 17 bytes in SFrame format version 1.
+@item
+Add a new flag SFRAME_F_FDE_FUNC_START_PCREL, as an erratum to SFrame
+Version 2, to indicate the encoding of the SFrame FDE function start address
+field:
+ @itemize @minus
+ @item if set, @code{sfde_func_start_address} field contains the offset in
+bytes to the start PC of the associated function from the field itself.
+ @item if unset, @code{sfde_func_start_address} field contains the offset in
+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.
@@ -227,21 +271,28 @@ describe various section-wide properties.
The following flags are currently defined.
-@multitable {@code{SFRAME_F_FRAME_POINTER}} {Versions} {Value} {Function Descriptor Entries}
-@headitem Flag @tab Versions @tab Value @tab Meaning
+@multitable {@code{SFRAME_F_FRAME_POINTER}} {Version} {Value} {Function Descriptor Entries are sorted}
+@headitem Flag @tab Version @tab Value @tab Meaning
@tindex SFRAME_F_FDE_SORTED
@item @code{SFRAME_F_FDE_SORTED} @tab All @tab 0x1 @tab Function Descriptor
Entries are sorted on PC.
@tindex SFRAME_F_FRAME_POINTER
@item @code{SFRAME_F_FRAME_POINTER} @tab All @tab 0x2
@tab All functions in the object file preserve frame pointer.
+@tindex SFRAME_F_FDE_FUNC_START_PCREL
+@item @code{SFRAME_F_FDE_FUNC_START_PCREL} @tab 2 @tab 0x4
+@tab The @code{sfde_func_start_address} field in the SFrame FDE is an offset in
+bytes to the function's start address, from the field itself. If unset, the
+@code{sfde_func_start_address} field in the SFrame FDE is an offset in bytes to
+the function's start address, from the start of the SFrame section.
@end multitable
The purpose of SFRAME_F_FRAME_POINTER flag is to facilitate stack tracers to
reliably fallback on the frame pointer based stack tracing method, if SFrame
information is not present for some function in the SFrame section.
-Further flags may be added in future.
+Further flags may be added in future. Bits corresponding to the currently
+undefined flags must be set to zero.
@node SFrame Header
@section SFrame Header
@@ -396,10 +447,14 @@ 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
-stack trace generators to make certain ABI-specific decisions.
+stack trace generators to make certain ABI/arch-specific decisions.
@node SFrame Function Descriptor Entries
@section SFrame FDE
@@ -454,7 +509,11 @@ Following table describes each component of the SFrame FDE structure:
@tab @code{int32_t}
@tab @code{sfde_func_start_address}
@tab Signed 32-bit integral field denoting the virtual memory address of the
-described function.
+described function, for which the SFrame FDE applies. If the flag
+@code{SFRAME_F_FDE_FUNC_START_PCREL}, @xref{SFrame Flags}, in the SFrame
+header is set, the value encoded in the @code{sfde_func_start_address} field is
+the offset in bytes to the function's start address, from the SFrame
+@code{sfde_func_start_address} field.
@item 0x04
@tab @code{uint32_t}
@@ -612,41 +671,42 @@ identifier to reflect the chosen SFrame FRE type is stored in the
@cindex SFrame FRE
The SFrame frame row entry sub-section contains the core of the stack trace
-information.
+information. An SFrame frame row entry (FRE) is a self-sufficient record
+containing SFrame stack trace information for a range of contiguous
+(instruction) addresses, starting at the specified offset from the start of the
+function.
-An SFrame frame row entry is a self-sufficient record containing SFrame stack
-trace information for a range of contiguous addresses, starting at the
-specified offset from the start of the function. Each SFrame frame row entry
-is followed by S*N bytes, where:
+Each SFrame FRE encodes the stack offsets to recover the CFA, FP and RA (where
+applicable) for the respective instruction addresses. To encode this
+information, each SFrame FRE is followed by S*N bytes, where:
@itemize @minus
@item
-@code{S} is the size of the stack frame offset for the FRE, and
+@code{S} is the size of a stack offset for the FRE, and
@item
-@code{N} is the number of stack frame offsets in the FRE
+@code{N} is the number of stack offsets in the FRE
@end itemize
-The stack offsets, following the FRE, are interpreted in order as follows:
+The entities @code{S}, @code{N} are encoded in the SFrame FRE info word, via
+the @code{fre_offset_size} and the @code{fre_offset_count} respectively. More
+information about the precise encoding and range of values for @code{S} and
+@code{N} is provided later in the @xref{The SFrame FRE Info Word}.
-@itemize @minus
-@item
-The first offset is always used to locate the CFA, by interpreting it as:
-CFA = @code{BASE_REG} + offset1.
-@item
-If RA is being tracked, the second offset is always used to locate the RA, by
-interpreting it as: RA = CFA + offset2. If RA is @emph{not} being tracked
-@emph{and} FP is being tracked, the second offset will be used to locate the
-FP, by interpreting it as: FP = CFA + offset2.
-@item
-If both RA and FP are being tracked, the third offset will be used to locate
-the FP, by interpreting it as FP = CFA + offset3.
-@end itemize
+@cindex Provisions for future ABIs
+It is important to underline here that although the canonical interpretation
+of these bytes is as stack offsets (to recover CFA, FP and RA), these bytes
+@emph{may} be used by future ABIs/architectures to convey other information on
+a per SFrame FRE basis.
-The entities @code{S}, @code{N} and @code{BASE_REG} are identified using the
-SFrame FRE info word, a.k.a. the @code{sframe_fre_info}
-@xref{The SFrame FRE Info Word}.
+In summary, SFrame file format, by design, supports a variable number of stack
+offsets at the tail end of each SFrame FRE. To keep the SFrame file
+format specification flexible yet extensible, the interpretation of the stack
+offsets is ABI/arch-specific. The precise interpretation of the FRE stack
+offsets in the currently supported ABIs/architectures is covered in the
+ABI/arch-specific definition of the SFrame file format,
+@xref{ABI/arch-specific Definition}.
-Following are the definitions of the allowed SFrame FRE:
+Next, the definitions of the three SFrame FRE types are as follows:
@example
typedef struct sframe_frame_row_entry_addr1
@@ -678,12 +738,12 @@ serializing and deserializing entities, if unaligned accesses need to be
avoided.
@code{sfre_start_address} is an unsigned 8-bit/16-bit/32-bit integral field
-identifies the start address of the range of program counters, for which the
+denoting the start address of a range of program counters, for which the
SFrame FRE applies. The value encoded in the @code{sfre_start_address} field
-is the offset in bytes of the start address of the SFrame FRE, from the start
-address of the function.
+is the offset in bytes of the range's start address, from the start address
+of the function.
-Further FRE types may be added in future.
+Further SFrame FRE types may be added in future.
@menu
* The SFrame FRE Info Word::
@@ -710,7 +770,8 @@ SFRAME_FRE_OFFSET_4B.
@item 1-4
@tab @code{fre_offset_count}
-@tab A value of upto 3 is allowed to track all three of CFA, FP and RA.
+@tab A max value of 15 is allowed. Typically, a value of upto 3 is sufficient
+for most ABIs to track all three of CFA, FP and RA.
@item 0
@tab @code{fre_cfa_base_reg_id}
@@ -741,6 +802,238 @@ long.
@end multitable
+@node ABI/arch-specific Definition
+@chapter ABI/arch-specific Definition
+@cindex ABI/arch-specific Definition
+
+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 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
+blueprint for interpreting the variable number of bytes at the tail end of the
+SFrame FRE for the specific ABI/arch. Any further provisions, e.g., using the
+auxiliary SFrame header, etc., if used, must also be outlined here.
+
+@menu
+* AMD64::
+* AArch64::
+* s390x::
+@end menu
+
+@node AMD64
+@section AMD64
+
+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 word.
+
+In AMD64, the return address (RA) is always saved on stack when a function
+call is executed. Further, AMD64 ABI mandates that the RA be saved at a
+@code{fixed offset} from the CFA when entering a new function. This means
+that the RA does not need to be tracked per SFrame FRE. The fixed offset is
+encoded in the SFrame file format in the field @code{sfh_cfa_fixed_ra_offset}
+in the SFrame header. @xref{SFrame Header}.
+
+Hence, the second stack offset (in the SFrame FRE), when present, will be used
+to locate the FP, by interpreting it as: FP = CFA + offset2.
+
+Hence, in summary:
+
+@multitable {Offset ID} {Interpretation in AMD64 in AMD64}
+@headitem Offset ID @tab Interpretation in AMD64
+@item 1 @tab CFA = @code{BASE_REG} + offset1
+@item 2 @tab FP = CFA + offset2
+@end multitable
+
+@node AArch64
+@section AArch64
+
+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 word.
+
+In AARCH64, the AAPCS64 standard specifies that the Frame Record saves both FP
+and LR (a.k.a the RA). However, the standard does not mandate the precise
+location in the function where the frame record is created, 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 will be used to
+locate the FP, by interpreting it as: FP = CFA + offset3.
+
+Given the nature of things, the number of stack offsets seen on AARCH64 per
+SFrame FRE is either 1 or 3.
+
+Hence, in summary:
+
+@multitable {Offset ID} {Interpretation in AArch64 in X}
+@headitem Offset ID @tab Interpretation in AArch64
+@item 1 @tab CFA = @code{BASE_REG} + offset1
+@item 2 @tab RA = CFA + offset2
+@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
+
+Using some C-like pseudocode, this section highlights how SFrame provides a
+simple, fast and low-overhead mechanism to generate stack traces. Needless to
+say that for generating accurate and useful stack traces, several other aspects
+will need attention: finding and decoding bits of SFrame section(s) in the
+program binary, symbolization of addresses, to name a few.
+
+In the current context, a @code{frame} is the abstract construct that
+encapsulates the following information:
+@itemize @minus
+@item
+program counter (PC),
+@item
+stack pointer (SP), and
+@item
+frame pointer (FP)
+@end itemize
+
+With that said, establishing the first @code{frame} should be trivial:
+
+@example
+ // frame 0
+ frame->pc = current_IP;
+ frame->sp = get_reg_value (REG_SP);
+ frame->fp = get_reg_value (REG_FP);
+@end example
+
+where @code{REG_SP} and @code{REG_FP} are are ABI-designated stack pointer and
+frame pointer registers respectively.
+
+Next, given frame N, generating stack trace needs us to get frame N+1. This
+can be done as follows:
+
+@example
+ // Get the PC, SP, and FP for frame N.
+ pc = frame->pc;
+ sp = frame->sp;
+ fp = frame->fp;
+ // Populate frame N+1.
+ int err = get_next_frame (&next_frame, pc, sp, fp);
+@end example
+
+where given the values of the program counter, stack pointer and frame pointer
+from frame N, @code{get_next_frame} populates the provided @code{next_frame}
+object and returns the error code, if any. In the following pseudocode for
+@code{get_next_frame}, the @code{sframe_*} functions fetch information from the
+SFrame section.
+
+@example
+ fre = sframe_find_fre (pc);
+ if (fre)
+ // Whether the base register for CFA tracking is REG_FP.
+ base_reg_val = sframe_fre_base_reg_fp_p (fre) ? fp : sp;
+ // Get the CFA stack offset from the FRE.
+ cfa_offset = sframe_fre_get_cfa_offset (fre);
+ // Get the fixed RA offset or FRE stack offset as applicable.
+ ra_offset = sframe_fre_get_ra_offset (fre);
+ // Get the fixed FP offset or FRE stack offset as applicable.
+ fp_offset = sframe_fre_get_fp_offset (fre);
+
+ cfa = base_reg_val + cfa_offset;
+ 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.
+ next_frame->pc = read_value (ra_stack_loc);
+
+ if (fp_offset is VALID)
+ fp_stack_loc = cfa + fp_offset;
+ // Get the value stored in the stack location.
+ next_frame->fp = read_value (fp_stack_loc);
+ else
+ // Continue to use the value of fp as it has not
+ // been clobbered by the current frame yet.
+ next_frame->fp = fp;
+ else
+ ret = ERR_NO_SFRAME_FRE;
+@end example
+
@node Index
@unnumbered Index