public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* [PATCH,RFC 0/7] Definition and Implementation of CTF Frame format
@ 2022-05-07  0:52 Indu Bhagat
  2022-05-07  0:52 ` [PATCH,RFC 1/7] ctf-frame.h: Add CTF Frame format definition Indu Bhagat
                   ` (7 more replies)
  0 siblings, 8 replies; 11+ messages in thread
From: Indu Bhagat @ 2022-05-07  0:52 UTC (permalink / raw)
  To: binutils

Hello folks,

This patch series is to invite comments, feedback and discussion on the CTF
Frame format.

At the GNU Tools Track at LPC 2021 
(https://linuxplumbersconf.org/event/11/contributions/1003/), it was proposed
to create a lightweight and compact unwind format intended to be kept in
binaries and to be used by online debugging tools like stack unwinders.  We are
calling this format "CTF Frame".  Some notes around the motivation and use case
of the CTF Frame format were previously also discussed here
https://sourceware.org/pipermail/binutils/2021-December/118880.html.

This patch series contains:
- The definition of the CTF Frame format.  This is the binary representation of
  what does in the .ctf_frame section and is generated using the CFI assembler
  directives.
- A little and simple library that provides encoders and decoders to write and
  read the CTF Frame binary data - libctfframe.
- Implementation in assembler and linker to create and merge .ctf_frame
  sections.
- Support for CTF Frame in readelf and objdump.
- A very simple example online unwinder that is based in CTF Frame.  For
  simplicity it provides the same interface as the backtrace(3) glibc call.
- For completeness, build system changes for gdb/ and sim/.  These changes are
  necessary to accommodate the newly created libctfframe library.

CTF Frame format
----------------
CTF Frame format provides basic unwind information for programs and libraries
via a .ctf_frame section. The .ctf_frame section is an allocatable, loadable
data section in a segment of its own (new segment introduced as
PT_GNU_CTF_FRAME) in ELF linked binaries. The CTF Frame format specifies the
minimal necessary unwind information, i.e., information needed to recover only
the CFA and the return address (RA) for all insns of a program.  This
information is a subset of what .eh_frame can convey: .eh_frame specifies how
to resurrect all callee-saved registers, if need be.

CTF Frame format is meant to be independent of the CTF type format: a
.ctf_frame section can have existence independent of the .ctf section. Type
information of the sort provided by CTF is not needed to unwind stack frames,
so CTF Frame is totally independent from CTF. The term "CTF" in the name,
however, is a deliberate choice as the CTF Frame format aligns well to the core
principles of the CTF debug format: compact and simple. Further, in future,
there will be support to gather the C types and the original value of function
arguments for generating more meaninful backtraces.  Some of this information
will come from the .ctf section.  But the unwind information in .ctf_frame
section will remain usable with or without a .ctf section.  In other words,
that future functionality will rely on additional section(s) that can establish
the relationship between unwind info in .ctf_frame section with the type info
in the .ctf section.

A CTF Frame section consists of a CTF Frame header, a list of CTF FDEs (CTF
Frame Description Entries) and a list of CTF FREs (CTF Frame Row Entries). A
CTF FRE serves a set of consecutive PCs, all of which have the same unwind
information. Together with the associated CTF FDE, each CTF FRE forms a 
self-sufficient record to recover the CFA and RA of the set of PCs served by
the CTF FRE. For more details on the CTF Frame format, please see the
ctf-frame.h header file included in this series.  The CTF Frame format supports
x86_64 and aarch64 at this time.

Compared to the size of the unwind information in .eh_frame (plus
.eh_frame_hdr), .ctf_frame sections currently are faring OK.  Here is ratio of
the size of (.ctf_frame) over size of (.eh_frame+.eh_frame_hdr) for x86_64 and
aarch64 for some randomly chosen programs in binutils:

ratio = (.ctf_frame / (.eh_frame+.eh_frame_hdr))

---------------------------------------------------
program    |  [x86_64] ratio  |  [aarch64] ratio
---------------------------------------------------
addr2line  |       1.13       |    0.67
ar         |       1.03       |    0.70
as         |       1.00       |    0.73
c++filt    |       1.08       |    0.64
elfedit    |       1.03       |    0.66
gprof      |       1.06       |    0.71
ld         |       1.02       |    0.73
nm         |       1.07       |    0.69
objcopy    |       1.08       |    0.72
objdump    |       1.08       |    0.73
size       |       1.10       |    0.67
strings    |       1.09       |    0.67

On an average, for x86_64, the .ctf_frame sections are about 6% larger than the
.eh_frame* sections.  There exist ways to improve .ctf_frame sizes on x86_64,
which have not been explored in depth yet.  One of the causes of the bloat in
.ctf_frame sections is known to be the unwind information for PLT entries.  The
.eh_frame unwind information for PLT section on x86_64 is particularly compact
as it can be encoded using a DWARF expression which evaluates
"rsp + 8 + ((((rip & 15)>= 11)? 1 : 0)<< 3)" for each value of rip in the PLT
section at run-time.  The CTF Frame format does not support expressions at all,
causing a bloat because subsequent PCs in the PLT sections have different unwind
rules, landing each PC of the PLT entry an entry of its own in the CTF Frame
section (each PC hence has a distinct CTF Frame row entry).  This issue can be
resolved by incorporating some more careful design ideas to compactly encode
information in the CTF Frame Row Entries, such that it exploits the "regular
pattern" in the unwind information for the PLT entries.

High-level implementation notes
-------------------------------
Creation and Linking:
The creation of .ctf_frame section is the onus of the assembler which creates
the section by using the .cfi_* directives embedded by the compiler.  If
.ctf_frame section is to be created, the compiler must emit a ".ctf_frame" in
the .cfi_sections directive.  Because the CTF Frame format does not support
complex expressions, those functions whose .cfi_ directives specify complex
expressions are skipped.  In practice, DWARF CFA expressions are not very
prevalent for CFA, RA recovery.

At link-time, the linker merges the input .ctf_sections by combining the
contents and sorting the FDEs on the start PC.  This helps an unwinder retrieve
the relevant unwind information quickly.

The libctfframe library:
The linker uses a library called libctfframe which provides the basic
functionality needed to decode a .ctf_frame section, and also to encode and
eventually write out the .ctf_frame in its binary format.

Unwinder:
A basic unwinder based on CTF Frame format is relatively simple to write.  We
are providing one as an example and proof of concept.

Testing
-------
Testing on x86_64 and aarch64 looks promising so far - basic unwinding is
working. This patch series is work in progress.  There still remain open issues
to be resolved,  crude code stubs to be revisited and addition of testsuites.
This will be taken care of in subsequent iterations of the series.
 
Please comment and provide feedback, it will help shape the format.  Here are a
few of the aspects that particularly need discussion:

1. What is a good place for an unwinder based on CTF Frame format ? Currently
to facilitate discussion, it is presented in a library of its own:
libctfbacktrace which, in turn, uses the libctfframe library for decoding the
.ctf_frame section for unwinding.  We brainstormed a bit about the possible
candidates being libbacktace, libgcc or libunwind ? Are there any
recommendations ?
2. Are there some ideas for smartly dealing with the issue of bloat caused by
the CTF Frame unwind information for the PLT entries ?
 
Thanks,

Indu Bhagat (5):
  ctf-frame.h: Add CTF Frame format definition
  gas: generate .ctf_frame
  bfd: linker: merge .ctf_frame sections
  readelf/objdump: support for CTF Frame section
  gdb: sim: buildsystem changes to accommodate libctfframe

Weimin Pan (2):
  libctfframe: add the CTF Frame library
  unwinder: generate backtrace using CTF Frame format

 Makefile.def                       |     5 +
 Makefile.in                        |  1289 ++-
 bfd/Makefile.am                    |     6 +-
 bfd/Makefile.in                    |     7 +-
 bfd/bfd-in2.h                      |     1 +
 bfd/configure                      |     2 +-
 bfd/configure.ac                   |     2 +-
 bfd/elf-bfd.h                      |    55 +
 bfd/elf-ctf-frame.c                |   490 +
 bfd/elf.c                          |    31 +
 bfd/elf64-x86-64.c                 |    97 +-
 bfd/elflink.c                      |    52 +
 bfd/elfxx-x86.c                    |   303 +-
 bfd/elfxx-x86.h                    |    46 +
 bfd/section.c                      |     1 +
 binutils/Makefile.am               |    10 +-
 binutils/Makefile.in               |    10 +-
 binutils/doc/binutils.texi         |     4 +
 binutils/doc/ctfframe.options.texi |    10 +
 binutils/objdump.c                 |    74 +
 binutils/readelf.c                 |    44 +
 configure                          |     2 +-
 configure.ac                       |     2 +-
 gas/Makefile.am                    |     3 +
 gas/Makefile.in                    |    22 +-
 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/ctffreopt.c                    |   158 +
 gas/dw2gencfi.c                    |    30 +-
 gas/dw2gencfi.h                    |     1 +
 gas/gen-ctf-frame.c                |  1188 +++
 gas/gen-ctf-frame.h                |   142 +
 gas/write.c                        |    13 +
 gdb/Makefile.in                    |     8 +-
 gdb/acinclude.m4                   |     4 +-
 gdb/configure                      |    35 +-
 gdb/configure.ac                   |    11 +
 include/ctf-backtrace-api.h        |    57 +
 include/ctf-frame-api.h            |   213 +
 include/ctf-frame.h                |   257 +
 include/elf/common.h               |     1 +
 include/elf/internal.h             |     1 +
 ld/ld.texi                         |     4 +-
 ld/scripttempl/elf.sc              |     2 +
 libctfframe/Makefile.am            |    41 +
 libctfframe/Makefile.in            |   966 ++
 libctfframe/aclocal.m4             |  1241 +++
 libctfframe/config.h.in            |   144 +
 libctfframe/configure              | 15118 +++++++++++++++++++++++++++
 libctfframe/configure.ac           |    75 +
 libctfframe/ctf-backtrace-err.c    |    46 +
 libctfframe/ctf-backtrace.c        |   617 ++
 libctfframe/ctf-frame-dump.c       |   162 +
 libctfframe/ctf-frame-error.c      |    49 +
 libctfframe/ctf-frame-impl.h       |    55 +
 libctfframe/ctf-frame.c            |  1515 +++
 libctfframe/ttest.c                |    78 +
 sim/common/Make-common.in          |     7 +-
 62 files changed, 24914 insertions(+), 47 deletions(-)
 create mode 100644 bfd/elf-ctf-frame.c
 create mode 100644 binutils/doc/ctfframe.options.texi
 create mode 100644 gas/ctffreopt.c
 create mode 100644 gas/gen-ctf-frame.c
 create mode 100644 gas/gen-ctf-frame.h
 create mode 100644 include/ctf-backtrace-api.h
 create mode 100644 include/ctf-frame-api.h
 create mode 100644 include/ctf-frame.h
 create mode 100644 libctfframe/Makefile.am
 create mode 100644 libctfframe/Makefile.in
 create mode 100644 libctfframe/aclocal.m4
 create mode 100644 libctfframe/config.h.in
 create mode 100755 libctfframe/configure
 create mode 100644 libctfframe/configure.ac
 create mode 100644 libctfframe/ctf-backtrace-err.c
 create mode 100644 libctfframe/ctf-backtrace.c
 create mode 100644 libctfframe/ctf-frame-dump.c
 create mode 100644 libctfframe/ctf-frame-error.c
 create mode 100644 libctfframe/ctf-frame-impl.h
 create mode 100644 libctfframe/ctf-frame.c
 create mode 100644 libctfframe/ttest.c

-- 
2.31.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH,RFC 1/7] ctf-frame.h: Add CTF Frame format definition
  2022-05-07  0:52 [PATCH,RFC 0/7] Definition and Implementation of CTF Frame format Indu Bhagat
@ 2022-05-07  0:52 ` Indu Bhagat
  2022-05-07  0:52 ` [PATCH,RFC 2/7] gas: generate .ctf_frame Indu Bhagat
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Indu Bhagat @ 2022-05-07  0:52 UTC (permalink / raw)
  To: binutils

The header ctf-frame.h defines the CTF Frame format.

The CTF Frame format is a simple, compact format for representing
unwind information.  This information can be used for generating
backtraces.  The current version supports AMD64 and AARCH64.

More details of the CTF Frame format are included in the documentation
of the header file in this patch.

include/ChangeLog:
	* ctf-frame.h: New file.
---
 include/ctf-frame.h | 257 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 257 insertions(+)
 create mode 100644 include/ctf-frame.h

