[PATCH,V6 03/10] gas: generate .ctf_frame

Indu Bhagat indu.bhagat@oracle.com
Tue Aug 2 08:04:45 GMT 2022


[No changes from V5]

[Changes from V4]
 - bugfix: Disabling CTF_FRE_TYPE_SELECTION_OPT causes gas to crash
 - Tested various cfi_* directives in isolation and in combination with
   each other to ensure graceful handling in all scenarios.  Added
   further testcases in gas testsuite.
[End of changes from V4]

[Changes from V3]
 - Do not err out if .ctf_frame is not supported for a target, just warn
   instead.
 - Other changes around API ctf_frame_fde_func_info () as it now
   includes an fde_type along with the earlier lone argument of
   fre_type.
[End of changes from V3]

[Changes from V2]
 - consistent use of terminology.
 - updated documentation in code comments around FRE start address
   optimization (fragment fixup).  Also renamed the type rs_ctf_fre to
   rs_ctf_frame.
 - added gas testsuite.
[End of changes from V2]

[Changes from V1]
 - generate .ctf_frame if --gctf-frame is specified OR if .cfi_sections
   .ctf_frame is specified.
 - bugfix: use startswith in .cfi_sections. Earlier, strcmp was being
   used incorrectly.
 - generate .ctf_frame section even if there are no FDEs (no functions
   with unwind information).  A .ctf_frame section with no FDEs is valid.
   Such a CTF Frame section will have a valid header with other fields
   set to appropriate values.
 - Support conditional compilation on the basis of support_ctf_frame_p.
   Compile in the CTF Frame generation APIs only when the tagets support
   CTF Frame.  Currently, gas emits CTF Frame unwind information for x86_64
    and aarch64 only.
 - doc: update documentation for .cfi_sections to include .ctf_frame
   section in the list.
[End of changes from V1]

[PS: Currently, the compiler has not been adapted to generate
".cfi_sections" with ".ctf_frame" in it.  The newly added command line
option of --gctf-frame provides an easy way to try out .ctf_frame 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-ctf-frame.[ch] sub-system to, in turn, create the CTF Frame unwind
information.  These internal DWARF structures are read-only for the
purpose of CTF Frame unwind info generation.

CTF Frame unwind info generation does not impact .eh_frame unwind info
generation.  Both .eh_frame and .ctf_frame can co-exist in an ELF file,
if so desired by the user.

Recall that CTF Frame 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 .ctf_frame section.

Consequently, gen-ctf-frame.[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 CTF Frame 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 CTF Frame unwind information generated for them.
Holes in instructions covered by FREs are not representable in the CTF
unwind format.

As few examples, following opcodes are not processed for .ctf_frame
generation, and are skipped:
- .cfi_personality*
- .cfi_*lsda
- .cfi_escape
- .cfi_negate_ra_state
- ...

Not processing .cfi_escape, .cfi_negate_ra_state will cause CTF Frame
unwind information to be absent for CTF 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 .ctf_frame generation to provide gas with architecture specific
information like SP/FP/RA register numbers and a CTF Frame specific ABI
marker.

Lastly, the patch also implements an optimization for size, where
specific fragments containing CTF FRE start address and CTF FDE function
info 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
CTF_FRE_START_ADDR_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-ctf-frame.c and ctf-frame-opt.c.
	* gas/Makefile.in: Regenerated.
	* gas/as.h (enum _relax_state): Add new state rs_ctf_fre.
	(ctf_frame_estimate_size_before_relax): New function.
	(ctf_frame_relax_frag): Likewise.
	(ctf_frame_convert_frag): Likewise.
	* gas/config/tc-aarch64.c (enum aarch64_abi_type): New
	declaration.
	(aarch64_support_ctf_frame_p): Likewise.
	(aarch64_ctf_frame_ra_tracking_p): Likewise.
	(aarch64_ctf_frame_cfa_ra_offset): Likewise.
	(aarch64_ctf_frame_get_abi_arch): Likewise.
	(md_begin): Set values of sp/fp/ra registers.
	* gas/config/tc-aarch64.h (aarch64_support_ctf_frame_p): New
	declaration.
	(support_ctf_frame_p): Likewise.
	(CTF_FRAME_CFA_SP_REG): Likewise.
	(CTF_FRAME_CFA_FP_REG): Likewise.
	(CTF_FRAME_CFA_RA_REG): Likewise.
	(aarch64_ctf_frame_ra_tracking_p): Likewise.
	(ctf_frame_ra_tracking_p): Likewise.
	(aarch64_ctf_frame_cfa_ra_offset): Likewise.
	(ctf_frame_cfa_ra_offset): Likewise.
	(aarch64_ctf_frame_get_abi_arch): Likewise.
	(ctf_frame_get_abi_arch): Likewise.
	* gas/config/tc-i386.c (md_begin): Set values of sp/fp/ra.
	(x86_support_ctf_frame_p): New definition.
	(x86_ctf_frame_ra_tracking_p): Likewise.
	(x86_ctf_frame_cfa_ra_offset): Likewise.
	(x86_ctf_frame_get_abi_arch): Likewise.
	* gas/config/tc-i386.h (x86_support_ctf_frame_p): New
	declaration.
	(support_ctf_frame_p): Likewise.
	(CTF_FRAME_CFA_SP_REG): Likewise.
	(CTF_FRAME_CFA_FP_REG): Likewise.
	(x86_ctf_frame_ra_tracking_p): Likewise.
	(ctf_frame_ra_tracking_p): Likewise.
	(x86_ctf_frame_cfa_ra_offset): Likewise.
	(ctf_frame_cfa_ra_offset): Likewise.
	(x86_ctf_frame_get_abi_arch): Likewise.
	(ctf_frame_get_abi_arch): Likewise.
	* gas/config/tc-xtensa.c (unrelaxed_frag_max_size): Add case for
	rs_ctf_fre.
	* gas/dw2gencfi.c (cfi_finish): Create a .ctf_frame section.
	* gas/dw2gencfi.h (CFI_EMIT_ctf_frame): New definition.
	* gas/write.c (cvt_frag_to_fill): Handle rs_ctf_fre.
	(relax_segment): Handle rs_ctf_fre.
	* gas/ctf-frame-opt.c: New file.
	* gas/gen-ctf-frame.c: New file.
	* gas/gen-ctf-frame.h: New file.
	* gas/testsuite/gas/cfi-ctf/cfi-ctf-common-1.d: Likewise.
	* gas/testsuite/gas/cfi-ctf/cfi-ctf-common-1.s: Likewise.
	* gas/testsuite/gas/cfi-ctf/cfi-ctf-common-2.d: Likewise.
	* gas/testsuite/gas/cfi-ctf/cfi-ctf-common-2.s: Likewise.
	* gas/testsuite/gas/cfi-ctf/cfi-ctf-common-3.d: Likewise.
	* gas/testsuite/gas/cfi-ctf/cfi-ctf-common-3.s: Likewise.
	* gas/testsuite/gas/cfi-ctf/cfi-ctf-common-4.d: Likewise.
	* gas/testsuite/gas/cfi-ctf/cfi-ctf-common-4.s: Likewise.
	* gas/testsuite/gas/cfi-ctf/cfi-ctf-common-5.d: Likewise..
	* gas/testsuite/gas/cfi-ctf/cfi-ctf-common-5.s: Likewise.
	* gas/testsuite/gas/cfi-ctf/cfi-ctf-common-6.d: Likewise.
	* gas/testsuite/gas/cfi-ctf/cfi-ctf-common-6.s: Likewise.
	* gas/testsuite/gas/cfi-ctf/cfi-ctf-common-7.d: Likewise.
	* gas/testsuite/gas/cfi-ctf/cfi-ctf-common-7.s: Likewise.
	* gas/testsuite/gas/cfi-ctf/cfi-ctf-common-8.d: Likewise.
	* gas/testsuite/gas/cfi-ctf/cfi-ctf-common-8.s: Likewise.
	* gas/testsuite/gas/cfi-ctf/cfi-ctf-x86_64-1.d: Likewise.
	* gas/testsuite/gas/cfi-ctf/cfi-ctf-x86_64-1.s: Likewise.
	* gas/testsuite/gas/cfi-ctf/cfi-ctf-aarch64-1.d: Likewise.
	* gas/testsuite/gas/cfi-ctf/cfi-ctf-aarch64-1.s: Likewise.
	* gas/testsuite/gas/cfi-ctf/cfi-ctf.exp: Testsuite for CTF Frame.
---
 gas/Makefile.am                               |    3 +
 gas/Makefile.in                               |   23 +-
 gas/as.h                                      |   10 +-
 gas/config/tc-aarch64.c                       |   42 +
 gas/config/tc-aarch64.h                       |   29 +
 gas/config/tc-i386.c                          |   46 +
 gas/config/tc-i386.h                          |   26 +
 gas/config/tc-xtensa.c                        |    1 +
 gas/ctf-frame-opt.c                           |  158 ++
 gas/doc/as.texi                               |   14 +-
 gas/dw2gencfi.c                               |   30 +
 gas/dw2gencfi.h                               |    1 +
 gas/gen-ctf-frame.c                           | 1297 +++++++++++++++++
 gas/gen-ctf-frame.h                           |  142 ++
 gas/testsuite/gas/cfi-ctf/cfi-ctf-aarch64-1.d |   20 +
 gas/testsuite/gas/cfi-ctf/cfi-ctf-aarch64-1.s |   61 +
 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-1.d  |   17 +
 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-1.s  |    3 +
 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-2.d  |   17 +
 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-2.s  |    2 +
 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-3.d  |   17 +
 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-3.s  |    4 +
 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-4.d  |   21 +
 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-4.s  |    8 +
 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-5.d  |   21 +
 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-5.s  |    7 +
 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-6.d  |   21 +
 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-6.s  |    7 +
 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-7.d  |   21 +
 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-7.s  |    7 +
 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-8.d  |   20 +
 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-8.s  |   12 +
 gas/testsuite/gas/cfi-ctf/cfi-ctf-x86_64-1.d  |   22 +
 gas/testsuite/gas/cfi-ctf/cfi-ctf-x86_64-1.s  |   30 +
 gas/testsuite/gas/cfi-ctf/cfi-ctf.exp         |   58 +
 gas/testsuite/gas/cfi-ctf/common-empty-1.d    |   14 +
 gas/testsuite/gas/cfi-ctf/common-empty-1.s    |    5 +
 gas/testsuite/gas/cfi-ctf/common-empty-2.d    |   14 +
 gas/testsuite/gas/cfi-ctf/common-empty-2.s    |    8 +
 gas/testsuite/gas/cfi-ctf/common-empty-3.d    |   14 +
 gas/testsuite/gas/cfi-ctf/common-empty-3.s    |    9 +
 gas/testsuite/gas/cfi-ctf/common-empty-4.d    |   14 +
 gas/testsuite/gas/cfi-ctf/common-empty-4.s    |   18 +
 gas/write.c                                   |   13 +
 44 files changed, 2313 insertions(+), 14 deletions(-)
 create mode 100644 gas/ctf-frame-opt.c
 create mode 100644 gas/gen-ctf-frame.c
 create mode 100644 gas/gen-ctf-frame.h
 create mode 100644 gas/testsuite/gas/cfi-ctf/cfi-ctf-aarch64-1.d
 create mode 100644 gas/testsuite/gas/cfi-ctf/cfi-ctf-aarch64-1.s
 create mode 100644 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-1.d
 create mode 100644 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-1.s
 create mode 100644 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-2.d
 create mode 100644 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-2.s
 create mode 100644 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-3.d
 create mode 100644 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-3.s
 create mode 100644 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-4.d
 create mode 100644 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-4.s
 create mode 100644 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-5.d
 create mode 100644 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-5.s
 create mode 100644 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-6.d
 create mode 100644 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-6.s
 create mode 100644 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-7.d
 create mode 100644 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-7.s
 create mode 100644 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-8.d
 create mode 100644 gas/testsuite/gas/cfi-ctf/cfi-ctf-common-8.s
 create mode 100644 gas/testsuite/gas/cfi-ctf/cfi-ctf-x86_64-1.d
 create mode 100644 gas/testsuite/gas/cfi-ctf/cfi-ctf-x86_64-1.s
 create mode 100644 gas/testsuite/gas/cfi-ctf/cfi-ctf.exp
 create mode 100644 gas/testsuite/gas/cfi-ctf/common-empty-1.d
 create mode 100644 gas/testsuite/gas/cfi-ctf/common-empty-1.s
 create mode 100644 gas/testsuite/gas/cfi-ctf/common-empty-2.d
 create mode 100644 gas/testsuite/gas/cfi-ctf/common-empty-2.s
 create mode 100644 gas/testsuite/gas/cfi-ctf/common-empty-3.d
 create mode 100644 gas/testsuite/gas/cfi-ctf/common-empty-3.s
 create mode 100644 gas/testsuite/gas/cfi-ctf/common-empty-4.d
 create mode 100644 gas/testsuite/gas/cfi-ctf/common-empty-4.s

diff --git a/gas/Makefile.am b/gas/Makefile.am
index bd597398671..58f28930c64 100644
--- a/gas/Makefile.am
+++ b/gas/Makefile.am
@@ -70,6 +70,8 @@ GAS_CFILES = \
 	atof-generic.c \
 	compress-debug.c \
 	cond.c \
+	ctf-frame-opt.c \
+	gen-ctf-frame.c \
 	depend.c \
 	dwarf2dbg.c \
 	dw2gencfi.c \
@@ -105,6 +107,7 @@ HFILES = \
 	bit_fix.h \
 	cgen.h \
 	compress-debug.h \
+	gen-ctf-frame.h \
 	dwarf2dbg.h \
 	dw2gencfi.h \
 	ecoff.h \
diff --git a/gas/Makefile.in b/gas/Makefile.in
index c57d78f82c4..7ae8859b0a6 100644
--- a/gas/Makefile.in
+++ b/gas/Makefile.in
@@ -160,14 +160,16 @@ CONFIG_CLEAN_FILES = gdb.ini .gdbinit po/Makefile.in
 CONFIG_CLEAN_VPATH_FILES =
 PROGRAMS = $(noinst_PROGRAMS)
 am__objects_1 = app.$(OBJEXT) as.$(OBJEXT) atof-generic.$(OBJEXT) \
-	compress-debug.$(OBJEXT) cond.$(OBJEXT) depend.$(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) \
+	compress-debug.$(OBJEXT) cond.$(OBJEXT) \
+	ctf-frame-opt.$(OBJEXT) gen-ctf-frame.$(OBJEXT) \
+	depend.$(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) \
 	symbols.$(OBJEXT) write.$(OBJEXT)
 am_as_new_OBJECTS = $(am__objects_1)
 am__dirstamp = $(am__leading_dot)dirstamp
@@ -549,6 +551,8 @@ GAS_CFILES = \
 	atof-generic.c \
 	compress-debug.c \
 	cond.c \
+	ctf-frame-opt.c \
+	gen-ctf-frame.c \
 	depend.c \
 	dwarf2dbg.c \
 	dw2gencfi.c \
@@ -583,6 +587,7 @@ HFILES = \
 	bit_fix.h \
 	cgen.h \
 	compress-debug.h \
+	gen-ctf-frame.h \
 	dwarf2dbg.h \
 	dw2gencfi.h \
 	ecoff.h \
@@ -1285,6 +1290,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cgen.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compress-debug.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cond.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-frame-opt.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/depend.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dw2gencfi.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwarf2dbg.Po@am__quote@
@@ -1295,6 +1301,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-ctf-frame.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@
diff --git a/gas/as.h b/gas/as.h
index 92e039b163a..f0bb3bd66bf 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,
+
+  /* CTF Frame FRE type selection optimization.  */
+  rs_ctf_frame
 };
 
 typedef enum _relax_state relax_stateT;
@@ -527,6 +530,11 @@ int eh_frame_relax_frag (fragS *);
 void eh_frame_convert_frag (fragS *);
 int generic_force_reloc (struct fix *);
 
+/* CTF Frame FRE optimization.  */
+int ctf_frame_estimate_size_before_relax (fragS *);
+int ctf_frame_relax_frag (fragS *);
+void ctf_frame_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 f023e5b0a28..8c78ed3a173 100644
--- a/gas/config/tc-aarch64.c
+++ b/gas/config/tc-aarch64.c
@@ -31,6 +31,7 @@
 #ifdef OBJ_ELF
 #include "elf/aarch64.h"
 #include "dw2gencfi.h"
+#include "gen-ctf-frame.h"
 #endif
 
 #include "dwarf2dbg.h"
@@ -70,6 +71,11 @@ enum aarch64_abi_type
   AARCH64_ABI_ILP32 = 2
 };
 
