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.
This commit is contained in:
Indu Bhagat 2022-11-15 15:06:46 -08:00
parent b07a297816
commit b52c4ee466
15 changed files with 1844 additions and 10 deletions

View File

@ -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 \

View File

@ -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@

View File

@ -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. */

View File

@ -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. */

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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}.

View File

@ -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;
@ -2494,6 +2502,28 @@ cfi_finish (void)
flag_traditional_format = save_flag_traditional_format;
}
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)
{

View File

@ -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 */

1294
gas/gen-sframe.c Normal file

File diff suppressed because it is too large Load Diff

153
gas/gen-sframe.h Normal file
View File

@ -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 */

158
gas/sframe-opt.c Normal file
View File

@ -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;
}

View File

@ -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;