diff --git a/include/ctf-frame.h b/include/ctf-frame.h
new file mode 100644
index 00000000000..abc22d133e0
--- /dev/null
+++ b/include/ctf-frame.h
@@ -0,0 +1,257 @@
+/* CTF frame format description.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+
+   This file is part of libctfframe.
+
+   libctfframe 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.
+
+   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; see the file COPYING.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef	_CTF_FRAME_H
+#define	_CTF_FRAME_H
+
+#include <sys/types.h>
+#include <limits.h>
+#include <stdint.h>
+
+
+#ifdef	__cplusplus
+extern "C"
+{
+#endif
+
+/* CTF frame format.
+
+   CTF frame format can be used to compactly represent the information needed
+   for virtual stack unwinding.  CTF frame format keeps track of the minimal
+   necessary information needed for stack unwinding:
+     - Canonical Frame Address (CFA)
+     - Frame Pointer (FP)
+     - Return Address (RA)
+
+   The CTF frame section itself has the following structure:
+
+       +--------+------------+---------+
+       |  file  |  function  | frame   |
+       | header | descriptor |  row    |
+       |        |   entries  | entries |
+       +--------+------------+---------+
+
+   The file header stores a magic number and version information, flags, and
+   the byte offset of each of the sections relative to the end of the header
+   itself.  The file header also specifies the total number of function
+   descriptor entries, frame row entries and length of the FRE sub-section.
+
+   Following the header is a list of Function Descriptor Entries (FDEs).
+   This list may be sorted if the flags in the file header indicate it to be
+   so.  The sort order, if applicable, is the order of functions in the
+   .text.* sections in the resulting binary artifact.  Each function
+   descriptor entry specifies the start PC of a function, the size in bytes
+   of the function and an offset to its first Frame Row Entry (FRE).  Each FDE
+   additionally also specifies the type of FRE it uses to encode the unwind
+   information.
+
+   Next, the frame row entries section is a list of variable size records,
+   each of which represent unwind information for a set of PCs. A singular
+   frame row entry is a self-sufficient record with information on how to
+   virtually unwind the stack for the applicable set of PCs.
+
+   */
+
+
+/* CTF frame format versions.  */
+#define CTF_FRAME_VERSION_1	1
+/* CTF frame magic number.  */
+#define CTF_FRAME_MAGIC		0xdee2
+/* Current version of CTF frame format.  */
+#define CTF_FRAME_VERSION	CTF_FRAME_VERSION_1
+
+/* Various flags for CTF frame.  */
+
+/* Function descriptor entries are sorted on PC.  */
+#define CTF_FRAME_F_FDE_SORTED	0x1
+/* Frame-pointer based unwinding.  */
+#define CTF_FRAME_F_FRAME_POINTER 0x2
+
+#define CTF_FRAME_CFA_FIXED_FP_INVALID 0
+#define CTF_FRAME_CFA_FIXED_RA_INVALID 0
+
+/* Supported ABIs/Arch.  */
+#define CTF_FRAME_ABI_AARCH64_ENDIAN_BIG      1 /* AARCH64 little endian.  */
+#define CTF_FRAME_ABI_AARCH64_ENDIAN_LITTLE   2 /* AARCH64 big endian.  */
+#define CTF_FRAME_ABI_AMD64_ENDIAN_LITTLE     3 /* AMD64 little endian.  */
+
+/* CTF frame FRE types.  */
+#define CTF_FRAME_ROW_ENTRY_TYPE_ADDR1  1
+#define CTF_FRAME_ROW_ENTRY_TYPE_ADDR2  2
+#define CTF_FRAME_ROW_ENTRY_TYPE_ADDR4	3
+
+typedef struct ctf_frame_preamble
+{
+  unsigned short ctfp_magic;	/* Magic number (CTF_FRAME_MAGIC).  */
+  unsigned char ctfp_version;	/* Data format version number (CTF_FRAME_VERSION).  */
+  unsigned char ctfp_flags;	/* Flags */
+} __attribute__ ((packed)) ctf_frame_preamble;
+
+typedef struct ctf_frame_header
+{
+  ctf_frame_preamble cth_frame_preamble;
+  /* Information about the arch (endianness) and ABI.  */
+  unsigned char cth_frame_abi_arch;
+  /* Offset for the Frame Pointer (FP) from CFA may be fixed for some
+     ABIs (e.g, in AMD64 when -fno-omit-frame-pointer is used).  When fixed,
+     this field specifies the fixed stack frame offset and the individual
+     FREs do not need to track it. When not fixed, it is set to
+     CTF_FRAME_CFA_FIXED_FP_INVALID, and the individual FREs may provide
+     the applicable stack frame offset, if any.  */
+  int8_t cth_cfa_fixed_fp_offset;
+  /* Offset for the Return Address from CFA is fixed for some ABIs
+     (e.g., AMD64 has it as CFA-8).  When fixed, the header specifies the
+     fixed stack frame offset and the individual FREs do not track it.  When
+     not fixed, it is set to CTF_FRAME_CFA_FIXED_RA_INVALID, and individual
+     FREs provide the applicable stack frame offset, if any.  */
+  int8_t cth_cfa_fixed_ra_offset;
+  /* Number of CTF FDEs in this CTF frame section.  May be useful for
+     debugging.  */
+  uint32_t cth_num_fdes;
+  /* Number of CTF frame row entries.  */
+  uint32_t cth_num_fres;
+  /* Number of bytes in the CTF frame row entry section. */
+  uint32_t cth_fre_len;
+  /* Offset of CTF function descriptor entry section.  */
+  uint32_t cth_fdeoff;
+  /* Offset of CTF frame row entry section.  */
+  uint32_t cth_freoff;
+} __attribute__ ((packed)) ctf_frame_header;
+
+typedef struct ctf_frame_func_desc_entry
+{
+  /* Function start address.  Encoded as a signed offset, relative to the
+     beginning of the current FDE.  */
+  int32_t ctf_func_start_address;
+  /* Size of the function in bytes.  */
+  uint32_t ctf_func_size;
+  /* Offset of the first CTF frame row entry of the function, relative to the
+     beginning of the CTF frame row entry sub-section.  */
+  uint32_t ctf_func_start_fre_off;
+  /* Number of frame row entries for the function.  */
+  uint32_t ctf_func_num_fres;
+  /* Additional information for deciphering the unwind information for the
+     function.
+     - 4-bits: encoding type of the FREs for the function.
+     - 4-bits: unused.
+     --------------------------------
+     |     Unused    |   FRE type   |
+     --------------------------------
+     7               4              0     */
+  unsigned char ctf_func_info;
+} __attribute__ ((packed)) ctf_frame_func_desc_entry;
+
+/* Macros to compose and decompose function info in FDE.  */
+
+#define CTF_FRAME_V1_FUNC_INFO(fre_enc_type) \
+  ((fre_enc_type))
+
+#define CTF_FRAME_V1_FUNC_FRE_TYPE(data)	  ((data) & 0xf)
+
+/* Size of stack frame offsets in a CTF frame row entry.  A single CTF frame
+   row entry has all offsets of the same size.  Offset size may vary across
+   frame row entries.  */
+#define CTF_FRAME_FRE_OFFSET_1B	  0
+#define CTF_FRAME_FRE_OFFSET_2B	  1
+#define CTF_FRAME_FRE_OFFSET_4B	  2
+
+/* A CTF frame row entry can be SP or FP based.  */
+#define CTF_FRAME_BASE_REG_FP	0
+#define CTF_FRAME_BASE_REG_SP	1
+
+/* The index at which a specific offset is presented in the variable length
+   bytes of an FRE.  */
+#define CTF_FRAME_FRE_CFA_OFFSET_IDX  0
+#define CTF_FRAME_FRE_FP_OFFSET_IDX   1
+#define CTF_FRAME_FRE_RA_OFFSET_IDX   2
+
+typedef struct ctf_frame_fre_info
+{
+  /* Information about
+     - 1 bit: base reg for CFA
+     - 4 bits: Number of offsets (N).  A value of upto 3 is allowed to track
+     all three of CFA, FP and RA (fixed implicit order).
+     - 2 bits: information about size of the offsets (S) in bytes.
+     Valid values are CTF_FRAME_FRE_OFFSET_1B, CTF_FRAME_FRE_OFFSET_2B,
+     CTF_FRAME_FRE_OFFSET_4B
+     - 1 bit: Unused.
+     -----------------------------------------------------------------------
+     |  Unused  |  Size of offsets   |   Number of offsets    |   base_reg |
+     -----------------------------------------------------------------------
+     8          7                    5                        1            0
+
+     */
+  unsigned char fre_info;
+} ctf_frame_fre_info;
+
+/* Macros to compose and decompose FRE info.  */
+
+#define CTF_FRAME_V1_FRE_INFO(base_reg_id, offset_num, offset_size) \
+  ((offset_size << 5) | (offset_num << 1) | (base_reg_id))
+
+#define CTF_FRAME_V1_FRE_CFA_BASE_REG_ID(data)	  ((data) & 0x1)
+#define CTF_FRAME_V1_FRE_OFFSET_COUNT(data)	  (((data) >> 1) & 0xf)
+#define CTF_FRAME_V1_FRE_OFFSET_SIZE(data)	  (((data) >> 5) & 0x3)
+
+/* CTF frame row entry definitions.
+
+   Used for both AMD64 and AARCH64.
+
+   A frame row entry is a self-sufficient record containing CTF unwind
+   info for a range of addresses, starting at the specified offset in
+   the function. Each frame row entry is is followed by S*N bytes,
+   where:
+     S is the size of the stack frame offset for the FRE, and
+     N is the number of stack frame offsets in the FRE
+
+   The offsets are interpreted in order as follows:
+   offset1 [interpreted as CFA = BASE_REG + offset1]
+   offset2 [interpreted as FP = CFA + offset2]
+   offset3 [interpreted as RA = CFA + offset3]
+*/
+
+typedef struct ctf_frame_row_entry_addr1
+{
+  /* Start address of the frame row entry.  Encoded as an 1-byte unsigned
+     offset, relative to the start address of the function.  */
+  uint8_t ctf_fre_start_address;
+  ctf_frame_fre_info ctf_fre_info;
+} __attribute__ ((packed)) ctf_frame_row_entry_addr1;
+
+typedef struct ctf_frame_row_entry_addr2
+{
+  /* Start address of the frame row entry.  Encoded as an 2-byte unsigned
+     offset, relative to the start address of the function.  */
+  uint16_t ctf_fre_start_address;
+  ctf_frame_fre_info ctf_fre_info;
+} __attribute__ ((packed)) ctf_frame_row_entry_addr2;
+
+typedef struct ctf_frame_row_entry_addr4
+{
+  /* Start address of the frame row entry.  Encoded as a 4-byte unsigned
+     offset, relative to the start address of the function.  */
+  uint32_t ctf_fre_start_address;
+  ctf_frame_fre_info ctf_fre_info;
+} __attribute__ ((packed)) ctf_frame_row_entry_addr4;
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif				/* _CTF_FRAME_H */
-- 
2.31.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH,RFC 2/7] gas: generate .ctf_frame
  2022-05-07  0:52 [PATCH,RFC 0/7] Definition and Implementation of CTF Frame format Indu Bhagat
  2022-05-07  0:52 ` [PATCH,RFC 1/7] ctf-frame.h: Add CTF Frame format definition Indu Bhagat
@ 2022-05-07  0:52 ` Indu Bhagat
  2022-05-07  0:52 ` [PATCH,RFC 4/7] bfd: linker: merge .ctf_frame sections Indu Bhagat
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Indu Bhagat @ 2022-05-07  0:52 UTC (permalink / raw)
  To: binutils

[PS: This commit will be adapted as noted in the code comments once the
compiler has been adapted to generate .cfi_sections with .ctf_frame
specified in it.  gas will then create .ctf_frame section only when
specified by the user via appropriate compiler options. ]

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 needed to be processed for
.ctf_frame generation, and are skipped:
- .cfi_personality*
- .cfi_*lsda
- .cfi_escape
- ...

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 ctffreopt.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 (aarch64_ctf_frame_cfa_sp_reg): New
	declaration.
	(aarch64_ctf_frame_cfa_fp_reg): Likewise.
	(aarch64_ctf_frame_cfa_ra_reg): Likewise.
	(aarch64_support_ctf_frame_p): New definition.
	(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/ctffreopt.c: New file.
	* gas/gen-ctf-frame.c: New file.
	* gas/gen-ctf-frame.h: New file.
---
 gas/Makefile.am         |    3 +
 gas/Makefile.in         |   22 +-
 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/ctffreopt.c         |  158 ++++++
 gas/dw2gencfi.c         |   30 +-
 gas/dw2gencfi.h         |    1 +
 gas/gen-ctf-frame.c     | 1188 +++++++++++++++++++++++++++++++++++++++
 gas/gen-ctf-frame.h     |  142 +++++
 gas/write.c             |   13 +
 14 files changed, 1701 insertions(+), 10 deletions(-)
 create mode 100644 gas/ctffreopt.c
 create mode 100644 gas/gen-ctf-frame.c
 create mode 100644 gas/gen-ctf-frame.h

diff --git a/gas/Makefile.am b/gas/Makefile.am
index 7159be56a08..b84a6adca7e 100644
--- a/gas/Makefile.am
+++ b/gas/Makefile.am
@@ -70,6 +70,8 @@ GAS_CFILES = \
 	atof-generic.c \
 	compress-debug.c \
 	cond.c \
+	ctffreopt.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 67dac53f68c..b00b02ccc15 100644
--- a/gas/Makefile.in
+++ b/gas/Makefile.in
@@ -160,14 +160,15 @@ 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) ctffreopt.$(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 +550,8 @@ GAS_CFILES = \
 	atof-generic.c \
 	compress-debug.c \
 	cond.c \
+	ctffreopt.c \
+	gen-ctf-frame.c \
 	depend.c \
 	dwarf2dbg.c \
 	dw2gencfi.c \
@@ -583,6 +586,7 @@ HFILES = \
 	bit_fix.h \
 	cgen.h \
 	compress-debug.h \
+	gen-ctf-frame.h \
 	dwarf2dbg.h \
 	dw2gencfi.h \
 	ecoff.h \
@@ -1289,6 +1293,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)/ctffreopt.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@
@@ -1299,6 +1304,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 135abc8f23d..f98024154c0 100644
--- a/gas/as.h
+++ b/gas/as.h
@@ -253,7 +253,10 @@ enum _relax_state
   rs_cfa,
 
   /* Cross-fragment dwarf2 line number optimization.  */
-  rs_dwarf2dbg
+  rs_dwarf2dbg,
+
+  /* CTF frame FRE start address size encoding optimization.  */
+  rs_ctf_fre
 };
 
 typedef enum _relax_state relax_stateT;
@@ -510,6 +513,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 dee7d19d94a..ea57fa5d4d1 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 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
@@ -8372,6 +8378,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.  */
@@ -9634,6 +9670,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..c72d8d77165 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 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 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 61b9af1c24b..77c932b3bc3 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>
@@ -594,6 +595,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 unwind info.  */
+unsigned int x86_ctf_frame_cfa_fp_reg;
+unsigned int x86_ctf_frame_cfa_ra_reg;
+
 #endif
 
 /* 1 for intel syntax,
@@ -3169,6 +3176,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
     {
@@ -9199,6 +9210,41 @@ x86_cleanup (void)
   if (seg && subseg)
     subseg_set (seg, subseg);
 }
+
+bool
+x86_support_ctf_frame_p (void)
+{
+  /* At this time, CTF 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 d7c6b33c778..928915252dd 100644
--- a/gas/config/tc-i386.h
+++ b/gas/config/tc-i386.h
@@ -356,6 +356,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 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 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 3e3deb5dd36..3108b6c9ec2 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_fre:
       /* No further adjustments needed.  */
       break;
     case rs_machine_dependent:
diff --git a/gas/ctffreopt.c b/gas/ctffreopt.c
new file mode 100644
index 00000000000..118c1ad40bd
--- /dev/null
+++ b/gas/ctffreopt.c
@@ -0,0 +1,158 @@
+/* ctffreopt.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_fre 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_fre 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_fre 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/dw2gencfi.c b/gas/dw2gencfi.c
index 961a3b92188..3fde8229d3d 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
 
@@ -1235,6 +1236,8 @@ dot_cfi_sections (int ignored ATTRIBUTE_UNUSED)
 	else if (strcmp (name, tc_cfi_section_name) == 0)
 	  sections |= CFI_EMIT_target;
 #endif
+	else if (strcmp (name, ".ctf_frame"))
+	    sections |= CFI_EMIT_ctf_frame;
 	else
 	  {
 	    *input_line_pointer = c;
@@ -2280,7 +2283,7 @@ void
 cfi_finish (void)
 {
   struct cie_entry *cie, *cie_next;
-  segT cfi_seg, ccseg;
+  segT cfi_seg, ccseg, ctf_frame_seg;
   struct fde_entry *fde;
   struct cfi_insn_data *first;
   int save_flag_traditional_format, seek_next_seg;
@@ -2475,6 +2478,31 @@ 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)
+    {
+#ifdef support_ctf_frame_p
+      if (support_ctf_frame_p ())
+	{
+	  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_bad (_(".ctf_frame not supported for target"));
+
+#else
+	as_bad (_(".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..a09e054a1f6
--- /dev/null
+++ b/gas/gen-ctf-frame.c
@@ -0,0 +1,1188 @@
+/* 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"
+
+/* 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
+
+/* Some fragments generated for CTF frame section are fixed up later.  This
+   optimization (for size) is on by default.  */
+
+#ifndef CTF_FRE_START_ADDR_OPT
+# define CTF_FRE_START_ADDR_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 fre_type)
+{
+  unsigned char func_info;
+  func_info = CTF_FRAME_V1_FUNC_INFO (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 fre_type)
+{
+  return ctf_frame_ver_ops.set_func_info (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_START_ADDR_OPT
+
+/* FIXME add comments.  Cumulative expression = diff OP_absent width.  */
+
+static void
+create_width_expression (expressionS *cexp, symbolS *fre_pc_begin,
+			 symbolS *fde_start_address,
+			 symbolS *fde_end_address)
+{
+  expressionS diff;
+  expressionS width;
+
+  /* diff expression stores the FDE start address offset from the start PC
+     of function.  */
+  diff.X_op = O_subtract;
+  diff.X_add_symbol = fre_pc_begin;
+  diff.X_op_symbol = fde_start_address;
+  diff.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 (&diff);
+  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? */
+
+  /* FRE Start Address.  */
+#if CTF_FRE_START_ADDR_OPT
+  create_width_expression (&exp, ctf_fre->pc_begin, fde_start_addr, fde_end_addr);
+  frag_grow (fre_addr_size);
+  frag_var (rs_ctf_fre, 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);
+
+  /* Function info.  */
+#if CTF_FRE_START_ADDR_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_fre, 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_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);
+  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).  */
+  ctf_fre_set_cfa_offset (cur_fre, cfi_insn->u.i);
+  cur_fre->merge_candidate = false;
+  
+  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_remember_state (struct ctf_frame_xlate_ctx *xlate_ctx)
+{
+  struct ctf_frame_row_entry *last_fre = xlate_ctx->last_fre;
+
+  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_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.  */
+	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;
+
+  /* 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 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.  */
+  gas_assert (xlate_ctx->cur_fre);
+  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.  */
+  gas_assert (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.  */
+      if (ctf_frame_do_fde (xlate_ctx, dw_fde))
+	{
+	  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 unwind information.  */
+  create_ctf_frame_all ();
+
+  if (get_num_ctf_fdes ())
+    output_ctf_frame_internal ();
+}
diff --git a/gas/gen-ctf-frame.h b/gas/gen-ctf-frame.h
new file mode 100644
index 00000000000..42a95866e81
--- /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);
+};
+
+/* Generate CTF 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/write.c b/gas/write.c
index f607562794a..a29fc46190f 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_fre:
+      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_fre:
+	  /* 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_fre:
+		growth = ctf_frame_relax_frag (fragP);
+		break;
+
 	      default:
 		BAD_CASE (fragP->fr_type);
 		break;
-- 
2.31.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH,RFC 4/7] bfd: linker: merge .ctf_frame sections
  2022-05-07  0:52 [PATCH,RFC 0/7] Definition and Implementation of CTF Frame format Indu Bhagat
  2022-05-07  0:52 ` [PATCH,RFC 1/7] ctf-frame.h: Add CTF Frame format definition Indu Bhagat
  2022-05-07  0:52 ` [PATCH,RFC 2/7] gas: generate .ctf_frame Indu Bhagat
@ 2022-05-07  0:52 ` Indu Bhagat
  2022-05-07  0:52 ` [PATCH,RFC 5/7] readelf/objdump: support for CTF Frame section Indu Bhagat
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Indu Bhagat @ 2022-05-07  0:52 UTC (permalink / raw)
  To: binutils

The linker merges all the input .ctf_frame sections.  When merging, the
linker verifies that all the input .ctf_frame sections have the same
abi/arch.

The linker uses libctfframe library to perform key actions on the
.ctf_frame sections - decode, read, and create output data.  This
implies buildsystem changes to make and install libctfframe before
libbfd.

The linker places the output .ctf_frame section in a new segment of its
own: PT_GNU_CTF_FRAME.

When a section is discarded from the final link, the corresponding
entries in the .ctf_frame section for those functions are also deleted.

This patch also adds support for generation of CTF Frame unwind
information for the .plt* sections on x86_64.  CTF Frame unwind info is
generated for IBT enabled PLT, lazy/non-lazy PLT. These bits are arguably
unsatisfactory in terms of code quality and will be improved upon.  Also
recall that the CTF Frame format for PLT entries needs adjustments, so
these chunks around .plt* sections are bound to evolve for the better.

The existing linker option --no-ld-generated-unwind-info has been
adapted to include the control of whether .ctf_frame unwind information
will be generated for the linker generated sections like PLT.

Changes to the linker script have been made as necessary.

ChangeLog:

	* Makefile.def: Add install dependency on libctfframe for libbfd.
	* Makefile.in: Regenerated.
	* bfd/Makefile.am: Add elf-ctf-frame.c
	* bfd/Makefile.in: Regenerated.
	* bfd/bfd-in2.h (SEC_INFO_TYPE_CTF_FRAME): Regenerated.
	* bfd/configure: Regenerate.
	* bfd/configure.ac: Add elf-ctf-frame.lo.
	* bfd/elf-bfd.h (struct ctf_frame_func_bfdinfo): New struct.
	(struct ctf_frame_dec_info): Likewise.
	(struct ctf_frame_enc_info): Likewise.
	(struct elf_link_hash_table): New member for encoded .ctf_frame
	object.
	(struct output_elf_obj_tdata): New member.
	(elf_ctf_frame): New access macro.
	(_bfd_elf_set_section_ctf_frame): New declaration.
	* bfd/elf.c (get_segment_type): Handle new segment
	PT_GNU_CTF_FRAME.
	(bfd_section_from_phdr): Likewise.
	(get_program_header_size): Likewise.
	(_bfd_elf_map_sections_to_segments): Likewise.
	* bfd/elf64-x86-64.c (elf_x86_64_link_setup_gnu_properties): Add
	contents to the .ctf_frame sections or .plt* entries.
	* bfd/elflink.c (elf_section_ignore_discarded_relocs): Handle
	SEC_INFO_TYPE_CTF_FRAME.
	(_bfd_elf_default_action_discarded): Handle .ctf_frame section.
	(elf_link_input_bfd): Merge .ctf_frame section.
	(bfd_elf_final_link): Write the output .ctf_frame section.
	(bfd_elf_discard_info): Handle discarding .ctf_frame section.
	* bfd/elfxx-x86.c (_bfd_x86_elf_size_dynamic_sections): Create
	.ctf_frame section for .plt and .plt.sec.
	(_bfd_x86_elf_finish_dynamic_sections): Handle .ctf_frame from
	.plt* sections.
	* bfd/elfxx-x86.h (PLT_CTF_FRAME_FDE_START_OFFSET): New
	definition.
	(CTF_FRAME_PLT0_MAX_NUM_FRES): Likewise.
	(CTF_FRAME_PLTN_MAX_NUM_FRES): Likewise.
	(struct elf_x86_ctf_frame_plt): New structure.
	(struct elf_x86_link_hash_table): New member.
	(struct elf_x86_init_table): New members for .ctf_frame
	creation.
	* bfd/section.c: Add new definition SEC_INFO_TYPE_CTF_FRAME.
	* binutils/readelf.c (get_segment_type): Handle new segment
	PT_GNU_CTF_FRAME.
	* ld/ld.texi: Update documentation for
	--no-ld-generated-unwind-info.
	* ld/scripttempl/elf.sc: Support .ctf_frame sections.
	* bfd/elf-ctf-frame.c: New file.

include/ChangeLog:

	* elf/common.h (PT_GNU_CTF_FRAME): New definition.
	* elf/internal.h (struct elf_segment_map): Handle new segment
	type PT_GNU_CTF_FRAME.
---
 Makefile.def           |   5 +-
 Makefile.in            |  21 +-
 bfd/Makefile.am        |   6 +-
 bfd/Makefile.in        |   7 +-
 bfd/bfd-in2.h          |   1 +
 bfd/configure          |   2 +-
 bfd/configure.ac       |   2 +-
 bfd/elf-bfd.h          |  55 +++++
 bfd/elf-ctf-frame.c    | 490 +++++++++++++++++++++++++++++++++++++++++
 bfd/elf.c              |  31 +++
 bfd/elf64-x86-64.c     |  97 +++++++-
 bfd/elflink.c          |  52 +++++
 bfd/elfxx-x86.c        | 303 ++++++++++++++++++++++++-
 bfd/elfxx-x86.h        |  46 ++++
 bfd/section.c          |   1 +
 binutils/readelf.c     |   1 +
 include/elf/common.h   |   1 +
 include/elf/internal.h |   1 +
 ld/ld.texi             |   4 +-
 ld/scripttempl/elf.sc  |   2 +
 20 files changed, 1105 insertions(+), 23 deletions(-)
 create mode 100644 bfd/elf-ctf-frame.c

diff --git a/Makefile.def b/Makefile.def
index a63c3966513..2aacfec6c81 100644
--- a/Makefile.def
+++ b/Makefile.def
@@ -458,11 +458,14 @@ dependencies = { module=all-gdbsupport; on=all-gnulib; };
 dependencies = { module=all-gdbsupport; on=all-intl; };
 
 // Host modules specific to binutils.
+// build libctfframe before bfd for encoder/decoder support for linking
+// CTF frame sections
 dependencies = { module=configure-bfd; on=configure-libiberty; hard=true; };
 dependencies = { module=configure-bfd; on=configure-intl; };
 dependencies = { module=all-bfd; on=all-libiberty; };
 dependencies = { module=all-bfd; on=all-intl; };
 dependencies = { module=all-bfd; on=all-zlib; };
+dependencies = { module=all-bfd; on=all-libctfframe; };
 dependencies = { module=configure-opcodes; on=configure-libiberty; hard=true; };
 dependencies = { module=all-opcodes; on=all-libiberty; };
 
@@ -479,7 +482,6 @@ dependencies = { module=all-binutils; on=all-intl; };
 dependencies = { module=all-binutils; on=all-gas; };
 dependencies = { module=all-binutils; on=all-libctf; };
 dependencies = { module=all-ld; on=all-libctf; };
-dependencies = { module=all-binutils; on=all-libctfframe; };
 
 // We put install-opcodes before install-binutils because the installed
 // binutils might be on PATH, and they might need the shared opcodes
@@ -488,6 +490,7 @@ dependencies = { module=install-binutils; on=install-opcodes; };
 dependencies = { module=install-strip-binutils; on=install-strip-opcodes; };
 
 // Likewise for ld, libctf, and bfd.
+dependencies = { module=install-bfd; on=install-libctfframe; };
 dependencies = { module=install-libctf; on=install-bfd; };
 dependencies = { module=install-ld; on=install-bfd; };
 dependencies = { module=install-ld; on=install-libctf; };
diff --git a/Makefile.in b/Makefile.in
index 7b93fd14ffb..08e32e13f24 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -64407,6 +64407,16 @@ all-stagetrain-bfd: maybe-all-stagetrain-zlib
 all-stagefeedback-bfd: maybe-all-stagefeedback-zlib
 all-stageautoprofile-bfd: maybe-all-stageautoprofile-zlib
 all-stageautofeedback-bfd: maybe-all-stageautofeedback-zlib
+all-bfd: maybe-all-libctfframe
+all-stage1-bfd: maybe-all-stage1-libctfframe
+all-stage2-bfd: maybe-all-stage2-libctfframe
+all-stage3-bfd: maybe-all-stage3-libctfframe
+all-stage4-bfd: maybe-all-stage4-libctfframe
+all-stageprofile-bfd: maybe-all-stageprofile-libctfframe
+all-stagetrain-bfd: maybe-all-stagetrain-libctfframe
+all-stagefeedback-bfd: maybe-all-stagefeedback-libctfframe
+all-stageautoprofile-bfd: maybe-all-stageautoprofile-libctfframe
+all-stageautofeedback-bfd: maybe-all-stageautofeedback-libctfframe
 configure-opcodes: configure-libiberty
 configure-stage1-opcodes: configure-stage1-libiberty
 configure-stage2-opcodes: configure-stage2-libiberty
@@ -64527,18 +64537,9 @@ all-stagetrain-ld: maybe-all-stagetrain-libctf
 all-stagefeedback-ld: maybe-all-stagefeedback-libctf
 all-stageautoprofile-ld: maybe-all-stageautoprofile-libctf
 all-stageautofeedback-ld: maybe-all-stageautofeedback-libctf
-all-binutils: maybe-all-libctfframe
-all-stage1-binutils: maybe-all-stage1-libctfframe
-all-stage2-binutils: maybe-all-stage2-libctfframe
-all-stage3-binutils: maybe-all-stage3-libctfframe
-all-stage4-binutils: maybe-all-stage4-libctfframe
-all-stageprofile-binutils: maybe-all-stageprofile-libctfframe
-all-stagetrain-binutils: maybe-all-stagetrain-libctfframe
-all-stagefeedback-binutils: maybe-all-stagefeedback-libctfframe
-all-stageautoprofile-binutils: maybe-all-stageautoprofile-libctfframe
-all-stageautofeedback-binutils: maybe-all-stageautofeedback-libctfframe
 install-binutils: maybe-install-opcodes
 install-strip-binutils: maybe-install-strip-opcodes
+install-bfd: maybe-install-libctfframe
 install-libctf: maybe-install-bfd
 install-ld: maybe-install-bfd
 install-ld: maybe-install-libctf
diff --git a/bfd/Makefile.am b/bfd/Makefile.am
index f2f70590e88..0ba216d4580 100644
--- a/bfd/Makefile.am
+++ b/bfd/Makefile.am
@@ -288,6 +288,7 @@ BFD32_BACKENDS = \
 	ecofflink.lo \
 	elf-attrs.lo \
 	elf-eh-frame.lo \
+	elf-ctf-frame.lo \
 	elf-ifunc.lo \
 	elf-m10200.lo \
 	elf-m10300.lo \
@@ -421,6 +422,7 @@ BFD32_BACKENDS_CFILES = \
 	ecofflink.c \
 	elf-attrs.c \
 	elf-eh-frame.c \
+	elf-ctf-frame.c \
 	elf-ifunc.c \
 	elf-m10200.c \
 	elf-m10300.c \
@@ -774,8 +776,8 @@ ofiles: stamp-ofiles ; @true
 # dependency tracking fragments are picked up in the Makefile.
 libbfd_la_SOURCES = $(BFD32_LIBS_CFILES)
 EXTRA_libbfd_la_SOURCES = $(CFILES)
-libbfd_la_DEPENDENCIES = $(OFILES) ofiles
-libbfd_la_LIBADD = `cat ofiles` @SHARED_LIBADD@ $(LIBDL) $(ZLIB)
+libbfd_la_DEPENDENCIES = $(OFILES) ofiles ../libctfframe/libctfframe.la
+libbfd_la_LIBADD = `cat ofiles` @SHARED_LIBADD@ $(LIBDL) $(ZLIB) ../libctfframe/libctfframe.la
 libbfd_la_LDFLAGS += -release `cat libtool-soversion` @SHARED_LDFLAGS@
 
 # libtool will build .libs/libbfd.a.  We create libbfd.a in the build
diff --git a/bfd/Makefile.in b/bfd/Makefile.in
index 3068560c48b..3e2a9ccfed7 100644
--- a/bfd/Makefile.in
+++ b/bfd/Makefile.in
@@ -756,6 +756,7 @@ BFD32_BACKENDS = \
 	ecofflink.lo \
 	elf-attrs.lo \
 	elf-eh-frame.lo \
+	elf-ctf-frame.lo \
 	elf-ifunc.lo \
 	elf-m10200.lo \
 	elf-m10300.lo \
@@ -889,6 +890,7 @@ BFD32_BACKENDS_CFILES = \
 	ecofflink.c \
 	elf-attrs.c \
 	elf-eh-frame.c \
+	elf-ctf-frame.c \
 	elf-ifunc.c \
 	elf-m10200.c \
 	elf-m10300.c \
@@ -1203,8 +1205,8 @@ OFILES = $(BFD_BACKENDS) $(BFD_MACHINES) @COREFILE@ @bfd64_libs@
 # dependency tracking fragments are picked up in the Makefile.
 libbfd_la_SOURCES = $(BFD32_LIBS_CFILES)
 EXTRA_libbfd_la_SOURCES = $(CFILES)
-libbfd_la_DEPENDENCIES = $(OFILES) ofiles
-libbfd_la_LIBADD = `cat ofiles` @SHARED_LIBADD@ $(LIBDL) $(ZLIB)
+libbfd_la_DEPENDENCIES = $(OFILES) ofiles ../libctfframe/libctfframe.la
+libbfd_la_LIBADD = `cat ofiles` @SHARED_LIBADD@ $(LIBDL) $(ZLIB) ../libctfframe/libctfframe.la
 
 # libtool will build .libs/libbfd.a.  We create libbfd.a in the build
 # directory so that we don't have to convert all the programs that use
@@ -1565,6 +1567,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ecoff.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ecofflink.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf-attrs.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf-ctf-frame.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf-eh-frame.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf-ifunc.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf-m10200.Plo@am__quote@
diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 50e26fc691d..7d441c87db0 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -1023,6 +1023,7 @@ typedef struct bfd_section
 #define SEC_INFO_TYPE_JUST_SYMS 4
 #define SEC_INFO_TYPE_TARGET    5
 #define SEC_INFO_TYPE_EH_FRAME_ENTRY 6
+#define SEC_INFO_TYPE_CTF_FRAME  7
 
   /* Nonzero if this section uses RELA relocations, rather than REL.  */
   unsigned int use_rela_p:1;
diff --git a/bfd/configure b/bfd/configure
index ee2bbe69c87..3e3ca07e4b8 100755
--- a/bfd/configure
+++ b/bfd/configure
@@ -13342,7 +13342,7 @@ selarchs="$f"
 tb=
 
 elf="elf.lo elflink.lo elf-attrs.lo elf-strtab.lo elf-eh-frame.lo
-     dwarf1.lo dwarf2.lo"
+     elf-ctf-frame.lo dwarf1.lo dwarf2.lo"
 coffgen="coffgen.lo dwarf2.lo"
 coff="cofflink.lo $coffgen"
 ecoff="ecofflink.lo $coffgen"
diff --git a/bfd/configure.ac b/bfd/configure.ac
index 8004c3ef4bf..7c9fbde7050 100644
--- a/bfd/configure.ac
+++ b/bfd/configure.ac
@@ -415,7 +415,7 @@ selarchs="$f"
 tb=
 
 elf="elf.lo elflink.lo elf-attrs.lo elf-strtab.lo elf-eh-frame.lo
-     dwarf1.lo dwarf2.lo"
+     elf-ctf-frame.lo dwarf1.lo dwarf2.lo"
 coffgen="coffgen.lo dwarf2.lo"
 coff="cofflink.lo $coffgen"
 ecoff="ecofflink.lo $coffgen"
diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index c7c0a793b15..47c58fb5292 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -490,6 +490,40 @@ struct eh_frame_hdr_info
   u;
 };
 
+/* Additional information for each function (used at link time).  */
+struct ctf_frame_func_bfdinfo
+{
+  /* Whether the function has been discarded from the final output.  */
+  bool func_deleted_p;
+  /* Relocation offset.  */
+  unsigned int func_r_offset;
+  /* Relocation index.  */
+  unsigned int func_reloc_index;
+};
+
+/* CTF frame decoder info.
+   Contains all information for a decoded .ctf_frame section.  */
+struct ctf_frame_dec_info
+{
+  /* Decoder context.  */
+  struct ctf_frame_decoder_ctx *cfd_ctx;
+  /* Number of function descriptor entries in this .ctf_frame.  */
+  unsigned int cfd_fde_count;
+  /* Additional information for linking.  */
+  struct ctf_frame_func_bfdinfo *cfd_func_bfdinfo;
+};
+
+/* CTF frame encoder info.
+   Contains all information for an encoded .ctf_frame section to be
+   written out.  */
+struct ctf_frame_enc_info
+{
+  /* Encoder context.  */
+  struct ctf_frame_encoder_ctx *cfe_ctx;
+  /* Output section.  */
+  asection *ctf_frame_section;
+};
+
 /* Enum used to identify target specific extensions to the elf_obj_tdata
    and elf_link_hash_table structures.  Note the enums deliberately start
    from 1 so that we can detect an uninitialized field.  The generic value
@@ -668,6 +702,9 @@ struct elf_link_hash_table
   /* Used by eh_frame code when editing .eh_frame.  */
   struct eh_frame_hdr_info eh_info;
 
+  /* Used to link unwind data in .ctf_frame sections.  */
+  struct ctf_frame_enc_info cfe_info;
+
   /* A linked list of local symbols to be added to .dynsym.  */
   struct elf_link_local_dynamic_entry *dynlocal;
 
@@ -1933,6 +1970,10 @@ struct output_elf_obj_tdata
   /* Segment flags for the PT_GNU_STACK segment.  */
   unsigned int stack_flags;
 
+  /* Used to determine if PT_GNU_CTF_FRAME segment header should be
+     created.  */
+  asection *ctf_frame;
+
   /* Used to determine if the e_flags field has been initialized */
   bool flags_init;
 };
@@ -2114,6 +2155,7 @@ struct elf_obj_tdata
 #define elf_link_info(bfd)	(elf_tdata(bfd) -> o->link_info)
 #define elf_next_file_pos(bfd)	(elf_tdata(bfd) -> o->next_file_pos)
 #define elf_stack_flags(bfd)	(elf_tdata(bfd) -> o->stack_flags)
+#define elf_ctf_frame(bfd)	(elf_tdata(bfd) -> o->ctf_frame)
 #define elf_shstrtab(bfd)	(elf_tdata(bfd) -> o->strtab_ptr)
 #define elf_onesymtab(bfd)	(elf_tdata(bfd) -> symtab_section)
 #define elf_symtab_shndx_list(bfd)	(elf_tdata(bfd) -> symtab_shndx_list)
@@ -2425,6 +2467,19 @@ extern bool _bfd_elf_eh_frame_entry_present
 extern bool _bfd_elf_maybe_strip_eh_frame_hdr
   (struct bfd_link_info *);
 
+extern bool _bfd_elf_ctf_frame_present
+  (struct bfd_link_info *);
+extern void _bfd_elf_parse_ctf_frame
+  (bfd *, struct bfd_link_info *, asection *, struct elf_reloc_cookie *);
+extern bool _bfd_elf_discard_section_ctf_frame
+  (bfd *, struct bfd_link_info *, asection *,
+   bool (*) (bfd_vma, void *), struct elf_reloc_cookie *);
+extern bool _bfd_elf_merge_section_ctf_frame
+  (bfd *, struct bfd_link_info *, asection *, bfd_byte *);
+extern bool _bfd_elf_write_section_ctf_frame
+  (bfd *, struct bfd_link_info *);
+extern bool _bfd_elf_set_section_ctf_frame (bfd *, struct bfd_link_info *);
+
 extern bool _bfd_elf_hash_symbol (struct elf_link_hash_entry *);
 
 extern long _bfd_elf_link_lookup_local_dynindx
diff --git a/bfd/elf-ctf-frame.c b/bfd/elf-ctf-frame.c
new file mode 100644
index 00000000000..c5a79ccd66f
--- /dev/null
+++ b/bfd/elf-ctf-frame.c
@@ -0,0 +1,490 @@
+/* .ctf_frame section processing.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+
+   This file is part of BFD, the Binary File Descriptor library.
+
+   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.  */
+
+#include "sysdep.h"
+#include "bfd.h"
+#include "libbfd.h"
+#include "elf-bfd.h"
+#include "ctf-frame-api.h"
+
+/* Return TRUE if the function has been marked for deletion during the linking
+   process.  */
+
+static bool
+ctf_frame_decoder_func_deleted_p (struct ctf_frame_dec_info *cfd_info,
+				  unsigned int func_idx)
+{
+  return cfd_info->cfd_func_bfdinfo[func_idx].func_deleted_p;
+}
+
+/* Mark the function in the decoder info for deletion.  */
+
+static void
+ctf_frame_decoder_mark_func_deleted (struct ctf_frame_dec_info *cfd_info,
+				     unsigned int func_idx)
+{
+  BFD_ASSERT (func_idx < cfd_info->cfd_fde_count);
+  cfd_info->cfd_func_bfdinfo[func_idx].func_deleted_p = true;
+}
+
+/* Get the relocation offset from the decoder info for the given function.  */
+
+static unsigned int
+ctf_frame_decoder_get_func_r_offset (struct ctf_frame_dec_info *cfd_info,
+				     unsigned int func_idx)
+{
+  BFD_ASSERT (func_idx < cfd_info->cfd_fde_count);
+  unsigned int func_r_offset
+    = cfd_info->cfd_func_bfdinfo[func_idx].func_r_offset;
+  /* There must have been a reloc.  */
+  BFD_ASSERT (func_r_offset);
+  return func_r_offset;
+}
+
+/* Bookkeep the function relocation offset in the decoder info.  */
+
+static void
+ctf_frame_decoder_set_func_r_offset (struct ctf_frame_dec_info *cfd_info,
+				     unsigned int func_idx,
+				     unsigned int r_offset)
+{
+  BFD_ASSERT (func_idx < cfd_info->cfd_fde_count);
+
+  cfd_info->cfd_func_bfdinfo[func_idx].func_r_offset = r_offset;
+}
+
+/* Get the relocation index in the elf_reloc_cookie for the function.  */
+
+static unsigned int
+ctf_frame_decoder_get_func_reloc_index (struct ctf_frame_dec_info *cfd_info,
+					unsigned int func_idx)
+{
+  BFD_ASSERT (func_idx < cfd_info->cfd_fde_count);
+  return cfd_info->cfd_func_bfdinfo[func_idx].func_reloc_index;
+}
+
+/* Bookkeep the relocation index in the elf_reloc_cookie for the function.  */
+
+static void
+ctf_frame_decoder_set_func_reloc_index (struct ctf_frame_dec_info *cfd_info,
+					unsigned int func_idx,
+					unsigned int reloc_index)
+{
+  BFD_ASSERT (func_idx < cfd_info->cfd_fde_count);
+  cfd_info->cfd_func_bfdinfo[func_idx].func_reloc_index = reloc_index;
+}
+
+/* Initialize the set of additional information in CFD_INFO,
+   needed for linking SEC.  Returns TRUE if setup is done successfully.  */
+
+static bool
+ctf_frame_decoder_init_func_bfdinfo (asection *sec,
+				     struct ctf_frame_dec_info *cfd_info,
+				     struct elf_reloc_cookie *cookie)
+{
+  unsigned int fde_count;
+  unsigned int func_bfdinfo_size, i;
+
+  fde_count = ctf_frame_decoder_get_num_fidx (cfd_info->cfd_ctx);
+  cfd_info->cfd_fde_count = fde_count;
+
+  /* Allocate and clear the memory.  */
+  func_bfdinfo_size = (sizeof (struct ctf_frame_func_bfdinfo)) * fde_count;
+  cfd_info->cfd_func_bfdinfo
+    = (struct ctf_frame_func_bfdinfo*) bfd_malloc (func_bfdinfo_size);
+  if (cfd_info->cfd_func_bfdinfo == NULL)
+    return false;
+  memset (cfd_info->cfd_func_bfdinfo, 0, func_bfdinfo_size);
+
+  /* For linker generated .ctf_frame sections, we have no relocs. Skip.  */
+  if ((sec->flags & SEC_LINKER_CREATED) && cookie->rels == NULL)
+    return true;
+
+  for (i = 0; i < fde_count; i++)
+    {
+      cookie->rel = cookie->rels + i;
+      BFD_ASSERT (cookie->rel < cookie->relend);
+      /* Bookkeep the relocation offset and relocation index of each function
+	 for later use.  */
+      ctf_frame_decoder_set_func_r_offset (cfd_info, i, cookie->rel->r_offset);
+      ctf_frame_decoder_set_func_reloc_index (cfd_info, i,
+					      (cookie->rel - cookie->rels));
+
+      cookie->rel++;
+    }
+  BFD_ASSERT (cookie->rel == cookie->relend);
+
+  return true;
+}
+
+/* Read the value from CONTENTS at the specified OFFSET for the given ABFD.  */
+
+static bfd_vma
+ctf_frame_read_value (bfd *abfd, bfd_byte *contents, unsigned int offset,
+		      unsigned int width)
+{
+  BFD_ASSERT (contents && offset);
+  /* Supporting the usecase of reading only the 4-byte relocated
+     value (signed offset for func start addr) for now.  */
+  BFD_ASSERT (width == 4);
+  /* FIXME endianness ?? */
+  unsigned char *buf = contents + offset;
+  bfd_vma value = bfd_get_signed_32 (abfd, buf);
+  return value;
+}
+
+/* Return true if there is at least one non-empty .ctf_frame section in
+   input files.  Can only be called after ld has mapped input to
+   output sections, and before sections are stripped.  */
+
+bool
+_bfd_elf_ctf_frame_present (struct bfd_link_info *info)
+{
+  asection *ctff = bfd_get_section_by_name (info->output_bfd, ".ctf_frame");
+
+  if (ctff == NULL)
+    return false;
+
+  /* Count only sections which have at least a single FDE.  */
+  for (ctff = ctff->map_head.s; ctff != NULL; ctff = ctff->map_head.s)
+    if (ctff->size > sizeof (ctf_frame_header))
+      return true;
+  return false;
+}
+
+/* Try to parse .ctf_frame section SEC, which belongs to ABFD.  Store the
+   information in the section's sec_info field on success.  COOKIE
+   describes the relocations in SEC.  */
+
+void
+_bfd_elf_parse_ctf_frame (bfd *abfd, struct bfd_link_info *info,
+			  asection *sec, struct elf_reloc_cookie *cookie)
+{
+  bfd_byte *ctfbuf = NULL;
+  struct ctf_frame_dec_info *cfd_info;
+  bfd_size_type cf_size;
+  int decerr = 0;
+
+  if (sec->size == 0
+      || sec->sec_info_type != SEC_INFO_TYPE_NONE)
+    {
+      /* This file does not contain .ctf_frame information.  */
+      return;
+    }
+
+  if (bfd_is_abs_section (sec->output_section))
+    {
+      /* At least one of the sections is being discarded from the
+	 link, so we should just ignore them.  */
+      return;
+    }
+
+  /* Read the ctf frame unwind information from abfd.  */
+  if (!bfd_malloc_and_get_section (abfd, sec, &ctfbuf))
+    goto fail_no_free;
+
+  /* Decode the buffer and keep decoded contents for later use.
+     Relocations are performed later, but are such that the section's
+     size is unaffected.  */
+  cfd_info = bfd_malloc (sizeof (struct ctf_frame_dec_info));
+  cf_size = sec->size;
+  BFD_ASSERT (cf_size);
+  cfd_info->cfd_ctx = ctf_frame_decode ((const char*)ctfbuf, cf_size, &decerr);
+  if (!cfd_info->cfd_ctx)
+    {
+      ctf_free_decoder (cfd_info->cfd_ctx);
+      goto fail_no_free;
+    }
+
+  if (!ctf_frame_decoder_init_func_bfdinfo (sec, cfd_info, cookie))
+    {
+      ctf_free_decoder (cfd_info->cfd_ctx);
+      goto fail_no_free;
+    }
+
+  elf_section_data (sec)->sec_info = cfd_info;
+  sec->sec_info_type = SEC_INFO_TYPE_CTF_FRAME;
+
+  BFD_ASSERT (abfd && info && cookie);
+  goto success;
+
+fail_no_free:
+  _bfd_error_handler
+   (_("error in %pB(%pA); no .ctf_frame will be created"),
+    abfd, sec);
+success:
+  free (ctfbuf);
+}
+
+/* This function is called for each input file before the .ctf_frame section
+   is relocated.  It discards CTF FDEs and FREs for discarded functions.  The
+   function returns TRUE iff any entries have been deleted.  */
+
+bool
+_bfd_elf_discard_section_ctf_frame
+   (bfd *abfd, struct bfd_link_info *info, asection *sec,
+    bool (*reloc_symbol_deleted_p) (bfd_vma, void *),
+    struct elf_reloc_cookie *cookie)
+{
+  struct ctf_frame_dec_info *cfd_info;
+  BFD_ASSERT (abfd && info);
+  BFD_ASSERT (reloc_symbol_deleted_p);
+  cfd_info = (struct ctf_frame_dec_info *) elf_section_data (sec)->sec_info;
+
+  bool changed = false;
+  /* FIXME - if relocatable link and changed = true, how does the final
+     .rela.ctf_frame get updated ?.  */
+  bool keep = false;
+  unsigned int i = 0;
+  unsigned int func_desc_offset = 0;
+  unsigned int num_fidx = 0;
+
+  /* Skip checking for the linker created .ctf_frame sections
+     (for PLT sections).  */
+  if ((sec->flags & SEC_LINKER_CREATED) == 0 || cookie->rels != NULL)
+    {
+      num_fidx = ctf_frame_decoder_get_num_fidx (cfd_info->cfd_ctx);
+      for (i = 0; i < num_fidx; i++)
+	{
+	  func_desc_offset = ctf_frame_decoder_get_func_r_offset (cfd_info, i);
+
+	  cookie->rel = cookie->rels
+	    + ctf_frame_decoder_get_func_reloc_index (cfd_info, i);
+	  keep = !(*reloc_symbol_deleted_p) (func_desc_offset, cookie);
+
+	  if (!keep)
+	    {
+	      ctf_frame_decoder_mark_func_deleted (cfd_info, i);
+	      changed = true;
+	    }
+	}
+    }
+  return changed;
+}
+
+/* Update the reference to the output .ctf_frame section in the output ELF
+   BFD ABFD.  Returns true if no error.  */
+
+bool
+_bfd_elf_set_section_ctf_frame (bfd *abfd,
+				struct bfd_link_info *info)
+{
+  asection *cfsec;
+
+  cfsec = bfd_get_section_by_name (info->output_bfd, ".ctf_frame");
+  if (!cfsec)
+    return false;
+
+  elf_ctf_frame (abfd) = cfsec;
+
+  return true;
+}
+
+/* Merge .ctf_frame section SEC.  This is called with the relocated
+   CONTENTS.  */
+
+bool
+_bfd_elf_merge_section_ctf_frame (bfd *abfd,
+				  struct bfd_link_info *info,
+				  asection *sec,
+				  bfd_byte *contents)
+{
+  struct ctf_frame_dec_info *cfd_info;
+  struct ctf_frame_enc_info *cfe_info;
+  ctf_frame_decoder_ctx *cfd_ctx;
+  ctf_frame_encoder_ctx *cfe_ctx;
+  unsigned char cfd_ctx_abi_arch = 0;
+  int encerr = 0;
+
+  struct elf_link_hash_table *htab;
+  asection *cfsec;
+
+  BFD_ASSERT (abfd && info && sec && contents);
+
+  BFD_ASSERT (sec->sec_info_type == SEC_INFO_TYPE_CTF_FRAME);
+
+  cfd_info = (struct ctf_frame_dec_info *) elf_section_data (sec)->sec_info;
+  cfd_ctx = cfd_info->cfd_ctx;
+
+  htab = elf_hash_table (info);
+  cfe_info = &(htab->cfe_info);
+  cfe_ctx = cfe_info->cfe_ctx;
+
+  BFD_ASSERT (cfd_info && htab);
+  BFD_ASSERT (cfe_info);
+  BFD_ASSERT (sec->output_section);
+
+  if (htab->cfe_info.cfe_ctx == NULL)
+    {
+      cfd_ctx_abi_arch = ctf_frame_decoder_get_abi_arch (cfd_ctx);
+      BFD_ASSERT (cfd_ctx_abi_arch); /* Valid values are non-zero.  */
+      htab->cfe_info.cfe_ctx = ctf_frame_encode (CTF_FRAME_VERSION_1,
+						 0, /* CTF Frame flags.  */
+						 cfd_ctx_abi_arch,
+						 &encerr);
+      /* FIXME handle errors from ctf_frame_encode.  */
+    }
+  cfe_ctx = cfe_info->cfe_ctx;
+
+  if (cfe_info->ctf_frame_section == NULL)
+    {
+      /* Make sure things are set for an eventual write.
+	 Size of the output section is not known until
+	 _bfd_elf_write_section_ctf_frame is ready with the buffer
+	 to write out.  */
+      cfsec = bfd_get_section_by_name (info->output_bfd, ".ctf_frame");
+      if (cfsec)
+	{
+	  cfe_info->ctf_frame_section = cfsec;
+	  // elf_ctf_frame (abfd) = cfsec;
+	}
+      else
+	return false;
+    }
+
+  /* Check that all .ctf_frame sections being linked have the same
+     ABI/arch.  */
+  if (ctf_frame_decoder_get_abi_arch (cfd_ctx)
+      != ctf_frame_encoder_get_abi_arch (cfe_ctx))
+    return false;
+
+  /* Iterate over the function descriptor entries and the FREs of the
+     function from the decoder context.  Add each of them to the encoder
+     context, if suitable.  */
+  unsigned int i = 0, j = 0, cur_fidx = 0;
+
+  unsigned int num_fidx = ctf_frame_decoder_get_num_fidx (cfd_ctx);
+  unsigned int num_enc_fidx = ctf_frame_encoder_get_num_fidx (cfe_ctx);
+
+  for (i = 0; i < num_fidx; i++)
+    {
+      unsigned int num_fres = 0;
+      int32_t func_start_address;
+      bfd_vma address;
+      uint32_t func_size = 0;
+      unsigned char func_info = 0;
+      unsigned int r_offset = 0;
+      if (!ctf_frame_decoder_get_funcdesc (cfd_ctx, i, &num_fres, &func_size,
+					   &func_start_address, &func_info))
+	{
+	  /* If function belongs to a deleted section, skip editing the
+	     function descriptor entry.  */
+	  if (ctf_frame_decoder_func_deleted_p(cfd_info, i))
+	    continue;
+
+	  /* Don't edit function descriptor entries for relocatable link.  */
+	  if (!bfd_link_relocatable (info))
+	    {
+	      if (!(sec->flags & SEC_LINKER_CREATED))
+		{
+		  /* Get relocated contents by reading the value of the
+		     relocated function start address at the beginning of the
+		     function descriptor entry.  */
+		  r_offset = ctf_frame_decoder_get_func_r_offset (cfd_info, i);
+		}
+	      else
+		{
+		  /* Expected to happen for CTF frame unwind info as created
+		     for the .plt* sections.  These sections have only one
+		     FDE entry.  */
+		  BFD_ASSERT (num_fidx == 1);
+		  /* Use the same value as PLT_CTF_FRAME_FDE_START_OFFSET.  */
+		  r_offset = sizeof (ctf_frame_header);
+		}
+
+	      /* Function start address after relocation.  */
+	      address = ctf_frame_read_value (abfd, contents, r_offset, 4);
+	      address += (sec->output_offset + r_offset);
+
+	      /* FIXME For testing only. Cleanup later.  */
+	      // address += (sec->output_section->vma);
+
+	      func_start_address = address;
+	    }
+
+	  /* Update the encoder context with updated content.  */
+	  int err = ctf_frame_encoder_add_funcdesc (cfe_ctx, func_start_address,
+						    func_size, func_info, num_fres);
+	  cur_fidx++;
+	  BFD_ASSERT (!err);
+	}
+
+      for (j = 0; j < num_fres; j++)
+	{
+	  ctf_frame_row_entry fre;
+	  if (!ctf_frame_decoder_get_fre (cfd_ctx, i, j, &fre))
+	    {
+	      int err = ctf_frame_encoder_add_fre (cfe_ctx,
+						   cur_fidx-1+num_enc_fidx,
+						   &fre);
+	      BFD_ASSERT (!err);
+	    }
+	}
+    }
+  /* Free the CTF frame decoder context.  */
+  ctf_free_decoder (cfd_info->cfd_ctx);
+
+  return true;
+}
+
+/* Write out the .ctf_frame section.  This must be called after
+   _bfd_elf_merge_section_ctf_frame has been called on all input
+   .ctf_frame sections.  */
+
+bool
+_bfd_elf_write_section_ctf_frame (bfd *abfd, struct bfd_link_info *info)
+{
+  bool retval = true;
+  BFD_ASSERT (abfd && info);
+
+  struct elf_link_hash_table *htab;
+  struct ctf_frame_enc_info *cfe_info;
+  asection *sec;
+  void *contents;
+  bfd_size_type sec_size;
+  int err = 0;
+
+  htab = elf_hash_table (info);
+  cfe_info = &htab->cfe_info;
+  sec = cfe_info->ctf_frame_section;
+
+  // FIXME use assert
+  // BFD_ASSERT (sec);
+  if (sec == NULL)
+    return true;
+
+  contents = ctf_frame_write_encoder (cfe_info->cfe_ctx, &sec_size, &err);
+  sec->size = sec_size;
+
+  if (!bfd_set_section_contents (abfd, sec->output_section, contents,
+				 (file_ptr) sec->output_offset,
+				 sec_size))
+    retval = false;
+  else
+    {
+      /* FIXME - is this appropriate to do here ?  */
+      Elf_Internal_Shdr *hdr = &elf_section_data (sec)->this_hdr;
+      hdr->sh_size = sec_size;
+    }
+
+  ctf_free_encoder (cfe_info->cfe_ctx);
+
+  return retval;
+}
diff --git a/bfd/elf.c b/bfd/elf.c
index f046994e3a8..fd76c8c833e 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -1646,6 +1646,7 @@ get_segment_type (unsigned int p_type)
     case PT_GNU_EH_FRAME: pt = "EH_FRAME"; break;
     case PT_GNU_STACK: pt = "STACK"; break;
     case PT_GNU_RELRO: pt = "RELRO"; break;
+    case PT_GNU_CTF_FRAME: pt = "CTF_FRAME"; break;
     default: pt = NULL; break;
     }
   return pt;
@@ -3060,6 +3061,10 @@ bfd_section_from_phdr (bfd *abfd, Elf_Internal_Phdr *hdr, int hdr_index)
     case PT_GNU_RELRO:
       return _bfd_elf_make_section_from_phdr (abfd, hdr, hdr_index, "relro");
 
+    case PT_GNU_CTF_FRAME:
+      return _bfd_elf_make_section_from_phdr (abfd, hdr, hdr_index,
+					      "ctf_frame");
+
     default:
       /* Check for any processor-specific program segment types.  */
       bed = get_elf_backend_data (abfd);
@@ -4408,6 +4413,12 @@ get_program_header_size (bfd *abfd, struct bfd_link_info *info)
       ++segs;
     }
 
+  if (elf_ctf_frame (abfd))
+    {
+      /* We need a PT_GNU_CTF_FRAME segment.  */
+      ++segs;
+    }
+
   s = bfd_get_section_by_name (abfd,
 			       NOTE_GNU_PROPERTY_SECTION_NAME);
   if (s != NULL && s->size != 0)
@@ -4673,6 +4684,7 @@ _bfd_elf_map_sections_to_segments (bfd *abfd,
       asection *first_tls = NULL;
       asection *first_mbind = NULL;
       asection *dynsec, *eh_frame_hdr;
+      asection *ctf_frame;
       size_t amt;
       bfd_vma addr_mask, wrap_to = 0;  /* Bytes.  */
       bfd_size_type phdr_size;  /* Octets/bytes.  */
@@ -5190,6 +5202,25 @@ _bfd_elf_map_sections_to_segments (bfd *abfd,
 	  pm = &m->next;
 	}
 
+      /* If there is a .ctf_frame section, throw in a PT_GNU_CTF_FRAME
+	 segment.  */
+      ctf_frame = elf_ctf_frame (abfd);
+      if (ctf_frame != NULL
+	  && (ctf_frame->output_section->flags & SEC_LOAD) != 0)
+	{
+	  amt = sizeof (struct elf_segment_map);
+	  m = (struct elf_segment_map *) bfd_zalloc (abfd, amt);
+	  if (m == NULL)
+	    goto error_return;
+	  m->next = NULL;
+	  m->p_type = PT_GNU_CTF_FRAME;
+	  m->count = 1;
+	  m->sections[0] = ctf_frame->output_section;
+
+	  *pm = m;
+	  pm = &m->next;
+	}
+
       if (info != NULL && info->relro)
 	{
 	  for (m = mfirst; m != NULL; m = m->next)
diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
index 6d69d6141ee..2135db71b9b 100644
--- a/bfd/elf64-x86-64.c
+++ b/bfd/elf64-x86-64.c
@@ -22,6 +22,7 @@
 #include "elfxx-x86.h"
 #include "dwarf2.h"
 #include "libiberty.h"
+#include "ctf-frame.h"
 
 #include "opcode/i386.h"
 
@@ -818,6 +819,87 @@ static const bfd_byte elf_x86_64_eh_frame_non_lazy_plt[] =
   DW_CFA_nop, DW_CFA_nop, DW_CFA_nop
 };
 
+static const ctf_frame_row_entry elf_x86_64_ctf_frame_null_fre =
+{
+  0,
+  CTF_FRAME_V1_FRE_INFO (CTF_FRAME_BASE_REG_SP, 1, CTF_FRAME_FRE_OFFSET_1B), /* FRE info.  */
+  {16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} /* 12 bytes.  */
+};
+
+/* .ctf_frame FRE covering the .plt section entry.  */
+static const ctf_frame_row_entry elf_x86_64_ctf_frame_plt0_fre1 =
+{
+  0, /* CTF FRE start address.  */
+  CTF_FRAME_V1_FRE_INFO (CTF_FRAME_BASE_REG_SP, 1, CTF_FRAME_FRE_OFFSET_1B), /* FRE info.  */
+  {16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} /* 12 bytes.  */
+};
+
+/* .ctf_frame FRE covering the .plt section entry.  */
+static const ctf_frame_row_entry elf_x86_64_ctf_frame_plt0_fre2 =
+{
+  6, /* CTF FRE start address.  */
+  CTF_FRAME_V1_FRE_INFO (CTF_FRAME_BASE_REG_SP, 1, CTF_FRAME_FRE_OFFSET_1B), /* FRE info.  */
+  {24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} /* 12 bytes.  */
+};
+
+/* .ctf_frame FRE covering the .plt section entry.  */
+static const ctf_frame_row_entry elf_x86_64_ctf_frame_pltn_fre1 =
+{
+  0, /* CTF FRE start address.  */
+  CTF_FRAME_V1_FRE_INFO (CTF_FRAME_BASE_REG_SP, 1, CTF_FRAME_FRE_OFFSET_1B), /* FRE info.  */
+  {8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} /* 12 bytes.  */
+};
+
+/* .ctf_frame FRE covering the .plt section entry.  */
+static const ctf_frame_row_entry elf_x86_64_ctf_frame_pltn_fre2 =
+{
+  11, /* CTF FRE start address.  */
+  CTF_FRAME_V1_FRE_INFO (CTF_FRAME_BASE_REG_SP, 1, CTF_FRAME_FRE_OFFSET_1B), /* FRE info.  */
+  {16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} /* 12 bytes.  */
+};
+
+/* .ctf_frame FRE covering the second .plt section entry.  */
+static const ctf_frame_row_entry elf_x86_64_ctf_frame_sec_pltn_fre1 =
+{
+  0, /* CTF FRE start address.  */
+  CTF_FRAME_V1_FRE_INFO (CTF_FRAME_BASE_REG_SP, 1, CTF_FRAME_FRE_OFFSET_1B), /* FRE info.  */
+  {8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} /* 12 bytes.  */
+};
+
+/* CTF frame helper object for non-lazy PLT.  Also used for IBT enabled PLT.  */
+static const struct elf_x86_ctf_frame_plt elf_x86_64_ctf_frame_non_lazy_plt =
+{
+  LAZY_PLT_ENTRY_SIZE,
+  2, /* Number of FREs for PLT0.  */
+  /* Array of CTF Frame FREs for plt0. */
+  { &elf_x86_64_ctf_frame_plt0_fre1, &elf_x86_64_ctf_frame_plt0_fre2 },
+  LAZY_PLT_ENTRY_SIZE,
+  1, /* Number of FREs for PLTn.  */
+  /* Array of CTF Frame FREs for plt.  */
+  { &elf_x86_64_ctf_frame_sec_pltn_fre1, &elf_x86_64_ctf_frame_null_fre },
+  0,
+  0, /* There is no second PLT necessary.  */
+  { &elf_x86_64_ctf_frame_null_fre }
+};
+
+/* CTF frame helper object for lazy PLT.  Also used for IBT enabled PLT.  */
+static const struct elf_x86_ctf_frame_plt elf_x86_64_ctf_frame_plt =
+{
+  LAZY_PLT_ENTRY_SIZE,
+  2, /* Number of FREs for PLT0.  */
+  /* Array of CTF Frame FREs for plt0. */
+  { &elf_x86_64_ctf_frame_plt0_fre1, &elf_x86_64_ctf_frame_plt0_fre2 },
+  LAZY_PLT_ENTRY_SIZE,
+  2, /* Number of FREs for PLTn.  */
+  /* Array of CTF Frame FREs for plt.  */
+  { &elf_x86_64_ctf_frame_pltn_fre1, &elf_x86_64_ctf_frame_pltn_fre2 },
+  NON_LAZY_PLT_ENTRY_SIZE,
+  1, /* Number of FREs for PLTn for second PLT.  */
+  /* FREs for second plt ( unwind info for .plt.got is
+     identical).  Used when IBT or non-lazy PLT is in effect.  */
+  { &elf_x86_64_ctf_frame_sec_pltn_fre1 }
+};
+
 /* These are the standard parameters.  */
 static const struct elf_x86_lazy_plt_layout elf_x86_64_lazy_plt =
   {
@@ -971,7 +1053,6 @@ static const struct elf_x86_non_lazy_plt_layout elf_x32_non_lazy_ibt_plt =
     sizeof (elf_x86_64_eh_frame_non_lazy_plt) /* eh_frame_plt_size */
   };
 
-
 static bool
 elf64_x86_64_elf_object_p (bfd *abfd)
 {
@@ -5223,6 +5304,20 @@ elf_x86_64_link_setup_gnu_properties (struct bfd_link_info *info)
       init_table.non_lazy_ibt_plt = &elf_x32_non_lazy_ibt_plt;
     }
 
+  if (ABI_64_P (info->output_bfd))
+    {
+      init_table.ctf_frame_lazy_plt = &elf_x86_64_ctf_frame_plt;
+      init_table.ctf_frame_non_lazy_plt = &elf_x86_64_ctf_frame_non_lazy_plt;
+      init_table.ctf_frame_lazy_ibt_plt = &elf_x86_64_ctf_frame_plt;
+      init_table.ctf_frame_non_lazy_ibt_plt = &elf_x86_64_ctf_frame_non_lazy_plt;
+    }
+  else
+    {
+      /* CTF frame is not supported for non AMD64.  */
+      init_table.ctf_frame_lazy_plt = NULL;
+      init_table.ctf_frame_non_lazy_plt = NULL;
+    }
+
   if (ABI_64_P (info->output_bfd))
     {
       init_table.r_info = elf64_r_info;
diff --git a/bfd/elflink.c b/bfd/elflink.c
index 4d6fe663f68..d3cd8426d9c 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -10904,6 +10904,7 @@ elf_section_ignore_discarded_relocs (asection *sec)
     case SEC_INFO_TYPE_STABS:
     case SEC_INFO_TYPE_EH_FRAME:
     case SEC_INFO_TYPE_EH_FRAME_ENTRY:
+    case SEC_INFO_TYPE_CTF_FRAME:
       return true;
     default:
       break;
@@ -10935,6 +10936,9 @@ _bfd_elf_default_action_discarded (asection *sec)
   if (strcmp (".eh_frame", sec->name) == 0)
     return 0;
 
+  if (strcmp (".ctf_frame", sec->name) == 0)
+    return 0;
+
   if (strcmp (".gcc_except_table", sec->name) == 0)
     return 0;
 
@@ -11868,6 +11872,16 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
 	      return false;
 	  }
 	  break;
+	case SEC_INFO_TYPE_CTF_FRAME:
+	    {
+	      /* Merge .ctf_frame sections into the ctf frame encoder
+		 context of the output_bfd's section.  The final .ctf_frame
+		 output section will be written out later.  */
+	      if (!_bfd_elf_merge_section_ctf_frame (output_bfd, flinfo->info,
+						     o, contents))
+		return false;
+	    }
+	    break;
 	default:
 	  {
 	    if (! (o->flags & SEC_EXCLUDE))
@@ -13451,6 +13465,9 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
   if (! _bfd_elf_write_section_eh_frame_hdr (abfd, info))
     goto error_return;
 
+  if (! _bfd_elf_write_section_ctf_frame (abfd, info))
+    goto error_return;
+
   if (info->callbacks->emit_ctf)
       info->callbacks->emit_ctf ();
 
@@ -14906,6 +14923,41 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
 				_bfd_elf_adjust_eh_frame_global_symbol, NULL);
     }
 
+  o = bfd_get_section_by_name (output_bfd, ".ctf_frame");
+  if (o != NULL)
+    {
+      asection *i;
+
+      for (i = o->map_head.s; i != NULL; i = i->map_head.s)
+	{
+	  if (i->size == 0)
+	    continue;
+
+	  abfd = i->owner;
+	  if (bfd_get_flavour (abfd) != bfd_target_elf_flavour)
+	    continue;
+
+	  if (!init_reloc_cookie_for_section (&cookie, info, i))
+	    return -1;
+
+	  _bfd_elf_parse_ctf_frame (abfd, info, i, &cookie);
+
+	  if (_bfd_elf_discard_section_ctf_frame (abfd, info, i,
+						  bfd_elf_reloc_symbol_deleted_p,
+						  &cookie))
+	    {
+	      if (i->size != i->rawsize)
+		changed = 1;
+	    }
+
+	  fini_reloc_cookie_for_section (&cookie, i);
+	}
+      /* Update the reference to the output .ctf_frame section.  Used to
+	 determine later if PT_GNU_CTF_FRAME segment is to be generated.  */
+      if (!_bfd_elf_set_section_ctf_frame (output_bfd, info))
+	return -1;
+    }
+
   for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link.next)
     {
       const struct elf_backend_data *bed;
diff --git a/bfd/elfxx-x86.c b/bfd/elfxx-x86.c
index acb2cc8528d..ea7f2b50503 100644
--- a/bfd/elfxx-x86.c
+++ b/bfd/elfxx-x86.c
@@ -2268,6 +2268,129 @@ _bfd_x86_elf_size_dynamic_sections (bfd *output_bfd,
 	  = htab->non_lazy_plt->eh_frame_plt_size;
     }
 
+  /* No need to size the .ctf_frame section explicitly because the write-out
+     mechanism is different.  Simply prep up the FDE/FRE for the
+     .plt section.  */
+  if (_bfd_elf_ctf_frame_present (info))
+    {
+      if (htab->plt_ctf_frame != NULL
+	  && htab->elf.splt != NULL
+	  && htab->elf.splt->size != 0
+	  && !bfd_is_abs_section (htab->elf.splt->output_section))
+	{
+	  bool plt0_generated = false;
+	  unsigned int plt0_entry_size = 0;
+	  int err = 0;
+	  if (htab->plt.has_plt0 && htab->elf.splt == NULL)
+	    {
+	      plt0_generated = true;
+	      plt0_entry_size = htab->ctf_frame_plt->plt0_entry_size;
+	    }
+
+	  htab->plt_cfe_ctx = ctf_frame_encode (CTF_FRAME_VERSION_1,
+						0,
+						CTF_FRAME_ABI_AMD64_ENDIAN_LITTLE,
+						&err);
+	  unsigned char func_info;
+	  unsigned int fre_type;
+	  fre_type = ctf_frame_calc_fre_type (htab->elf.splt->size);
+	  func_info = ctf_frame_fde_func_info (fre_type);
+	  /* Unwind information for any flavor of PLT must have the same size
+	     as the PLT.  Contents will vary depending on the kind of PLT.  */
+	  ctf_frame_encoder_add_funcdesc (htab->plt_cfe_ctx,
+					  0, /* func start addr, updated later.  */
+					  htab->elf.splt->size,
+					  func_info,
+					  0 /* Num FREs.  */);
+
+	  /* Add FREs for plt0 if plt0 has been generated.  */
+	  if (plt0_generated)
+	    {
+	      ctf_frame_row_entry plt0_fre;
+	      unsigned int num_plt0_fres = htab->ctf_frame_plt->plt0_num_fres;
+	      for (unsigned int j = 0; j < num_plt0_fres; j++)
+		{
+		  plt0_fre = *(htab->ctf_frame_plt->plt0_fres[j]);
+		  ctf_frame_encoder_add_fre (htab->plt_cfe_ctx, 0, &plt0_fre);
+		}
+	    }
+
+	  ctf_frame_row_entry pltn_fre;
+	  unsigned int num_pltn_fres = htab->ctf_frame_plt->pltn_num_fres;
+	  unsigned int num_pltn_entries
+	    = (htab->elf.splt->size - plt0_entry_size) / htab->plt.plt_entry_size;
+	  for (unsigned int i = 0; i < num_pltn_entries; i++)
+	  {
+	    for (unsigned int j = 0; j < num_pltn_fres; j++)
+	      {
+		pltn_fre = *(htab->ctf_frame_plt->pltn_fres[j]);
+		if (plt0_generated)
+		  pltn_fre.fre_start_addr += plt0_entry_size;
+		/* Adjust the start address of FREs and add to FDE at id 0.  There
+		   is only a single FDE necessary per .plt section.  */
+		if (i > 0)
+		  pltn_fre.fre_start_addr += i*htab->plt.plt_entry_size;
+		ctf_frame_encoder_add_fre (htab->plt_cfe_ctx, 0, &pltn_fre);
+	      }
+	  }
+
+	  /* FIXME - Dirty Hack. Set the size to something non-zero for now,
+	     so that the section does not get stripped out below.  */
+	  htab->plt_ctf_frame->size = sizeof (ctf_frame_header) + 1;
+      }
+
+      /* FIXME - generate for .got.plt ?  */
+
+
+      /* Unwind info for the second PLT.  */
+      if (htab->plt_second_ctf_frame != NULL
+	  && htab->plt_second != NULL
+	  && htab->plt_second->size != 0
+	  && !bfd_is_abs_section (htab->plt_second->output_section))
+	{
+	  int err = 0;
+	  htab->plt_second_cfe_ctx = ctf_frame_encode (CTF_FRAME_VERSION_1,
+						       0,
+						       CTF_FRAME_ABI_AMD64_ENDIAN_LITTLE,
+						       &err);
+	  unsigned char func_info;
+	  unsigned int fre_type;
+	  fre_type = ctf_frame_calc_fre_type (htab->plt_second_eh_frame->size);
+	  func_info = ctf_frame_fde_func_info (fre_type);
+
+	  ctf_frame_encoder_add_funcdesc (htab->plt_second_cfe_ctx,
+					  0, /* func start addr, updated later.  */
+					  htab->plt_second_eh_frame->size,
+					  func_info,
+					  0 /* Num FREs.  */);
+
+	  ctf_frame_row_entry pltn_fre;
+	  unsigned int num_pltn_fres = htab->ctf_frame_plt->sec_pltn_num_fres;
+	  unsigned int num_pltn_entries
+	    = htab->plt_second_eh_frame->size / htab->ctf_frame_plt->sec_pltn_entry_size;
+
+	  if (num_pltn_fres == 1)
+	    num_pltn_entries = 1;
+
+	  for (unsigned int i = 0; i < num_pltn_entries; i++)
+	  {
+	    for (unsigned int j = 0; j < num_pltn_fres; j++)
+	      {
+		pltn_fre = *(htab->ctf_frame_plt->pltn_fres[j]);
+		/* Adjust the start address of FREs and add to FDE at id 0.  There
+		   is only a single FDE necessary per .plt section.  */
+		if (i > 0)
+		  pltn_fre.fre_start_addr += i*htab->plt.plt_entry_size;
+		ctf_frame_encoder_add_fre (htab->plt_second_cfe_ctx, 0, &pltn_fre);
+	      }
+	  }
+
+	  /* FIXME - Dirty Hack. Set the size to something non-zero for now,
+	     so that the section does not get stripped out below.  */
+	  htab->plt_second_ctf_frame->size = sizeof (ctf_frame_header) + 1;
+	}
+    }
+
   /* We now have determined the sizes of the various dynamic sections.
      Allocate memory for them.  */
   relocs = false;
@@ -2303,6 +2426,8 @@ _bfd_x86_elf_size_dynamic_sections (bfd *output_bfd,
 	       || s == htab->plt_eh_frame
 	       || s == htab->plt_got_eh_frame
 	       || s == htab->plt_second_eh_frame
+	       || s == htab->plt_ctf_frame
+	       || s == htab->plt_second_ctf_frame
 	       || s == htab->elf.sdynbss
 	       || s == htab->elf.sdynrelro)
 	{
@@ -2345,6 +2470,11 @@ _bfd_x86_elf_size_dynamic_sections (bfd *output_bfd,
       if ((s->flags & SEC_HAS_CONTENTS) == 0)
 	continue;
 
+      /* Skip allocating contents for .ctf_frame section as it is written
+	 out differently.  See below.  */
+      if ((s == htab->plt_ctf_frame) || (s == htab->plt_second_ctf_frame))
+	continue;
+
       /* NB: Initially, the iplt section has minimal alignment to
 	 avoid moving dot of the following section backwards when
 	 it is empty.  Update its section alignment now since it
@@ -2394,6 +2524,44 @@ _bfd_x86_elf_size_dynamic_sections (bfd *output_bfd,
 		   + PLT_FDE_LEN_OFFSET));
     }
 
+  if (htab->plt_ctf_frame != NULL
+      && htab->elf.splt != NULL
+      && htab->elf.splt->size != 0
+      && htab->plt_ctf_frame->contents == NULL)
+    {
+      BFD_ASSERT (htab->plt_cfe_ctx);
+      bfd_size_type plt_ctf_frame_sec_size;
+      int err = 0;
+      void *contents = ctf_frame_write_encoder (htab->plt_cfe_ctx,
+						&plt_ctf_frame_sec_size,
+						&err);
+      htab->plt_ctf_frame->size = plt_ctf_frame_sec_size;
+      htab->plt_ctf_frame->contents
+	= (unsigned char *) bfd_zalloc (dynobj, htab->plt_ctf_frame->size);
+      memcpy (htab->plt_ctf_frame->contents, contents, plt_ctf_frame_sec_size);
+
+      ctf_free_encoder (htab->plt_cfe_ctx);
+    }
+
+  if (htab->plt_second_ctf_frame != NULL
+      && htab->elf.splt != NULL
+      && htab->elf.splt->size != 0
+      && htab->plt_second_ctf_frame->contents == NULL)
+    {
+      BFD_ASSERT (htab->plt_second_cfe_ctx);
+      bfd_size_type plt_ctf_frame_sec_size;
+      int err = 0;
+      void *contents = ctf_frame_write_encoder (htab->plt_second_cfe_ctx,
+						&plt_ctf_frame_sec_size,
+						&err);
+      htab->plt_second_ctf_frame->size = plt_ctf_frame_sec_size;
+      htab->plt_second_ctf_frame->contents
+	= (unsigned char *) bfd_zalloc (dynobj, htab->plt_second_ctf_frame->size);
+      memcpy (htab->plt_second_ctf_frame->contents, contents, plt_ctf_frame_sec_size);
+
+      ctf_free_encoder (htab->plt_second_cfe_ctx);
+    }
+
   return _bfd_elf_maybe_vxworks_add_dynamic_tags (output_bfd, info,
 						  relocs);
 }
@@ -2608,6 +2776,77 @@ _bfd_x86_elf_finish_dynamic_sections (bfd *output_bfd,
 	}
     }
 
+  /* Make any adjustment if necessary and merge .ctf_frame section to
+     create the final .ctf_frame section for output_bfd.  */
+  if (htab->plt_ctf_frame != NULL
+      && htab->plt_ctf_frame->contents != NULL)
+    {
+      if (htab->elf.splt != NULL
+	  && htab->elf.splt->size != 0
+	  && (htab->elf.splt->flags & SEC_EXCLUDE) == 0
+	  && htab->elf.splt->output_section != NULL
+	  && htab->plt_ctf_frame->output_section != NULL)
+	{
+	  bfd_vma plt_start = htab->elf.splt->output_section->vma;
+	  bfd_vma ctf_frame_start = htab->plt_ctf_frame->output_section->vma
+				   + htab->plt_ctf_frame->output_offset
+				   + PLT_CTF_FRAME_FDE_START_OFFSET;
+#if 0 /* FIXME Testing only. Remove before review.  */
+	  bfd_vma test_value = (plt_start - ctf_frame_start)
+	    + htab->plt_ctf_frame->output_section->vma
+	    + htab->plt_ctf_frame->output_offset
+	    + PLT_CTF_FRAME_FDE_START_OFFSET;
+	  bfd_put_signed_32 (dynobj, test_value,
+#endif
+	  bfd_put_signed_32 (dynobj, plt_start - ctf_frame_start,
+			     htab->plt_ctf_frame->contents
+			     + PLT_CTF_FRAME_FDE_START_OFFSET);
+	}
+      if (htab->plt_ctf_frame->sec_info_type == SEC_INFO_TYPE_CTF_FRAME)
+	{
+	  /* FIXME - contents need to be relocated for this call.  Need to
+	     reflect the relocated contents manually ?  */
+	  if (! _bfd_elf_merge_section_ctf_frame (output_bfd, info,
+						 htab->plt_ctf_frame,
+						 htab->plt_ctf_frame->contents))
+	    return NULL;
+	}
+    }
+
+  if (htab->plt_second_ctf_frame != NULL
+      && htab->plt_second_ctf_frame->contents != NULL)
+    {
+      if (htab->plt_second != NULL
+	  && htab->plt_second->size != 0
+	  && (htab->plt_second->flags & SEC_EXCLUDE) == 0
+	  && htab->plt_second->output_section != NULL
+	  && htab->plt_second_ctf_frame->output_section != NULL)
+	{
+	  bfd_vma plt_start = htab->plt_second->output_section->vma;
+	  bfd_vma ctf_frame_start = htab->plt_second_ctf_frame->output_section->vma
+				   + htab->plt_second_ctf_frame->output_offset
+				   + PLT_CTF_FRAME_FDE_START_OFFSET;
+#if 0 /* FIXME Testing only. Remove before review.  */
+	  bfd_vma test_value = (plt_start - ctf_frame_start)
+	    + htab->plt_second_ctf_frame->output_section->vma
+	    + htab->plt_second_ctf_frame->output_offset
+	    + PLT_CTF_FRAME_FDE_START_OFFSET;
+	  bfd_put_signed_32 (dynobj, test_value,
+#endif
+	  bfd_put_signed_32 (dynobj, plt_start - ctf_frame_start,
+			     htab->plt_second_ctf_frame->contents
+			     + PLT_CTF_FRAME_FDE_START_OFFSET);
+	}
+      if (htab->plt_second_ctf_frame->sec_info_type == SEC_INFO_TYPE_CTF_FRAME)
+	{
+	  /* FIXME - contents need to be relocated for this call.  Need to
+	     reflect the relocated contents manually ?  */
+	  if (! _bfd_elf_merge_section_ctf_frame (output_bfd, info,
+						 htab->plt_second_ctf_frame,
+						 htab->plt_second_ctf_frame->contents))
+	    return NULL;
+	}
+    }
   if (htab->elf.sgot && htab->elf.sgot->size > 0)
     elf_section_data (htab->elf.sgot->output_section)->this_hdr.sh_entsize
       = htab->got_entry_size;
@@ -3932,12 +4171,36 @@ _bfd_x86_elf_link_setup_gnu_properties
 
   pltsec = htab->elf.splt;
 
-  /* If the non-lazy PLT is available, use it for all PLT entries if
-     there are no PLT0 or no .plt section.  */
   if (htab->non_lazy_plt != NULL
       && (!htab->plt.has_plt0 || pltsec == NULL))
+    lazy_plt = false;
+  else
+    lazy_plt = true;
+
+  if (normal_target)
+    {
+      if (use_ibt_plt)
+	{
+	  if (lazy_plt)
+	    htab->ctf_frame_plt = init_table->ctf_frame_lazy_ibt_plt;
+	  else
+	    htab->ctf_frame_plt = init_table->ctf_frame_non_lazy_ibt_plt;
+	}
+      else
+	{
+	  if (lazy_plt)
+	    htab->ctf_frame_plt = init_table->ctf_frame_lazy_plt;
+	  else
+	    htab->ctf_frame_plt = init_table->ctf_frame_non_lazy_plt;
+	}
+    }
+  else
+    htab->ctf_frame_plt = NULL;
+
+  /* If the non-lazy PLT is available, use it for all PLT entries if
+     there are no PLT0 or no .plt section.  */
+  if (!lazy_plt)
     {
-      lazy_plt = false;
       if (bfd_link_pic (info))
 	htab->plt.plt_entry = htab->non_lazy_plt->pic_plt_entry;
       else
@@ -3952,7 +4215,6 @@ _bfd_x86_elf_link_setup_gnu_properties
     }
   else
     {
-      lazy_plt = true;
       if (bfd_link_pic (info))
 	{
 	  htab->plt.plt0_entry = htab->lazy_plt->pic_plt0_entry;
@@ -4128,6 +4390,39 @@ _bfd_x86_elf_link_setup_gnu_properties
 	      htab->plt_second_eh_frame = sec;
 	    }
 	}
+
+      /* .ctf_frame sections are emitted for AMD64 ABI only.  */
+      if (ABI_64_P (info->output_bfd) && !info->no_ld_generated_unwind_info)
+	{
+	  flagword flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
+			    | SEC_HAS_CONTENTS | SEC_IN_MEMORY
+			    | SEC_LINKER_CREATED);
+
+	  sec = bfd_make_section_anyway_with_flags (dynobj,
+						    ".ctf_frame",
+						    flags);
+	  if (sec == NULL)
+	    info->callbacks->einfo (_("%F%P: failed to create PLT .ctf_frame section\n"));
+
+	  // FIXME check this
+	  // if (!bfd_set_section_alignment (sec, class_align))
+	  //  goto error_alignment;
+
+	  htab->plt_ctf_frame = sec;
+
+	  /* Second PLT is generated for Intel IBT / MPX Support + lazy plt.  */
+	  if (htab->plt_second != NULL)
+	    {
+	      sec = bfd_make_section_anyway_with_flags (dynobj,
+							".ctf_frame",
+							flags);
+	      if (sec == NULL)
+		info->callbacks->einfo (_("%F%P: failed to create second PLT .ctf_frame section\n"));
+
+	      htab->plt_second_ctf_frame = sec;
+	    }
+	  /* FIXME - add later for plt_got. */
+	}
     }
 
   /* The .iplt section is used for IFUNC symbols in static
diff --git a/bfd/elfxx-x86.h b/bfd/elfxx-x86.h
index 77fb1ad72bc..84c945e50b8 100644
--- a/bfd/elfxx-x86.h
+++ b/bfd/elfxx-x86.h
@@ -30,6 +30,7 @@
 #include "elf-linker-x86.h"
 #include "elf/i386.h"
 #include "elf/x86-64.h"
+#include "ctf-frame-api.h"
 
 #define X86_64_PCREL_TYPE_P(TYPE) \
   ((TYPE) == R_X86_64_PC8 \
@@ -105,6 +106,8 @@
    || (TYPE) == R_X86_64_PC32_BND \
    || (TYPE) == R_X86_64_PC64)
 
+#define PLT_CTF_FRAME_FDE_START_OFFSET	sizeof (ctf_frame_header)
+
 #define ABI_64_P(abfd) \
   (get_elf_backend_data (abfd)->s->elfclass == ELFCLASS64)
 
@@ -389,6 +392,24 @@ struct elf_x86_link_hash_entry
   bfd_vma tlsdesc_got;
 };
 
+#define CTF_FRAME_PLT0_MAX_NUM_FRES 2
+#define CTF_FRAME_PLTN_MAX_NUM_FRES 2
+
+struct elf_x86_ctf_frame_plt
+{
+  unsigned int plt0_entry_size;
+  unsigned int plt0_num_fres;
+  const ctf_frame_row_entry *plt0_fres[CTF_FRAME_PLT0_MAX_NUM_FRES];
+
+  unsigned int pltn_entry_size;
+  unsigned int pltn_num_fres;
+  const ctf_frame_row_entry *pltn_fres[CTF_FRAME_PLTN_MAX_NUM_FRES];
+
+  unsigned int sec_pltn_entry_size;
+  unsigned int sec_pltn_num_fres;
+  const ctf_frame_row_entry *sec_pltn_fres[CTF_FRAME_PLTN_MAX_NUM_FRES];
+};
+
 struct elf_x86_lazy_plt_layout
 {
   /* The first entry in a lazy procedure linkage table looks like this.  */
@@ -585,6 +606,11 @@ struct elf_x86_link_hash_table
   asection *plt_got;
   asection *plt_got_eh_frame;
 
+  ctf_frame_encoder_ctx *plt_cfe_ctx;
+  asection *plt_ctf_frame;
+  ctf_frame_encoder_ctx *plt_second_cfe_ctx;
+  asection *plt_second_ctf_frame;
+
   /* Parameters describing PLT generation, lazy or non-lazy.  */
   struct elf_x86_plt_layout plt;
 
@@ -594,6 +620,10 @@ struct elf_x86_link_hash_table
   /* Parameters describing non-lazy PLT generation.  */
   const struct elf_x86_non_lazy_plt_layout *non_lazy_plt;
 
+  /* The .ctf_frame helper object for .plt section.
+     This is used for x86-64 only.  */
+  const struct elf_x86_ctf_frame_plt *ctf_frame_plt;
+
   union
   {
     bfd_signed_vma refcount;
@@ -683,6 +713,22 @@ struct elf_x86_init_table
   /* The non-lazy PLT layout for IBT.  */
   const struct elf_x86_non_lazy_plt_layout *non_lazy_ibt_plt;
 
+  /* The .ctf_frame helper object for lazy .plt section.
+     This is used for x86-64 only.  */
+  const struct elf_x86_ctf_frame_plt *ctf_frame_lazy_plt;
+
+  /* The .ctf_frame helper object for non-lazy .plt section.
+     This is used for x86-64 only.  */
+  const struct elf_x86_ctf_frame_plt *ctf_frame_non_lazy_plt;
+
+  /* The .ctf_frame helper object for lazy IBT .plt section.
+     This is used for x86-64 only.  */
+  const struct elf_x86_ctf_frame_plt *ctf_frame_lazy_ibt_plt;
+
+  /* The .ctf_frame helper object for non-lazy IBT .plt section.
+     This is used for x86-64 only.  */
+  const struct elf_x86_ctf_frame_plt *ctf_frame_non_lazy_ibt_plt;
+
   bfd_byte plt0_pad_byte;
 
   bfd_vma (*r_info) (bfd_vma, bfd_vma);
diff --git a/bfd/section.c b/bfd/section.c
index 5a487ce6c6f..fe65a22de57 100644
--- a/bfd/section.c
+++ b/bfd/section.c
@@ -408,6 +408,7 @@ CODE_FRAGMENT
 .#define SEC_INFO_TYPE_JUST_SYMS 4
 .#define SEC_INFO_TYPE_TARGET    5
 .#define SEC_INFO_TYPE_EH_FRAME_ENTRY 6
+.#define SEC_INFO_TYPE_CTF_FRAME  7
 .
 .  {* Nonzero if this section uses RELA relocations, rather than REL.  *}
 .  unsigned int use_rela_p:1;
diff --git a/binutils/readelf.c b/binutils/readelf.c
index d45e0920788..a86fafb0701 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -4653,6 +4653,7 @@ get_segment_type (Filedata * filedata, unsigned long p_type)
     case PT_GNU_STACK:	return "GNU_STACK";
     case PT_GNU_RELRO:  return "GNU_RELRO";
     case PT_GNU_PROPERTY: return "GNU_PROPERTY";
+    case PT_GNU_CTF_FRAME: return "GNU_CTF_FRAME";
 
     case PT_OPENBSD_RANDOMIZE: return "OPENBSD_RANDOMIZE";
     case PT_OPENBSD_WXNEEDED: return "OPENBSD_WXNEEDED";
diff --git a/include/elf/common.h b/include/elf/common.h
index e4bc53e35b4..9dee53d7cd5 100644
--- a/include/elf/common.h
+++ b/include/elf/common.h
@@ -489,6 +489,7 @@
 #define PT_GNU_STACK	(PT_LOOS + 0x474e551) /* Stack flags */
 #define PT_GNU_RELRO	(PT_LOOS + 0x474e552) /* Read-only after relocation */
 #define PT_GNU_PROPERTY	(PT_LOOS + 0x474e553) /* GNU property */
+#define PT_GNU_CTF_FRAME  (PT_LOOS + 0x474e554) /* CTF Frame unwind information */
 
 /* OpenBSD segment types.  */
 #define PT_OPENBSD_RANDOMIZE (PT_LOOS + 0x5a3dbe6)  /* Fill with random data.  */
diff --git a/include/elf/internal.h b/include/elf/internal.h
index 8affb3d9b2e..0988a66679f 100644
--- a/include/elf/internal.h
+++ b/include/elf/internal.h
@@ -339,6 +339,7 @@ struct elf_segment_map
 	    || (segment)->p_type == PT_GNU_EH_FRAME			\
 	    || (segment)->p_type == PT_GNU_STACK			\
 	    || (segment)->p_type == PT_GNU_RELRO			\
+	    || (segment)->p_type == PT_GNU_CTF_FRAME			\
 	    || ((segment)->p_type >= PT_GNU_MBIND_LO			\
 		&& (segment)->p_type <= PT_GNU_MBIND_HI)))		\
    /* Any section besides one of type SHT_NOBITS must have file		\
diff --git a/ld/ld.texi b/ld/ld.texi
index 8cad8478140..dcdad543191 100644
--- a/ld/ld.texi
+++ b/ld/ld.texi
@@ -2828,7 +2828,9 @@ section and ELF @code{PT_GNU_EH_FRAME} segment header.
 @item --no-ld-generated-unwind-info
 Request creation of @code{.eh_frame} unwind info for linker
 generated code sections like PLT.  This option is on by default
-if linker generated unwind info is supported.
+if linker generated unwind info is supported.  This option also
+controls the generation of @code{.ctf_frame} unwind info for linker
+generated code sections like PLT.
 
 @kindex --enable-new-dtags
 @kindex --disable-new-dtags
diff --git a/ld/scripttempl/elf.sc b/ld/scripttempl/elf.sc
index bf2268bb0ad..db2c4043d90 100644
--- a/ld/scripttempl/elf.sc
+++ b/ld/scripttempl/elf.sc
@@ -601,6 +601,7 @@ cat <<EOF
   ${OTHER_READONLY_SECTIONS}
   .eh_frame_hdr ${RELOCATING-0} : { *(.eh_frame_hdr)${RELOCATING+ *(.eh_frame_entry .eh_frame_entry.*)} }
   .eh_frame     ${RELOCATING-0} : ONLY_IF_RO { KEEP (*(.eh_frame))${RELOCATING+ *(.eh_frame.*)} }
+  .ctf_frame    ${RELOCATING-0} : ONLY_IF_RO { KEEP (*(.ctf_frame))${RELOCATING+ *(.ctf_frame.*)} }
   .gcc_except_table ${RELOCATING-0} : ONLY_IF_RO { *(.gcc_except_table${RELOCATING+ .gcc_except_table.*}) }
   .gnu_extab ${RELOCATING-0} : ONLY_IF_RO { *(.gnu_extab*) }
   /* These sections are generated by the Sun/Oracle C++ compiler.  */
@@ -619,6 +620,7 @@ cat <<EOF
 
   /* Exception handling  */
   .eh_frame     ${RELOCATING-0} : ONLY_IF_RW { KEEP (*(.eh_frame))${RELOCATING+ *(.eh_frame.*)} }
+  .ctf_frame    ${RELOCATING-0} : ONLY_IF_RW { KEEP (*(.ctf_frame))${RELOCATING+ *(.ctf_frame.*)} }
   .gnu_extab    ${RELOCATING-0} : ONLY_IF_RW { *(.gnu_extab) }
   .gcc_except_table ${RELOCATING-0} : ONLY_IF_RW { *(.gcc_except_table${RELOCATING+ .gcc_except_table.*}) }
   .exception_ranges ${RELOCATING-0} : ONLY_IF_RW { *(.exception_ranges${RELOCATING+*}) }
-- 
2.31.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH,RFC 5/7] readelf/objdump: support for CTF Frame section
  2022-05-07  0:52 [PATCH,RFC 0/7] Definition and Implementation of CTF Frame format Indu Bhagat
                   ` (2 preceding siblings ...)
  2022-05-07  0:52 ` [PATCH,RFC 4/7] bfd: linker: merge .ctf_frame sections Indu Bhagat
@ 2022-05-07  0:52 ` Indu Bhagat
  2022-05-07  0:52 ` [PATCH,RFC 6/7] unwinder: generate backtrace using CTF Frame format Indu Bhagat
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Indu Bhagat @ 2022-05-07  0:52 UTC (permalink / raw)
  To: binutils

This patch adds support for CTF Frame in readelf and objdump.

Changelog:
	* binutils/Makefile.am: Add dependency on libctfframe for
	readelf and objdump.
	* binutils/Makefile.in: Regenerated.
	* binutils/doc/binutils.texi: Document --ctf-frame=[section].
	* binutils/doc/ctfframe.options.texi: Likewise.
	* binutils/objdump.c: Add support for CTF Frame format.
	* binutils/readelf.c: Likewise.
	* include/ctf-frame-api.h: Add new API for dumping .ctf_frame
	section.
	* libctfframe/Makefile.am: Add ctf-frame-dump.c.
	* libctfframe/Makefile.in: Regenerated.
	* libctfframe/ctf-frame-dump.c: New file.
---
 binutils/Makefile.am               |   8 +-
 binutils/Makefile.in               |   9 +-
 binutils/doc/binutils.texi         |   4 +
 binutils/doc/ctfframe.options.texi |  10 ++
 binutils/objdump.c                 |  74 +++++++++++++
 binutils/readelf.c                 |  43 ++++++++
 include/ctf-frame-api.h            |   3 +
 libctfframe/Makefile.am            |   2 +-
 libctfframe/Makefile.in            |   6 +-
 libctfframe/ctf-frame-dump.c       | 162 +++++++++++++++++++++++++++++
 10 files changed, 310 insertions(+), 11 deletions(-)
 create mode 100644 binutils/doc/ctfframe.options.texi
 create mode 100644 libctfframe/ctf-frame-dump.c

diff --git a/binutils/Makefile.am b/binutils/Makefile.am
index 17dcc27fc86..7cebe055d55 100644
--- a/binutils/Makefile.am
+++ b/binutils/Makefile.am
@@ -228,7 +228,7 @@ installcheck-local:
 # which depends on libintl, since we don't know whether LIBINTL_DEP will be
 # non-empty until configure time.  Ugh!
 size_DEPENDENCIES =      $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
-objdump_DEPENDENCIES =   $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB) $(OPCODES) $(LIBCTF) $(OBJDUMP_PRIVATE_OFILES)
+objdump_DEPENDENCIES =   $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB) $(OPCODES) $(LIBCTF) $(OBJDUMP_PRIVATE_OFILES) $(LIBCTFFRAME)
 nm_new_DEPENDENCIES =    $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
 ar_DEPENDENCIES =        $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
 strings_DEPENDENCIES =   $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
@@ -243,7 +243,7 @@ dlltool_DEPENDENCIES =   $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
 windres_DEPENDENCIES =   $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
 windmc_DEPENDENCIES =    $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
 addr2line_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
-readelf_DEPENDENCIES =   $(LIBINTL_DEP) $(LIBIBERTY) $(LIBCTF_NOBFD)
+readelf_DEPENDENCIES =   $(LIBINTL_DEP) $(LIBIBERTY) $(LIBCTF_NOBFD) $(LIBCTFFRAME)
 elfedit_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY)
 dllwrap_DEPENDENCIES =   $(LIBINTL_DEP) $(LIBIBERTY)
 bfdtest1_DEPENDENCIES =  $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
@@ -258,7 +258,7 @@ objcopy_SOURCES = objcopy.c not-strip.c rename.c $(WRITE_DEBUG_SRCS) $(BULIBS)
 strings_SOURCES = strings.c $(BULIBS)
 
 readelf_SOURCES = readelf.c version.c unwind-ia64.c dwarf.c demanguse.c $(ELFLIBS)
-readelf_LDADD   = $(LIBCTF_NOBFD) $(LIBINTL) $(LIBIBERTY) $(ZLIB) $(DEBUGINFOD_LIBS) $(MSGPACK_LIBS)
+readelf_LDADD   = $(LIBCTF_NOBFD) $(LIBINTL) $(LIBIBERTY) $(ZLIB) $(DEBUGINFOD_LIBS) $(MSGPACK_LIBS) $(LIBCTFFRAME)
 
 elfedit_SOURCES = elfedit.c version.c $(ELFLIBS)
 elfedit_LDADD = $(LIBINTL) $(LIBIBERTY)
@@ -269,7 +269,7 @@ nm_new_SOURCES = nm.c demanguse.c $(BULIBS)
 
 objdump_SOURCES = objdump.c dwarf.c prdbg.c demanguse.c $(DEBUG_SRCS) $(BULIBS) $(ELFLIBS)
 EXTRA_objdump_SOURCES = od-xcoff.c
-objdump_LDADD = $(OBJDUMP_PRIVATE_OFILES) $(OPCODES) $(LIBCTF) $(BFDLIB) $(LIBIBERTY) $(LIBINTL) $(DEBUGINFOD_LIBS)
+objdump_LDADD = $(OBJDUMP_PRIVATE_OFILES) $(OPCODES) $(LIBCTF) $(BFDLIB) $(LIBIBERTY) $(LIBINTL) $(DEBUGINFOD_LIBS) $(LIBCTFFRAME)
 
 objdump.@OBJEXT@:objdump.c
 if am__fastdepCC
diff --git a/binutils/Makefile.in b/binutils/Makefile.in
index 2ed7f05bf37..4876b6c711b 100644
--- a/binutils/Makefile.in
+++ b/binutils/Makefile.in
@@ -618,6 +618,7 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
+runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -763,7 +764,7 @@ CC_FOR_TARGET = ` \
 # which depends on libintl, since we don't know whether LIBINTL_DEP will be
 # non-empty until configure time.  Ugh!
 size_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