+unsigned int aarch64_ctf_frame_cfa_sp_reg;
+/* The other CFA base register for CTF Frame unwind info.  */
+unsigned int aarch64_ctf_frame_cfa_fp_reg;
+unsigned int aarch64_ctf_frame_cfa_ra_reg;
+
 #ifndef DEFAULT_ARCH
 #define DEFAULT_ARCH "aarch64"
 #endif
@@ -8406,6 +8412,36 @@ tc_aarch64_frame_initial_instructions (void)
 {
   cfi_add_CFA_def_cfa (REG_SP, 0);
 }
+
+bool
+aarch64_support_ctf_frame_p (void)
+{
+  if (aarch64_abi == AARCH64_ABI_LP64)
+    return 1;
+  return 0;
+}
+
+bool
+aarch64_ctf_frame_ra_tracking_p (void)
+{
+  return 1;
+}
+
+offsetT
+aarch64_ctf_frame_cfa_ra_offset (void)
+{
+  return (offsetT)0;
+}
+
+unsigned char
+aarch64_ctf_frame_get_abi_arch (void)
+{
+  if (aarch64_support_ctf_frame_p ())
+    return ctf_frame_get_abi_arch_callback ("aarch64", target_big_endian);
+  else
+    return 0;
+}
+
 #endif /* OBJ_ELF */
 
 /* Convert REGNAME to a DWARF-2 register number.  */
@@ -9669,6 +9705,12 @@ md_begin (void)
   mach = ilp32_p ? bfd_mach_aarch64_ilp32 : bfd_mach_aarch64;
 
   bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach);
+#ifdef OBJ_ELF
+  // FIXME - is there a better way to do it ? 
+  aarch64_ctf_frame_cfa_sp_reg = 31;
+  aarch64_ctf_frame_cfa_fp_reg = 29; /* x29.  */
+  aarch64_ctf_frame_cfa_ra_reg = 30;
+#endif
 }
 
 /* Command line processing.  */
diff --git a/gas/config/tc-aarch64.h b/gas/config/tc-aarch64.h
index f5c17523796..f1274a2d3aa 100644
--- a/gas/config/tc-aarch64.h
+++ b/gas/config/tc-aarch64.h
@@ -235,6 +235,35 @@ struct aarch64_segment_info_type
 /* We want .cfi_* pseudo-ops for generating unwind info.  */
 #define TARGET_USE_CFIPOP              1
 
+/* Whether CTF Frame unwind info is supported.  */
+extern bool aarch64_support_ctf_frame_p (void);
+#define support_ctf_frame_p aarch64_support_ctf_frame_p
+
+/* The stack-pointer register number for CTF Frame unwind info.  */
+extern unsigned int aarch64_ctf_frame_cfa_sp_reg;
+#define CTF_FRAME_CFA_SP_REG aarch64_ctf_frame_cfa_sp_reg
+
+/* The base-pointer register number for CFA unwind info.  */
+extern unsigned int aarch64_ctf_frame_cfa_fp_reg;
+#define CTF_FRAME_CFA_FP_REG aarch64_ctf_frame_cfa_fp_reg
+
+/* The return address register number for CFA unwind info.  */
+extern unsigned int aarch64_ctf_frame_cfa_ra_reg;
+#define CTF_FRAME_CFA_RA_REG aarch64_ctf_frame_cfa_ra_reg
+
+/* Specify if RA tracking is needed.  */
+extern bool aarch64_ctf_frame_ra_tracking_p (void);
+#define ctf_frame_ra_tracking_p aarch64_ctf_frame_ra_tracking_p
+
+/* Specify the fixed offset to recover RA from CFA.
+   (useful only when RA tracking is not needed).  */
+extern offsetT aarch64_ctf_frame_cfa_ra_offset (void);
+#define ctf_frame_cfa_ra_offset aarch64_ctf_frame_cfa_ra_offset
+
+/* The abi/arch indentifier for CTF Frame.  */
+unsigned char aarch64_ctf_frame_get_abi_arch (void);
+#define ctf_frame_get_abi_arch aarch64_ctf_frame_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 d40a71a492a..7181dfddb83 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -30,6 +30,7 @@
 #include "subsegs.h"
 #include "dwarf2dbg.h"
 #include "dw2gencfi.h"
+#include "gen-ctf-frame.h"
 #include "elf/x86-64.h"
 #include "opcodes/i386-init.h"
 #include <limits.h>
@@ -586,6 +587,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_ctf_frame_cfa_sp_reg;
+/* The other CFA base register for CTF Frame unwind info.  */
+unsigned int x86_ctf_frame_cfa_fp_reg;
+unsigned int x86_ctf_frame_cfa_ra_reg;
+
 #endif
 
 /* 1 for intel syntax,
@@ -3080,6 +3087,10 @@ md_begin (void)
       x86_dwarf2_return_column = 16;
 #endif
       x86_cie_data_alignment = -8;
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+      x86_ctf_frame_cfa_sp_reg = 7;
+      x86_ctf_frame_cfa_fp_reg = 6;
+#endif
     }
   else
     {
@@ -9147,6 +9158,41 @@ x86_cleanup (void)
   if (seg && subseg)
     subseg_set (seg, subseg);
 }
+
+bool
+x86_support_ctf_frame_p (void)
+{
+  /* At this time, CTF Frame unwind is supported for AMD64 ABI only.  */
+  if (x86_elf_abi == X86_64_ABI)
+    return true;
+  return false;
+}
+
+bool
+x86_ctf_frame_ra_tracking_p (void)
+{
+  /* In AMD64, return address is always stored on the stack at a fixed offset
+     from the CFA (provided via x86_ctf_frame_cfa_ra_offset ()).
+     Do not track explicitly via a CTF Frame Row Entry.  */
+  return false;
+}
+
+offsetT
+x86_ctf_frame_cfa_ra_offset (void)
+{
+  gas_assert (x86_elf_abi == X86_64_ABI);
+  return (offsetT)-8;
+}
+
+unsigned char
+x86_ctf_frame_get_abi_arch (void)
+{
+  if (x86_support_ctf_frame_p ())
+    return ctf_frame_get_abi_arch_callback ("x86-64", target_big_endian);
+  else
+    return 0;
+}
+
 #endif
 
 static unsigned int
