aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gas/Makefile.am3
-rw-r--r--gas/Makefile.in14
-rw-r--r--gas/as.h10
-rw-r--r--gas/config/tc-aarch64.c58
-rw-r--r--gas/config/tc-aarch64.h29
-rw-r--r--gas/config/tc-i386.c50
-rw-r--r--gas/config/tc-i386.h26
-rw-r--r--gas/config/tc-xtensa.c1
-rw-r--r--gas/doc/as.texi14
-rw-r--r--gas/dw2gencfi.c30
-rw-r--r--gas/dw2gencfi.h1
-rw-r--r--gas/gen-sframe.c1294
-rw-r--r--gas/gen-sframe.h153
-rw-r--r--gas/sframe-opt.c158
-rw-r--r--gas/write.c13
15 files changed, 1844 insertions, 10 deletions
diff --git a/gas/Makefile.am b/gas/Makefile.am
index 5f0f24a..4661a718 100644
--- a/gas/Makefile.am
+++ b/gas/Makefile.am
@@ -80,6 +80,7 @@ GAS_CFILES = \
flonum-konst.c \
flonum-mult.c \
frags.c \
+ gen-sframe.c \
hash.c \
input-file.c \
input-scrub.c \
@@ -91,6 +92,7 @@ GAS_CFILES = \
read.c \
remap.c \
sb.c \
+ sframe-opt.c \
stabs.c \
subsegs.c \
symbols.c \
@@ -113,6 +115,7 @@ HFILES = \
expr.h \
flonum.h \
frags.h \
+ gen-sframe.h \
hash.h \
input-file.h \
itbl-lex.h \
diff --git a/gas/Makefile.in b/gas/Makefile.in
index 5a4dd70..1e01f34 100644
--- a/gas/Makefile.in
+++ b/gas/Makefile.in
@@ -166,10 +166,11 @@ am__objects_1 = app.$(OBJEXT) as.$(OBJEXT) atof-generic.$(OBJEXT) \
dwarf2dbg.$(OBJEXT) dw2gencfi.$(OBJEXT) ecoff.$(OBJEXT) \
ehopt.$(OBJEXT) expr.$(OBJEXT) flonum-copy.$(OBJEXT) \
flonum-konst.$(OBJEXT) flonum-mult.$(OBJEXT) frags.$(OBJEXT) \
- hash.$(OBJEXT) input-file.$(OBJEXT) input-scrub.$(OBJEXT) \
- listing.$(OBJEXT) literal.$(OBJEXT) macro.$(OBJEXT) \
- messages.$(OBJEXT) output-file.$(OBJEXT) read.$(OBJEXT) \
- remap.$(OBJEXT) sb.$(OBJEXT) stabs.$(OBJEXT) subsegs.$(OBJEXT) \
+ gen-sframe.$(OBJEXT) hash.$(OBJEXT) input-file.$(OBJEXT) \
+ input-scrub.$(OBJEXT) listing.$(OBJEXT) literal.$(OBJEXT) \
+ macro.$(OBJEXT) messages.$(OBJEXT) output-file.$(OBJEXT) \
+ read.$(OBJEXT) remap.$(OBJEXT) sb.$(OBJEXT) \
+ sframe-opt.$(OBJEXT) stabs.$(OBJEXT) subsegs.$(OBJEXT) \
symbols.$(OBJEXT) write.$(OBJEXT)
am_as_new_OBJECTS = $(am__objects_1)
am__dirstamp = $(am__leading_dot)dirstamp
@@ -566,6 +567,7 @@ GAS_CFILES = \
flonum-konst.c \
flonum-mult.c \
frags.c \
+ gen-sframe.c \
hash.c \
input-file.c \
input-scrub.c \
@@ -577,6 +579,7 @@ GAS_CFILES = \
read.c \
remap.c \
sb.c \
+ sframe-opt.c \
stabs.c \
subsegs.c \
symbols.c \
@@ -598,6 +601,7 @@ HFILES = \
expr.h \
flonum.h \
frags.h \
+ gen-sframe.h \
hash.h \
input-file.h \
itbl-lex.h \
@@ -1302,6 +1306,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flonum-konst.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flonum-mult.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/frags.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gen-sframe.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/input-file.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/input-scrub.Po@am__quote@
@@ -1316,6 +1321,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/read.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/remap.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sb.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sframe-opt.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stabs.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/subsegs.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/symbols.Po@am__quote@
diff --git a/gas/as.h b/gas/as.h
index 1b92407..23542e4 100644
--- a/gas/as.h
+++ b/gas/as.h
@@ -261,7 +261,10 @@ enum _relax_state
rs_cfa,
/* Cross-fragment dwarf2 line number optimization. */
- rs_dwarf2dbg
+ rs_dwarf2dbg,
+
+ /* SFrame FRE type selection optimization. */
+ rs_sframe
};
typedef enum _relax_state relax_stateT;
@@ -528,6 +531,11 @@ int eh_frame_relax_frag (fragS *);
void eh_frame_convert_frag (fragS *);
int generic_force_reloc (struct fix *);
+/* SFrame FRE optimization. */
+int sframe_estimate_size_before_relax (fragS *);
+int sframe_relax_frag (fragS *);
+void sframe_convert_frag (fragS *);
+
#include "expr.h" /* Before targ-*.h */
/* This one starts the chain of target dependent headers. */
diff --git a/gas/config/tc-aarch64.c b/gas/config/tc-aarch64.c
index 165d927..c679d93 100644
--- a/gas/config/tc-aarch64.c
+++ b/gas/config/tc-aarch64.c
@@ -30,6 +30,9 @@
#ifdef OBJ_ELF
#include "elf/aarch64.h"
+#include "dw2gencfi.h"
+#include "sframe.h"
+#include "gen-sframe.h"
#endif
#include "dw2gencfi.h"
@@ -72,6 +75,11 @@ enum aarch64_abi_type
AARCH64_ABI_LLP64 = 3
};
+unsigned int aarch64_sframe_cfa_sp_reg;
+/* The other CFA base register for SFrame unwind info. */
+unsigned int aarch64_sframe_cfa_fp_reg;
+unsigned int aarch64_sframe_cfa_ra_reg;
+
#ifndef DEFAULT_ARCH
#define DEFAULT_ARCH "aarch64"
#endif
@@ -8403,6 +8411,50 @@ aarch64_init_frag (fragS * fragP, int max_chars)
break;
}
}
+
+/* Whether SFrame unwind info is supported. */
+
+bool
+aarch64_support_sframe_p (void)
+{
+ /* At this time, SFrame is supported for aarch64 only. */
+ return (aarch64_abi == AARCH64_ABI_LP64);
+}
+
+/* Specify if RA tracking is needed. */
+
+bool
+aarch64_sframe_ra_tracking_p (void)
+{
+ return true;
+}
+
+/* Specify the fixed offset to recover RA from CFA.
+ (useful only when RA tracking is not needed). */
+
+offsetT
+aarch64_sframe_cfa_ra_offset (void)
+{
+ return (offsetT) SFRAME_CFA_FIXED_RA_INVALID;
+}
+
+/* Get the abi/arch indentifier for SFrame. */
+
+unsigned char
+aarch64_sframe_get_abi_arch (void)
+{
+ unsigned char sframe_abi_arch = 0;
+
+ if (aarch64_support_sframe_p ())
+ {
+ sframe_abi_arch = target_big_endian
+ ? SFRAME_ABI_AARCH64_ENDIAN_BIG
+ : SFRAME_ABI_AARCH64_ENDIAN_LITTLE;
+ }
+
+ return sframe_abi_arch;
+}
+
#endif /* OBJ_ELF */
/* Initialize the DWARF-2 unwind information for this procedure. */
@@ -9688,6 +9740,12 @@ md_begin (void)
mach = bfd_mach_aarch64;
bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach);
+#ifdef OBJ_ELF
+ /* FIXME - is there a better way to do it ? */
+ aarch64_sframe_cfa_sp_reg = 31;
+ aarch64_sframe_cfa_fp_reg = 29; /* x29. */
+ aarch64_sframe_cfa_ra_reg = 30;
+#endif
}
/* Command line processing. */
diff --git a/gas/config/tc-aarch64.h b/gas/config/tc-aarch64.h
index 2d514ff..91412ce 100644
--- a/gas/config/tc-aarch64.h
+++ b/gas/config/tc-aarch64.h
@@ -239,6 +239,35 @@ struct aarch64_segment_info_type
/* We want .cfi_* pseudo-ops for generating unwind info. */
#define TARGET_USE_CFIPOP 1
+/* Whether SFrame unwind info is supported. */
+extern bool aarch64_support_sframe_p (void);
+#define support_sframe_p aarch64_support_sframe_p
+
+/* The stack-pointer register number for SFrame unwind info. */
+extern unsigned int aarch64_sframe_cfa_sp_reg;
+#define SFRAME_CFA_SP_REG aarch64_sframe_cfa_sp_reg
+
+/* The base-pointer register number for CFA unwind info. */
+extern unsigned int aarch64_sframe_cfa_fp_reg;
+#define SFRAME_CFA_FP_REG aarch64_sframe_cfa_fp_reg
+
+/* The return address register number for CFA unwind info. */
+extern unsigned int aarch64_sframe_cfa_ra_reg;
+#define SFRAME_CFA_RA_REG aarch64_sframe_cfa_ra_reg
+
+/* Specify if RA tracking is needed. */
+extern bool aarch64_sframe_ra_tracking_p (void);
+#define sframe_ra_tracking_p aarch64_sframe_ra_tracking_p
+
+/* Specify the fixed offset to recover RA from CFA.
+ (useful only when RA tracking is not needed). */
+extern offsetT aarch64_sframe_cfa_ra_offset (void);
+#define sframe_cfa_ra_offset aarch64_sframe_cfa_ra_offset
+
+/* The abi/arch indentifier for SFrame. */
+unsigned char aarch64_sframe_get_abi_arch (void);
+#define sframe_get_abi_arch aarch64_sframe_get_abi_arch
+
/* CFI hooks. */
#define tc_regname_to_dw2regnum tc_aarch64_regname_to_dw2regnum
#define tc_cfi_frame_initial_instructions tc_aarch64_frame_initial_instructions
diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
index a5ea9b1..5bc3a56 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -30,6 +30,8 @@
#include "subsegs.h"
#include "dwarf2dbg.h"
#include "dw2gencfi.h"
+#include "gen-sframe.h"
+#include "sframe.h"
#include "elf/x86-64.h"
#include "opcodes/i386-init.h"
#include <limits.h>
@@ -591,6 +593,12 @@ static int use_big_obj = 0;
#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
/* 1 if generating code for a shared library. */
static int shared = 0;
+
+unsigned int x86_sframe_cfa_sp_reg;
+/* The other CFA base register for SFrame unwind info. */
+unsigned int x86_sframe_cfa_fp_reg;
+unsigned int x86_sframe_cfa_ra_reg;
+
#endif
/* 1 for intel syntax,
@@ -3099,6 +3107,10 @@ md_begin (void)
x86_dwarf2_return_column = 16;
#endif
x86_cie_data_alignment = -8;
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ x86_sframe_cfa_sp_reg = 7;
+ x86_sframe_cfa_fp_reg = 6;
+#endif
}
else
{
@@ -9131,6 +9143,44 @@ x86_cleanup (void)
if (seg && subseg)
subseg_set (seg, subseg);
}
+
+bool
+x86_support_sframe_p (void)
+{
+ /* At this time, SFrame unwind is supported for AMD64 ABI only. */
+ return (x86_elf_abi == X86_64_ABI);
+}
+
+bool
+x86_sframe_ra_tracking_p (void)
+{
+ /* In AMD64, return address is always stored on the stack at a fixed offset
+ from the CFA (provided via x86_sframe_cfa_ra_offset ()).
+ Do not track explicitly via an SFrame Frame Row Entry. */
+ return false;
+}
+
+offsetT
+x86_sframe_cfa_ra_offset (void)
+{
+ gas_assert (x86_elf_abi == X86_64_ABI);
+ return (offsetT) -8;
+}
+
+unsigned char
+x86_sframe_get_abi_arch (void)
+{
+ unsigned char sframe_abi_arch = 0;
+
+ if (x86_support_sframe_p ())
+ {
+ gas_assert (!target_big_endian);
+ sframe_abi_arch = SFRAME_ABI_AMD64_ENDIAN_LITTLE;
+ }
+
+ return sframe_abi_arch;
+}
+
#endif
static unsigned int
diff --git a/gas/config/tc-i386.h b/gas/config/tc-i386.h
index a6e096e..9003726 100644
--- a/gas/config/tc-i386.h
+++ b/gas/config/tc-i386.h
@@ -362,6 +362,32 @@ extern bfd_vma x86_64_section_letter (int, const char **);
#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
extern void x86_cleanup (void);
#define md_cleanup() x86_cleanup ()
+
+/* Whether SFrame unwind info is supported. */
+extern bool x86_support_sframe_p (void);
+#define support_sframe_p x86_support_sframe_p
+
+/* The stack-pointer register number for SFrame unwind info. */
+extern unsigned int x86_sframe_cfa_sp_reg;
+#define SFRAME_CFA_SP_REG x86_sframe_cfa_sp_reg
+
+/* The frame-pointer register number for CFA unwind info. */
+extern unsigned int x86_sframe_cfa_fp_reg;
+#define SFRAME_CFA_FP_REG x86_sframe_cfa_fp_reg
+
+/* Specify if RA tracking is needed. */
+extern bool x86_sframe_ra_tracking_p (void);
+#define sframe_ra_tracking_p x86_sframe_ra_tracking_p
+
+/* Specify the fixed offset to recover RA from CFA.
+ (useful only when RA tracking is not needed). */
+extern offsetT x86_sframe_cfa_ra_offset (void);
+#define sframe_cfa_ra_offset x86_sframe_cfa_ra_offset
+
+/* The abi/arch indentifier for SFrame. */
+extern unsigned char x86_sframe_get_abi_arch (void);
+#define sframe_get_abi_arch x86_sframe_get_abi_arch
+
#endif
#ifdef TE_PE
diff --git a/gas/config/tc-xtensa.c b/gas/config/tc-xtensa.c
index b7403ac..f7a28d3 100644
--- a/gas/config/tc-xtensa.c
+++ b/gas/config/tc-xtensa.c
@@ -8616,6 +8616,7 @@ unrelaxed_frag_max_size (fragS *fragP)
case rs_leb128:
case rs_cfa:
case rs_dwarf2dbg:
+ case rs_sframe:
/* No further adjustments needed. */
break;
case rs_machine_dependent:
diff --git a/gas/doc/as.texi b/gas/doc/as.texi
index 83f49c5..32300d7 100644
--- a/gas/doc/as.texi
+++ b/gas/doc/as.texi
@@ -4883,11 +4883,15 @@ Each expression is assembled into the next byte.
@subsection @code{.cfi_sections @var{section_list}}
@cindex @code{cfi_sections} directive
@code{.cfi_sections} may be used to specify whether CFI directives
-should emit @code{.eh_frame} section and/or @code{.debug_frame} section.
-If @var{section_list} is @code{.eh_frame}, @code{.eh_frame} is emitted,
-if @var{section_list} is @code{.debug_frame}, @code{.debug_frame} is emitted.
-To emit both use @code{.eh_frame, .debug_frame}. The default if this
-directive is not used is @code{.cfi_sections .eh_frame}.
+should emit @code{.eh_frame} section, @code{.debug_frame} section and/or
+@code{.sframe} section. If @var{section_list} contains @code{.eh_frame},
+@code{.eh_frame} is emitted, if @var{section_list} contains
+@code{.debug_frame}, @code{.debug_frame} is emitted, and finally, if
+@var{section_list} contains @code{.sframe}, @code{.sframe} is emitted.
+To emit multiple sections, specify them together in a list. For example, to
+emit both @code{.eh_frame} and @code{.debug_frame}, use
+@code{.eh_frame, .debug_frame}. The default if this directive is not used
+is @code{.cfi_sections .eh_frame}.
On targets that support compact unwinding tables these can be generated
by specifying @code{.eh_frame_entry} instead of @code{.eh_frame}.
diff --git a/gas/dw2gencfi.c b/gas/dw2gencfi.c
index 80b2628..d18cd26 100644
--- a/gas/dw2gencfi.c
+++ b/gas/dw2gencfi.c
@@ -23,6 +23,7 @@
#include "dw2gencfi.h"
#include "subsegs.h"
#include "dwarf2dbg.h"
+#include "gen-sframe.h"
#ifdef TARGET_USE_CFIPOP
@@ -102,6 +103,11 @@
#define tc_cfi_reloc_for_encoding(e) BFD_RELOC_NONE
#endif
+/* Targets which support SFrame format will define this and return true. */
+#ifndef support_sframe_p
+# define support_sframe_p() false
+#endif
+
/* Private segment collection list. */
struct dwcfi_seg_list
{
@@ -1235,6 +1241,8 @@ dot_cfi_sections (int ignored ATTRIBUTE_UNUSED)
else if (strcmp (name, tc_cfi_section_name) == 0)
sections |= CFI_EMIT_target;
#endif
+ else if (startswith (name, ".sframe"))
+ sections |= CFI_EMIT_sframe;
else
{
*input_line_pointer = c;
@@ -2495,6 +2503,28 @@ cfi_finish (void)
}
cfi_sections_set = true;
+ /* Generate SFrame section if the user specifies:
+ - the command line option to gas, or
+ - .sframe in the .cfi_sections directive. */
+ if (flag_gen_sframe || (all_cfi_sections & CFI_EMIT_sframe) != 0)
+ {
+ if (support_sframe_p ())
+ {
+ segT sframe_seg;
+ int alignment = ffs (DWARF2_ADDR_SIZE (stdoutput)) - 1;
+
+ if (!SUPPORT_FRAME_LINKONCE)
+ sframe_seg = get_cfi_seg (NULL, ".sframe",
+ (SEC_ALLOC | SEC_LOAD | SEC_DATA
+ | DWARF2_EH_FRAME_READ_ONLY),
+ alignment);
+ output_sframe (sframe_seg);
+ }
+ else
+ as_bad (_(".sframe not supported for target"));
+ }
+
+ cfi_sections_set = true;
if ((all_cfi_sections & CFI_EMIT_debug_frame) != 0)
{
int alignment = ffs (DWARF2_ADDR_SIZE (stdoutput)) - 1;
diff --git a/gas/dw2gencfi.h b/gas/dw2gencfi.h
index 0f5ae77..4ac70d0 100644
--- a/gas/dw2gencfi.h
+++ b/gas/dw2gencfi.h
@@ -205,5 +205,6 @@ extern struct fde_entry *all_fde_data;
#define CFI_EMIT_debug_frame (1 << 1)
#define CFI_EMIT_target (1 << 2)
#define CFI_EMIT_eh_frame_compact (1 << 3)
+#define CFI_EMIT_sframe (1 << 4)
#endif /* DW2GENCFI_H */
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 */
diff --git a/gas/gen-sframe.h b/gas/gen-sframe.h
new file mode 100644
index 0000000..93af499
--- /dev/null
+++ b/gas/gen-sframe.h
@@ -0,0 +1,153 @@
+/* gen-sframe.h - Support for generating SFrame.
+ 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. */
+
+#ifndef GENSFRAME_H
+#define GENSFRAME_H
+
+#define SFRAME_FRE_ELEM_LOC_REG 0
+#define SFRAME_FRE_ELEM_LOC_STACK 1
+
+/* SFrame Frame Row Entry (FRE).
+
+ A frame row entry is a slice of the frame and can be valid for a set of
+ program instructions. It keeps all information needed to retrieve the CFA
+ and the Return Address (RA) if tracked.
+
+ A frame row entry effectively stores accumulated information gathered by
+ interpreting multiple CFI instructions. More precisely, it is a
+ self-sufficient record in its own right. Only the subset of information
+ necessary for unwinding is stored: Given a PC, how to retrieve the CFA and
+ the RA.
+*/
+
+struct sframe_row_entry
+{
+ /* A linked list. */
+ struct sframe_row_entry *next;
+
+ /* Start and end of the frame row entry. */
+ symbolS *pc_begin;
+ symbolS *pc_end;
+
+ /* A frame row entry is a merge candidate if new information can be updated
+ on it. */
+ bool merge_candidate;
+
+ /* Track CFA base (architectural) register ID. */
+ unsigned int cfa_base_reg;
+ /* Offset from the CFA base register for recovering CFA. */
+ offsetT cfa_offset;
+
+ /* Track the other register used as base register for CFA. Specify whether
+ it is in register or memory. */
+ unsigned int base_reg;
+ unsigned int bp_loc;
+ /* If the other register is stashed on stack, note the offset. */
+ offsetT bp_offset;
+
+ /* Track RA location. Specify whether it is in register or memory. */
+ unsigned int ra_loc;
+ /* If RA is stashed on stack, note the offset. */
+ offsetT ra_offset;
+};
+
+/* SFrame Function Description Entry. */
+
+struct sframe_func_entry
+{
+ /* A linked list. */
+ struct sframe_func_entry *next;
+
+ /* Reference to the FDE created from CFI in dw2gencfi. Some information
+ like the start_address and the segment is made available via this
+ member. */
+ const struct fde_entry *dw_fde;
+
+ /* Reference to the first FRE for this function. */
+ struct sframe_row_entry *sframe_fres;
+
+ unsigned int num_fres;
+};
+
+/* SFrame Function Description Entry Translation Context. */
+
+struct sframe_xlate_ctx
+{
+ /* Reference to the FDE created from CFI in dw2gencfi. Information
+ like the FDE start_address, end_address and the cfi insns are
+ made available via this member. */
+ const struct fde_entry *dw_fde;
+
+ /* List of FREs in the current FDE translation context, bounded by first_fre
+ and last_fre. */
+
+ /* Keep track of the first FRE for the purpose of restoring state if
+ necessary (for DW_CFA_restore). */
+ struct sframe_row_entry *first_fre;
+ /* The last FRE in the list. */
+ struct sframe_row_entry *last_fre;
+
+ /* The current FRE under construction. */
+ struct sframe_row_entry *cur_fre;
+ /* Remember FRE for an eventual restore. */
+ struct sframe_row_entry *remember_fre;
+
+ unsigned num_xlate_fres;
+};
+
+/* Error codes for SFrame translation context. */
+enum sframe_xlate_err
+{
+ /* Success. */
+ SFRAME_XLATE_OK = 0,
+ /* Error. */
+ SFRAME_XLATE_ERROR = 1,
+ /* Detailed error codes. */
+ SFRAME_XLATE_ERR_INVAL = -1,
+ SFRAME_XLATE_ERR_NOTREPRESENTED = -2,
+};
+
+/* Callback to create the abi/arch identifier for SFrame section. */
+
+unsigned char
+sframe_get_abi_arch_callback (const char *target_arch,
+ int big_endian_p);
+
+/* The list of all FDEs with data in SFrame internal representation. */
+
+extern struct sframe_func_entry *all_sframe_fdes;
+
+/* SFrame version specific operations structure. */
+
+struct sframe_version_ops
+{
+ unsigned char format_version; /* SFrame format version. */
+ /* set SFrame FRE info. */
+ unsigned char (*set_fre_info) (unsigned int, unsigned int, unsigned int);
+ /* set SFrame Func info. */
+ unsigned char (*set_func_info) (unsigned int, unsigned int);
+};
+
+/* Generate SFrame unwind info and prepare contents for the output.
+ outout_sframe () is called at the end of file. */
+
+extern void output_sframe (segT sframe_seg);
+
+#endif /* GENSFRAME_H */
diff --git a/gas/sframe-opt.c b/gas/sframe-opt.c
new file mode 100644
index 0000000..c17fd6b
--- /dev/null
+++ b/gas/sframe-opt.c
@@ -0,0 +1,158 @@
+/* sframe-opt.c - optimize FRE and FDE information in SFrame.
+ 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 "sframe.h"
+
+/* The function estimates the size of a rs_sframe variant frag based on
+ the current values of the symbols. It is called before the
+ relaxation loop. We set fr_subtype{0:2} to the expected length. */
+
+int
+sframe_estimate_size_before_relax (fragS *frag)
+{
+ offsetT width;
+ expressionS *exp;
+ symbolS *widthS;
+ int ret;
+
+ /* We are dealing with two different kind of fragments here which need
+ to be fixed up:
+ - first, FRE start address in each FRE, and
+ - second, Function info in each FDE (function info stores the FRE type)
+ The two kind of fragments can be differentiated based on the opcode
+ of the symbol. */
+ exp = symbol_get_value_expression (frag->fr_symbol);
+ gas_assert ((exp->X_op == O_subtract) || (exp->X_op == O_absent));
+ /* Fragment for function info in an SFrame FDE will always write
+ only one byte. */
+ if (exp->X_op == O_subtract)
+ ret = 1;
+ /* Fragment for the start address in an SFrame FRE may write out
+ 1/2/4 bytes depending on the value of the diff. */
+ else
+ {
+ /* Get the width expression from the symbol. */
+ widthS = exp->X_op_symbol;
+ width = resolve_symbol_value (widthS);
+
+ if (width < 0x100)
+ ret = 1;
+ else if (width < 0x10000)
+ ret = 2;
+ else
+ ret = 4;
+ }
+
+ frag->fr_subtype = (frag->fr_subtype & ~7) | (ret & 7);
+
+ return ret;
+}
+
+/* This function relaxes a rs_sframe variant frag based on the current
+ values of the symbols. fr_subtype{0:2} is the current length of
+ the frag. This returns the change in frag length. */
+
+int
+sframe_relax_frag (fragS *frag)
+{
+ int oldsize, newsize;
+
+ oldsize = frag->fr_subtype & 7;
+ if (oldsize == 7)
+ oldsize = -1;
+ newsize = sframe_estimate_size_before_relax (frag);
+ return newsize - oldsize;
+}
+
+/* This function converts a rs_sframe variant frag into a normal fill
+ frag. This is called after all relaxation has been done.
+ fr_subtype{0:2} will be the desired length of the frag. */
+
+void
+sframe_convert_frag (fragS *frag)
+{
+ offsetT fsize;
+ offsetT diff;
+ offsetT value;
+ unsigned char func_info = SFRAME_FRE_TYPE_ADDR4;
+ expressionS *exp;
+ symbolS *fsizeS, *diffS;
+
+ /* We are dealing with two different kind of fragments here which need
+ to be fixed up:
+ - first, FRE start address in each FRE, and
+ - second, Function info in each FDE (function info stores the FRE type)
+ The two kind of fragments can be differentiated based on the opcode
+ of the symbol. */
+ exp = symbol_get_value_expression (frag->fr_symbol);
+ gas_assert ((exp->X_op == O_subtract) || (exp->X_op == O_absent));
+ /* Fragment for function info in an SFrame FDE. */
+ if (exp->X_op == O_subtract)
+ {
+ fsizeS = frag->fr_symbol;
+ fsize = resolve_symbol_value (fsizeS);
+ if (fsize < 0x100)
+ func_info = SFRAME_FRE_TYPE_ADDR1;
+ else if (fsize < 0x10000)
+ func_info = SFRAME_FRE_TYPE_ADDR2;
+ else
+ func_info = SFRAME_FRE_TYPE_ADDR4;
+ value = func_info;
+
+ frag->fr_literal[frag->fr_fix] = value;
+ }
+ /* Fragment for the start address in an SFrame FRE. */
+ else
+ {
+ /* Get the fsize expression from the symbol. */
+ fsizeS = exp->X_op_symbol;
+ fsize = resolve_symbol_value (fsizeS);
+ /* Get the diff expression from the symbol. */
+ diffS= exp->X_add_symbol;
+ diff = resolve_symbol_value (diffS);
+ value = diff;
+
+ switch (frag->fr_subtype & 7)
+ {
+ case 1:
+ gas_assert (fsize < 0x100);
+ frag->fr_literal[frag->fr_fix] = diff;
+ break;
+ case 2:
+ gas_assert (fsize < 0x10000);
+ md_number_to_chars (frag->fr_literal + frag->fr_fix, diff, 2);
+ break;
+ case 4:
+ md_number_to_chars (frag->fr_literal + frag->fr_fix, diff, 4);
+ break;
+ default:
+ abort ();
+ }
+ }
+
+ frag->fr_fix += frag->fr_subtype & 7;
+ frag->fr_type = rs_fill;
+ frag->fr_subtype = 0;
+ frag->fr_offset = 0;
+ /* FIXME do this now because we have evaluated and fixed up the fragments
+ manually ? */
+ frag->fr_symbol = 0;
+}
diff --git a/gas/write.c b/gas/write.c
index 3014f68..fa748eb 100644
--- a/gas/write.c
+++ b/gas/write.c
@@ -487,6 +487,10 @@ cvt_frag_to_fill (segT sec ATTRIBUTE_UNUSED, fragS *fragP)
dwarf2dbg_convert_frag (fragP);
break;
+ case rs_sframe:
+ sframe_convert_frag (fragP);
+ break;
+
case rs_machine_dependent:
md_convert_frag (stdoutput, sec, fragP);
@@ -2781,6 +2785,11 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
address += dwarf2dbg_estimate_size_before_relax (fragP);
break;
+ case rs_sframe:
+ /* Initial estimate can be set to atleast 1 byte. */
+ address += sframe_estimate_size_before_relax (fragP);
+ break;
+
default:
BAD_CASE (fragP->fr_type);
break;
@@ -3124,6 +3133,10 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
growth = dwarf2dbg_relax_frag (fragP);
break;
+ case rs_sframe:
+ growth = sframe_relax_frag (fragP);
+ break;
+
default:
BAD_CASE (fragP->fr_type);
break;