-objdump_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB) $(OPCODES) $(LIBCTF) $(OBJDUMP_PRIVATE_OFILES)
+objdump_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB) $(OPCODES) $(LIBCTF) $(OBJDUMP_PRIVATE_OFILES) $(LIBCTFFRAME)
 nm_new_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
 ar_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
 strings_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
@@ -778,7 +779,7 @@ dlltool_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
 windres_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
 windmc_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
 addr2line_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
-readelf_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(LIBCTF_NOBFD)
+readelf_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(LIBCTF_NOBFD) $(LIBCTFFRAME)
 elfedit_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY)
 dllwrap_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY)
 bfdtest1_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB)
@@ -788,14 +789,14 @@ size_SOURCES = size.c $(BULIBS)
 objcopy_SOURCES = objcopy.c not-strip.c rename.c $(WRITE_DEBUG_SRCS) $(BULIBS)
 strings_SOURCES = strings.c $(BULIBS)
 readelf_SOURCES = readelf.c version.c unwind-ia64.c dwarf.c demanguse.c $(ELFLIBS)
-readelf_LDADD = $(LIBCTF_NOBFD) $(LIBINTL) $(LIBIBERTY) $(ZLIB) $(DEBUGINFOD_LIBS) $(MSGPACK_LIBS)
+readelf_LDADD = $(LIBCTF_NOBFD) $(LIBINTL) $(LIBIBERTY) $(ZLIB) $(DEBUGINFOD_LIBS) $(MSGPACK_LIBS) $(LIBCTFFRAME)
 elfedit_SOURCES = elfedit.c version.c $(ELFLIBS)
 elfedit_LDADD = $(LIBINTL) $(LIBIBERTY)
 strip_new_SOURCES = objcopy.c is-strip.c rename.c $(WRITE_DEBUG_SRCS) $(BULIBS)
 nm_new_SOURCES = nm.c demanguse.c $(BULIBS)
 objdump_SOURCES = objdump.c dwarf.c prdbg.c demanguse.c $(DEBUG_SRCS) $(BULIBS) $(ELFLIBS)
 EXTRA_objdump_SOURCES = od-xcoff.c