diff --git a/gas/config/tc-i386.h b/gas/config/tc-i386.h
index a6e096ee110..1607ef6cfd2 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 CTF Frame unwind info is supported.  */
+extern bool x86_support_ctf_frame_p (void);
+#define support_ctf_frame_p x86_support_ctf_frame_p
+
+/* The stack-pointer register number for CTF Frame unwind info.  */
+extern unsigned int x86_ctf_frame_cfa_sp_reg;
+#define CTF_FRAME_CFA_SP_REG x86_ctf_frame_cfa_sp_reg
+
+/* The frame-pointer register number for CFA unwind info.  */
+extern unsigned int x86_ctf_frame_cfa_fp_reg;
+#define CTF_FRAME_CFA_FP_REG x86_ctf_frame_cfa_fp_reg
+
+/* Specify if RA tracking is needed.  */
+extern bool x86_ctf_frame_ra_tracking_p (void);
+#define ctf_frame_ra_tracking_p x86_ctf_frame_ra_tracking_p
+
+/* Specify the fixed offset to recover RA from CFA.
+   (useful only when RA tracking is not needed).  */
+extern offsetT x86_ctf_frame_cfa_ra_offset (void);
+#define ctf_frame_cfa_ra_offset x86_ctf_frame_cfa_ra_offset
+
+/* The abi/arch indentifier for CTF Frame.  */
+extern unsigned char x86_ctf_frame_get_abi_arch (void);
+#define ctf_frame_get_abi_arch x86_ctf_frame_get_abi_arch
+
 #endif
 
 #ifdef TE_PE
diff --git a/gas/config/tc-xtensa.c b/gas/config/tc-xtensa.c
index b7403ac45d4..9b909cfffba 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_ctf_frame:
       /* No further adjustments needed.  */
       break;
     case rs_machine_dependent:
diff --git a/gas/ctf-frame-opt.c b/gas/ctf-frame-opt.c
new file mode 100644
index 00000000000..a3a4561cc89
--- /dev/null
+++ b/gas/ctf-frame-opt.c
@@ -0,0 +1,158 @@
+/* ctf-frame-opt.c - optimize FRE and FDE information in CTF Frame.
+   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 "ctf-frame.h"
+
+/* The function estimates the size of a rs_ctf_frame 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
+ctf_frame_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 a CTF Frame FDE will always write
+     only one byte.  */
+  if (exp->X_op == O_subtract)
+    ret = 1;
+  /* Fragment for the start address in a CTF Frame 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_ctf_frame 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
+ctf_frame_relax_frag (fragS *frag)
+{
+  int oldsize, newsize;
+
+  oldsize = frag->fr_subtype & 7;
+  if (oldsize == 7)
+    oldsize = -1;
+  newsize = ctf_frame_estimate_size_before_relax (frag);
+  return newsize - oldsize;
+}
+
+/* This function converts a rs_ctf_frame 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
+ctf_frame_convert_frag (fragS *frag)
+{
+  offsetT fsize;
+  offsetT diff;
+  offsetT value;
+  unsigned char func_info = CTF_FRAME_ROW_ENTRY_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 a CTF Frame FDE.  */
+  if (exp->X_op == O_subtract)
+    {
+      fsizeS = frag->fr_symbol;
+      fsize = resolve_symbol_value (fsizeS);
+      if (fsize < 0x100)
+	func_info = CTF_FRAME_ROW_ENTRY_TYPE_ADDR1;
+      else if (fsize < 0x10000)
+	func_info = CTF_FRAME_ROW_ENTRY_TYPE_ADDR2;
+      else
+	func_info = CTF_FRAME_ROW_ENTRY_TYPE_ADDR4;
+      value = func_info;
+
+      frag->fr_literal[frag->fr_fix] = value;
+    }
+  /* Fragment for the start address in a CTF Frame 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/doc/as.texi b/gas/doc/as.texi
index c74b9eee3d6..c1c3233ece6 100644
--- a/gas/doc/as.texi
+++ b/gas/doc/as.texi
@@ -4846,11 +4846,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{.ctf_frame} 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{.ctf_frame}, @code{.ctf_frame} 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 6be8cb50495..b2882ff527d 100644
--- a/gas/dw2gencfi.c
+++ b/gas/dw2gencfi.c
@@ -23,6 +23,7 @@
 #include "dw2gencfi.h"
 #include "subsegs.h"
 #include "dwarf2dbg.h"
+#include "gen-ctf-frame.h"
 
 #ifdef TARGET_USE_CFIPOP
 
@@ -1231,6 +1232,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, ".ctf_frame"))
+	    sections |= CFI_EMIT_ctf_frame;
 	else
 	  {
 	    *input_line_pointer = c;
@@ -2471,6 +2474,33 @@ cfi_finish (void)
       flag_traditional_format = save_flag_traditional_format;
     }
 
+  cfi_sections_set = true;
+  // FIXME - remove this commented line once the compiler can specify
+  // .ctf_frame for .cfi_sections directive
+  // if ((all_cfi_sections & CFI_EMIT_ctf_frame) != 0)
+  if (flag_gen_ctf_frame || (all_cfi_sections & CFI_EMIT_ctf_frame) != 0)
+    {
+#ifdef support_ctf_frame_p
+      if (support_ctf_frame_p ())
+	{
+	  segT ctf_frame_seg;
+	  int alignment = ffs (DWARF2_ADDR_SIZE (stdoutput)) - 1;
+
+	  if (!SUPPORT_FRAME_LINKONCE)
+	    ctf_frame_seg = get_cfi_seg (NULL, ".ctf_frame",
+					 (SEC_ALLOC | SEC_LOAD | SEC_DATA
+					  | DWARF2_EH_FRAME_READ_ONLY),
+					 alignment);
+	  output_ctf_frame (ctf_frame_seg);
+	}
+      else
+	as_warn (_(".ctf_frame not supported for target"));
+
+#else
+	as_warn (_(".ctf_frame not supported for target"));
+#endif
+    }
+
   cfi_sections_set = true;
   if ((all_cfi_sections & CFI_EMIT_debug_frame) != 0)
     {
diff --git a/gas/dw2gencfi.h b/gas/dw2gencfi.h
index d570cdb8db3..02ef27af7ca 100644
--- a/gas/dw2gencfi.h
+++ b/gas/dw2gencfi.h
@@ -200,5 +200,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_ctf_frame              (1 << 4)
 
 #endif /* DW2GENCFI_H */
