aboutsummaryrefslogtreecommitdiff
path: root/gas/gen-sframe.c
diff options
context:
space:
mode:
authorIndu Bhagat <indu.bhagat@oracle.com>2022-11-15 15:06:46 -0800
committerIndu Bhagat <indu.bhagat@oracle.com>2022-11-15 15:24:06 -0800
commitb52c4ee46657eb5a70095d48cbc2938d024cc3b6 (patch)
tree10fb1afba608156a25ebe9732cfe5e3fe1d85833 /gas/gen-sframe.c
parentb07a29781610756a9b75a931c7c13735b7555d9f (diff)
downloadgdb-b52c4ee46657eb5a70095d48cbc2938d024cc3b6.zip
gdb-b52c4ee46657eb5a70095d48cbc2938d024cc3b6.tar.gz
gdb-b52c4ee46657eb5a70095d48cbc2938d024cc3b6.tar.bz2
gas: generate .sframe from CFI directives
Currently supported for x86_64 and aarch64 only. [PS: Currently, the compiler has not been adapted to generate ".cfi_sections" with ".sframe" in it. The newly added command line option of --gsframe provides an easy way to try out .sframe support in the toolchain.] gas interprets the CFI directives to generate DWARF-based .eh_frame info. These internal DWARF structures are now consumed by gen-sframe.[ch] sub-system to, in turn, create the SFrame unwind information. These internal DWARF structures are read-only for the purpose of SFrame unwind info generation. SFrame unwind info generation does not impact .eh_frame unwind info generation. Both .eh_frame and .sframe can co-exist in an ELF file, if so desired by the user. Recall that SFrame unwind information only contains the minimal necessary information to generate backtraces and does not provide information to recover all callee-saved registers. The reason being that callee-saved registers other than FP are not needed for stack unwinding, and hence are not included in the .sframe section. Consequently, gen-sframe.[ch] only needs to interpret a subset of DWARF opcodes in gas. More details follow. [Set 1, Interpreted] The following opcodes are interpreted: - DW_CFA_advance_loc - DW_CFA_def_cfa - DW_CFA_def_cfa_register - DW_CFA_def_cfa_offset - DW_CFA_offset - DW_CFA_remember_state - DW_CFA_restore_state - DW_CFA_restore [Set 2, Bypassed] The following opcodes are acknowledged but are not necessary for generating SFrame unwind info: - DW_CFA_undefined - DW_CFA_same_value Anything else apart from the two above-mentioned sets is skipped altogether. This means that any function containing a CFI directive not in Set 1 or Set 2 above, will not have any SFrame unwind information generated for them. Holes in instructions covered by FREs of a single FDE are not representable in the SFrame unwind format. As few examples, following opcodes are not processed for .sframe generation, and are skipped: - .cfi_personality* - .cfi_*lsda - .cfi_escape - .cfi_negate_ra_state - ... Not processing .cfi_escape, .cfi_negate_ra_state will cause SFrame unwind information to be absent for SFrame FDEs that contain these CFI directives, hence affecting the asynchronicity. x86-64 and aarch64 backends need to have a few new definitions and functions for .sframe generation. These provide gas with architecture specific information like the SP/FP/RA register numbers and an SFrame-specific ABI marker. Lastly, the patch also implements an optimization for size, where specific fragments containing SFrame FRE start address and SFrame FDE function are fixed up. This is similar to other similar optimizations in gas, where fragments are sized and fixed up when the associated symbols can be resolved. This optimization is controlled by a #define SFRAME_FRE_TYPE_SELECTION_OPT and should be easy to turn off if needed. The optimization is on by default for both x86_64 and aarch64. ChangeLog: * gas/Makefile.am: Include gen-sframe.c and sframe-opt.c. * gas/Makefile.in: Regenerated. * gas/as.h (enum _relax_state): Add new state rs_sframe. (sframe_estimate_size_before_relax): New function. (sframe_relax_frag): Likewise. (sframe_convert_frag): Likewise. * gas/config/tc-aarch64.c (aarch64_support_sframe_p): New definition. (aarch64_sframe_ra_tracking_p): Likewise. (aarch64_sframe_cfa_ra_offset): Likewise. (aarch64_sframe_get_abi_arch): Likewise. (md_begin): Set values of sp/fp/ra registers. * gas/config/tc-aarch64.h (aarch64_support_sframe_p): New declaration. (support_sframe_p): Likewise. (SFRAME_CFA_SP_REG): Likewise. (SFRAME_CFA_FP_REG): Likewise. (SFRAME_CFA_RA_REG): Likewise. (aarch64_sframe_ra_tracking_p): Likewise. (sframe_ra_tracking_p): Likewise. (aarch64_sframe_cfa_ra_offset): Likewise. (sframe_cfa_ra_offset): Likewise. (aarch64_sframe_get_abi_arch): Likewise. (sframe_get_abi_arch): Likewise. * gas/config/tc-i386.c (x86_support_sframe_p): New definition. (x86_sframe_ra_tracking_p): Likewise. (x86_sframe_cfa_ra_offset): Likewise. (x86_sframe_get_abi_arch): Likewise. * gas/config/tc-i386.h (x86_support_sframe_p): New declaration. (support_sframe_p): Likewise. (SFRAME_CFA_SP_REG): Likewise. (SFRAME_CFA_FP_REG): Likewise. (x86_sframe_ra_tracking_p): Likewise. (sframe_ra_tracking_p): Likewise. (x86_sframe_cfa_ra_offset): Likewise. (sframe_cfa_ra_offset): Likewise. (x86_sframe_get_abi_arch): Likewise. (sframe_get_abi_arch): Likewise. * gas/config/tc-xtensa.c (unrelaxed_frag_max_size): Add case for rs_sframe. * gas/doc/as.texi: Add .sframe to the documentation for .cfi_sections. * gas/dw2gencfi.c (cfi_finish): Create a .sframe section. * gas/dw2gencfi.h (CFI_EMIT_sframe): New definition. * gas/write.c (cvt_frag_to_fill): Handle rs_sframe. (relax_segment): Likewise. * gas/gen-sframe.c: New file. * gas/gen-sframe.h: New file. * gas/sframe-opt.c: New file.
Diffstat (limited to 'gas/gen-sframe.c')
-rw-r--r--gas/gen-sframe.c1294
1 files changed, 1294 insertions, 0 deletions
diff --git a/gas/gen-sframe.c b/gas/gen-sframe.c
new file mode 100644
index 0000000..13f5bda
--- /dev/null
+++ b/gas/gen-sframe.c
@@ -0,0 +1,1294 @@
+/* gen-sframe.c - Support for generating SFrame section.
+ Copyright (C) 2022 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS 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, or (at your option)
+ any later version.
+
+ GAS 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 GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include "subsegs.h"
+#include "sframe.h"
+#include "gen-sframe.h"
+#include "dw2gencfi.h"
+
+#ifdef support_sframe_p
+
+/* By default, use 32-bit relocations from .sframe into .text. */
+#ifndef SFRAME_RELOC_SIZE
+# define SFRAME_RELOC_SIZE 4
+#endif
+
+/* Whether frame row entries track RA.
+
+ A target may not need return address tracking for stack unwinding. If it
+ does need the same, SFRAME_CFA_RA_REG must be defined with the return
+ address register number. */
+
+#if defined (sframe_ra_tracking_p) && defined (SFRAME_CFA_RA_REG)
+# ifndef SFRAME_FRE_RA_TRACKING
+# define SFRAME_FRE_RA_TRACKING 1
+# endif
+#endif
+
+/* SFrame FRE type selection optimization is an optimization for size.
+
+ There are three flavors of SFrame FRE representation in the binary format:
+ - sframe_frame_row_entry_addr1 where the FRE start address is 1 byte.
+ - sframe_frame_row_entry_addr2 where the FRE start address is 2 bytes.
+ - sframe_frame_row_entry_addr4 where the FRE start address is 4 bytes.
+
+ Note that in the SFrame format, all SFrame FREs of a function use one
+ single representation. The SFrame FRE type itself is identified via the
+ information in the SFrame FDE function info.
+
+ Now, to select the minimum required one from the list above, one needs to
+ make a decision based on the size (in bytes) of the function.
+
+ As a result, for this optimization, some fragments (generated with a new
+ type rs_sframe) for the SFrame section are fixed up later.
+
+ This optimization (for size) is enabled by default. */
+
+#ifndef SFRAME_FRE_TYPE_SELECTION_OPT
+# define SFRAME_FRE_TYPE_SELECTION_OPT 1
+#endif
+
+/* Emit a single byte into the current segment. */
+
+static inline void
+out_one (int byte)
+{
+ FRAG_APPEND_1_CHAR (byte);
+}
+
+/* Emit a two-byte word into the current segment. */
+
+static inline void
+out_two (int data)
+{
+ md_number_to_chars (frag_more (2), data, 2);
+}
+
+/* Emit a four byte word into the current segment. */
+
+static inline void
+out_four (int data)
+{
+ md_number_to_chars (frag_more (4), data, 4);
+}
+
+/* Get the start address symbol from the DWARF FDE. */
+
+static symbolS*
+get_dw_fde_start_addrS (const struct fde_entry *dw_fde)
+{
+ return dw_fde->start_address;
+}
+
+/* Get the start address symbol from the DWARF FDE. */
+
+static symbolS*
+get_dw_fde_end_addrS (const struct fde_entry *dw_fde)
+{
+ return dw_fde->end_address;
+}
+
+/* SFrame Frame Row Entry (FRE) related functions. */
+
+static void
+sframe_fre_set_begin_addr (struct sframe_row_entry *fre, symbolS *beginS)
+{
+ fre->pc_begin = beginS;
+}
+
+static void
+sframe_fre_set_end_addr (struct sframe_row_entry *fre, symbolS *endS)
+{
+ fre->pc_end = endS;
+}
+
+static void
+sframe_fre_set_cfa_base_reg (struct sframe_row_entry *fre,
+ unsigned int cfa_base_reg)
+{
+ fre->cfa_base_reg = cfa_base_reg;
+ fre->merge_candidate = false;
+}
+
+static void
+sframe_fre_set_cfa_offset (struct sframe_row_entry *fre,
+ offsetT cfa_offset)
+{
+ fre->cfa_offset = cfa_offset;
+ fre->merge_candidate = false;
+}
+
+#ifdef SFRAME_FRE_RA_TRACKING
+static void
+sframe_fre_set_ra_track (struct sframe_row_entry *fre, offsetT ra_offset)
+{
+ fre->ra_loc = SFRAME_FRE_ELEM_LOC_STACK;
+ fre->ra_offset = ra_offset;
+ fre->merge_candidate = false;
+}
+#endif
+
+static void
+sframe_fre_set_bp_track (struct sframe_row_entry *fre, offsetT bp_offset)
+{
+ fre->bp_loc = SFRAME_FRE_ELEM_LOC_STACK;
+ fre->bp_offset = bp_offset;
+ fre->merge_candidate = false;
+}
+
+/* All stack offset values within an FRE are uniformly encoded in the same
+ number of bytes. The size of the stack offset values will, however, vary
+ across FREs. */
+
+#define VALUE_8BIT 0x7f
+#define VALUE_16BIT 0x7fff
+#define VALUE_32BIT 0x7fffffff
+#define VALUE_64BIT 0x7fffffffffffffff
+
+/* Given a signed offset, return the size in bytes needed to represent it. */
+
+static unsigned int
+get_offset_size_in_bytes (offsetT value)
+{
+ unsigned int size = 0;
+
+ if (value <= VALUE_8BIT && value >= (offsetT) -VALUE_8BIT)
+ size = 1;
+ else if (value <= VALUE_16BIT && value >= (offsetT) -VALUE_16BIT)
+ size = 2;
+ else if (value <= VALUE_32BIT && value >= (offsetT) -VALUE_32BIT)
+ size = 4;
+ else if ((sizeof (offsetT) > 4) && (value <= (offsetT) VALUE_64BIT
+ && value >= (offsetT) -VALUE_64BIT))
+ size = 8;
+
+ return size;
+}
+
+#define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_1B 0 /* SFRAME_FRE_OFFSET_1B. */
+#define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_2B 1 /* SFRAME_FRE_OFFSET_2B. */
+#define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_4B 2 /* SFRAME_FRE_OFFSET_4B. */
+#define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_8B 3 /* Not supported in SFrame. */
+#define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_MAX SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_8B
+
+/* Helper struct for mapping offset size to output functions. */
+
+struct sframe_fre_offset_func_map
+{
+ unsigned int offset_size;
+ void (*out_func)(int);
+};
+
+/* Given an OFFSET_SIZE, return the size in bytes needed to represent it. */
+
+static unsigned int
+sframe_fre_offset_func_map_index (unsigned int offset_size)
+{
+ unsigned int index = SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_MAX;
+
+ switch (offset_size)
+ {
+ case SFRAME_FRE_OFFSET_1B:
+ index = SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_1B;
+ break;
+ case SFRAME_FRE_OFFSET_2B:
+ index = SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_2B;
+ break;
+ case SFRAME_FRE_OFFSET_4B:
+ index = SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_4B;
+ break;
+ default:
+ /* Not supported in SFrame. */
+ break;
+ }
+
+ return index;
+}
+
+/* Mapping from offset size to the output function to emit the value. */
+
+static const
+struct sframe_fre_offset_func_map
+fre_offset_func_map[SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_MAX+1] =
+{
+ { SFRAME_FRE_OFFSET_1B, out_one },
+ { SFRAME_FRE_OFFSET_2B, out_two },
+ { SFRAME_FRE_OFFSET_4B, out_four },
+ { -1, NULL } /* Not Supported in SFrame. */
+};
+
+/* SFrame version specific operations access. */
+
+static struct sframe_version_ops sframe_ver_ops;
+
+/* SFrame (SFRAME_VERSION_1) set FRE info. */
+
+static unsigned char
+sframe_v1_set_fre_info (unsigned int base_reg, unsigned int num_offsets,
+ unsigned int offset_size)
+{
+ unsigned char fre_info;
+ fre_info = SFRAME_V1_FRE_INFO (base_reg, num_offsets, offset_size);
+ return fre_info;
+}
+
+/* SFrame (SFRAME_VERSION_1) set function info. */
+static unsigned char
+sframe_v1_set_func_info (unsigned int fde_type, unsigned int fre_type)
+{
+ unsigned char func_info;
+ func_info = SFRAME_V1_FUNC_INFO (fde_type, fre_type);
+ return func_info;
+}
+
+/* SFrame version specific operations setup. */
+
+static void
+sframe_set_version (uint32_t sframe_version __attribute__((unused)))
+{
+ sframe_ver_ops.format_version = SFRAME_VERSION_1;
+
+ sframe_ver_ops.set_fre_info = sframe_v1_set_fre_info;
+
+ sframe_ver_ops.set_func_info = sframe_v1_set_func_info;
+}
+
+/* SFrame set FRE info. */
+
+static unsigned char
+sframe_set_fre_info (unsigned int base_reg, unsigned int num_offsets,
+ unsigned int offset_size)
+{
+ return sframe_ver_ops.set_fre_info (base_reg, num_offsets,
+ offset_size);
+}
+
+/* SFrame set func info. */
+
+ATTRIBUTE_UNUSED static unsigned char
+sframe_set_func_info (unsigned int fde_type, unsigned int fre_type)
+{
+ return sframe_ver_ops.set_func_info (fde_type, fre_type);
+}
+
+/* Get the number of SFrame FDEs for the current file. */
+
+static unsigned int
+get_num_sframe_fdes (void);
+
+/* Get the number of SFrame frame row entries for the current file. */
+
+static unsigned int
+get_num_sframe_fres (void);
+
+/* Get CFA base register ID as represented in SFrame Frame Row Entry. */
+
+static unsigned int
+get_fre_base_reg_id (struct sframe_row_entry *sframe_fre)
+{
+ unsigned int cfi_insn_cfa_base_reg = sframe_fre->cfa_base_reg;
+ unsigned fre_base_reg = SFRAME_BASE_REG_SP;
+
+ if (cfi_insn_cfa_base_reg == SFRAME_CFA_FP_REG)
+ fre_base_reg = SFRAME_BASE_REG_FP;
+
+ /* Only one bit is reserved in SFRAME_VERSION_1. */
+ gas_assert (fre_base_reg == SFRAME_BASE_REG_SP
+ || fre_base_reg == SFRAME_BASE_REG_FP);
+
+ return fre_base_reg;
+}
+
+/* Get number of offsets necessary for the SFrame Frame Row Entry. */
+
+static unsigned int
+get_fre_num_offsets (struct sframe_row_entry *sframe_fre)
+{
+ /* Atleast 1 must always be present (to recover CFA). */
+ unsigned int fre_num_offsets = 1;
+
+ if (sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK)
+ fre_num_offsets++;
+#ifdef SFRAME_FRE_RA_TRACKING
+ if (sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK)
+ fre_num_offsets++;
+#endif
+ return fre_num_offsets;
+}
+
+/* Get the minimum necessary offset size (in bytes) for this
+ SFrame frame row entry. */
+
+static unsigned int
+sframe_get_fre_offset_size (struct sframe_row_entry *sframe_fre)
+{
+ unsigned int max_offset_size = 0;
+ unsigned int cfa_offset_size = 0;
+ unsigned int bp_offset_size = 0;
+ unsigned int ra_offset_size = 0;
+
+ unsigned int fre_offset_size = 0;
+
+ /* What size of offsets appear in this frame row entry. */
+ cfa_offset_size = get_offset_size_in_bytes (sframe_fre->cfa_offset);
+ if (sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK)
+ bp_offset_size = get_offset_size_in_bytes (sframe_fre->bp_offset);
+#ifdef SFRAME_FRE_RA_TRACKING
+ if (sframe_ra_tracking_p ()
+ && sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK)
+ ra_offset_size = get_offset_size_in_bytes (sframe_fre->ra_offset);
+#endif
+
+ /* Get the maximum size needed to represent the offsets. */
+ max_offset_size = cfa_offset_size;
+ if (bp_offset_size > max_offset_size)
+ max_offset_size = bp_offset_size;
+ if (ra_offset_size > max_offset_size)
+ max_offset_size = ra_offset_size;
+
+ gas_assert (max_offset_size);
+
+ switch (max_offset_size)
+ {
+ case 1:
+ fre_offset_size = SFRAME_FRE_OFFSET_1B;
+ break;
+ case 2:
+ fre_offset_size = SFRAME_FRE_OFFSET_2B;
+ break;
+ case 4:
+ fre_offset_size = SFRAME_FRE_OFFSET_4B;
+ break;
+ default:
+ /* Offset of size 8 bytes is not supported in SFrame format
+ version 1. */
+ as_fatal (_("SFrame unsupported offset value\n"));
+ break;
+ }
+
+ return fre_offset_size;
+}
+
+#if SFRAME_FRE_TYPE_SELECTION_OPT
+
+/* Create a composite exression CEXP (for SFrame FRE start address) such that:
+
+ exp = <val> OP_absent <width>, where,
+
+ - <val> and <width> are themselves expressionS.
+ - <val> stores the expression which when evaluated gives the value of the
+ start address offset of the FRE.
+ - <width> stores the expression when when evaluated gives the number of
+ bytes needed to encode the start address offset of the FRE.
+
+ The use of OP_absent as the X_op_symbol helps identify this expression
+ later when fragments are fixed up. */
+
+static void
+create_fre_start_addr_exp (expressionS *cexp, symbolS *fre_pc_begin,
+ symbolS *fde_start_address,
+ symbolS *fde_end_address)
+{
+ expressionS val;
+ expressionS width;
+
+ /* val expression stores the FDE start address offset from the start PC
+ of function. */
+ val.X_op = O_subtract;
+ val.X_add_symbol = fre_pc_begin;
+ val.X_op_symbol = fde_start_address;
+ val.X_add_number = 0;
+
+ /* width expressions stores the size of the function. This is used later
+ to determine the number of bytes to be used to encode the FRE start
+ address of each FRE of the function. */
+ width.X_op = O_subtract;
+ width.X_add_symbol = fde_end_address;
+ width.X_op_symbol = fde_start_address;
+ width.X_add_number = 0;
+
+ cexp->X_op = O_absent;
+ cexp->X_add_symbol = make_expr_symbol (&val);
+ cexp->X_op_symbol = make_expr_symbol (&width);
+ cexp->X_add_number = 0;
+}
+
+#endif
+
+static void
+output_sframe_row_entry (symbolS *fde_start_addr,
+ symbolS *fde_end_addr,
+ struct sframe_row_entry *sframe_fre)
+{
+ unsigned char fre_info;
+ unsigned int fre_num_offsets;
+ unsigned int fre_offset_size;
+ unsigned int fre_base_reg;
+ expressionS exp;
+ unsigned int fre_addr_size;
+
+ unsigned int index = 0;
+ unsigned int fre_write_offsets = 0;
+
+ fre_addr_size = 4; /* 4 bytes by default. FIXME tie it to fre_type? */
+
+ /* SFrame FRE Start Address. */
+#if SFRAME_FRE_TYPE_SELECTION_OPT
+ create_fre_start_addr_exp (&exp, sframe_fre->pc_begin, fde_start_addr,
+ fde_end_addr);
+ frag_grow (fre_addr_size);
+ frag_var (rs_sframe, fre_addr_size, 0, (relax_substateT) 0,
+ make_expr_symbol (&exp), 0, (char *) frag_now);
+#else
+ gas_assert (fde_end_addr);
+ exp.X_op = O_subtract;
+ exp.X_add_symbol = sframe_fre->pc_begin; /* to. */
+ exp.X_op_symbol = fde_start_addr; /* from. */
+ exp.X_add_number = 0;
+ emit_expr (&exp, fre_addr_size);
+#endif
+
+ /* Create the fre_info using the CFA base register, number of offsets and max
+ size of offset in this frame row entry. */
+ fre_base_reg = get_fre_base_reg_id (sframe_fre);
+ fre_num_offsets = get_fre_num_offsets (sframe_fre);
+ fre_offset_size = sframe_get_fre_offset_size (sframe_fre);
+ fre_info = sframe_set_fre_info (fre_base_reg, fre_num_offsets,
+ fre_offset_size);
+ out_one (fre_info);
+
+ index = sframe_fre_offset_func_map_index (fre_offset_size);
+ gas_assert (index < SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_MAX);
+
+ /* Write out the offsets in order - cfa, bp, ra. */
+ fre_offset_func_map[index].out_func (sframe_fre->cfa_offset);
+ fre_write_offsets++;
+
+#ifdef SFRAME_FRE_RA_TRACKING
+ if (sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK)
+ {
+ fre_offset_func_map[index].out_func (sframe_fre->ra_offset);
+ fre_write_offsets++;
+ }
+#endif
+ if (sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK)
+ {
+ fre_offset_func_map[index].out_func (sframe_fre->bp_offset);
+ fre_write_offsets++;
+ }
+
+ /* Check if the expected number offsets have been written out
+ in this FRE. */
+ gas_assert (fre_write_offsets == fre_num_offsets);
+}
+
+static void
+output_sframe_funcdesc (symbolS *start_of_fre_section,
+ symbolS *fre_symbol,
+ struct sframe_func_entry *sframe_fde)
+{
+ expressionS exp;
+ unsigned int addr_size;
+ symbolS *dw_fde_start_addrS, *dw_fde_end_addrS;
+
+ addr_size = SFRAME_RELOC_SIZE;
+ dw_fde_start_addrS = get_dw_fde_start_addrS (sframe_fde->dw_fde);
+ dw_fde_end_addrS = get_dw_fde_end_addrS (sframe_fde->dw_fde);
+
+ /* Start address of the function. */
+ exp.X_op = O_subtract;
+ exp.X_add_symbol = dw_fde_start_addrS; /* to location. */
+ exp.X_op_symbol = symbol_temp_new_now (); /* from location. */
+ exp.X_add_number = 0;
+ emit_expr (&exp, addr_size);
+
+ /* Size of the function in bytes. */
+ exp.X_op = O_subtract;
+ exp.X_add_symbol = dw_fde_end_addrS;
+ exp.X_op_symbol = dw_fde_start_addrS;
+ exp.X_add_number = 0;
+ emit_expr (&exp, addr_size);
+
+ /* Offset to the first frame row entry. */
+ exp.X_op = O_subtract;
+ exp.X_add_symbol = fre_symbol; /* Minuend. */
+ exp.X_op_symbol = start_of_fre_section; /* Subtrahend. */
+ exp.X_add_number = 0;
+ emit_expr (&exp, addr_size);
+
+ /* Number of FREs. */
+ out_four (sframe_fde->num_fres);
+
+ /* SFrame FDE function info. */
+#if SFRAME_FRE_TYPE_SELECTION_OPT
+ expressionS width;
+ width.X_op = O_subtract;
+ width.X_add_symbol = dw_fde_end_addrS;
+ width.X_op_symbol = dw_fde_start_addrS;
+ width.X_add_number = 0;
+ frag_grow (1); /* Size of func info is unsigned char. */
+ frag_var (rs_sframe, 1, 0, (relax_substateT) 0,
+ make_expr_symbol (&width), 0, (char *) frag_now);
+#else
+ unsigned char func_info;
+ func_info = sframe_set_func_info (SFRAME_FDE_TYPE_PCINC,
+ SFRAME_FRE_TYPE_ADDR4);
+ out_one (func_info);
+#endif
+}
+
+static void
+output_sframe_internal (void)
+{
+ expressionS exp;
+ unsigned int i = 0;
+
+ symbolS *end_of_frame_hdr;
+ symbolS *end_of_frame_section;
+ symbolS *start_of_func_desc_section;
+ symbolS *start_of_fre_section;
+ struct sframe_func_entry *sframe_fde;
+ struct sframe_row_entry *sframe_fre;
+ unsigned char abi_arch = 0;
+ int fixed_bp_offset = SFRAME_CFA_FIXED_FP_INVALID;
+ int fixed_ra_offset = SFRAME_CFA_FIXED_RA_INVALID;
+ unsigned int addr_size;
+
+ addr_size = SFRAME_RELOC_SIZE;
+
+ /* The function desciptor entries as dumped by the assembler are not
+ sorted on PCs. */
+ unsigned char sframe_flags = 0;
+ sframe_flags |= !SFRAME_F_FDE_SORTED;
+
+ unsigned int num_fdes = get_num_sframe_fdes ();
+ unsigned int num_fres = get_num_sframe_fres ();
+ symbolS **fre_symbols = XNEWVEC (symbolS *, num_fres);
+ for (i = 0; i < num_fres; i++)
+ fre_symbols[i] = symbol_temp_make ();
+
+ end_of_frame_hdr = symbol_temp_make ();
+ start_of_fre_section = symbol_temp_make ();
+ start_of_func_desc_section = symbol_temp_make ();
+ end_of_frame_section = symbol_temp_make ();
+
+ /* Output the preamble of SFrame section. */
+ out_two (SFRAME_MAGIC);
+ out_one (SFRAME_VERSION);
+ out_one (sframe_flags);
+ /* abi/arch. */
+#ifdef sframe_get_abi_arch
+ abi_arch = sframe_get_abi_arch ();
+#endif
+ gas_assert (abi_arch);
+ out_one (abi_arch);
+
+ /* Offset for the BP register from CFA. Neither of the AMD64 or AAPCS64
+ ABIs have a fixed offset for the BP register from the CFA. This may be
+ useful in future (but not without additional support in the toolchain)
+ for specialized handling/encoding for cases where, for example,
+ -fno-omit-frame-pointer is used. */
+ out_one (fixed_bp_offset);
+
+ /* Offset for the return address from CFA is fixed for some ABIs
+ (e.g., AMD64), output a zero otherwise. */
+#ifdef sframe_ra_tracking_p
+ if (!sframe_ra_tracking_p ())
+ fixed_ra_offset = sframe_cfa_ra_offset ();
+#endif
+ out_one (fixed_ra_offset);
+
+ /* None of the AMD64, or AARCH64 ABIs need the auxilliary header.
+ When the need does arise to use this field, the appropriate backend
+ must provide this information. */
+ out_one (0); /* Auxilliary SFrame header length. */
+
+ out_four (num_fdes); /* Number of FDEs. */
+ out_four (num_fres); /* Number of FREs. */
+
+ /* FRE sub-section len. */
+ exp.X_op = O_subtract;
+ exp.X_add_symbol = end_of_frame_section;
+ exp.X_op_symbol = start_of_fre_section;
+ exp.X_add_number = 0;
+ emit_expr (&exp, addr_size);
+
+ /* Offset of Function Index sub-section. */
+ exp.X_op = O_subtract;
+ exp.X_add_symbol = end_of_frame_hdr;
+ exp.X_op_symbol = start_of_func_desc_section;
+ exp.X_add_number = 0;
+ emit_expr (&exp, addr_size);
+
+ /* Offset of FRE sub-section. */
+ exp.X_op = O_subtract;
+ exp.X_add_symbol = start_of_fre_section;
+ exp.X_op_symbol = end_of_frame_hdr;
+ exp.X_add_number = 0;
+ emit_expr (&exp, addr_size);
+
+ symbol_set_value_now (end_of_frame_hdr);
+ symbol_set_value_now (start_of_func_desc_section);
+
+ /* Output the SFrame function descriptor entries. */
+ i = 0;
+ for (sframe_fde = all_sframe_fdes; sframe_fde; sframe_fde = sframe_fde->next)
+ {
+ output_sframe_funcdesc (start_of_fre_section,
+ fre_symbols[i], sframe_fde);
+ i += sframe_fde->num_fres;
+ }
+
+ symbol_set_value_now (start_of_fre_section);
+
+ /* Output the SFrame FREs. */
+ i = 0;
+ sframe_fde = all_sframe_fdes;
+
+ for (sframe_fde = all_sframe_fdes; sframe_fde; sframe_fde = sframe_fde->next)
+ {
+ for (sframe_fre = sframe_fde->sframe_fres;
+ sframe_fre;
+ sframe_fre = sframe_fre->next)
+ {
+ symbol_set_value_now (fre_symbols[i]);
+ output_sframe_row_entry (get_dw_fde_start_addrS (sframe_fde->dw_fde),
+ get_dw_fde_end_addrS (sframe_fde->dw_fde),
+ sframe_fre);
+ i++;
+ }
+ }
+
+ symbol_set_value_now (end_of_frame_section);
+
+ gas_assert (i == num_fres);
+
+ free (fre_symbols);
+ fre_symbols = NULL;
+}
+
+/* List of SFrame FDE entries. */
+
+struct sframe_func_entry *all_sframe_fdes;
+
+/* Tail of the list to add to. */
+
+static struct sframe_func_entry **last_sframe_fde = &all_sframe_fdes;
+
+static unsigned int
+get_num_sframe_fdes (void)
+{
+ struct sframe_func_entry *sframe_fde;
+ unsigned int total_fdes = 0;
+
+ for (sframe_fde = all_sframe_fdes; sframe_fde ; sframe_fde = sframe_fde->next)
+ total_fdes++;
+
+ return total_fdes;
+}
+
+/* Get the total number of SFrame row entries across the FDEs. */
+
+static unsigned int
+get_num_sframe_fres (void)
+{
+ struct sframe_func_entry *sframe_fde;
+ unsigned int total_fres = 0;
+
+ for (sframe_fde = all_sframe_fdes; sframe_fde ; sframe_fde = sframe_fde->next)
+ total_fres += sframe_fde->num_fres;
+
+ return total_fres;
+}
+
+/* Allocate an SFrame FDE. */
+
+static struct sframe_func_entry*
+sframe_fde_alloc (void)
+{
+ struct sframe_func_entry *sframe_fde = XCNEW (struct sframe_func_entry);
+ return sframe_fde;
+}
+
+/* Link the SFrame FDE in. */
+
+static int
+sframe_fde_link (struct sframe_func_entry *sframe_fde)
+{
+ *last_sframe_fde = sframe_fde;
+ last_sframe_fde = &sframe_fde->next;
+
+ return 0;
+}
+
+/* Free up the SFrame FDE. */
+
+static void
+sframe_fde_free (struct sframe_func_entry *sframe_fde)
+{
+ XDELETE (sframe_fde);
+ sframe_fde = NULL;
+}
+
+/* SFrame translation context functions. */
+
+/* Allocate a new SFrame translation context. */
+
+static struct sframe_xlate_ctx*
+sframe_xlate_ctx_alloc (void)
+{
+ struct sframe_xlate_ctx* xlate_ctx = XCNEW (struct sframe_xlate_ctx);
+ return xlate_ctx;
+}
+
+/* Initialize the given SFrame translation context. */
+
+static void
+sframe_xlate_ctx_init (struct sframe_xlate_ctx *xlate_ctx)
+{
+ xlate_ctx->dw_fde = NULL;
+ xlate_ctx->first_fre = NULL;
+ xlate_ctx->last_fre = NULL;
+ xlate_ctx->cur_fre = NULL;
+ xlate_ctx->remember_fre = NULL;
+ xlate_ctx->num_xlate_fres = 0;
+}
+
+/* Cleanup the given SFrame translation context. */
+
+static void
+sframe_xlate_ctx_cleanup (struct sframe_xlate_ctx *xlate_ctx)
+{
+ struct sframe_row_entry *fre, *fre_next;
+
+ if (xlate_ctx->num_xlate_fres)
+ {
+ fre = xlate_ctx->first_fre;
+ while (fre)
+ {
+ fre_next = fre->next;
+ XDELETE (fre);
+ fre = fre_next;
+ }
+ }
+
+ sframe_xlate_ctx_init (xlate_ctx);
+}
+
+/* Transfer the state from the SFrame translation context to the SFrame FDE. */
+
+static void
+sframe_xlate_ctx_finalize (struct sframe_xlate_ctx *xlate_ctx,
+ struct sframe_func_entry *sframe_fde)
+{
+ sframe_fde->dw_fde = xlate_ctx->dw_fde;
+ sframe_fde->sframe_fres = xlate_ctx->first_fre;
+ sframe_fde->num_fres = xlate_ctx->num_xlate_fres;
+}
+
+static struct sframe_row_entry*
+sframe_row_entry_new (void)
+{
+ struct sframe_row_entry *fre = XCNEW (struct sframe_row_entry);
+ /* Reset cfa_base_reg to -1. A value of 0 will imply some valid register
+ for the supported arches. */
+ fre->cfa_base_reg = -1;
+ fre->merge_candidate = true;
+
+ return fre;
+}
+
+/* Add the given FRE in the list of frame row entries in the given FDE
+ translation context. */
+
+static void
+sframe_xlate_ctx_add_fre (struct sframe_xlate_ctx *xlate_ctx,
+ struct sframe_row_entry *fre)
+{
+ gas_assert (xlate_ctx && fre);
+
+ /* Add the frame row entry. */
+ if (!xlate_ctx->first_fre)
+ xlate_ctx->first_fre = fre;
+ else if (xlate_ctx->last_fre)
+ xlate_ctx->last_fre->next = fre;
+
+ xlate_ctx->last_fre = fre;
+
+ /* Keep track of the total number of SFrame frame row entries. */
+ xlate_ctx->num_xlate_fres++;
+}
+
+/* A SFrame Frame Row Entry is self-sufficient in terms of unwind information
+ for a given PC. It contains information assimilated from multiple CFI
+ instructions, and hence, a new SFrame FRE is initialized with the data from
+ the previous known FRE, if any.
+
+ Understandably, not all information (especially the instruction begin
+ and end boundaries) needs to be relayed. Hence, the caller of this API
+ must set the pc_begin and pc_end as applicable. */
+
+static void
+sframe_row_entry_initialize (struct sframe_row_entry *cur_fre,
+ struct sframe_row_entry *prev_fre)
+{
+ gas_assert (prev_fre);
+ cur_fre->cfa_base_reg = prev_fre->cfa_base_reg;
+ cur_fre->cfa_offset = prev_fre->cfa_offset;
+ cur_fre->bp_loc = prev_fre->bp_loc;
+ cur_fre->bp_offset = prev_fre->bp_offset;
+ cur_fre->ra_loc = prev_fre->ra_loc;
+ cur_fre->ra_offset = prev_fre->ra_offset;
+}
+
+/* Translate DW_CFA_advance_loc into SFrame context.
+ Return SFRAME_XLATE_OK if success. */
+
+static int
+sframe_xlate_do_advance_loc (struct sframe_xlate_ctx *xlate_ctx,
+ struct cfi_insn_data *cfi_insn)
+{
+ struct sframe_row_entry *last_fre = xlate_ctx->last_fre;
+ /* Get the scratchpad FRE currently being updated as the cfi_insn's
+ get 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;
+
+ if (cur_fre)
+ {
+ if (!cur_fre->merge_candidate)
+ {
+ sframe_fre_set_end_addr (cur_fre, cfi_insn->u.ll.lab2);
+
+ sframe_xlate_ctx_add_fre (xlate_ctx, cur_fre);
+ last_fre = xlate_ctx->last_fre;
+
+ xlate_ctx->cur_fre = sframe_row_entry_new ();
+ cur_fre = xlate_ctx->cur_fre;
+
+ if (last_fre)
+ sframe_row_entry_initialize (cur_fre, last_fre);
+ }
+ else
+ {
+ sframe_fre_set_end_addr (last_fre, cfi_insn->u.ll.lab2);
+ gas_assert (last_fre->merge_candidate == false);
+ }
+ }
+ else
+ {
+ xlate_ctx->cur_fre = sframe_row_entry_new ();
+ cur_fre = xlate_ctx->cur_fre;
+ }
+
+ gas_assert (cur_fre);
+ sframe_fre_set_begin_addr (cur_fre, cfi_insn->u.ll.lab2);
+
+ return SFRAME_XLATE_OK;
+}
+
+/* Translate DW_CFA_def_cfa into SFrame context.
+ Return SFRAME_XLATE_OK if success. */
+
+static int
+sframe_xlate_do_def_cfa (struct sframe_xlate_ctx *xlate_ctx,
+ struct cfi_insn_data *cfi_insn)
+
+{
+ /* Get the scratchpad FRE. This FRE will eventually get linked in. */
+ struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre;
+ if (!cur_fre)
+ {
+ xlate_ctx->cur_fre = sframe_row_entry_new ();
+ cur_fre = xlate_ctx->cur_fre;
+ sframe_fre_set_begin_addr (cur_fre,
+ get_dw_fde_start_addrS (xlate_ctx->dw_fde));
+ }
+ /* Define the current CFA rule to use the provided register and
+ offset. */
+ sframe_fre_set_cfa_base_reg (cur_fre, cfi_insn->u.ri.reg);
+ sframe_fre_set_cfa_offset (cur_fre, cfi_insn->u.ri.offset);
+ cur_fre->merge_candidate = false;
+
+ return SFRAME_XLATE_OK;
+}
+
+/* Translate DW_CFA_def_cfa_register into SFrame context.
+ Return SFRAME_XLATE_OK if success. */
+
+static int
+sframe_xlate_do_def_cfa_register (struct sframe_xlate_ctx *xlate_ctx,
+ struct cfi_insn_data *cfi_insn)
+{
+ struct sframe_row_entry *last_fre = xlate_ctx->last_fre;
+ /* Get the scratchpad FRE. This FRE will eventually get linked in. */
+ struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre;
+ gas_assert (cur_fre);
+ /* Define the current CFA rule to use the provided register (but to
+ keep the old offset). */
+ sframe_fre_set_cfa_base_reg (cur_fre, cfi_insn->u.ri.reg);
+ sframe_fre_set_cfa_offset (cur_fre, last_fre->cfa_offset);
+ cur_fre->merge_candidate = false;
+
+ return SFRAME_XLATE_OK;
+}
+
+/* Translate DW_CFA_def_cfa_offset into SFrame context.
+ Return SFRAME_XLATE_OK if success. */
+
+static int
+sframe_xlate_do_def_cfa_offset (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);
+ /* Define the current CFA rule to use the provided offset (but to keep
+ the old register). However, if the old register is not FP/SP,
+ skip creating SFrame unwind info for the function. */
+ if ((cur_fre->cfa_base_reg == SFRAME_CFA_FP_REG)
+ || (cur_fre->cfa_base_reg == SFRAME_CFA_SP_REG))
+ {
+ sframe_fre_set_cfa_offset (cur_fre, cfi_insn->u.i);
+ cur_fre->merge_candidate = false;
+ }
+ else
+ return SFRAME_XLATE_ERR_NOTREPRESENTED;
+
+ return SFRAME_XLATE_OK;
+}
+
+/* Translate DW_CFA_offset into SFrame context.
+ Return SFRAME_XLATE_OK if success. */
+
+static int
+sframe_xlate_do_offset (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 offset. */
+ if (cfi_insn->u.r == SFRAME_CFA_FP_REG)
+ {
+ gas_assert (!cur_fre->base_reg);
+ sframe_fre_set_bp_track (cur_fre, cfi_insn->u.ri.offset);
+ cur_fre->merge_candidate = false;
+ }
+#ifdef SFRAME_FRE_RA_TRACKING
+ else if (sframe_ra_tracking_p ()
+ && cfi_insn->u.r == SFRAME_CFA_RA_REG)
+ {
+ sframe_fre_set_ra_track (cur_fre, cfi_insn->u.ri.offset);
+ cur_fre->merge_candidate = false;
+ }
+#endif
+ /* This is used to track changes to non-rsp registers, skip all others
+ except FP / RA for now. */
+ return SFRAME_XLATE_OK;
+}
+
+/* Translate DW_CFA_val_offset into SFrame context.
+ Return SFRAME_XLATE_OK if success. */
+
+static int
+sframe_xlate_do_val_offset (struct sframe_xlate_ctx *xlate_ctx ATTRIBUTE_UNUSED,
+ struct cfi_insn_data *cfi_insn)
+{
+ /* Previous value of register is CFA + offset. However, if the specified
+ register is not interesting (FP or RA reg), the current DW_CFA_val_offset
+ instruction can be safely skipped without sacrificing the asynchonicity of
+ unwind information. */
+ if (cfi_insn->u.r == SFRAME_CFA_FP_REG)
+ return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */
+#ifdef SFRAME_FRE_RA_TRACKING
+ else if (sframe_ra_tracking_p ()
+ && cfi_insn->u.r == SFRAME_CFA_RA_REG)
+ return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */
+#endif
+
+ /* Safe to skip. */
+ return SFRAME_XLATE_OK;
+}
+
+/* Translate DW_CFA_remember_state into SFrame context.
+ Return SFRAME_XLATE_OK if success. */
+
+static int
+sframe_xlate_do_remember_state (struct sframe_xlate_ctx *xlate_ctx)
+{
+ struct sframe_row_entry *last_fre = xlate_ctx->last_fre;
+
+ /* If there is no FRE state to remember, nothing to do here. Return
+ early with non-zero error code, this will cause no SFrame unwind info
+ for the function involved. */
+ if (!last_fre)
+ return SFRAME_XLATE_ERR_INVAL;
+
+ if (!xlate_ctx->remember_fre)
+ xlate_ctx->remember_fre = sframe_row_entry_new ();
+ sframe_row_entry_initialize (xlate_ctx->remember_fre, last_fre);
+
+ return SFRAME_XLATE_OK;
+}
+
+/* Translate DW_CFA_restore_state into SFrame context.
+ Return SFRAME_XLATE_OK if success. */
+
+static int
+sframe_xlate_do_restore_state (struct sframe_xlate_ctx *xlate_ctx)
+{
+ /* 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 (xlate_ctx->remember_fre);
+ gas_assert (cur_fre && cur_fre->merge_candidate);
+
+ /* Get the CFA state from the DW_CFA_remember_state insn. */
+ sframe_row_entry_initialize (cur_fre, xlate_ctx->remember_fre);
+ /* The PC boundaries of the current SFrame FRE are updated
+ via other machinery. */
+ cur_fre->merge_candidate = false;
+ return SFRAME_XLATE_OK;
+}
+
+/* Translate DW_CFA_restore into SFrame context.
+ Return SFRAME_XLATE_OK if success. */
+
+static int
+sframe_xlate_do_restore (struct sframe_xlate_ctx *xlate_ctx,
+ struct cfi_insn_data *cfi_insn)
+{
+ struct sframe_row_entry *cie_fre = xlate_ctx->first_fre;
+ /* 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;
+
+ /* Change the rule for the indicated register to the rule assigned to
+ it by the initial_instructions in the CIE. */
+ gas_assert (cie_fre);
+ /* SFrame FREs track only CFA and FP / RA for backtracing purposes;
+ skip the other .cfi_restore directives. */
+ if (cfi_insn->u.r == SFRAME_CFA_FP_REG)
+ {
+ gas_assert (cur_fre);
+ cur_fre->bp_loc = cie_fre->bp_loc;
+ cur_fre->bp_offset = cie_fre->bp_offset;
+ cur_fre->merge_candidate = false;
+ }
+#ifdef SFRAME_FRE_RA_TRACKING
+ else if (sframe_ra_tracking_p ()
+ && cfi_insn->u.r == SFRAME_CFA_RA_REG)
+ {
+ gas_assert (cur_fre);
+ cur_fre->ra_loc = cie_fre->ra_loc;
+ cur_fre->ra_offset = cie_fre->ra_offset;
+ cur_fre->merge_candidate = false;
+ }
+#endif
+ return SFRAME_XLATE_OK;
+}
+
+/* Process CFI_INSN and update the translation context with the FRE
+ information.
+
+ Returns an error code (sframe_xlate_err) if CFI_INSN is not successfully
+ processed. */
+
+static int
+sframe_do_cfi_insn (struct sframe_xlate_ctx *xlate_ctx,
+ struct cfi_insn_data *cfi_insn)
+{
+ int err = 0;
+
+ /* Atleast one cfi_insn per FDE is expected. */
+ gas_assert (cfi_insn);
+ int op = cfi_insn->insn;
+
+ switch (op)
+ {
+ case DW_CFA_advance_loc:
+ err = sframe_xlate_do_advance_loc (xlate_ctx, cfi_insn);
+ break;
+ case DW_CFA_def_cfa:
+ err = sframe_xlate_do_def_cfa (xlate_ctx, cfi_insn);
+ break;
+ case DW_CFA_def_cfa_register:
+ err = sframe_xlate_do_def_cfa_register (xlate_ctx, cfi_insn);
+ break;
+ case DW_CFA_def_cfa_offset:
+ err = sframe_xlate_do_def_cfa_offset (xlate_ctx, cfi_insn);
+ break;
+ case DW_CFA_offset:
+ err = sframe_xlate_do_offset (xlate_ctx, cfi_insn);
+ break;
+ case DW_CFA_val_offset:
+ err = sframe_xlate_do_val_offset (xlate_ctx, cfi_insn);
+ break;
+ case DW_CFA_remember_state:
+ err = sframe_xlate_do_remember_state (xlate_ctx);
+ break;
+ case DW_CFA_restore_state:
+ err = sframe_xlate_do_restore_state (xlate_ctx);
+ break;
+ case DW_CFA_restore:
+ err = sframe_xlate_do_restore (xlate_ctx, cfi_insn);
+ break;
+ case DW_CFA_undefined:
+ case DW_CFA_same_value:
+ break;
+ default:
+ {
+ /* Other CFI opcodes are not processed at this time.
+ These do not impact the coverage of the basic stack unwinding
+ information as conveyed in the SFrame format.
+ - DW_CFA_register,
+ - ...
+
+ Following skipped operations do, however, impact the asynchronicity:
+ - CFI_escape,
+ - DW_CFA_GNU_window_save,
+ - DW_CFA_AARCH64_negate_ra_state (multiplexed with
+ DW_CFA_GNU_window_save) */
+
+ err = SFRAME_XLATE_ERR_NOTREPRESENTED;
+ // printf (_("SFrame Unsupported or unknown Dwarf CFI number: %#x\n"), op);
+ }
+ }
+
+ return err;
+}
+
+
+static int
+sframe_do_fde (struct sframe_xlate_ctx *xlate_ctx,
+ const struct fde_entry *dw_fde)
+{
+ struct cfi_insn_data *cfi_insn;
+ int err = SFRAME_XLATE_OK;
+
+ xlate_ctx->dw_fde = dw_fde;
+
+ /* If the return column is not RIP, SFrame format cannot represent it. */
+ if (xlate_ctx->dw_fde->return_column != DWARF2_DEFAULT_RETURN_COLUMN)
+ return SFRAME_XLATE_ERR_NOTREPRESENTED;
+
+ /* Iterate over the CFIs and create SFrame FREs. */
+ for (cfi_insn = dw_fde->data; cfi_insn; cfi_insn = cfi_insn->next)
+ {
+ /* Translate each CFI, and buffer the state in translation context. */
+ err = sframe_do_cfi_insn (xlate_ctx, cfi_insn);
+ if (err != SFRAME_XLATE_OK)
+ {
+ /* Skip generating SFrame unwind info for the function if any
+ offending CFI is encountered by sframe_do_cfi_insn (). */
+ return err; /* Return the error code. */
+ }
+ }
+
+ /* No errors encountered. */
+
+ /* Link in the scratchpad FRE that the last few CFI insns helped create. */
+ if (xlate_ctx->cur_fre)
+ {
+ sframe_xlate_ctx_add_fre (xlate_ctx, xlate_ctx->cur_fre);
+ xlate_ctx->cur_fre = NULL;
+ }
+ /* Designate the end of the last SFrame FRE. */
+ if (xlate_ctx->last_fre)
+ {
+ xlate_ctx->last_fre->pc_end
+ = get_dw_fde_end_addrS (xlate_ctx->dw_fde);
+ }
+
+ return SFRAME_XLATE_OK;
+}
+
+/* Create SFrame unwind info for all functions.
+
+ This function consumes the already generated FDEs (by dw2gencfi) and
+ generates unwind data in SFrame format. */
+
+static void
+create_sframe_all (void)
+{
+ struct fde_entry *dw_fde = NULL;
+ struct sframe_func_entry *sframe_fde = NULL;
+
+ struct sframe_xlate_ctx *xlate_ctx = sframe_xlate_ctx_alloc ();
+
+ for (dw_fde = all_fde_data; dw_fde ; dw_fde = dw_fde->next)
+ {
+ sframe_fde = sframe_fde_alloc ();
+ /* Initialize the translation context with information anew. */
+ sframe_xlate_ctx_init (xlate_ctx);
+
+ /* Process and link SFrame FDEs if no error. Also skip adding an SFrame
+ FDE if it does not contain any SFrame FREs. There is little use of an
+ SFrame FDE if there is no unwind information about the function. */
+ int err = sframe_do_fde (xlate_ctx, dw_fde);
+ if (err || xlate_ctx->num_xlate_fres == 0)
+ {
+ sframe_xlate_ctx_cleanup (xlate_ctx);
+ sframe_fde_free (sframe_fde);
+ }
+ else
+ {
+ /* All done. Transfer the state from the SFrame translation
+ context to the SFrame FDE. */
+ sframe_xlate_ctx_finalize (xlate_ctx, sframe_fde);
+ sframe_fde_link (sframe_fde);
+ }
+ }
+}
+
+void
+output_sframe (segT sframe_seg)
+{
+ (void) sframe_seg;
+
+ /* Setup the version specific access functions. */
+ sframe_set_version (SFRAME_VERSION_1);
+
+ /* Process all fdes and create SFrame unwind information. */
+ create_sframe_all ();
+
+ output_sframe_internal ();
+}
+
+#else /* support_sframe_p */
+
+void
+output_sframe (segT sframe_seg __attribute__((unused)))
+{
+}
+
+#endif /* support_sframe_p */