-objdump_LDADD = $(OBJDUMP_PRIVATE_OFILES) $(OPCODES) $(LIBCTF) $(BFDLIB) $(LIBIBERTY) $(LIBINTL) $(DEBUGINFOD_LIBS)
+objdump_LDADD = $(OBJDUMP_PRIVATE_OFILES) $(OPCODES) $(LIBCTF) $(BFDLIB) $(LIBIBERTY) $(LIBINTL) $(DEBUGINFOD_LIBS) $(LIBCTFFRAME)
 cxxfilt_SOURCES = cxxfilt.c $(BULIBS)
 ar_SOURCES = arparse.y arlex.l ar.c not-ranlib.c arsup.c rename.c binemul.c \
 	emul_$(EMULATION).c $(BULIBS)
diff --git a/binutils/doc/binutils.texi b/binutils/doc/binutils.texi
index 2c234c682aa..777e1ed1c37 100644
--- a/binutils/doc/binutils.texi
+++ b/binutils/doc/binutils.texi
@@ -2250,6 +2250,7 @@ objdump [@option{-a}|@option{--archive-headers}]
         [@option{-wE}|@option{--dwarf=do-not-use-debuginfod}]
         [@option{-L}|@option{--process-links}]
         [@option{--ctf=}@var{section}]