diff --git a/gas/gen-ctf-frame.c b/gas/gen-ctf-frame.c
new file mode 100644
index 00000000000..251699166bc
--- /dev/null
+++ b/gas/gen-ctf-frame.c
@@ -0,0 +1,1297 @@
+/* gen-ctf-frame.c - Support for generating CTF Frame section.
+   Copyright (C) 2021 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 "ctf-frame.h"
+#include "gen-ctf-frame.h"
+#include "dw2gencfi.h"
+
+#ifdef support_ctf_frame_p
+
+/* By default, use 32-bit relocations from .ctf_frame into .text.  */
+#ifndef CTF_FRAME_RELOC_SIZE
+# define CTF_FRAME_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, CTF_FRAME_CFA_RA_REG must be defined with the return
+   address register number.  */
+
+#if defined (ctf_frame_ra_tracking_p) && defined (CTF_FRAME_CFA_RA_REG)
+# ifndef CTFF_ROW_ENTRY_RA_TRACKING
+# define CTFF_ROW_ENTRY_RA_TRACKING 1
+# endif
+#endif
+
+/* CTF FRE type selection optimization is an optimization for size.
+
+   There are three flavors of CTF FRE representation in the binary format:
+     - ctf_frame_row_entry_addr1 where the FRE start address is 1 byte.
+     - ctf_frame_row_entry_addr2 where the FRE start address is 2 bytes.
+     - ctf_frame_row_entry_addr4 where the FRE start address is 4 bytes.
+
+   Note that in the CTF Frame format, all CTF FREs of a function use one
+   single representation.  The CTF FRE type itself is identified via the
+   information in the CTF 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_ctf_frame) for the CTF Frame section are fixed up later.
+
+   This optimization (for size) is enabled by default.  */
+
+#ifndef CTF_FRE_TYPE_SELECTION_OPT
+# define CTF_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;
+}
+
+/* Callback to create the abi/arch identifier for CTF Frame section.  */
+
+unsigned char
+ctf_frame_get_abi_arch_callback (const char *target_arch,
+				 int big_endian_p)
+{
+  unsigned char ctf_frame_abi_arch = 0;
+
+  if (strcmp (target_arch, "aarch64") == 0)
+    {
+      ctf_frame_abi_arch = big_endian_p
+	? CTF_FRAME_ABI_AARCH64_ENDIAN_BIG
+	: CTF_FRAME_ABI_AARCH64_ENDIAN_LITTLE;
+    }
+  else if (strcmp (target_arch, "x86-64") == 0)
+    {
+      gas_assert (!big_endian_p);
+      ctf_frame_abi_arch = CTF_FRAME_ABI_AMD64_ENDIAN_LITTLE;
+    }
+  else
+    {
+      /* Other abi/arch are not supported.  Should be unreachable.  */
+      printf (_("CTF Unsupported abi or arch\n"));
+      abort ();
+    }
+
+  return ctf_frame_abi_arch;
+}
+
+/* CTF Frame Row Entry (FRE) related functions.  */
+
+static void
+ctf_fre_set_begin_addr (struct ctf_frame_row_entry *fre, symbolS *beginS)
+{
+  fre->pc_begin = beginS;
+}
+
+static void
+ctf_fre_set_end_addr (struct ctf_frame_row_entry *fre, symbolS *endS)
+{
+  fre->pc_end = endS;
+}
+
+static void
+ctf_fre_set_cfa_base_reg (struct ctf_frame_row_entry *fre,
+			  unsigned int cfa_base_reg)
+{
+  fre->cfa_base_reg = cfa_base_reg;
+  fre->merge_candidate = false;
+}
+
+static void
+ctf_fre_set_cfa_offset (struct ctf_frame_row_entry *fre, offsetT cfa_offset)
+{
+  fre->cfa_offset = cfa_offset;
+  fre->merge_candidate = false;
+}
+
+#ifdef CTFF_ROW_ENTRY_RA_TRACKING
+static void
+ctf_fre_set_ra_track (struct ctf_frame_row_entry *fre, offsetT ra_offset)
+{
+  fre->ra_loc = CTF_FRE_ELEM_LOC_STACK;
+  fre->ra_offset = ra_offset;
+  fre->merge_candidate = false;
+}
+#endif
+
+static void
+ctf_fre_set_bp_track (struct ctf_frame_row_entry *fre, offsetT bp_offset)
+{
+  fre->bp_loc = CTF_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 >= (long)-VALUE_8BIT)
+    size = 1;
+  else if (value <= VALUE_16BIT && value >= (long)-VALUE_16BIT)
+    size = 2;
+  else if (value <= VALUE_32BIT && value >= (long)-VALUE_32BIT)
+    size = 4;
+  else if (value <= VALUE_64BIT && value >= (long)-VALUE_64BIT)
+    size = 8;
+
+  return size;
+}
+
+#define CTF_FRE_OFFSET_FUNC_MAP_INDEX_1B  0 /* CTF_FRAME_FRE_OFFSET_1B.  */
+#define CTF_FRE_OFFSET_FUNC_MAP_INDEX_2B  1 /* CTF_FRAME_FRE_OFFSET_2B.  */
+#define CTF_FRE_OFFSET_FUNC_MAP_INDEX_4B  2 /* CTF_FRAME_FRE_OFFSET_4B.  */
+#define CTF_FRE_OFFSET_FUNC_MAP_INDEX_8B  3 /* Not supported in CTF.  */
+#define CTF_FRE_OFFSET_FUNC_MAP_INDEX_MAX CTF_FRE_OFFSET_FUNC_MAP_INDEX_8B
+
+/* Helper struct for mapping offset size to output functions.  */
+
+struct ctf_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
+ctf_fre_offset_func_map_index (unsigned int offset_size)
+{
+  unsigned int index = CTF_FRE_OFFSET_FUNC_MAP_INDEX_MAX;
+
+  switch (offset_size)
+    {
+      case CTF_FRAME_FRE_OFFSET_1B:
+	index = CTF_FRE_OFFSET_FUNC_MAP_INDEX_1B;
+	break;
+      case CTF_FRAME_FRE_OFFSET_2B:
+	index = CTF_FRE_OFFSET_FUNC_MAP_INDEX_2B;
+	break;
+      case CTF_FRAME_FRE_OFFSET_4B:
+	index = CTF_FRE_OFFSET_FUNC_MAP_INDEX_4B;
+	break;
+      default:
+	/* Not supported in CTF Frame.  */
+	break;
+    }
+
+  return index;
+}
+
+/* Mapping from offset size to the output function to emit the value.  */
+
+static const
+struct ctf_fre_offset_func_map
+fre_offset_func_map[CTF_FRE_OFFSET_FUNC_MAP_INDEX_MAX+1] =
+{
+  { CTF_FRAME_FRE_OFFSET_1B, out_one },
+  { CTF_FRAME_FRE_OFFSET_2B, out_two },
+  { CTF_FRAME_FRE_OFFSET_4B, out_four },
+  { -1, NULL } /* Not Supported in CTF Frame.  */
+};
+
+/* CTF Frame version specific operations access.  */
+
+static struct ctf_frame_version_ops ctf_frame_ver_ops;
+
+/* CTF Frame (CTF_FRAME_VERSION_1) set FRE info.  */
+
+static unsigned char
+ctf_frame_v1_set_fre_info (unsigned int base_reg, unsigned int num_offsets,
+			   unsigned int offset_size)
+{
+  unsigned char fre_info;
+  fre_info = CTF_FRAME_V1_FRE_INFO (base_reg, num_offsets, offset_size);
+  return fre_info;
+}
+
+/* CTF Frame (CTF_FRAME_VERSION_1) set function info.  */
+static unsigned char
+ctf_frame_v1_set_func_info (unsigned int fde_type, unsigned int fre_type)
+{
+  unsigned char func_info;
+  func_info = CTF_FRAME_V1_FUNC_INFO (fde_type, fre_type);
+  return func_info;
+}
+
+/* CTF Frame version specific operations setup.  */
+
+static void
+ctf_frame_set_version (uint32_t ctf_frame_version __attribute__((unused)))
+{
+  ctf_frame_ver_ops.format_version = CTF_FRAME_VERSION_1;
+
+  ctf_frame_ver_ops.set_fre_info = ctf_frame_v1_set_fre_info;
+
+  ctf_frame_ver_ops.set_func_info = ctf_frame_v1_set_func_info;
+}
+
+/* CTF Frame set FRE info.  */
+
+static unsigned char
+ctf_frame_set_fre_info (unsigned int base_reg, unsigned int num_offsets,
+			unsigned int offset_size)
+{
+  return ctf_frame_ver_ops.set_fre_info (base_reg, num_offsets,
+					 offset_size);
+}
+
+/* CTF Frame set func info. */
+
+ATTRIBUTE_UNUSED static unsigned char
+ctf_frame_set_func_info (unsigned int fde_type, unsigned int fre_type)
+{
+  return ctf_frame_ver_ops.set_func_info (fde_type, fre_type);
+}
+
+/* Get the number of CTF FDEs for the current file.  */
+
+static unsigned int
+get_num_ctf_fdes (void);
+
+/* Get the number of CTF frame row entries for the current file.  */
+
+static unsigned int
+get_num_ctf_fres (void);
+
+/* Get CFA base register ID as represented in CTF Frame Row Entry.  */
+
+static unsigned int
+get_fre_base_reg_id (struct ctf_frame_row_entry *ctf_fre)
+{
+  unsigned int cfi_insn_cfa_base_reg = ctf_fre->cfa_base_reg;
+  unsigned fre_base_reg = CTF_FRAME_BASE_REG_SP;
+
+  if (cfi_insn_cfa_base_reg == CTF_FRAME_CFA_FP_REG)
+    fre_base_reg = CTF_FRAME_BASE_REG_FP;
+
+  /* Only one bit is reserved in CTF_FRAME_VERSION_1.  */
+  gas_assert (fre_base_reg == CTF_FRAME_BASE_REG_SP
+	      || fre_base_reg == CTF_FRAME_BASE_REG_FP);
+
+  return fre_base_reg;
+}
+
+/* Get number of offsets necessary for the CTF Frame Row Entry.  */
+
+static unsigned int
+get_fre_num_offsets (struct ctf_frame_row_entry *ctf_fre)
+{
+  /* Atleast 1 must always be present (to recover CFA).  */
+  unsigned int fre_num_offsets = 1;
+
+  if (ctf_fre->bp_loc == CTF_FRE_ELEM_LOC_STACK)
+    fre_num_offsets++;
+#ifdef CTFF_ROW_ENTRY_RA_TRACKING
+  if (ctf_fre->ra_loc == CTF_FRE_ELEM_LOC_STACK)
+    fre_num_offsets++;
+#endif
+  return fre_num_offsets;
+}
+
+/* Get the minimum necessary offset size (in bytes) for this CTF frame
+   row entry.  */
+
+static unsigned int
+ctf_frame_get_fre_offset_size (struct ctf_frame_row_entry *ctf_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 (ctf_fre->cfa_offset);
+  if (ctf_fre->bp_loc == CTF_FRE_ELEM_LOC_STACK)
+    bp_offset_size = get_offset_size_in_bytes (ctf_fre->bp_offset);
+#ifdef CTFF_ROW_ENTRY_RA_TRACKING
+  if (ctf_frame_ra_tracking_p ()
+      && ctf_fre->ra_loc == CTF_FRE_ELEM_LOC_STACK)
+    ra_offset_size = get_offset_size_in_bytes (ctf_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 = CTF_FRAME_FRE_OFFSET_1B;
+      break;
+    case 2:
+      fre_offset_size = CTF_FRAME_FRE_OFFSET_2B;
+      break;
+    case 4:
+      fre_offset_size = CTF_FRAME_FRE_OFFSET_4B;
+      break;
+    default:
+      /* Offset of size 8 bytes is not supported in CTF Frame format
+	 version 1.  */
+      printf (_("CTF Unsupported offset value\n"));
+      abort ();
+      break;
+    }
+
+  return fre_offset_size;
+}
+
+#if CTF_FRE_TYPE_SELECTION_OPT
+
+/* Create a composite exression CEXP (for CTF 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_ctf_frame_row_entry (symbolS *fde_start_addr,
+			    symbolS *fde_end_addr,
+			    struct ctf_frame_row_entry *ctf_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? */
+
+  /* CTF FRE Start Address.  */
+#if CTF_FRE_TYPE_SELECTION_OPT
+  create_fre_start_addr_exp (&exp, ctf_fre->pc_begin, fde_start_addr,
+			     fde_end_addr);
+  frag_grow (fre_addr_size);
+  frag_var (rs_ctf_frame, 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 = ctf_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 (ctf_fre);
+  fre_num_offsets = get_fre_num_offsets (ctf_fre);
+  fre_offset_size = ctf_frame_get_fre_offset_size (ctf_fre);
+  fre_info = ctf_frame_set_fre_info (fre_base_reg, fre_num_offsets,
+				     fre_offset_size);
+  out_one (fre_info);
+
+  index = ctf_fre_offset_func_map_index (fre_offset_size);
+  gas_assert (index < CTF_FRE_OFFSET_FUNC_MAP_INDEX_MAX);
+
+  /* Write out the offsets in order - cfa, bp, ra.  */
+  fre_offset_func_map[index].out_func (ctf_fre->cfa_offset);
+  fre_write_offsets++;
+
+  if (ctf_fre->bp_loc == CTF_FRE_ELEM_LOC_STACK)
+    {
+      fre_offset_func_map[index].out_func (ctf_fre->bp_offset);
+      fre_write_offsets++;
+    }
+
+#ifdef CTFF_ROW_ENTRY_RA_TRACKING
+  if (ctf_fre->ra_loc == CTF_FRE_ELEM_LOC_STACK)
+    {
+      fre_offset_func_map[index].out_func (ctf_fre->ra_offset);
+      fre_write_offsets++;
+    }
+#endif
+  /* Check if the expected number offsets have been written out
+     in this FRE.  */
+  gas_assert (fre_write_offsets == fre_num_offsets);
+}
+
+static void
+output_ctf_frame_funcdesc (symbolS *start_of_fre_section,
+			    symbolS *fre_symbol,
+			    struct ctf_func_desc_entry *ctf_fde)
+{
+  expressionS exp;
+  unsigned int addr_size;
+  symbolS *dw_fde_start_addrS, *dw_fde_end_addrS;
+
+  addr_size = CTF_FRAME_RELOC_SIZE;
+  dw_fde_start_addrS = get_dw_fde_start_addrS (ctf_fde->dw_fde);
+  dw_fde_end_addrS = get_dw_fde_end_addrS (ctf_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 (ctf_fde->num_fres);
+
+  /* CTF FDE function info.  */
+#if CTF_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_ctf_frame, 1, 0, (relax_substateT) 0,
+	    make_expr_symbol (&width), 0, (char *) frag_now);
+#else
+  unsigned char func_info;
+  func_info = ctf_frame_set_func_info (CTF_FRAME_FUNC_DESC_ENTRY_TYPE_PCINC,
+				       CTF_FRAME_ROW_ENTRY_TYPE_ADDR4);
+  out_one (func_info);
+#endif
+}
+
+static void
+output_ctf_frame_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 ctf_func_desc_entry *ctf_fde;
+  struct ctf_frame_row_entry *ctf_fre;
+  unsigned char abi_arch = 0;
+  int fixed_bp_offset = CTF_FRAME_CFA_FIXED_FP_INVALID;
+  int fixed_ra_offset = CTF_FRAME_CFA_FIXED_RA_INVALID;
+  unsigned int addr_size;
+
+  addr_size = CTF_FRAME_RELOC_SIZE;
+
+  /* The function desciptor entries as dumped by the assembler are not
+     sorted on PCs.  */
+  unsigned char ctf_frame_flags = 0;
+  ctf_frame_flags |= !CTF_FRAME_F_FDE_SORTED;
+
+  unsigned int num_fdes = get_num_ctf_fdes ();
+  unsigned int num_fres = get_num_ctf_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 CTF Frame section.  */
+  out_two (CTF_FRAME_MAGIC);
+  out_one (CTF_FRAME_VERSION);
+  out_one (ctf_frame_flags);
+  /* abi/arch.  */
+#ifdef ctf_frame_get_abi_arch
+  abi_arch = ctf_frame_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 CTFF_ROW_ENTRY_RA_TRACKING
+  if (!ctf_frame_ra_tracking_p ())
+    fixed_ra_offset = ctf_frame_cfa_ra_offset ();
+#endif
+  out_one (fixed_ra_offset);
+
+  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 CTF Frame function descriptor entries.  */
+  i = 0;
+  for (ctf_fde = all_ctf_fde_data; ctf_fde; ctf_fde = ctf_fde->next)
+    {
+      output_ctf_frame_funcdesc (start_of_fre_section,
+				 fre_symbols[i], ctf_fde);
+      i += ctf_fde->num_fres;
+    }
+
+  symbol_set_value_now (start_of_fre_section);
+
+  /* Output the CTF Frame FREs.  */
+  i = 0;
+  ctf_fde = all_ctf_fde_data;
+
+  for (ctf_fde = all_ctf_fde_data; ctf_fde; ctf_fde = ctf_fde->next)
+    {
+      for (ctf_fre = ctf_fde->ctf_fres; ctf_fre; ctf_fre = ctf_fre->next)
+	{
+	  symbol_set_value_now (fre_symbols[i]);
+	  output_ctf_frame_row_entry (get_dw_fde_start_addrS (ctf_fde->dw_fde),
+				      get_dw_fde_end_addrS (ctf_fde->dw_fde),
+				      ctf_fre);
+	  i++;
+	}
+    }
+
+  symbol_set_value_now (end_of_frame_section);
+
+  gas_assert (i == num_fres);
+
+  free (fre_symbols);
+  fre_symbols = NULL;
+}
+
+/* List of CTF FDE entries.  */
+
+struct ctf_func_desc_entry *all_ctf_fde_data;
+
+/* Tail of the list to add to.  */
+
+static struct ctf_func_desc_entry **last_ctf_fde_data = &all_ctf_fde_data;
+
+static unsigned int
+get_num_ctf_fdes (void)
+{
+  struct ctf_func_desc_entry *ctf_fde;
+  unsigned int total_fdes = 0;
+
+  for (ctf_fde = all_ctf_fde_data; ctf_fde ; ctf_fde = ctf_fde->next)
+    total_fdes++;
+
+  return total_fdes;
+}
+
+/* Get the total number of CTF Frame row entries across the FDEs.  */
+
+static unsigned int
+get_num_ctf_fres (void)
+{
+  struct ctf_func_desc_entry *ctf_fde;
+  unsigned int total_fres = 0;
+
+  for (ctf_fde = all_ctf_fde_data; ctf_fde ; ctf_fde = ctf_fde->next)
+    total_fres += ctf_fde->num_fres;
+
+  return total_fres;
+}
+
+/* Allocate a CTF FDE.  */
+
+static struct ctf_func_desc_entry*
+ctf_fde_alloc (void)
+{
+  struct ctf_func_desc_entry *ctf_fde = XCNEW (struct ctf_func_desc_entry);
+  return ctf_fde;
+}
+
+/* Link the CTF FDE in.  */
+
+static int
+ctf_fde_link (struct ctf_func_desc_entry *ctf_fde)
+{
+  *last_ctf_fde_data = ctf_fde;
+  last_ctf_fde_data = &ctf_fde->next;
+
+  return 0;
+}
+
+/* Free up the CTF FDE.  */
+
+static void
+ctf_fde_free (struct ctf_func_desc_entry *ctf_fde)
+{
+  XDELETE (ctf_fde);
+  ctf_fde = NULL;
+}
+
+/* CTF Frame translation context functions.  */
+
+/* Allocate a new CTF Frame translation context.  */
+
+static struct ctf_frame_xlate_ctx*
+ctf_frame_xlate_ctx_alloc (void)
+{
+  struct ctf_frame_xlate_ctx* xlate_ctx = XCNEW (struct ctf_frame_xlate_ctx);
+  return xlate_ctx;
+}
+
+/* Initialize the given CTF Frame translation context.  */
+
+static void
+ctf_frame_xlate_ctx_init (struct ctf_frame_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 CTF Frame translation context.  */
+
+static void
+ctf_frame_xlate_ctx_cleanup (struct ctf_frame_xlate_ctx *xlate_ctx)
+{
+  struct ctf_frame_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;
+    }
+  }
+
+  ctf_frame_xlate_ctx_init (xlate_ctx);
+}
+
+/* Transfer the state from the CTF Frame translation context to the CTF FDE.  */
+
+static void
+ctf_frame_xlate_ctx_finalize (struct ctf_frame_xlate_ctx *xlate_ctx,
+			      struct ctf_func_desc_entry *ctf_fde)
+{
+  ctf_fde->dw_fde = xlate_ctx->dw_fde;
+  ctf_fde->ctf_fres = xlate_ctx->first_fre;
+  ctf_fde->num_fres = xlate_ctx->num_xlate_fres;
+}
+
+static struct ctf_frame_row_entry*
+ctf_frame_row_entry_new (void)
+{
+  struct ctf_frame_row_entry *fre = XCNEW (struct ctf_frame_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
+ctf_frame_xlate_ctx_add_fre (struct ctf_frame_xlate_ctx *xlate_ctx,
+			 struct ctf_frame_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 CTF frame row entries.  */
+  xlate_ctx->num_xlate_fres++;
+}
+
+/* A CTF 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 CTF 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
+ctf_frame_row_entry_initialize (struct ctf_frame_row_entry *cur_fre,
+				struct ctf_frame_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;
+}
+
+static int
+ctf_xlate_do_advance_loc (struct ctf_frame_xlate_ctx *xlate_ctx,
+			 struct cfi_insn_data *cfi_insn)
+{
+  struct ctf_frame_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 ctf_frame_row_entry *cur_fre = xlate_ctx->cur_fre;
+
+  if (cur_fre)
+    {
+      if (!cur_fre->merge_candidate)
+	{
+	  ctf_fre_set_end_addr (cur_fre, cfi_insn->u.ll.lab2);
+
+	  ctf_frame_xlate_ctx_add_fre (xlate_ctx, cur_fre);
+	  last_fre = xlate_ctx->last_fre;
+
+	  xlate_ctx->cur_fre = ctf_frame_row_entry_new ();
+	  cur_fre = xlate_ctx->cur_fre;
+
+	  if (last_fre)
+	    ctf_frame_row_entry_initialize (cur_fre, last_fre);
+	}
+      else
+	{
+	  ctf_fre_set_end_addr (last_fre, cfi_insn->u.ll.lab2);
+	  gas_assert (last_fre->merge_candidate == false);
+	}
+    }
+  else
+    {
+      xlate_ctx->cur_fre = ctf_frame_row_entry_new ();
+      cur_fre = xlate_ctx->cur_fre;
+    }
+
+  gas_assert (cur_fre);
+  ctf_fre_set_begin_addr (cur_fre, cfi_insn->u.ll.lab2);
+
+  return 0;
+}
+
+static int
+ctf_xlate_do_def_cfa (struct ctf_frame_xlate_ctx *xlate_ctx,
+		      struct cfi_insn_data *cfi_insn)
+
+{
+  /* Get the scratchpad FRE.  This FRE will eventually get linked in.  */
+  struct ctf_frame_row_entry *cur_fre = xlate_ctx->cur_fre;
+  if (!cur_fre)
+  {
+    xlate_ctx->cur_fre = ctf_frame_row_entry_new ();
+    cur_fre = xlate_ctx->cur_fre;
+    ctf_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.  */
+  ctf_fre_set_cfa_base_reg (cur_fre, cfi_insn->u.ri.reg);
+  ctf_fre_set_cfa_offset (cur_fre, cfi_insn->u.ri.offset);
+  cur_fre->merge_candidate = false;
+
+  return 0;
+}
+
+static int
+ctf_xlate_do_def_cfa_register (struct ctf_frame_xlate_ctx *xlate_ctx,
+			       struct cfi_insn_data *cfi_insn)
+{
+  struct ctf_frame_row_entry *last_fre = xlate_ctx->last_fre;
+  /* Get the scratchpad FRE.  This FRE will eventually get linked in.  */
+  struct ctf_frame_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).  */
+  ctf_fre_set_cfa_base_reg (cur_fre, cfi_insn->u.ri.reg);
+  ctf_fre_set_cfa_offset (cur_fre, last_fre->cfa_offset);
+  cur_fre->merge_candidate = false;
+
+  return 0;
+}
+
+static int
+ctf_xlate_do_def_cfa_offset (struct ctf_frame_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 ctf_frame_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 CTF Frame unwind info for the function. */
+  if ((cur_fre->cfa_base_reg == CTF_FRAME_CFA_FP_REG)
+      || (cur_fre->cfa_base_reg == CTF_FRAME_CFA_SP_REG))
+    {
+      ctf_fre_set_cfa_offset (cur_fre, cfi_insn->u.i);
+      cur_fre->merge_candidate = false;
+    }
+  else
+    return -1;
+
+  return 0;
+}
+
+static int
+ctf_xlate_do_offset (struct ctf_frame_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 ctf_frame_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 == CTF_FRAME_CFA_FP_REG)
+    {
+      gas_assert (!cur_fre->base_reg);
+      ctf_fre_set_bp_track (cur_fre, cfi_insn->u.ri.offset);
+      cur_fre->merge_candidate = false;
+    }
+#ifdef CTFF_ROW_ENTRY_RA_TRACKING
+  else if (ctf_frame_ra_tracking_p ()
+	   && cfi_insn->u.r == CTF_FRAME_CFA_RA_REG)
+    {
+      ctf_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 0;
+}
+
+static int
+ctf_xlate_do_val_offset (struct ctf_frame_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 == CTF_FRAME_CFA_FP_REG)
+    return 1; /* Not represented.  */
+#ifdef CTFF_ROW_ENTRY_RA_TRACKING
+  else if (ctf_frame_ra_tracking_p ()
+	   && cfi_insn->u.r == CTF_FRAME_CFA_RA_REG)
+    return 1; /* Not represented.  */
+#endif
+
+  /* Safe to skip.  */
+  return 0;
+}
+
+
+static int
+ctf_xlate_do_remember_state (struct ctf_frame_xlate_ctx *xlate_ctx)
+{
+  struct ctf_frame_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 CTF Frame unwind info
+     for the function involved.  */
+  if (!last_fre)
+    return -1;
+
+  if (!xlate_ctx->remember_fre)
+    xlate_ctx->remember_fre = ctf_frame_row_entry_new ();
+  ctf_frame_row_entry_initialize (xlate_ctx->remember_fre, last_fre);
+
+  return 0;
+}
+
+static int
+ctf_xlate_do_restore_state (struct ctf_frame_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 ctf_frame_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.  */
+  ctf_frame_row_entry_initialize (cur_fre, xlate_ctx->remember_fre);
+  /* The PC boundaries of the current CTF FRE are updated
+     via other machinery.  */
+  cur_fre->merge_candidate = false;
+  return 0;
+}
+
+static int
+ctf_xlate_do_restore (struct ctf_frame_xlate_ctx *xlate_ctx,
+		      struct cfi_insn_data *cfi_insn)
+{
+  struct ctf_frame_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 ctf_frame_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);
+  /* CTF FREs track only CFA and FP / RA for backtracing purposes;
+     skip the other .cfi_restore directives.  */
+  if (cfi_insn->u.r == CTF_FRAME_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 CTFF_ROW_ENTRY_RA_TRACKING
+  else if (ctf_frame_ra_tracking_p ()
+	   && cfi_insn->u.r == CTF_FRAME_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 0;
+}
+
+/* Process CFI_INSN and update the translation context with the FRE
+   information.
+
+   Returns an error code if CFI_INSN is not successfully processed.  */
+
+static int
+ctf_frame_do_cfi_insn (struct ctf_frame_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 = ctf_xlate_do_advance_loc (xlate_ctx, cfi_insn);
+      break;
+    case DW_CFA_def_cfa:
+      err = ctf_xlate_do_def_cfa (xlate_ctx, cfi_insn);
+      break;
+    case DW_CFA_def_cfa_register:
+      err = ctf_xlate_do_def_cfa_register (xlate_ctx, cfi_insn);
+      break;
+    case DW_CFA_def_cfa_offset:
+      err = ctf_xlate_do_def_cfa_offset (xlate_ctx, cfi_insn);
+      break;
+    case DW_CFA_offset:
+      err = ctf_xlate_do_offset (xlate_ctx, cfi_insn);
+      break;
+    case DW_CFA_val_offset:
+      err = ctf_xlate_do_val_offset (xlate_ctx, cfi_insn);
+      break;
+    case DW_CFA_remember_state:
+      err = ctf_xlate_do_remember_state (xlate_ctx);
+      break;
+    case DW_CFA_restore_state:
+      err = ctf_xlate_do_restore_state (xlate_ctx);
+      break;
+    case DW_CFA_restore:
+      err = ctf_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 CTF Frame 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 = 1;
+	// printf (_("CTF Unsupported or unknown Dwarf CFI number: %#x\n"), op);
+      }
+    }
+
+  return err;
+}
+
+static int ctf_frame_do_fde (struct ctf_frame_xlate_ctx *xlate_ctx,
+			     const struct fde_entry *dw_fde)
+{
+  struct cfi_insn_data *cfi_insn;
+
+  xlate_ctx->dw_fde = dw_fde;
+
+  /* If the return column is not RIP, CTF Frame format cannot represent it.  */
+  if (xlate_ctx->dw_fde->return_column != DWARF2_DEFAULT_RETURN_COLUMN)
+    return 1;
+
+  /* Iterate over the CFIs and create CTF FREs.  */
+  for (cfi_insn = dw_fde->data; cfi_insn; cfi_insn = cfi_insn->next)
+    {
+      /* Translate each CFI, and buffer the state in translation context.  */
+      if (ctf_frame_do_cfi_insn (xlate_ctx, cfi_insn))
+	{
+	  /* Skip generating CTF Frame unwind info for the function if any offending
+	     CFI is encountered by ctf_frame_do_cfi_insn ().  */
+	  /* FIXME - get a detailed look at the individual cases.  */
+	  // printf ("WATCH THIS ONE! ctf_frame_do_cfi_insn skipped. \n");
+	  return 1;  /* Error.  */
+	}
+    }
+
+  /* No errors encountered.  */
+
+  /* Link in the scratchpad FRE that the last few CFI insns helped create.  */
+  if (xlate_ctx->cur_fre)
+    {
+      ctf_frame_xlate_ctx_add_fre (xlate_ctx, xlate_ctx->cur_fre);
+      xlate_ctx->cur_fre = NULL;
+    }
+  /* Designate the end of the last CTF FRE.  */
+  if (xlate_ctx->last_fre)
+    {
+      xlate_ctx->last_fre->pc_end
+	= get_dw_fde_end_addrS (xlate_ctx->dw_fde);
+    }
+
+  return 0;
+}
+
+/* Create CTF Frame unwind info for all functions.
+
+   This function consumes the already generated FDEs (by dw2gencfi) and
+   generates unwind data in CTF format.  */
+
+static void create_ctf_frame_all (void)
+{
+  struct fde_entry *dw_fde = NULL;
+  struct ctf_func_desc_entry *ctf_fde = NULL;
+
+  struct ctf_frame_xlate_ctx *xlate_ctx = ctf_frame_xlate_ctx_alloc ();
+
+  for (dw_fde = all_fde_data; dw_fde ; dw_fde = dw_fde->next)
+    {
+      /* FIXME - enable this assert once the compiler is generating
+	 .cfi_sections with .ctf_frame enabled.  */
+      // gas_assert ((fde->sections & CFI_EMIT_ctf_frame));
+      ctf_fde = ctf_fde_alloc ();
+      /* Initialize the translation context with information anew.  */
+      ctf_frame_xlate_ctx_init (xlate_ctx);
+
+      /* Process and link CTF Frame FDEs if no error.  Also skip adding a CTF
+	 FDE if it does not contain any CTF FREs.  There is little use of a CTF
+	 FDE if there is no unwind information about the function.  */
+      int err = ctf_frame_do_fde (xlate_ctx, dw_fde);
+      if (err || xlate_ctx->num_xlate_fres == 0)
+	{
+	  ctf_frame_xlate_ctx_cleanup (xlate_ctx);
+	  ctf_fde_free (ctf_fde);
+	}
+      else
+	{
+	  /* All done.  Transfer the state from the CTF Frame translation
+	     context to the CTF FDE.  */
+	  ctf_frame_xlate_ctx_finalize (xlate_ctx, ctf_fde);
+	  ctf_fde_link (ctf_fde);
+	}
+    }
+}
+
+void output_ctf_frame (segT ctf_frame_seg)
+{
+  (void) ctf_frame_seg;
+
+  /* Setup the version specific access functions.  */
+  ctf_frame_set_version (CTF_FRAME_VERSION_1);
+
+  /* Process all fdes and create CTF Frame unwind information.  */
+  create_ctf_frame_all ();
+
+  output_ctf_frame_internal ();
+}
+
+#else  /*  support_ctf_frame_p  */
+
+/* Callback to create the abi/arch identifier for CTF Frame section.  */
+
+unsigned char
+ctf_frame_get_abi_arch_callback (const char *tarch __attribute__((unused)),
+				 int big_endian_p __attribute__((unused)))
+{
+  unsigned char ctf_frame_abi_arch = 0;
+
+  return ctf_frame_abi_arch;
+}
+
+void output_ctf_frame (segT ctf_frame_seg __attribute__((unused)))
+{
+}
+
+#endif /*  support_ctf_frame_p  */
diff --git a/gas/gen-ctf-frame.h b/gas/gen-ctf-frame.h
new file mode 100644
index 00000000000..6b27a7f2256
--- /dev/null
+++ b/gas/gen-ctf-frame.h
@@ -0,0 +1,142 @@
+/* gen-ctf-frame.h - Support for generating CTF Frame.
+   Copyright (C) 2021 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 GENCTFFRAME_H
+#define GENCTFFRAME_H
+
+#define CTF_FRE_ELEM_LOC_REG		0
+#define CTF_FRE_ELEM_LOC_STACK		1
+
+/* CTF 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 ctf_frame_row_entry
+{
+  /* A linked list.  */
+  struct ctf_frame_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;
+};
+
+/* CTF Function Description Entry.  */
+
+struct ctf_func_desc_entry
+{
+  /* A linked list.  */
+  struct ctf_func_desc_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 ctf_frame_row_entry *ctf_fres;
+
+  unsigned int num_fres;
+};
+
+/* CTF Frame Function Description Entry Translation Context.  */
+
+struct ctf_frame_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 ctf_frame_row_entry *first_fre;
+  /* The last FRE in the list.  */
+  struct ctf_frame_row_entry *last_fre;
+
+  /* The current FRE under construction.  */
+  struct ctf_frame_row_entry *cur_fre;
+  /* Remember FRE for an eventual restore.  */
+  struct ctf_frame_row_entry *remember_fre;
+
+  unsigned num_xlate_fres;
+};
+
+/* Callback to create the abi/arch identifier for CTF Frame section.  */
+
+unsigned char
+ctf_frame_get_abi_arch_callback (const char *target_arch,
+				 int big_endian_p);
+
+/* The list of all FDEs with data in CTF internal representation.  */
+
+extern struct ctf_func_desc_entry *all_ctf_fde_data;
+
+/* CTF Frame version specific operations structure.  */
+
+struct ctf_frame_version_ops
+{
+  unsigned char format_version;    /* CTF Frame format version.  */
+  /* set CTF Frame FRE info.  */
+  unsigned char (*set_fre_info) (unsigned int, unsigned int, unsigned int);
+  /* set CTF Frame Func info.  */
+  unsigned char (*set_func_info) (unsigned int, unsigned int);
+};
+
+/* Generate CTF Frame unwind info and prepare contents for the output.
+   outout_ctf_frame ()  is called at the end of file.  */
+
+extern void output_ctf_frame (segT ctf_frame_seg);
+
+#endif /* GENCTFFRAME_H */
+
diff --git a/gas/testsuite/gas/cfi-ctf/cfi-ctf-aarch64-1.d b/gas/testsuite/gas/cfi-ctf/cfi-ctf-aarch64-1.d
new file mode 100644
index 00000000000..60ee0f794ea
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/cfi-ctf-aarch64-1.d
@@ -0,0 +1,20 @@
+#as:
+#objdump: --ctf-frame=.ctf_frame
+#name: CTF Frame generation on aarch64
+#...
+Contents of the CTF Frame section .ctf_frame:
+  Header :
+
+    Version: CTF_FRAME_VERSION_1
+    Flags: NONE
+    Num FDEs: 1
+    Num FREs: 3
+
+  Function Index :
+
+    func idx \[0\]: pc = 0x0, size = 80 bytes
+    STARTPC +CFA +FP +RA +
+    0+0000 +sp\+0 +u +u +
+    0+0004 +sp\+144 +u +u +
+    0+004c +sp\+0 +u +u +
+#pass
diff --git a/gas/testsuite/gas/cfi-ctf/cfi-ctf-aarch64-1.s b/gas/testsuite/gas/cfi-ctf/cfi-ctf-aarch64-1.s
new file mode 100644
index 00000000000..44c6124bf70
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/cfi-ctf-aarch64-1.s
@@ -0,0 +1,61 @@
+        .cfi_sections .ctf_frame
+	.cfi_startproc
+	stp	x19, x20, [sp, -144]!
+	.cfi_def_cfa_offset 144
+	.cfi_offset 19, -144
+	.cfi_offset 20, -136
+	stp	x21, x22, [sp, 16]
+	stp	x23, x24, [sp, 32]
+	stp	x25, x26, [sp, 48]
+	stp	x27, x28, [sp, 64]
+	stp	d8, d9, [sp, 80]
+	stp	d10, d11, [sp, 96]
+	stp	d12, d13, [sp, 112]
+	stp	d14, d15, [sp, 128]
+	.cfi_offset 21, -128
+	.cfi_offset 22, -120
+	.cfi_offset 23, -112
+	.cfi_offset 24, -104
+	.cfi_offset 25, -96
+	.cfi_offset 26, -88
+	.cfi_offset 27, -80
+	.cfi_offset 28, -72
+	.cfi_offset 72, -64
+	.cfi_offset 73, -56
+	.cfi_offset 74, -48
+	.cfi_offset 75, -40
+	.cfi_offset 76, -32
+	.cfi_offset 77, -24
+	.cfi_offset 78, -16
+	.cfi_offset 79, -8
+	nop
+	ldp	x21, x22, [sp, 16]
+	ldp	x23, x24, [sp, 32]
+	ldp	x25, x26, [sp, 48]
+	ldp	x27, x28, [sp, 64]
+	ldp	d8, d9, [sp, 80]
+	ldp	d10, d11, [sp, 96]
+	ldp	d12, d13, [sp, 112]
+	ldp	d14, d15, [sp, 128]
+	ldp	x19, x20, [sp], 144
+	.cfi_restore 20
+	.cfi_restore 19
+	.cfi_restore 78
+	.cfi_restore 79
+	.cfi_restore 76
+	.cfi_restore 77
+	.cfi_restore 74
+	.cfi_restore 75
+	.cfi_restore 72
+	.cfi_restore 73
+	.cfi_restore 27
+	.cfi_restore 28
+	.cfi_restore 25
+	.cfi_restore 26
+	.cfi_restore 23
+	.cfi_restore 24
+	.cfi_restore 21
+	.cfi_restore 22
+	.cfi_def_cfa_offset 0
+	ret
+	.cfi_endproc
diff --git a/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-1.d b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-1.d
new file mode 100644
index 00000000000..d35ab5f62b7
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-1.d
@@ -0,0 +1,17 @@
+#as: --gctf-frame
+#objdump: --ctf-frame=.ctf_frame
+#name: CTF Frame generation using CFI directive .cfi_sections
+#...
+Contents of the CTF Frame section .ctf_frame:
+
+  Header :
+
+    Version: CTF_FRAME_VERSION_1
+    Flags: NONE
+    Num FDEs: 1
+    Num FREs: 1
+
+  Function Index :
+    func idx \[0\]: pc = 0x0, size = 0 bytes
+    STARTPC + CFA + FP + RA +
+#pass
diff --git a/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-1.s b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-1.s
new file mode 100644
index 00000000000..112b1212a01
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-1.s
@@ -0,0 +1,3 @@
+	.cfi_sections .ctf_frame
+	.cfi_startproc
+	.cfi_endproc
diff --git a/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-2.d b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-2.d
new file mode 100644
index 00000000000..52eae6d0fa0
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-2.d
@@ -0,0 +1,17 @@
+#as: --gctf-frame
+#objdump: --ctf-frame=.ctf_frame
+#name: Command line option for generating CTF Frame
+#...
+Contents of the CTF Frame section .ctf_frame:
+
+  Header :
+
+    Version: CTF_FRAME_VERSION_1
+    Flags: NONE
+    Num FDEs: 1
+    Num FREs: 1
+
+  Function Index :
+    func idx \[0\]: pc = 0x0, size = 0 bytes
+    STARTPC + CFA + FP + RA +
+#pass
diff --git a/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-2.s b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-2.s
new file mode 100644
index 00000000000..659b3b9d99b
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-2.s
@@ -0,0 +1,2 @@
+	.cfi_startproc
+	.cfi_endproc
diff --git a/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-3.d b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-3.d
new file mode 100644
index 00000000000..a30387174cd
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-3.d
@@ -0,0 +1,17 @@
+#as:
+#objdump: --ctf-frame=.ctf_frame
+#name: CTF Frame can co-exist with EH Frame
+#...
+Contents of the CTF Frame section .ctf_frame:
+
+  Header :
+
+    Version: CTF_FRAME_VERSION_1
+    Flags: NONE
+    Num FDEs: 1
+    Num FREs: 1
+
+  Function Index :
+    func idx \[0\]: pc = 0x0, size = 0 bytes
+    STARTPC + CFA + FP + RA +
+#pass
diff --git a/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-3.s b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-3.s
new file mode 100644
index 00000000000..c61ad201bdf
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-3.s
@@ -0,0 +1,4 @@
+	.cfi_sections .eh_frame
+	.cfi_sections .ctf_frame
+	.cfi_startproc
+	.cfi_endproc
diff --git a/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-4.d b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-4.d
new file mode 100644
index 00000000000..477abb8a82f
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-4.d
@@ -0,0 +1,21 @@
+#as: --gctf-frame
+#objdump: --ctf-frame=.ctf_frame
+#name: CTF Frame - cfi_def_cfa_offset test
+#...
+Contents of the CTF Frame section .ctf_frame:
+
+  Header :
+
+    Version: CTF_FRAME_VERSION_1
+    Flags: NONE
+    Num FDEs: 1
+    Num FREs: 3
+
+  Function Index :
+    func idx \[0\]: pc = 0x0, size = 12 bytes
+    STARTPC + CFA + FP + RA +
+#...
+    0+0004 +sp\+16 +u +u +
+    0+0008 +sp\+32 +u +u +
+
+#pass
diff --git a/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-4.s b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-4.s
new file mode 100644
index 00000000000..0d026bba71a
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-4.s
@@ -0,0 +1,8 @@
+## Testcase for cfi_def_cfa_offset
+	.cfi_startproc
+	.long 0
+	.cfi_def_cfa_offset 16
+	.long 0
+	.cfi_def_cfa_offset 32
+	.long 0
+	.cfi_endproc
diff --git a/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-5.d b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-5.d
new file mode 100644
index 00000000000..e5c115f1f9b
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-5.d
@@ -0,0 +1,21 @@
+#as: --gctf-frame
+#objdump: --ctf-frame=.ctf_frame
+#name: CTF Frame cfi_adjust_cfa_offset test
+#...
+Contents of the CTF Frame section .ctf_frame:
+
+  Header :
+
+    Version: CTF_FRAME_VERSION_1
+    Flags: NONE
+    Num FDEs: 1
+    Num FREs: 3
+
+  Function Index :
+    func idx \[0\]: pc = 0x0, size = 12 bytes
+    STARTPC + CFA + FP + RA +
+#...
+    0+0004 +sp\+16 +u +u +
+    0+0008 +sp\+24 +u +u +
+
+#pass
diff --git a/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-5.s b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-5.s
new file mode 100644
index 00000000000..c985c39af9d
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-5.s
@@ -0,0 +1,7 @@
+	.cfi_startproc
+	.long 0
+	.cfi_def_cfa_offset 16
+	.long 0
+	.cfi_adjust_cfa_offset 8
+	.long 0
+	.cfi_endproc
diff --git a/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-6.d b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-6.d
new file mode 100644
index 00000000000..28752bb11b4
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-6.d
@@ -0,0 +1,21 @@
+#as: --gctf-frame
+#objdump: --ctf-frame=.ctf_frame
+#name: CTF Frame cfi_offset test
+#...
+Contents of the CTF Frame section .ctf_frame:
+
+  Header :
+
+    Version: CTF_FRAME_VERSION_1
+    Flags: NONE
+    Num FDEs: 1
+    Num FREs: 3
+
+  Function Index :
+    func idx \[0\]: pc = 0x0, size = 12 bytes
+    STARTPC + CFA + FP + RA +
+#...
+    0+0004 +sp\+8 +u +u +
+    0+0008 +sp\+8 +u +u +
+
+#pass
diff --git a/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-6.s b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-6.s
new file mode 100644
index 00000000000..389f324dc5f
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-6.s
@@ -0,0 +1,7 @@
+	.cfi_startproc
+	.long 0
+	.cfi_def_cfa_offset 8
+	.long 0
+	.cfi_offset 0, 8
+	.long 0
+	.cfi_endproc
diff --git a/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-7.d b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-7.d
new file mode 100644
index 00000000000..d9912043338
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-7.d
@@ -0,0 +1,21 @@
+#as: --gctf-frame
+#objdump: --ctf-frame=.ctf_frame
+#name: CTF Frame cfi_rel_offset test
+#...
+Contents of the CTF Frame section .ctf_frame:
+
+  Header :
+
+    Version: CTF_FRAME_VERSION_1
+    Flags: NONE
+    Num FDEs: 1
+    Num FREs: 3
+
+  Function Index :
+    func idx \[0\]: pc = 0x0, size = 12 bytes
+    STARTPC + CFA + FP + RA +
+#...
+    0+0004 +sp\+8 +u +u +
+    0+0008 +sp\+8 +u +u +
+
+#pass
diff --git a/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-7.s b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-7.s
new file mode 100644
index 00000000000..21fa031fb30
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-7.s
@@ -0,0 +1,7 @@
+	.cfi_startproc
+	.long 0
+	.cfi_def_cfa_offset 8
+	.long 0
+	.cfi_rel_offset 1, 8
+	.long 0
+	.cfi_endproc
diff --git a/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-8.d b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-8.d
new file mode 100644
index 00000000000..bd0c6cd390d
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-8.d
@@ -0,0 +1,20 @@
+#as: --gctf-frame
+#objdump: --ctf-frame=.ctf_frame
+#name: CTF Frame cfi_val_offset test
+#...
+Contents of the CTF Frame section .ctf_frame:
+
+  Header :
+
+    Version: CTF_FRAME_VERSION_1
+    Flags: NONE
+    Num FDEs: 1
+    Num FREs: 2
+
+  Function Index :
+    func idx \[0\]: pc = 0x0, size = 8 bytes
+    STARTPC + CFA + FP + RA +
+#...
+    0+0004 +sp\+16 +u +u +
+
+#pass
diff --git a/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-8.s b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-8.s
new file mode 100644
index 00000000000..4d223e34120
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/cfi-ctf-common-8.s
@@ -0,0 +1,12 @@
+## cfi_val_offset when used with "not interesting" registers (from the
+## perspective of CTF Frame section, non FP/RA registers are not
+## interesting) does not affect the asynchronicity of the CTF Frame
+## unwind information.  Such CFI directives can be skipped for CTF Frame
+## unwind info generation.
+	.cfi_startproc
+	.long 0
+	.cfi_def_cfa_offset 16
+	.cfi_val_offset 1, 8
+	.cfi_val_offset 2, -32
+	.long 0
+	.cfi_endproc
diff --git a/gas/testsuite/gas/cfi-ctf/cfi-ctf-x86_64-1.d b/gas/testsuite/gas/cfi-ctf/cfi-ctf-x86_64-1.d
new file mode 100644
index 00000000000..383821c1580
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/cfi-ctf-x86_64-1.d
@@ -0,0 +1,22 @@
+#as: -O0
+#objdump: --ctf-frame=.ctf_frame
+#name: CTF Frame generation on x86_64
+#...
+Contents of the CTF Frame section .ctf_frame:
+
+  Header :
+
+    Version: CTF_FRAME_VERSION_1
+    Flags: NONE
+    Num FDEs: 1
+    Num FREs: 4
+
+  Function Index :
+
+    func idx \[0\]: pc = 0x0, size = 25 bytes
+    STARTPC +CFA +FP +RA +
+    0+0000 +sp\+8 +u +u +
+    0+0001 +sp\+16 +c\-16 +u +
+    0+0004 +fp\+16 +c\-16 +u +
+    0+0018 +sp\+8 +c\-16 +u +
+#pass
diff --git a/gas/testsuite/gas/cfi-ctf/cfi-ctf-x86_64-1.s b/gas/testsuite/gas/cfi-ctf/cfi-ctf-x86_64-1.s
new file mode 100644
index 00000000000..49a093ffffa
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/cfi-ctf-x86_64-1.s
@@ -0,0 +1,30 @@
+        .cfi_sections .ctf_frame
+	.cfi_startproc
+        pushq   %rbp
+        .cfi_def_cfa_offset 16
+        .cfi_offset 6, -16
+        movq    %rsp, %rbp
+        .cfi_def_cfa_register 6
+        pushq   %r15
+        pushq   %r14
+        pushq   %r13
+        pushq   %r12
+        pushq   %rbx
+  ## These CFI opcodes are not interesting
+  ## for CTF Frame generation and will be
+  ## skipped.
+        .cfi_offset 15, -24
+        .cfi_offset 14, -32
+        .cfi_offset 13, -40
+        .cfi_offset 12, -48
+        .cfi_offset 3, -56
+        nop
+        popq    %rbx
+        popq    %r12
+        popq    %r13
+        popq    %r14
+        popq    %r15
+        popq    %rbp
+        .cfi_def_cfa 7, 8
+        ret
+        .cfi_endproc
diff --git a/gas/testsuite/gas/cfi-ctf/cfi-ctf.exp b/gas/testsuite/gas/cfi-ctf/cfi-ctf.exp
new file mode 100644
index 00000000000..8f49c9a9cc3
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/cfi-ctf.exp
@@ -0,0 +1,58 @@
+# Copyright (C) 2022 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+
+if { ![is_elf_format] } then {
+    return
+}
+
+proc gas_x86_64_check { } {
+    global NM
+    global NMFLAGS
+
+    set status [gas_host_run "$NM $NMFLAGS --help" ""]
+    return [regexp "targets:.*x86-64" [lindex $status 1]];
+}
+
+if  { [istarget "x86_64-*-*"] || [istarget "aarch64-*-*"] } then {
+
+    global ASFLAGS
+    set old_ASFLAGS "$ASFLAGS"
+
+    run_dump_test "cfi-ctf-common-1"
+    run_dump_test "cfi-ctf-common-2"
+    run_dump_test "cfi-ctf-common-3"
+    run_dump_test "cfi-ctf-common-4"
+    run_dump_test "cfi-ctf-common-5"
+    run_dump_test "cfi-ctf-common-6"
+    run_dump_test "cfi-ctf-common-7"
+    run_dump_test "cfi-ctf-common-8"
+
+    run_dump_test "common-empty-1"
+    run_dump_test "common-empty-2"
+    run_dump_test "common-empty-3"
+    run_dump_test "common-empty-4"
+
+    if { [gas_x86_64_check] } then {
+	set ASFLAGS "$ASFLAGS --64"
+	run_dump_test "cfi-ctf-x86_64-1"
+	set ASFLAGS "$old_ASFLAGS"
+    }
+}
+
+# aarch64 specific tests
+if { [istarget "aarch64-*-*"] } then {
+    run_dump_test "cfi-ctf-aarch64-1"
+}
diff --git a/gas/testsuite/gas/cfi-ctf/common-empty-1.d b/gas/testsuite/gas/cfi-ctf/common-empty-1.d
new file mode 100644
index 00000000000..c49aac436c2
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/common-empty-1.d
@@ -0,0 +1,14 @@
+#as: --gctf-frame
+#objdump: --ctf-frame=.ctf_frame
+#name: Uninteresting cfi directives generate an empty CTF Frame section
+#...
+Contents of the CTF Frame section .ctf_frame:
+
+  Header :
+
+    Version: CTF_FRAME_VERSION_1
+    Flags: NONE
+    Num FDEs: 0
+    Num FREs: 0
+
+#pass
diff --git a/gas/testsuite/gas/cfi-ctf/common-empty-1.s b/gas/testsuite/gas/cfi-ctf/common-empty-1.s
new file mode 100644
index 00000000000..91303df269b
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/common-empty-1.s
@@ -0,0 +1,5 @@
+      .cfi_sections .ctf_frame
+      .cfi_startproc
+      .cfi_remember_state
+      .cfi_restore_state
+      .cfi_endproc
diff --git a/gas/testsuite/gas/cfi-ctf/common-empty-2.d b/gas/testsuite/gas/cfi-ctf/common-empty-2.d
new file mode 100644
index 00000000000..e6611568411
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/common-empty-2.d
@@ -0,0 +1,14 @@
+#as: --gctf-frame
+#objdump: --ctf-frame=.ctf_frame
+#name: CTF Frame supports only FP/SP based CFA
+#...
+Contents of the CTF Frame section .ctf_frame:
+
+  Header :
+
+    Version: CTF_FRAME_VERSION_1
+    Flags: NONE
+    Num FDEs: 0
+    Num FREs: 0
+
+#pass
diff --git a/gas/testsuite/gas/cfi-ctf/common-empty-2.s b/gas/testsuite/gas/cfi-ctf/common-empty-2.s
new file mode 100644
index 00000000000..82e0b077b7a
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/common-empty-2.s
@@ -0,0 +1,8 @@
+## CFA register is not defined to be SP/FP.
+## No CTF Frame unwind info for this function will be generated.
+	.cfi_startproc simple
+	.long 0
+	.long 0
+	.cfi_adjust_cfa_offset 16
+	.long 0
+	.cfi_endproc
diff --git a/gas/testsuite/gas/cfi-ctf/common-empty-3.d b/gas/testsuite/gas/cfi-ctf/common-empty-3.d
new file mode 100644
index 00000000000..9dc3d7bee8a
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/common-empty-3.d
@@ -0,0 +1,14 @@
+#as: --gctf-frame
+#objdump: --ctf-frame=.ctf_frame
+#name: CTF Frame supports only default return column
+#...
+Contents of the CTF Frame section .ctf_frame:
+
+  Header :
+
+    Version: CTF_FRAME_VERSION_1
+    Flags: NONE
+    Num FDEs: 0
+    Num FREs: 0
+
+#pass
diff --git a/gas/testsuite/gas/cfi-ctf/common-empty-3.s b/gas/testsuite/gas/cfi-ctf/common-empty-3.s
new file mode 100644
index 00000000000..5cf164a7e3e
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/common-empty-3.s
@@ -0,0 +1,9 @@
+## The return column is not the default value.
+## No CTF Frame unwind info for this function will be generated.
+	.cfi_startproc
+	.cfi_return_column 0
+	.long 0
+	.long 0
+	.cfi_adjust_cfa_offset 16
+	.long 0
+	.cfi_endproc
diff --git a/gas/testsuite/gas/cfi-ctf/common-empty-4.d b/gas/testsuite/gas/cfi-ctf/common-empty-4.d
new file mode 100644
index 00000000000..9dc3d7bee8a
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/common-empty-4.d
@@ -0,0 +1,14 @@
+#as: --gctf-frame
+#objdump: --ctf-frame=.ctf_frame
+#name: CTF Frame supports only default return column
+#...
+Contents of the CTF Frame section .ctf_frame:
+
+  Header :
+
+    Version: CTF_FRAME_VERSION_1
+    Flags: NONE
+    Num FDEs: 0
+    Num FREs: 0
+
+#pass
diff --git a/gas/testsuite/gas/cfi-ctf/common-empty-4.s b/gas/testsuite/gas/cfi-ctf/common-empty-4.s
new file mode 100644
index 00000000000..8d80d67da4d
--- /dev/null
+++ b/gas/testsuite/gas/cfi-ctf/common-empty-4.s
@@ -0,0 +1,18 @@
+## ARMv8.3 addded support a new security feature named Pointer Authentication. The
+## main idea behind this is to use the unused bits in the pointer values.
+## Each pointer is patched with a PAC before writing to memory, and is verified
+## before using it.
+## When the pointers are mangled, the unwinder needs to know so it can mask off
+## the PAC from the pointer value to recover the return address, and
+## conversely, skip doing so if the pointers are not mangled.
+## 
+## .cfi_negate_ra_state CFI directive is used to convey this information.
+##
+## CTF Frame does not have any means to represent this information at this time.
+	.cfi_startproc
+	.long 0
+	.cfi_def_cfa_offset 16
+	.cfi_negate_ra_state
+	.long 0
+	.cfi_endproc
+
diff --git a/gas/write.c b/gas/write.c
index 20f5ce24d5f..90656ab620c 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_ctf_frame:
+      ctf_frame_convert_frag (fragP);
+      break;
+
     case rs_machine_dependent:
       md_convert_frag (stdoutput, sec, fragP);
 
@@ -2788,6 +2792,11 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
 	  address += dwarf2dbg_estimate_size_before_relax (fragP);
 	  break;
 
+	case rs_ctf_frame:
+	  /* Initial estimate can be set to atleast 1 byte.  */
+	  address += ctf_frame_estimate_size_before_relax (fragP);
+	  break;
+
 	default:
 	  BAD_CASE (fragP->fr_type);
 	  break;
@@ -3131,6 +3140,10 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
 		growth = dwarf2dbg_relax_frag (fragP);
 		break;
 
+	      case rs_ctf_frame:
+		growth = ctf_frame_relax_frag (fragP);
+		break;
+
 	      default:
 		BAD_CASE (fragP->fr_type);
 		break;
-- 
2.37.1



More information about the Binutils mailing list