+        [@option{--ctf-frame=}@var{section}]
         [@option{-G}|@option{--stabs}]
         [@option{-t}|@option{--syms}]
         [@option{-T}|@option{--dynamic-syms}]
@@ -2827,6 +2828,8 @@ Enable additional checks for consistency of Dwarf information.
 
 @include ctf.options.texi
 
+@include ctfframe.options.texi
+
 @item -G
 @itemx --stabs
 @cindex stab
@@ -4902,6 +4905,7 @@ readelf [@option{-a}|@option{--all}]
         [@option{--dwarf-depth=@var{n}}]
         [@option{--dwarf-start=@var{n}}]
         [@option{--ctf=}@var{section}]
+        [@option{--ctf-frame=}@var{section}]
         [@option{--ctf-parent=}@var{section}]
         [@option{--ctf-symbols=}@var{section}]
         [@option{--ctf-strings=}@var{section}]
diff --git a/binutils/doc/ctfframe.options.texi b/binutils/doc/ctfframe.options.texi
new file mode 100644
index 00000000000..bc74e891005
--- /dev/null
+++ b/binutils/doc/ctfframe.options.texi
@@ -0,0 +1,10 @@
+@c This file contains the entry for the --ctfframe option that is
+@c common to both readeld and objdump.
+
+@item --ctf-frame[=@var{section}]
+@cindex CTF Frame
+
+Display the contents of the specified CTF Frame section.
+
+By default, display the name of the section named @var{.ctf_frame}, which is the
+name emitted by @command{ld}.
diff --git a/binutils/objdump.c b/binutils/objdump.c
index 060a136efa4..8a5ffa2007c 100644
--- a/binutils/objdump.c
+++ b/binutils/objdump.c
@@ -58,6 +58,7 @@
 #include "demanguse.h"
 #include "dwarf.h"
 #include "ctf-api.h"
+#include "ctf-frame-api.h"
 #include "getopt.h"
 #include "safe-ctype.h"
 #include "dis-asm.h"
@@ -104,6 +105,8 @@ static int dump_stab_section_info;	/* --stabs */
 static int dump_ctf_section_info;       /* --ctf */
 static char *dump_ctf_section_name;
 static char *dump_ctf_parent_name;	/* --ctf-parent */
+static int dump_ctf_frame_section_info; /* --ctf-frame */
+static char *dump_ctf_frame_section_name;
 static int do_demangle;			/* -C, --demangle */
 static bool disassemble;		/* -d */
 static bool disassemble_all;		/* -D */
@@ -306,6 +309,9 @@ usage (FILE *stream, int status)
   fprintf (stream, _("\
       --ctf[=SECTION]      Display CTF info from SECTION, (default `.ctf')\n"));
 #endif
+  fprintf (stream, _("\
+      --ctf-frame[=SECTION]\n\
+                           Display CTF info from SECTION, (default `.ctf_frame)\n"));
   fprintf (stream, _("\
   -t, --syms               Display the contents of the symbol table(s)\n"));
   fprintf (stream, _("\
@@ -450,6 +456,7 @@ enum option_values
     OPTION_CTF,
     OPTION_CTF_PARENT,
 #endif
+    OPTION_CTF_FRAME,
     OPTION_VISUALIZE_JUMPS,
     OPTION_DISASSEMBLER_COLOR
   };
@@ -464,6 +471,7 @@ static struct option long_options[]=
   {"ctf", optional_argument, NULL, OPTION_CTF},
   {"ctf-parent", required_argument, NULL, OPTION_CTF_PARENT},
 #endif
+  {"ctf-frame", optional_argument, NULL, OPTION_CTF_FRAME},
   {"debugging", no_argument, NULL, 'g'},
   {"debugging-tags", no_argument, NULL, 'e'},
   {"demangle", optional_argument, NULL, 'C'},
@@ -4593,6 +4601,64 @@ dump_ctf (bfd *abfd ATTRIBUTE_UNUSED, const char *sect_name ATTRIBUTE_UNUSED,
 	  const char *parent_name ATTRIBUTE_UNUSED) {}
 #endif
 
+static bfd_byte*
+read_section_ctf_frame (bfd *abfd, const char *sect_name, bfd_size_type *size_ptr,
+			bfd_vma *ctf_frame_vma)
+{
+  asection *ctf_frame_sect;
+  bfd_byte *contents;
+
+  ctf_frame_sect = bfd_get_section_by_name (abfd, sect_name);
+  if (ctf_frame_sect == NULL)
+    {
+      printf (_("No %s section present\n\n"),
+	      sanitize_string (sect_name));
+      return false;
+    }
+
+  if (!bfd_malloc_and_get_section (abfd, ctf_frame_sect, &contents))
+    {
+      non_fatal (_("reading %s section of %s failed: %s"),
+		 sect_name, bfd_get_filename (abfd),
+		 bfd_errmsg (bfd_get_error ()));
+      exit_status = 1;
+      free (contents);
+      return NULL;
+    }
+
+  *size_ptr = bfd_section_size (ctf_frame_sect);
+  *ctf_frame_vma = bfd_section_vma (ctf_frame_sect);
+
+  return contents;
+}
+
+static void
+dump_section_ctf_frame (bfd *abfd ATTRIBUTE_UNUSED,
+			const char * sect_name)
+{
+  ctf_frame_decoder_ctx	  *cfd_ctx = NULL;
+  size_t cf_size;
+  bfd_byte *ctf_frame_data = NULL;
+  bfd_vma cf_vma;
+  int err = 0;
+
+  if (sect_name == NULL)
+    sect_name = ".ctf_frame";
+
+  ctf_frame_data = read_section_ctf_frame (abfd, sect_name, &cf_size, &cf_vma);
+
+  if (ctf_frame_data == NULL)
+    bfd_fatal (bfd_get_filename (abfd));
+
+  /* Decode the contents of the section.  */
+  cfd_ctx = ctf_frame_decode ((const char*)ctf_frame_data, cf_size, &err);
+  if (!cfd_ctx)
+    bfd_fatal (bfd_get_filename (abfd));
+
+  /* Dump the contents as text.  */
+  dump_ctf_frame (cfd_ctx, cf_vma);
+}
+
 \f
 static void
 dump_bfd_private_header (bfd *abfd)
@@ -5334,6 +5400,8 @@ dump_bfd (bfd *abfd, bool is_mainfile)
     {
       if (dump_ctf_section_info)
 	dump_ctf (abfd, dump_ctf_section_name, dump_ctf_parent_name);
+      if (dump_ctf_frame_section_info)
+	dump_section_ctf_frame (abfd, dump_ctf_frame_section_name);
       if (dump_stab_section_info)
 	dump_stabs (abfd);
       if (dump_reloc_info && ! disassemble)
@@ -5825,6 +5893,12 @@ main (int argc, char **argv)
 	  dump_ctf_parent_name = xstrdup (optarg);
 	  break;
 #endif
+	case OPTION_CTF_FRAME:
+	  dump_ctf_frame_section_info = true;
+	  if (optarg)
+	    dump_ctf_frame_section_name = xstrdup (optarg);
+	  seenflag = true;
+	  break;
 	case 'G':
 	  dump_stab_section_info = true;
 	  seenflag = true;
diff --git a/binutils/readelf.c b/binutils/readelf.c
index a86fafb0701..bb933764245 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -64,6 +64,7 @@
 #include "demanguse.h"
 #include "dwarf.h"
 #include "ctf-api.h"
+#include "ctf-frame-api.h"
 #include "demangle.h"
 
 #include "elf/common.h"
@@ -192,6 +193,7 @@ typedef struct elf_section_list
 #define STRING_DUMP     (1 << 3)	/* The -p command line switch.  */
 #define RELOC_DUMP      (1 << 4)	/* The -R command line switch.  */
 #define CTF_DUMP	(1 << 5)	/* The --ctf command line switch.  */
+#define CTF_FRAME_DUMP	(1 << 6)	/* The --ctf-frame command line switch.  */
 
 typedef unsigned char dump_type;
 
@@ -235,6 +237,7 @@ static bool do_version = false;
 static bool do_histogram = false;
 static bool do_debugging = false;
 static bool do_ctf = false;
+static bool do_ctf_frame = false;
 static bool do_arch = false;
 static bool do_notes = false;
 static bool do_archive_index = false;
@@ -5120,6 +5123,7 @@ enum long_option_values
   OPTION_CTF_PARENT,
   OPTION_CTF_SYMBOLS,
   OPTION_CTF_STRINGS,
+  OPTION_CTF_FRAME_DUMP,
   OPTION_WITH_SYMBOL_VERSIONS,
   OPTION_RECURSE_LIMIT,
   OPTION_NO_RECURSE_LIMIT,
@@ -5182,6 +5186,7 @@ static struct option options[] =
   {"ctf-strings",      required_argument, 0, OPTION_CTF_STRINGS},
   {"ctf-parent",       required_argument, 0, OPTION_CTF_PARENT},
 #endif
+  {"ctf-frame",	       required_argument, 0, OPTION_CTF_FRAME_DUMP},
   {"sym-base",	       optional_argument, 0, OPTION_SYM_BASE},
 
   {0,		       no_argument, 0, 0}
@@ -5322,6 +5327,8 @@ usage (FILE * stream)
   --ctf-strings=<number|name>\n\
                          Use section <number|name> as the CTF external strtab\n"));
 #endif
+  fprintf (stream, _("\
+  --ctf-frame=<name>     Display CTF frame info from section name\n"));
 
 #ifdef SUPPORT_DISASSEMBLY
   fprintf (stream, _("\
@@ -5587,6 +5594,10 @@ parse_args (struct dump_data *dumpdata, int argc, char ** argv)
 	  free (dump_ctf_parent_name);
 	  dump_ctf_parent_name = strdup (optarg);
 	  break;
+	case OPTION_CTF_FRAME_DUMP:
+	  do_ctf_frame = true;
+	  request_dump (dumpdata, CTF_FRAME_DUMP);
+	  break;
 	case OPTION_DYN_SYMS:
 	  do_dyn_syms = true;
 	  break;
@@ -15832,6 +15843,33 @@ dump_section_as_ctf (Elf_Internal_Shdr * section, Filedata * filedata)
 }
 #endif
 
+static bool
+dump_section_as_ctf_frame (Elf_Internal_Shdr * section, Filedata * filedata)
+{
+  void *		  data = NULL;
+  ctf_frame_decoder_ctx	  *cfd_ctx = NULL;
+
+  bool ret = true;
+  size_t cf_size;
+  int err = 0;
+
+  data = get_section_contents (section, filedata);
+  cf_size = section->sh_size;
+  /* Decode the contents of the section.  */
+  cfd_ctx = ctf_frame_decode ((const char*)data, cf_size, &err);
+  if (!cfd_ctx)
+    {
+      ret = false;
+      goto fail;
+    }
+  /* Dump the contents as text.  */
+  dump_ctf_frame (cfd_ctx, section->sh_addr);
+
+ fail:
+  free (data);
+  return ret;
+}
+
 static bool
 load_specific_debug_section (enum dwarf_section_display_enum  debug,
 			     const Elf_Internal_Shdr *        sec,
@@ -16331,6 +16369,11 @@ process_section_contents (Filedata * filedata)
 	    res = false;
 	}
 #endif
+      if (dump & CTF_FRAME_DUMP)
+	{
+	  if (! dump_section_as_ctf_frame (section, filedata))
+	    res = false;
+	}
     }
 
   if (! filedata->is_separate)
diff --git a/include/ctf-frame-api.h b/include/ctf-frame-api.h
index 076c4a40dad..bfee671bef0 100644
--- a/include/ctf-frame-api.h
+++ b/include/ctf-frame-api.h
@@ -144,6 +144,9 @@ ctf_frame_decoder_get_funcdesc (ctf_frame_decoder_ctx *ctx,
 				int32_t *func_start_address,
 				unsigned char *func_info);
 
+/* CTF Frame textual dump.  */
+extern void
+dump_ctf_frame (ctf_frame_decoder_ctx *decoder, uint64_t addr);
 
 /* Get the base reg id from the FRE info.  Sets errp if fails.  */
 extern unsigned int
diff --git a/libctfframe/Makefile.am b/libctfframe/Makefile.am
index 722e526be20..e321e071517 100644
--- a/libctfframe/Makefile.am
+++ b/libctfframe/Makefile.am
@@ -35,4 +35,4 @@ include_HEADERS =
 noinst_LTLIBRARIES = libctfframe.la
 endif
 
-libctfframe_la_SOURCES = ctf-frame.c ctf-frame-error.c
+libctfframe_la_SOURCES = ctf-frame.c ctf-frame-dump.c ctf-frame-error.c
diff --git a/libctfframe/Makefile.in b/libctfframe/Makefile.in
index ad59fe2be39..50a720c8a99 100644
--- a/libctfframe/Makefile.in
+++ b/libctfframe/Makefile.in
@@ -160,7 +160,8 @@ am__uninstall_files_from_dir = { \
 am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"
 LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES)
 libctfframe_la_LIBADD =
-am_libctfframe_la_OBJECTS = ctf-frame.lo ctf-frame-error.lo
+am_libctfframe_la_OBJECTS = ctf-frame.lo ctf-frame-dump.lo \
+	ctf-frame-error.lo
 libctfframe_la_OBJECTS = $(am_libctfframe_la_OBJECTS)
 AM_V_lt = $(am__v_lt_@AM_V@)
 am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
@@ -389,7 +390,7 @@ AM_CFLAGS = -std=gnu99 @ac_libctfframe_warn_cflags@ @warn@ @c_warn@ @WARN_PEDANT
 @INSTALL_LIBBFD_FALSE@include_HEADERS = 
 @INSTALL_LIBBFD_TRUE@include_HEADERS = $(INCDIR)/ctf-frame.h $(INCDIR)/ctf-frame-api.h
 @INSTALL_LIBBFD_FALSE@noinst_LTLIBRARIES = libctfframe.la
-libctfframe_la_SOURCES = ctf-frame.c ctf-frame-error.c
+libctfframe_la_SOURCES = ctf-frame.c ctf-frame-dump.c ctf-frame-error.c
 all: config.h
 	$(MAKE) $(AM_MAKEFLAGS) all-am
 
@@ -499,6 +500,7 @@ mostlyclean-compile:
 distclean-compile:
 	-rm -f *.tab.c
 
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-frame-dump.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-frame-error.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-frame.Plo@am__quote@
 
diff --git a/libctfframe/ctf-frame-dump.c b/libctfframe/ctf-frame-dump.c
new file mode 100644
index 00000000000..c01e478e59b
--- /dev/null
+++ b/libctfframe/ctf-frame-dump.c
@@ -0,0 +1,162 @@
+/* ctf-frame-dump.c - Textual dump of .ctf_frame.
+
+   Copyright (C) 2022 Free Software Foundation, Inc.
+
+   his file is part of libctfframe.
+
+   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, see <http://www.gnu.org/licenses/>.  */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "ctf-frame-impl.h"
+
+#define CTF_FRAME_HEADER_FLAGS_STR_MAX_LEN 50
+
+static void
+dump_ctf_frame_header (ctf_frame_decoder_ctx *cfd_ctx)
+{
+  const char *verstr = NULL;
+  const ctf_frame_header *header = &(cfd_ctx->cfd_header);
+
+  /* Prepare CTF frame section version string.  */
+  const char *version_names[]
+    = { "NULL",
+	"CTF_FRAME_VERSION_1" };
+  unsigned char ver = header->cth_frame_preamble.ctfp_version;
+  if (ver <= CTF_FRAME_VERSION)
+    verstr = version_names[ver];
+
+  /* Prepare CTF frame section flags string.  */
+  unsigned char flags = header->cth_frame_preamble.ctfp_flags;
+  char *flags_str 
+    = (char*) calloc (sizeof (char), CTF_FRAME_HEADER_FLAGS_STR_MAX_LEN);
+  if (flags)
+    {
+      const char *flag_names[]
+	= { "CTF_FRAME_F_FDE_SORTED",
+	    "CTF_FRAME_F_FRAME_POINTER" };
+      unsigned char flags = header->cth_frame_preamble.ctfp_flags;
+      if (flags & CTF_FRAME_F_FDE_SORTED)
+	strcpy (flags_str, flag_names[0]);
+      if (flags & CTF_FRAME_F_FRAME_POINTER)
+	{
+	  if (strlen (flags_str) > 0)
+	    strcpy (flags_str, ",");
+	  strcpy (flags_str, flag_names[1]);
+	}
+    }
+  else
+    strcpy (flags_str, "NONE");
+
+  const char* subsec_name = "Header";
+  printf ("\n");
+  printf ("  %s :\n", subsec_name);
+  printf ("\n");
+  printf ("    Version: %s\n", verstr);
+  printf ("    Flags: %s\n", flags_str);
+  printf ("    Num FDEs: %d\n", header->cth_num_fdes);
+  printf ("    Num FREs: %d\n", header->cth_num_fres);
+
+  free (flags_str);
+}
+
+static void
+dump_ctf_frame_func_with_fres (ctf_frame_decoder_ctx *cfd_ctx,
+			       unsigned int funcidx,
+			       uint64_t sec_addr)
+{
+  uint32_t j = 0;
+  uint32_t num_fres = 0;
+  uint32_t func_size = 0;
+  int32_t func_start_address = 0;
+  unsigned char func_info = 0;
+
+  uint64_t func_start_pc_vma = 0;
+  uint64_t fre_start_pc_vma = 0;
+  const char *base_reg_str[] = {"fp", "sp"};
+  int32_t cfa_offset = 0;
+  int32_t fp_offset = 0;
+  int32_t ra_offset = 0;
+  unsigned int base_reg_id = 0;
+  int err[3] = {0, 0, 0};
+
+  ctf_frame_row_entry fre;
+
+  /* Get the CTF frame function descriptor.  */
+  ctf_frame_decoder_get_funcdesc (cfd_ctx, funcidx, &num_fres,
+				  &func_size, &func_start_address, &func_info);
+  ctf_frame_assert (func_info);
+  /* Calculate the virtual memory address for function start pc.  */
+  func_start_pc_vma = func_start_address + sec_addr;
+
+  printf ("\n    func idx [%d]: pc = 0x%lx, size = %d bytes",
+	  funcidx,
+	  func_start_pc_vma,
+	  func_size);
+
+  printf ("\n    %-16s\t%s\t%s\t%s", "STARTPC", "CFA", "FP", "RA");
+  for (j = 0; j < num_fres; j++)
+    {
+      ctf_frame_decoder_get_fre (cfd_ctx, funcidx, j, &fre);
+
+      fre_start_pc_vma
+	= func_start_pc_vma + fre.fre_start_addr;
+
+      /* FIXME - fixup the err caching in array.
+	 assert no error for base reg id.  */
+      base_reg_id = ctf_frame_fre_get_base_reg_id (&fre, &err[0]);
+      cfa_offset = ctf_frame_fre_get_cfa_offset (&fre, &err[0]);
+      fp_offset = ctf_frame_fre_get_fp_offset (&fre, &err[1]);
+      ra_offset = ctf_frame_fre_get_ra_offset (&fre, &err[2]);
+
+      printf ("\n");
+      printf ("    %016lx", fre_start_pc_vma);
+      printf ("\t%s+%d", base_reg_str[base_reg_id], cfa_offset);
+
+      if (err[1] == 0)
+	printf ("\tc%d", fp_offset); // FIXME + or - sign
+      else
+	printf ("\t%s", "u");
+
+      if (err[2] == 0)
+	printf ("\tc%d", ra_offset); // FIXME + or - sign
+      else
+	printf ("\t%s", "u");
+    }
+}
+
+static void
+dump_ctf_frame_functions (ctf_frame_decoder_ctx *cfd_ctx, uint64_t sec_addr)
+{
+  uint32_t i;
+  uint32_t num_fdes;
+
+  const char* subsec_name = "Function Index";
+  printf ("\n  %s :\n", subsec_name);
+
+  num_fdes = ctf_frame_decoder_get_num_fidx (cfd_ctx);
+  for (i = 0; i < num_fdes; i++)
+    {
+      dump_ctf_frame_func_with_fres (cfd_ctx, i, sec_addr);
+      printf ("\n");
+    }
+}
+
+void
+dump_ctf_frame (ctf_frame_decoder_ctx *cfd_ctx, uint64_t sec_addr)
+{
+  dump_ctf_frame_header (cfd_ctx);
+  dump_ctf_frame_functions (cfd_ctx, sec_addr);
+}
-- 
2.31.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH,RFC 6/7] unwinder: generate backtrace using CTF Frame format
  2022-05-07  0:52 [PATCH,RFC 0/7] Definition and Implementation of CTF Frame format Indu Bhagat
                   ` (3 preceding siblings ...)
  2022-05-07  0:52 ` [PATCH,RFC 5/7] readelf/objdump: support for CTF Frame section Indu Bhagat
@ 2022-05-07  0:52 ` Indu Bhagat
  2022-05-07  0:52 ` [PATCH, RFC 7/7] gdb: sim: buildsystem changes to accommodate libctfframe Indu Bhagat
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Indu Bhagat @ 2022-05-07  0:52 UTC (permalink / raw)
  To: binutils

From: Weimin Pan <weimin.pan@oracle.com>

A simple unwinder based on CTF Frame format.

Changelog:
	* include/ctf-backtrace-api.h: New file.
	* libctfframe/ctf-backtrace-err.c: Likewise.
	* libctfframe/ctf-backtrace.c: Likewise.
	* libctfframe/ttest.c: Likewise.
	* libctfframe/Makefile.am: Build backtrace functionality in its
	own library.
	* libctfframe/Makefile.in: Regenerated.
---
 include/ctf-backtrace-api.h     |  57 +++
 libctfframe/Makefile.am         |  13 +-
 libctfframe/Makefile.in         |  81 ++++-
 libctfframe/ctf-backtrace-err.c |  46 +++
 libctfframe/ctf-backtrace.c     | 617 ++++++++++++++++++++++++++++++++
 libctfframe/ttest.c             |  78 ++++
 6 files changed, 871 insertions(+), 21 deletions(-)
 create mode 100644 include/ctf-backtrace-api.h
 create mode 100644 libctfframe/ctf-backtrace-err.c
 create mode 100644 libctfframe/ctf-backtrace.c
 create mode 100644 libctfframe/ttest.c

diff --git a/include/ctf-backtrace-api.h b/include/ctf-backtrace-api.h
new file mode 100644
index 00000000000..7b3463d88a3
--- /dev/null
+++ b/include/ctf-backtrace-api.h
@@ -0,0 +1,57 @@
+/* Public API to CTF backtrace.
+
+   Copyright (C) 2022 Free Software Foundation, Inc.
+
+   This file is part of xxxxxx.    (FIXME)
+
+   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, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef	_CTF_BACKTRACE_API_H
+#define	_CTF_BACKTRACE_API_H
+
+#ifdef	__cplusplus
+extern "C"
+{
+#endif
+
+enum ctf_bt_errcode
+{
+  CTF_BT_OK,
+  CTF_BT_ERR_NOCTF,
+  CTF_BT_ERR_PHDR,
+  CTF_BT_ERR_ARG,
+  CTF_BT_ERR_MALLOC,
+  CTF_BT_ERR_REALLOC,
+  CTF_BT_ERR_OPEN,
+  CTF_BT_ERR_READLINK,
+  CTF_BT_ERR_LSEEK,
+  CTF_BT_ERR_READ,
+  CTF_BT_ERR_GETCONTEXT,
+  CTF_BT_ERR_DECODE,
+  CTF_BT_ERR_CFA_OFFSET,
+};
+
+/* Get the backtrace of the calling program by storing return addresses
+   in BUFFER. The SIZE argument specifies the maximum number of addresses
+   that can be stored in the buffer. Return the number of return addresses
+   collected or -1 if there is any error.  */
+extern int ctf_backtrace (void **buffer, int size, int *errp);
+
+extern const char *ctf_bt_errmsg (enum ctf_bt_errcode ecode);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif				/* _CTF_BACKTRACE_API_H */
diff --git a/libctfframe/Makefile.am b/libctfframe/Makefile.am
index e321e071517..7af718d68b3 100644
--- a/libctfframe/Makefile.am
+++ b/libctfframe/Makefile.am
@@ -23,16 +23,19 @@ AUTOMAKE_OPTIONS = foreign no-texinfo.tex
 
 BASEDIR = $(srcdir)/..
 INCDIR = $(srcdir)/../include
-# include libctf for swap.h
-AM_CPPFLAGS = -D_GNU_SOURCE -I$(srcdir) -I$(srcdir)/../include -I$(srcdir)/../libctf
 AM_CFLAGS = -std=gnu99 @ac_libctfframe_warn_cflags@ @warn@ @c_warn@ @WARN_PEDANTIC@ @WERROR@
 
 if INSTALL_LIBBFD
-lib_LTLIBRARIES = libctfframe.la
-include_HEADERS = $(INCDIR)/ctf-frame.h $(INCDIR)/ctf-frame-api.h
+lib_LTLIBRARIES = libctfframe.la libctfbacktrace.la
+include_HEADERS = $(INCDIR)/ctf-frame.h $(INCDIR)/ctf-frame-api.h $(INCDIR)/ctf-backtrace-api.h
 else
 include_HEADERS =
-noinst_LTLIBRARIES = libctfframe.la
+noinst_LTLIBRARIES = libctfframe.la libctfbacktrace.la
 endif
 
 libctfframe_la_SOURCES = ctf-frame.c ctf-frame-dump.c ctf-frame-error.c
+libctfframe_la_CPPFLAGS = -D_GNU_SOURCE -I$(srcdir) -I$(srcdir)/../include \
+                          -I$(srcdir)/../libctf
+
+libctfbacktrace_la_SOURCES = ctf-backtrace.c ctf-backtrace-err.c
+libctfbacktrace_la_CPPFLAGS = -I$(srcdir) -I$(srcdir)/../include
diff --git a/libctfframe/Makefile.in b/libctfframe/Makefile.in
index 50a720c8a99..dfba303ffe7 100644
--- a/libctfframe/Makefile.in
+++ b/libctfframe/Makefile.in
@@ -159,14 +159,21 @@ am__uninstall_files_from_dir = { \
   }
 am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"
 LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES)
-libctfframe_la_LIBADD =
-am_libctfframe_la_OBJECTS = ctf-frame.lo ctf-frame-dump.lo \
-	ctf-frame-error.lo
-libctfframe_la_OBJECTS = $(am_libctfframe_la_OBJECTS)
+libctfbacktrace_la_LIBADD =
+am_libctfbacktrace_la_OBJECTS = libctfbacktrace_la-ctf-backtrace.lo \
+	libctfbacktrace_la-ctf-backtrace-err.lo
+libctfbacktrace_la_OBJECTS = $(am_libctfbacktrace_la_OBJECTS)
 AM_V_lt = $(am__v_lt_@AM_V@)
 am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
 am__v_lt_0 = --silent
 am__v_lt_1 = 
+@INSTALL_LIBBFD_FALSE@am_libctfbacktrace_la_rpath =
+@INSTALL_LIBBFD_TRUE@am_libctfbacktrace_la_rpath = -rpath $(libdir)
+libctfframe_la_LIBADD =
+am_libctfframe_la_OBJECTS = libctfframe_la-ctf-frame.lo \
+	libctfframe_la-ctf-frame-dump.lo \
+	libctfframe_la-ctf-frame-error.lo
+libctfframe_la_OBJECTS = $(am_libctfframe_la_OBJECTS)
 @INSTALL_LIBBFD_FALSE@am_libctfframe_la_rpath =
 @INSTALL_LIBBFD_TRUE@am_libctfframe_la_rpath = -rpath $(libdir)
 AM_V_P = $(am__v_P_@AM_V@)
@@ -203,15 +210,15 @@ AM_V_CCLD = $(am__v_CCLD_@AM_V@)
 am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
 am__v_CCLD_0 = @echo "  CCLD    " $@;
 am__v_CCLD_1 = 
-SOURCES = $(libctfframe_la_SOURCES)
-DIST_SOURCES = $(libctfframe_la_SOURCES)
+SOURCES = $(libctfbacktrace_la_SOURCES) $(libctfframe_la_SOURCES)
+DIST_SOURCES = $(libctfbacktrace_la_SOURCES) $(libctfframe_la_SOURCES)
 am__can_run_installinfo = \
   case $$AM_UPDATE_INFO_DIR in \
     n|no|NO) false;; \
     *) (install-info --version) >/dev/null 2>&1;; \
   esac
 am__include_HEADERS_DIST = $(INCDIR)/ctf-frame.h \
-	$(INCDIR)/ctf-frame-api.h
+	$(INCDIR)/ctf-frame-api.h $(INCDIR)/ctf-backtrace-api.h
 HEADERS = $(include_HEADERS)
 am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \
 	$(LISP)config.h.in
@@ -368,7 +375,6 @@ pdfdir = @pdfdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
-runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
@@ -383,14 +389,17 @@ ACLOCAL_AMFLAGS = -I .. -I ../config
 AUTOMAKE_OPTIONS = foreign no-texinfo.tex
 BASEDIR = $(srcdir)/..
 INCDIR = $(srcdir)/../include
-# include libctf for swap.h
-AM_CPPFLAGS = -D_GNU_SOURCE -I$(srcdir) -I$(srcdir)/../include -I$(srcdir)/../libctf
 AM_CFLAGS = -std=gnu99 @ac_libctfframe_warn_cflags@ @warn@ @c_warn@ @WARN_PEDANTIC@ @WERROR@
-@INSTALL_LIBBFD_TRUE@lib_LTLIBRARIES = libctfframe.la
+@INSTALL_LIBBFD_TRUE@lib_LTLIBRARIES = libctfframe.la libctfbacktrace.la
 @INSTALL_LIBBFD_FALSE@include_HEADERS = 
-@INSTALL_LIBBFD_TRUE@include_HEADERS = $(INCDIR)/ctf-frame.h $(INCDIR)/ctf-frame-api.h
-@INSTALL_LIBBFD_FALSE@noinst_LTLIBRARIES = libctfframe.la
+@INSTALL_LIBBFD_TRUE@include_HEADERS = $(INCDIR)/ctf-frame.h $(INCDIR)/ctf-frame-api.h $(INCDIR)/ctf-backtrace-api.h
+@INSTALL_LIBBFD_FALSE@noinst_LTLIBRARIES = libctfframe.la libctfbacktrace.la
 libctfframe_la_SOURCES = ctf-frame.c ctf-frame-dump.c ctf-frame-error.c
+libctfframe_la_CPPFLAGS = -D_GNU_SOURCE -I$(srcdir) -I$(srcdir)/../include \
+                          -I$(srcdir)/../libctf
+
+libctfbacktrace_la_SOURCES = ctf-backtrace.c ctf-backtrace-err.c
+libctfbacktrace_la_CPPFLAGS = -I$(srcdir) -I$(srcdir)/../include
 all: config.h
 	$(MAKE) $(AM_MAKEFLAGS) all-am
 
@@ -491,6 +500,9 @@ clean-noinstLTLIBRARIES:
 	  rm -f $${locs}; \
 	}
 
+libctfbacktrace.la: $(libctfbacktrace_la_OBJECTS) $(libctfbacktrace_la_DEPENDENCIES) $(EXTRA_libctfbacktrace_la_DEPENDENCIES) 
+	$(AM_V_CCLD)$(LINK) $(am_libctfbacktrace_la_rpath) $(libctfbacktrace_la_OBJECTS) $(libctfbacktrace_la_LIBADD) $(LIBS)
+
 libctfframe.la: $(libctfframe_la_OBJECTS) $(libctfframe_la_DEPENDENCIES) $(EXTRA_libctfframe_la_DEPENDENCIES) 
 	$(AM_V_CCLD)$(LINK) $(am_libctfframe_la_rpath) $(libctfframe_la_OBJECTS) $(libctfframe_la_LIBADD) $(LIBS)
 
@@ -500,9 +512,11 @@ mostlyclean-compile:
 distclean-compile:
 	-rm -f *.tab.c
 
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-frame-dump.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-frame-error.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-frame.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libctfbacktrace_la-ctf-backtrace-err.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libctfbacktrace_la-ctf-backtrace.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libctfframe_la-ctf-frame-dump.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libctfframe_la-ctf-frame-error.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libctfframe_la-ctf-frame.Plo@am__quote@
 
 .c.o:
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -525,6 +539,41 @@ distclean-compile:
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
 
+libctfbacktrace_la-ctf-backtrace.lo: ctf-backtrace.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libctfbacktrace_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libctfbacktrace_la-ctf-backtrace.lo -MD -MP -MF $(DEPDIR)/libctfbacktrace_la-ctf-backtrace.Tpo -c -o libctfbacktrace_la-ctf-backtrace.lo `test -f 'ctf-backtrace.c' || echo '$(srcdir)/'`ctf-backtrace.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libctfbacktrace_la-ctf-backtrace.Tpo $(DEPDIR)/libctfbacktrace_la-ctf-backtrace.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='ctf-backtrace.c' object='libctfbacktrace_la-ctf-backtrace.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libctfbacktrace_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libctfbacktrace_la-ctf-backtrace.lo `test -f 'ctf-backtrace.c' || echo '$(srcdir)/'`ctf-backtrace.c
+
+libctfbacktrace_la-ctf-backtrace-err.lo: ctf-backtrace-err.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libctfbacktrace_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libctfbacktrace_la-ctf-backtrace-err.lo -MD -MP -MF $(DEPDIR)/libctfbacktrace_la-ctf-backtrace-err.Tpo -c -o libctfbacktrace_la-ctf-backtrace-err.lo `test -f 'ctf-backtrace-err.c' || echo '$(srcdir)/'`ctf-backtrace-err.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libctfbacktrace_la-ctf-backtrace-err.Tpo $(DEPDIR)/libctfbacktrace_la-ctf-backtrace-err.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='ctf-backtrace-err.c' object='libctfbacktrace_la-ctf-backtrace-err.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libctfbacktrace_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libctfbacktrace_la-ctf-backtrace-err.lo `test -f 'ctf-backtrace-err.c' || echo '$(srcdir)/'`ctf-backtrace-err.c
+
+libctfframe_la-ctf-frame.lo: ctf-frame.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libctfframe_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libctfframe_la-ctf-frame.lo -MD -MP -MF $(DEPDIR)/libctfframe_la-ctf-frame.Tpo -c -o libctfframe_la-ctf-frame.lo `test -f 'ctf-frame.c' || echo '$(srcdir)/'`ctf-frame.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libctfframe_la-ctf-frame.Tpo $(DEPDIR)/libctfframe_la-ctf-frame.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='ctf-frame.c' object='libctfframe_la-ctf-frame.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libctfframe_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libctfframe_la-ctf-frame.lo `test -f 'ctf-frame.c' || echo '$(srcdir)/'`ctf-frame.c
+
+libctfframe_la-ctf-frame-dump.lo: ctf-frame-dump.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libctfframe_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libctfframe_la-ctf-frame-dump.lo -MD -MP -MF $(DEPDIR)/libctfframe_la-ctf-frame-dump.Tpo -c -o libctfframe_la-ctf-frame-dump.lo `test -f 'ctf-frame-dump.c' || echo '$(srcdir)/'`ctf-frame-dump.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libctfframe_la-ctf-frame-dump.Tpo $(DEPDIR)/libctfframe_la-ctf-frame-dump.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='ctf-frame-dump.c' object='libctfframe_la-ctf-frame-dump.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libctfframe_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libctfframe_la-ctf-frame-dump.lo `test -f 'ctf-frame-dump.c' || echo '$(srcdir)/'`ctf-frame-dump.c
+
+libctfframe_la-ctf-frame-error.lo: ctf-frame-error.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libctfframe_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libctfframe_la-ctf-frame-error.lo -MD -MP -MF $(DEPDIR)/libctfframe_la-ctf-frame-error.Tpo -c -o libctfframe_la-ctf-frame-error.lo `test -f 'ctf-frame-error.c' || echo '$(srcdir)/'`ctf-frame-error.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libctfframe_la-ctf-frame-error.Tpo $(DEPDIR)/libctfframe_la-ctf-frame-error.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='ctf-frame-error.c' object='libctfframe_la-ctf-frame-error.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libctfframe_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libctfframe_la-ctf-frame-error.lo `test -f 'ctf-frame-error.c' || echo '$(srcdir)/'`ctf-frame-error.c
+
 mostlyclean-libtool:
 	-rm -f *.lo
 
diff --git a/libctfframe/ctf-backtrace-err.c b/libctfframe/ctf-backtrace-err.c
new file mode 100644
index 00000000000..374ceaf3266
--- /dev/null
+++ b/libctfframe/ctf-backtrace-err.c
@@ -0,0 +1,46 @@
+/* ctf-backtrace-err.c - CTF Backtrace Error table.
+
+   Copyright (C) 2022 Free Software Foundation, Inc.
+
+   This file is part of libctfbacktrace.
+
+   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, see <http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "ctf-backtrace-api.h"
+
+/* CTF backtrace error messages.  */
+static const char *const ctf_bt_errlist[] =
+{
+  "",
+  "File does not contain CTF Frame data",
+  "Iterating shared object reading error",
+  "Failed to malloc memory space",
+  "Failed to realloc memory space",
+  "Failed to open file",
+  "Failed on resolve canonical file name",
+  "Failed to reposition file offset",
+  "Failed to read from a file descriptor",
+  "Failed to get the user context",
+  "Failed to set up decode data",
+  "Illegal CFA offset"
+};
+
+/* ctf_bt_perror -Return the error message associated with the error code.  */
+
+const char *
+ctf_bt_errmsg (enum ctf_bt_errcode ecode)
+{
+  return ctf_bt_errlist[ecode];
+}
diff --git a/libctfframe/ctf-backtrace.c b/libctfframe/ctf-backtrace.c
new file mode 100644
index 00000000000..c17a8dcfffd
--- /dev/null
+++ b/libctfframe/ctf-backtrace.c
@@ -0,0 +1,617 @@
+/* ctf-backtrace.c - The CTF Frame unwinder.
+
+   Copyright (C) 2022 Free Software Foundation, Inc.
+
+   This file is part of libctfbacktrace.
+
+   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, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include "config.h"
+#include <elf.h>
+#include <link.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <execinfo.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ucontext.h>
+#include <stdarg.h>
+#include "ansidecl.h"
+#include "ctf-frame-api.h"
+#include "ctf-backtrace-api.h"
+
+#ifndef PT_CTF_FRAME
+#define PT_CTF_FRAME 0x6474e554		/* FIXME.  */
+#endif
+
+#define _cf_printflike_(string_index,first_to_check) \
+    __attribute__ ((__format__ (__printf__, (string_index), (first_to_check))))
+
+static int _ctf_unwind_debug;	/* Control for printing out debug info.  */
+static int no_of_entries = 32;
+
+/* CTF decode data for the main module or a DSO.  */
+struct ctf_decode_data
+{
+  char *cdd_data;			/* CTF decode data.  */
+  int cdd_data_size;			/* CTF decode data size.  */
+  uint64_t cdd_text_vma;		/* Text segment's virtual address.  */
+  int cdd_text_size;			/* Text segment's size.  */
+  uint64_t cdd_ctf_vma;			/* CTF segment's virtual address.  */	
+  ctf_frame_decoder_ctx *cdd_ctf_ctx;	/* CTF decoder context.  */
+};
+
+/* List that holds CTF frame info for the shared libraries.  */
+struct dso_cfi_list
+{
+  int alloced;				/* Entries allocated.  */
+  int used;				/* Entries used.  */
+  struct ctf_decode_data *entry;	/* DSO's decode data.  */
+};
+
+/* Data that's passed through ctf_frame_callback.  */
+struct ctf_unwind_info
+{
+  int cui_fd;				/* File descriptor.  */
+  struct ctf_decode_data cui_ctx;	/* The decode data.  */
+  struct dso_cfi_list cui_dsos;		/* The DSO list.  */
+};
+
+static void
+ctf_unwind_init_debug (void)
+{
+  static int inited;
+
+  if (!inited)
+    {
+      _ctf_unwind_debug = getenv ("CTF_UNWIND_DEBUG") != NULL;
+      inited = 1;
+    }
+}
+
+_cf_printflike_ (1, 2)
+static void
+debug_printf (const char *format, ...)
+{
+  if (_ctf_unwind_debug)
+    {
+      va_list args;
+
+      va_start (args, format);
+      __builtin_vprintf (format, args);
+      va_end (args);
+    }
+}
+
+/* ctf_bt_errno - Check if there is error code in ERRP.  */
+
+static int
+ctf_bt_errno (int *errp)
+{
+  if (errp == NULL)
+    return 0;
+
+  return (*errp != CTF_BT_OK);
+}
+
+/* ctf_bt_set_errno - Store the specified error code ERROR into ERRP if
+   it is non-NULL.  */
+
+static void
+ctf_bt_set_errno (int *errp, int error)
+{
+  if (errp != NULL)
+    *errp = error;
+}
+
+/* ctf_add_dso - Add .ctf_frame info in D_DATA, which is associated with
+   a dynamic shared object, to D_LIST.  */
+
+static void
+ctf_add_dso (struct dso_cfi_list *d_list,
+	     struct ctf_decode_data d_data,
+	     int *errp)
+{
+  if (d_list->alloced == 0)
+    {
+      d_list->entry = malloc (no_of_entries * sizeof (struct ctf_decode_data));
+      if (d_list->entry == NULL)
+	{
+	  ctf_bt_set_errno (errp, CTF_BT_ERR_MALLOC);
+	  return;
+	}
+      memset (d_list->entry, 0,
+	      no_of_entries * sizeof (struct ctf_decode_data));
+      d_list->alloced = no_of_entries;
+    }
+  else if (d_list->used == d_list->alloced)
+    {
+      d_list->entry = realloc (d_list->entry,
+			(d_list->alloced + no_of_entries) *
+			sizeof (struct ctf_decode_data));
+      if (d_list->entry == NULL)
+	{
+	  ctf_bt_set_errno (errp, CTF_BT_ERR_REALLOC);
+	  return;
+	}
+
+      memset (&d_list->entry[d_list->alloced], 0,
+	      no_of_entries * sizeof (struct ctf_decode_data));
+      d_list->alloced += no_of_entries;
+    }
+
+  ctf_bt_set_errno (errp, CTF_BT_OK);
+  d_list->entry[d_list->used++] = d_data;
+}
+
+/* ctf_free_cfi - Free up space allocated for .ctf_frame info for CF.  */
+
+static void
+ctf_free_cfi (struct ctf_unwind_info *cf)
+{
+  struct dso_cfi_list *d_list;
+  int i;
+
+  if (cf == NULL)
+    return;
+
+  free (cf->cui_ctx.cdd_data);
+  ctf_free_decoder (cf->cui_ctx.cdd_ctf_ctx);
+  close (cf->cui_fd);
+
+  d_list = &cf-> cui_dsos;
+  if (d_list == NULL)
+    return;
+
+  for (i = 0; i < d_list->used; ++i)
+    {
+      free (d_list->entry[i].cdd_data);
+      ctf_free_decoder (d_list->entry[i].cdd_ctf_ctx);
+    }
+}
+
+/* ctf_find_context - Find the decode data that contains ADDR from CF.
+   Return the pointer to the decode data or NULL.  */
+
+static struct ctf_decode_data *
+ctf_find_context (struct ctf_unwind_info *cf, uint64_t addr)
+{
+  struct dso_cfi_list *d_list;
+  int i;
+
+  if (cf == NULL)
+    return NULL;
+
+  if (cf->cui_ctx.cdd_text_vma < addr
+      && cf->cui_ctx.cdd_text_vma + cf->cui_ctx.cdd_text_size > addr)
+    return &cf->cui_ctx;
+
+  d_list = &cf->cui_dsos;
+  for (i = 0; i < cf->cui_dsos.used; ++i)
+    {
+      if (d_list->entry[i].cdd_text_vma <= addr &&
+	  d_list->entry[i].cdd_text_vma
+	  + d_list->entry[i].cdd_text_size >= addr)
+	return &d_list->entry[i];
+    }
+
+  return NULL;
+}
+
+/* ctf_valid_addr - Check if ADDR is valid in CF. The address is considered
+   invalid, with regards to CTF, if it's not in any address range of the main
+   module or any of its DSO's. Return 1 if valid, 0 otherwise.  */
+
+static int
+ctf_valid_addr (struct ctf_unwind_info *cf, uint64_t addr)
+{
+  struct ctf_decode_data *cdp;
+
+  if (cf == NULL)
+    return 0;
+
+  cdp = ctf_find_context (cf, addr);
+  return cdp ? 1 : 0;
+}
+
+/* ctf_load_ctx - Call decoder to create and set up the ctf frame info for
+   either the main module or one of the DSOs from CF, based on the input
+   RADDR argument.  Return the newly created decode context or NULL.  */
+
+static ctf_frame_decoder_ctx *
+ctf_load_ctx (struct ctf_unwind_info *cf, uint64_t raddr)
+{
+  ctf_frame_decoder_ctx *nctx;
+  struct ctf_decode_data *cdp;
+
+  if (cf == NULL)
+    return NULL;
+
+  cdp = ctf_find_context (cf, raddr);
+  if (cdp == NULL)
+    return NULL;
+
+  if (cdp->cdd_ctf_ctx == NULL)
+    {
+      int err; 
+      nctx = ctf_frame_decode (cdp->cdd_data, cdp->cdd_data_size, &err);
+      if (nctx == NULL)
+	return NULL;
+      cdp->cdd_ctf_ctx = nctx;
+      return nctx;
+    }
+
+  return NULL;
+}
+
+/* ctf_update_ctx - Check if need to do a decode context switch, based on
+   the input RADDR argument, from CF. A new decode context will be created
+   and set up if it isn't already done so. Return the new decode context in
+   CTX and vma in CFI_VMA.  */
+
+static void
+ctf_update_ctx (struct ctf_unwind_info *cf, uint64_t raddr,
+		ctf_frame_decoder_ctx **ctx, uint64_t *cfi_vma)
+{
+  ctf_frame_decoder_ctx *nctx;
+  struct ctf_decode_data *cdp;
+
+  cdp = ctf_find_context (cf, raddr);
+  if (cdp != NULL)
+    {
+      if (cdp->cdd_ctf_ctx == NULL)
+	{
+	  int err; 
+	  nctx = ctf_frame_decode (cdp->cdd_data, cdp->cdd_data_size, &err);
+	  if (nctx == NULL)
+	    {
+	      *ctx = NULL;
+	      return;
+	    }
+	  cdp->cdd_ctf_ctx = nctx;
+	}
+	*ctx = cdp->cdd_ctf_ctx;
+	*cfi_vma = cdp->cdd_ctf_vma;
+    }
+}
+
+/* get_contents - Return contents at ADDR from file descriptor FD.  */
+
+static uint64_t
+get_contents (int fd, uint64_t addr, int *errp)
+{
+  uint64_t data;
+  size_t sz;
+
+  ctf_bt_set_errno (errp, CTF_BT_OK);
+  if (lseek (fd, addr, SEEK_SET) == -1)
+    {
+      ctf_bt_set_errno (errp, CTF_BT_ERR_LSEEK);
+      return 0;
+    }
+  sz = read (fd, &data, sizeof (uint64_t));
+  if (sz != sizeof (uint64_t))
+    {
+      ctf_bt_set_errno (errp, CTF_BT_ERR_READ);
+      return 0;
+    }
+
+  return data;
+}
+
+/* ctf_fd_open - Open /proc image associated with the process id and return
+   the file descriptor.  */
+
+static int
+ctf_fd_open (int *errp)
+{
+  char filename[PATH_MAX];
+  pid_t pid;
+  int fd;
+
+  pid = getpid ();
+  snprintf (filename, sizeof filename, "/proc/%d/task/%d/mem", pid, pid);
+  if ((fd = open (filename, O_RDONLY)) == -1)
+    {
+      ctf_bt_set_errno (errp, CTF_BT_ERR_OPEN);
+      return -1;
+    }
+
+  return fd;
+}
+
+/* ctf_frame_callback - The callback from dl_iterate_phdr with header info
+   in INFO.
+   Return CTF frame info for either the main module or a DSO in DATA.  */
+
+static int
+ctf_frame_callback (struct dl_phdr_info *info,
+		    size_t size ATTRIBUTE_UNUSED,
+		    void *data)
+{
+  struct ctf_unwind_info *cf = (struct ctf_unwind_info *) data;
+  int p_type, i, fd, ctf_err;
+  ssize_t len;
+  uint64_t text_vma = 0;
+  int text_size = 0;
+
+  if (data == NULL || info == NULL)
+    return 1;
+
+  debug_printf ("-- name: %s %14p\n", info->dlpi_name, (void *)info->dlpi_addr);
+
+  for (i = 0; i < info->dlpi_phnum; i++)
+    {
+      debug_printf("    %2d: [%14p; memsz:%7lx] flags: 0x%x; \n", i,
+		   (void *) info->dlpi_phdr[i].p_vaddr,
+		   info->dlpi_phdr[i].p_memsz,
+		   info->dlpi_phdr[i].p_flags);
+
+      p_type = info->dlpi_phdr[i].p_type;
+      if (p_type == PT_LOAD && info->dlpi_phdr[i].p_flags & PF_X)
+	{
+	  text_vma = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
+	  text_size = info->dlpi_phdr[i].p_memsz;
+	  continue;
+	}
+      if (p_type != PT_CTF_FRAME)
+	continue;
+
+      if (info->dlpi_name[0] == '\0')		/* the main module.  */
+	{
+	  fd = ctf_fd_open (&ctf_err);
+	  if (fd == -1)
+	    return 1;
+	  if (lseek (fd, info->dlpi_addr + info->dlpi_phdr[i].p_vaddr,
+		     SEEK_SET) == -1)
+	    {
+	      ctf_bt_set_errno (&ctf_err, CTF_BT_ERR_LSEEK);
+	      return 1;
+	    }
+
+	  cf->cui_ctx.cdd_data = (char *) malloc (info->dlpi_phdr[i].p_memsz);
+	  if (cf->cui_ctx.cdd_data == NULL)
+	    {
+	      ctf_bt_set_errno (&ctf_err, CTF_BT_ERR_MALLOC);
+	      return 1;
+	    }
+
+	  len = read (fd, cf->cui_ctx.cdd_data, info->dlpi_phdr[i].p_memsz);
+	  if (len == -1 || len != (ssize_t) info->dlpi_phdr[i].p_memsz)
+	    {
+	      ctf_bt_set_errno (&ctf_err, CTF_BT_ERR_READ);
+	      return 1;
+	    }
+
+	  assert (text_vma);
+	  cf->cui_ctx.cdd_data_size = len;
+	  cf->cui_ctx.cdd_ctf_vma = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
+	  cf->cui_fd = fd;
+	  cf->cui_ctx.cdd_text_vma = text_vma;
+	  cf->cui_ctx.cdd_text_size = text_size;
+	  text_vma = 0;
+	  return 0;
+	}
+      else
+	{					/* a dynamic shared object.  */
+	  struct ctf_decode_data dt;
+	  memset (&dt, 0, sizeof (struct ctf_decode_data));
+	  assert (cf->cui_fd);
+	  if (lseek (cf->cui_fd, info->dlpi_addr + info->dlpi_phdr[i].p_vaddr,
+		     SEEK_SET) == -1)
+	    {
+	      ctf_bt_set_errno (&ctf_err, CTF_BT_ERR_LSEEK);
+	      return 1;
+	    }
+
+	  dt.cdd_data = (char *) malloc (info->dlpi_phdr[i].p_memsz);
+	  if (dt.cdd_data == NULL)
+	    {
+	      ctf_bt_set_errno (&ctf_err, CTF_BT_ERR_MALLOC);
+	      return 1;
+	    }
+
+	  len = read (cf->cui_fd, dt.cdd_data, info->dlpi_phdr[i].p_memsz);
+	  if (len == -1 || len != (ssize_t) info->dlpi_phdr[i].p_memsz)
+	    {
+	      ctf_bt_set_errno (&ctf_err, CTF_BT_ERR_READ);
+	      return 1;
+	    }
+
+	  assert (text_vma);
+	  dt.cdd_data_size = len;
+	  dt.cdd_ctf_vma = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
+	  dt.cdd_text_vma = text_vma;
+	  dt.cdd_text_size = text_size;
+	  text_vma = 0;
+	  ctf_add_dso (&cf->cui_dsos, dt, &ctf_err);
+	  if (ctf_err != CTF_BT_OK)
+	    return 1;
+	  return 0;
+	}
+    }
+
+    return 0;
+}
+
+/* ctf_frame_unwind - Unwind the stack backtrace for CF. If successful,
+   store the return addresses in RA_LST. The RA_SIZE argument specifies
+   the maximum number of return addresses that can be stored in RA_LST
+   and contains the number of the addresses collected.  */
+
+static void
+ctf_frame_unwind (struct ctf_unwind_info *cf, void **ra_lst,
+		  int *ra_size, int *errp)
+{
+  uint64_t cfa, return_addr, ra_stack_loc, rfp_stack_loc;
+  ctf_frame_decoder_ctx *ctx;
+  int cfa_offset, rfp_offset, errnum, i, count;
+  ctf_frame_row_entry fred, *frep = &fred;
+  uint64_t pc, rfp, rsp, cfi_vma;
+  ucontext_t context, *cp = &context;
+
+  if (cf == NULL || ra_lst == NULL || ra_size == NULL)
+    {
+      ctf_bt_set_errno (errp, CTF_BT_ERR_ARG);
+      return;
+    }
+
+  /* Get the user context for its registers.  */
+  if (getcontext (cp) != 0)
+    {
+      ctf_bt_set_errno (errp, CTF_BT_ERR_GETCONTEXT);
+      return;
+    }
+  ctf_bt_set_errno (errp, CTF_BT_OK);
+
+#ifdef __x86_64__
+  pc = cp->uc_mcontext.gregs[REG_RIP];
+  rsp = cp->uc_mcontext.gregs[REG_RSP];
+  rfp = cp->uc_mcontext.gregs[REG_RBP];
+#else
+#ifdef __aarch64__
+#define UNWIND_AARCH64_X29		29	/* 64-bit frame pointer.  */
+#define UNWIND_AARCH64_X30		30	/* 64-bit link pointer.  */
+  pc = cp->uc_mcontext.pc;
+  rsp = cp->uc_mcontext.sp;
+  rfp = cp->uc_mcontext.regs[UNWIND_AARCH64_X29];
+  uint64_t ra = cp->uc_mcontext.regs[UNWIND_AARCH64_X30];
+#endif
+#endif
+
+  /* Load and set up the decoder.  */
+  ctx = ctf_load_ctx (cf, pc);
+  if (ctx == NULL)
+    {
+      ctf_bt_set_errno (errp, CTF_BT_ERR_DECODE);
+      return;
+    }
+  cfi_vma = cf->cui_ctx.cdd_ctf_vma;
+  count = *ra_size;
+
+  for (i = 0; i < count; ++i)
+    {
+      pc -= cfi_vma;
+      errnum = ctf_frame_find_fre (ctx, pc, frep);
+      if (errnum == 0)
+	{
+	  cfa_offset = ctf_frame_fre_get_cfa_offset (frep, &errnum);
+	  if (errnum == ECTF_FRAME_FREOFFSET_NOPRESENT)
+	    {
+	      ctf_bt_set_errno (errp, CTF_BT_ERR_CFA_OFFSET);
+	      return;
+	    }
+
+	  cfa = ((frep->fre_info & 0x1) == CTF_FRAME_BASE_REG_SP
+	    ? rsp : rfp) + cfa_offset;
+
+#ifdef __x86_64__
+	  /* For x86, read the return address from cfa - 8.  */
+	  ra_stack_loc = cfa - 8;
+	  return_addr = get_contents (cf->cui_fd, ra_stack_loc, errp);
+	  if (ctf_bt_errno (errp))
+	    return;
+#else
+#ifdef __aarch64__
+	  int ra_offset = ctf_frame_fre_get_ra_offset (frep, &errnum);
+	  if (errnum == 0)
+	    {
+	      ra_stack_loc = cfa + ra_offset;
+	      return_addr = get_contents (cf->cui_fd, ra_stack_loc, errp);
+	      if (ctf_bt_errno (errp))
+		return;
+	    }
+	  else
+	    return_addr = ra;
+#endif
+#endif
+
+	  /* Validate and add return address to the list.  */
+	  if (ctf_valid_addr (cf, return_addr) == 0)
+	    {
+	      i -= 1;
+	      goto find_fre_ra_err;
+	    }
+	  if (i != 0)		/* exclude self.  */
+	    ra_lst[i-1] = (void *)return_addr;
+
+	  /* Set up for the next frame.  */
+	  rfp_offset = ctf_frame_fre_get_fp_offset (frep, &errnum);
+	  if (errnum == 0)
+	    {
+	      rfp_stack_loc = cfa + rfp_offset;
+	      rfp = get_contents (cf->cui_fd, rfp_stack_loc, errp);
+	      if (ctf_bt_errno (errp))
+		return;
+	    }
+	  rsp = cfa;
+	  pc = return_addr;
+
+	  /* Check if need to update the decoder context and vma.  */
+	  ctf_update_ctx (cf, return_addr, &ctx, &cfi_vma);
+	  if (ctx == NULL)
+	    {
+	      ctf_bt_set_errno (errp, CTF_BT_ERR_DECODE);
+	      return;
+	    }
+	}
+      else
+	{
+	  i -= 1;
+	  goto find_fre_ra_err;
+	}
+    }
+
+find_fre_ra_err:
+  *ra_size = i;
+}
+
+/* ctf_backtrace - Main API that user program calls to get a backtrace.
+   The BUFFER argument provides space for the list of the return addresses
+   and the SIZE argument specifies the maximum number of addresses that
+   can be stored in the buffer.  Return the number of return addresses
+   collected or -1 if there is any error.  */
+
+int
+ctf_backtrace (void **buffer, int size, int *errp)
+{
+  struct ctf_unwind_info ctfinfo;
+
+  ctf_unwind_init_debug ();
+
+  memset (&ctfinfo, 0, sizeof (struct ctf_unwind_info));
+
+  /* find and set up the .ctf_frame sections.  */
+  (void) dl_iterate_phdr (ctf_frame_callback, (void *)&ctfinfo);
+  if (ctfinfo.cui_fd == 0)
+    {
+      ctf_bt_set_errno (errp, CTF_BT_ERR_NOCTF);
+      return -1;
+    }
+
+  /* Do the stack unwinding.  */
+  ctf_frame_unwind (&ctfinfo, buffer, &size, errp);
+  if (ctf_bt_errno (errp))
+    return -1;
+
+  ctf_free_cfi (&ctfinfo);
+
+  return size;
+}
diff --git a/libctfframe/ttest.c b/libctfframe/ttest.c
new file mode 100644
index 00000000000..c0b8434aad5
--- /dev/null
+++ b/libctfframe/ttest.c
@@ -0,0 +1,78 @@
+#include <execinfo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "ctf-backtrace-api.h"
+
+#define BT_BUF_SIZE 100
+
+/* funclist for running "ttest.x 3".  */
+static const char *const func_list[] =
+{
+  "myfunc3",
+  "()",
+  "myfunc",
+  "myfunc",
+  "myfunc",
+  "main"
+};
+
+void
+myfunc3 (void)
+{
+    void *buffer[BT_BUF_SIZE];
+    int j, nptrs, err;
+    char **strings;
+
+    /* Call the unwinder to get an array of return addresses.  */
+    nptrs = ctf_backtrace (buffer, BT_BUF_SIZE, &err);
+    if (nptrs == -1 || nptrs != 6)
+      {
+	printf ("CTF error: %s\n", ctf_bt_errmsg (err));
+	return;
+      }
+
+    /* Get these addresses symbolically.  */
+    strings = backtrace_symbols (buffer, nptrs);
+    if (strings == NULL) {
+        perror("backtrace_symbols");
+        exit(EXIT_FAILURE);
+    }
+
+    /* Verify the results.  */
+    for (j = 0; j < nptrs; j++)
+      if (!strstr (strings[j], func_list[j]))
+	break;
+
+    free(strings);
+
+    printf ("%s\n", j == nptrs ? "PASS" : "FAIL");
+}
+
+static void   /* "static" means don't export the symbol... */
+myfunc2 (void)
+{
+    myfunc3 ();
+}
+
+void
+myfunc (int ncalls)
+{
+    if (ncalls > 1)
+        myfunc (ncalls - 1);
+    else
+        myfunc2 ();
+}
+
+int
+main (int argc, char *argv[])
+{
+    if (argc != 2) {
+        fprintf (stderr, "%s num-calls\n", argv[0]);
+        exit (EXIT_FAILURE);
+    }
+
+    myfunc (atoi(argv[1]));
+    exit (EXIT_SUCCESS);
+}
-- 
2.31.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH, RFC 7/7] gdb: sim: buildsystem changes to accommodate libctfframe
  2022-05-07  0:52 [PATCH,RFC 0/7] Definition and Implementation of CTF Frame format Indu Bhagat
                   ` (4 preceding siblings ...)
  2022-05-07  0:52 ` [PATCH,RFC 6/7] unwinder: generate backtrace using CTF Frame format Indu Bhagat
@ 2022-05-07  0:52 ` Indu Bhagat
       [not found] ` <20220507005223.3093035-4-indu.bhagat@oracle.com>
  2022-05-13 13:08 ` [PATCH,RFC 0/7] Definition and Implementation of CTF Frame format Michael Matz
  7 siblings, 0 replies; 11+ messages in thread
From: Indu Bhagat @ 2022-05-07  0:52 UTC (permalink / raw)
  To: binutils

Both gdb and sim need buildsystem fixes to now include libctfframe for a
successful build.

gdb/Changelog:
	* acinclude.m4: Fix GDB_AC_CHECK_BFD to include libctfframe.
	* Makefile.in: Bring in libctfframe for linking.
	* configure.ac: Check for static or shared.
	* configure: Regenerated.

sim/common/Changelog:
	* sim/common/Make-common.in: Bring in libctfframe.a for linking.
---
 gdb/Makefile.in           |  8 ++++++--
 gdb/acinclude.m4          |  4 ++--
 gdb/configure             | 35 +++++++++++++++++++++++++++++++----
 gdb/configure.ac          | 11 +++++++++++
 sim/common/Make-common.in |  7 +++++--
 5 files changed, 55 insertions(+), 10 deletions(-)

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 418094775a5..9f3eb83ba5e 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -161,6 +161,10 @@ LIBIBERTY = ../libiberty/libiberty.a
 LIBCTF = @LIBCTF@
 CTF_DEPS = @CTF_DEPS@
 
+# Where is the CTF frame library?  Typically in ../libctfframe.
+LIBCTFFRAME = @LIBCTFFRAME@
+CTFFRAME_DEPS = @CTFFRAME_DEPS@
+
 # Where is the BFD library?  Typically in ../bfd.
 BFD_DIR = ../bfd
 BFD = $(BFD_DIR)/libbfd.a
@@ -645,7 +649,7 @@ INTERNAL_LDFLAGS = \
 # Libraries and corresponding dependencies for compiling gdb.
 # XM_CLIBS, defined in *config files, have host-dependent libs.
 # LIBIBERTY appears twice on purpose.
-CLIBS = $(SIM) $(READLINE) $(OPCODES) $(LIBCTF) $(BFD) $(ZLIB) \
+CLIBS = $(SIM) $(READLINE) $(OPCODES) $(LIBCTF) $(BFD) $(LIBCTFFRAME) $(ZLIB) \
         $(LIBSUPPORT) $(INTL) $(LIBIBERTY) $(LIBDECNUMBER) \
 	$(XM_CLIBS) $(GDBTKLIBS)  $(LIBBACKTRACE_LIB) \
 	@LIBS@ @GUILE_LIBS@ @PYTHON_LIBS@ \
@@ -653,7 +657,7 @@ CLIBS = $(SIM) $(READLINE) $(OPCODES) $(LIBCTF) $(BFD) $(ZLIB) \
 	$(WIN32LIBS) $(LIBGNU) $(LIBGNU_EXTRA_LIBS) $(LIBICONV) \
 	$(LIBMPFR) $(LIBGMP) $(SRCHIGH_LIBS) $(LIBXXHASH) $(PTHREAD_LIBS) \
 	$(DEBUGINFOD_LIBS) $(LIBBABELTRACE_LIB)
-CDEPS = $(NAT_CDEPS) $(SIM) $(BFD) $(READLINE_DEPS) $(CTF_DEPS) \
+CDEPS = $(NAT_CDEPS) $(SIM) $(CTFFRAME_DEPS) $(BFD) $(READLINE_DEPS) $(CTF_DEPS) \
 	$(OPCODES) $(INTL_DEPS) $(LIBIBERTY) $(CONFIG_DEPS) $(LIBGNU) \
 	$(LIBSUPPORT)
 
diff --git a/gdb/acinclude.m4 b/gdb/acinclude.m4
index 95ff2b6f35e..f3a4ebba1be 100644
--- a/gdb/acinclude.m4
+++ b/gdb/acinclude.m4
@@ -233,9 +233,9 @@ AC_DEFUN([GDB_AC_CHECK_BFD], [
   # always want our bfd.
   CFLAGS="-I${srcdir}/../include -I../bfd -I${srcdir}/../bfd $CFLAGS"
   ZLIBDIR=`echo $zlibdir | sed 's,\$(top_builddir)/,,g'`
-  LDFLAGS="-L../bfd -L../libiberty $ZLIBDIR $LDFLAGS"
+  LDFLAGS="-L../bfd -L../libiberty -L../libctfframe/.libs/ $ZLIBDIR $LDFLAGS"
   intl=`echo $LIBINTL | sed 's,${top_builddir}/,,g'`
-  LIBS="-lbfd -liberty -lz $intl $LIBS"
+  LIBS="-lbfd -liberty -lz -lctfframe $intl $LIBS"
   AC_CACHE_CHECK(
     [$1],
     [$2],
diff --git a/gdb/configure b/gdb/configure
index 1008cbef28b..e9ac29f1b50 100755
--- a/gdb/configure
+++ b/gdb/configure
@@ -631,6 +631,8 @@ GDB_NM_FILE
 LTLIBXXHASH
 LIBXXHASH
 HAVE_LIBXXHASH
+CTFFRAME_DEPS
+LIBCTFFRAME
 CTF_DEPS
 LIBCTF
 LTLIBBABELTRACE
@@ -936,6 +938,7 @@ with_libbabeltrace_prefix
 with_libbabeltrace_type
 with_xxhash
 enable_libctf
+enable_libctfframe
 with_libxxhash_prefix
 with_libxxhash_type
 enable_unit_tests
@@ -1612,6 +1615,7 @@ Optional Features:
   --enable-libbacktrace   use libbacktrace to write a backtrace after a fatal
                           signal.
   --enable-libctf         Handle .ctf type-info sections [default=yes]
+  --enable-libctfframe    Handle .ctf_frame sections [default=yes]
   --enable-unit-tests     Enable the inclusion of unit tests when compiling
                           GDB
 
@@ -17259,9 +17263,9 @@ WIN32LIBS="$WIN32LIBS $WIN32APILIBS"
   # always want our bfd.
   CFLAGS="-I${srcdir}/../include -I../bfd -I${srcdir}/../bfd $CFLAGS"
   ZLIBDIR=`echo $zlibdir | sed 's,\$(top_builddir)/,,g'`
-  LDFLAGS="-L../bfd -L../libiberty $ZLIBDIR $LDFLAGS"
+  LDFLAGS="-L../bfd -L../libiberty -L../libctfframe/.libs/ $ZLIBDIR $LDFLAGS"
   intl=`echo $LIBINTL | sed 's,${top_builddir}/,,g'`
-  LIBS="-lbfd -liberty -lz $intl $LIBS"
+  LIBS="-lbfd -liberty -lz -lctfframe $intl $LIBS"
   { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ELF support in BFD" >&5
 $as_echo_n "checking for ELF support in BFD... " >&6; }
 if ${gdb_cv_var_elf+:} false; then :
@@ -17374,9 +17378,9 @@ fi
   # always want our bfd.
   CFLAGS="-I${srcdir}/../include -I../bfd -I${srcdir}/../bfd $CFLAGS"
   ZLIBDIR=`echo $zlibdir | sed 's,\$(top_builddir)/,,g'`
-  LDFLAGS="-L../bfd -L../libiberty $ZLIBDIR $LDFLAGS"
+  LDFLAGS="-L../bfd -L../libiberty -L../libctfframe/.libs/ $ZLIBDIR $LDFLAGS"
   intl=`echo $LIBINTL | sed 's,${top_builddir}/,,g'`
-  LIBS="-lbfd -liberty -lz $intl $LIBS"
+  LIBS="-lbfd -liberty -lz -lctfframe $intl $LIBS"
   { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Mach-O support in BFD" >&5
 $as_echo_n "checking for Mach-O support in BFD... " >&6; }
 if ${gdb_cv_var_macho+:} false; then :
@@ -19341,6 +19345,29 @@ fi
 
 
 
+ # Check whether --enable-libctfframe was given.
+if test "${enable_libctfframe+set}" = set; then :
+  enableval=$enable_libctfframe;
+      case "$enableval" in
+       yes|no) ;;
+       *) as_fn_error $? "Argument to enable/disable libctfframe must be yes or no" "$LINENO" 5 ;;
+      esac
+
+else
+  enable_libctfframe=yes
+fi
+
+
+if test x${enable_static} = xno; then
+  LIBCTFFRAME="-Wl,--rpath,../libctfframe/.libs ../libctfframe/.libs/libctfframe.so"
+  CTFFRAME_DEPS="../libctfframe/.libs/libctfframe.so"
+else
+  LIBCTFFRAME="../libctfframe/.libs/libctfframe.a"
+  CTFFRAME_DEPS="$LIBCTFFRAME"
+fi
+
+
+
 # If nativefile (NAT_FILE) is not set in configure.nat, we link to an
 # empty version.
 
diff --git a/gdb/configure.ac b/gdb/configure.ac
index e3c19bc8859..a8d9a411004 100644
--- a/gdb/configure.ac
+++ b/gdb/configure.ac
@@ -2190,6 +2190,17 @@ fi
 AC_SUBST(LIBCTF)
 AC_SUBST(CTF_DEPS)
 
+GCC_ENABLE([libctfframe], [yes], [], [Handle .ctf_frame sections])
+if test x${enable_static} = xno; then
+  LIBCTFFRAME="-Wl,--rpath,../libctfframe/.libs ../libctfframe/.libs/libctfframe.so"
+  CTFFRAME_DEPS="../libctfframe/.libs/libctfframe.so"
+else
+  LIBCTFFRAME="../libctfframe/.libs/libctfframe.a"
+  CTFFRAME_DEPS="$LIBCTFFRAME"
+fi
+AC_SUBST(LIBCTFFRAME)
+AC_SUBST(CTFFRAME_DEPS)
+
 # If nativefile (NAT_FILE) is not set in configure.nat, we link to an
 # empty version.
 
diff --git a/sim/common/Make-common.in b/sim/common/Make-common.in
index 74e5dad3049..ac974087798 100644
--- a/sim/common/Make-common.in
+++ b/sim/common/Make-common.in
@@ -222,11 +222,14 @@ SIM_HW_DEVICES = cfi core pal glue $(SIM_EXTRA_HW_DEVICES)
 ZLIB = $(zlibdir) -lz
 LIBIBERTY_LIB = ../../libiberty/libiberty.a
 BFD_LIB = ../../bfd/libbfd.a
+LIBCTFFRAME_LIB = ../../libctfframe/.libs/libctfframe.a
 OPCODES_LIB = ../../opcodes/libopcodes.a
 CONFIG_LIBS = $(COMMON_LIBS) @LIBS@ $(ZLIB)
-LIBDEPS = $(BFD_LIB) $(OPCODES_LIB) $(LIBINTL_DEP) $(LIBIBERTY_LIB)
+LIBDEPS = $(BFD_LIB) $(OPCODES_LIB) $(LIBINTL_DEP) $(LIBIBERTY_LIB) \
+	  $(LIBCTFFRAME_LIB)
 EXTRA_LIBS = $(BFD_LIB) $(OPCODES_LIB) $(LIBINTL) $(LIBIBERTY_LIB) \
-	$(CONFIG_LIBS) $(SIM_EXTRA_LIBS) $(LIBDL) $(LIBGNU) $(LIBGNU_EXTRA_LIBS)
+	     $(LIBCTFFRAME_LIB) $(CONFIG_LIBS) $(SIM_EXTRA_LIBS) \
+	     $(LIBDL) $(LIBGNU) $(LIBGNU_EXTRA_LIBS)
 
 COMMON_OBJS_NAMES = \
 	callback.o \
-- 
2.31.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH,RFC 3/7] libctfframe: add the CTF Frame library
       [not found] ` <20220507005223.3093035-4-indu.bhagat@oracle.com>
@ 2022-05-08 22:00   ` Indu Bhagat
  2022-05-17 13:44     ` Jan Beulich
  0 siblings, 1 reply; 11+ messages in thread
From: Indu Bhagat @ 2022-05-08 22:00 UTC (permalink / raw)
  To: binutils

On 5/6/22 5:52 PM, Indu Bhagat wrote:
> From: Weimin Pan <weimin.pan@oracle.com>
> 
> libctfframe is a library that allows you to:
> - decode a .ctf_frame section
> - probe and inspect a .ctf_frame section
> - encode (and eventually write) a .ctf_frame section.
> 
> This library is currently being used by the linker, readelf and objdump.
> The file include/ctf-frame-api.h defines the user-facing APIs for decoding,
> encoding and probing .ctf_frame sections. A set of error codes together
> with their error message strings are also defined.
> 
> Endian flipping is performed automatically at read and write time, if
> cross-endianness is detected.
> 
> ChangeLog:
> 
> 	* Makefile.def: Add libctfframe as new module with its
> 	dependencies.
> 	* Makefile.in: Regenerated.
> 	* binutils/Makefile.am: Add libctfframe.
> 	* binutils/Makefile.in: Regenerated.
> 	* configure: Regenerated
> 	* configure.ac: Add libctfframe to host_libs.
> 	* libctfframe/Makefile.am: New file.
> 	* libctfframe/Makefile.in: New file.
> 	* libctfframe/aclocal.m4: New file.
> 	* libctfframe/config.h.in: New file.
> 	* libctfframe/configure: New file.
> 	* libctfframe/configure.ac: New file.
> 	* libctfframe/ctf-frame-error.c: New file.
> 	* libctfframe/ctf-frame-impl.h: New file.
> 	* libctfframe/ctf-frame.c: New file.
> 
> include/ChangeLog:
> 
> 	* ctf-frame-api.h: New file.
> ---
>   Makefile.def                  |     2 +
>   Makefile.in                   |  1288 ++-
>   binutils/Makefile.am          |     2 +
>   binutils/Makefile.in          |     1 +
>   configure                     |     2 +-
>   configure.ac                  |     2 +-
>   include/ctf-frame-api.h       |   210 +
>   libctfframe/Makefile.am       |    38 +
>   libctfframe/Makefile.in       |   915 ++
>   libctfframe/aclocal.m4        |  1241 +++
>   libctfframe/config.h.in       |   144 +
>   libctfframe/configure         | 15118 ++++++++++++++++++++++++++++++++
>   libctfframe/configure.ac      |    75 +
>   libctfframe/ctf-frame-error.c |    49 +
>   libctfframe/ctf-frame-impl.h  |    55 +
>   libctfframe/ctf-frame.c       |  1515 ++++
>   16 files changed, 20650 insertions(+), 7 deletions(-)
>   create mode 100644 include/ctf-frame-api.h
>   create mode 100644 libctfframe/Makefile.am
>   create mode 100644 libctfframe/Makefile.in
>   create mode 100644 libctfframe/aclocal.m4
>   create mode 100644 libctfframe/config.h.in
>   create mode 100755 libctfframe/configure
>   create mode 100644 libctfframe/configure.ac
>   create mode 100644 libctfframe/ctf-frame-error.c
>   create mode 100644 libctfframe/ctf-frame-impl.h
>   create mode 100644 libctfframe/ctf-frame.c
> 

Hi,

This patch did not make to the list as it exceeds the 400 KB limit

--------------

Your mail to 'Binutils' with the subject

     [PATCH,RFC 3/7] libctfframe: add the CTF Frame library

Is being held until the list moderator can review it for approval.

The reason it is being held:

     Message body is too big: 666389 bytes with a limit of 400 KB

-------------

What is the recommended way to address this ? Splitting this patch up 
with libctfframe/configure in a separate commit will not address the 
issue as the libctfframe/configure will still be larger than 400 KB.

Will this eventually get moderator approval or should I push it all in a 
publicly available branch on github ?

Thanks
Indu

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH,RFC 0/7] Definition and Implementation of CTF Frame format
  2022-05-07  0:52 [PATCH,RFC 0/7] Definition and Implementation of CTF Frame format Indu Bhagat
                   ` (6 preceding siblings ...)
       [not found] ` <20220507005223.3093035-4-indu.bhagat@oracle.com>
@ 2022-05-13 13:08 ` Michael Matz
  2022-05-13 17:50   ` [PATCH, RFC " Indu Bhagat
  7 siblings, 1 reply; 11+ messages in thread
From: Michael Matz @ 2022-05-13 13:08 UTC (permalink / raw)
  To: Indu Bhagat; +Cc: binutils

Hello,

On Fri, 6 May 2022, Indu Bhagat via Binutils wrote:

> PT_GNU_CTF_FRAME) in ELF linked binaries. The CTF Frame format specifies the
> minimal necessary unwind information, i.e., information needed to recover only
> the CFA and the return address (RA) for all insns of a program.  This
> information is a subset of what .eh_frame can convey: .eh_frame specifies how
> to resurrect all callee-saved registers, if need be.

So, it's designed to be less complete than .eh_frame.  That in itself is 
not a problem, but ...

> ratio = (.ctf_frame / (.eh_frame+.eh_frame_hdr))
> 
> ---------------------------------------------------
> program    |  [x86_64] ratio  |  [aarch64] ratio
> ---------------------------------------------------
> addr2line  |       1.13       |    0.67
> ar         |       1.03       |    0.70
> as         |       1.00       |    0.73
> c++filt    |       1.08       |    0.64
> elfedit    |       1.03       |    0.66
> gprof      |       1.06       |    0.71
> ld         |       1.02       |    0.73
> nm         |       1.07       |    0.69
> objcopy    |       1.08       |    0.72
> objdump    |       1.08       |    0.73
> size       |       1.10       |    0.67
> strings    |       1.09       |    0.67

... this is.  You need more space on x86-64 to store severely less 
information, and even on aarch64 the savings are meager when compared to 
the information loss.

So, hmm, who would want to ever enable .ctf_frame instead of .eh_frame?


Ciao,
Michael.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH, RFC 0/7] Definition and Implementation of CTF Frame format
  2022-05-13 13:08 ` [PATCH,RFC 0/7] Definition and Implementation of CTF Frame format Michael Matz
@ 2022-05-13 17:50   ` Indu Bhagat
  0 siblings, 0 replies; 11+ messages in thread
From: Indu Bhagat @ 2022-05-13 17:50 UTC (permalink / raw)
  To: Michael Matz; +Cc: binutils

Hi Michael,

On 5/13/22 6:08 AM, Michael Matz wrote:
> Hello,
> 
> On Fri, 6 May 2022, Indu Bhagat via Binutils wrote:
> 
>> PT_GNU_CTF_FRAME) in ELF linked binaries. The CTF Frame format specifies the
>> minimal necessary unwind information, i.e., information needed to recover only
>> the CFA and the return address (RA) for all insns of a program.  This
>> information is a subset of what .eh_frame can convey: .eh_frame specifies how
>> to resurrect all callee-saved registers, if need be.
> 
> So, it's designed to be less complete than .eh_frame.  That in itself is
> not a problem, but ...
> 
>> ratio = (.ctf_frame / (.eh_frame+.eh_frame_hdr))
>>
>> ---------------------------------------------------
>> program    |  [x86_64] ratio  |  [aarch64] ratio
>> ---------------------------------------------------
>> addr2line  |       1.13       |    0.67
>> ar         |       1.03       |    0.70
>> as         |       1.00       |    0.73
>> c++filt    |       1.08       |    0.64
>> elfedit    |       1.03       |    0.66
>> gprof      |       1.06       |    0.71
>> ld         |       1.02       |    0.73
>> nm         |       1.07       |    0.69
>> objcopy    |       1.08       |    0.72
>> objdump    |       1.08       |    0.73
>> size       |       1.10       |    0.67
>> strings    |       1.09       |    0.67
> 
> ... this is.  You need more space on x86-64 to store severely less
> information, and even on aarch64 the savings are meager when compared to
> the information loss.
> 
> So, hmm, who would want to ever enable .ctf_frame instead of .eh_frame?
> 

Those applications which struggle with the following two complaints will 
want to consider .ctf_frame:
- eh_frame-based unwinders are complex and slow to manage with
- eh_frame-based unwinders are large as they need to deal with DWARF 
opcodes via a stack machine implementation

We know of two large applications of interest which have adopted ad-hoc 
solutions (based on post processing of binaries etc.) _and_ devised 
*their own* unwind formats to address the above issues.  The issue is 
that these ad-hoc solutions are themselves reaching their limits or are 
borderline unmaintainable because of the need to support more 
architectures over time, the need to regularly fix issues in different 
components involved as the generated code evolves.

Also see 
https://sourceware.org/pipermail/binutils/2021-December/118880.html.

So, the main premise of CTF Frame format is to address these issues, and 
more importantly make itself available via the toolchain because 
compiler/as/ld has the visibility needed to correctly generate unwind 
information. Post-processing binaries is not the right choice (IMO).

Highlighting two of the most important differences between .ctf_frame 
and .eh_frame in this context:

-- Simple unwinding with no stack machine.
    .eh_frame based unwinding needs a stack machine based implementation 
to work through the DWARF opcodes.
    .ctf_frame based unwinder is very simple to write and need no stack 
machine.

-- Self-sufficient FREs
    To achieve compactness, .eh_frame stores the DWARF opcodes in a 
sequence such that all the applicable instructions need to be executed 
until the desired PC is reached.
    .ctf_frame unwinding, on the other hand, needs to locate what is 
called the CTF Frame Row Entry (CTF FRE) in the .ctf_frame section, for 
the given PC.  Each CTF FRE is a _self-sufficient_ entry which contains 
information to unwind from the given PC.

This should make unwinding faster with .ctf_frame.

That said, the sizes of .ctf_frame sections on x86_64 will improve soon. 
  The current issue of having to encode a distinct CTF FRE for each PC 
in the .plt* section, is causing some bloat on x86_64. I expect some 
reduction (~10-15% ?), but we will see :)

Thanks
Indu

> 
> Ciao,
> Michael.
> 


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH,RFC 3/7] libctfframe: add the CTF Frame library
  2022-05-08 22:00   ` [PATCH,RFC 3/7] libctfframe: add the CTF Frame library Indu Bhagat
@ 2022-05-17 13:44     ` Jan Beulich
  0 siblings, 0 replies; 11+ messages in thread
From: Jan Beulich @ 2022-05-17 13:44 UTC (permalink / raw)
  To: Indu Bhagat; +Cc: binutils

On 09.05.2022 00:00, Indu Bhagat via Binutils wrote:
> On 5/6/22 5:52 PM, Indu Bhagat wrote:
>> From: Weimin Pan <weimin.pan@oracle.com>
>>
>> libctfframe is a library that allows you to:
>> - decode a .ctf_frame section
>> - probe and inspect a .ctf_frame section
>> - encode (and eventually write) a .ctf_frame section.
>>
>> This library is currently being used by the linker, readelf and objdump.
>> The file include/ctf-frame-api.h defines the user-facing APIs for decoding,
>> encoding and probing .ctf_frame sections. A set of error codes together
>> with their error message strings are also defined.
>>
>> Endian flipping is performed automatically at read and write time, if
>> cross-endianness is detected.
>>
>> ChangeLog:
>>
>> 	* Makefile.def: Add libctfframe as new module with its
>> 	dependencies.
>> 	* Makefile.in: Regenerated.
>> 	* binutils/Makefile.am: Add libctfframe.
>> 	* binutils/Makefile.in: Regenerated.
>> 	* configure: Regenerated
>> 	* configure.ac: Add libctfframe to host_libs.
>> 	* libctfframe/Makefile.am: New file.
>> 	* libctfframe/Makefile.in: New file.
>> 	* libctfframe/aclocal.m4: New file.
>> 	* libctfframe/config.h.in: New file.
>> 	* libctfframe/configure: New file.
>> 	* libctfframe/configure.ac: New file.
>> 	* libctfframe/ctf-frame-error.c: New file.
>> 	* libctfframe/ctf-frame-impl.h: New file.
>> 	* libctfframe/ctf-frame.c: New file.
>>
>> include/ChangeLog:
>>
>> 	* ctf-frame-api.h: New file.
>> ---
>>   Makefile.def                  |     2 +
>>   Makefile.in                   |  1288 ++-
>>   binutils/Makefile.am          |     2 +
>>   binutils/Makefile.in          |     1 +
>>   configure                     |     2 +-
>>   configure.ac                  |     2 +-
>>   include/ctf-frame-api.h       |   210 +
>>   libctfframe/Makefile.am       |    38 +
>>   libctfframe/Makefile.in       |   915 ++
>>   libctfframe/aclocal.m4        |  1241 +++
>>   libctfframe/config.h.in       |   144 +
>>   libctfframe/configure         | 15118 ++++++++++++++++++++++++++++++++
>>   libctfframe/configure.ac      |    75 +
>>   libctfframe/ctf-frame-error.c |    49 +
>>   libctfframe/ctf-frame-impl.h  |    55 +
>>   libctfframe/ctf-frame.c       |  1515 ++++
>>   16 files changed, 20650 insertions(+), 7 deletions(-)
>>   create mode 100644 include/ctf-frame-api.h
>>   create mode 100644 libctfframe/Makefile.am
>>   create mode 100644 libctfframe/Makefile.in
>>   create mode 100644 libctfframe/aclocal.m4
>>   create mode 100644 libctfframe/config.h.in
>>   create mode 100755 libctfframe/configure
>>   create mode 100644 libctfframe/configure.ac
>>   create mode 100644 libctfframe/ctf-frame-error.c
>>   create mode 100644 libctfframe/ctf-frame-impl.h
>>   create mode 100644 libctfframe/ctf-frame.c
>>
> 
> Hi,
> 
> This patch did not make to the list as it exceeds the 400 KB limit
> 
> --------------
> 
> Your mail to 'Binutils' with the subject
> 
>      [PATCH,RFC 3/7] libctfframe: add the CTF Frame library
> 
> Is being held until the list moderator can review it for approval.
> 
> The reason it is being held:
> 
>      Message body is too big: 666389 bytes with a limit of 400 KB
> 
> -------------
> 
> What is the recommended way to address this ? Splitting this patch up 
> with libctfframe/configure in a separate commit will not address the 
> issue as the libctfframe/configure will still be larger than 400 KB.

A common way around this is to supply the patch as an attachment instead,
possibly even compressed. Typically such large patches are mostly
mechanical changes (or even changes to generated files, as looks to be
the case here), and hence fair parts of the patch won't need commenting
on. It can therefore make sense to attach the full patch, but at the
same time inline the non-mechanical parts for easier commenting. Such
splitting will of course want pointing out very prominently.

Jan


^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2022-05-17 13:44 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-05-07  0:52 [PATCH,RFC 0/7] Definition and Implementation of CTF Frame format Indu Bhagat
2022-05-07  0:52 ` [PATCH,RFC 1/7] ctf-frame.h: Add CTF Frame format definition Indu Bhagat
2022-05-07  0:52 ` [PATCH,RFC 2/7] gas: generate .ctf_frame Indu Bhagat
2022-05-07  0:52 ` [PATCH,RFC 4/7] bfd: linker: merge .ctf_frame sections Indu Bhagat
2022-05-07  0:52 ` [PATCH,RFC 5/7] readelf/objdump: support for CTF Frame section Indu Bhagat
2022-05-07  0:52 ` [PATCH,RFC 6/7] unwinder: generate backtrace using CTF Frame format Indu Bhagat
2022-05-07  0:52 ` [PATCH, RFC 7/7] gdb: sim: buildsystem changes to accommodate libctfframe Indu Bhagat
     [not found] ` <20220507005223.3093035-4-indu.bhagat@oracle.com>
2022-05-08 22:00   ` [PATCH,RFC 3/7] libctfframe: add the CTF Frame library Indu Bhagat
2022-05-17 13:44     ` Jan Beulich
2022-05-13 13:08 ` [PATCH,RFC 0/7] Definition and Implementation of CTF Frame format Michael Matz
2022-05-13 17:50   ` [PATCH, RFC " Indu Bhagat

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).