public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* [PATCH, V2 00/10] Synthesize CFI for hand-written asm
@ 2023-10-30 16:51 Indu Bhagat
  2023-10-30 16:51 ` [PATCH, V2 01/10] gas: dw2gencfi: minor rejig for cfi_sections_set and all_cfi_sections Indu Bhagat
                   ` (9 more replies)
  0 siblings, 10 replies; 30+ messages in thread
From: Indu Bhagat @ 2023-10-30 16:51 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

Hello,

The patches in the V1 series sent earlier
(https://sourceware.org/pipermail/binutils/2023-October/130163.html)
were not bisectable (Thanks to the Linaro-TCWG-CI automation for the
alert).  The V2 series fixes the issue, and adds a new patch from V1:
  - [PATCH, V2 08/10] gas: doc: update documentation for the new listing
    option

I have updated the patches in user branch users/ibhagat/try-scfi-v2.

Thanks
Indu

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

This patch series adds support in GAS to synthesize CFI for hand-written
asm, acronym'd as SCFI.  The RFC patch series were sent earlier 
(https://sourceware.org/pipermail/binutils/2023-September/129560.html).
I have addressed the review comments, tested the implementation further
and created the V2 series here.  If you prefer to try out the code etc.,
the implementation is also available in the branch
users/ibhagat/try-scfi-v2.

Each patch in this series has notes on how it has evolved since the RFC
posting, but here is a brief summary:
  - Updated some documentation stubs.
  - ginsn are now emitted in the listing output by using the flag -i.
  - GAS now warns once per file if there are CFI directives embedded in
    the user input of asm.  The warning message is:
    "Warning: --scfi=all ignores some user-specified CFI directives"
  - A distinct set of pseudo_typeS are now defined in scfidwgen.c to
    handle (i.e. ignore most of) the .cfi_* directives when --scfi is in
    effect.  Handling of .cfi_sections is TBD.
  - Bug fixes and other improvements.
  - Beginning of a asm block is identified with a .type  foo, @function.
    If the user does not close the block with a .size foo, .-foo, the
    asm block is assumed to continue until the next .type bar, @function
    directive.  The testcases scfi-asm-marker-* should be helpful.

Motivation for the patch series is to alleviate users from manually
adding the CFI directives in hand-written asm. Manually adding CFI
annotations to user input asm needs additional expertise; human-errors
are possible and indeed occur more often than one may like.  These
errors, if present at the time of virtual stack unwind, lead to
unfavorable outcomes: incorrect stacktraces, program state corruption
etc.

For synthesis of CFI to work, the user input must adhere to the ABI and
the appropriate calling conventions, as GAS derives the set of
callee-saved registers based on that contract.  The SCFI implementation
is based on some heuristics/rules, please see the patch "gas: synthesize
CFI for hand-written asm" for more details.  Further, at the moment,
SCFI does not help in cases when the control flow graph of the input asm
cannot be generated (e.g., in case of indirect jumps, jump tables).

Thanks,

Indu Bhagat (10):
  gas: dw2gencfi: minor rejig for cfi_sections_set and all_cfi_sections
  gas: dw2gencfi: use all_cfi_sections instead of cfi_sections
  gas: dw2gencfi: expose a new cfi_set_last_fde API
  gas: dw2gencfi: move some tc_* defines to the header file
  gas: add new command line option --scfi[=all,none]
  gas: scfidw2gen: new functionality to prepapre for SCFI
  gas: synthesize CFI for hand-written asm
  gas: doc: update documentation for the new listing option
  gas: testsuite: add a x86_64 testsuite for SCFI
  gas/NEWS: announce the new command line option

 gas/Makefile.am                               |    6 +
 gas/Makefile.in                               |   18 +-
 gas/NEWS                                      |    2 +
 gas/as.c                                      |   27 +-
 gas/as.h                                      |    8 +
 gas/config/obj-elf.c                          |   16 +
 gas/config/tc-i386.c                          |  735 +++++++++-
 gas/config/tc-i386.h                          |   21 +
 gas/doc/as.texi                               |   28 +-
 gas/dw2gencfi.c                               |   42 +-
 gas/dw2gencfi.h                               |   14 +
 gas/ginsn.c                                   | 1225 +++++++++++++++++
 gas/ginsn.h                                   |  392 ++++++
 gas/listing.h                                 |    1 +
 gas/read.c                                    |   21 +-
 gas/scfi.c                                    | 1222 ++++++++++++++++
 gas/scfi.h                                    |   38 +
 gas/scfidw2gen.c                              |  325 +++++
 gas/scfidw2gen.h                              |   37 +
 gas/subsegs.c                                 |    1 +
 gas/subsegs.h                                 |    2 +
 gas/symbols.c                                 |    3 +
 gas/testsuite/gas/scfi/README                 |   17 +
 gas/testsuite/gas/scfi/x86_64/scfi-add-1.d    |   25 +
 gas/testsuite/gas/scfi/x86_64/scfi-add-1.l    |    2 +
 gas/testsuite/gas/scfi/x86_64/scfi-add-1.s    |   13 +
 gas/testsuite/gas/scfi/x86_64/scfi-add-2.d    |   36 +
 gas/testsuite/gas/scfi/x86_64/scfi-add-2.l    |    2 +
 gas/testsuite/gas/scfi/x86_64/scfi-add-2.s    |   48 +
 .../gas/scfi/x86_64/scfi-asm-marker-1.d       |   28 +
 .../gas/scfi/x86_64/scfi-asm-marker-1.l       |    3 +
 .../gas/scfi/x86_64/scfi-asm-marker-1.s       |   27 +
 .../gas/scfi/x86_64/scfi-asm-marker-2.d       |   24 +
 .../gas/scfi/x86_64/scfi-asm-marker-2.l       |    3 +
 .../gas/scfi/x86_64/scfi-asm-marker-2.s       |   11 +
 .../gas/scfi/x86_64/scfi-asm-marker-3.d       |   31 +
 .../gas/scfi/x86_64/scfi-asm-marker-3.l       |    2 +
 .../gas/scfi/x86_64/scfi-asm-marker-3.s       |   38 +
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.d  |   31 +
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.l  |    2 +
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.s  |   22 +
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.d  |   57 +
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.l  |    2 +
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.s  |   49 +
 .../gas/scfi/x86_64/scfi-callee-saved-1.d     |   40 +
 .../gas/scfi/x86_64/scfi-callee-saved-1.l     |    2 +
 .../gas/scfi/x86_64/scfi-callee-saved-1.s     |   26 +
 .../gas/scfi/x86_64/scfi-callee-saved-2.d     |   41 +
 .../gas/scfi/x86_64/scfi-callee-saved-2.l     |    2 +
 .../gas/scfi/x86_64/scfi-callee-saved-2.s     |   42 +
 .../gas/scfi/x86_64/scfi-callee-saved-3.d     |   42 +
 .../gas/scfi/x86_64/scfi-callee-saved-3.l     |    3 +
 .../gas/scfi/x86_64/scfi-callee-saved-3.s     |   40 +
 .../gas/scfi/x86_64/scfi-callee-saved-4.d     |   40 +
 .../gas/scfi/x86_64/scfi-callee-saved-4.l     |    3 +
 .../gas/scfi/x86_64/scfi-callee-saved-4.s     |   55 +
 gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.d    |   36 +
 gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.l    |    2 +
 gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.s    |   47 +
 gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.d    |   28 +
 gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.l    |    2 +
 gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.s    |   21 +
 .../gas/scfi/x86_64/scfi-cfi-label-1.d        |   37 +
 .../gas/scfi/x86_64/scfi-cfi-label-1.l        |    2 +
 .../gas/scfi/x86_64/scfi-cfi-label-1.s        |   19 +
 gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.d   |    5 +
 gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.l   |    3 +
 gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.s   |   23 +
 gas/testsuite/gas/scfi/x86_64/scfi-diag-1.l   |    4 +
 gas/testsuite/gas/scfi/x86_64/scfi-diag-1.s   |   23 +
 gas/testsuite/gas/scfi/x86_64/scfi-diag-2.l   |    4 +
 gas/testsuite/gas/scfi/x86_64/scfi-diag-2.s   |   28 +
 .../gas/scfi/x86_64/scfi-dyn-stack-1.d        |   23 +
 .../gas/scfi/x86_64/scfi-dyn-stack-1.l        |    2 +
 .../gas/scfi/x86_64/scfi-dyn-stack-1.s        |   50 +
 .../gas/scfi/x86_64/scfi-fp-diag-2.l          |    3 +
 .../gas/scfi/x86_64/scfi-fp-diag-2.s          |   55 +
 gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.d |   26 +
 gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.l |    2 +
 gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.s |   13 +
 .../gas/scfi/x86_64/scfi-indirect-mov-1.d     |   51 +
 .../gas/scfi/x86_64/scfi-indirect-mov-1.l     |    2 +
 .../gas/scfi/x86_64/scfi-indirect-mov-1.s     |   48 +
 .../gas/scfi/x86_64/scfi-indirect-mov-2.d     |   41 +
 .../gas/scfi/x86_64/scfi-indirect-mov-2.l     |    2 +
 .../gas/scfi/x86_64/scfi-indirect-mov-2.s     |   38 +
 .../gas/scfi/x86_64/scfi-indirect-mov-3.d     |   41 +
 .../gas/scfi/x86_64/scfi-indirect-mov-3.l     |    2 +
 .../gas/scfi/x86_64/scfi-indirect-mov-3.s     |   38 +
 .../gas/scfi/x86_64/scfi-indirect-mov-4.d     |   63 +
 .../gas/scfi/x86_64/scfi-indirect-mov-4.l     |    3 +
 .../gas/scfi/x86_64/scfi-indirect-mov-4.s     |   68 +
 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.d    |   37 +
 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.l    |    2 +
 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.s    |   40 +
 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.d  |   36 +
 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.l  |    2 +
 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.s  |   26 +
 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.d  |   35 +
 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.l  |    2 +
 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.s  |   24 +
 .../gas/scfi/x86_64/scfi-pushsection-1.d      |   43 +
 .../gas/scfi/x86_64/scfi-pushsection-1.l      |    2 +
 .../gas/scfi/x86_64/scfi-pushsection-1.s      |   40 +
 .../gas/scfi/x86_64/scfi-pushsection-2.d      |   39 +
 .../gas/scfi/x86_64/scfi-pushsection-2.l      |    2 +
 .../gas/scfi/x86_64/scfi-pushsection-2.s      |   38 +
 .../gas/scfi/x86_64/scfi-selfalign-func-1.d   |   31 +
 .../gas/scfi/x86_64/scfi-selfalign-func-1.l   |    2 +
 .../gas/scfi/x86_64/scfi-selfalign-func-1.s   |   36 +
 gas/testsuite/gas/scfi/x86_64/scfi-simple-1.d |   26 +
 gas/testsuite/gas/scfi/x86_64/scfi-simple-1.l |    2 +
 gas/testsuite/gas/scfi/x86_64/scfi-simple-1.s |   15 +
 gas/testsuite/gas/scfi/x86_64/scfi-simple-2.d |   30 +
 gas/testsuite/gas/scfi/x86_64/scfi-simple-2.l |    2 +
 gas/testsuite/gas/scfi/x86_64/scfi-simple-2.s |   16 +
 gas/testsuite/gas/scfi/x86_64/scfi-sub-1.d    |   25 +
 gas/testsuite/gas/scfi/x86_64/scfi-sub-1.l    |    2 +
 gas/testsuite/gas/scfi/x86_64/scfi-sub-1.s    |   12 +
 gas/testsuite/gas/scfi/x86_64/scfi-sub-2.d    |   31 +
 gas/testsuite/gas/scfi/x86_64/scfi-sub-2.l    |    2 +
 gas/testsuite/gas/scfi/x86_64/scfi-sub-2.s    |   29 +
 .../gas/scfi/x86_64/scfi-unsupported-1.l      |    2 +
 .../gas/scfi/x86_64/scfi-unsupported-1.s      |   10 +
 .../gas/scfi/x86_64/scfi-unsupported-2.l      |    3 +
 .../gas/scfi/x86_64/scfi-unsupported-2.s      |   14 +
 .../gas/scfi/x86_64/scfi-unsupported-3.l      |    3 +
 .../gas/scfi/x86_64/scfi-unsupported-3.s      |   14 +
 .../gas/scfi/x86_64/scfi-unsupported-4.l      |    4 +
 .../gas/scfi/x86_64/scfi-unsupported-4.s      |   23 +
 .../gas/scfi/x86_64/scfi-unsupported-cfg-1.l  |    3 +
 .../gas/scfi/x86_64/scfi-unsupported-cfg-1.s  |   53 +
 .../gas/scfi/x86_64/scfi-unsupported-drap-1.l |    4 +
 .../gas/scfi/x86_64/scfi-unsupported-drap-1.s |   75 +
 gas/testsuite/gas/scfi/x86_64/scfi-x86-64.exp |  103 ++
 135 files changed, 6749 insertions(+), 39 deletions(-)
 create mode 100644 gas/ginsn.c
 create mode 100644 gas/ginsn.h
 create mode 100644 gas/scfi.c
 create mode 100644 gas/scfi.h
 create mode 100644 gas/scfidw2gen.c
 create mode 100644 gas/scfidw2gen.h
 create mode 100644 gas/testsuite/gas/scfi/README
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-add-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-add-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-add-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-add-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-add-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-add-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-diag-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-diag-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-diag-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-diag-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-simple-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-simple-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-simple-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-simple-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-simple-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-simple-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-sub-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-sub-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-sub-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-sub-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-sub-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-sub-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-3.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-3.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-4.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-4.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-drap-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-drap-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-x86-64.exp

-- 
2.41.0


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

* [PATCH, V2 01/10] gas: dw2gencfi: minor rejig for cfi_sections_set and all_cfi_sections
  2023-10-30 16:51 [PATCH, V2 00/10] Synthesize CFI for hand-written asm Indu Bhagat
@ 2023-10-30 16:51 ` Indu Bhagat
  2023-10-30 16:51 ` [PATCH, V2 02/10] gas: dw2gencfi: use all_cfi_sections instead of cfi_sections Indu Bhagat
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 30+ messages in thread
From: Indu Bhagat @ 2023-10-30 16:51 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[No changes from the RFC patch set]

- cfi_sections_set is best set to true in cfi_dot_startproc ().  Setting
  it to true again in other APIs (dot_cfi_endproc, dot_cfi_fde_data, and
  cfi_finish) is unnecessary.
- move setting the global var all_cfi_sections into cfi_set_sections ().

gas/
        * dw2gencfi.c (cfi_set_sections): Set cfi_sections_set and
	cfi_sections here.
        (dot_cfi_startproc): Remove unnecessarily setting
	cfi_set_sections to true.
        (dot_cfi_endproc): Likewise.
        (dot_cfi_fde_data): Likewise.
        (cfi_finish): Likewise.
---
 gas/dw2gencfi.c | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

diff --git a/gas/dw2gencfi.c b/gas/dw2gencfi.c
index f57479457c4..cbfee0a0169 100644
--- a/gas/dw2gencfi.c
+++ b/gas/dw2gencfi.c
@@ -492,6 +492,7 @@ cfi_set_return_column (unsigned regno)
 void
 cfi_set_sections (void)
 {
+  all_cfi_sections |= cfi_sections;
   frchain_now->frch_cfi_data->cur_fde_data->sections = all_cfi_sections;
   cfi_sections_set = true;
 }
@@ -1309,9 +1310,8 @@ dot_cfi_startproc (int ignored ATTRIBUTE_UNUSED)
     }
   demand_empty_rest_of_line ();
 
-  cfi_sections_set = true;
-  all_cfi_sections |= cfi_sections;
   cfi_set_sections ();
+
   frchain_now->frch_cfi_data->cur_cfa_offset = 0;
   if (!simple)
     tc_cfi_frame_initial_instructions ();
@@ -1336,7 +1336,6 @@ dot_cfi_endproc (int ignored ATTRIBUTE_UNUSED)
 
   demand_empty_rest_of_line ();
 
-  cfi_sections_set = true;
   if ((cfi_sections & CFI_EMIT_target) != 0)
     tc_cfi_endproc (last_fde);
 }
@@ -1418,7 +1417,6 @@ dot_cfi_fde_data (int ignored ATTRIBUTE_UNUSED)
 
   last_fde = frchain_now->frch_cfi_data->cur_fde_data;
 
-  cfi_sections_set = true;
   if ((cfi_sections & CFI_EMIT_target) != 0
       || (cfi_sections & CFI_EMIT_eh_frame_compact) != 0)
     {
@@ -2315,7 +2313,6 @@ cfi_finish (void)
   if (all_fde_data == 0)
     return;
 
-  cfi_sections_set = true;
   if ((all_cfi_sections & CFI_EMIT_eh_frame) != 0
       || (all_cfi_sections & CFI_EMIT_eh_frame_compact) != 0)
     {
@@ -2502,7 +2499,6 @@ cfi_finish (void)
       flag_traditional_format = save_flag_traditional_format;
     }
 
-  cfi_sections_set = true;
   /* Generate SFrame section if the user specifies:
 	- the command line option to gas, or
 	- .sframe in the .cfi_sections directive.  */
@@ -2524,7 +2520,6 @@ cfi_finish (void)
 	as_bad (_(".sframe not supported for target"));
     }
 
-  cfi_sections_set = true;
   if ((all_cfi_sections & CFI_EMIT_debug_frame) != 0)
     {
       int alignment = ffs (DWARF2_ADDR_SIZE (stdoutput)) - 1;
-- 
2.41.0


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

* [PATCH, V2 02/10] gas: dw2gencfi: use all_cfi_sections instead of cfi_sections
  2023-10-30 16:51 [PATCH, V2 00/10] Synthesize CFI for hand-written asm Indu Bhagat
  2023-10-30 16:51 ` [PATCH, V2 01/10] gas: dw2gencfi: minor rejig for cfi_sections_set and all_cfi_sections Indu Bhagat
@ 2023-10-30 16:51 ` Indu Bhagat
  2023-10-30 16:51 ` [PATCH, V2 03/10] gas: dw2gencfi: expose a new cfi_set_last_fde API Indu Bhagat
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 30+ messages in thread
From: Indu Bhagat @ 2023-10-30 16:51 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[No changes from RFC patch set]

The code in dw2gencfi.c was checking variable cfi_sections and
all_cfi_sections seemingly randomly.  Accessing all_cfi_sections seems
to the correct variable to access.

The data in cfi_sections has already been propagated to all_cfi_sections
once cfi_dot_startproc () has been called.

gas/
        * dw2gencfi.c (dot_cfi_startproc): Use all_cfi_sections
	instead.
        (dot_cfi_endproc): Likewise.
        (dot_cfi_fde_data): Likewise.
---
 gas/dw2gencfi.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/gas/dw2gencfi.c b/gas/dw2gencfi.c
index cbfee0a0169..5fcfb373935 100644
--- a/gas/dw2gencfi.c
+++ b/gas/dw2gencfi.c
@@ -1316,7 +1316,7 @@ dot_cfi_startproc (int ignored ATTRIBUTE_UNUSED)
   if (!simple)
     tc_cfi_frame_initial_instructions ();
 
-  if ((cfi_sections & CFI_EMIT_target) != 0)
+  if ((all_cfi_sections & CFI_EMIT_target) != 0)
     tc_cfi_startproc ();
 }
 
@@ -1336,7 +1336,7 @@ dot_cfi_endproc (int ignored ATTRIBUTE_UNUSED)
 
   demand_empty_rest_of_line ();
 
-  if ((cfi_sections & CFI_EMIT_target) != 0)
+  if ((all_cfi_sections & CFI_EMIT_target) != 0)
     tc_cfi_endproc (last_fde);
 }
 
@@ -1417,8 +1417,8 @@ dot_cfi_fde_data (int ignored ATTRIBUTE_UNUSED)
 
   last_fde = frchain_now->frch_cfi_data->cur_fde_data;
 
-  if ((cfi_sections & CFI_EMIT_target) != 0
-      || (cfi_sections & CFI_EMIT_eh_frame_compact) != 0)
+  if ((all_cfi_sections & CFI_EMIT_target) != 0
+      || (all_cfi_sections & CFI_EMIT_eh_frame_compact) != 0)
     {
       struct cfi_escape_data *head, **tail, *e;
       int num_ops = 0;
-- 
2.41.0


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

* [PATCH, V2 03/10] gas: dw2gencfi: expose a new cfi_set_last_fde API
  2023-10-30 16:51 [PATCH, V2 00/10] Synthesize CFI for hand-written asm Indu Bhagat
  2023-10-30 16:51 ` [PATCH, V2 01/10] gas: dw2gencfi: minor rejig for cfi_sections_set and all_cfi_sections Indu Bhagat
  2023-10-30 16:51 ` [PATCH, V2 02/10] gas: dw2gencfi: use all_cfi_sections instead of cfi_sections Indu Bhagat
@ 2023-10-30 16:51 ` Indu Bhagat
  2023-10-30 16:51 ` [PATCH, V2 04/10] gas: dw2gencfi: move some tc_* defines to the header file Indu Bhagat
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 30+ messages in thread
From: Indu Bhagat @ 2023-10-30 16:51 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[No changes from the RFC patch set]

gas/
	* dw2gencfi.c (cfi_set_last_fde): New definition.
	(dot_cfi_endproc): Use it.
	(dot_cfi_fde_data): Likewise.
	(dot_cfi_inline_lsda): Likewise.
	* dw2gencfi.h (struct fde_entry): New declaration.
	(cfi_set_last_fde): Likewise.
---
 gas/dw2gencfi.c | 13 ++++++++++---
 gas/dw2gencfi.h |  2 ++
 2 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/gas/dw2gencfi.c b/gas/dw2gencfi.c
index 5fcfb373935..a69854ddc0c 100644
--- a/gas/dw2gencfi.c
+++ b/gas/dw2gencfi.c
@@ -481,6 +481,13 @@ cfi_end_fde (symbolS *label)
   frchain_now->frch_cfi_data = NULL;
 }
 
+/* Set the last FDE  .*/
+void
+cfi_set_last_fde (struct fde_entry *fde)
+{
+  last_fde = fde;
+}
+
 /* Set the return column for the current FDE.  */
 
 void
@@ -1330,7 +1337,7 @@ dot_cfi_endproc (int ignored ATTRIBUTE_UNUSED)
       return;
     }
 
-  last_fde = frchain_now->frch_cfi_data->cur_fde_data;
+  cfi_set_last_fde (frchain_now->frch_cfi_data->cur_fde_data);
 
   cfi_end_fde (symbol_temp_new_now ());
 
@@ -1415,7 +1422,7 @@ dot_cfi_fde_data (int ignored ATTRIBUTE_UNUSED)
       return;
     }
 
-  last_fde = frchain_now->frch_cfi_data->cur_fde_data;
+  cfi_set_last_fde (frchain_now->frch_cfi_data->cur_fde_data);
 
   if ((all_cfi_sections & CFI_EMIT_target) != 0
       || (all_cfi_sections & CFI_EMIT_eh_frame_compact) != 0)
@@ -1568,7 +1575,7 @@ dot_cfi_inline_lsda (int ignored ATTRIBUTE_UNUSED)
   if (last_fde->eh_header_type == EH_COMPACT_HAS_LSDA)
     output_compact_unwind_data (last_fde, align);
 
-  last_fde = NULL;
+  cfi_set_last_fde (NULL);
 
   return;
 }
diff --git a/gas/dw2gencfi.h b/gas/dw2gencfi.h
index 962fdcaed17..aa8693df342 100644
--- a/gas/dw2gencfi.h
+++ b/gas/dw2gencfi.h
@@ -25,6 +25,7 @@
 #include "dwarf2.h"
 
 struct symbol;
+struct fde_entry;
 
 extern const pseudo_typeS cfi_pseudo_table[];
 
@@ -35,6 +36,7 @@ extern void cfi_finish (void);
 /* Entry points for backends to add unwind information.  */
 extern void cfi_new_fde (struct symbol *);
 extern void cfi_end_fde (struct symbol *);
+extern void cfi_set_last_fde (struct fde_entry *fde);
 extern void cfi_set_return_column (unsigned);
 extern void cfi_set_sections (void);
 extern void cfi_add_advance_loc (struct symbol *);
-- 
2.41.0


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

* [PATCH, V2 04/10] gas: dw2gencfi: move some tc_* defines to the header file
  2023-10-30 16:51 [PATCH, V2 00/10] Synthesize CFI for hand-written asm Indu Bhagat
                   ` (2 preceding siblings ...)
  2023-10-30 16:51 ` [PATCH, V2 03/10] gas: dw2gencfi: expose a new cfi_set_last_fde API Indu Bhagat
@ 2023-10-30 16:51 ` Indu Bhagat
  2023-10-30 16:51 ` [PATCH, V2 05/10] gas: add new command line option --scfi[=all,none] Indu Bhagat
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 30+ messages in thread
From: Indu Bhagat @ 2023-10-30 16:51 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[No changes from the RFC patch set]

Move the following three defines to the header file, so the SCFI
machinery can use them:
 - tc_cfi_frame_initial_instructions
 - tc_cfi_startproc
 - tc_cfi_endproc

Although, the symthesized CFI cannot be emitted when CFI_EMIT_target is
set, but in future, we may want to allow synthesized CFI to be emitted
to all outputs currently supported by GAS for DWARF CFI.

gas/
        * dw2gencfi.c: Move from ...
	* dw2gencfi.h: ... to here.
---
 gas/dw2gencfi.c | 12 ------------
 gas/dw2gencfi.h | 12 ++++++++++++
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/gas/dw2gencfi.c b/gas/dw2gencfi.c
index a69854ddc0c..cdef8d09978 100644
--- a/gas/dw2gencfi.c
+++ b/gas/dw2gencfi.c
@@ -64,18 +64,6 @@
 #define EH_FRAME_ALIGNMENT (bfd_get_arch_size (stdoutput) == 64 ? 3 : 2)
 #endif
 
-#ifndef tc_cfi_frame_initial_instructions
-#define tc_cfi_frame_initial_instructions() ((void)0)
-#endif
-
-#ifndef tc_cfi_startproc
-# define tc_cfi_startproc() ((void)0)
-#endif
-
-#ifndef tc_cfi_endproc
-# define tc_cfi_endproc(fde) ((void) (fde))
-#endif
-
 #define EH_FRAME_LINKONCE (SUPPORT_FRAME_LINKONCE || compact_eh \
 			   || TARGET_MULTIPLE_EH_FRAME_SECTIONS)
 
diff --git a/gas/dw2gencfi.h b/gas/dw2gencfi.h
index aa8693df342..f574826456a 100644
--- a/gas/dw2gencfi.h
+++ b/gas/dw2gencfi.h
@@ -29,6 +29,18 @@ struct fde_entry;
 
 extern const pseudo_typeS cfi_pseudo_table[];
 
+#ifndef tc_cfi_frame_initial_instructions
+#define tc_cfi_frame_initial_instructions() ((void)0)
+#endif
+
+#ifndef tc_cfi_startproc
+# define tc_cfi_startproc() ((void)0)
+#endif
+
+#ifndef tc_cfi_endproc
+# define tc_cfi_endproc(fde) ((void) (fde))
+#endif
+
 /* cfi_finish() is called at the end of file. It will complain if
    the last CFI wasn't properly closed by .cfi_endproc.  */
 extern void cfi_finish (void);
-- 
2.41.0


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

* [PATCH, V2 05/10] gas: add new command line option --scfi[=all,none]
  2023-10-30 16:51 [PATCH, V2 00/10] Synthesize CFI for hand-written asm Indu Bhagat
                   ` (3 preceding siblings ...)
  2023-10-30 16:51 ` [PATCH, V2 04/10] gas: dw2gencfi: move some tc_* defines to the header file Indu Bhagat
@ 2023-10-30 16:51 ` Indu Bhagat
  2023-10-30 16:51 ` [PATCH, V2 06/10] gas: scfidw2gen: new functionality to prepapre for SCFI Indu Bhagat
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 30+ messages in thread
From: Indu Bhagat @ 2023-10-30 16:51 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[Changes from the RFC patch set]
  - Update documentation to make it clearer.
  - Guard the --scfi option processing behind both TARGET_USE_GINSN and
    TARGET_USE_GINSN.
[End of changes]

When the command line option --scfi (default is --scfi=all) is passed to
the GNU assembler, it will synthesize DWARF call frame information (CFI)
for the input assembly.

The option --scfi=all will also ignore many of the existing .cfi_*
directives, if already contained in the provided input file.  Only the
following CFI directives will not be ignored:
  - .cfi_sections,
  - .cfi_label,
  - .cfi_signal_frame

To use SCFI, a target will need to:
    - define TARGET_USE_SCFI and TARGET_USE_GINSN, and other necessary
    definitions,
    - provide means to help GAS understand the target specific instruction
    semantics by creating ginsns.

The --scfi=[all,none] may see more options added in future.  For
example, --scfi=inline, for dealing with inline asm may be added in the
future.  In this option, the GNU assembler will consume (and not ignore)
the compiler generated CFI for the code surrounding the inline asm.

Also document the option.

gas/
        * as.c (show_usage): Add support for --scfi.
	(parse_args): Likewise.
        * as.h (enum synth_cfi_type): Define new type.
        * doc/as.texi: Document the new option.
---
 gas/as.c        | 22 +++++++++++++++++++++-
 gas/as.h        |  8 ++++++++
 gas/doc/as.texi | 12 ++++++++++++
 3 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/gas/as.c b/gas/as.c
index 6839c841588..97b0ed38fb6 100644
--- a/gas/as.c
+++ b/gas/as.c
@@ -372,6 +372,11 @@ Options:\n\
   -R                      fold data section into text section\n"));
   fprintf (stream, _("\
   --reduce-memory-overheads ignored\n"));
+#if defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN)
+  fprintf (stream, _("\
+  --scfi=[all,none]	  (default: all)\n\
+			  Synthesize DWARF CFI for hand-written asm\n"));
+# endif
   fprintf (stream, _("\
   --statistics            print various measured statistics from execution\n"));
   fprintf (stream, _("\
@@ -511,7 +516,8 @@ parse_args (int * pargc, char *** pargv)
       OPTION_NOCOMPRESS_DEBUG,
       OPTION_NO_PAD_SECTIONS,
       OPTION_MULTIBYTE_HANDLING,  /* = STD_BASE + 40 */
-      OPTION_SFRAME
+      OPTION_SFRAME,
+      OPTION_SCFI
     /* When you add options here, check that they do
        not collide with OPTION_MD_BASE.  See as.h.  */
     };
@@ -586,6 +592,9 @@ parse_args (int * pargc, char *** pargv)
     ,{"no-pad-sections", no_argument, NULL, OPTION_NO_PAD_SECTIONS}
     ,{"no-warn", no_argument, NULL, 'W'}
     ,{"reduce-memory-overheads", no_argument, NULL, OPTION_REDUCE_MEMORY_OVERHEADS}
+#if defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN)
+    ,{"scfi", no_argument, NULL, OPTION_SCFI}
+#endif
     ,{"statistics", no_argument, NULL, OPTION_STATISTICS}
     ,{"strip-local-absolute", no_argument, NULL, OPTION_STRIP_LOCAL_ABSOLUTE}
     ,{"version", no_argument, NULL, OPTION_VERSION}
@@ -982,6 +991,17 @@ This program has absolutely no warranty.\n"));
 	  flag_execstack = 0;
 	  break;
 
+#if defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN)
+	case OPTION_SCFI:
+	  if (!optarg || strcasecmp (optarg, "all") == 0)
+	    flag_synth_cfi = SYNTH_CFI_ALL;
+	  else if (strcasecmp (optarg, "none") == 0)
+	    flag_synth_cfi = SYNTH_CFI_NONE;
+	  else
+	    as_fatal (_("Invalid --scfi= option: `%s'"), optarg);
+	  break;
+#endif
+
 	case OPTION_SIZE_CHECK:
 	  if (strcasecmp (optarg, "error") == 0)
 	    flag_allow_nonconst_size = false;
diff --git a/gas/as.h b/gas/as.h
index 46dd0d0776d..378653e2200 100644
--- a/gas/as.h
+++ b/gas/as.h
@@ -324,6 +324,14 @@ COMMON int flag_fatal_warnings; /* --fatal-warnings */
    are detected.  */
 COMMON unsigned char flag_always_generate_output; /* -Z */
 
+enum synth_cfi_type
+{
+  SYNTH_CFI_NONE = 0,
+  SYNTH_CFI_ALL,
+};
+
+COMMON enum synth_cfi_type flag_synth_cfi;
+
 /* This is true if the assembler should output time and space usage.  */
 COMMON unsigned char flag_print_statistics;
 
diff --git a/gas/doc/as.texi b/gas/doc/as.texi
index 52571d95dd2..cfc1078c36b 100644
--- a/gas/doc/as.texi
+++ b/gas/doc/as.texi
@@ -255,6 +255,7 @@ gcc(1), ld(1), and the Info entries for @file{binutils} and @file{ld}.
  [@b{--multibyte-handling=[allow|warn|warn-sym-only]}]
  [@b{--no-pad-sections}]
  [@b{-o} @var{objfile}] [@b{-R}]
+ [@b{--scfi=[all,none]}]
  [@b{--sectname-subst}]
  [@b{--size-check=[error|warning]}]
  [@b{--statistics}]
@@ -932,6 +933,17 @@ Ignored.  Supported for compatibility with tools that apss the same option to
 both the assembler and the linker.
 
 @ifset ELF
+@item --scfi=none
+@itemx --scfi=all
+These options control whether the assembler should synthesize CFI for
+hand-written input.  If the input already contains some synthesizable CFI
+directives, the assembler ignores them and emits a warning.  Note that
+@code{--scfi=all} is not intended to be used for compiler-generated code,
+including inline assembly.
+
+The input asm must begin with the @code{.type} directive, and should ideally be
+closed off using a @code{.size} directive.
+
 @item --sectname-subst
 Honor substitution sequences in section names.
 @ifclear man
-- 
2.41.0


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

* [PATCH, V2 06/10] gas: scfidw2gen: new functionality to prepapre for SCFI
  2023-10-30 16:51 [PATCH, V2 00/10] Synthesize CFI for hand-written asm Indu Bhagat
                   ` (4 preceding siblings ...)
  2023-10-30 16:51 ` [PATCH, V2 05/10] gas: add new command line option --scfi[=all,none] Indu Bhagat
@ 2023-10-30 16:51 ` Indu Bhagat
  2023-10-31 11:28   ` Jan Beulich
  2023-10-30 16:51 ` [PATCH, V2 07/10] gas: synthesize CFI for hand-written asm Indu Bhagat
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 30+ messages in thread
From: Indu Bhagat @ 2023-10-30 16:51 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[Changes from the RFC patch set]
  - scfidw2gen.c now defines its own set of handlers.  For many of those
    CFI directives, we simply warn ("Warning: --scfi=all ignores some
    user-specified CFI directives") and return.
  - The above-mentioned warning is issued only once per file.
  - The advantage of adding new set of handlers in scfidwgen.c is that
    dw2gencfi now does not need to add a condition for each handler to
    simply return if --scfi is in effect (the latter was the approach in
    the RFC patch set.)
[End of changes]

Define a new set of handlers for CFI directives for the purpose of SCFI.
The SCFI machinery ignores many of the user-specified CFI direcives when
--scfi=all is in effect.  The following CFI directives, however, will not
ignored:
      - .cfi_sections
      - .cfi_label
      - .cfi_signal_frame

The handling of .cfi_label, and .cfi_signal_frame will be added in a
follow up patch, as it needs the ginsn implementation.

Since the option --scfi=inline still needs to be worked out, the
implementation in scfidw2gen may need to be adjusted later.

gas/
	* Makefile.am: Add new files to GAS_CFILES and HFILES.
	* Makefile.in: Likewise.
	* gas/read.c (scfi_pop_insert): New define.
	(pobegin): Use the SCFI handlers.
	* scfidw2gen.c: New file.
	* scfidw2gen.h: New file.
---
 gas/Makefile.am  |   2 +
 gas/Makefile.in  |   9 +-
 gas/read.c       |  17 ++-
 gas/scfidw2gen.c | 305 +++++++++++++++++++++++++++++++++++++++++++++++
 gas/scfidw2gen.h |  37 ++++++
 5 files changed, 365 insertions(+), 5 deletions(-)
 create mode 100644 gas/scfidw2gen.c
 create mode 100644 gas/scfidw2gen.h

diff --git a/gas/Makefile.am b/gas/Makefile.am
index 0e98ca3ec85..e174305ca62 100644
--- a/gas/Makefile.am
+++ b/gas/Makefile.am
@@ -93,6 +93,7 @@ GAS_CFILES = \
 	read.c \
 	remap.c \
 	sb.c \
+	scfidw2gen.c \
 	sframe-opt.c \
 	stabs.c \
 	subsegs.c \
@@ -128,6 +129,7 @@ HFILES = \
 	output-file.h \
 	read.h \
 	sb.h \
+	scfidw2gen.h \
 	subsegs.h \
 	symbols.h \
 	tc.h \
diff --git a/gas/Makefile.in b/gas/Makefile.in
index fae3a47c144..87428bc46b8 100644
--- a/gas/Makefile.in
+++ b/gas/Makefile.in
@@ -170,9 +170,9 @@ am__objects_1 = app.$(OBJEXT) as.$(OBJEXT) atof-generic.$(OBJEXT) \
 	hash.$(OBJEXT) input-file.$(OBJEXT) input-scrub.$(OBJEXT) \
 	listing.$(OBJEXT) literal.$(OBJEXT) macro.$(OBJEXT) \
 	messages.$(OBJEXT) output-file.$(OBJEXT) read.$(OBJEXT) \
-	remap.$(OBJEXT) sb.$(OBJEXT) sframe-opt.$(OBJEXT) \
-	stabs.$(OBJEXT) subsegs.$(OBJEXT) symbols.$(OBJEXT) \
-	write.$(OBJEXT)
+	remap.$(OBJEXT) sb.$(OBJEXT) scfidw2gen.$(OBJEXT) \
+	sframe-opt.$(OBJEXT) stabs.$(OBJEXT) subsegs.$(OBJEXT) \
+	symbols.$(OBJEXT) write.$(OBJEXT)
 am_as_new_OBJECTS = $(am__objects_1)
 am__dirstamp = $(am__leading_dot)dirstamp
 as_new_OBJECTS = $(am_as_new_OBJECTS)
@@ -581,6 +581,7 @@ GAS_CFILES = \
 	read.c \
 	remap.c \
 	sb.c \
+	scfidw2gen.c \
 	sframe-opt.c \
 	stabs.c \
 	subsegs.c \
@@ -615,6 +616,7 @@ HFILES = \
 	output-file.h \
 	read.h \
 	sb.h \
+	scfidw2gen.h \
 	subsegs.h \
 	symbols.h \
 	tc.h \
@@ -1337,6 +1339,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/read.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/remap.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sb.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scfidw2gen.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sframe-opt.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stabs.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/subsegs.Po@am__quote@
diff --git a/gas/read.c b/gas/read.c
index 826156db3fe..9068072493a 100644
--- a/gas/read.c
+++ b/gas/read.c
@@ -38,6 +38,7 @@
 #include "obstack.h"
 #include "ecoff.h"
 #include "dw2gencfi.h"
+#include "scfidw2gen.h"
 #include "codeview.h"
 #include "wchar.h"
 #include "filenames.h"
@@ -587,6 +588,10 @@ pop_insert (const pseudo_typeS *table)
 #define cfi_pop_insert()	pop_insert(cfi_pseudo_table)
 #endif
 
+#ifndef scfi_pop_insert
+#define scfi_pop_insert()	pop_insert(scfi_pseudo_table)
+#endif
+
 static void
 pobegin (void)
 {
@@ -607,8 +612,16 @@ pobegin (void)
   pop_insert (potable);
 
   /* Now CFI ones.  */
-  pop_table_name = "cfi";
-  cfi_pop_insert ();
+  if (flag_synth_cfi)
+    {
+      pop_table_name = "scfi";
+      scfi_pop_insert ();
+    }
+  else
+    {
+      pop_table_name = "cfi";
+      cfi_pop_insert ();
+    }
 }
 
 static void
diff --git a/gas/scfidw2gen.c b/gas/scfidw2gen.c
new file mode 100644
index 00000000000..188699a882f
--- /dev/null
+++ b/gas/scfidw2gen.c
@@ -0,0 +1,305 @@
+/* scfidw2gen.c - Support for emission of synthesized Dwarf2 CFI.
+   Copyright (C) 2003-2023 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 "dw2gencfi.h"
+#include "subsegs.h"
+#include "scfidw2gen.h"
+
+#if defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN)
+
+static int scfi_ignore_warn_once = 0;
+
+static void dot_scfi_sections (int);
+static void dot_scfi_ignore (int);
+static void dot_scfi (int);
+
+const pseudo_typeS scfi_pseudo_table[] =
+  {
+    { "cfi_sections", dot_scfi_sections, 0 }, /* No ignore.  */
+    { "cfi_startproc", dot_scfi_ignore, 0 },
+    { "cfi_endproc", dot_scfi_ignore, 0 },
+    { "cfi_fde_data", dot_scfi_ignore, 0 },
+    { "cfi_def_cfa", dot_scfi_ignore, 0 },
+    { "cfi_def_cfa_register", dot_scfi_ignore, 0 },
+    { "cfi_def_cfa_offset", dot_scfi_ignore, 0 },
+    { "cfi_adjust_cfa_offset", dot_scfi_ignore, 0 },
+    { "cfi_offset", dot_scfi_ignore, 0 },
+    { "cfi_rel_offset", dot_scfi_ignore, 0 },
+    { "cfi_register", dot_scfi_ignore, 0 },
+    { "cfi_return_column", dot_scfi_ignore, 0 },
+    { "cfi_restore", dot_scfi_ignore, 0 },
+    { "cfi_undefined", dot_scfi_ignore, 0 },
+    { "cfi_same_value", dot_scfi_ignore, 0 },
+    { "cfi_remember_state", dot_scfi_ignore, 0 },
+    { "cfi_restore_state", dot_scfi_ignore, 0 },
+    { "cfi_window_save", dot_scfi_ignore, 0 },
+    { "cfi_negate_ra_state", dot_scfi_ignore, 0 },
+    { "cfi_escape", dot_scfi_ignore, 0 },
+    { "cfi_signal_frame", dot_scfi, CFI_signal_frame }, /* No ignore.  */
+    { "cfi_personality", dot_scfi_ignore, 0 },
+    { "cfi_personality_id", dot_scfi_ignore, 0 },
+    { "cfi_lsda", dot_scfi_ignore, 0 },
+    { "cfi_val_encoded_addr", dot_scfi_ignore, 0 },
+    { "cfi_inline_lsda", dot_scfi_ignore, 0 },
+    { "cfi_label", dot_scfi, CFI_label }, /* No ignore.  */
+    { "cfi_val_offset", dot_scfi_ignore, 0 },
+    { NULL, NULL, 0 }
+  };
+
+static void
+dot_scfi_ignore (int ignored ATTRIBUTE_UNUSED)
+{
+  gas_assert (flag_synth_cfi);
+
+  if (scfi_ignore_warn_once == 0)
+    {
+      as_warn (_("--scfi=all ignores some user-specified CFI directives"));
+      scfi_ignore_warn_once = 1;
+    }
+  ignore_rest_of_line ();
+}
+
+static void
+dot_scfi_sections (int ignored ATTRIBUTE_UNUSED)
+{
+  /* To be implemented.  */
+  return;
+}
+
+static void
+scfi_process_cfi_label (void)
+{
+  /* To be implemented. */
+  return;
+}
+
+static void
+scfi_process_cfi_signal_frame (void)
+{
+  /* To be implemented.  */
+  return;
+}
+
+static void
+dot_scfi (int arg)
+{
+  switch (arg)
+    {
+      case CFI_label:
+	scfi_process_cfi_label ();
+	break;
+      case CFI_signal_frame:
+	scfi_process_cfi_signal_frame ();
+	break;
+      default:
+	abort ();
+    }
+}
+
+void
+scfi_dot_cfi_startproc (symbolS *start_sym)
+{
+  if (frchain_now->frch_cfi_data != NULL)
+    {
+      as_bad (_("previous CFI entry not closed (missing .cfi_endproc)"));
+      return;
+    }
+
+  cfi_new_fde (start_sym);
+
+  cfi_set_sections ();
+
+  frchain_now->frch_cfi_data->cur_cfa_offset = 0;
+
+  /* By default, SCFI machinery assumes .cfi_startproc is used without
+     parameter simple.  */
+  tc_cfi_frame_initial_instructions ();
+
+  if ((all_cfi_sections & CFI_EMIT_target) != 0)
+    tc_cfi_startproc ();
+}
+
+void
+scfi_dot_cfi_endproc (symbolS *end_sym)
+{
+  struct fde_entry *fde_last;
+
+  if (frchain_now->frch_cfi_data == NULL)
+    {
+      as_bad (_(".cfi_endproc without corresponding .cfi_startproc"));
+      return;
+    }
+
+  fde_last = frchain_now->frch_cfi_data->cur_fde_data;
+  cfi_set_last_fde (fde_last);
+
+  cfi_end_fde (end_sym);
+
+  if ((all_cfi_sections & CFI_EMIT_target) != 0)
+    tc_cfi_endproc (fde_last);
+}
+
+void
+scfi_dot_cfi (int arg, unsigned reg1, unsigned reg2, offsetT offset,
+	      const char *name, symbolS *advloc)
+{
+  if (frchain_now->frch_cfi_data == NULL)
+    {
+      as_bad (_("CFI instruction used without previous .cfi_startproc"));
+      return;
+    }
+
+  /* If the last address was not at the current PC, advance to current.  */
+  if (frchain_now->frch_cfi_data->last_address != advloc)
+    cfi_add_advance_loc (advloc);
+
+  switch (arg)
+    {
+    case DW_CFA_offset:
+      cfi_add_CFA_offset (reg1, offset);
+      break;
+
+    case DW_CFA_val_offset:
+      cfi_add_CFA_val_offset (reg1, offset);
+      break;
+
+    case CFI_rel_offset:
+      cfi_add_CFA_offset (reg1,
+			  offset - frchain_now->frch_cfi_data->cur_cfa_offset);
+      break;
+
+    case DW_CFA_def_cfa:
+      cfi_add_CFA_def_cfa (reg1, offset);
+      break;
+
+    case DW_CFA_register:
+      cfi_add_CFA_register (reg1, reg2);
+      break;
+
+    case DW_CFA_def_cfa_register:
+      cfi_add_CFA_def_cfa_register (reg1);
+      break;
+
+    case DW_CFA_def_cfa_offset:
+      cfi_add_CFA_def_cfa_offset (offset);
+      break;
+
+    case CFI_adjust_cfa_offset:
+      cfi_add_CFA_def_cfa_offset (frchain_now->frch_cfi_data->cur_cfa_offset
+				  + offset);
+      break;
+
+    case DW_CFA_restore:
+      cfi_add_CFA_restore (reg1);
+      break;
+
+    case DW_CFA_remember_state:
+      cfi_add_CFA_remember_state ();
+      break;
+
+    case DW_CFA_restore_state:
+      cfi_add_CFA_restore_state ();
+      break;
+
+    case CFI_label:
+      cfi_add_label (name);
+      break;
+
+    case CFI_signal_frame:
+      frchain_now->frch_cfi_data->cur_fde_data->signal_frame = 1;
+      break;
+
+/*
+    case DW_CFA_undefined:
+      for (;;)
+	{
+	  reg1 = cfi_parse_reg ();
+	  cfi_add_CFA_undefined (reg1);
+	  SKIP_WHITESPACE ();
+	  if (*input_line_pointer != ',')
+	    break;
+	  ++input_line_pointer;
+	}
+      break;
+
+    case DW_CFA_same_value:
+      reg1 = cfi_parse_reg ();
+      cfi_add_CFA_same_value (reg1);
+      break;
+
+    case CFI_return_column:
+      reg1 = cfi_parse_reg ();
+      cfi_set_return_column (reg1);
+      break;
+
+    case DW_CFA_GNU_window_save:
+      cfi_add_CFA_insn (DW_CFA_GNU_window_save);
+      break;
+
+*/
+    default:
+      abort ();
+    }
+}
+
+#else
+
+static void
+dot_scfi_dummy (int ignored ATTRIBUTE_UNUSED)
+{
+  as_bad (_("SCFI is not supported for this target"));
+  ignore_rest_of_line ();
+}
+
+const pseudo_typeS scfi_pseudo_table[] =
+  {
+    { "cfi_sections", dot_scfi_dummy, 0 },
+    { "cfi_startproc", dot_scfi_dummy, 0 },
+    { "cfi_endproc", dot_scfi_dummy, 0 },
+    { "cfi_fde_data", dot_scfi_dummy, 0 },
+    { "cfi_def_cfa", dot_scfi_dummy, 0 },
+    { "cfi_def_cfa_register", dot_scfi_dummy, 0 },
+    { "cfi_def_cfa_offset", dot_scfi_dummy, 0 },
+    { "cfi_adjust_cfa_offset", dot_scfi_dummy, 0 },
+    { "cfi_offset", dot_scfi_dummy, 0 },
+    { "cfi_rel_offset", dot_scfi_dummy, 0 },
+    { "cfi_register", dot_scfi_dummy, 0 },
+    { "cfi_return_column", dot_scfi_dummy, 0 },
+    { "cfi_restore", dot_scfi_dummy, 0 },
+    { "cfi_undefined", dot_scfi_dummy, 0 },
+    { "cfi_same_value", dot_scfi_dummy, 0 },
+    { "cfi_remember_state", dot_scfi_dummy, 0 },
+    { "cfi_restore_state", dot_scfi_dummy, 0 },
+    { "cfi_window_save", dot_scfi_dummy, 0 },
+    { "cfi_negate_ra_state", dot_scfi_dummy, 0 },
+    { "cfi_escape", dot_scfi_dummy, 0 },
+    { "cfi_signal_frame", dot_scfi_dummy, 0 },
+    { "cfi_personality", dot_scfi_dummy, 0 },
+    { "cfi_personality_id", dot_scfi_dummy, 0 },
+    { "cfi_lsda", dot_scfi_dummy, 0 },
+    { "cfi_val_encoded_addr", dot_scfi_dummy, 0 },
+    { "cfi_inline_lsda", dot_scfi_dummy, 0 },
+    { "cfi_label", dot_scfi_dummy, 0 },
+    { "cfi_val_offset", dot_scfi_dummy, 0 },
+    { NULL, NULL, 0 }
+  };
+
+#endif
diff --git a/gas/scfidw2gen.h b/gas/scfidw2gen.h
new file mode 100644
index 00000000000..c3d6f80b3db
--- /dev/null
+++ b/gas/scfidw2gen.h
@@ -0,0 +1,37 @@
+/* scfidw2gen.h - Support for emitting synthesized Dwarf2 CFI.
+   Copyright (C) 2003-2023 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 SCFIDW2GEN_H
+#define SCFIDW2GEN_H
+
+#include "as.h"
+#include "dwarf2.h"
+
+extern int all_cfi_sections;
+
+extern const pseudo_typeS scfi_pseudo_table[];
+
+void scfi_dot_cfi_startproc (symbolS *start_sym);
+void scfi_dot_cfi_endproc (symbolS *end_sym);
+void scfi_dot_cfi (int arg, unsigned reg1, unsigned reg2, offsetT offset,
+		   const char *name, symbolS *advloc);
+
+#endif
+
-- 
2.41.0


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

* [PATCH, V2 07/10] gas: synthesize CFI for hand-written asm
  2023-10-30 16:51 [PATCH, V2 00/10] Synthesize CFI for hand-written asm Indu Bhagat
                   ` (5 preceding siblings ...)
  2023-10-30 16:51 ` [PATCH, V2 06/10] gas: scfidw2gen: new functionality to prepapre for SCFI Indu Bhagat
@ 2023-10-30 16:51 ` Indu Bhagat
  2023-10-31 14:10   ` Jan Beulich
  2023-11-02 15:53   ` Jan Beulich
  2023-10-30 16:51 ` [PATCH, V2 08/10] gas: doc: update documentation for the new listing option Indu Bhagat
                   ` (2 subsequent siblings)
  9 siblings, 2 replies; 30+ messages in thread
From: Indu Bhagat @ 2023-10-30 16:51 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[Changes from the RFC patch set]
  - Fixed few more unhandled cases in the x86 creation of ginsn.
  - Added skeleton functionality to print ginsn in the listing output of
    GAS.  The sub-option chosen is "i":
    $ as --help
    -a[sub-option...]    turn on listings
                         Sub-options [default hls]:
                         ...
                         d      omit debugging directives
                         ...
                         i      include ginsn and synthesized CFI info
    Documentation for ginsn is still pending.
  - Other bugfixes in the SCFI machinery.
[End of changes]

This patch adds support in GAS to create generic GAS instructions
(a.k.a., the ginsn) for the x86 backend (AMD64 ABI only at this time).
Using this ginsn infrastructure, GAS can then synthesize CFI for
hand-written asm for x86_64.

A ginsn is a target-independent representation of the machine
instructions.  One machine instruction may need one or more ginsn.

This patch also adds skeleton support for printing ginsn in the listing
output for debugging purposes.

Since the current use-case of ginsn is to synthesize CFI, the x86 target
generates ginsns necessary for the following machine instructions only:

 - All change of flow instructions, including all conditional and
   unconditional branches, call and return from functions.
 - All register saves and unsaves to the stack.
 - All instructions affecting the two registers that could potentially
   be used as the base register for CFA tracking.  For SCFI, the base
   register for CFA tracking is limited to REG_SP and REG_FP only for
   now.

The representation of ginsn is kept simple:

- GAS instruction has GINSN_NUM_SRC_OPNDS (defined to be 2 at this time)
  number of source operands and one destination operand at this time.
- GAS instruction uses DWARF register numbers in its representation and
  does not track register size.
- GAS instructions carry location information (file name and line
  number).
- GAS instructions are ID's with a natural number in order of their
  addtion to the list.  This can be used as a proxy for the static
  program order of the corresponding machine instructions.

Note that, GAS instruction (ginsn) format does not support
GINSN_TYPE_PUSH and GINSN_TYPE_POP.  Some architectures, like aarch64,
do not have push and pop instructions, but rather STP/LDP/STR/LDR etc.
instructions.  Further these instructions have a variety of addressing
modes, like offset, pre-indexing and post-indexing etc.  Among other
things, one of differences in these addressing modes is _when_ the addr
register is updated with the result of the address calculation: before
or after the memory operation.  To best support such needs, the generic
instructions like GINSN_TYPE_LOAD, GINSN_TYPE_STORE together with
GINSN_TYPE_ADD, and GINSN_TYPE_SUB may be used.  For load and store to
stack, the addressing mode with src / dst type of
GINSN_SRC_TYPE_INDIRECT / GINSN_DST_TYPE_INDIRECT respectively and the
register of REG_SP is expected.

The functionality provided in ginsn.c and scfi.c is compiled in when a
target defines TARGET_USE_SCFI and TARGET_USE_GINSN.  This can be
revisited later when there are other use-cases of creating ginsn's in
GAS, apart from the current use-case of synthesizing CFI for
hand-written asm.

Support is added only for AMD64 ABI at this time.  If the user
specifies, --scfi --32, GAS issues an error:

  "Fatal error: Synthesizing CFI is not supported for this ABI"

For synthesizing (DWARF) CFI, the SCFI machinery requires the programmer
to adhere to some pre-requisites for their asm:
   - Hand-written asm block must begin with a .type   foo, @function
It is highly recommended to, additionally, also ensure that:
   - Hand-written asm block ends with a .size foo, .-foo

The SCFI machinery encodes some rules which align with the standard
calling convention specified by the ABI.  Apart from the rules, the SCFI
machinery employs some heuristics.  For example:
   - The base register for CFA tracking may be either REG_SP or REG_FP.
   - If the base register for CFA tracking is REG_SP, the precise amount of
     stack usage (and hence, the value of REG_SP) must be known at all times.
   - If using dynamic stack allocation, the function must switch to
     FP-based CFA.  This means using instructions like the following (in
     AMD64) in prologue:
        pushq   %rbp
        movq    %rsp, %rbp
     and analogous instructions in epilogue.
   - Save and Restore of callee-saved registers must be symmetrical.
     However, the SCFI machinery at this time only warns if any such asymmetry
     is seen.

These heuristics / rules are architecture-independent and are meant to
employed for all architectures/ABIs using SCFI in the future.

gas/
	* Makefile.am: Add new files.
	* Makefile.in: Regenerated.
	* as.c (defined): Handle documentation and listing option for
	ginsns and SCFI.
	* config/obj-elf.c (obj_elf_size): Invoke ginsn_data_end.
	(obj_elf_type): Invoke ginsn_data_begin.
	* config/tc-i386.c (ginsn_new): New functionality to generate
	ginsns.
	(x86_scfi_callee_saved_p): New function.
	(ginsn_dw2_regnum): Likewise.
	(ginsn_set_where): Likewise.
	(x86_ginsn_alu): Likewise.
	(x86_ginsn_move): Likewise.
	(x86_ginsn_lea): Likewise.
	(x86_ginsn_jump): Likewise.
	(x86_ginsn_jump_cond): Likewise.
	(md_assemble): Invoke ginsn_new.
	(s_insn): Likewise.
	(i386_target_format): Add hard error for usage of --scfi with non AMD64 ABIs.
	* config/tc-i386.h (TARGET_USE_GINSN): New definition.
	(TARGET_USE_SCFI): Likewise.
	(SCFI_NUM_REGS): Likewise.
	(REG_FP): Likewise.
	(REG_SP): Likewise.
	(SCFI_INIT_CFA_OFFSET): Likewise.
	(SCFI_CALLEE_SAVED_REG_P): Likewise.
	(x86_scfi_callee_saved_p): Likewise.
	* gas/listing.h (LISTING_GINSN_SCFI): New define for ginsn and
	SCFI.
	* gas/read.c (read_a_source_file): Close SCFI processing at end
	of file read.
	* gas/scfidw2gen.c (scfi_process_cfi_label): Add implementation.
	(scfi_process_cfi_signal_frame): Likewise.
	* subsegs.h (struct frch_ginsn_data): New forward declaration.
	(struct frchain): New member for ginsn data.
	* gas/subsegs.c (subseg_set_rest): Initialize the new member.
	* symbols.c (colon): Invoke ginsn_frob_label to convey
	user-defined labels to ginsn infrastructure.
	* ginsn.c: New file.
	* ginsn.h: New file.
	* scfi.c: New file.
	* scfi.h: New file.
---
 gas/Makefile.am      |    4 +
 gas/Makefile.in      |   19 +-
 gas/as.c             |    5 +
 gas/config/obj-elf.c |   16 +
 gas/config/tc-i386.c |  735 ++++++++++++++++++++++++-
 gas/config/tc-i386.h |   21 +
 gas/ginsn.c          | 1225 ++++++++++++++++++++++++++++++++++++++++++
 gas/ginsn.h          |  392 ++++++++++++++
 gas/listing.h        |    1 +
 gas/read.c           |    4 +
 gas/scfi.c           | 1222 +++++++++++++++++++++++++++++++++++++++++
 gas/scfi.h           |   38 ++
 gas/scfidw2gen.c     |   28 +-
 gas/subsegs.c        |    1 +
 gas/subsegs.h        |    2 +
 gas/symbols.c        |    3 +
 16 files changed, 3705 insertions(+), 11 deletions(-)
 create mode 100644 gas/ginsn.c
 create mode 100644 gas/ginsn.h
 create mode 100644 gas/scfi.c
 create mode 100644 gas/scfi.h

diff --git a/gas/Makefile.am b/gas/Makefile.am
index e174305ca62..b477d74cb53 100644
--- a/gas/Makefile.am
+++ b/gas/Makefile.am
@@ -82,6 +82,7 @@ GAS_CFILES = \
 	flonum-mult.c \
 	frags.c \
 	gen-sframe.c \
+	ginsn.c \
 	hash.c \
 	input-file.c \
 	input-scrub.c \
@@ -94,6 +95,7 @@ GAS_CFILES = \
 	remap.c \
 	sb.c \
 	scfidw2gen.c \
+	scfi.c \
 	sframe-opt.c \
 	stabs.c \
 	subsegs.c \
@@ -119,6 +121,7 @@ HFILES = \
 	flonum.h \
 	frags.h \
 	gen-sframe.h \
+	ginsn.h \
 	hash.h \
 	input-file.h \
 	itbl-lex.h \
@@ -130,6 +133,7 @@ HFILES = \
 	read.h \
 	sb.h \
 	scfidw2gen.h \
+	scfi.h \
 	subsegs.h \
 	symbols.h \
 	tc.h \
diff --git a/gas/Makefile.in b/gas/Makefile.in
index 87428bc46b8..99edb365a00 100644
--- a/gas/Makefile.in
+++ b/gas/Makefile.in
@@ -167,12 +167,13 @@ am__objects_1 = app.$(OBJEXT) as.$(OBJEXT) atof-generic.$(OBJEXT) \
 	ecoff.$(OBJEXT) ehopt.$(OBJEXT) expr.$(OBJEXT) \
 	flonum-copy.$(OBJEXT) flonum-konst.$(OBJEXT) \
 	flonum-mult.$(OBJEXT) frags.$(OBJEXT) gen-sframe.$(OBJEXT) \
-	hash.$(OBJEXT) input-file.$(OBJEXT) input-scrub.$(OBJEXT) \
-	listing.$(OBJEXT) literal.$(OBJEXT) macro.$(OBJEXT) \
-	messages.$(OBJEXT) output-file.$(OBJEXT) read.$(OBJEXT) \
-	remap.$(OBJEXT) sb.$(OBJEXT) scfidw2gen.$(OBJEXT) \
-	sframe-opt.$(OBJEXT) stabs.$(OBJEXT) subsegs.$(OBJEXT) \
-	symbols.$(OBJEXT) write.$(OBJEXT)
+	ginsn.$(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) \
+	scfidw2gen.$(OBJEXT) scfi.$(OBJEXT) sframe-opt.$(OBJEXT) \
+	stabs.$(OBJEXT) subsegs.$(OBJEXT) symbols.$(OBJEXT) \
+	write.$(OBJEXT)
 am_as_new_OBJECTS = $(am__objects_1)
 am__dirstamp = $(am__leading_dot)dirstamp
 as_new_OBJECTS = $(am_as_new_OBJECTS)
@@ -570,6 +571,7 @@ GAS_CFILES = \
 	flonum-mult.c \
 	frags.c \
 	gen-sframe.c \
+	ginsn.c \
 	hash.c \
 	input-file.c \
 	input-scrub.c \
@@ -582,6 +584,7 @@ GAS_CFILES = \
 	remap.c \
 	sb.c \
 	scfidw2gen.c \
+	scfi.c \
 	sframe-opt.c \
 	stabs.c \
 	subsegs.c \
@@ -606,6 +609,7 @@ HFILES = \
 	flonum.h \
 	frags.h \
 	gen-sframe.h \
+	ginsn.h \
 	hash.h \
 	input-file.h \
 	itbl-lex.h \
@@ -617,6 +621,7 @@ HFILES = \
 	read.h \
 	sb.h \
 	scfidw2gen.h \
+	scfi.h \
 	subsegs.h \
 	symbols.h \
 	tc.h \
@@ -1325,6 +1330,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flonum-mult.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/frags.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gen-sframe.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ginsn.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@
@@ -1339,6 +1345,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/read.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/remap.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sb.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scfi.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scfidw2gen.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sframe-opt.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stabs.Po@am__quote@
diff --git a/gas/as.c b/gas/as.c
index 97b0ed38fb6..9f962fd5816 100644
--- a/gas/as.c
+++ b/gas/as.c
@@ -45,6 +45,7 @@
 #include "codeview.h"
 #include "bfdver.h"
 #include "write.h"
+#include "ginsn.h"
 
 #ifdef HAVE_ITBL_CPU
 #include "itbl-ops.h"
@@ -245,6 +246,7 @@ Options:\n\
                       	  d      omit debugging directives\n\
                       	  g      include general info\n\
                       	  h      include high-level source\n\
+			  i      include ginsn and synthesized CFI info\n\
                       	  l      include assembly\n\
                       	  m      include macro expansions\n\
                       	  n      omit forms processing\n\
@@ -1089,6 +1091,9 @@ This program has absolutely no warranty.\n"));
 		    case 'h':
 		      listing |= LISTING_HLL;
 		      break;
+		    case 'i':
+		      listing |= LISTING_GINSN_SCFI;
+		      break;
 		    case 'l':
 		      listing |= LISTING_LISTING;
 		      break;
diff --git a/gas/config/obj-elf.c b/gas/config/obj-elf.c
index eaf2d6a2f29..4da007cbb38 100644
--- a/gas/config/obj-elf.c
+++ b/gas/config/obj-elf.c
@@ -24,6 +24,7 @@
 #include "subsegs.h"
 #include "obstack.h"
 #include "dwarf2dbg.h"
+#include "ginsn.h"
 
 #ifndef ECOFF_DEBUGGING
 #define ECOFF_DEBUGGING 0
@@ -2302,6 +2303,13 @@ obj_elf_size (int ignore ATTRIBUTE_UNUSED)
       symbol_get_obj (sym)->size = XNEW (expressionS);
       *symbol_get_obj (sym)->size = exp;
     }
+
+  /* If the symbol in the directive matches the current function being
+     processed, indicate end of the current stream of ginsns.  */
+  if (flag_synth_cfi
+      && S_IS_FUNCTION (sym) && sym == ginsn_data_func_symbol ())
+    ginsn_data_end (symbol_temp_new_now ());
+
   demand_empty_rest_of_line ();
 }
 
@@ -2490,6 +2498,14 @@ obj_elf_type (int ignore ATTRIBUTE_UNUSED)
 	elfsym->symbol.flags &= ~mask;
     }
 
+  if (S_IS_FUNCTION (sym) && flag_synth_cfi)
+    {
+      /* Wrap up processing the previous block of ginsns first.  */
+      if (frchain_now->frch_ginsn_data)
+	ginsn_data_end (symbol_temp_new_now ());
+      ginsn_data_begin (sym);
+    }
+
   demand_empty_rest_of_line ();
 }
 
diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
index 57ae6c522a7..0276aa9f48b 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 "scfi.h"
 #include "gen-sframe.h"
 #include "sframe.h"
 #include "elf/x86-64.h"
@@ -193,8 +194,11 @@ static unsigned int x86_isa_1_used;
 static unsigned int x86_feature_2_used;
 /* Generate x86 used ISA and feature properties.  */
 static unsigned int x86_used_note = DEFAULT_X86_USED_NOTE;
+
 #endif
 
+static ginsnS *ginsn_new (symbolS *sym, enum ginsn_gen_mode gmode);
+
 static const char *default_arch = DEFAULT_ARCH;
 
 /* parse_register() returns this when a register alias cannot be used.  */
@@ -5116,6 +5120,716 @@ static INLINE bool may_need_pass2 (const insn_template *t)
 	       && t->base_opcode == 0x63);
 }
 
+bool
+x86_scfi_callee_saved_p (uint32_t dw2reg_num)
+{
+  if (dw2reg_num == 3 /* rbx.  */
+      || dw2reg_num == REG_FP /* rbp.  */
+      || dw2reg_num == REG_SP /* rsp.  */
+      || (dw2reg_num >= 12 && dw2reg_num <= 15) /* r12 - r15.  */)
+    return true;
+
+  return false;
+}
+
+static uint32_t
+ginsn_dw2_regnum (const reg_entry *ireg)
+{
+  /* PS: Note the data type here as int32_t, because of Dw2Inval (-1).  */
+  int32_t dwarf_reg = Dw2Inval;
+  const reg_entry *temp;
+
+  if (ireg->dw2_regnum[0] == Dw2Inval && ireg->dw2_regnum[1] == Dw2Inval)
+    return dwarf_reg;
+
+  dwarf_reg = ireg->dw2_regnum[flag_code >> 1];
+  if (dwarf_reg == Dw2Inval)
+    {
+      temp = ireg + 16;
+      dwarf_reg = ginsn_dw2_regnum (temp);
+    }
+
+  if (dwarf_reg == Dw2Inval)
+    gas_assert (1); /* Needs to be addressed.  */
+
+  return (uint32_t) dwarf_reg;
+}
+
+static void
+ginsn_set_where (ginsnS* ginsn)
+{
+  const char *file;
+  unsigned int line;
+  file = as_where (&line);
+  ginsn_set_file_line (ginsn, file, line);
+}
+
+static ginsnS *
+x86_ginsn_alu (i386_insn insn, symbolS *insn_end_sym)
+{
+  offsetT src_imm;
+  uint32_t dw2_regnum;
+  enum ginsn_src_type src_type;
+  enum ginsn_dst_type dst_type;
+  ginsnS *ginsn = NULL;
+
+  /* FIXME - create ginsn for REG_SP target only ? */
+  /* Map for insn.tm.extension_opcode
+     000 ADD    100 AND
+     001 OR     101 SUB
+     010 ADC    110 XOR
+     011 SBB    111 CMP  */
+
+  /* add/sub imm, %reg.
+     and imm, %reg only at this time for SCFI. */
+  if (!(insn.tm.extension_opcode == 0
+	|| insn.tm.extension_opcode == 4
+	|| insn.tm.extension_opcode == 5))
+    return ginsn;
+
+  /* TBD_GINSN_REPRESENTATION_LIMIT: There is no representation for when a
+     symbol is used as an operand, like so:
+	  addq    $simd_cmp_op+8, %rdx
+     Skip generating any ginsn for this.  */
+  if (insn.imm_operands == 1
+      && insn.op[0].imms->X_op == O_symbol)
+    return ginsn;
+
+  gas_assert (insn.imm_operands == 1
+	      && insn.op[0].imms->X_op == O_constant);
+  src_imm = insn.op[0].imms->X_add_number;
+  /* The second operand may be a register or indirect access.  */
+  if (insn.mem_operands == 1 && insn.base_reg)
+    {
+      dw2_regnum = ginsn_dw2_regnum (insn.base_reg);
+      src_type = GINSN_SRC_INDIRECT;
+      dst_type = GINSN_DST_INDIRECT;
+    }
+  else if (insn.mem_operands == 1 && insn.index_reg)
+    {
+      dw2_regnum = ginsn_dw2_regnum (insn.index_reg);
+      src_type = GINSN_SRC_INDIRECT;
+      dst_type = GINSN_DST_INDIRECT;
+    }
+  else
+    {
+      gas_assert (insn.reg_operands == 1);
+      dw2_regnum = ginsn_dw2_regnum (insn.op[1].regs);
+      src_type = GINSN_SRC_REG;
+      dst_type = GINSN_DST_REG;
+    }
+
+  /* For ginsn, keep the imm as second src operand.  */
+  if (insn.tm.extension_opcode == 5)
+    ginsn = ginsn_new_sub (insn_end_sym, true,
+			   src_type, dw2_regnum, 0,
+			   GINSN_SRC_IMM, 0, src_imm,
+			   dst_type, dw2_regnum, 0);
+  else if (insn.tm.extension_opcode == 4)
+    ginsn = ginsn_new_and (insn_end_sym, true,
+			   src_type, dw2_regnum, 0,
+			   GINSN_SRC_IMM, 0, src_imm,
+			   dst_type, dw2_regnum, 0);
+  else if (insn.tm.extension_opcode == 0)
+    ginsn = ginsn_new_add (insn_end_sym, true,
+			   src_type, dw2_regnum, 0,
+			   GINSN_SRC_IMM, 0, src_imm,
+			   dst_type, dw2_regnum, 0);
+
+  ginsn_set_where (ginsn);
+
+  return ginsn;
+}
+
+static ginsnS *
+x86_ginsn_move (i386_insn insn, symbolS *insn_end_sym)
+{
+  ginsnS *ginsn;
+  uint16_t opcode;
+  uint32_t dst_reg;
+  uint32_t src_reg;
+  offsetT dst_disp;
+  offsetT src_disp;
+  const reg_entry *dst = NULL;
+  const reg_entry *src = NULL;
+  enum ginsn_dst_type dst_type;
+  enum ginsn_src_type src_type;
+
+  opcode = insn.tm.base_opcode;
+  src_type = GINSN_SRC_REG;
+  src_disp = dst_disp = 0;
+  dst_type = GINSN_DST_REG;
+
+  if (opcode == 0x8b)
+    {
+      /* mov  disp(%reg), %reg.  */
+      if (insn.mem_operands && insn.base_reg)
+	{
+	  src = insn.base_reg;
+	  if (insn.disp_operands == 1)
+	    src_disp = insn.op[0].disps->X_add_number;
+	  src_type = GINSN_SRC_INDIRECT;
+	}
+      else
+	src = insn.op[0].regs;
+
+      dst = insn.op[1].regs;
+    }
+  else if (opcode == 0x89 || opcode == 0x88)
+    {
+      /* mov %reg, disp(%reg).  */
+      src = insn.op[0].regs;
+      if (insn.mem_operands && insn.base_reg)
+	{
+	  dst = insn.base_reg;
+	  if (insn.disp_operands == 1)
+	    dst_disp = insn.op[1].disps->X_add_number;
+	  dst_type = GINSN_DST_INDIRECT;
+	}
+      else
+	dst = insn.op[1].regs;
+    }
+
+  src_reg = ginsn_dw2_regnum (src);
+  dst_reg = ginsn_dw2_regnum (dst);
+
+  ginsn = ginsn_new_mov (insn_end_sym, true,
+			 src_type, src_reg, src_disp,
+			 dst_type, dst_reg, dst_disp);
+  ginsn_set_where (ginsn);
+
+  return ginsn;
+}
+
+static ginsnS *
+x86_ginsn_lea (i386_insn insn, symbolS *insn_end_sym)
+{
+  offsetT src_disp = 0;
+  ginsnS *ginsn = NULL;
+  uint32_t base_reg;
+  uint32_t index_reg;
+  offsetT index_scale;
+  uint32_t dst_reg;
+
+  if (!insn.index_reg && !insn.base_reg)
+    {
+      /* lea symbol, %rN.  */
+      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
+      /* FIXME - Skip encoding information about the symbol.
+	 This is TBD_GINSN_INFO_LOSS, but it is fine if the mode is
+	 GINSN_GEN_SCFI.  */
+      ginsn = ginsn_new_mov (insn_end_sym, false,
+			     GINSN_SRC_IMM, 0xf /* arbitrary const.  */, 0,
+			     GINSN_DST_REG, dst_reg, 0);
+    }
+  else if (insn.base_reg && !insn.index_reg)
+    {
+      /* lea    -0x2(%base),%dst.  */
+      base_reg = ginsn_dw2_regnum (insn.base_reg);
+      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
+
+      if (insn.disp_operands)
+	src_disp = insn.op[0].disps->X_add_number;
+
+      if (src_disp)
+	/* Generate an ADD ginsn.  */
+	ginsn = ginsn_new_add (insn_end_sym, true,
+			       GINSN_SRC_REG, base_reg, 0,
+			       GINSN_SRC_IMM, 0, src_disp,
+			       GINSN_DST_REG, dst_reg, 0);
+      else
+	  /* Generate a MOV ginsn.  */
+	  ginsn = ginsn_new_mov (insn_end_sym, true,
+				 GINSN_SRC_REG, base_reg, 0,
+				 GINSN_DST_REG, dst_reg, 0);
+    }
+  else if (!insn.base_reg && insn.index_reg)
+    {
+      /* lea (,%index,imm), %dst.  */
+      /* FIXME - Skip encoding an explicit multiply operation, instead use
+	 GINSN_TYPE_OTHER.  This is TBD_GINSN_INFO_LOSS, but it is fine if
+	 the mode is GINSN_GEN_SCFI.  */
+      index_scale = insn.log2_scale_factor;
+      index_reg = ginsn_dw2_regnum (insn.index_reg);
+      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
+      ginsn = ginsn_new_other (insn_end_sym, true,
+			       GINSN_SRC_REG, index_reg,
+			       GINSN_SRC_IMM, index_scale,
+			       GINSN_DST_REG, dst_reg);
+    }
+  else
+    {
+      /* lea disp(%base,%index,imm) %dst.  */
+      /* FIXME - Skip encoding information about the disp and imm for index
+	 reg.  This is TBD_GINSN_INFO_LOSS, but it is fine if the mode is
+	 GINSN_GEN_SCFI.  */
+      base_reg = ginsn_dw2_regnum (insn.base_reg);
+      index_reg = ginsn_dw2_regnum (insn.index_reg);
+      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
+      /* Generate an ADD ginsn.  */
+      ginsn = ginsn_new_add (insn_end_sym, true,
+			     GINSN_SRC_REG, base_reg, 0,
+			     GINSN_SRC_REG, index_reg, 0,
+			     GINSN_DST_REG, dst_reg, 0);
+    }
+
+  ginsn_set_where (ginsn);
+
+  return ginsn;
+}
+
+static ginsnS *
+x86_ginsn_jump (i386_insn insn, symbolS *insn_end_sym)
+{
+  ginsnS *ginsn = NULL;
+  symbolS *src_symbol;
+
+  gas_assert (insn.disp_operands == 1);
+
+  if (insn.op[0].disps->X_op == O_symbol)
+    {
+      src_symbol = insn.op[0].disps->X_add_symbol;
+      /* The jump target is expected to be a symbol with 0 addend.
+	 Assert for now to see if this assumption is true.  */
+      gas_assert (insn.op[0].disps->X_add_number == 0);
+      ginsn = ginsn_new_jump (insn_end_sym, true,
+			      GINSN_SRC_SYMBOL, 0, src_symbol);
+
+      ginsn_set_where (ginsn);
+    }
+
+  return ginsn;
+}
+
+static ginsnS *
+x86_ginsn_jump_cond (i386_insn insn, symbolS *insn_end_sym)
+{
+  ginsnS *ginsn = NULL;
+  symbolS *src_symbol;
+
+  /* TBD_GINSN_GEN_NOT_SCFI: Ignore move to or from xmm reg for mode.  */
+  if (i.tm.opcode_space == SPACE_0F)
+    return ginsn;
+
+  gas_assert (insn.disp_operands == 1);
+
+  if (insn.op[0].disps->X_op == O_symbol)
+    {
+      src_symbol = insn.op[0].disps->X_add_symbol;
+      /* The jump target is expected to be a symbol with 0 addend.
+	 Assert for now to see if this assumption is true.  */
+      gas_assert (insn.op[0].disps->X_add_number == 0);
+      ginsn = ginsn_new_jump_cond (insn_end_sym, true,
+				   GINSN_SRC_SYMBOL, 0, src_symbol);
+      ginsn_set_where (ginsn);
+    }
+  else
+    /* Catch them for now so we know what we are dealing with.  */
+    gas_assert (0);
+
+  return ginsn;
+}
+
+/* Generate one or more GAS instructions for the current machine dependent
+   instruction.
+
+   Returns the head of linked list of ginsn(s) added, if success;
+   Returns NULL if failure.  */
+
+static ginsnS *
+ginsn_new (symbolS *insn_end_sym, enum ginsn_gen_mode gmode)
+{
+  uint16_t opcode;
+  uint32_t dw2_regnum;
+  uint32_t src2_dw2_regnum;
+  int32_t gdisp = 0;
+  ginsnS *ginsn = NULL;
+  ginsnS *ginsn_next = NULL;
+  ginsnS *ginsn_last = NULL;
+
+  /* FIXME - Need a way to check whether the decoding is sane.  The specific
+     checks around i.tm.opcode_space were added as issues were seen.  Likely
+     insufficient.  */
+
+  /* Currently supports generation of selected ginsns, sufficient for
+     the use-case of SCFI only.  To remove this condition will require
+     work on this target-specific process of creation of ginsns.  Some
+     of such places are tagged with TBD_GINSN_GEN_NOT_SCFI to serve as
+     examples.  */
+  if (gmode != GINSN_GEN_SCFI)
+    return ginsn;
+
+  opcode = i.tm.base_opcode;
+
+  switch (opcode)
+    {
+    case 0x1:
+      /* add reg, reg.  */
+      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+
+      if (i.reg_operands == 2)
+	{
+	  src2_dw2_regnum = ginsn_dw2_regnum (i.op[1].regs);
+	  ginsn = ginsn_new_add (insn_end_sym, true,
+				 GINSN_SRC_REG, dw2_regnum, 0,
+				 GINSN_SRC_REG, src2_dw2_regnum, 0,
+				 GINSN_DST_REG, src2_dw2_regnum, 0);
+	  ginsn_set_where (ginsn);
+	}
+      else if (i.mem_operands && i.base_reg)
+	{
+	  src2_dw2_regnum = ginsn_dw2_regnum (i.base_reg);
+	  if (i.disp_operands == 1)
+	    gdisp = i.op[1].disps->X_add_number;
+
+	  ginsn = ginsn_new_add (insn_end_sym, true,
+				 GINSN_SRC_REG, dw2_regnum, 0,
+				 GINSN_SRC_INDIRECT, src2_dw2_regnum, gdisp,
+				 GINSN_DST_INDIRECT, src2_dw2_regnum, gdisp);
+	  ginsn_set_where (ginsn);
+	}
+      else
+	/* Catch them for now so we know what we are dealing with.  */
+	gas_assert (0);
+
+      break;
+    case 0x29:
+      /* If opcode_space == SPACE_0F, this is a movaps insn.  Skip it
+	 for GINSN_GEN_SCFI.  */
+      if (i.tm.opcode_space == SPACE_0F)
+	break;
+
+      /* sub reg, reg.  */
+      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+
+      if (i.reg_operands == 2)
+	{
+	  src2_dw2_regnum = ginsn_dw2_regnum (i.op[1].regs);
+	  ginsn = ginsn_new_sub (insn_end_sym, true,
+				 GINSN_SRC_REG, dw2_regnum, 0,
+				 GINSN_SRC_REG, src2_dw2_regnum, 0,
+				 GINSN_DST_REG, src2_dw2_regnum, 0);
+	  ginsn_set_where (ginsn);
+	}
+      else if (i.mem_operands && i.base_reg)
+	{
+	  src2_dw2_regnum = ginsn_dw2_regnum (i.base_reg);
+	  if (i.disp_operands == 1)
+	    gdisp = i.op[1].disps->X_add_number;
+
+	  ginsn = ginsn_new_sub (insn_end_sym, true,
+				 GINSN_SRC_REG, dw2_regnum, 0,
+				 GINSN_SRC_INDIRECT, src2_dw2_regnum, gdisp,
+				 GINSN_DST_INDIRECT, src2_dw2_regnum, gdisp);
+	  ginsn_set_where (ginsn);
+	}
+      else
+	/* Catch them for now so we know what we are dealing with.  */
+	gas_assert (0);
+
+      break;
+    case 0xa0:
+    case 0xa8:
+      /* If opcode_space != SPACE_0F, this is a test insn.  Skip it
+	 for GINSN_GEN_SCFI.  */
+      if (i.tm.opcode_space != SPACE_0F)
+	break;
+
+      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+      /* push fs / push gs.  */
+      ginsn = ginsn_new_sub (insn_end_sym, false,
+			     GINSN_SRC_REG, REG_SP, 0,
+			     GINSN_SRC_IMM, 0, 8,
+			     GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn);
+
+      ginsn_next = ginsn_new_store (insn_end_sym, false,
+				    GINSN_SRC_REG, dw2_regnum,
+				    GINSN_DST_INDIRECT, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+    case 0xa1:
+    case 0xa9:
+      /* If opcode_space != SPACE_0F, this is test insn.  Skip it
+	 for GINSN_GEN_SCFI.  */
+      if (i.tm.opcode_space != SPACE_0F)
+	break;
+
+      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+      /* pop fs / pop gs.  */
+      ginsn = ginsn_new_load (insn_end_sym, false,
+			      GINSN_SRC_INDIRECT, REG_SP, 0,
+			      GINSN_DST_REG, dw2_regnum);
+      ginsn_set_where (ginsn);
+
+      ginsn_next = ginsn_new_add (insn_end_sym, false,
+				  GINSN_SRC_REG, REG_SP, 0,
+				  GINSN_SRC_IMM, 0, 8,
+				  GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+    case 0x50 ... 0x57:
+      /* push reg.  */
+      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+      ginsn = ginsn_new_sub (insn_end_sym, false,
+			     GINSN_SRC_REG, REG_SP, 0,
+			     GINSN_SRC_IMM, 0, 8,
+			     GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn);
+
+      ginsn_next = ginsn_new_store (insn_end_sym, false,
+				    GINSN_SRC_REG, dw2_regnum,
+				    GINSN_DST_INDIRECT, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+    case 0x58 ... 0x5f:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* pop reg.  */
+      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+      ginsn = ginsn_new_load (insn_end_sym, false,
+			      GINSN_SRC_INDIRECT, REG_SP, 0,
+			      GINSN_DST_REG, dw2_regnum);
+      ginsn_set_where (ginsn);
+
+      ginsn_next = ginsn_new_add (insn_end_sym, false,
+				  GINSN_SRC_REG, REG_SP, 0,
+				  GINSN_SRC_IMM, 0, 8,
+				  GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+    case 0x68:
+    case 0x6a:
+      /* push imm. */
+      /* Skip getting the value of imm from machine instruction
+	 because for ginsn generation this is not important.  */
+      ginsn = ginsn_new_sub (insn_end_sym, false,
+			     GINSN_SRC_REG, REG_SP, 0,
+			     GINSN_SRC_IMM, 0, 8,
+			     GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn);
+
+      ginsn_next = ginsn_new_store (insn_end_sym, false,
+				    GINSN_SRC_IMM, 0,
+				    GINSN_DST_INDIRECT, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+    case 0x70 ... 0x7f:
+      ginsn = x86_ginsn_jump_cond (i, insn_end_sym);
+      break;
+    case 0x81:
+    case 0x83:
+      ginsn = x86_ginsn_alu (i, insn_end_sym);
+      break;
+    case 0x8b:
+      /* Move r/m64 to r64.  */
+    case 0x88:
+    case 0x89:
+      /* mov reg, reg/mem.  */
+      ginsn = x86_ginsn_move (i, insn_end_sym);
+      break;
+    case 0x8d:
+      /* lea disp(%src), %dst */
+      ginsn = x86_ginsn_lea (i, insn_end_sym);
+      break;
+    case 0x8f:
+      /* pop to mem.  */
+      gas_assert (i.base_reg);
+      dw2_regnum = ginsn_dw2_regnum (i.base_reg);
+      ginsn = ginsn_new_load (insn_end_sym, false,
+			      GINSN_SRC_INDIRECT, REG_SP, 0,
+			      GINSN_DST_INDIRECT, dw2_regnum);
+      ginsn_set_where (ginsn);
+
+      ginsn_next = ginsn_new_add (insn_end_sym, false,
+				  GINSN_SRC_REG, REG_SP, 0,
+				  GINSN_SRC_IMM, 0, 8,
+				  GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+    case 0x9c:
+      /* pushf / pushfd / pushfq.
+	 Tracking EFLAGS register by number is not necessary.  */
+      ginsn = ginsn_new_sub (insn_end_sym, false,
+			     GINSN_SRC_REG, REG_SP, 0,
+			     GINSN_SRC_IMM, 0, 8,
+			     GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn);
+
+      ginsn_next = ginsn_new_store (insn_end_sym, false,
+				    GINSN_SRC_IMM, 0,
+				    GINSN_DST_INDIRECT, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+
+      break;
+    case 0xff:
+      /* push from mem.  */
+      if (i.tm.extension_opcode == 6)
+	{
+	  ginsn = ginsn_new_sub (insn_end_sym, false,
+				 GINSN_SRC_REG, REG_SP, 0,
+				 GINSN_SRC_IMM, 0, 8,
+				 GINSN_DST_REG, REG_SP, 0);
+	  ginsn_set_where (ginsn);
+
+	  /* These instructions have no imm, only indirect access.  */
+	  gas_assert (i.base_reg);
+	  dw2_regnum = ginsn_dw2_regnum (i.base_reg);
+	  ginsn_next = ginsn_new_store (insn_end_sym, false,
+					GINSN_SRC_INDIRECT, dw2_regnum,
+					GINSN_DST_INDIRECT, REG_SP, 0);
+	  ginsn_set_where (ginsn_next);
+
+	  gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+	}
+      else if (i.tm.extension_opcode == 4)
+	{
+	  /* jmp r/m.  E.g., notrack jmp *%rax.  */
+	  if (i.reg_operands)
+	    {
+	      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+	      ginsn = ginsn_new_jump (insn_end_sym, true,
+				      GINSN_SRC_REG, dw2_regnum, NULL);
+	      ginsn_set_where (ginsn);
+	    }
+	  else if (i.mem_operands && i.index_reg)
+	    {
+	      /* jmp    *0x0(,%rax,8).  */
+	      dw2_regnum = ginsn_dw2_regnum (i.index_reg);
+	      ginsn = ginsn_new_jump (insn_end_sym, true,
+				      GINSN_SRC_REG, dw2_regnum, NULL);
+	      ginsn_set_where (ginsn);
+	    }
+	  else if (i.mem_operands && i.base_reg)
+	    {
+	      dw2_regnum = ginsn_dw2_regnum (i.base_reg);
+	      ginsn = ginsn_new_jump (insn_end_sym, true,
+				      GINSN_SRC_REG, dw2_regnum, NULL);
+	      ginsn_set_where (ginsn);
+	    }
+	  else
+	    /* Catch them for now so we know what we are dealing with.  */
+	    gas_assert (0);
+	}
+      else if (i.tm.extension_opcode == 2)
+	{
+	  /* 0xFF /2 (call).  */
+	  if (i.reg_operands)
+	    {
+	      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+	      ginsn = ginsn_new_call (insn_end_sym, true,
+				      GINSN_SRC_REG, dw2_regnum, NULL);
+	      ginsn_set_where (ginsn);
+	    }
+	  else if (i.mem_operands && i.base_reg)
+	    {
+	      dw2_regnum = ginsn_dw2_regnum (i.base_reg);
+	      ginsn = ginsn_new_call (insn_end_sym, true,
+				      GINSN_SRC_REG, dw2_regnum, NULL);
+	      ginsn_set_where (ginsn);
+	    }
+	  else
+	    /* Catch them for now so we know what we are dealing with.  */
+	    gas_assert (0);
+	}
+      else
+	/* Catch them for now so we know what we are dealing with.  */
+	gas_assert (0);
+      break;
+    case 0xc2:
+    case 0xc3:
+      /* Near ret.  */
+      ginsn = ginsn_new_return (insn_end_sym, true);
+      ginsn_set_where (ginsn);
+      break;
+    case 0xc9:
+      /* The 'leave' instruction copies the contents of the RBP register
+	 into the RSP register to release all stack space allocated to the
+	 procedure.  */
+      ginsn = ginsn_new_mov (insn_end_sym, false,
+			     GINSN_SRC_REG, REG_FP, 0,
+			     GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn);
+
+      /* Then it restores the old value of the RBP register from the stack.  */
+      ginsn_next = ginsn_new_load (insn_end_sym, false,
+				   GINSN_SRC_INDIRECT, REG_SP, 0,
+				   GINSN_DST_REG, REG_FP);
+      ginsn_set_where (ginsn_next);
+
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      ginsn_last = ginsn_new_add (insn_end_sym, false,
+				  GINSN_SRC_REG, REG_SP, 0,
+				  GINSN_SRC_IMM, 0, 8,
+				  GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+
+      gas_assert (!ginsn_link_next (ginsn_next, ginsn_last));
+      break;
+    case 0xe8:
+      /* PS: SCFI machinery does not care about which func is being
+	 called.  OK to skip that info.  */
+      ginsn = ginsn_new_call (insn_end_sym, true,
+			      GINSN_SRC_SYMBOL, 0, NULL);
+      ginsn_set_where (ginsn);
+      break;
+    case 0xe9:
+    case 0xeb:
+      /* If opcode_space == SPACE_0F, this is a psubw por insn.  Skip it
+	 for GINSN_GEN_SCFI.  */
+      if (i.tm.opcode_space == SPACE_0F)
+	break;
+
+      /* Unconditional jmp.  */
+      ginsn = x86_ginsn_jump (i, insn_end_sym);
+      ginsn_set_where (ginsn);
+      break;
+      /* Fall Through.  */
+    default:
+      /* TBD_GINSN_GEN_NOT_SCFI: Keep a warning, for now, to find out about
+	 possibly missed instructions affecting REG_SP or REG_FP.  These
+	 checks may not be completely exhaustive as they do not involve
+	 index / base reg.  */
+      if (i.op[0].regs)
+	{
+	  dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+	  if (dw2_regnum == REG_SP || dw2_regnum == REG_FP)
+	    as_warn_where (last_insn.file, last_insn.line,
+			   _("SCFI: unhandled op 0x%x may cause incorrect CFI"),
+			   i.tm.base_opcode);
+	}
+      if (i.op[1].regs)
+	{
+	  dw2_regnum = ginsn_dw2_regnum (i.op[1].regs);
+	  if (dw2_regnum == REG_SP || dw2_regnum == REG_FP)
+	    as_warn_where (last_insn.file, last_insn.line,
+			   _("SCFI: unhandled op 0x%x may cause incorrect CFI"),
+			   i.tm.base_opcode);
+	}
+      /* Keep an eye on other instructions affecting control flow.  */
+      gas_assert (!i.tm.opcode_modifier.jump);
+      /* TBD_GINSN_GEN_NOT_SCFI: Skip all other opcodes uninteresting for
+	 GINSN_GEN_SCFI mode.  */
+      break;
+    }
+
+  return ginsn;
+}
+
 /* This is the guts of the machine-dependent assembler.  LINE points to a
    machine dependent instruction.  This function is supposed to emit
    the frags/bytes it assembles to.  */
@@ -5128,6 +5842,7 @@ md_assemble (char *line)
   const char *end, *pass1_mnem = NULL;
   enum i386_error pass1_err = 0;
   const insn_template *t;
+  ginsnS *ginsn;
 
   /* Initialize globals.  */
   current_templates = NULL;
@@ -5659,6 +6374,13 @@ md_assemble (char *line)
   /* We are ready to output the insn.  */
   output_insn ();
 
+  /* At this time, SCFI is enabled only for AMD64 ABI.  */
+  if (flag_synth_cfi && x86_elf_abi == X86_64_ABI)
+    {
+      ginsn = ginsn_new (symbol_temp_new_now (), frch_ginsn_gen_mode ());
+      frch_ginsn_data_append (ginsn);
+    }
+
   insert_lfence_after ();
 
   last_insn.seg = now_seg;
@@ -10904,6 +11626,7 @@ s_insn (int dummy ATTRIBUTE_UNUSED)
   valueT val;
   bool vex = false, xop = false, evex = false;
   static const templates tt = { &i.tm, &i.tm + 1 };
+  ginsnS *ginsn;
 
   init_globals ();
 
@@ -11658,7 +12381,14 @@ s_insn (int dummy ATTRIBUTE_UNUSED)
 
   output_insn ();
 
- done:
+  /* At this time, SCFI is enabled only for AMD64 ABI.  */
+  if (flag_synth_cfi && x86_elf_abi == X86_64_ABI)
+    {
+      ginsn = ginsn_new (symbol_temp_new_now (), frch_ginsn_gen_mode ());
+      frch_ginsn_data_append (ginsn);
+    }
+
+done:
   *saved_ilp = saved_char;
   input_line_pointer = line;
 
@@ -15293,6 +16023,9 @@ i386_target_format (void)
   else
     as_fatal (_("unknown architecture"));
 
+  if (flag_synth_cfi && x86_elf_abi != X86_64_ABI)
+    as_fatal (_("Synthesizing CFI is not supported for this ABI"));
+
   if (cpu_flags_all_zero (&cpu_arch_isa_flags))
     cpu_arch_isa_flags = cpu_arch[flag_code == CODE_64BIT].enable;
 
diff --git a/gas/config/tc-i386.h b/gas/config/tc-i386.h
index a3d463d961f..f63ad1694a9 100644
--- a/gas/config/tc-i386.h
+++ b/gas/config/tc-i386.h
@@ -373,6 +373,27 @@ extern int i386_elf_section_type (const char *, size_t);
 extern void i386_solaris_fix_up_eh_frame (segT);
 #endif
 
+#define TARGET_USE_GINSN 1
+/* Allow GAS to synthesize DWARF CFI for hand-written asm.
+   PS: TARGET_USE_CFIPOP is a pre-condition.  */
+#define TARGET_USE_SCFI 1
+/* Identify the maximum DWARF register number of all the registers being
+   tracked for SCFI.  This is the last DWARF register number of the set
+   of SP, BP, and all callee-saved registers.  For AMD64, this means
+   R15 (15).  Use SCFI_CALLEE_SAVED_REG_P to identify which registers
+   are callee-saved from this set.  */
+#define SCFI_NUM_REGS 15
+/* Identify the DWARF register number of the frame-pointer register.  */
+#define REG_FP 6
+/* Identify the DWARF register number of the stack-pointer register.  */
+#define REG_SP 7
+/* Some ABIs, like AMD64, use stack for call instruction.  For such an ABI,
+   identify the initial (CFA) offset from RSP at the entry of function.  */
+#define SCFI_INIT_CFA_OFFSET 8
+
+#define SCFI_CALLEE_SAVED_REG_P(dw2reg)  x86_scfi_callee_saved_p (dw2reg)
+extern bool x86_scfi_callee_saved_p (uint32_t dw2reg_num);
+
 /* Support for SHF_X86_64_LARGE */
 extern bfd_vma x86_64_section_letter (int, const char **);
 #define md_elf_section_letter(LETTER, PTR_MSG)	x86_64_section_letter (LETTER, PTR_MSG)
diff --git a/gas/ginsn.c b/gas/ginsn.c
new file mode 100644
index 00000000000..1d2b6c858b5
--- /dev/null
+++ b/gas/ginsn.c
@@ -0,0 +1,1225 @@
+/* ginsn.h - GAS instruction representation.
+   Copyright (C) 2023 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 "ginsn.h"
+#include "scfi.h"
+
+#ifdef TARGET_USE_GINSN
+
+const char *const ginsn_type_names[] =
+{
+#define _GINSN_TYPE_ITEM(NAME, STR) STR,
+  _GINSN_TYPES
+#undef _GINSN_TYPE_ITEM
+};
+
+const char *const ginsn_src_type_names[] =
+{
+#define _GINSN_SRC_TYPE_ITEM(NAME, STR) STR,
+  _GINSN_SRC_TYPES
+#undef _GINSN_SRC_TYPE_ITEM
+};
+const char *const ginsn_dst_type_names[] =
+{
+#define _GINSN_DST_TYPE_ITEM(NAME, STR) STR,
+  _GINSN_DST_TYPES
+#undef _GINSN_DST_TYPE_ITEM
+};
+
+static
+ginsnS *ginsn_alloc (void)
+{
+  ginsnS *ginsn = XCNEW (ginsnS);
+  return ginsn;
+}
+
+static ginsnS*
+ginsn_init (enum ginsn_type type, symbolS *sym, bool real_p)
+{
+  ginsnS *ginsn = ginsn_alloc ();
+  ginsn->type = type;
+  ginsn->sym = sym;
+  if (real_p)
+    ginsn->flags |= GINSN_F_INSN_REAL;
+  return ginsn;
+}
+
+static void
+ginsn_cleanup (ginsnS **ginsnp)
+{
+  ginsnS *ginsn;
+
+  if (!ginsnp || !*ginsnp)
+    return;
+
+  ginsn = *ginsnp;
+  if (ginsn->scfi_ops)
+    {
+      scfi_ops_cleanup (ginsn->scfi_ops);
+      ginsn->scfi_ops = NULL;
+    }
+
+  free (ginsn);
+  ginsn = NULL;
+}
+
+static void
+ginsn_set_src (struct ginsn_src *src, enum ginsn_src_type type, uint32_t reg,
+	       int32_t immdisp)
+{
+  if (!src)
+    return;
+
+  src->type = type;
+  /* Even when the use-case is SCFI, the value of reg may be > SCFI_NUM_REGS.
+     E.g., in AMD64, push fs etc.  */
+  src->reg = reg;
+
+  if (type == GINSN_SRC_IMM || type == GINSN_SRC_INDIRECT)
+    src->immdisp = immdisp;
+}
+
+static void
+ginsn_set_dst (struct ginsn_dst *dst, enum ginsn_dst_type type, uint32_t reg,
+	       int32_t disp)
+{
+  if (!dst)
+    return;
+
+  dst->type = type;
+  dst->reg = reg;
+
+  if (type == GINSN_DST_INDIRECT)
+    dst->disp = disp;
+}
+
+struct ginsn_src *
+ginsn_get_src1 (ginsnS *ginsn)
+{
+  return &ginsn->src[0];
+}
+
+struct ginsn_src *
+ginsn_get_src2 (ginsnS *ginsn)
+{
+  return &ginsn->src[1];
+}
+
+struct ginsn_dst *
+ginsn_get_dst (ginsnS *ginsn)
+{
+  return &ginsn->dst;
+}
+
+uint32_t
+ginsn_get_src_reg (struct ginsn_src *src)
+{
+  return src->reg;
+}
+
+enum ginsn_src_type
+ginsn_get_src_type (struct ginsn_src *src)
+{
+  return src->type;
+}
+
+uint32_t
+ginsn_get_src_disp (struct ginsn_src *src)
+{
+  return src->immdisp;
+}
+
+uint32_t
+ginsn_get_src_imm (struct ginsn_src *src)
+{
+  return src->immdisp;
+}
+
+uint32_t
+ginsn_get_dst_reg (struct ginsn_dst *dst)
+{
+  return dst->reg;
+}
+
+enum ginsn_dst_type
+ginsn_get_dst_type (struct ginsn_dst *dst)
+{
+  return dst->type;
+}
+
+int32_t
+ginsn_get_dst_disp (struct ginsn_dst *dst)
+{
+  return (int32_t) dst->disp;
+}
+
+void
+label_ginsn_map_insert (symbolS *label, ginsnS *ginsn)
+{
+  const char *name = S_GET_NAME (label);
+  str_hash_insert (frchain_now->frch_ginsn_data->label_ginsn_map,
+		   name, ginsn, 0 /* noreplace.  */);
+}
+
+ginsnS *
+label_ginsn_map_find (symbolS *label)
+{
+  const char *name = S_GET_NAME (label);
+  ginsnS *ginsn
+    = (ginsnS *) str_hash_find (frchain_now->frch_ginsn_data->label_ginsn_map,
+				name);
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_phantom (symbolS *sym)
+{
+  ginsnS *ginsn = ginsn_alloc ();
+  ginsn->type = GINSN_TYPE_PHANTOM;
+  ginsn->sym = sym;
+  /* By default, GINSN_F_INSN_REAL is not set in ginsn->flags.  */
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_symbol (symbolS *sym, bool func_begin_p)
+{
+  ginsnS *ginsn = ginsn_alloc ();
+  ginsn->type = GINSN_TYPE_SYMBOL;
+  ginsn->sym = sym;
+  if (func_begin_p)
+    ginsn->flags |= GINSN_F_FUNC_MARKER;
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_symbol_func_begin (symbolS *sym)
+{
+  return ginsn_new_symbol (sym, true);
+}
+
+ginsnS *
+ginsn_new_symbol_func_end (symbolS *sym)
+{
+  return ginsn_new_symbol (sym, false);
+}
+
+ginsnS *
+ginsn_new_symbol_user_label (symbolS *sym)
+{
+  ginsnS *ginsn = ginsn_new_symbol (sym, false);
+  ginsn->flags |= GINSN_F_USER_LABEL;
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_add (symbolS *sym, bool real_p,
+	       enum ginsn_src_type src1_type, uint32_t src1_val, int32_t src1_disp,
+	       enum ginsn_src_type src2_type, uint32_t src2_val, int32_t src2_disp,
+	       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp)
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_ADD, sym, real_p);
+  /* src info.  */
+  ginsn_set_src (&ginsn->src[0], src1_type, src1_val, src1_disp);
+  ginsn_set_src (&ginsn->src[1], src2_type, src2_val, src2_disp);
+  /* dst info.  */
+  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
+
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_and (symbolS *sym, bool real_p,
+	       enum ginsn_src_type src1_type, uint32_t src1_val, int32_t src1_disp,
+	       enum ginsn_src_type src2_type, uint32_t src2_val, int32_t src2_disp,
+	       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp)
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_AND, sym, real_p);
+  /* src info.  */
+  ginsn_set_src (&ginsn->src[0], src1_type, src1_val, src1_disp);
+  ginsn_set_src (&ginsn->src[1], src2_type, src2_val, src2_disp);
+  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
+
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_call (symbolS *sym, bool real_p,
+		enum ginsn_src_type src_type, uint32_t src_val,
+		symbolS *src_text_sym)
+
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_CALL, sym, real_p);
+  /* src info.  */
+  ginsn_set_src (&ginsn->src[0], src_type, src_val, 0);
+
+  if (src_type == GINSN_SRC_SYMBOL)
+    ginsn->src[0].sym = src_text_sym;
+
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_jump (symbolS *sym, bool real_p,
+		enum ginsn_src_type src_type, uint32_t src_val,
+		symbolS *src_ginsn_sym)
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_JUMP, sym, real_p);
+  /* src info.  */
+  ginsn_set_src (&ginsn->src[0], src_type, src_val, 0);
+
+  if (src_type == GINSN_SRC_SYMBOL)
+    ginsn->src[0].sym = src_ginsn_sym;
+
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_jump_cond (symbolS *sym, bool real_p,
+		     enum ginsn_src_type src_type, uint32_t src_val,
+		     symbolS *src_ginsn_sym)
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_JUMP_COND, sym, real_p);
+  /* src info.  */
+  ginsn_set_src (&ginsn->src[0], src_type, src_val, 0);
+
+  if (src_type == GINSN_SRC_SYMBOL)
+    ginsn->src[0].sym = src_ginsn_sym;
+
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_mov (symbolS *sym, bool real_p,
+	       enum ginsn_src_type src_type, uint32_t src_reg, int32_t src_disp,
+	       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp)
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_MOV, sym, real_p);
+  /* src info.  */
+  ginsn_set_src (&ginsn->src[0], src_type, src_reg, src_disp);
+  /* dst info.  */
+  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
+
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_store (symbolS *sym, bool real_p,
+		 enum ginsn_src_type src_type, uint32_t src_reg,
+		 enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp)
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_STORE, sym, real_p);
+  /* src info.  */
+  ginsn_set_src (&ginsn->src[0], src_type, src_reg, 0);
+  /* dst info.  */
+  gas_assert (dst_type == GINSN_DST_INDIRECT);
+  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
+
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_load (symbolS *sym, bool real_p,
+		enum ginsn_src_type src_type, uint32_t src_reg, int32_t src_disp,
+		enum ginsn_dst_type dst_type, uint32_t dst_reg)
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_LOAD, sym, real_p);
+  /* src info.  */
+  gas_assert (src_type == GINSN_SRC_INDIRECT);
+  ginsn_set_src (&ginsn->src[0], src_type, src_reg, src_disp);
+  /* dst info.  */
+  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, 0);
+
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_sub (symbolS *sym, bool real_p,
+	       enum ginsn_src_type src1_type, uint32_t src1_val, int32_t src1_disp,
+	       enum ginsn_src_type src2_type, uint32_t src2_val, int32_t src2_disp,
+	       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp)
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_SUB, sym, real_p);
+  /* src info.  */
+  ginsn_set_src (&ginsn->src[0], src1_type, src1_val, src1_disp);
+  ginsn_set_src (&ginsn->src[1], src2_type, src2_val, src2_disp);
+  /* dst info.  */
+  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
+
+  return ginsn;
+}
+
+/* PS: Note this API does not identify the displacement values of
+   src1/src2/dst.  At this time, it is unnecessary for correctness to support
+   the additional argument.  */
+
+ginsnS *
+ginsn_new_other (symbolS *sym, bool real_p,
+		 enum ginsn_src_type src1_type, uint32_t src1_val,
+		 enum ginsn_src_type src2_type, uint32_t src2_val,
+		 enum ginsn_dst_type dst_type, uint32_t dst_reg)
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_OTHER, sym, real_p);
+  /* src info.  */
+  ginsn_set_src (&ginsn->src[0], src1_type, src1_val, src1_val);
+  /* GINSN_SRC_INDIRECT src2_type is not expected.  */
+  gas_assert (src2_type != GINSN_SRC_INDIRECT);
+  ginsn_set_src (&ginsn->src[1], src2_type, src2_val, src2_val);
+  /* dst info.  */
+  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, 0);
+
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_return (symbolS *sym, bool real_p)
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_RETURN, sym, real_p);
+  return ginsn;
+}
+
+void
+ginsn_set_file_line (ginsnS *ginsn, const char *file, unsigned int line)
+{
+  if (!ginsn)
+    return;
+
+  ginsn->file = file;
+  ginsn->line = line;
+}
+
+int
+ginsn_link_next (ginsnS *ginsn, ginsnS *next)
+{
+  int ret = 0;
+
+  /* Avoid data corruption by limiting the scope of the API.  */
+  if (!ginsn || ginsn->next)
+    return 1;
+
+  ginsn->next = next;
+
+  return ret;
+}
+
+bool
+ginsn_track_reg_p (uint32_t dw2reg, enum ginsn_gen_mode gmode)
+{
+  bool track_p = false;
+
+  if (gmode == GINSN_GEN_SCFI && dw2reg <= SCFI_NUM_REGS)
+    {
+      /* FIXME - rename this to tc_ ? */
+      track_p |= SCFI_CALLEE_SAVED_REG_P (dw2reg);
+      track_p |= (dw2reg == REG_FP);
+      track_p |= (dw2reg == REG_SP);
+    }
+
+  return track_p;
+}
+
+static bool
+ginsn_indirect_jump_p (ginsnS *ginsn)
+{
+  bool ret_p = false;
+  if (!ginsn)
+    return ret_p;
+
+  ret_p = (ginsn->type == GINSN_TYPE_JUMP
+	   && ginsn->src[0].type == GINSN_SRC_REG);
+  return ret_p;
+}
+
+static bool
+ginsn_direct_local_jump_p (ginsnS *ginsn)
+{
+  bool ret_p = false;
+  if (!ginsn)
+    return ret_p;
+
+  ret_p |= (ginsn->type == GINSN_TYPE_JUMP
+	    && ginsn->src[0].type == GINSN_SRC_SYMBOL
+	    && S_IS_LOCAL (ginsn->src[0].sym));
+  return ret_p;
+}
+
+static char*
+ginsn_src_print (struct ginsn_src *src)
+{
+  size_t len = 39;
+  char *src_str = XNEWVEC (char, len);
+
+  memset (src_str, 0, len);
+
+  if (src->type == GINSN_SRC_REG)
+    {
+      char *buf = XNEWVEC (char, 32);
+      sprintf (buf, "%%r%d, ", ginsn_get_src_reg (src));
+      strcat (src_str, buf);
+    }
+  else if (src->type == GINSN_SRC_IMM)
+    {
+      char *buf = XNEWVEC (char, 32);
+      sprintf (buf, "%d, ", ginsn_get_src_imm (src));
+      strcat (src_str, buf);
+    }
+  else if (src->type == GINSN_SRC_INDIRECT)
+    {
+      char *buf = XNEWVEC (char, 32);
+      sprintf (buf, "[%%r%d+%d], ", ginsn_get_src_reg (src),
+	       ginsn_get_src_disp (src));
+      strcat (src_str, buf);
+    }
+
+  return src_str;
+}
+
+static char*
+ginsn_dst_print (struct ginsn_dst *dst)
+{
+  size_t len = GINSN_LISTING_OPND_LEN;
+  char *dst_str = XNEWVEC (char, len);
+
+  memset (dst_str, 0, len);
+
+  if (dst->type == GINSN_DST_REG)
+    {
+      char *buf = XNEWVEC (char, 32);
+      sprintf (buf, "%%r%d", ginsn_get_dst_reg (dst));
+      strcat (dst_str, buf);
+    }
+  else if (dst->type == GINSN_DST_INDIRECT)
+    {
+      char *buf = XNEWVEC (char, 32);
+      sprintf (buf, "[%%r%d+%d]", ginsn_get_dst_reg (dst),
+	       ginsn_get_dst_disp (dst));
+      strcat (dst_str, buf);
+    }
+
+  gas_assert (strlen (dst_str) < GINSN_LISTING_OPND_LEN);
+
+  return dst_str;
+}
+
+static const char*
+ginsn_type_sym_begin_end_print (ginsnS *ginsn)
+{
+  int id = 0;
+  const char *ginsn_sym_strs[]
+    = { "", "FUNC_BEGIN", "FUNC_END" };
+
+  if (GINSN_F_FUNC_BEGIN_P (ginsn))
+    id = 1;
+  else if (GINSN_F_FUNC_END_P (ginsn))
+    id = 2;
+
+  return ginsn_sym_strs[id];
+}
+
+static char*
+ginsn_print (ginsnS *ginsn)
+{
+  struct ginsn_src *src;
+  struct ginsn_dst *dst;
+  size_t len = GINSN_LISTING_LEN;
+  char *ginsn_str = XNEWVEC (char, len);
+
+  memset (ginsn_str, 0, len);
+
+  strcpy (ginsn_str, "ginsn: ");
+
+  strcat (ginsn_str, ginsn_type_names[ginsn->type]);
+  strcat (ginsn_str, " ");
+
+  /* For some ginsn types, no further information is printed for now.  */
+  if (ginsn->type == GINSN_TYPE_CALL
+      || ginsn->type == GINSN_TYPE_RETURN
+      || ginsn->type == GINSN_TYPE_OTHER)
+    goto end;
+  else if (ginsn->type == GINSN_TYPE_SYMBOL)
+    {
+      if (GINSN_F_USER_LABEL_P (ginsn))
+	strncat (ginsn_str, S_GET_NAME (ginsn->sym), len - 10);
+      else
+	strcat (ginsn_str, ginsn_type_sym_begin_end_print (ginsn));
+      goto end;
+    }
+
+  /* src 1.  */
+  src = ginsn_get_src1 (ginsn);
+  strcat (ginsn_str, ginsn_src_print (src));
+
+  /* src 2.  */
+  src = ginsn_get_src2 (ginsn);
+  strcat (ginsn_str, ginsn_src_print (src));
+
+  /* dst.  */
+  dst = ginsn_get_dst (ginsn);
+  strcat (ginsn_str, ginsn_dst_print (dst));
+
+end:
+  gas_assert (strlen (ginsn_str) < GINSN_LISTING_LEN);
+  return ginsn_str;
+}
+
+static void
+gbb_cleanup (gbbS **bbp)
+{
+  gbbS *bb = NULL;
+
+  if (!bbp && !*bbp)
+    return;
+
+  bb = *bbp;
+
+  if (bb->entry_state)
+    {
+      free (bb->entry_state);
+      bb->entry_state = NULL;
+    }
+  if (bb->exit_state)
+    {
+      free (bb->exit_state);
+      bb->exit_state = NULL;
+    }
+  free (bb);
+  *bbp = NULL;
+}
+
+static void
+bb_add_edge (gbbS* from_bb, gbbS *to_bb)
+{
+  gedgeS *tmpedge = NULL;
+  gedgeS *gedge;
+  bool exists = false;
+
+  if (!from_bb || !to_bb)
+    return;
+
+  /* Create a new edge object.  */
+  gedge = XCNEW (gedgeS);
+  gedge->dst_bb = to_bb;
+  gedge->next = NULL;
+  gedge->visited = false;
+
+  /* Add it in.  */
+  if (from_bb->out_gedges == NULL)
+    {
+      from_bb->out_gedges = gedge;
+      from_bb->num_out_gedges++;
+    }
+  else
+    {
+      /* Get the tail of the list.  */
+      tmpedge = from_bb->out_gedges;
+      while (tmpedge)
+	{
+	  /* Do not add duplicate edges.  Duplicated edges will cause unwanted
+	     failures in the forward and backward passes for SCFI.  */
+	  if (tmpedge->dst_bb == to_bb)
+	    {
+	      exists = true;
+	      break;
+	    }
+	  if (tmpedge->next)
+	    tmpedge = tmpedge->next;
+	  else
+	    break;
+	}
+
+      if (!exists)
+	{
+	  tmpedge->next = gedge;
+	  from_bb->num_out_gedges++;
+	}
+      else
+	free (gedge);
+    }
+}
+
+static void
+cfg_add_bb (gcfgS *gcfg, gbbS *gbb)
+{
+  gbbS *last_bb = NULL;
+
+  if (!gcfg->root_bb)
+    gcfg->root_bb = gbb;
+  else
+    {
+      last_bb = gcfg->root_bb;
+      while (last_bb->next)
+	last_bb = last_bb->next;
+
+      last_bb->next = gbb;
+    }
+  gcfg->num_gbbs++;
+
+  gbb->id = gcfg->num_gbbs;
+}
+
+static gbbS*
+add_bb_at_ginsn (symbolS *func, gcfgS *gcfg, ginsnS *ginsn, gbbS *prev_bb,
+		 int *errp);
+
+static gbbS*
+find_bb (gcfgS *gcfg, ginsnS *ginsn)
+{
+  gbbS *found_bb = NULL;
+  gbbS *gbb = NULL;
+
+  if (!ginsn)
+    return found_bb;
+
+  if (ginsn->visited)
+    {
+      cfg_for_each_bb (gcfg, gbb)
+	{
+	  if (gbb->first_ginsn == ginsn)
+	    {
+	      found_bb = gbb;
+	      break;
+	    }
+	}
+      /* Must be found if ginsn is visited.  */
+      gas_assert (found_bb);
+    }
+
+  return found_bb;
+}
+
+static gbbS*
+find_or_make_bb (symbolS *func, gcfgS *gcfg, ginsnS *ginsn, gbbS *prev_bb,
+		 int *errp)
+{
+  gbbS *found_bb = NULL;
+
+  found_bb = find_bb (gcfg, ginsn);
+  if (found_bb)
+    return found_bb;
+
+  return add_bb_at_ginsn (func, gcfg, ginsn, prev_bb, errp);
+}
+
+/* Add the basic block starting at GINSN to the given GCFG.
+   Also adds an edge from the PREV_BB to the newly added basic block.
+
+   This is a recursive function which returns the root of the added
+   basic blocks.  */
+
+static gbbS*
+add_bb_at_ginsn (symbolS *func, gcfgS *gcfg, ginsnS *ginsn, gbbS *prev_bb,
+		 int *errp)
+{
+  gbbS *current_bb = NULL;
+  ginsnS *target_ginsn = NULL;
+  symbolS *taken_label;
+
+  while (ginsn)
+    {
+      /* Skip these as they may be right after a GINSN_TYPE_RETURN.
+	 For GINSN_TYPE_RETURN, we have already considered that as
+	 end of bb, and a logical exit from function.  */
+      if (GINSN_F_FUNC_END_P (ginsn))
+	{
+	  ginsn = ginsn->next;
+	  continue;
+	}
+
+      if (ginsn->visited)
+	{
+	  /* If the ginsn has been visited earlier, the bb must exist by now
+	     in the cfg.  */
+	  prev_bb = current_bb;
+	  current_bb = find_bb (gcfg, ginsn);
+	  gas_assert (current_bb);
+	  /* Add edge from the prev_bb.  */
+	  if (prev_bb)
+	    bb_add_edge (prev_bb, current_bb);
+	  break;
+	}
+      else if (current_bb && GINSN_F_USER_LABEL_P (ginsn))
+	{
+	  /* Create new bb starting at this label ginsn.  */
+	  prev_bb = current_bb;
+	  find_or_make_bb (func, gcfg, ginsn, prev_bb, errp);
+	  break;
+	}
+
+      if (current_bb == NULL)
+	{
+	  /* Create a new bb.  */
+	  current_bb = XCNEW (gbbS);
+	  cfg_add_bb (gcfg, current_bb);
+	  /* Add edge for the Not Taken, or Fall-through path.  */
+	  if (prev_bb)
+	    bb_add_edge (prev_bb, current_bb);
+	}
+
+      if (current_bb->first_ginsn == NULL)
+	current_bb->first_ginsn = ginsn;
+
+      ginsn->visited = true;
+      current_bb->num_ginsns++;
+      current_bb->last_ginsn = ginsn;
+
+      /* Note that BB is _not_ split on ginsn of type GINSN_TYPE_CALL.  */
+      if (ginsn->type == GINSN_TYPE_JUMP
+	  || ginsn->type == GINSN_TYPE_JUMP_COND
+	  || ginsn->type == GINSN_TYPE_RETURN)
+	{
+	  /* Indirect Jumps or direct jumps to symbols non-local to the
+	     function must not be seen here.  The caller must have already
+	     checked for that.  */
+	  gas_assert (!ginsn_indirect_jump_p (ginsn));
+	  if (ginsn->type == GINSN_TYPE_JUMP)
+	    gas_assert (ginsn_direct_local_jump_p (ginsn));
+
+	  /* Direct Jumps.  May include conditional or unconditional change of
+	     flow.  What is important for CFG creation is that the target be
+	     local to function.  */
+	  if (ginsn->type == GINSN_TYPE_JUMP_COND
+	      || ginsn_direct_local_jump_p (ginsn))
+	    {
+	      gas_assert (ginsn->src[0].type == GINSN_SRC_SYMBOL);
+	      taken_label = ginsn->src[0].sym;
+	      gas_assert (taken_label && S_IS_LOCAL (taken_label));
+
+	      /* Preserve the prev_bb to be the dominator bb as we are
+		 going to follow the taken path of the conditional branch
+		 soon.  */
+	      prev_bb = current_bb;
+
+	      /* Follow the target on the taken path.  */
+	      target_ginsn = label_ginsn_map_find (taken_label);
+	      /* Add the bb for the target of the taken branch.  */
+	      if (target_ginsn)
+		find_or_make_bb (func, gcfg, target_ginsn, prev_bb, errp);
+	      else
+		{
+		  *errp = GCFG_JLABEL_NOT_PRESENT;
+		  as_warn_where (ginsn->file, ginsn->line,
+				 _("missing label '%s' in func '%s' may result in imprecise cfg"),
+				 S_GET_NAME (taken_label), S_GET_NAME (func));
+		}
+	      /* Add the bb for the fall through path.  */
+	      find_or_make_bb (func, gcfg, ginsn->next, prev_bb, errp);
+	    }
+	 else if (ginsn->type == GINSN_TYPE_RETURN)
+	   {
+	     /* We'll come back to the ginsns following GINSN_TYPE_RETURN
+		from another path if they are indeed reachable code.  */
+	     break;
+	   }
+
+	 /* Current BB has been processed.  */
+	 current_bb = NULL;
+	}
+      ginsn = ginsn->next;
+    }
+
+  return current_bb;
+}
+
+static int
+gbbs_compare (const void *v1, const void *v2)
+{
+  const gbbS *bb1 = *(const gbbS **) v1;
+  const gbbS *bb2 = *(const gbbS **) v2;
+
+  if (bb1->first_ginsn->id < bb2->first_ginsn->id)
+    return -1;
+  else if (bb1->first_ginsn->id > bb2->first_ginsn->id)
+    return 1;
+  else if (bb1->first_ginsn->id == bb2->first_ginsn->id)
+    return 0;
+
+  return 0;
+}
+
+/* Traverse the list of ginsns for the function and warn if some
+   ginsns are not visited.
+
+   FIXME - this code assumes the caller has already performed a pass over
+   ginsns such that the reachable ginsns are already marked.  Revisit this - we
+   should ideally make this pass self-sufficient.  */
+
+static int
+ginsn_pass_warn_unreachable_code (symbolS *func, gcfgS *gcfg ATTRIBUTE_UNUSED,
+				  ginsnS *root_ginsn)
+{
+  ginsnS *ginsn;
+  bool unreach_p = false;
+
+  if (!gcfg || !func || !root_ginsn)
+    return 0;
+
+  ginsn = root_ginsn;
+
+  while (ginsn)
+    {
+      /* Some ginsns of type GINSN_TYPE_SYMBOL remain unvisited.  Some
+	 may even be excluded from the CFG as they are not reachable, given
+	 their function, e.g., user labels after return machine insn.  */
+      if (!ginsn->visited
+	  && !GINSN_F_FUNC_END_P (ginsn)
+	  && !GINSN_F_USER_LABEL_P (ginsn))
+	{
+	  unreach_p = true;
+	  break;
+	}
+      ginsn = ginsn->next;
+    }
+
+  if (unreach_p)
+    as_warn_where (ginsn->file, ginsn->line,
+		   _("GINSN: found unreachable code in func '%s'"),
+		   S_GET_NAME (func));
+
+  return unreach_p;
+}
+
+void
+gcfg_get_bbs_in_prog_order (gcfgS *gcfg, gbbS **prog_order_bbs)
+{
+  int i = 0;
+  gbbS *gbb;
+
+  if (!prog_order_bbs)
+    return;
+
+  cfg_for_each_bb (gcfg, gbb)
+    {
+      gas_assert (i < gcfg->num_gbbs);
+      prog_order_bbs[i++] = gbb;
+    }
+
+  qsort (prog_order_bbs, gcfg->num_gbbs, sizeof (gbbS *), gbbs_compare);
+}
+
+/* Build the control flow graph for the ginsns of the function.
+
+   It is important that the target adds an appropriate ginsn:
+     - GINSN_TYPE_JUMP,
+     - GINSN_TYPE_JUMP_COND,
+     - GINSN_TYPE_CALL,
+     - GINSN_TYPE_RET
+  at the associated points in the function.  The correctness of the CFG
+  depends on the accuracy of these 'change of flow instructions'.  */
+
+gcfgS *
+gcfg_build (symbolS *func, int *errp)
+{
+  gcfgS *gcfg;
+  ginsnS *first_ginsn;
+
+  gcfg = XCNEW (gcfgS);
+  first_ginsn = frchain_now->frch_ginsn_data->gins_rootP;
+  add_bb_at_ginsn (func, gcfg, first_ginsn, NULL /* prev_bb.  */, errp);
+
+  return gcfg;
+}
+
+void
+gcfg_cleanup (gcfgS **gcfgp)
+{
+  gcfgS *cfg;
+  gbbS *bb, *next_bb;
+  gedgeS *edge, *next_edge;
+
+  if (!gcfgp || !*gcfgp)
+    return;
+
+  cfg = *gcfgp;
+  bb = gcfg_get_rootbb (cfg);
+
+  while (bb)
+    {
+      next_bb = bb->next;
+
+      /* Cleanup all the edges.  */
+      edge = bb->out_gedges;
+      while (edge)
+	{
+	  next_edge = edge->next;
+	  free (edge);
+	  edge = next_edge;
+	}
+
+      gbb_cleanup (&bb);
+      bb = next_bb;
+    }
+
+  free (cfg);
+  *gcfgp = NULL;
+}
+
+gbbS *
+gcfg_get_rootbb (gcfgS *gcfg)
+{
+  gbbS *rootbb = NULL;
+
+  if (!gcfg || !gcfg->num_gbbs)
+    return NULL;
+
+  rootbb = gcfg->root_bb;
+
+  return rootbb;
+}
+
+void
+frch_ginsn_data_init (symbolS *func, symbolS *start_addr,
+		      enum ginsn_gen_mode gmode)
+{
+  /* FIXME - error out if prev object is not free'd ?  */
+  frchain_now->frch_ginsn_data = XCNEW (struct frch_ginsn_data);
+
+  frchain_now->frch_ginsn_data->mode = gmode;
+  /* Annotate with the current function symbol.  */
+  frchain_now->frch_ginsn_data->func = func;
+  /* Create a new start address symbol now.  */
+  frchain_now->frch_ginsn_data->start_addr = start_addr;
+  /* Assume the set of ginsn are apt for CFG creation, by default.  */
+  frchain_now->frch_ginsn_data->gcfg_apt_p = true;
+
+  frchain_now->frch_ginsn_data->label_ginsn_map = str_htab_create ();
+}
+
+void
+frch_ginsn_data_cleanup (void)
+{
+  ginsnS *ginsn = NULL;
+  ginsnS *next_ginsn = NULL;
+
+  ginsn = frchain_now->frch_ginsn_data->gins_rootP;
+  while (ginsn)
+    {
+      next_ginsn = ginsn->next;
+      ginsn_cleanup (&ginsn);
+      ginsn = next_ginsn;
+    }
+
+  if (frchain_now->frch_ginsn_data->label_ginsn_map)
+    htab_delete (frchain_now->frch_ginsn_data->label_ginsn_map);
+
+  free (frchain_now->frch_ginsn_data);
+  frchain_now->frch_ginsn_data = NULL;
+}
+
+/* Append GINSN to the list of ginsns for the current function being
+   assembled.  */
+
+int
+frch_ginsn_data_append (ginsnS *ginsn)
+{
+  ginsnS *last = NULL;
+  ginsnS *temp = NULL;
+  uint64_t id = 0;
+
+  if (!ginsn)
+    return 1;
+
+  if (frchain_now->frch_ginsn_data->gins_lastP)
+    id = frchain_now->frch_ginsn_data->gins_lastP->id;
+
+  /* Do the necessary preprocessing on the set of input GINSNs:
+       - Update each ginsn with its ID.
+     While you iterate, also keep gcfg_apt_p updated by checking whether any
+     ginsn is inappropriate for GCFG creation.  */
+  temp = ginsn;
+  while (temp)
+    {
+      temp->id = ++id;
+
+      if (ginsn_indirect_jump_p (temp)
+	  || (ginsn->type == GINSN_TYPE_JUMP
+	      && !ginsn_direct_local_jump_p (temp)))
+	frchain_now->frch_ginsn_data->gcfg_apt_p = false;
+
+      if (listing & LISTING_GINSN_SCFI)
+	listing_newline (ginsn_print (temp));
+
+      /* The input GINSN may be a linked list of multiple ginsns chained
+	 together.  Find the last ginsn in the input chain of ginsns.  */
+      last = temp;
+
+      temp = temp->next;
+    }
+
+  /* Link in the ginsn to the tail.  */
+  if (!frchain_now->frch_ginsn_data->gins_rootP)
+    frchain_now->frch_ginsn_data->gins_rootP = ginsn;
+  else
+    ginsn_link_next (frchain_now->frch_ginsn_data->gins_lastP, ginsn);
+
+  frchain_now->frch_ginsn_data->gins_lastP = last;
+
+  return 0;
+}
+
+enum ginsn_gen_mode
+frch_ginsn_gen_mode (void)
+{
+  enum ginsn_gen_mode gmode = GINSN_GEN_NONE;
+
+  if (frchain_now->frch_ginsn_data)
+    gmode = frchain_now->frch_ginsn_data->mode;
+
+  return gmode;
+}
+
+int
+ginsn_data_begin (symbolS *func)
+{
+  ginsnS *ginsn;
+
+  /* The previous block of asm must have been processed by now.  */
+  if (frchain_now->frch_ginsn_data)
+    as_bad (_("GINSN process for prev func not done"));
+
+  /* FIXME - hard code the mode to GINSN_GEN_SCFI.
+     This can be changed later when other passes on ginsns are formalised.  */
+  frch_ginsn_data_init (func, symbol_temp_new_now (), GINSN_GEN_SCFI);
+
+  /* Create and insert ginsn with function begin marker.  */
+  ginsn = ginsn_new_symbol_func_begin (func);
+  frch_ginsn_data_append (ginsn);
+
+  return 0;
+}
+
+int
+ginsn_data_end (symbolS *label)
+{
+  ginsnS *ginsn;
+  gbbS *root_bb;
+  gcfgS *gcfg = NULL;
+  symbolS *func;
+  int err = 0;
+
+  if (!frchain_now->frch_ginsn_data)
+    return err;
+
+  /* Insert Function end marker.  */
+  ginsn = ginsn_new_symbol_func_end (label);
+  frch_ginsn_data_append (ginsn);
+
+  func = frchain_now->frch_ginsn_data->func;
+
+  /* Build the cfg of ginsn(s) of the function.  */
+  if (!frchain_now->frch_ginsn_data->gcfg_apt_p)
+    {
+      as_warn (_("Untraceable control flow for func '%s'; Skipping SCFI"),
+	       S_GET_NAME (func));
+      goto end;
+    }
+
+  gcfg = gcfg_build (func, &err);
+
+  root_bb = gcfg_get_rootbb (gcfg);
+  if (!root_bb)
+    {
+      as_bad (_("Bad cfg of ginsn of func '%s'"), S_GET_NAME (func));
+      goto end;
+    }
+
+  /* Synthesize DWARF CFI and emit it.  */
+  err = scfi_synthesize_dw2cfi (func, gcfg, root_bb);
+  if (err)
+    goto end;
+  scfi_emit_dw2cfi (func);
+
+  /* Other passes, e.g., warn for unreachable code can be enabled too.  */
+  ginsn = frchain_now->frch_ginsn_data->gins_rootP;
+  ginsn_pass_warn_unreachable_code (func, gcfg, ginsn);
+
+end:
+  if (gcfg)
+    gcfg_cleanup (&gcfg);
+  frch_ginsn_data_cleanup ();
+  return err;
+}
+
+/* Add GINSN_TYPE_SYMBOL type ginsn for user-defined labels.  These may be
+   branch targets, and hence are necessary for control flow graph.  */
+
+void
+ginsn_frob_label (symbolS *label)
+{
+  ginsnS *label_ginsn;
+  const char *file;
+  unsigned int line;
+
+  if (frchain_now->frch_ginsn_data)
+    {
+      /* PS: Note how we keep the actual LABEL symbol as ginsn->sym.
+	 Take care to avoid inadvertent updates or cleanups of symbols.  */
+      label_ginsn = ginsn_new_symbol_user_label (label);
+      /* Keep the location updated.  */
+      file = as_where (&line);
+      ginsn_set_file_line (label_ginsn, file, line);
+
+      frch_ginsn_data_append (label_ginsn);
+
+      label_ginsn_map_insert (label, label_ginsn);
+    }
+}
+
+symbolS *
+ginsn_data_func_symbol (void)
+{
+  symbolS *func = NULL;
+
+  if (frchain_now->frch_ginsn_data)
+    func = frchain_now->frch_ginsn_data->func;
+
+  return func;
+}
+
+#else
+
+int
+ginsn_data_begin (symbolS *func ATTRIBUTE_UNUSED)
+{
+  as_bad (_("ginsn unsupported for target"));
+  return 1;
+}
+
+int
+ginsn_data_end (symbolS *label ATTRIBUTE_UNUSED)
+{
+  as_bad (_("ginsn unsupported for target"));
+  return 1;
+}
+
+void
+ginsn_frob_label (symbolS *sym ATTRIBUTE_UNUSED)
+{
+  return;
+}
+
+symbolS *
+ginsn_data_func_symbol (void)
+{
+  return NULL;
+}
+
+#endif  /* TARGET_USE_GINSN.  */
diff --git a/gas/ginsn.h b/gas/ginsn.h
new file mode 100644
index 00000000000..c930b5b2ff3
--- /dev/null
+++ b/gas/ginsn.h
@@ -0,0 +1,392 @@
+/* ginsn.h - GAS instruction representation.
+   Copyright (C) 2023 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 GINSN_H
+#define GINSN_H
+
+#include "as.h"
+
+/* Maximum number of source operands of a ginsn.  */
+#define GINSN_NUM_SRC_OPNDS   2
+
+/* A ginsn in printed in the following format:
+      "ginsn: OPCD SRC1, SRC2, DST"
+      "<-5->  <--------125------->"
+   where each of SRC1, SRC2, and DST are in the form:
+      "%rNN,"  (up to 5 chars)
+      "imm,"   (up to int32_t+1 chars)
+      "[%rNN+-imm]," (up to int32_t+9 chars)
+      Hence a max of 19 chars.  */
+
+#define GINSN_LISTING_OPND_LEN	40
+#define GINSN_LISTING_LEN 156
+
+enum ginsn_gen_mode
+{
+  GINSN_GEN_NONE,
+  /* Generate ginsns for program validation passes.  */
+  GINSN_GEN_FVAL,
+  /* Generate ginsns for synthesizing DWARF CFI.  */
+  GINSN_GEN_SCFI,
+};
+
+/* ginsn types.
+
+   GINSN_TYPE_PHANTOM are phantom ginsns.  They are used where there is no real
+   machine instruction counterpart, but a ginsn is needed only to carry
+   information to GAS.  For example, to carry an SCFI Op.
+
+   Note that, ginsns do not have a push / pop instructions.
+   Instead, following are used:
+      type=GINSN_TYPE_LOAD, src=GINSN_SRC_INDIRECT, REG_SP: Load from stack.
+      type=GINSN_TYPE_STORE, dst=GINSN_DST_INDIRECT, REG_SP: Store to stack.
+*/
+
+#define _GINSN_TYPES \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_SYMBOL, "SYM") \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_PHANTOM, "PHANTOM")  \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_ADD, "ADD")  \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_AND, "AND")  \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_CALL, "CALL") \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_JUMP, "JMP") \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_JUMP_COND, "JCC")  \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_MOV, "MOV")  \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_LOAD, "LOAD")  \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_STORE, "STORE")  \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_RETURN, "RET") \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_SUB, "SUB")  \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_OTHER, "OTH")
+
+enum ginsn_type
+{
+#define _GINSN_TYPE_ITEM(NAME, STR) NAME,
+  _GINSN_TYPES
+#undef _GINSN_TYPE_ITEM
+};
+
+#define _GINSN_SRC_TYPES \
+  _GINSN_SRC_TYPE_ITEM (GINSN_SRC_UNKNOWN, "UNK") \
+  _GINSN_SRC_TYPE_ITEM (GINSN_SRC_REG, "REG") \
+  _GINSN_SRC_TYPE_ITEM (GINSN_SRC_IMM, "IMM") \
+  _GINSN_SRC_TYPE_ITEM (GINSN_SRC_INDIRECT, "IND")  \
+  _GINSN_SRC_TYPE_ITEM (GINSN_SRC_SYMBOL, "SYM")
+
+enum ginsn_src_type
+{
+#define _GINSN_SRC_TYPE_ITEM(NAME, STR)	NAME,
+  _GINSN_SRC_TYPES
+#undef _GINSN_SRC_TYPE_ITEM
+};
+
+/* GAS instruction source operand representation.  */
+
+struct ginsn_src
+{
+  enum ginsn_src_type type;
+  /* DWARF register number.  */
+  uint32_t reg;
+  /* 32-bit immediate or disp for indirect memory access.  */
+  int32_t immdisp;
+  /* Src symbol.  May be needed for some control flow instructions.  */
+  symbolS *sym;
+};
+
+#define _GINSN_DST_TYPES  \
+  _GINSN_DST_TYPE_ITEM (GINSN_DST_UNKNOWN, "UNK") \
+  _GINSN_DST_TYPE_ITEM (GINSN_DST_REG, "REG") \
+  _GINSN_DST_TYPE_ITEM (GINSN_DST_INDIRECT, "IND")
+
+enum ginsn_dst_type
+{
+#define _GINSN_DST_TYPE_ITEM(NAME, STR) NAME,
+  _GINSN_DST_TYPES
+#undef _GINSN_DST_TYPE_ITEM
+};
+
+/* GAS instruction destination operand representation.  */
+
+struct ginsn_dst
+{
+  enum ginsn_dst_type type;
+  /* DWARF register number.  */
+  uint32_t reg;
+  /* 32-bit disp for indirect memory access.  */
+  int32_t disp;
+};
+
+/* Various flags for additional information per GAS instruction.  */
+
+/* Function begin or end symbol.  */
+#define GINSN_F_FUNC_MARKER	    0x1
+/* Identify real or implicit GAS insn.
+   Some targets employ CISC-like instructions.  Multiple ginsn's may be used
+   for a single machine instruction in some ISAs.  For some optimizations,
+   there is need to identify whether a ginsn, e.g., GINSN_TYPE_ADD or
+   GINSN_TYPE_SUB is a result of an user-specified instruction or not.  */
+#define GINSN_F_INSN_REAL	    0x2
+/* Identify if the GAS insn of type GINSN_TYPE_SYMBOL is due to a user-defined
+   label.  Each user-defined labels in a function will cause addition of a new
+   ginsn.  This simplifies control flow graph creation.
+   See htab_t label_ginsn_map usage.  */
+#define GINSN_F_USER_LABEL	    0x4
+/* Max bit position for flags (uint32_t).  */
+#define GINSN_F_MAX		    0x20
+
+#define GINSN_F_FUNC_BEGIN_P(ginsn)	    \
+  ((ginsn != NULL)			    \
+   && (ginsn->type == GINSN_TYPE_SYMBOL)    \
+   && (ginsn->flags & GINSN_F_FUNC_MARKER))
+
+/* PS: For ginsn associated with a user-defined symbol location,
+   GINSN_F_FUNC_MARKER is unset, but GINSN_F_USER_LABEL is set.  */
+#define GINSN_F_FUNC_END_P(ginsn)	    \
+  ((ginsn != NULL)			    \
+   && (ginsn->type == GINSN_TYPE_SYMBOL)    \
+   && !(ginsn->flags & GINSN_F_FUNC_MARKER) \
+   && !(ginsn->flags & GINSN_F_USER_LABEL))
+
+#define GINSN_F_INSN_REAL_P(ginsn)	    \
+  ((ginsn != NULL)			    \
+   && (ginsn->flags & GINSN_F_INSN_REAL))
+
+#define GINSN_F_USER_LABEL_P(ginsn)	    \
+  ((ginsn != NULL)			    \
+   && (ginsn->type == GINSN_TYPE_SYMBOL)    \
+   && !(ginsn->flags & GINSN_F_FUNC_MARKER) \
+   && (ginsn->flags & GINSN_F_USER_LABEL))
+
+typedef struct ginsn ginsnS;
+typedef struct scfi_op scfi_opS;
+typedef struct scfi_state scfi_stateS;
+
+/* GAS generic instruction.
+
+   Generic instructions are used by GAS to abstract out the binary machine
+   instructions.  In other words, ginsn is a target/ABI independent internal
+   representation for GAS.  Note that, depending on the target, there may be
+   more than one ginsn per binary machine instruction.
+
+   ginsns can be used by GAS to perform validations, or even generate
+   additional information like, sythesizing DWARF CFI for hand-written asm.  */
+
+struct ginsn
+{
+  enum ginsn_type type;
+  /* GAS instructions are simple instructions with GINSN_NUM_SRC_OPNDS number
+     of source operands and one destination operand at this time.  */
+  struct ginsn_src src[GINSN_NUM_SRC_OPNDS];
+  struct ginsn_dst dst;
+  /* Additional information per instruction.  */
+  uint32_t flags;
+  /* Symbol.  For ginsn of type other than GINSN_TYPE_SYMBOL, this identifies
+     the end of the corresponding machine instruction in the .text segment.
+     These symbols are created anew by the targets and are not used elsewhere
+     in GAS.  The only exception is some ginsns of type GINSN_TYPE_SYMBOL, when
+     generated for the user-defined labels.  See ginsn_frob_label.  */
+  symbolS *sym;
+  /* Identifier (linearly increasing natural number) for each ginsn.  Used as
+     a proxy for program order of ginsns.  */
+  uint64_t id;
+  /* Location information for user-interfacing messaging.  Only ginsns with
+     GINSN_F_FUNC_BEGIN_P and GINSN_F_FUNC_END_P may present themselves with no
+     file or line information.  */
+  const char *file;
+  unsigned int line;
+
+  /* Information needed for synthesizing CFI.  */
+  scfi_opS **scfi_ops;
+  uint32_t num_scfi_ops;
+
+  /* Flag to keep track of visited instructions for CFG creation.  */
+  bool visited;
+
+  ginsnS *next; /* A linked list.  */
+};
+
+struct ginsn_src *ginsn_get_src1 (ginsnS *ginsn);
+struct ginsn_src *ginsn_get_src2 (ginsnS *ginsn);
+struct ginsn_dst *ginsn_get_dst (ginsnS *ginsn);
+
+uint32_t ginsn_get_src_reg (struct ginsn_src *src);
+enum ginsn_src_type ginsn_get_src_type (struct ginsn_src *src);
+uint32_t ginsn_get_src_disp (struct ginsn_src *src);
+uint32_t ginsn_get_src_imm (struct ginsn_src *src);
+
+uint32_t ginsn_get_dst_reg (struct ginsn_dst *dst);
+enum ginsn_dst_type ginsn_get_dst_type (struct ginsn_dst *dst);
+int32_t ginsn_get_dst_disp (struct ginsn_dst *dst);
+
+/* Data object for book-keeping information related to GAS generic
+   instructions.  */
+struct frch_ginsn_data
+{
+  /* Mode for GINSN creation.  */
+  enum ginsn_gen_mode mode;
+  /* Head of the list of ginsns.  */
+  ginsnS *gins_rootP;
+  /* Tail of the list of ginsns.  */
+  ginsnS *gins_lastP;
+  /* Function symbol.  */
+  symbolS *func;
+  /* Start address of the function.  */
+  symbolS *start_addr;
+  /* User-defined label to ginsn mapping.  */
+  htab_t label_ginsn_map;
+  /* Is the list of ginsn apt for creating CFG.  */
+  bool gcfg_apt_p;
+};
+
+int ginsn_data_begin (symbolS *func);
+int ginsn_data_end (symbolS *label);
+symbolS *ginsn_data_func_symbol (void);
+void ginsn_frob_label (symbolS *sym);
+
+void frch_ginsn_data_init (symbolS *func, symbolS *start_addr,
+			   enum ginsn_gen_mode gmode);
+void frch_ginsn_data_cleanup (void);
+int frch_ginsn_data_append (ginsnS *ginsn);
+enum ginsn_gen_mode frch_ginsn_gen_mode (void);
+
+void label_ginsn_map_insert (symbolS *label, ginsnS *ginsn);
+ginsnS *label_ginsn_map_find (symbolS *label);
+
+ginsnS *ginsn_new_symbol_func_begin (symbolS *sym);
+ginsnS *ginsn_new_symbol_func_end (symbolS *sym);
+ginsnS *ginsn_new_symbol_user_label (symbolS *sym);
+
+ginsnS* ginsn_new_phantom (symbolS *sym);
+ginsnS *ginsn_new_symbol (symbolS *sym, bool real_p);
+ginsnS *ginsn_new_add (symbolS *sym, bool real_p,
+		       enum ginsn_src_type src1_type, uint32_t src1_val, int32_t src1_disp,
+		       enum ginsn_src_type src2_type, uint32_t src2_val, int32_t src2_disp,
+		       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp);
+ginsnS *ginsn_new_and (symbolS *sym, bool real_p,
+		       enum ginsn_src_type src1_type, uint32_t src1_val, int32_t src1_disp,
+		       enum ginsn_src_type src2_type, uint32_t src2_val, int32_t src2_disp,
+		       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp);
+ginsnS *ginsn_new_call (symbolS *sym, bool real_p,
+			enum ginsn_src_type src_type, uint32_t src_val,
+			symbolS *src_text_sym);
+ginsnS *ginsn_new_jump (symbolS *sym, bool real_p,
+			enum ginsn_src_type src_type, uint32_t src_val,
+			symbolS *src_ginsn_sym);
+ginsnS *ginsn_new_jump_cond (symbolS *sym, bool real_p,
+			     enum ginsn_src_type src_type, uint32_t src_val,
+			     symbolS *src_ginsn_sym);
+ginsnS *ginsn_new_mov (symbolS *sym, bool real_p,
+		       enum ginsn_src_type src_type, uint32_t src_reg, int32_t src_disp,
+		       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp);
+ginsnS *ginsn_new_store (symbolS *sym, bool real_p,
+			 enum ginsn_src_type src_type, uint32_t src_reg,
+			 enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp);
+ginsnS *ginsn_new_load (symbolS *sym, bool real_p,
+			enum ginsn_src_type src_type, uint32_t src_reg, int32_t src_disp,
+			enum ginsn_dst_type dst_type, uint32_t dst_reg);
+ginsnS *ginsn_new_sub (symbolS *sym, bool real_p,
+		       enum ginsn_src_type src1_type, uint32_t src1_val, int32_t src1_disp,
+		       enum ginsn_src_type src2_type, uint32_t src2_val, int32_t src2_disp,
+		       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp);
+ginsnS *ginsn_new_other (symbolS *sym, bool real_p,
+			 enum ginsn_src_type src1_type, uint32_t src1_val,
+			 enum ginsn_src_type src2_type, uint32_t src2_val,
+			 enum ginsn_dst_type dst_type, uint32_t dst_reg);
+ginsnS *ginsn_new_return (symbolS *sym, bool real_p);
+
+void ginsn_set_file_line (ginsnS *ginsn, const char *file, unsigned int line);
+
+bool ginsn_track_reg_p (uint32_t dw2reg, enum ginsn_gen_mode);
+
+int ginsn_link_next (ginsnS *ginsn, ginsnS *next);
+
+enum gcfg_err_code
+{
+  GCFG_OK = 0,
+  GCFG_JLABEL_NOT_PRESENT = 1, /* Warning-level code.  */
+};
+
+typedef struct gbb gbbS;
+typedef struct gedge gedgeS;
+
+/* GBB - Basic block of generic GAS instructions.  */
+
+struct gbb
+{
+  ginsnS *first_ginsn;
+  ginsnS *last_ginsn;
+  int64_t num_ginsns;
+
+  /* Identifier (linearly increasing natural number) for each gbb.  Added for
+     debugging purpose only.  */
+  int64_t id;
+
+  bool visited;
+
+  int32_t num_out_gedges;
+  gedgeS *out_gedges;
+
+  /* FIXME - keep a separate map or add like this.  */
+  /* SCFI state at the entry of basic block.  */
+  scfi_stateS *entry_state;
+  /* SCFI state at the exit of basic block.  */
+  scfi_stateS *exit_state;
+  /* A linked list.  In order of addition.  */
+  gbbS *next;
+};
+
+struct gedge
+{
+  gbbS *dst_bb;
+  /* A linked list.  In order of addition.  */
+  gedgeS *next;
+  bool visited;
+};
+
+/* Control flow graph of generic GAS instructions.  */
+
+struct gcfg
+{
+  int64_t num_gbbs;
+  gbbS *root_bb;
+};
+
+typedef struct gcfg gcfgS;
+
+#define bb_for_each_insn(bb, ginsn)  \
+  for (ginsn = bb->first_ginsn; ginsn; \
+       ginsn = (ginsn != bb->last_ginsn) ? ginsn->next : NULL)
+
+#define bb_for_each_edge(bb, edge) \
+  for (edge = (edge == NULL) ? bb->out_gedges : edge; edge; edge = edge->next)
+
+#define cfg_for_each_bb(cfg, bb) \
+  for (bb = cfg->root_bb; bb; bb = bb->next)
+
+#define bb_get_first_ginsn(bb)	  \
+  (bb->first_ginsn)
+
+#define bb_get_last_ginsn(bb)	  \
+  (bb->last_ginsn)
+
+gcfgS *gcfg_build (symbolS *func, int *errp);
+void gcfg_cleanup (gcfgS **gcfg);
+gbbS *gcfg_get_rootbb (gcfgS *gcfg);
+void gcfg_get_bbs_in_prog_order (gcfgS *gcfg, gbbS **prog_order_bbs);
+
+#endif /* GINSN_H.  */
diff --git a/gas/listing.h b/gas/listing.h
index a2cc1f51958..ff89f2219dd 100644
--- a/gas/listing.h
+++ b/gas/listing.h
@@ -29,6 +29,7 @@
 #define LISTING_NOCOND    32
 #define LISTING_MACEXP    64
 #define LISTING_GENERAL  128
+#define LISTING_GINSN_SCFI  256
 
 #define LISTING_DEFAULT    (LISTING_LISTING | LISTING_HLL | LISTING_SYMBOLS)
 
diff --git a/gas/read.c b/gas/read.c
index 9068072493a..8ffc3d35ce1 100644
--- a/gas/read.c
+++ b/gas/read.c
@@ -42,6 +42,7 @@
 #include "codeview.h"
 #include "wchar.h"
 #include "filenames.h"
+#include "ginsn.h"
 
 #include <limits.h>
 
@@ -1382,6 +1383,9 @@ read_a_source_file (const char *name)
     }
 #endif
 
+  if (flag_synth_cfi)
+    ginsn_data_end (symbol_temp_new_now ());
+
 #ifdef md_cleanup
   md_cleanup ();
 #endif
diff --git a/gas/scfi.c b/gas/scfi.c
new file mode 100644
index 00000000000..ca02ebe42ae
--- /dev/null
+++ b/gas/scfi.c
@@ -0,0 +1,1222 @@
+/* scfi.c - Support for synthesizing DWARF CFI for hand-written asm.
+   Copyright (C) 2023 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 "scfi.h"
+#include "subsegs.h"
+#include "scfidw2gen.h"
+#include "dw2gencfi.h"
+
+# if defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN)
+
+/* Beyond the target defined number of registers to be tracked (SCFI_NUM_REGS),
+   keep the next register ID, in sequence, for REG_CFA.  */
+#define REG_CFA	      (SCFI_NUM_REGS+1)
+/* Define the total number of registers being tracked.  Used as index into an
+   array of cfi_reglocS.  */
+#define MAX_NUM_SCFI_REGS   (REG_CFA+1)
+
+enum cfi_reglocstate
+{
+  CFI_UNDEFINED,
+  CFI_IN_REG,
+  CFI_ON_STACK
+};
+
+/* Location at which CFI register is saved.
+
+   A CFI register (callee-saved registers, RA/LR) are always an offset from
+   the CFA.  REG_CFA itself, however, may have REG_SP or REG_FP as base
+   register.  Hence, keep the base reg ID and offset per tracked register.  */
+
+struct cfi_regloc
+{
+  /* Base reg ID (DWARF register number).  */
+  uint32_t base;
+  /* Location as offset from the CFA.  */
+  int32_t offset;
+  /* Current state of the CFI register.  */
+  enum cfi_reglocstate state;
+};
+
+typedef struct cfi_regloc cfi_reglocS;
+
+struct scfi_op_data
+{
+  const char *name;
+};
+
+typedef struct scfi_op_data scfi_op_dataS;
+
+/* SCFI operation.
+
+   An SCFI operation represents a single atomic change to the SCFI state.
+   This can also be understood as an abstraction for what eventually gets
+   emitted as a DWARF CFI operation.  */
+
+struct scfi_op
+{
+  /* An SCFI op updates the state of either the CFA or other tracked
+     (callee-saved, REG_SP etc) registers.  'reg' is in the DWARF register
+     number space and must be strictly less than MAX_NUM_SCFI_REGS.  */
+  uint32_t reg;
+  /* Location of the reg.  */
+  cfi_reglocS loc;
+  /* DWARF CFI opcode.  */
+  uint32_t dw2cfi_op;
+  /* Some SCFI ops, e.g., for CFI_label, may need to carry additional data.  */
+  scfi_op_dataS *op_data;
+  /* A linked list.  */
+  struct scfi_op *next;
+};
+
+/* SCFI State - accumulated unwind information at a PC.
+
+   SCFI state is the accumulated unwind information encompassing:
+      - REG_SP, REG_FP,
+      - RA, and
+      - all callee-saved registers.
+
+    Note that SCFI_NUM_REGS is target/ABI dependent and is provided by the
+    backends.  The backend must also identify the REG_SP, and REG_FP
+    registers.  */
+
+struct scfi_state
+{
+  cfi_reglocS regs[MAX_NUM_SCFI_REGS];
+  cfi_reglocS scratch[MAX_NUM_SCFI_REGS];
+  /* Current stack size.  */
+  int32_t stack_size;
+  /* Whether the stack size is known.
+     Stack size may become untraceable depending on the specific stack
+     manipulation machine instruction, e.g., rsp = rsp op reg instruction
+     makes the stack size untraceable.  */
+  bool traceable_p;
+};
+
+/* Initialize a new SCFI op.  */
+
+static scfi_opS *
+init_scfi_op (void)
+{
+  scfi_opS *op = XCNEW (scfi_opS);
+
+  return op;
+}
+
+void
+scfi_ops_cleanup (scfi_opS **head)
+{
+  scfi_opS *op;
+  scfi_opS *next;
+
+  if (!head || !*head)
+    return;
+
+  op = *head;
+  next = op->next;
+
+  while (op)
+    {
+      free (op);
+      op = next;
+      next = op ? op->next : NULL;
+    }
+}
+
+/* Compare two SCFI states.  */
+
+static int
+cmp_scfi_state (scfi_stateS *state1, scfi_stateS *state2)
+{
+  int ret;
+
+  if (!state1 || !state2)
+    ret = 1;
+
+  /* Skip comparing the scratch[] value of registers.  The user visible
+     unwind information is derived from the regs[] from the SCFI state.  */
+  ret = memcmp (state1->regs, state2->regs,
+		sizeof (cfi_reglocS) * MAX_NUM_SCFI_REGS);
+  ret |= state1->stack_size != state2->stack_size;
+  ret |= state1->traceable_p != state2->traceable_p;
+
+  return ret;
+}
+
+#if 0
+static void
+scfi_state_update_reg (scfi_stateS *state, uint32_t dst, uint32_t base,
+		       int32_t offset)
+{
+  if (dst >= MAX_NUM_SCFI_REGS)
+    return;
+
+  state->regs[dst].base = base;
+  state->regs[dst].offset = offset;
+}
+#endif
+
+/* Update the SCFI state of REG as available on execution stack at OFFSET
+   from REG_CFA (BASE).
+
+   Note that BASE must be REG_CFA, because any other base (REG_SP, REG_FP)
+   is by definition transitory in the function.  */
+
+static void
+scfi_state_save_reg (scfi_stateS *state, uint32_t reg, uint32_t base,
+		     int32_t offset)
+{
+  if (reg >= MAX_NUM_SCFI_REGS)
+    return;
+
+  gas_assert (base == REG_CFA);
+
+  state->regs[reg].base = base;
+  state->regs[reg].offset = offset;
+  state->regs[reg].state = CFI_ON_STACK;
+}
+
+static void
+scfi_state_restore_reg (scfi_stateS *state, uint32_t reg)
+{
+  if (reg >= MAX_NUM_SCFI_REGS)
+    return;
+
+  /* Sanity check.  See Rule 4.  */
+  gas_assert (state->regs[reg].state == CFI_ON_STACK);
+  gas_assert (state->regs[reg].base == REG_CFA);
+
+  state->regs[reg].base = reg;
+  state->regs[reg].offset = 0;
+  /* PS: the register may still be on stack much after the restore, but the
+     SCFI state keeps the state as 'in register'.  */
+  state->regs[reg].state = CFI_IN_REG;
+}
+
+/* Identify if the given GAS instruction GINSN saves a register
+   (of interest) on stack.  */
+
+static bool
+ginsn_scfi_save_reg_p (ginsnS *ginsn, scfi_stateS *state)
+{
+  bool save_reg_p = false;
+  struct ginsn_src *src;
+  struct ginsn_dst *dst;
+
+  src = ginsn_get_src1 (ginsn);
+  dst = ginsn_get_dst (ginsn);
+
+  /* The first save to stack of callee-saved register is deemed as
+     register save.  */
+  if (!ginsn_track_reg_p (ginsn_get_src_reg (src), GINSN_GEN_SCFI)
+      || state->regs[ginsn_get_src_reg (src)].state == CFI_ON_STACK)
+    return save_reg_p;
+
+  /* A register save insn may be an indirect mov.  */
+  if (ginsn->type == GINSN_TYPE_MOV
+      && ginsn_get_dst_type (dst) == GINSN_DST_INDIRECT
+      && (ginsn_get_dst_reg (dst) == REG_SP
+	  || (ginsn_get_dst_reg (dst) == REG_FP
+	      && state->regs[REG_CFA].base == REG_FP)))
+    save_reg_p = true;
+  /* or an explicit store to stack.  */
+  else if (ginsn->type == GINSN_TYPE_STORE
+	   && ginsn_get_dst_type (dst) == GINSN_DST_INDIRECT
+	   && ginsn_get_dst_reg (dst) == REG_SP)
+    save_reg_p = true;
+
+  return save_reg_p;
+}
+
+/* Identify if the given GAS instruction GINSN restores a register
+   (of interest) on stack.  */
+
+static bool
+ginsn_scfi_restore_reg_p (ginsnS *ginsn, scfi_stateS *state)
+{
+  bool restore_reg_p = false;
+  struct ginsn_dst *dst;
+  struct ginsn_src *src1;
+
+  dst = ginsn_get_dst (ginsn);
+  src1 = ginsn_get_src1 (ginsn);
+
+  if (!ginsn_track_reg_p (ginsn_get_dst_reg (dst), GINSN_GEN_SCFI))
+    return restore_reg_p;
+
+  /* A register restore insn may be an indirect mov.  */
+  if (ginsn->type == GINSN_TYPE_MOV
+      && ginsn_get_src_type (src1) == GINSN_SRC_INDIRECT
+      && (ginsn_get_src_reg (src1) == REG_SP
+	  || (ginsn_get_src_reg (src1) == REG_FP
+	      && state->regs[REG_CFA].base == REG_FP)))
+    restore_reg_p = true;
+  /* or an explicit load from stack.  */
+  else if (ginsn->type == GINSN_TYPE_LOAD
+	   && ginsn_get_src_type (src1) == GINSN_SRC_INDIRECT
+	   && ginsn_get_src_reg (src1) == REG_SP)
+    restore_reg_p = true;
+
+  return restore_reg_p;
+}
+
+/* Append the SCFI operation OP to the list of SCFI operations in the
+   given GINSN.  */
+
+static int
+ginsn_append_scfi_op (ginsnS *ginsn, scfi_opS *op)
+{
+  scfi_opS *sop;
+
+  if (!ginsn || !op)
+    return 1;
+
+  if (!ginsn->scfi_ops)
+    {
+      ginsn->scfi_ops = XCNEW (scfi_opS *);
+      *ginsn->scfi_ops = op;
+    }
+  else
+    {
+      /* Add to tail.  Most ginsns have a single SCFI operation,
+	 so this traversal for every insertion is acceptable for now.  */
+      sop = *ginsn->scfi_ops;
+      while (sop->next)
+	sop = sop->next;
+
+      sop->next = op;
+    }
+  ginsn->num_scfi_ops++;
+
+  return 0;
+}
+
+static void
+scfi_op_add_def_cfa_reg (scfi_stateS *state, ginsnS *ginsn, uint32_t reg)
+{
+  scfi_opS *op = NULL;
+
+  state->regs[REG_CFA].base = reg;
+
+  op = init_scfi_op ();
+
+  op->dw2cfi_op = DW_CFA_def_cfa_register;
+  op->reg = REG_CFA;
+  op->loc = state->regs[REG_CFA];
+
+  ginsn_append_scfi_op (ginsn, op);
+}
+
+static void
+scfi_op_add_cfa_offset_inc (scfi_stateS *state, ginsnS *ginsn, int32_t num)
+{
+  scfi_opS *op = NULL;
+
+  state->regs[REG_CFA].offset -= num;
+
+  op = init_scfi_op ();
+
+  op->dw2cfi_op = DW_CFA_def_cfa_offset;
+  op->reg = REG_CFA;
+  op->loc = state->regs[REG_CFA];
+
+  ginsn_append_scfi_op (ginsn, op);
+}
+
+static void
+scfi_op_add_cfa_offset_dec (scfi_stateS *state, ginsnS *ginsn, int32_t num)
+{
+  scfi_opS *op = NULL;
+
+  state->regs[REG_CFA].offset += num;
+
+  op = init_scfi_op ();
+
+  op->dw2cfi_op = DW_CFA_def_cfa_offset;
+  op->reg = REG_CFA;
+  op->loc = state->regs[REG_CFA];
+
+  ginsn_append_scfi_op (ginsn, op);
+}
+
+static void
+scfi_op_add_def_cfa (scfi_stateS *state, ginsnS *ginsn, uint32_t reg,
+		     int32_t num)
+{
+  scfi_opS *op = NULL;
+
+  state->regs[REG_CFA].base = reg;
+  state->regs[REG_CFA].offset = num;
+
+  op = init_scfi_op ();
+
+  op->dw2cfi_op = DW_CFA_def_cfa;
+  op->reg = REG_CFA;
+  op->loc = state->regs[REG_CFA];
+
+  ginsn_append_scfi_op (ginsn, op);
+}
+
+static void
+scfi_op_add_cfi_offset (scfi_stateS *state, ginsnS *ginsn, uint32_t reg)
+{
+  scfi_opS *op = NULL;
+
+  op = init_scfi_op ();
+
+  op->dw2cfi_op = DW_CFA_offset;
+  op->reg = reg;
+  op->loc = state->regs[reg];
+
+  ginsn_append_scfi_op (ginsn, op);
+}
+
+static void
+scfi_op_add_cfa_restore (ginsnS *ginsn, uint32_t reg)
+{
+  scfi_opS *op = NULL;
+
+  op = init_scfi_op ();
+
+  op->dw2cfi_op = DW_CFA_restore;
+  op->reg = reg;
+  op->loc.base = -1; /* FIXME invalidate.  */
+  op->loc.offset = 0;
+
+  ginsn_append_scfi_op (ginsn, op);
+}
+
+static void
+scfi_op_add_cfi_remember_state (ginsnS *ginsn)
+{
+  scfi_opS *op = NULL;
+
+  op = init_scfi_op ();
+
+  op->dw2cfi_op = DW_CFA_remember_state;
+
+  ginsn_append_scfi_op (ginsn, op);
+}
+
+static void
+scfi_op_add_cfi_restore_state (ginsnS *ginsn)
+{
+  scfi_opS *op = NULL;
+
+  op = init_scfi_op ();
+
+  op->dw2cfi_op = DW_CFA_restore_state;
+
+  /* FIXME - add to the beginning of the scfi_ops.  */
+  ginsn_append_scfi_op (ginsn, op);
+}
+
+void
+scfi_op_add_cfi_label (ginsnS *ginsn, const char *name)
+{
+  scfi_opS *op = NULL;
+
+  op = init_scfi_op ();
+  op->dw2cfi_op = CFI_label;
+  op->op_data = XCNEW (scfi_op_dataS);
+  op->op_data->name = name;
+
+  ginsn_append_scfi_op (ginsn, op);
+}
+
+void
+scfi_op_add_signal_frame (ginsnS *ginsn)
+{
+  scfi_opS *op = NULL;
+
+  op = init_scfi_op ();
+  op->dw2cfi_op = CFI_signal_frame;
+
+  ginsn_append_scfi_op (ginsn, op);
+}
+
+static int
+verify_heuristic_traceable_reg_fp (ginsnS *ginsn, scfi_stateS *state)
+{
+  /* The function uses this variable to issue error to user right away.  */
+  int fp_traceable_p = 0;
+  struct ginsn_dst *dst;
+  struct ginsn_src *src1;
+  struct ginsn_src *src2;
+
+  src1 = ginsn_get_src1 (ginsn);
+  src2 = ginsn_get_src2 (ginsn);
+  dst = ginsn_get_dst (ginsn);
+
+  /* Stack manipulation can be done in a variety of ways.  A program may
+     allocate stack statically or may perform dynamic stack allocation in
+     the prologue.
+
+     The SCFI machinery in GAS is based on some heuristics:
+
+       - Rule 3 If the base register for CFA tracking is REG_FP, the program
+       must not clobber REG_FP, unless it is for switch to REG_SP based CFA
+       tracking (via say, a pop %rbp in X86).  */
+
+  /* Check all applicable instructions with dest REG_FP, when the CFA base
+     register is REG_FP.  */
+  if (state->regs[REG_CFA].base == REG_FP && ginsn_get_dst_reg (dst) == REG_FP)
+    {
+      /* Excuse the add/sub with imm usage: They are OK.  */
+      if ((ginsn->type == GINSN_TYPE_ADD || ginsn->type == GINSN_TYPE_SUB)
+	  && ginsn_get_src_reg (src1) == REG_FP
+	  && ginsn_get_src_type (src2) == GINSN_SRC_IMM)
+	fp_traceable_p = 0;
+      /* REG_FP restore is OK too.  */
+      else if (ginsn->type == GINSN_TYPE_LOAD)
+	fp_traceable_p = 0;
+      /* mov's to memory with REG_FP base do not make REG_FP untraceable.  */
+      else if (ginsn_get_dst_type (dst) == GINSN_DST_INDIRECT
+	       && (ginsn->type == GINSN_TYPE_MOV
+		   || ginsn->type == GINSN_TYPE_STORE))
+	fp_traceable_p = 0;
+      /* Manipulations of the values possibly on stack are OK too.  */
+      else if ((ginsn->type == GINSN_TYPE_ADD || ginsn->type == GINSN_TYPE_SUB
+		|| ginsn->type == GINSN_TYPE_AND)
+	       && ginsn_get_dst_type (dst) == GINSN_DST_INDIRECT)
+	fp_traceable_p = 0;
+      /* All other ginsns with REG_FP as destination make REG_FP not
+	 traceable.  */
+      else
+	fp_traceable_p = 1;
+    }
+
+  if (fp_traceable_p)
+    as_bad_where (ginsn->file, ginsn->line,
+		  _("SCFI: usage of REG_FP as scratch not supported"));
+
+  return fp_traceable_p;
+}
+
+static int
+verify_heuristic_traceable_stack_manipulation (ginsnS *ginsn,
+					       scfi_stateS *state)
+{
+  /* The function uses this variable to issue error to user right away.  */
+  int sp_untraceable_p = 0;
+  bool possibly_untraceable = false;
+  struct ginsn_dst *dst;
+  struct ginsn_src *src1;
+  struct ginsn_src *src2;
+
+  src1 = ginsn_get_src1 (ginsn);
+  src2 = ginsn_get_src2 (ginsn);
+  dst = ginsn_get_dst (ginsn);
+
+  /* Stack manipulation can be done in a variety of ways.  A program may
+     allocate stack statically in prologue or may need to do dynamic stack
+     allocation.
+
+     The SCFI machinery in GAS is based on some heuristics:
+
+       - Rule 1 The base register for CFA tracking may be either REG_SP or
+       REG_FP.
+
+       - Rule 2 If the base register for CFA tracking is REG_SP, the precise
+       amount of stack usage (and hence, the value of rsp) must be known at
+       all times.  */
+
+  if (ginsn->type == GINSN_TYPE_MOV
+      && ginsn_get_dst_type (dst) == GINSN_DST_REG
+      && ginsn_get_dst_reg (dst) == REG_SP
+      && ginsn_get_src_type (src1) == GINSN_SRC_REG
+      /* Exclude mov %rbp, %rsp from this check.  */
+      && ginsn_get_src_reg (src1) != REG_FP)
+    {
+      /* mov %reg, %rsp.  */
+      /* A previous mov %rsp, %reg must have been seen earlier for this to be
+	 an OK for stack manipulation.  */
+      if (state->scratch[ginsn_get_src_reg (src1)].base != REG_CFA
+	  || state->scratch[ginsn_get_src_reg (src1)].state != CFI_IN_REG)
+	{
+	  possibly_untraceable = true;
+	}
+    }
+  /* Check add/sub/and insn usage when CFA base register is REG_SP.
+     Any stack size manipulation, including stack realignment is not allowed
+     if CFA base register is REG_SP.  */
+  else if (ginsn_get_dst_type (dst) == GINSN_DST_REG
+	   && ginsn_get_dst_reg (dst) == REG_SP
+	   && (((ginsn->type == GINSN_TYPE_ADD || ginsn->type == GINSN_TYPE_SUB)
+		&& ginsn_get_src_type (src2) != GINSN_SRC_IMM)
+	       || ginsn->type == GINSN_TYPE_AND))
+    possibly_untraceable = true;
+  /* If a register save operation is seen when REG_SP is untraceable,
+     CFI cannot be synthesized for register saves, hence bail out.  */
+  else if (ginsn_scfi_save_reg_p (ginsn, state) && !state->traceable_p)
+    {
+      sp_untraceable_p = 1;
+      /* If, however, the register save is an REG_FP-based, indirect mov
+	 like: mov reg, disp(%rbp) and CFA base register is REG_BP,
+	 untraceable REG_SP is not a problem.  */
+      if (ginsn->type == GINSN_TYPE_MOV
+	  && ginsn_get_dst_type (dst) == GINSN_DST_INDIRECT
+	  && (ginsn_get_dst_reg (dst) == REG_FP
+	      && state->regs[REG_CFA].base == REG_FP))
+	sp_untraceable_p = 0;
+    }
+  else if (ginsn_scfi_restore_reg_p (ginsn, state) && !state->traceable_p)
+    {
+      if (ginsn->type == GINSN_TYPE_MOV
+	  && ginsn_get_dst_type (dst) == GINSN_DST_INDIRECT
+	  && (ginsn_get_src_reg (src1) == REG_SP
+	      || (ginsn_get_src_reg (src1) == REG_FP
+		  && state->regs[REG_CFA].base != REG_FP)))
+	sp_untraceable_p = 1;
+    }
+
+  if (possibly_untraceable)
+    {
+      /* See Rule 2.  For SP-based CFA, this makes CFA tracking not possible.
+	 Propagate now to caller.  */
+      if (state->regs[REG_CFA].base == REG_SP)
+	sp_untraceable_p = 1;
+      else if (state->traceable_p)
+	{
+	  /* An extension of Rule 2.
+	     For FP-based CFA, this may be a problem *if* certain specific
+	     changes to the SCFI state are seen beyond this point, e.g.,
+	     register save / restore from stack.  */
+	  gas_assert (state->regs[REG_CFA].base == REG_FP);
+	  /* Simply make a note in the SCFI state object for now and
+	     continue.  Indicate an error when register save / restore
+	     for callee-saved registers is seen.  */
+	  sp_untraceable_p = 0;
+	  state->traceable_p = false;
+	}
+    }
+
+  if (sp_untraceable_p)
+    as_bad_where (ginsn->file, ginsn->line,
+		  _("SCFI: unsupported stack manipulation pattern"));
+
+  return sp_untraceable_p;
+}
+
+static int
+verify_heuristic_symmetrical_restore_reg (scfi_stateS *state, ginsnS* ginsn)
+{
+  int sym_restore = true;
+  int32_t expected_offset = 0;
+  struct ginsn_src *src1;
+  struct ginsn_dst *dst;
+  uint32_t reg;
+
+  /* Rule 4: Save and Restore of callee-saved registers must be symmetrical.
+     It is expected that value of the saved register is restored correctly.
+     E.g.,
+	push  reg1
+	push  reg2
+	...
+	body of func which uses reg1 , reg2 as scratch,
+	and may be even spills them to stack.
+	...
+	pop   reg2
+	pop   reg1
+     It is difficult to verify the Rule 4 in all cases.  For the SCFI machinery,
+     it is difficult to separate prologue-epilogue from the body of the function
+
+     Hence, the SCFI machinery at this time, should only warn on an asymetrical
+     restore.  */
+  src1 = ginsn_get_src1 (ginsn);
+  dst = ginsn_get_dst (ginsn);
+  reg = ginsn_get_dst_reg (dst);
+
+  /* For non callee-saved registers, calling the API is meaningless.  */
+  if (!ginsn_track_reg_p (ginsn_get_dst_reg (dst), GINSN_GEN_SCFI))
+    return sym_restore;
+
+  /* The register must have been saved on stack, for sure.  */
+  gas_assert (state->regs[reg].state == CFI_ON_STACK);
+  gas_assert (state->regs[reg].base == REG_CFA);
+
+  if ((ginsn->type == GINSN_TYPE_MOV
+       || ginsn->type == GINSN_TYPE_LOAD)
+      && ginsn_get_src_type (src1) == GINSN_SRC_INDIRECT
+      && (ginsn_get_src_reg (src1) == REG_SP
+	  || (ginsn_get_src_reg (src1) == REG_FP
+	      && state->regs[REG_CFA].base == REG_FP)))
+    {
+      /* mov disp(%rsp), reg.  */
+      /* mov disp(%rbp), reg.  */
+      expected_offset = (((ginsn_get_src_reg (src1) == REG_SP)
+			  ? -state->stack_size
+			  : state->regs[REG_FP].offset)
+			 + ginsn_get_src_disp (src1));
+    }
+
+  sym_restore = (expected_offset == state->regs[reg].offset);
+
+  return sym_restore;
+}
+
+/* Perform symbolic execution of the GINSN and update its list of scfi_ops.
+   scfi_ops are later used to directly generate the DWARF CFI directives.
+   Also update the SCFI state object STATE for the caller.  */
+
+static int
+gen_scfi_ops (ginsnS *ginsn, scfi_stateS *state)
+{
+  int ret = 0;
+  int32_t offset;
+  struct ginsn_src *src1;
+  struct ginsn_src *src2;
+  struct ginsn_dst *dst;
+
+  if (!ginsn || !state)
+    ret = 1;
+
+  /* For the first ginsn (of type GINSN_TYPE_SYMBOL) in the gbb, generate
+     the SCFI op with DW_CFA_def_cfa.  Note that the register and offset are
+     target-specific.  */
+  if (GINSN_F_FUNC_BEGIN_P (ginsn))
+    {
+      scfi_op_add_def_cfa (state, ginsn, REG_SP, SCFI_INIT_CFA_OFFSET);
+      state->stack_size += SCFI_INIT_CFA_OFFSET;
+      return ret;
+    }
+
+  src1 = ginsn_get_src1 (ginsn);
+  src2 = ginsn_get_src2 (ginsn);
+  dst = ginsn_get_dst (ginsn);
+
+  ret = verify_heuristic_traceable_stack_manipulation (ginsn, state);
+  if (ret)
+    return ret;
+
+  ret = verify_heuristic_traceable_reg_fp (ginsn, state);
+  if (ret)
+    return ret;
+
+  switch (ginsn->dst.type)
+    {
+    case GINSN_DST_REG:
+      switch (ginsn->type)
+	{
+	case GINSN_TYPE_MOV:
+	  if (ginsn_get_src_type (src1) == GINSN_SRC_REG
+	      && ginsn_get_src_reg (src1) == REG_SP
+	      && ginsn_get_dst_reg (dst) == REG_FP
+	      && state->regs[REG_CFA].base == REG_SP
+	      /* That REG_FP has just been saved on stack previously.  This
+		 check guards against the case that there is stack usage
+		 between the two insns:
+		    pushq %rbp
+		    ...stack usage
+		    mov %rsp, %rbp
+		 Do not switch CFA tracking in this case.  */
+	      && state->regs[REG_FP].offset == -state->stack_size)
+	    {
+	      /* mov %rsp, %rbp.  */
+	      scfi_op_add_def_cfa_reg (state, ginsn, ginsn_get_dst_reg (dst));
+	    }
+	  else if (ginsn_get_src_type (src1) == GINSN_SRC_REG
+		   && ginsn_get_src_reg (src1) == REG_FP
+		   && ginsn_get_dst_reg (dst) == REG_SP
+		   && state->regs[REG_CFA].base == REG_FP)
+	    {
+	      /* mov %rbp, %rsp.  */
+	      state->stack_size = -state->regs[REG_FP].offset;
+	      scfi_op_add_def_cfa_reg (state, ginsn, ginsn_get_dst_reg (dst));
+	      state->traceable_p = true;
+	    }
+	  else if (ginsn_get_src_type (src1) == GINSN_SRC_INDIRECT
+		   && (ginsn_get_src_reg (src1) == REG_SP
+		       || ginsn_get_src_reg (src1) == REG_FP)
+		   && ginsn_track_reg_p (ginsn_get_dst_reg (dst), GINSN_GEN_SCFI))
+	    {
+	      /* mov disp(%rsp), reg.  */
+	      /* mov disp(%rbp), reg.  */
+	      if (verify_heuristic_symmetrical_restore_reg (state, ginsn))
+		{
+		  scfi_state_restore_reg (state, ginsn_get_dst_reg (dst));
+		  scfi_op_add_cfa_restore (ginsn, ginsn_get_dst_reg (dst));
+		}
+	      else
+		as_warn_where (ginsn->file, ginsn->line,
+			       _("SCFI: asymetrical register restore"));
+	    }
+	  else if (ginsn_get_src_type (src1) == GINSN_SRC_REG
+		   && ginsn_get_dst_type (dst) == GINSN_DST_REG
+		   && ginsn_get_src_reg (src1) == REG_SP)
+	    {
+	      /* mov %rsp, %reg.  */
+	      /* The value of rsp is taken directly from state->stack_size.
+		 IMP: The workflow in gen_scfi_ops must keep it updated.
+		 PS: Not taking the value from state->scratch[REG_SP] is
+		 intentional.  */
+	      state->scratch[ginsn_get_dst_reg (dst)].base = REG_CFA;
+	      state->scratch[ginsn_get_dst_reg (dst)].offset = -state->stack_size;
+	      state->scratch[ginsn_get_dst_reg (dst)].state = CFI_IN_REG;
+	    }
+	  else if (ginsn_get_src_type (src1) == GINSN_SRC_REG
+		   && ginsn_get_dst_type (dst) == GINSN_DST_REG
+		   && ginsn_get_dst_reg (dst) == REG_SP)
+	    {
+	      /* mov %reg, %rsp.  */
+	      /* Keep the value of REG_SP updated.  */
+	      if (state->scratch[ginsn_get_src_reg (src1)].state == CFI_IN_REG)
+		{
+		  state->stack_size = -state->scratch[ginsn_get_src_reg (src1)].offset;
+		  state->traceable_p = true;
+		}
+# if 0
+	      scfi_state_update_reg (state, ginsn_get_dst_reg (dst),
+				     state->scratch[ginsn_get_src_reg (src1)].base,
+				     state->scratch[ginsn_get_src_reg (src1)].offset);
+#endif
+
+	    }
+	  break;
+	case GINSN_TYPE_SUB:
+	  if (ginsn_get_src_reg (src1) == REG_SP
+	      && ginsn_get_dst_reg (dst) == REG_SP)
+	    {
+	      /* Stack inc/dec offset, when generated due to stack push and pop is
+		 target-specific.  Use the value encoded in the ginsn.  */
+	      state->stack_size += ginsn_get_src_imm (src2);
+	      if (state->regs[REG_CFA].base == REG_SP)
+		{
+		  /* push reg.  */
+		  scfi_op_add_cfa_offset_dec (state, ginsn, ginsn_get_src_imm (src2));
+		}
+	    }
+	  break;
+	case GINSN_TYPE_ADD:
+	  if (ginsn_get_src_reg (src1) == REG_SP
+	      && ginsn_get_dst_reg (dst) == REG_SP)
+	    {
+	      /* Stack inc/dec offset is target-specific.  Use the value
+		 encoded in the ginsn.  */
+	      state->stack_size -= ginsn_get_src_imm (src2);
+	      /* pop %reg affects CFA offset only if CFA is currently
+		 stack-pointer based.  */
+	      if (state->regs[REG_CFA].base == REG_SP)
+		{
+		  scfi_op_add_cfa_offset_inc (state, ginsn, ginsn_get_src_imm (src2));
+		}
+	    }
+	  else if (ginsn_get_src_reg (src1) == REG_FP
+		   && ginsn_get_dst_reg (dst) == REG_SP
+		   && state->regs[REG_CFA].base == REG_FP)
+	    {
+	      /* FIXME - what is this for ? */
+	      state->stack_size =  0 - (state->regs[REG_FP].offset + ginsn_get_src_imm (src2));
+	    }
+	  break;
+	case GINSN_TYPE_LOAD:
+	  /* If this is a load from stack.  */
+	  if (ginsn_get_src_type (src1) == GINSN_SRC_INDIRECT
+	      && (ginsn_get_src_reg (src1) == REG_SP
+		  || (ginsn_get_src_reg (src1) == REG_FP
+		      && state->regs[REG_CFA].base == REG_FP)))
+	    {
+	      /* pop %rbp when CFA tracking is REG_FP based.  */
+	      if (ginsn_get_dst_reg (dst) == REG_FP
+		  && state->regs[REG_CFA].base == REG_FP)
+		{
+		  scfi_op_add_def_cfa_reg (state, ginsn, REG_SP);
+		}
+	      if (ginsn_track_reg_p (ginsn_get_dst_reg (dst), GINSN_GEN_SCFI))
+		{
+		  if (verify_heuristic_symmetrical_restore_reg (state, ginsn))
+		    {
+		      scfi_state_restore_reg (state, ginsn_get_dst_reg (dst));
+		      scfi_op_add_cfa_restore (ginsn, ginsn_get_dst_reg (dst));
+		    }
+		  else
+		    as_warn_where (ginsn->file, ginsn->line,
+				   _("SCFI: asymetrical register restore"));
+		}
+	    }
+	  break;
+	default:
+	  break;
+	}
+      break;
+
+    case GINSN_DST_INDIRECT:
+      /* Some operations with an indirect access to memory (or even to stack)
+	 may still be uninteresting for SCFI purpose (e.g, addl %edx, -32(%rsp)
+	 in x86).  In case of x86_64, these can neither be a register
+	 save / unsave, nor can alter the stack size.
+	 PS: This condition may need to be revisited for other arches.  */
+      if (ginsn->type == GINSN_TYPE_ADD || ginsn->type == GINSN_TYPE_SUB
+	  || ginsn->type == GINSN_TYPE_AND)
+	break;
+
+      gas_assert (ginsn->type == GINSN_TYPE_MOV
+		  || ginsn->type == GINSN_TYPE_STORE);
+      /* mov reg, disp(%rbp) */
+      /* mov reg, disp(%rsp) */
+      if (ginsn_scfi_save_reg_p (ginsn, state))
+	{
+	  if (ginsn_get_dst_reg (dst) == REG_SP)
+	    {
+	      /* mov reg, disp(%rsp) */
+	      offset = 0 - state->stack_size + ginsn_get_dst_disp (dst);
+	      scfi_state_save_reg (state, ginsn_get_src_reg (src1), REG_CFA, offset);
+	      scfi_op_add_cfi_offset (state, ginsn, ginsn_get_src_reg (src1));
+	    }
+	  else if (ginsn_get_dst_reg (dst) == REG_FP)
+	    {
+	      gas_assert (state->regs[REG_CFA].base == REG_FP);
+	      /* mov reg, disp(%rbp) */
+	      offset = 0 - state->regs[REG_CFA].offset + ginsn_get_dst_disp (dst);
+	      scfi_state_save_reg (state, ginsn_get_src_reg (src1), REG_CFA, offset);
+	      scfi_op_add_cfi_offset (state, ginsn, ginsn_get_src_reg (src1));
+	    }
+	}
+      break;
+
+    default:
+      /* Skip GINSN_DST_UNKNOWN and GINSN_DST_MEM as they are uninteresting
+	 currently for SCFI.  */
+      break;
+    }
+
+  return ret;
+}
+
+/* Recursively perform forward flow of the (unwind information) SCFI state
+   starting at basic block GBB.
+
+   The forward flow process propagates the SCFI state at exit of a basic block
+   to the successor basic block.
+
+   Returns error code, if any.  */
+
+static int
+forward_flow_scfi_state (gcfgS *gcfg, gbbS *gbb, scfi_stateS *state)
+{
+  ginsnS *ginsn;
+  gbbS *prev_bb;
+  gedgeS *gedge = NULL;
+  int ret = 0;
+
+  if (gbb->visited)
+    {
+      /* Check that the SCFI state is the same as previous.  */
+      ret = cmp_scfi_state (state, gbb->entry_state);
+      if (ret)
+	as_bad (_("SCFI: Bad CFI propagation perhaps"));
+      return ret;
+    }
+
+  gbb->visited = true;
+
+  gbb->entry_state = XCNEW (scfi_stateS);
+  memcpy (gbb->entry_state, state, sizeof (scfi_stateS));
+
+  /* Perform symbolic execution of each ginsn in the gbb and update the
+     scfi_ops list of each ginsn (and also update the STATE object).   */
+  bb_for_each_insn(gbb, ginsn)
+    {
+      ret = gen_scfi_ops (ginsn, state);
+      if (ret)
+	goto fail;
+    }
+
+  gbb->exit_state = XCNEW (scfi_stateS);
+  memcpy (gbb->exit_state, state, sizeof (scfi_stateS));
+
+  /* Forward flow the SCFI state.  Currently, we process the next basic block
+     in DFS order.  But any forward traversal order should be fine.  */
+  prev_bb = gbb;
+  if (gbb->num_out_gedges)
+    {
+      bb_for_each_edge(gbb, gedge)
+	{
+	  gbb = gedge->dst_bb;
+	  if (gbb->visited)
+	    {
+	      ret = cmp_scfi_state (gbb->entry_state, state);
+	      if (ret)
+		goto fail;
+	    }
+
+	  if (!gedge->visited)
+	    {
+	      gedge->visited = true;
+
+	      /* Entry SCFI state for the destination bb of the edge is the
+		 same as the exit SCFI state of the source bb of the edge.  */
+	      memcpy (state, prev_bb->exit_state, sizeof (scfi_stateS));
+	      ret = forward_flow_scfi_state (gcfg, gbb, state);
+	      if (ret)
+		goto fail;
+	    }
+	}
+    }
+
+  return 0;
+
+fail:
+
+  if (gedge)
+    gedge->visited = true;
+  return 1;
+}
+
+static int
+backward_flow_scfi_state (symbolS *func ATTRIBUTE_UNUSED, gcfgS *gcfg)
+{
+  gbbS **prog_order_bbs;
+  gbbS **restore_bbs;
+  gbbS *current_bb;
+  gbbS *prev_bb;
+  gbbS *dst_bb;
+  ginsnS *ginsn;
+  gedgeS *gedge = NULL;
+
+  int ret = 0;
+  int i, j;
+
+  /* Basic blocks in reverse program order.  */
+  prog_order_bbs = XCNEWVEC (gbbS *, gcfg->num_gbbs);
+  /* Basic blocks for which CFI remember op needs to be generated.  */
+  restore_bbs = XCNEWVEC (gbbS *, gcfg->num_gbbs);
+
+  gcfg_get_bbs_in_prog_order (gcfg, prog_order_bbs);
+
+  i = gcfg->num_gbbs - 1;
+  /* Traverse in reverse program order.  */
+  while (i > 0)
+    {
+      current_bb = prog_order_bbs[i];
+      prev_bb = prog_order_bbs[i-1];
+      if (cmp_scfi_state (prev_bb->exit_state, current_bb->entry_state))
+	{
+	  /* Candidate for .cfi_restore_state found.  */
+	  ginsn = bb_get_first_ginsn (current_bb);
+	  scfi_op_add_cfi_restore_state (ginsn);
+	  /* Memorize current_bb now to find location for its remember state
+	     later.  */
+	  restore_bbs[i] = current_bb;
+	}
+      else
+	{
+	  bb_for_each_edge (current_bb, gedge)
+	    {
+	      dst_bb = gedge->dst_bb;
+	      for (j = 0; j < gcfg->num_gbbs; j++)
+		if (restore_bbs[j] == dst_bb)
+		  {
+		    ginsn = bb_get_last_ginsn (current_bb);
+		    scfi_op_add_cfi_remember_state (ginsn);
+		    /* Remove the memorised restore_bb from the list.  */
+		    restore_bbs[j] = NULL;
+		    break;
+		  }
+	    }
+	}
+      i--;
+    }
+
+  /* All .cfi_restore_state pseudo-ops must have a corresponding
+     .cfi_remember_state by now.  */
+  for (j = 0; j < gcfg->num_gbbs; j++)
+    if (restore_bbs[j] != NULL)
+      {
+	ret = 1;
+	break;
+      }
+
+  free (restore_bbs);
+  free (prog_order_bbs);
+
+  return ret;
+}
+
+/* Synthesize DWARF CFI for a function.  */
+
+int
+scfi_synthesize_dw2cfi (symbolS *func, gcfgS *gcfg, gbbS *root_bb)
+{
+  int ret;
+  scfi_stateS *init_state;
+
+  init_state = XCNEW (scfi_stateS);
+  init_state->traceable_p = true;
+
+  /* Traverse the input GCFG and perform forward flow of information.
+     Update the scfi_op(s) per ginsn.  */
+  ret = forward_flow_scfi_state (gcfg, root_bb, init_state);
+  if (ret)
+    {
+      as_warn (_("SCFI: forward pass failed for func '%s'"), S_GET_NAME (func));
+      goto end;
+    }
+
+  ret = backward_flow_scfi_state (func, gcfg);
+  if (ret)
+    {
+      as_warn (_("SCFI: backward pass failed for func '%s'"), S_GET_NAME (func));
+      goto end;
+    }
+
+end:
+  free (init_state);
+  return ret;
+}
+
+static int
+handle_scfi_dot_cfi (ginsnS *ginsn)
+{
+  scfi_opS *op;
+
+  /* Nothing to do.  */
+  if (!ginsn->scfi_ops)
+    return 0;
+
+  op = *ginsn->scfi_ops;
+  if (!op)
+    goto bad;
+
+  while (op)
+    {
+      switch (op->dw2cfi_op)
+	{
+	case DW_CFA_def_cfa_register:
+	  scfi_dot_cfi (DW_CFA_def_cfa_register, op->loc.base, 0, 0, NULL,
+			ginsn->sym);
+	  break;
+	case DW_CFA_def_cfa_offset:
+	  scfi_dot_cfi (DW_CFA_def_cfa_offset, op->loc.base, 0,
+			op->loc.offset, NULL, ginsn->sym);
+	  break;
+	case DW_CFA_def_cfa:
+	  scfi_dot_cfi (DW_CFA_def_cfa, op->loc.base, 0, op->loc.offset,
+			NULL, ginsn->sym);
+	  break;
+	case DW_CFA_offset:
+	  scfi_dot_cfi (DW_CFA_offset, op->reg, 0, op->loc.offset, NULL,
+			ginsn->sym);
+	  break;
+	case DW_CFA_restore:
+	  scfi_dot_cfi (DW_CFA_restore, op->reg, 0, 0, NULL, ginsn->sym);
+	  break;
+	case DW_CFA_remember_state:
+	  scfi_dot_cfi (DW_CFA_remember_state, 0, 0, 0, NULL, ginsn->sym);
+	  break;
+	case DW_CFA_restore_state:
+	  scfi_dot_cfi (DW_CFA_restore_state, 0, 0, 0, NULL, ginsn->sym);
+	  break;
+	case CFI_label:
+	  scfi_dot_cfi (CFI_label, 0, 0, 0, op->op_data->name, ginsn->sym);
+	  break;
+	case CFI_signal_frame:
+	  scfi_dot_cfi (CFI_signal_frame, 0, 0, 0, NULL, ginsn->sym);
+	  break;
+	default:
+	  goto bad;
+	  break;
+	}
+      op = op->next;
+    }
+
+  return 0;
+bad:
+  as_bad (_("SCFI: Invalid DWARF CFI opcode data"));
+  return 1;
+}
+
+/* Emit Synthesized DWARF CFI.  */
+
+int
+scfi_emit_dw2cfi (symbolS *func)
+{
+  struct frch_ginsn_data *frch_gdata;
+  ginsnS* ginsn = NULL;
+
+  frch_gdata = frchain_now->frch_ginsn_data;
+  ginsn = frch_gdata->gins_rootP;
+
+  while (ginsn)
+    {
+      switch (ginsn->type)
+	{
+	  case GINSN_TYPE_SYMBOL:
+	    /* .cfi_startproc and .cfi_endproc pseudo-ops.  */
+	    if (GINSN_F_FUNC_BEGIN_P (ginsn))
+	      {
+		scfi_dot_cfi_startproc (frch_gdata->start_addr);
+		break;
+	      }
+	    else if (GINSN_F_FUNC_END_P (ginsn))
+	      {
+		scfi_dot_cfi_endproc (ginsn->sym);
+		break;
+	      }
+	    /* Fall through.  */
+	  case GINSN_TYPE_ADD:
+	  case GINSN_TYPE_AND:
+	  case GINSN_TYPE_CALL:
+	  case GINSN_TYPE_JUMP:
+	  case GINSN_TYPE_JUMP_COND:
+	  case GINSN_TYPE_MOV:
+	  case GINSN_TYPE_LOAD:
+	  case GINSN_TYPE_PHANTOM:
+	  case GINSN_TYPE_STORE:
+	  case GINSN_TYPE_SUB:
+	  case GINSN_TYPE_OTHER:
+	  case GINSN_TYPE_RETURN:
+
+	    /* For all other SCFI ops, invoke the handler.  */
+	    if (ginsn->scfi_ops)
+	      handle_scfi_dot_cfi (ginsn);
+	    break;
+
+	  default:
+	    /* No other GINSN_TYPE_* expected.  */
+	    as_bad (_("SCFI: bad ginsn for func '%s'"),
+		    S_GET_NAME (func));
+	    break;
+	}
+      ginsn = ginsn->next;
+    }
+  return 0;
+}
+
+#else
+
+int
+scfi_emit_dw2cfi (symbolS *func ATTRIBUTE_UNUSED)
+{
+  as_bad (_("SCFI: unsupported for target"));
+  return 1;
+}
+
+int
+scfi_synthesize_dw2cfi (symbolS *func ATTRIBUTE_UNUSED,
+			gcfgS *gcfg ATTRIBUTE_UNUSED,
+			gbbS *root_bb ATTRIBUTE_UNUSED)
+{
+  as_bad (_("SCFI: unsupported for target"));
+  return 1;
+}
+
+#endif  /* defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN).  */
diff --git a/gas/scfi.h b/gas/scfi.h
new file mode 100644
index 00000000000..3eb1bf9ddd7
--- /dev/null
+++ b/gas/scfi.h
@@ -0,0 +1,38 @@
+/* scfi.h - Support for synthesizing CFI for asm.
+   Copyright (C) 2023 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 SCFI_H
+#define SCFI_H
+
+#include "as.h"
+#include "ginsn.h"
+
+void scfi_ops_cleanup (scfi_opS **head);
+
+/* Some SCFI ops are added externally when parsing the assembler input.
+   Two examples are CFI_label, and CFI_signal_frame.  */
+void scfi_op_add_cfi_label (ginsnS *ginsn, const char *name);
+void scfi_op_add_signal_frame (ginsnS *ginsn);
+
+int scfi_emit_dw2cfi (symbolS *func);
+
+int scfi_synthesize_dw2cfi (symbolS *func, gcfgS *gcfg, gbbS *root_bb);
+
+#endif /* SCFI_H.  */
diff --git a/gas/scfidw2gen.c b/gas/scfidw2gen.c
index 188699a882f..613ef919a1e 100644
--- a/gas/scfidw2gen.c
+++ b/gas/scfidw2gen.c
@@ -19,6 +19,8 @@
    02110-1301, USA.  */
 
 #include "as.h"
+#include "ginsn.h"
+#include "scfi.h"
 #include "dw2gencfi.h"
 #include "subsegs.h"
 #include "scfidw2gen.h"
@@ -87,15 +89,33 @@ dot_scfi_sections (int ignored ATTRIBUTE_UNUSED)
 static void
 scfi_process_cfi_label (void)
 {
-  /* To be implemented. */
-  return;
+  char *name;
+  ginsnS *ginsn;
+
+  name = read_symbol_name ();
+  if (name == NULL)
+    return;
+
+  /* Add a new ginsn.  */
+  ginsn = ginsn_new_phantom (symbol_temp_new_now ());
+  frch_ginsn_data_append (ginsn);
+
+  scfi_op_add_cfi_label (ginsn, name);
+  /* TODO.  */
+  // free (name);
+
+  demand_empty_rest_of_line ();
 }
 
 static void
 scfi_process_cfi_signal_frame (void)
 {
-  /* To be implemented.  */
-  return;
+  ginsnS *ginsn;
+
+  ginsn = ginsn_new_phantom (symbol_temp_new_now ());
+  frch_ginsn_data_append (ginsn);
+
+  scfi_op_add_signal_frame (ginsn);
 }
 
 static void
diff --git a/gas/subsegs.c b/gas/subsegs.c
index ae42b4e76d9..6b9834c8566 100644
--- a/gas/subsegs.c
+++ b/gas/subsegs.c
@@ -131,6 +131,7 @@ subseg_set_rest (segT seg, subsegT subseg)
       newP->frch_frag_now = frag_alloc (&newP->frch_obstack);
       newP->frch_frag_now->fr_type = rs_fill;
       newP->frch_cfi_data = NULL;
+      newP->frch_ginsn_data = NULL;
 
       newP->frch_root = newP->frch_last = newP->frch_frag_now;
 
diff --git a/gas/subsegs.h b/gas/subsegs.h
index ace0657bdfb..c90c5622465 100644
--- a/gas/subsegs.h
+++ b/gas/subsegs.h
@@ -40,6 +40,7 @@
 #include "obstack.h"
 
 struct frch_cfi_data;
+struct frch_ginsn_data;
 
 struct frchain			/* control building of a frag chain */
 {				/* FRCH = FRagment CHain control */
@@ -52,6 +53,7 @@ struct frchain			/* control building of a frag chain */
   struct obstack frch_obstack;	/* for objects in this frag chain */
   fragS *frch_frag_now;		/* frag_now for this subsegment */
   struct frch_cfi_data *frch_cfi_data;
+  struct frch_ginsn_data *frch_ginsn_data;
 };
 
 typedef struct frchain frchainS;
diff --git a/gas/symbols.c b/gas/symbols.c
index 45e46ed39b7..b595ffad104 100644
--- a/gas/symbols.c
+++ b/gas/symbols.c
@@ -25,6 +25,7 @@
 #include "obstack.h"		/* For "symbols.h" */
 #include "subsegs.h"
 #include "write.h"
+#include "scfi.h"
 
 #include <limits.h>
 #ifndef CHAR_BIT
@@ -709,6 +710,8 @@ colon (/* Just seen "x:" - rattle symbols & frags.  */
 #ifdef obj_frob_label
   obj_frob_label (symbolP);
 #endif
+  if (flag_synth_cfi)
+    ginsn_frob_label (symbolP);
 
   return symbolP;
 }
-- 
2.41.0


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

* [PATCH, V2 08/10] gas: doc: update documentation for the new listing option
  2023-10-30 16:51 [PATCH, V2 00/10] Synthesize CFI for hand-written asm Indu Bhagat
                   ` (6 preceding siblings ...)
  2023-10-30 16:51 ` [PATCH, V2 07/10] gas: synthesize CFI for hand-written asm Indu Bhagat
@ 2023-10-30 16:51 ` Indu Bhagat
  2023-10-30 16:51 ` [PATCH, V2 09/10] gas: testsuite: add a x86_64 testsuite for SCFI Indu Bhagat
  2023-10-30 16:51 ` [PATCH, V2 10/10] gas/NEWS: announce the new command line option Indu Bhagat
  9 siblings, 0 replies; 30+ messages in thread
From: Indu Bhagat @ 2023-10-30 16:51 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[New patch in the current patch set]

Add a new listing option, -i, to emit ginsn in the listing output.  We
may also emit other SCFI information if necessary in the future.

ginsn are most useful when seen alongside the assembly instructions.
Hence, they are emitted when the user includes the assembly instructions
in the listing output, i.e., "-ali=FILE".

gas/doc/:
	* as.texi: Add documentation for the new listing option, -i.
---
 gas/doc/as.texi | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/gas/doc/as.texi b/gas/doc/as.texi
index cfc1078c36b..6e0fa618712 100644
--- a/gas/doc/as.texi
+++ b/gas/doc/as.texi
@@ -226,7 +226,7 @@ gcc(1), ld(1), and the Info entries for @file{binutils} and @file{ld}.
 @c to be limited to one line for the header.
 @smallexample
 @c man begin SYNOPSIS
-@value{AS} [@b{-a}[@b{cdghlns}][=@var{file}]]
+@value{AS} [@b{-a}[@b{cdghilns}][=@var{file}]]
  [@b{--alternate}]
  [@b{--compress-debug-sections}] [@b{--nocompress-debug-sections}]
  [@b{-D}]
@@ -673,7 +673,7 @@ gcc(1), ld(1), and the Info entries for @file{binutils} and @file{ld}.
 @table @gcctabopt
 @include at-file.texi
 
-@item -a[cdghlmns]
+@item -a[cdghilmns]
 Turn on listings, in any of a variety of ways:
 
 @table @gcctabopt
@@ -692,6 +692,9 @@ include high-level source
 @item -al
 include assembly
 
+@item -ali
+include assembly with ginsn
+
 @item -am
 include macro expansions
 
@@ -2415,7 +2418,7 @@ assembler.)
 @c man end
 
 @menu
-* a::             -a[cdghlns] enable listings
+* a::             -a[cdghilns] enable listings
 * alternate::     --alternate enable alternate macro syntax
 * D::             -D for compatibility and debugging
 * f::             -f to work faster
@@ -2442,7 +2445,7 @@ assembler.)
 @end menu
 
 @node a
-@section Enable Listings: @option{-a[cdghlns]}
+@section Enable Listings: @option{-a[cdghilns]}
 
 @kindex -a
 @kindex -ac
@@ -2450,6 +2453,7 @@ assembler.)
 @kindex -ag
 @kindex -ah
 @kindex -al
+@kindex -ali
 @kindex -an
 @kindex -as
 @cindex listings, enabling
@@ -2459,7 +2463,9 @@ These options enable listing output from the assembler.  By itself,
 @samp{-a} requests high-level, assembly, and symbols listing.
 You can use other letters to select specific options for the list:
 @samp{-ah} requests a high-level language listing,
-@samp{-al} requests an output-program assembly listing, and
+@samp{-al} requests an output-program assembly listing,
+@samp{-ali} requests an output-program assembly listing along with the
+associated ginsn, and
 @samp{-as} requests a symbol table listing.
 High-level listings require that a compiler debugging option like
 @samp{-g} be used, and that assembly listings (@samp{-al}) be requested
-- 
2.41.0


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

* [PATCH, V2 09/10] gas: testsuite: add a x86_64 testsuite for SCFI
  2023-10-30 16:51 [PATCH, V2 00/10] Synthesize CFI for hand-written asm Indu Bhagat
                   ` (7 preceding siblings ...)
  2023-10-30 16:51 ` [PATCH, V2 08/10] gas: doc: update documentation for the new listing option Indu Bhagat
@ 2023-10-30 16:51 ` Indu Bhagat
  2023-10-31 16:13   ` Jan Beulich
  2023-10-30 16:51 ` [PATCH, V2 10/10] gas/NEWS: announce the new command line option Indu Bhagat
  9 siblings, 1 reply; 30+ messages in thread
From: Indu Bhagat @ 2023-10-30 16:51 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[Changes from the RFC patch set]
  - New testcases based on the fixes in the previous patches
  - New testcase for not ignoring .cfi_label
  - New testcases scfi-asm-marker-* which test for some patterns of
    .type and .size attributes in the input asm for --scfi=all.
  - Recall that all assembler testcases here include the .cfi_*
    directives corresponding to the asm.  GAS now issues a warning if
    --scfi is invoked with input assembly which includes .cfi_*
    directives:
    "Warning: --scfi=all ignores some user-specified CFI directives"
  - But the SCFI algorithm in GAS issues other SCFI specific warnings too.
    Hence, many of the tests are run twice, like so:
    + run_dump_test "scfi-add-1", which runs the test with a -W and checks
      the objdump output of the .eh_frame (or sometimes .sframe) section.
    + run_list_test "scfi-add-1" "--scfi --warn", which runs the test
      with warnings enabled, and explicitly checks all warning messages
      per test.
[End of changes]

The testsuite for SCFI contains target-specific tests.

As all the tests are executed with --scfi command line option, the CFI
annotations in the test .s files are skipped altogether by the GAS for
processing.  The CFI directives in the assembly files are added with the
intention to aid maintainence only: CFI annotations in .s files help
convey the expected EH Frame / SFrame data in a format-oblivious way.

Some testcases are used to highlight those asm constructs that the SCFI
machinery in GAS currently does not support:

  - Only AMD64 ABI is supported for now. Using --m32 with --scfi results
    in hard error.
    See scfi-unsupported-1.s.

  - Untraceable stack-pointer manipulation in function epilougue and prologue.
    See scfi-unsupported-2.s.

  - Using Dynamically Realigned Arguement Pointer (DRAP) register to
    realign the stack.  For SCFI, the CFA must be only REG_SP or REG_FP
    based.  See scfi-unsupported-drap-1.s

Some testcases are used to highlight some diagnostics that the SCFI
machinery in GAS currently issues, with an intent to help user correct
inadvertent errors in their hand-written asm.  An error is issued in a
situation where GAS is not sure it will be able to synthesize valid CFI.

  - (#1) "Warning: SCFI: Asymetrical register restore"
  - (#2) "Error: SCFI: usage of REG_FP as scratch not supported"
  - (#3) "Error: SCFI: unsupported stack manipulation pattern"

In case of (#2) and (#3), SCFI generation is skipped for the respective
function.  Above is a subset of the warnings/errors implemented in the
code.

gas/testsuite/:
	* gas/scfi/README: New test.
	* gas/scfi/x86_64/scfi-add-1.d: New test.
	* gas/scfi/x86_64/scfi-add-1.l: New test.
	* gas/scfi/x86_64/scfi-add-1.s: New test.
	* gas/scfi/x86_64/scfi-add-2.d: New test.
	* gas/scfi/x86_64/scfi-add-2.l: New test.
	* gas/scfi/x86_64/scfi-add-2.s: New test.
	* gas/scfi/x86_64/scfi-asm-marker-1.d: New test.
	* gas/scfi/x86_64/scfi-asm-marker-1.l: New test.
	* gas/scfi/x86_64/scfi-asm-marker-1.s: New test.
	* gas/scfi/x86_64/scfi-asm-marker-2.d: New test.
	* gas/scfi/x86_64/scfi-asm-marker-2.l: New test.
	* gas/scfi/x86_64/scfi-asm-marker-2.s: New test.
	* gas/scfi/x86_64/scfi-asm-marker-3.d: New test.
	* gas/scfi/x86_64/scfi-asm-marker-3.l: New test.
	* gas/scfi/x86_64/scfi-asm-marker-3.s: New test.
	* gas/scfi/x86_64/scfi-bp-sp-1.d: New test.
	* gas/scfi/x86_64/scfi-bp-sp-1.l: New test.
	* gas/scfi/x86_64/scfi-bp-sp-1.s: New test.
	* gas/scfi/x86_64/scfi-bp-sp-2.d: New test.
	* gas/scfi/x86_64/scfi-bp-sp-2.l: New test.
	* gas/scfi/x86_64/scfi-bp-sp-2.s: New test.
	* gas/scfi/x86_64/scfi-callee-saved-1.d: New test.
	* gas/scfi/x86_64/scfi-callee-saved-1.l: New test.
	* gas/scfi/x86_64/scfi-callee-saved-1.s: New test.
	* gas/scfi/x86_64/scfi-callee-saved-2.d: New test.
	* gas/scfi/x86_64/scfi-callee-saved-2.l: New test.
	* gas/scfi/x86_64/scfi-callee-saved-2.s: New test.
	* gas/scfi/x86_64/scfi-callee-saved-3.d: New test.
	* gas/scfi/x86_64/scfi-callee-saved-3.l: New test.
	* gas/scfi/x86_64/scfi-callee-saved-3.s: New test.
	* gas/scfi/x86_64/scfi-callee-saved-4.d: New test.
	* gas/scfi/x86_64/scfi-callee-saved-4.l: New test.
	* gas/scfi/x86_64/scfi-callee-saved-4.s: New test.
	* gas/scfi/x86_64/scfi-cfg-1.d: New test.
	* gas/scfi/x86_64/scfi-cfg-1.l: New test.
	* gas/scfi/x86_64/scfi-cfg-1.s: New test.
	* gas/scfi/x86_64/scfi-cfg-2.d: New test.
	* gas/scfi/x86_64/scfi-cfg-2.l: New test.
	* gas/scfi/x86_64/scfi-cfg-2.s: New test.
	* gas/scfi/x86_64/scfi-cfi-label-1.d: New test.
	* gas/scfi/x86_64/scfi-cfi-label-1.l: New test.
	* gas/scfi/x86_64/scfi-cfi-label-1.s: New test.
	* gas/scfi/x86_64/scfi-cofi-1.d: New test.
	* gas/scfi/x86_64/scfi-cofi-1.l: New test.
	* gas/scfi/x86_64/scfi-cofi-1.s: New test.
	* gas/scfi/x86_64/scfi-diag-1.l: New test.
	* gas/scfi/x86_64/scfi-diag-1.s: New test.
	* gas/scfi/x86_64/scfi-diag-2.l: New test.
	* gas/scfi/x86_64/scfi-diag-2.s: New test.
	* gas/scfi/x86_64/scfi-dyn-stack-1.d: New test.
	* gas/scfi/x86_64/scfi-dyn-stack-1.l: New test.
	* gas/scfi/x86_64/scfi-dyn-stack-1.s: New test.
	* gas/scfi/x86_64/scfi-fp-diag-2.l: New test.
	* gas/scfi/x86_64/scfi-fp-diag-2.s: New test.
	* gas/scfi/x86_64/scfi-ignore-1.d: New test.
	* gas/scfi/x86_64/scfi-ignore-1.l: New test.
	* gas/scfi/x86_64/scfi-ignore-1.s: New test.
	* gas/scfi/x86_64/scfi-indirect-mov-1.d: New test.
	* gas/scfi/x86_64/scfi-indirect-mov-1.l: New test.
	* gas/scfi/x86_64/scfi-indirect-mov-1.s: New test.
	* gas/scfi/x86_64/scfi-indirect-mov-2.d: New test.
	* gas/scfi/x86_64/scfi-indirect-mov-2.l: New test.
	* gas/scfi/x86_64/scfi-indirect-mov-2.s: New test.
	* gas/scfi/x86_64/scfi-indirect-mov-3.d: New test.
	* gas/scfi/x86_64/scfi-indirect-mov-3.l: New test.
	* gas/scfi/x86_64/scfi-indirect-mov-3.s: New test.
	* gas/scfi/x86_64/scfi-indirect-mov-4.d: New test.
	* gas/scfi/x86_64/scfi-indirect-mov-4.l: New test.
	* gas/scfi/x86_64/scfi-indirect-mov-4.s: New test.
	* gas/scfi/x86_64/scfi-lea-1.d: New test.
	* gas/scfi/x86_64/scfi-lea-1.l: New test.
	* gas/scfi/x86_64/scfi-lea-1.s: New test.
	* gas/scfi/x86_64/scfi-leave-1.d: New test.
	* gas/scfi/x86_64/scfi-leave-1.l: New test.
	* gas/scfi/x86_64/scfi-leave-1.s: New test.
	* gas/scfi/x86_64/scfi-pushq-1.d: New test.
	* gas/scfi/x86_64/scfi-pushq-1.l: New test.
	* gas/scfi/x86_64/scfi-pushq-1.s: New test.
	* gas/scfi/x86_64/scfi-pushsection-1.d: New test.
	* gas/scfi/x86_64/scfi-pushsection-1.l: New test.
	* gas/scfi/x86_64/scfi-pushsection-1.s: New test.
	* gas/scfi/x86_64/scfi-pushsection-2.d: New test.
	* gas/scfi/x86_64/scfi-pushsection-2.l: New test.
	* gas/scfi/x86_64/scfi-pushsection-2.s: New test.
	* gas/scfi/x86_64/scfi-selfalign-func-1.d: New test.
	* gas/scfi/x86_64/scfi-selfalign-func-1.l: New test.
	* gas/scfi/x86_64/scfi-selfalign-func-1.s: New test.
	* gas/scfi/x86_64/scfi-simple-1.d: New test.
	* gas/scfi/x86_64/scfi-simple-1.l: New test.
	* gas/scfi/x86_64/scfi-simple-1.s: New test.
	* gas/scfi/x86_64/scfi-simple-2.d: New test.
	* gas/scfi/x86_64/scfi-simple-2.l: New test.
	* gas/scfi/x86_64/scfi-simple-2.s: New test.
	* gas/scfi/x86_64/scfi-sub-1.d: New test.
	* gas/scfi/x86_64/scfi-sub-1.l: New test.
	* gas/scfi/x86_64/scfi-sub-1.s: New test.
	* gas/scfi/x86_64/scfi-sub-2.d: New test.
	* gas/scfi/x86_64/scfi-sub-2.l: New test.
	* gas/scfi/x86_64/scfi-sub-2.s: New test.
	* gas/scfi/x86_64/scfi-unsupported-1.l: New test.
	* gas/scfi/x86_64/scfi-unsupported-1.s: New test.
	* gas/scfi/x86_64/scfi-unsupported-2.l: New test.
	* gas/scfi/x86_64/scfi-unsupported-2.s: New test.
	* gas/scfi/x86_64/scfi-unsupported-3.l: New test.
	* gas/scfi/x86_64/scfi-unsupported-3.s: New test.
	* gas/scfi/x86_64/scfi-unsupported-4.l: New test.
	* gas/scfi/x86_64/scfi-unsupported-4.s: New test.
	* gas/scfi/x86_64/scfi-unsupported-cfg-1.l: New test.
	* gas/scfi/x86_64/scfi-unsupported-cfg-1.s: New test.
	* gas/scfi/x86_64/scfi-unsupported-drap-1.l: New test.
	* gas/scfi/x86_64/scfi-unsupported-drap-1.s: New test.
	* gas/scfi/x86_64/scfi-x86-64.exp: New test.
---
 gas/testsuite/gas/scfi/README                 |  17 +++
 gas/testsuite/gas/scfi/x86_64/scfi-add-1.d    |  25 +++++
 gas/testsuite/gas/scfi/x86_64/scfi-add-1.l    |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-add-1.s    |  13 +++
 gas/testsuite/gas/scfi/x86_64/scfi-add-2.d    |  36 ++++++
 gas/testsuite/gas/scfi/x86_64/scfi-add-2.l    |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-add-2.s    |  48 ++++++++
 .../gas/scfi/x86_64/scfi-asm-marker-1.d       |  28 +++++
 .../gas/scfi/x86_64/scfi-asm-marker-1.l       |   3 +
 .../gas/scfi/x86_64/scfi-asm-marker-1.s       |  27 +++++
 .../gas/scfi/x86_64/scfi-asm-marker-2.d       |  24 ++++
 .../gas/scfi/x86_64/scfi-asm-marker-2.l       |   3 +
 .../gas/scfi/x86_64/scfi-asm-marker-2.s       |  11 ++
 .../gas/scfi/x86_64/scfi-asm-marker-3.d       |  31 ++++++
 .../gas/scfi/x86_64/scfi-asm-marker-3.l       |   2 +
 .../gas/scfi/x86_64/scfi-asm-marker-3.s       |  38 +++++++
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.d  |  31 ++++++
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.l  |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.s  |  22 ++++
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.d  |  57 ++++++++++
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.l  |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.s  |  49 +++++++++
 .../gas/scfi/x86_64/scfi-callee-saved-1.d     |  40 +++++++
 .../gas/scfi/x86_64/scfi-callee-saved-1.l     |   2 +
 .../gas/scfi/x86_64/scfi-callee-saved-1.s     |  26 +++++
 .../gas/scfi/x86_64/scfi-callee-saved-2.d     |  41 +++++++
 .../gas/scfi/x86_64/scfi-callee-saved-2.l     |   2 +
 .../gas/scfi/x86_64/scfi-callee-saved-2.s     |  42 +++++++
 .../gas/scfi/x86_64/scfi-callee-saved-3.d     |  42 +++++++
 .../gas/scfi/x86_64/scfi-callee-saved-3.l     |   3 +
 .../gas/scfi/x86_64/scfi-callee-saved-3.s     |  40 +++++++
 .../gas/scfi/x86_64/scfi-callee-saved-4.d     |  40 +++++++
 .../gas/scfi/x86_64/scfi-callee-saved-4.l     |   3 +
 .../gas/scfi/x86_64/scfi-callee-saved-4.s     |  55 ++++++++++
 gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.d    |  36 ++++++
 gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.l    |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.s    |  47 ++++++++
 gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.d    |  28 +++++
 gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.l    |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.s    |  21 ++++
 .../gas/scfi/x86_64/scfi-cfi-label-1.d        |  37 +++++++
 .../gas/scfi/x86_64/scfi-cfi-label-1.l        |   2 +
 .../gas/scfi/x86_64/scfi-cfi-label-1.s        |  19 ++++
 gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.d   |   5 +
 gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.l   |   3 +
 gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.s   |  23 ++++
 gas/testsuite/gas/scfi/x86_64/scfi-diag-1.l   |   4 +
 gas/testsuite/gas/scfi/x86_64/scfi-diag-1.s   |  23 ++++
 gas/testsuite/gas/scfi/x86_64/scfi-diag-2.l   |   4 +
 gas/testsuite/gas/scfi/x86_64/scfi-diag-2.s   |  28 +++++
 .../gas/scfi/x86_64/scfi-dyn-stack-1.d        |  23 ++++
 .../gas/scfi/x86_64/scfi-dyn-stack-1.l        |   2 +
 .../gas/scfi/x86_64/scfi-dyn-stack-1.s        |  50 +++++++++
 .../gas/scfi/x86_64/scfi-fp-diag-2.l          |   3 +
 .../gas/scfi/x86_64/scfi-fp-diag-2.s          |  55 ++++++++++
 gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.d |  26 +++++
 gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.l |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.s |  13 +++
 .../gas/scfi/x86_64/scfi-indirect-mov-1.d     |  51 +++++++++
 .../gas/scfi/x86_64/scfi-indirect-mov-1.l     |   2 +
 .../gas/scfi/x86_64/scfi-indirect-mov-1.s     |  48 ++++++++
 .../gas/scfi/x86_64/scfi-indirect-mov-2.d     |  41 +++++++
 .../gas/scfi/x86_64/scfi-indirect-mov-2.l     |   2 +
 .../gas/scfi/x86_64/scfi-indirect-mov-2.s     |  38 +++++++
 .../gas/scfi/x86_64/scfi-indirect-mov-3.d     |  41 +++++++
 .../gas/scfi/x86_64/scfi-indirect-mov-3.l     |   2 +
 .../gas/scfi/x86_64/scfi-indirect-mov-3.s     |  38 +++++++
 .../gas/scfi/x86_64/scfi-indirect-mov-4.d     |  63 +++++++++++
 .../gas/scfi/x86_64/scfi-indirect-mov-4.l     |   3 +
 .../gas/scfi/x86_64/scfi-indirect-mov-4.s     |  68 ++++++++++++
 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.d    |  37 +++++++
 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.l    |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.s    |  40 +++++++
 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.d  |  36 ++++++
 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.l  |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.s  |  26 +++++
 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.d  |  35 ++++++
 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.l  |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.s  |  24 ++++
 .../gas/scfi/x86_64/scfi-pushsection-1.d      |  43 ++++++++
 .../gas/scfi/x86_64/scfi-pushsection-1.l      |   2 +
 .../gas/scfi/x86_64/scfi-pushsection-1.s      |  40 +++++++
 .../gas/scfi/x86_64/scfi-pushsection-2.d      |  39 +++++++
 .../gas/scfi/x86_64/scfi-pushsection-2.l      |   2 +
 .../gas/scfi/x86_64/scfi-pushsection-2.s      |  38 +++++++
 .../gas/scfi/x86_64/scfi-selfalign-func-1.d   |  31 ++++++
 .../gas/scfi/x86_64/scfi-selfalign-func-1.l   |   2 +
 .../gas/scfi/x86_64/scfi-selfalign-func-1.s   |  36 ++++++
 gas/testsuite/gas/scfi/x86_64/scfi-simple-1.d |  26 +++++
 gas/testsuite/gas/scfi/x86_64/scfi-simple-1.l |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-simple-1.s |  15 +++
 gas/testsuite/gas/scfi/x86_64/scfi-simple-2.d |  30 +++++
 gas/testsuite/gas/scfi/x86_64/scfi-simple-2.l |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-simple-2.s |  16 +++
 gas/testsuite/gas/scfi/x86_64/scfi-sub-1.d    |  25 +++++
 gas/testsuite/gas/scfi/x86_64/scfi-sub-1.l    |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-sub-1.s    |  12 ++
 gas/testsuite/gas/scfi/x86_64/scfi-sub-2.d    |  31 ++++++
 gas/testsuite/gas/scfi/x86_64/scfi-sub-2.l    |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-sub-2.s    |  29 +++++
 .../gas/scfi/x86_64/scfi-unsupported-1.l      |   2 +
 .../gas/scfi/x86_64/scfi-unsupported-1.s      |  10 ++
 .../gas/scfi/x86_64/scfi-unsupported-2.l      |   3 +
 .../gas/scfi/x86_64/scfi-unsupported-2.s      |  14 +++
 .../gas/scfi/x86_64/scfi-unsupported-3.l      |   3 +
 .../gas/scfi/x86_64/scfi-unsupported-3.s      |  14 +++
 .../gas/scfi/x86_64/scfi-unsupported-4.l      |   4 +
 .../gas/scfi/x86_64/scfi-unsupported-4.s      |  23 ++++
 .../gas/scfi/x86_64/scfi-unsupported-cfg-1.l  |   3 +
 .../gas/scfi/x86_64/scfi-unsupported-cfg-1.s  |  53 +++++++++
 .../gas/scfi/x86_64/scfi-unsupported-drap-1.l |   4 +
 .../gas/scfi/x86_64/scfi-unsupported-drap-1.s |  75 +++++++++++++
 gas/testsuite/gas/scfi/x86_64/scfi-x86-64.exp | 103 ++++++++++++++++++
 113 files changed, 2604 insertions(+)
 create mode 100644 gas/testsuite/gas/scfi/README
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-add-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-add-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-add-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-add-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-add-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-add-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-diag-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-diag-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-diag-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-diag-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-simple-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-simple-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-simple-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-simple-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-simple-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-simple-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-sub-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-sub-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-sub-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-sub-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-sub-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-sub-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-3.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-3.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-4.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-4.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-drap-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-drap-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-x86-64.exp

diff --git a/gas/testsuite/gas/scfi/README b/gas/testsuite/gas/scfi/README
new file mode 100644
index 00000000000..75dcebb602d
--- /dev/null
+++ b/gas/testsuite/gas/scfi/README
@@ -0,0 +1,17 @@
+Notes on the SCFI testsuite in GAS:
+
+* At this time, SCFI machinery is only supported for x86_64.
+
+* When adding more tests, please keep CFI annotations updated in the .s files.
+  Recall that user-specified, synthesizable CFI annotations are ignored by the
+  GAS when --scfi (=all) is in effect.  Adding CFI annocations, irrespectively,
+  makes the testcases clearer in terms of understanding the expected unwind
+  data.
+
+* Note that GAS issues a warning:
+    "Warning: --scfi=all ignores some user-specified CFI directive"
+  when it encounters CFI directives in the input assembly and --scfi (=all) is
+  active.  To bypass the noise from this warning, while keeping the testcases
+  useful/understandable, most tests are run twice. E.g.,
+     - run_dump_test "scfi-cfi-add-1" // ignores warnings
+     - run_list_test "scfi-cfi-add-1" "--scfi --warn" // tests warnings
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-add-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-add-1.d
new file mode 100644
index 00000000000..d224990641a
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-add-1.d
@@ -0,0 +1,25 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: Synthesize CFI for add insn 1
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0014 0+001c FDE cie=0+0000 pc=0+0000..0+0005
+  DW_CFA_advance_loc: 4 to 0+0004
+  DW_CFA_def_cfa_offset: 0
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-add-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-add-1.l
new file mode 100644
index 00000000000..0e094077edd
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-add-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: --scfi=all ignores some user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-add-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-add-1.s
new file mode 100644
index 00000000000..99002c5e34e
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-add-1.s
@@ -0,0 +1,13 @@
+# Testcase for add instruction.
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+	.cfi_startproc
+	addq    $8, %rsp
+	.cfi_def_cfa_offset 0
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	foo, .-foo
+
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-add-2.d b/gas/testsuite/gas/scfi/x86_64/scfi-add-2.d
new file mode 100644
index 00000000000..4b378caa117
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-add-2.d
@@ -0,0 +1,36 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: Synthesize CFI for add insn 2
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0024 0+001c FDE cie=0+0000 pc=0+0000..0+002c
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_advance_loc: 3 to 0+0004
+  DW_CFA_def_cfa_register: r6 \(rbp\)
+  DW_CFA_advance_loc: 2 to 0+0006
+  DW_CFA_offset: r12 \(r12\) at cfa-24
+  DW_CFA_advance_loc: 36 to 0+002a
+  DW_CFA_restore: r12 \(r12\)
+  DW_CFA_advance_loc: 1 to 0+002b
+  DW_CFA_def_cfa_register: r7 \(rsp\)
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-add-2.l b/gas/testsuite/gas/scfi/x86_64/scfi-add-2.l
new file mode 100644
index 00000000000..147ffcb6bda
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-add-2.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*14: Warning: --scfi=all ignores some user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-add-2.s b/gas/testsuite/gas/scfi/x86_64/scfi-add-2.s
new file mode 100644
index 00000000000..6ea0fefc7b5
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-add-2.s
@@ -0,0 +1,48 @@
+        .section        .rodata
+	.type	simd_cmp_op, @object
+	.size	simd_cmp_op, 8
+simd_cmp_op:
+	.long	2
+	.zero	4
+
+# Testcase for add instruction.
+# add reg, reg instruction
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+	.cfi_startproc
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset 6, -16
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register 6
+	pushq	%r12
+	.cfi_offset 12, -24
+	mov	%rsp, %r12
+# Stack manipulation is permitted if the base register for
+# tracking CFA has been changed to FP.
+	addq    %rdx, %rsp
+	addq	%rsp, %rax
+# Some add instructions may access the stack indirectly.  Such
+# accesses do not make REG_FP untraceable.
+	addl    %eax, -84(%rbp)
+# Other kind of add instructions should not error out in the
+# x86_64 -> ginsn translator
+	addq    $simd_cmp_op+8, %rdx
+	addl    %edx, -32(%rsp)
+	addl    $1, fb_low_counter(,%rbx,4)
+	mov	%r12, %rsp
+# Popping a callee-saved register.
+# RSP must be traceable.
+	pop     %r12
+	.cfi_restore 12
+	leave
+	.cfi_def_cfa_register 7
+	.cfi_restore 6
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	foo, .-foo
+
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.d
new file mode 100644
index 00000000000..9103e9f1881
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.d
@@ -0,0 +1,28 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: Synthesize CFI for demarcated code blocks 1
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+00000018 0+0010 0000001c FDE cie=00000000 pc=0+0000..0+000f
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+
+0000002c 0+0010 00000030 FDE cie=00000000 pc=0+0000..0+0006
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.l
new file mode 100644
index 00000000000..f6a884c238f
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*8: Warning: --scfi=all ignores some user-specified CFI directives
+.*10: Warning: missing label '.L3' in func 'foo' may result in imprecise cfg
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.s
new file mode 100644
index 00000000000..88d78795643
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.s
@@ -0,0 +1,27 @@
+# Testcase where a user may define hot and cold areas of function
+# Note how the .type, and .size directives may be placed differently
+# than a regular function.
+
+	.globl  foo
+	.type   foo, @function
+foo:
+        .cfi_startproc
+        testl   %edi, %edi
+        je      .L3
+        movl    b(%rip), %eax
+        ret
+        .cfi_endproc
+        .section        .text.unlikely
+        .cfi_startproc
+        .type   foo.cold, @function
+foo.cold:
+.L3:
+        pushq   %rax
+        .cfi_def_cfa_offset 16
+        call    abort
+       .cfi_endproc
+.LFE11:
+        .text
+        .size   foo, .-foo
+        .section        .text.unlikely
+        .size   foo.cold, .-foo.cold
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.d b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.d
new file mode 100644
index 00000000000..3e5a4f05ea4
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.d
@@ -0,0 +1,24 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: Synthesize CFI for demarcated code blocks 2
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+00000018 0+0014 0000001c FDE cie=00000000 pc=0+0000..0+000f
+  DW_CFA_nop
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.l b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.l
new file mode 100644
index 00000000000..6688ea8b5d8
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*6: Warning: --scfi=all ignores some user-specified CFI directives
+.*8: Warning: missing label '.L3' in func 'foo' may result in imprecise cfg
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.s b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.s
new file mode 100644
index 00000000000..9bcfe8d2e0a
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.s
@@ -0,0 +1,11 @@
+# A programmer may not bother to set the size of the 
+# function symbols via an explicit .size directive.
+	.globl  foo
+	.type   foo, @function
+foo:
+        .cfi_startproc
+        testl   %edi, %edi
+        je      .L3
+        movl    b(%rip), %eax
+        ret
+       .cfi_endproc
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.d b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.d
new file mode 100644
index 00000000000..3c1fcb2c85c
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.d
@@ -0,0 +1,31 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: Synthesize CFI for demarcated code blocks 3
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+00000018 0+001c 0000001c FDE cie=00000000 pc=0+0000..0+0035
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_advance_loc: 3 to 0+0004
+  DW_CFA_def_cfa_register: r6 \(rbp\)
+  DW_CFA_advance_loc: 48 to 0+0034
+  DW_CFA_def_cfa_register: r7 \(rsp\)
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.l b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.l
new file mode 100644
index 00000000000..bcf4bacf823
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*9: Warning: --scfi=all ignores some user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.s b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.s
new file mode 100644
index 00000000000..6a2b8e1ac14
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.s
@@ -0,0 +1,38 @@
+# Testcase where the input may have interleaved sections,
+# possibly even text and data.
+	.globl	main
+	.type	main, @function
+main:
+.LFB1:
+#	.cfi_startproc
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset 6, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register 6
+	subq	$16, %rsp
+	movl	$17, %esi
+	movl	$5, %edi
+	call	add
+        .section        .rodata
+        .align 16
+        .type   __test_obj.0, @object
+        .size   __test_obj.0, 24
+__test_obj.0:
+        .string "test_elf_objs_in_rodata"
+.LC0:
+	.string	"the result is = %d\n"
+	.text
+	movl	%eax, -4(%rbp)
+	movl	-4(%rbp), %eax
+	movl	%eax, %esi
+	movl	$.LC0, %edi
+	movl	$0, %eax
+	call	printf
+	movl	$0, %eax
+	leave
+	.cfi_def_cfa_register 7
+	.cfi_restore 6
+	.cfi_def_cfa_offset 8
+	ret
+#	.cfi_endproc
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.d
new file mode 100644
index 00000000000..373895c7caf
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.d
@@ -0,0 +1,31 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: Synthesize CFI for SP/FP based CFA switching 1
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+001c 0+001c FDE cie=0+0000 pc=0+0000..0+000c
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_advance_loc: 3 to 0+0004
+  DW_CFA_def_cfa_register: r6 \(rbp\)
+  DW_CFA_advance_loc: 6 to 0+000a
+  DW_CFA_def_cfa_register: r7 \(rsp\)
+  DW_CFA_advance_loc: 1 to 0+000b
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 8
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.l
new file mode 100644
index 00000000000..0e094077edd
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: --scfi=all ignores some user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.s
new file mode 100644
index 00000000000..c9b949fdf8f
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.s
@@ -0,0 +1,22 @@
+# Testcase for switching between sp/fp based CFA.
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+	.cfi_startproc
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset 6, -16
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register 6
+	addq	%rax, %rdi
+	mov	%rbp, %rsp
+	.cfi_def_cfa_register 7
+	pop	%rbp
+	.cfi_restore 6
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	foo, .-foo
+
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.d b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.d
new file mode 100644
index 00000000000..3f167c3ef1a
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.d
@@ -0,0 +1,57 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: Synthesize CFI for SP/FP based CFA switching 2
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0044 0000001c FDE cie=00000000 pc=0+0000..0+0021
+  DW_CFA_advance_loc: 2 to 0+0002
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r14 \(r14\) at cfa-16
+  DW_CFA_advance_loc: 2 to 0+0004
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_offset: r13 \(r13\) at cfa-24
+  DW_CFA_advance_loc: 2 to 0+0006
+  DW_CFA_def_cfa_offset: 32
+  DW_CFA_offset: r12 \(r12\) at cfa-32
+  DW_CFA_advance_loc: 1 to 0+0007
+  DW_CFA_def_cfa_offset: 40
+  DW_CFA_offset: r6 \(rbp\) at cfa-40
+  DW_CFA_advance_loc: 1 to 0+0008
+  DW_CFA_def_cfa_offset: 48
+  DW_CFA_offset: r3 \(rbx\) at cfa-48
+  DW_CFA_advance_loc: 7 to 0+000f
+  DW_CFA_def_cfa_offset: 80
+  DW_CFA_advance_loc: 9 to 0+0018
+  DW_CFA_def_cfa_offset: 48
+  DW_CFA_advance_loc: 1 to 0+0019
+  DW_CFA_restore: r3 \(rbx\)
+  DW_CFA_def_cfa_offset: 40
+  DW_CFA_advance_loc: 1 to 0+001a
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 32
+  DW_CFA_advance_loc: 2 to 0+001c
+  DW_CFA_restore: r12 \(r12\)
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_advance_loc: 2 to 0+001e
+  DW_CFA_restore: r13 \(r13\)
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_advance_loc: 2 to 0+0020
+  DW_CFA_restore: r14 \(r14\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.l b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.l
new file mode 100644
index 00000000000..0e094077edd
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: --scfi=all ignores some user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.s b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.s
new file mode 100644
index 00000000000..f17c5c8dfc4
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.s
@@ -0,0 +1,49 @@
+# Testcase for switching between sp/fp based CFA.
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+        .cfi_startproc
+        pushq   %r14
+        .cfi_def_cfa_offset 16
+        .cfi_offset 14, -16
+        pushq   %r13
+        .cfi_def_cfa_offset 24
+        .cfi_offset 13, -24
+        pushq   %r12
+        .cfi_def_cfa_offset 32
+        .cfi_offset 12, -32
+        pushq   %rbp
+        .cfi_def_cfa_offset 40
+        .cfi_offset 6, -40
+        pushq   %rbx
+        .cfi_def_cfa_offset 48
+        .cfi_offset 3, -48
+        movq    %rdi, %rbx
+        subq    $32, %rsp
+        .cfi_def_cfa_offset 80
+# This mov does not switch CFA tracking to REG_FP, because there has already
+# been stack usage between here and the push %rbp
+        movq    %rsp, %rbp
+        xorl    %eax, %eax
+        addq    $32, %rsp
+        .cfi_def_cfa_offset 48
+        popq    %rbx
+	.cfi_restore 3
+        .cfi_def_cfa_offset 40
+        popq    %rbp
+	.cfi_restore 6
+        .cfi_def_cfa_offset 32
+        popq    %r12
+	.cfi_restore 12
+        .cfi_def_cfa_offset 24
+        popq    %r13
+	.cfi_restore 13
+        .cfi_def_cfa_offset 16
+        popq    %r14
+	.cfi_restore 14
+        .cfi_def_cfa_offset 8
+        ret
+	.cfi_endproc
+	.size	foo, .-foo
+
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.d
new file mode 100644
index 00000000000..feb93d28d7a
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.d
@@ -0,0 +1,40 @@
+#as: --scfi
+#objdump: -Wf
+#name: SCFI for callee-saved registers 1
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0002c 0+0001c FDE cie=0+0000 pc=0+0000..0+0007
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_advance_loc: 1 to 0+0002
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_offset: r3 \(rbx\) at cfa-24
+  DW_CFA_advance_loc: 1 to 0+0003
+  DW_CFA_def_cfa_offset: 32
+  DW_CFA_offset: r6 \(rbp\) at cfa-32
+  DW_CFA_advance_loc: 1 to 0+0004
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_advance_loc: 1 to 0+0005
+  DW_CFA_restore: r3 \(rbx\)
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_advance_loc: 1 to 0+0006
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.l
new file mode 100644
index 00000000000..2762adba5b1
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*8: Warning: --scfi=all ignores some user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.s
new file mode 100644
index 00000000000..db3a2690bf3
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.s
@@ -0,0 +1,26 @@
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+#	.cfi_startproc
+	pushq	%rax
+	.cfi_def_cfa_offset 16
+	push	%rbx
+	.cfi_def_cfa_offset 24
+	.cfi_offset 3, -24
+	pushq	%rbp
+        .cfi_def_cfa_offset 32
+        .cfi_offset 6, -32
+	popq	%rbp
+	.cfi_def_cfa_offset 24
+	.cfi_restore 6
+	popq	%rbx
+	.cfi_def_cfa_offset 16
+	.cfi_restore 3
+	popq	%rax
+        .cfi_def_cfa_offset 8
+	ret
+#	.cfi_endproc
+.LFE0:
+	.size	foo, .-foo
+
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.d b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.d
new file mode 100644
index 00000000000..57a99b04557
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.d
@@ -0,0 +1,41 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: SCFI for callee-saved registers 2
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+00000018 0+002c 0000001c FDE cie=00000000 pc=0+0000..0+0017
+  DW_CFA_advance_loc: 2 to 0+0002
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r12 \(r12\) at cfa-16
+  DW_CFA_advance_loc: 2 to 0+0004
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_offset: r13 \(r13\) at cfa-24
+  DW_CFA_advance_loc: 9 to 0+000d
+  DW_CFA_def_cfa_offset: 32
+  DW_CFA_advance_loc: 1 to 0+000e
+  DW_CFA_def_cfa_offset: 40
+  DW_CFA_advance_loc: 4 to 0+0012
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_advance_loc: 2 to 0+0014
+  DW_CFA_restore: r13 \(r13\)
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_advance_loc: 2 to 0+0016
+  DW_CFA_restore: r12 \(r12\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.l b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.l
new file mode 100644
index 00000000000..bcf4bacf823
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*9: Warning: --scfi=all ignores some user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.s b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.s
new file mode 100644
index 00000000000..7b10a2c1c6b
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.s
@@ -0,0 +1,42 @@
+# Testcase for save reg ops for callee-saved registers
+# These latter two pushq's of callee-saved regs must NOT generate
+# .cfi_offset.
+
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+	.cfi_startproc
+	pushq	%r12
+	.cfi_def_cfa_offset 16
+	.cfi_offset 12, -16
+	pushq	%r13
+	.cfi_def_cfa_offset 24
+	.cfi_offset 13, -24
+# The function may use callee-saved registers for its use, and may even
+# chose to spill them to stack if necessary.
+	addq    %rax, %r13
+	subq 	$8, %r13
+# These two pushq's of callee-saved regs must NOT generate
+# .cfi_offset.
+	pushq	%r13
+	.cfi_def_cfa_offset 32
+	pushq	%rax
+	.cfi_def_cfa_offset 40
+# Adjust the REG_SP to get rid of local stack usage.
+	addq	$16, %rsp
+	.cfi_def_cfa_offset 24
+# The SCFI machinery keeps track of where the callee-saved registers
+# are on the stack.  It generates a restore operation if the stack
+# offsets match.
+	popq	%r13
+	.cfi_restore 13
+	.cfi_def_cfa_offset 16
+	popq	%r12
+	.cfi_restore 12
+        .cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	foo, .-foo
+
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.d b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.d
new file mode 100644
index 00000000000..73eb147a539
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.d
@@ -0,0 +1,42 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: SCFI for callee-saved registers 3
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+00000018 0+002c 0000001c FDE cie=00000000 pc=0+0000..0+0016
+  DW_CFA_advance_loc: 2 to 0+0002
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r12 \(r12\) at cfa-16
+  DW_CFA_advance_loc: 2 to 0+0004
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_offset: r13 \(r13\) at cfa-24
+  DW_CFA_advance_loc: 9 to 0+000d
+  DW_CFA_def_cfa_offset: 32
+  DW_CFA_advance_loc: 1 to 0+000e
+  DW_CFA_def_cfa_offset: 40
+  DW_CFA_advance_loc: 1 to 0+000f
+  DW_CFA_def_cfa_offset: 32
+  DW_CFA_advance_loc: 2 to 0+0011
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_advance_loc: 2 to 0+0013
+  DW_CFA_restore: r13 \(r13\)
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_advance_loc: 2 to 0+0015
+  DW_CFA_restore: r12 \(r12\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.l b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.l
new file mode 100644
index 00000000000..2930658dc2d
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*6: Warning: --scfi=all ignores some user-specified CFI directives
+.*25: Warning: SCFI: asymetrical register restore
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.s b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.s
new file mode 100644
index 00000000000..a2bb53017f1
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.s
@@ -0,0 +1,40 @@
+# Testcase for save reg ops for callee-saved registers
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+	.cfi_startproc
+	pushq	%r12
+	.cfi_def_cfa_offset 16
+	.cfi_offset 12, -16
+	pushq	%r13
+	.cfi_def_cfa_offset 24
+	.cfi_offset 13, -24
+# The program may use callee-saved registers for its use, and may even
+# chose to spill them to stack if necessary.
+	addq    %rax, %r13
+	subq 	$8, %r13
+# These two pushq's of callee-saved regs must NOT generate
+# .cfi_offset.
+	pushq	%r13
+	.cfi_def_cfa_offset 32
+	pushq	%rax
+	.cfi_def_cfa_offset 40
+	popq	%rax
+	.cfi_def_cfa_offset 32
+	popq	%r13
+	.cfi_def_cfa_offset 24
+# The SCFI machinery keeps track of where the callee-saved registers
+# are on the stack.  It generates a restore operation if the stack
+# offsets match.
+	popq	%r13
+	.cfi_restore 13
+	.cfi_def_cfa_offset 16
+	popq	%r12
+	.cfi_restore 12
+        .cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	foo, .-foo
+
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.d b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.d
new file mode 100644
index 00000000000..d6543fa8f6c
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.d
@@ -0,0 +1,40 @@
+#as: -W --scfi
+#objdump: -Wf
+#name: SCFI for callee-saved registers 4
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+00000018 0+002c 0000001c FDE cie=00000000 pc=0+0000..0+005e
+  DW_CFA_advance_loc: 5 to 0+0005
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_advance_loc: 3 to 0+0008
+  DW_CFA_def_cfa_register: r6 \(rbp\)
+  DW_CFA_advance_loc: 2 to 0+000a
+  DW_CFA_offset: r12 \(r12\) at cfa-24
+  DW_CFA_advance_loc: 1 to 0+000b
+  DW_CFA_offset: r3 \(rbx\) at cfa-32
+  DW_CFA_advance_loc1: 79 to 0+005a
+  DW_CFA_restore: r3 \(rbx\)
+  DW_CFA_advance_loc: 2 to 0+005c
+  DW_CFA_restore: r12 \(r12\)
+  DW_CFA_advance_loc: 1 to 0+005d
+  DW_CFA_def_cfa_register: r7 \(rsp\)
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.l b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.l
new file mode 100644
index 00000000000..58b417460e5
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*4: Warning: --scfi=all ignores some user-specified CFI directives
+.*24: Warning: SCFI: asymetrical register restore
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.s b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.s
new file mode 100644
index 00000000000..be29a3ccfa8
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.s
@@ -0,0 +1,55 @@
+	.type	byte_insert_op1, @function
+byte_insert_op1:
+.LFB10:
+	.cfi_startproc
+	endbr64
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset 6, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register 6
+	pushq	%r12
+	.cfi_offset 12, -24
+	pushq	%rbx
+	.cfi_offset 3, -32
+	subq	$24, %rsp
+	movl	%edi, -20(%rbp)
+	movq	%rsi, -32(%rbp)
+	movl	%edx, -24(%rbp)
+	movq	%rcx, -40(%rbp)
+# The program may use callee-saved registers for its use, and may even
+# chose to read them from stack if necessary.  The following use should
+# not be treated as reg restore for SCFI purposes (because rbx has been
+# saved to -16(%rbp).
+	movq	-40(%rbp), %rbx
+	movq	-40(%rbp), %rax
+	leaq	3(%rax), %r12
+	jmp	.L563
+.L564:
+	subq	$1, %rbx
+	subq	$1, %r12
+	movzbl	(%rbx), %eax
+	movb	%al, (%r12)
+.L563:
+	cmpq	-32(%rbp), %rbx
+	jne	.L564
+	movl	-24(%rbp), %edx
+	movq	-32(%rbp), %rcx
+	movl	-20(%rbp), %eax
+	movq	%rcx, %rsi
+	movl	%eax, %edi
+	call	byte_store_op1
+	nop
+	addq	$24, %rsp
+	popq	%rbx
+	.cfi_restore 3
+	popq	%r12
+	.cfi_restore 12
+	popq	%rbp
+	.cfi_def_cfa_register 7
+	.cfi_restore 6
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE10:
+	.size	byte_insert_op1, .-byte_insert_op1
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.d
new file mode 100644
index 00000000000..aa14af48613
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.d
@@ -0,0 +1,36 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: Synthesize CFI in presence of control flow 1
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0024 0000001c FDE cie=00000000 pc=0+0000..0+003a
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r3 \(rbx\) at cfa-16
+  DW_CFA_advance_loc: 37 to 0+0026
+  DW_CFA_remember_state
+  DW_CFA_advance_loc: 1 to 0+0027
+  DW_CFA_restore: r3 \(rbx\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_advance_loc: 1 to 0+0028
+  DW_CFA_restore_state
+  DW_CFA_advance_loc: 9 to 0+0031
+  DW_CFA_restore: r3 \(rbx\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.l
new file mode 100644
index 00000000000..653937edbf9
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*15: Warning: --scfi=all ignores some user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.s
new file mode 100644
index 00000000000..9e72ed0ed2b
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.s
@@ -0,0 +1,47 @@
+# Testcase with one dominator bb and two exit bbs
+# Something like for: return ferror (f) || fclose (f) != 0;
+	.text
+	.section	.rodata.str1.1,"aMS",@progbits,1
+.LC0:
+	.string	"w"
+.LC1:
+	.string	"conftest.out"
+	.section	.text.startup,"ax",@progbits
+	.p2align 4
+	.globl	main
+	.type	main, @function
+main:
+.LFB11:
+	.cfi_startproc
+	pushq	%rbx
+	.cfi_def_cfa_offset 16
+	.cfi_offset 3, -16
+	movl	$.LC0, %esi
+	movl	$.LC1, %edi
+	call	fopen
+	movq	%rax, %rdi
+	movq	%rax, %rbx
+	call	ferror
+	movl	%eax, %edx
+	movl	$1, %eax
+	testl	%edx, %edx
+	je	.L7
+	.cfi_remember_state
+	popq	%rbx
+	.cfi_restore 3
+	.cfi_def_cfa_offset 8
+	ret
+.L7:
+	.cfi_restore_state
+	movq	%rbx, %rdi
+	call	fclose
+	popq	%rbx
+	.cfi_restore 3
+	.cfi_def_cfa_offset 8
+	testl	%eax, %eax
+	setne	%al
+	movzbl	%al, %eax
+	ret
+	.cfi_endproc
+.LFE11:
+	.size	main, .-main
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.d b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.d
new file mode 100644
index 00000000000..736c26386e7
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.d
@@ -0,0 +1,28 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: Synthesize CFI in presence of control flow 2
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0001c 0+001c FDE cie=00000000 pc=0000000000000000..0000000000000016
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_advance_loc: 3 to 0+0004
+  DW_CFA_def_cfa_register: r6 \(rbp\)
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.l b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.l
new file mode 100644
index 00000000000..2762adba5b1
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*8: Warning: --scfi=all ignores some user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.s b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.s
new file mode 100644
index 00000000000..adf1df08bc4
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.s
@@ -0,0 +1,21 @@
+# Testcase for CFG creation of ginsns
+# This testcase has no return instruction at the end.
+	.text
+	.globl	main
+	.type	main, @function
+main:
+.LFB7:
+	.cfi_startproc
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset 6, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register 6
+	call	foo
+	shrl	$31, %eax
+	movzbl	%al, %eax
+	movl	%eax, %edi
+	call	exit
+	.cfi_endproc
+.LFE7:
+	.size	main, .-main
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.d
new file mode 100644
index 00000000000..6e11583a534
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.d
@@ -0,0 +1,37 @@
+#as: --scfi -W
+#objdump: -tWf
+#name: SCFI no ignore .cfi_label
+#...
+.*\.o:     file format elf.*
+
+SYMBOL TABLE:
+0+0000 l    d  \.text	0+0000 \.text
+0+002b l       \.eh_frame	0+0000 cfi2
+0+0000 g     F \.text	0+0008 foo
+0+002a g       \.eh_frame	0+0000 cfi1
+
+
+Contents of the .eh_frame section:
+
+
+00000000 0+0014 00000000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     1b
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+00000018 0+0014 0000001c FDE cie=00000000 pc=0+0000..0+0008
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_advance_loc: 1 to 0+0002
+  DW_CFA_advance_loc: 1 to 0+0003
+  DW_CFA_advance_loc: 4 to 0+0007
+  DW_CFA_def_cfa_offset: 0
+  DW_CFA_nop
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.l
new file mode 100644
index 00000000000..0e094077edd
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: --scfi=all ignores some user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.s
new file mode 100644
index 00000000000..845696d4749
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.s
@@ -0,0 +1,19 @@
+# Testcase for .cfi_label directives
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+	.cfi_startproc
+	nop
+        .globl cfi1
+        .cfi_label cfi1
+	nop
+	.cfi_label cfi2
+	nop
+	.cfi_label .Lcfi3
+	addq    $8, %rsp
+	.cfi_def_cfa_offset 0
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.d
new file mode 100644
index 00000000000..af4fa429339
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.d
@@ -0,0 +1,5 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: Synthesize CFI for add insn
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.l
new file mode 100644
index 00000000000..fd57194cedf
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*12: Warning: --scfi=all ignores some user-specified CFI directives
+.*22: Warning: Untraceable control flow for func 'foo'; Skipping SCFI
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.s
new file mode 100644
index 00000000000..fa03b5ef5e4
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.s
@@ -0,0 +1,23 @@
+# Testcase with a variety of "change of flow instructions"
+#
+# Must be run with -W so it remains warning free.
+#
+# This test does not have much going on wrt synthesis of CFI;
+# it just aims to ensure x8_64 -> ginsn decoding must behave
+# gracefully for these "change of flow instructions"
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+	.cfi_startproc
+	addq    %rdx, %rax
+	notrack jmp     *%rax
+	call   *%r8
+	jmp     *48(%rdi)
+	jo      .L179
+.L179:
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	foo, .-foo
+
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-diag-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-diag-1.l
new file mode 100644
index 00000000000..ecc6b82ce97
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-diag-1.l
@@ -0,0 +1,4 @@
+.*Assembler messages:
+.*7: Warning: --scfi=all ignores some user-specified CFI directives
+.*14: Error: SCFI: usage of REG_FP as scratch not supported
+.*22: Warning: SCFI: forward pass failed for func 'foo'
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-diag-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-diag-1.s
new file mode 100644
index 00000000000..c09ec0da1c4
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-diag-1.s
@@ -0,0 +1,23 @@
+# Testcase for REG_FP based CFA
+# and using REG_FP as scratch.
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+	.cfi_startproc
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset 6, -16
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register 6
+# The following add causes REG_FP to become untraceable
+	addq	%rax, %rbp
+	.cfi_def_cfa_register 7
+	pop	%rbp
+	.cfi_restore 6
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	foo, .-foo
+
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-diag-2.l b/gas/testsuite/gas/scfi/x86_64/scfi-diag-2.l
new file mode 100644
index 00000000000..d3adbeca35a
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-diag-2.l
@@ -0,0 +1,4 @@
+.*Assembler messages:
+.*5: Warning: --scfi=all ignores some user-specified CFI directives
+.*21: Warning: SCFI: asymetrical register restore
+.*22: Warning: SCFI: asymetrical register restore
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-diag-2.s b/gas/testsuite/gas/scfi/x86_64/scfi-diag-2.s
new file mode 100644
index 00000000000..f58915d0a72
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-diag-2.s
@@ -0,0 +1,28 @@
+# Testcase for a diagnostic around assymetrical restore
+.type	foo, @function
+foo:
+.LFB10:
+	.cfi_startproc
+	endbr64
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset 6, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register 6
+	pushq	%r12
+	pushq	%rbx
+	subq	$24, %rsp
+	.cfi_offset 12, -24
+	.cfi_offset 3, -32
+	addq	$24, %rsp
+# Note that the order of r12 and rbx restore does not match
+# order of the corresponding save(s).
+# The SCFI machinery warns the user.
+	popq	%r12
+	popq	%rbx
+	popq	%rbp
+	.cfi_def_cfa 7, 8
+	ret
+	.cfi_endproc
+.LFE10:
+	.size	foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.d
new file mode 100644
index 00000000000..935deace54f
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.d
@@ -0,0 +1,23 @@
+#as: --scfi --gsframe -W
+#objdump: --sframe
+#name: SCFI for dynamic alloc stack
+#...
+
+Contents of the SFrame section .sframe:
+  Header :
+
+    Version: SFRAME_VERSION_2
+    Flags: NONE
+    Num FDEs: 1
+    Num FREs: 4
+
+  Function Index :
+
+    func idx \[0\]: pc = 0x0, size = 87 bytes
+    STARTPC + CFA + FP + RA           
+    0+0000 + sp\+8 + u + u            
+    0+0001 + sp\+16 + c-16 + u            
+    0+0004 + fp\+16 + c-16 + u            
+    0+0056 + sp\+8 + u + u            
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.l
new file mode 100644
index 00000000000..ce97dd14432
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*5: Warning: --scfi=all ignores some user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.s
new file mode 100644
index 00000000000..12f1cd78994
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.s
@@ -0,0 +1,50 @@
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+	.cfi_startproc
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset 6, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register 6
+	pushq	%r13
+	pushq	%r12
+	pushq	%rbx
+	subq	$8, %rsp
+	.cfi_offset 13, -24
+	.cfi_offset 12, -32
+	.cfi_offset 3, -40
+	call	read_user
+	testl	%eax, %eax
+	jle	.L2
+	cltq
+	xorl	%ebx, %ebx
+	leaq	0(,%rax,4), %r13
+	.p2align 4,,10
+	.p2align 3
+.L3:
+	leaq	15(%rbx), %rax
+	movq	%rsp, %r12
+	addq	$4, %rbx
+	andq	$-16, %rax
+	subq	%rax, %rsp
+	movq	%rsp, %rdi
+	call	foo
+	movq	%r12, %rsp
+	cmpq	%r13, %rbx
+	jne	.L3
+.L2:
+	leaq	-24(%rbp), %rsp
+	xorl	%eax, %eax
+	popq	%rbx
+	popq	%r12
+	popq	%r13
+	popq	%rbp
+	.cfi_restore 6
+	.cfi_def_cfa_register 7
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.l b/gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.l
new file mode 100644
index 00000000000..734666af914
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*7: Warning: --scfi=all ignores some user-specified CFI directives
+.*28: Warning: SCFI: asymetrical register restore
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.s b/gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.s
new file mode 100644
index 00000000000..fd46fe3f65c
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.s
@@ -0,0 +1,55 @@
+# Testcase for a diagnostic around assymetrical restore
+# Testcase inspired by byte_insert_op1 in libiberty
+# False positive for the diagnostic
+.type	foo, @function
+foo:
+.LFB10:
+	.cfi_startproc
+	endbr64
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset 6, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register 6
+	pushq	%r12
+	pushq	%rbx
+	subq	$24, %rsp
+	.cfi_offset 12, -24
+	.cfi_offset 3, -32
+	movl	%edi, -20(%rbp)
+	movq	%rsi, -32(%rbp)
+	movl	%edx, -24(%rbp)
+	movq	%rcx, -40(%rbp)
+# The assembler cannot differentiate that the following
+# mov to %rbx is not a true restore operation, but simply
+# %rbx register usage as a scratch reg of some sort.
+# The assembler merely warns of a possible assymetric restore operation
+# In this case, its noise for the user unfortunately.
+	movq	-40(%rbp), %rbx
+	movq	-40(%rbp), %rax
+	leaq	3(%rax), %r12
+	jmp	.L563
+.L564:
+	subq	$1, %rbx
+	subq	$1, %r12
+	movzbl	(%rbx), %eax
+	movb	%al, (%r12)
+.L563:
+	cmpq	-32(%rbp), %rbx
+	jne	.L564
+	movl	-24(%rbp), %edx
+	movq	-32(%rbp), %rcx
+	movl	-20(%rbp), %eax
+	movq	%rcx, %rsi
+	movl	%eax, %edi
+	call	byte_store_op1
+	nop
+	addq	$24, %rsp
+	popq	%rbx
+	popq	%r12
+	popq	%rbp
+	.cfi_def_cfa 7, 8
+	ret
+	.cfi_endproc
+.LFE10:
+	.size	foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.d
new file mode 100644
index 00000000000..1cf5919add6
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.d
@@ -0,0 +1,26 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: Ignore user specified CFI directives
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0014 0+001c FDE cie=0+0000 pc=0+0000..0+0002
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_nop
+  DW_CFA_nop
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.l
new file mode 100644
index 00000000000..ce97dd14432
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*5: Warning: --scfi=all ignores some user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.s
new file mode 100644
index 00000000000..68b731c58ba
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.s
@@ -0,0 +1,13 @@
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+	.cfi_startproc
+	pushq	%rbp
+        .cfi_def_cfa_offset 16
+        .cfi_offset 6, -16
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	foo, .-foo
+
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.d
new file mode 100644
index 00000000000..4f4436506f6
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.d
@@ -0,0 +1,51 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: Synthesize CFI for indirect mem op to stack 1
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0034 0+001c FDE cie=00000000 pc=0+0000..0+005b
+  DW_CFA_advance_loc: 4 to 0+0004
+  DW_CFA_def_cfa_offset: 64
+  DW_CFA_advance_loc: 4 to 0+0008
+  DW_CFA_offset: r3 \(rbx\) at cfa-64
+  DW_CFA_advance_loc: 5 to 0+000d
+  DW_CFA_offset: r6 \(rbp\) at cfa-56
+  DW_CFA_advance_loc: 5 to 0+0012
+  DW_CFA_offset: r12 \(r12\) at cfa-48
+  DW_CFA_advance_loc: 5 to 0+0017
+  DW_CFA_offset: r13 \(r13\) at cfa-40
+  DW_CFA_advance_loc: 5 to 0+001c
+  DW_CFA_offset: r14 \(r14\) at cfa-32
+  DW_CFA_advance_loc: 5 to 0+0021
+  DW_CFA_offset: r15 \(r15\) at cfa-24
+  DW_CFA_advance_loc: 29 to 0+003e
+  DW_CFA_restore: r15 \(r15\)
+  DW_CFA_advance_loc: 5 to 0+0043
+  DW_CFA_restore: r14 \(r14\)
+  DW_CFA_advance_loc: 5 to 0+0048
+  DW_CFA_restore: r13 \(r13\)
+  DW_CFA_advance_loc: 5 to 0+004d
+  DW_CFA_restore: r12 \(r12\)
+  DW_CFA_advance_loc: 5 to 0+0052
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_advance_loc: 4 to 0+0056
+  DW_CFA_restore: r3 \(rbx\)
+  DW_CFA_advance_loc: 4 to 0+005a
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.l
new file mode 100644
index 00000000000..229fb4e7035
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*4: Warning: --scfi=all ignores some user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.s
new file mode 100644
index 00000000000..e3b5ab30ff1
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.s
@@ -0,0 +1,48 @@
+# An example of static stack allocation by hand
+	.type foo, @function
+foo:
+	.cfi_startproc
+	/* Allocate space for 7 registers.  */
+	subq	$56,%rsp
+	.cfi_adjust_cfa_offset 56
+	movq	%rbx,(%rsp)
+	.cfi_rel_offset 3, 0
+	movq	%rbp,8(%rsp)
+	.cfi_rel_offset 6, 8
+	movq	%r12,16(%rsp)
+	.cfi_rel_offset 12, 16
+	movq	%r13,24(%rsp)
+	.cfi_rel_offset 13, 24
+	movq	%r14,32(%rsp)
+	.cfi_rel_offset 14, 32
+	movq	%r15,40(%rsp)
+	.cfi_rel_offset 15, 40
+	movq	%r9,48(%rsp)
+
+	/* Setup parameter for __foo_internal.  */
+	/* selfpc is the return address on the stack.  */
+	movq	56(%rsp),%rsi
+	/* Get frompc via the frame pointer.  */
+	movq	8(%rbp),%rdi
+	call __foo_internal
+	/* Pop the saved registers.  Please note that `foo' has no
+	   return value.  */
+	movq	48(%rsp),%r9
+
+	movq	40(%rsp),%r15
+	.cfi_restore 15
+	movq	32(%rsp),%r14
+	.cfi_restore 14
+	movq	24(%rsp),%r13
+	.cfi_restore 13
+	movq	16(%rsp),%r12
+	.cfi_restore 12
+	movq	8(%rsp),%rbp
+	.cfi_restore 6
+	movq	(%rsp),%rbx
+	.cfi_restore 3
+	addq	$56,%rsp
+	.cfi_adjust_cfa_offset -56
+	ret
+	.cfi_endproc
+	.size foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.d b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.d
new file mode 100644
index 00000000000..3caa66547f6
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.d
@@ -0,0 +1,41 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: Synthesize CFI for indirect mem op to stack 2
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+002c 0+001c FDE cie=00000000 pc=0+0000..0+0026
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_advance_loc: 3 to 0+0004
+  DW_CFA_def_cfa_register: r6 \(rbp\)
+  DW_CFA_advance_loc: 8 to 0+000c
+  DW_CFA_offset: r3 \(rbx\) at cfa-32
+  DW_CFA_advance_loc: 4 to 0+0010
+  DW_CFA_offset: r12 \(r12\) at cfa-24
+  DW_CFA_advance_loc: 9 to 0+0019
+  DW_CFA_restore: r3 \(rbx\)
+  DW_CFA_advance_loc: 4 to 0+001d
+  DW_CFA_restore: r12 \(r12\)
+  DW_CFA_advance_loc: 7 to 0+0024
+  DW_CFA_def_cfa_register: r7 \(rsp\)
+  DW_CFA_advance_loc: 1 to 0+0025
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.l b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.l
new file mode 100644
index 00000000000..0e094077edd
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: --scfi=all ignores some user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.s b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.s
new file mode 100644
index 00000000000..bed2050241c
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.s
@@ -0,0 +1,38 @@
+# Testcase for movq instructions
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+	.cfi_startproc
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset 6, -16
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register 6
+
+	subq	$16,%rsp
+
+# store rbx at %rsp; rsp = rbp - 16;
+	movq	%rbx, -16(%rbp)
+	.cfi_rel_offset 3, -16
+# store r12 at %rsp + 8; rsp = rbp -16;
+	movq	%r12, -8(%rbp)
+	.cfi_rel_offset 12, -8
+
+	call bar
+
+	movq	-16(%rbp), %rbx
+	.cfi_restore 3
+	movq	-8(%rbp), %r12
+	.cfi_restore 12
+
+	addq	$16,%rsp
+
+	mov	%rbp, %rsp
+	.cfi_def_cfa_register 7
+	pop	%rbp
+	.cfi_restore 6
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+	.size	foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.d b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.d
new file mode 100644
index 00000000000..8070e220b7a
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.d
@@ -0,0 +1,41 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: Synthesize CFI for indirect mem op to stack 3
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+002c 0+001c FDE cie=00000000 pc=0+0000..0+0028
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_advance_loc: 3 to 0+0004
+  DW_CFA_def_cfa_register: r6 \(rbp\)
+  DW_CFA_advance_loc: 8 to 0+000c
+  DW_CFA_offset: r3 \(rbx\) at cfa-32
+  DW_CFA_advance_loc: 5 to 0+0011
+  DW_CFA_offset: r12 \(r12\) at cfa-24
+  DW_CFA_advance_loc: 9 to 0+001a
+  DW_CFA_restore: r3 \(rbx\)
+  DW_CFA_advance_loc: 5 to 0+001f
+  DW_CFA_restore: r12 \(r12\)
+  DW_CFA_advance_loc: 7 to 0+0026
+  DW_CFA_def_cfa_register: r7 \(rsp\)
+  DW_CFA_advance_loc: 1 to 0+0027
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.l b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.l
new file mode 100644
index 00000000000..0e094077edd
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: --scfi=all ignores some user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.s b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.s
new file mode 100644
index 00000000000..0eb90bc34e3
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.s
@@ -0,0 +1,38 @@
+# Testcase for movq instructions
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+	.cfi_startproc
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset 6, -16
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register 6
+
+	subq	$16,%rsp
+
+# store rbx at %rsp; rsp = rbp - 16; rsp = CFA - 32
+	movq	%rbx, (%rsp)
+	.cfi_rel_offset 3, -16
+# store r12 at %rsp + 8; rsp = CFA - 32
+	movq	%r12, 8(%rsp)
+	.cfi_rel_offset 12, -8
+
+	call bar
+
+	movq	(%rsp), %rbx
+	.cfi_restore 3
+	movq	8(%rsp), %r12
+	.cfi_restore 12
+
+	addq	$16,%rsp
+
+	mov	%rbp, %rsp
+	.cfi_def_cfa_register 7
+	pop	%rbp
+	.cfi_restore 6
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+	.size	foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.d b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.d
new file mode 100644
index 00000000000..dbb16d7d05e
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.d
@@ -0,0 +1,63 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: Synthesize CFI for indirect mem op to stack 3
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+00000018 0+004c 0000001c FDE cie=00000000 pc=0+0000..0+003d
+  DW_CFA_advance_loc: 2 to 0+0002
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r15 \(r15\) at cfa-16
+  DW_CFA_advance_loc: 2 to 0+0004
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_offset: r14 \(r14\) at cfa-24
+  DW_CFA_advance_loc: 5 to 0+0009
+  DW_CFA_def_cfa_offset: 32
+  DW_CFA_offset: r13 \(r13\) at cfa-32
+  DW_CFA_advance_loc: 5 to 0+000e
+  DW_CFA_def_cfa_offset: 40
+  DW_CFA_offset: r12 \(r12\) at cfa-40
+  DW_CFA_advance_loc: 4 to 0+0012
+  DW_CFA_def_cfa_offset: 48
+  DW_CFA_offset: r6 \(rbp\) at cfa-48
+  DW_CFA_advance_loc: 4 to 0+0016
+  DW_CFA_def_cfa_offset: 56
+  DW_CFA_offset: r3 \(rbx\) at cfa-56
+  DW_CFA_advance_loc: 7 to 0+001d
+  DW_CFA_def_cfa_offset: 96
+  DW_CFA_advance_loc: 21 to 0+0032
+  DW_CFA_def_cfa_offset: 56
+  DW_CFA_advance_loc: 1 to 0+0033
+  DW_CFA_restore: r3 \(rbx\)
+  DW_CFA_def_cfa_offset: 48
+  DW_CFA_advance_loc: 1 to 0+0034
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 40
+  DW_CFA_advance_loc: 2 to 0+0036
+  DW_CFA_restore: r12 \(r12\)
+  DW_CFA_def_cfa_offset: 32
+  DW_CFA_advance_loc: 2 to 0+0038
+  DW_CFA_restore: r13 \(r13\)
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_advance_loc: 2 to 0+003a
+  DW_CFA_restore: r14 \(r14\)
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_advance_loc: 2 to 0+003c
+  DW_CFA_restore: r15 \(r15\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.l b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.l
new file mode 100644
index 00000000000..1793f4b4799
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*7: Warning: --scfi=all ignores some user-specified CFI directives
+.*41: Warning: SCFI: asymetrical register restore
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.s b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.s
new file mode 100644
index 00000000000..ae0c0fde16c
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.s
@@ -0,0 +1,68 @@
+# Testcase for save/unsave of callee-saved registers
+# Must be run -W as there is an expected warning as
+# noted below
+	.type	foo, @function
+foo:
+.LFB118:
+	.cfi_startproc
+	pushq	%r15
+	.cfi_def_cfa_offset 16
+	.cfi_offset 15, -16
+	pushq	%r14
+	.cfi_def_cfa_offset 24
+	.cfi_offset 14, -24
+	movl	%r8d, %r14d
+	pushq	%r13
+	.cfi_def_cfa_offset 32
+	.cfi_offset 13, -32
+	movq	%rdi, %r13
+	pushq	%r12
+	.cfi_def_cfa_offset 40
+	.cfi_offset 12, -40
+	movq	%rsi, %r12
+	pushq	%rbp
+	.cfi_def_cfa_offset 48
+	.cfi_offset 6, -48
+	movq	%rcx, %rbp
+	pushq	%rbx
+	.cfi_def_cfa_offset 56
+	.cfi_offset 3, -56
+	movq	%rdx, %rbx
+	subq	$40, %rsp
+	.cfi_def_cfa_offset 96
+	testb	$1, 37(%rdx)
+	je	.L2
+.L3:
+# The following is not a restore of r15: rbp has been used as
+# scratch register already.  The SCFI machinery must know that
+# REG_FP is not traceable.
+# A warning here is expected:
+# 41: Warning: SCFI: asymetrical register restore
+	movq	32(%rbp), %r15
+	cmpq	$0, 64(%r15)
+	je	.L2
+.L2:
+	addq	$40, %rsp
+	.cfi_def_cfa_offset 56
+	popq	%rbx
+	.cfi_restore 3
+	.cfi_def_cfa_offset 48
+	popq	%rbp
+	.cfi_restore 6
+	.cfi_def_cfa_offset 40
+	popq	%r12
+	.cfi_restore 12
+	.cfi_def_cfa_offset 32
+	popq	%r13
+	.cfi_restore 13
+	.cfi_def_cfa_offset 24
+	popq	%r14
+	.cfi_restore 14
+	.cfi_def_cfa_offset 16
+	popq	%r15
+	.cfi_restore 15
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE118:
+	.size	foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-lea-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-lea-1.d
new file mode 100644
index 00000000000..d2fa442df63
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-lea-1.d
@@ -0,0 +1,37 @@
+#as: --scfi -W -O2
+#objdump: -Wf
+#name: Synthesize CFI for various lea instructions (-O2)
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0024 0+001c FDE cie=00000000 pc=0+0000..0+0029
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_advance_loc: 3 to 0+0004
+  DW_CFA_def_cfa_register: r6 \(rbp\)
+  DW_CFA_advance_loc: 2 to 0+0006
+  DW_CFA_offset: r13 \(r13\) at cfa-24
+  DW_CFA_advance_loc: 33 to 0+0027
+  DW_CFA_restore: r13 \(r13\)
+  DW_CFA_advance_loc: 1 to 0+0028
+  DW_CFA_def_cfa_register: r7 \(rsp\)
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-lea-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-lea-1.l
new file mode 100644
index 00000000000..2762adba5b1
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-lea-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*8: Warning: --scfi=all ignores some user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-lea-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-lea-1.s
new file mode 100644
index 00000000000..8fb8fe794d6
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-lea-1.s
@@ -0,0 +1,40 @@
+## Testcase with a variety of lea.
+## This test is run with -O2 by default to check
+## SCFI in wake of certain target optimizations.
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+	.cfi_startproc
+	pushq   %rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset 6, -16
+# This lea gets transformed to mov %rsp, %rbp when -O2.
+# The SCFI machinery must see it as such.
+        lea    (%rsp), %rbp
+	.cfi_def_cfa_register 6
+	push   %r13
+	.cfi_offset 13, -24
+	subq   $8, %rsp
+	testl  %eax, %eax
+	jle   .L2
+.L3:
+	movq   %rsp, %r12
+        lea    -0x2(%r13),%rax
+        lea    0x8(%r12,%rdx,4),%r8
+	movq   %r12, %rsp
+	jne   .L3
+.L2:
+	leaq	-8(%rbp), %rsp
+	xorl	%eax, %eax
+	popq	%r13
+	.cfi_restore 13
+	popq	%rbp
+	.cfi_def_cfa_register 7
+	.cfi_restore 6
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	foo, .-foo
+
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-leave-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-leave-1.d
new file mode 100644
index 00000000000..0d9e5943a30
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-leave-1.d
@@ -0,0 +1,36 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: Synthesize CFI for leave insn
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0024 0+001c FDE cie=0+0000 pc=0+0000..0+000a
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_advance_loc: 3 to 0+0004
+  DW_CFA_def_cfa_register: r6 \(rbp\)
+  DW_CFA_advance_loc: 1 to 0+0005
+  DW_CFA_offset: r3 \(rbx\) at cfa-24
+  DW_CFA_advance_loc: 3 to 0+0008
+  DW_CFA_restore: r3 \(rbx\)
+  DW_CFA_advance_loc: 1 to 0+0009
+  DW_CFA_def_cfa_register: r7 \(rsp\)
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-leave-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-leave-1.l
new file mode 100644
index 00000000000..0e094077edd
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-leave-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: --scfi=all ignores some user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-leave-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-leave-1.s
new file mode 100644
index 00000000000..0466ab15708
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-leave-1.s
@@ -0,0 +1,26 @@
+# Testcase for leave insn
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+	.cfi_startproc
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset 6, -16
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register 6
+	push    %rbx
+	.cfi_offset 3, -24
+	push    %rdi
+	pop	%rdi
+	pop	%rbx
+	.cfi_restore 3
+	leave
+	.cfi_def_cfa_register 7
+	.cfi_restore 6
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	foo, .-foo
+
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.d
new file mode 100644
index 00000000000..e37d4a93917
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.d
@@ -0,0 +1,35 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: Synthesize CFI for pushq insns
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0024 0+001c FDE cie=0+0000 pc=0+0000..0+000c
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_advance_loc: 3 to 0+0004
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_advance_loc: 2 to 0+0006
+  DW_CFA_def_cfa_offset: 32
+  DW_CFA_advance_loc: 2 to 0+0008
+  DW_CFA_def_cfa_offset: 40
+  DW_CFA_advance_loc: 2 to 0+0000a
+  DW_CFA_def_cfa_offset: 48
+  DW_CFA_advance_loc: 1 to 0+0000b
+  DW_CFA_def_cfa_offset: 56
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.l
new file mode 100644
index 00000000000..bba199d24b9
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*7: Warning: --scfi=all ignores some user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.s
new file mode 100644
index 00000000000..b896a295b56
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.s
@@ -0,0 +1,24 @@
+## Testcase with a variety of pushq.
+## all push insns valid in 64-bit mode must be processed for SCFI.
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+	.cfi_startproc
+	pushq	%rax
+	.cfi_def_cfa_offset 16
+	pushq   16(%rax)
+	.cfi_def_cfa_offset 24
+	pushq	$36
+	.cfi_def_cfa_offset 32
+	pushq   %fs
+	.cfi_def_cfa_offset 40
+	pushq   %gs
+	.cfi_def_cfa_offset 48
+	pushf
+	.cfi_def_cfa_offset 56
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	foo, .-foo
+
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.d
new file mode 100644
index 00000000000..1fd0af4d8ae
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.d
@@ -0,0 +1,43 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: Synthesize CFI with pushsection 1
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+00000018 0+0010 0000001c FDE cie=00000000 pc=0+0000..0+0005
+  DW_CFA_advance_loc: 4 to 0+0004
+  DW_CFA_def_cfa_offset: 48
+
+0000002c 0+0028 00000030 FDE cie=00000000 pc=0+0000..0+0014
+  DW_CFA_advance_loc: 2 to 0+0002
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r12 \(r12\) at cfa-16
+  DW_CFA_advance_loc: 2 to 0+0004
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_offset: r13 \(r13\) at cfa-24
+  DW_CFA_advance_loc: 4 to 0+0008
+  DW_CFA_def_cfa_offset: 32
+  DW_CFA_advance_loc: 7 to 0+000f
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_advance_loc: 2 to 0+0011
+  DW_CFA_restore: r13 \(r13\)
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_advance_loc: 2 to 0+0013
+  DW_CFA_restore: r12 \(r12\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.l
new file mode 100644
index 00000000000..bba199d24b9
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*7: Warning: --scfi=all ignores some user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.s
new file mode 100644
index 00000000000..4bee003e6f5
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.s
@@ -0,0 +1,40 @@
+# Testcase for pushsection directive and SCFI.
+# The .pushsection directive must cause creation of a new FDE.
+        .text
+        .globl  foo
+        .type   foo, @function
+foo:
+        .cfi_startproc
+        pushq   %r12
+        .cfi_def_cfa_offset 16
+        .cfi_offset 12, -16
+        pushq   %r13
+        .cfi_def_cfa_offset 24
+        .cfi_offset 13, -24
+        subq    $8, %rsp
+        .cfi_def_cfa_offset 32
+	mov	%rax, %rbx
+	.pushsection .text2
+# It's the .type directive here that enforces SCFI generation
+# for the code block that follows
+	.type   foo2, @function
+foo2:
+	.cfi_startproc
+	subq    $40, %rsp
+	.cfi_def_cfa_offset 48
+	ret
+	.cfi_endproc
+	.size foo2, .-foo2
+	.popsection
+	addq	$8, %rsp
+	.cfi_def_cfa_offset 24
+        popq    %r13
+        .cfi_restore 13
+        .cfi_def_cfa_offset 16
+        popq    %r12
+        .cfi_restore 12
+        .cfi_def_cfa_offset 8
+        ret
+        .cfi_endproc
+.LFE0:
+        .size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.d b/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.d
new file mode 100644
index 00000000000..3e6ebc15aa3
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.d
@@ -0,0 +1,39 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: Synthesize CFI for demarcated code blocks 2
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+00000018 0+002c 0000001c FDE cie=00000000 pc=0+0000..0+0014
+  DW_CFA_advance_loc: 2 to 0+0002
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r12 \(r12\) at cfa-16
+  DW_CFA_advance_loc: 2 to 0+0004
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_offset: r13 \(r13\) at cfa-24
+  DW_CFA_advance_loc: 4 to 0+0008
+  DW_CFA_def_cfa_offset: 32
+  DW_CFA_advance_loc: 7 to 0+000f
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_advance_loc: 2 to 0+0011
+  DW_CFA_restore: r13 \(r13\)
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_advance_loc: 2 to 0+0013
+  DW_CFA_restore: r12 \(r12\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.l b/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.l
new file mode 100644
index 00000000000..0e094077edd
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: --scfi=all ignores some user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.s b/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.s
new file mode 100644
index 00000000000..e7b6dbc2526
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.s
@@ -0,0 +1,38 @@
+# Testcase for pushsection directive and SCFI.
+        .text
+        .globl  foo
+        .type   foo, @function
+foo:
+        .cfi_startproc
+        pushq   %r12
+        .cfi_def_cfa_offset 16
+        .cfi_offset 12, -16
+        pushq   %r13
+        .cfi_def_cfa_offset 24
+        .cfi_offset 13, -24
+        subq    $8, %rsp
+        .cfi_def_cfa_offset 32
+	mov	%rax, %rbx
+# The .pushsection directive creates a new code block,
+# which must not contribute ginsn to the existing one.
+	.pushsection .text2
+# For CFI to be synthesized for this block, the user should have
+# demarcated the beginning with a .type name, @function
+	.cfi_startproc
+	subq    $40, %rsp
+	.cfi_def_cfa_offset 48
+	ret
+	.cfi_endproc
+	.popsection
+	addq	$8, %rsp
+	.cfi_def_cfa_offset 24
+        popq    %r13
+        .cfi_restore 13
+        .cfi_def_cfa_offset 16
+        popq    %r12
+        .cfi_restore 12
+        .cfi_def_cfa_offset 8
+        ret
+        .cfi_endproc
+.LFE0:
+        .size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.d
new file mode 100644
index 00000000000..c05c0314c0f
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.d
@@ -0,0 +1,31 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: Synthesize CFI for self-aligning func
+#...
+Contents of the .eh_frame section:
+
+0+0000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+001c 0000001c FDE cie=00000000 pc=0+0000..0+002f
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_advance_loc: 3 to 0+0004
+  DW_CFA_def_cfa_register: r6 \(rbp\)
+  DW_CFA_advance_loc: 42 to 0+002e
+  DW_CFA_def_cfa_register: r7 \(rsp\)
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.l
new file mode 100644
index 00000000000..bba199d24b9
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*7: Warning: --scfi=all ignores some user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.s
new file mode 100644
index 00000000000..51a40b5940b
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.s
@@ -0,0 +1,36 @@
+# If it is known that the caller of self_aligning_foo may have had
+# the stack pointer unaligned to 16-bytes boundary,  such self-aligning
+# functions may be used by asm programmers.
+	.globl  self_aligning_foo
+        .type   self_aligning_foo, @function
+self_aligning_foo:
+        .cfi_startproc
+        pushq   %rbp
+        .cfi_def_cfa_offset 16
+        .cfi_offset 6, -16
+        movq    %rsp, %rbp
+        .cfi_def_cfa_register 6
+# The following 'and' op aligns the stack pointer.
+# At the same time, it causes REG_SP to become non-traceable
+# for SCFI purposes.  But no warning is issued as no further stack
+# size tracking is needed for SCFI purposes.
+        andq    $-16, %rsp
+        subq    $32, %rsp
+        movl    %edi, 12(%rsp)
+        movl    %esi, 8(%rsp)
+        movl    $0, %eax
+        call    vector_using_function
+        movaps  %xmm0, 16(%rsp)
+        movl    12(%rsp), %edx
+        movl    8(%rsp), %eax
+        addl    %edx, %eax
+        leave
+# GCC typically generates a '.cfi_def_cfa 7, 8' for leave
+# insn.  The SCFI however, will generate the following:
+	.cfi_def_cfa_register 7
+	.cfi_restore 6
+	.cfi_def_cfa_offset 8
+        ret
+       .cfi_endproc
+.LFE0:
+        .size   self_aligning_foo, .-self_aligning_foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-simple-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-simple-1.d
new file mode 100644
index 00000000000..849833cb5c8
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-simple-1.d
@@ -0,0 +1,26 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: Synthesize CFI simple test 1
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0014 0+001c FDE cie=0+0000 pc=0+0000..0+0002
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_nop
+  DW_CFA_nop
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-simple-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-simple-1.l
new file mode 100644
index 00000000000..bba199d24b9
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-simple-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*7: Warning: --scfi=all ignores some user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-simple-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-simple-1.s
new file mode 100644
index 00000000000..f36b553cbff
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-simple-1.s
@@ -0,0 +1,15 @@
+# Simple test
+# A wierd function, but SCFI machinery does not complain yet.
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+	.cfi_startproc
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset 6, -16
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	foo, .-foo
+
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-simple-2.d b/gas/testsuite/gas/scfi/x86_64/scfi-simple-2.d
new file mode 100644
index 00000000000..2fa5e565f8b
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-simple-2.d
@@ -0,0 +1,30 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: Synthesize CFI simple test 2
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0001c 0+0001c FDE cie=0+0000 pc=0+0000..0+0003
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_advance_loc: 1 to 0+0002
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-simple-2.l b/gas/testsuite/gas/scfi/x86_64/scfi-simple-2.l
new file mode 100644
index 00000000000..ce97dd14432
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-simple-2.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*5: Warning: --scfi=all ignores some user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-simple-2.s b/gas/testsuite/gas/scfi/x86_64/scfi-simple-2.s
new file mode 100644
index 00000000000..e778cef517b
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-simple-2.s
@@ -0,0 +1,16 @@
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+	.cfi_startproc
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset 6, -16
+	popq	%rbp
+	.cfi_restore 6
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	foo, .-foo
+
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-sub-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-sub-1.d
new file mode 100644
index 00000000000..80fb5bfb210
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-sub-1.d
@@ -0,0 +1,25 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: Synthesize CFI for sub insn 1
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0014 0+001c FDE cie=0+0000 pc=0+0000..0+0008
+  DW_CFA_advance_loc: 7 to 0+0007
+  DW_CFA_def_cfa_offset: 120016
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-sub-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-sub-1.l
new file mode 100644
index 00000000000..0e094077edd
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-sub-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: --scfi=all ignores some user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-sub-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-sub-1.s
new file mode 100644
index 00000000000..741a0abf69f
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-sub-1.s
@@ -0,0 +1,12 @@
+# Testcase for sub instruction.
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+	.cfi_startproc
+        subq    $120008, %rsp
+        .cfi_def_cfa_offset 120016
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-sub-2.d b/gas/testsuite/gas/scfi/x86_64/scfi-sub-2.d
new file mode 100644
index 00000000000..3fb232f1e1b
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-sub-2.d
@@ -0,0 +1,31 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: Synthesize CFI for sub insn 2
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+001c 0+001c FDE cie=0+0000 pc=0+0000..0+001d
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_advance_loc: 3 to 0+0004
+  DW_CFA_def_cfa_register: r6 \(rbp\)
+  DW_CFA_advance_loc: 24 to 0+001c
+  DW_CFA_def_cfa_register: r7 \(rsp\)
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-sub-2.l b/gas/testsuite/gas/scfi/x86_64/scfi-sub-2.l
new file mode 100644
index 00000000000..0e094077edd
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-sub-2.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: --scfi=all ignores some user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-sub-2.s b/gas/testsuite/gas/scfi/x86_64/scfi-sub-2.s
new file mode 100644
index 00000000000..2435c4d5c1a
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-sub-2.s
@@ -0,0 +1,29 @@
+# Testcase for sub reg, reg instruction.
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+	.cfi_startproc
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset 6, -16
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register 6
+	subq    %rax, %rsp
+# SCFI: Stack-pointer manipulation after switching
+# to RBP based tracking is OK.
+	addq	%rax, %rsp
+# Other kind of sub instructions should not error out in the
+# x86_64 -> ginsn translator
+	subq    (%r12), %rax
+	subq	$1,(%rdi)
+	subq    %rax, 40(%r12)
+	subl    %edx, -32(%rsp)
+	leave
+	.cfi_def_cfa_register 7
+	.cfi_restore 6
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.l
new file mode 100644
index 00000000000..c856c08c2ae
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.l
@@ -0,0 +1,2 @@
+Assembler messages:
+Fatal error: Synthesizing CFI is not supported for this ABI
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.s
new file mode 100644
index 00000000000..87d2a4971a1
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.s
@@ -0,0 +1,10 @@
+# Testcase run with --m32 (Not supported).
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+	pushq	%rbp
+	ret
+.LFE0:
+	.size	foo, .-foo
+
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-2.l b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-2.l
new file mode 100644
index 00000000000..69c71fb9d7e
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-2.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*8: Error: SCFI: unsupported stack manipulation pattern
+.*13: Warning: SCFI: forward pass failed for func 'foo'
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-2.s b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-2.s
new file mode 100644
index 00000000000..22e9d88ea58
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-2.s
@@ -0,0 +1,14 @@
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+	addq    %rdx, %rax
+# Stack manipulation without switching to RBP
+# based tracking is not supported for SCFI.
+	addq    %rax, %rsp
+	push    %rdi
+	leave
+	ret
+.LFE0:
+	.size	foo, .-foo
+
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-3.l b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-3.l
new file mode 100644
index 00000000000..69c71fb9d7e
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-3.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*8: Error: SCFI: unsupported stack manipulation pattern
+.*13: Warning: SCFI: forward pass failed for func 'foo'
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-3.s b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-3.s
new file mode 100644
index 00000000000..c515171f2b5
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-3.s
@@ -0,0 +1,14 @@
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+	addq    %rdx, %rax
+# Stack manipulation without switching to RBP
+# based tracking is not supported for SCFI.
+	movq    %rax, %rsp
+	push    %rdi
+	leave
+	ret
+.LFE0:
+	.size	foo, .-foo
+
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-4.l b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-4.l
new file mode 100644
index 00000000000..c30d2057ddb
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-4.l
@@ -0,0 +1,4 @@
+.*Assembler messages:
+.*6: Warning: --scfi=all ignores some user-specified CFI directives
+.*14: Error: SCFI: unsupported stack manipulation pattern
+.*22: Warning: SCFI: forward pass failed for func 'foo'
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-4.s b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-4.s
new file mode 100644
index 00000000000..51ff92b7e50
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-4.s
@@ -0,0 +1,23 @@
+	.type	foo, @function
+foo:
+.LFB16:
+#	.cfi_startproc
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset 6, -16
+	movq    %rsp, %rbp
+# The following stack manipulation is OK to do, as CFA tracking
+# REG_FP based.
+	movq	%rax, %rsp
+# But...not quite, if there is a follow up usage of stack
+# like a register save of a callee-saved register.
+	push	%rbx
+	mov	%rbp, %rsp
+	popq	%rbp
+	.cfi_def_cfa 7, 8
+	.cfi_restore 6
+	ret
+#	.cfi_endproc
+.LFE16:
+	.size	foo, .-foo
+
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-1.l
new file mode 100644
index 00000000000..ac0d49388d6
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-1.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*50: Warning: --scfi=all ignores some user-specified CFI directives
+.*52: Warning: Untraceable control flow for func 'foo'; Skipping SCFI
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-1.s
new file mode 100644
index 00000000000..7d241f880d7
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-1.s
@@ -0,0 +1,53 @@
+# Testcase with an indirect jump
+# Indirect jumps, when present, make the list of ginsn an invalid
+# candidate for CFG creation.  Hence, no SCFI can be generated either.
+#
+# The testcase is rather long to showcase a simple concept.  The reason of
+# such a long testcase is to discuss if it is important to deal with these
+# patterns.  It may be possible to deal with this, if we allow some special
+# directives for helping the assembler with the indirect jump (jump table).
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+        pushq   %rbp
+        movq    %rsp, %rbp
+        movl    %edi, -4(%rbp)
+        cmpl    $5, -4(%rbp)
+        ja      .L2
+        movl    -4(%rbp), %eax
+        movq    .L4(,%rax,8), %rax
+        jmp     *%rax
+.L4:
+        .quad   .L9
+        .quad   .L8
+        .quad   .L7
+        .quad   .L6
+        .quad   .L5
+        .quad   .L3
+.L9:
+        movl    $43, %eax
+        jmp     .L1
+.L8:
+        movl    $42, %eax
+        jmp     .L1
+.L7:
+        movl    $45, %eax
+        jmp     .L1
+.L6:
+        movl    $47, %eax
+        jmp     .L1
+.L5:
+        movl    $37, %eax
+        jmp     .L1
+.L3:
+        movl    $63, %eax
+        jmp     .L1
+.L2:
+.L1:
+        popq    %rbp
+        ret
+	.cfi_endproc
+.LFE0:
+	.size	foo, .-foo
+
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-drap-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-drap-1.l
new file mode 100644
index 00000000000..c4e48ced26e
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-drap-1.l
@@ -0,0 +1,4 @@
+.*Assembler messages:
+.*16: Warning: --scfi=all ignores some user-specified CFI directives
+.*19: Error: SCFI: unsupported stack manipulation pattern
+.*75: Warning: SCFI: forward pass failed for func 'drap_foo'
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-drap-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-drap-1.s
new file mode 100644
index 00000000000..c23828b8f9b
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-drap-1.s
@@ -0,0 +1,75 @@
+# Testcase with Dynamically Realigned Argument Pointer (DRAP)
+# register usage.
+#
+# There are two reasons why this cannot be supported with the current
+# SCFI machinery
+# 1. Not allowed: REG_CFA is r10 based for the few insns after
+#    'leaq    8(%rsp), %r10'.
+# 2. Untraceable stack size after 'andq    $-16, %rsp'
+# Both of these shortcomings may be worked out. FIXME DISCUSS Keep the rather
+# long testcase until then.
+	.text
+	.globl	drap_foo
+	.type	drap_foo, @function
+drap_foo:
+.LFB0:
+	.cfi_startproc
+	leaq	8(%rsp), %r10
+	.cfi_def_cfa 10, 0
+	andq	$-16, %rsp
+	pushq	-8(%r10)
+        .cfi_def_cfa 7, 8
+	pushq	%rbp
+	.cfi_offset 6, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register 6
+	pushq	%r15
+	pushq	%r14
+	pushq	%r13
+	pushq	%r12
+	pushq	%r10
+	.cfi_offset 15, -24
+	.cfi_offset 14, -32
+	.cfi_offset 13, -40
+	.cfi_offset 12, -48
+	pushq	%rbx
+	.cfi_offset 3, -64
+	subq	$32, %rsp
+	movq	$0, (%rdx)
+	cmpq	$0, (%rdi)
+	movq	$0, -56(%rbp)
+	je	.L21
+	movq	%rdi, %rbx
+	movq	%rsi, %rdi
+	movq	%rsi, %r12
+	call	func2@PLT
+	movq	(%rbx), %rdi
+	leaq	-56(%rbp), %rdx
+	movslq	%eax, %rsi
+	call	func1@PLT
+	testl	%eax, %eax
+	je	.L21
+	movq	-56(%rbp), %r13
+.L21:
+	addq	$32, %rsp
+	xorl	%eax, %eax
+	popq	%rbx
+	.cfi_restore 3
+	popq	%r10
+	popq	%r12
+	.cfi_restore 12
+	popq	%r13
+	.cfi_restore 13
+	popq	%r14
+	.cfi_restore 14
+	popq	%r15
+	.cfi_restore 15
+	popq	%rbp
+	.cfi_restore 6
+	.cfi_def_cfa_register 7
+	.cfi_def_cfa_offset 8
+	leaq	-8(%r10), %rsp
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	drap_foo, .-drap_foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-x86-64.exp b/gas/testsuite/gas/scfi/x86_64/scfi-x86-64.exp
new file mode 100644
index 00000000000..ff8f163f18e
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-x86-64.exp
@@ -0,0 +1,103 @@
+# Copyright (C) 2022-2023 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+
+if { ![is_elf_format] } then {
+    return
+}
+
+# common tests
+if  { ([istarget "x86_64-*-*"]) } then {
+
+    global ASFLAGS
+    set old_ASFLAGS "$ASFLAGS"
+
+    run_dump_test "scfi-cfi-label-1"
+    run_list_test "scfi-cfi-label-1" "--scfi --warn"
+
+    run_list_test "scfi-diag-1" "--scfi"
+    run_list_test "scfi-fp-diag-2" "--scfi"
+    run_list_test "scfi-diag-2" "--scfi"
+
+    run_list_test "scfi-unsupported-1" "--32 --scfi"
+    run_list_test "scfi-unsupported-2" "--scfi"
+    run_list_test "scfi-unsupported-3" "--scfi"
+    run_list_test "scfi-unsupported-4" "--scfi"
+    run_list_test "scfi-unsupported-drap-1" "--scfi"
+    run_list_test "scfi-unsupported-cfg-1" "--scfi"
+
+    run_dump_test "scfi-add-1"
+    run_list_test "scfi-add-1" "--scfi --warn"
+    run_dump_test "scfi-add-2"
+    run_list_test "scfi-add-2" "--scfi --warn"
+    run_dump_test "scfi-cfg-1"
+    run_list_test "scfi-cfg-1" "--scfi --warn"
+    run_dump_test "scfi-cfg-2"
+    run_list_test "scfi-cfg-2" "--scfi --warn"
+    run_dump_test "scfi-asm-marker-1"
+    run_list_test "scfi-asm-marker-1" "--scfi --warn"
+    run_dump_test "scfi-asm-marker-2"
+    run_list_test "scfi-asm-marker-2" "--scfi --warn"
+    run_dump_test "scfi-asm-marker-3"
+    run_list_test "scfi-asm-marker-3" "--scfi --warn"
+    run_dump_test "scfi-pushsection-1"
+    run_list_test "scfi-pushsection-1" "--scfi --warn"
+    run_dump_test "scfi-pushsection-2"
+    run_list_test "scfi-pushsection-2" "--scfi --warn"
+
+    run_dump_test "scfi-cofi-1"
+    run_list_test "scfi-cofi-1" "--scfi --warn"
+    run_dump_test "scfi-sub-1"
+    run_list_test "scfi-sub-1" "--scfi --warn"
+    run_dump_test "scfi-sub-2"
+    run_list_test "scfi-sub-2" "--scfi --warn"
+    run_dump_test "scfi-ignore-1"
+    run_list_test "scfi-ignore-1" "--scfi --warn"
+    run_dump_test "scfi-simple-1"
+    run_list_test "scfi-simple-1" "--scfi --warn"
+    run_dump_test "scfi-simple-2"
+    run_list_test "scfi-simple-2" "--scfi --warn"
+    run_dump_test "scfi-pushq-1"
+    run_list_test "scfi-pushq-1" "--scfi --warn"
+    run_dump_test "scfi-lea-1"
+    run_list_test "scfi-lea-1" "--scfi --warn"
+    run_dump_test "scfi-leave-1"
+    run_list_test "scfi-leave-1" "--scfi --warn"
+    run_dump_test "scfi-bp-sp-1"
+    run_list_test "scfi-bp-sp-1" "--scfi --warn"
+    run_dump_test "scfi-bp-sp-2"
+    run_list_test "scfi-bp-sp-2" "--scfi --warn"
+    run_dump_test "scfi-cfg-1"
+    run_list_test "scfi-cfg-1" "--scfi --warn"
+    run_dump_test "scfi-callee-saved-2"
+    run_list_test "scfi-callee-saved-2" "--scfi --warn"
+    run_dump_test "scfi-callee-saved-3"
+    run_list_test "scfi-callee-saved-3" "--scfi --warn"
+    run_dump_test "scfi-callee-saved-4"
+    run_list_test "scfi-callee-saved-4" "--scfi --warn"
+    run_dump_test "scfi-dyn-stack-1"
+    run_list_test "scfi-dyn-stack-1" "--scfi --warn"
+    run_dump_test "scfi-indirect-mov-1"
+    run_list_test "scfi-indirect-mov-1" "--scfi --warn"
+    run_dump_test "scfi-indirect-mov-2"
+    run_list_test "scfi-indirect-mov-2" "--scfi --warn"
+    run_dump_test "scfi-indirect-mov-3"
+    run_list_test "scfi-indirect-mov-3" "--scfi --warn"
+    run_dump_test "scfi-indirect-mov-4"
+    run_list_test "scfi-indirect-mov-4" "--scfi --warn"
+    run_dump_test "scfi-selfalign-func-1"
+    run_list_test "scfi-selfalign-func-1" "--scfi --warn"
+}
+
-- 
2.41.0


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

* [PATCH, V2 10/10] gas/NEWS: announce the new command line option
  2023-10-30 16:51 [PATCH, V2 00/10] Synthesize CFI for hand-written asm Indu Bhagat
                   ` (8 preceding siblings ...)
  2023-10-30 16:51 ` [PATCH, V2 09/10] gas: testsuite: add a x86_64 testsuite for SCFI Indu Bhagat
@ 2023-10-30 16:51 ` Indu Bhagat
  9 siblings, 0 replies; 30+ messages in thread
From: Indu Bhagat @ 2023-10-30 16:51 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[New patch in the current patch set]
---
 gas/NEWS | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/gas/NEWS b/gas/NEWS
index 71a1269b893..eddec7fa2c2 100644
--- a/gas/NEWS
+++ b/gas/NEWS
@@ -1,4 +1,6 @@
 -*- text -*-
+* New command line option --scfi[=all,none] to synthesize CFI for hand-written
+  asm.
 
 * Add support for Intel AVX10.1.
 
-- 
2.41.0


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

* Re: [PATCH, V2 06/10] gas: scfidw2gen: new functionality to prepapre for SCFI
  2023-10-30 16:51 ` [PATCH, V2 06/10] gas: scfidw2gen: new functionality to prepapre for SCFI Indu Bhagat
@ 2023-10-31 11:28   ` Jan Beulich
  2023-10-31 22:06     ` Indu Bhagat
  0 siblings, 1 reply; 30+ messages in thread
From: Jan Beulich @ 2023-10-31 11:28 UTC (permalink / raw)
  To: Indu Bhagat; +Cc: binutils

On 30.10.2023 17:51, Indu Bhagat wrote:
> --- /dev/null
> +++ b/gas/scfidw2gen.c
> @@ -0,0 +1,305 @@
> +/* scfidw2gen.c - Support for emission of synthesized Dwarf2 CFI.
> +   Copyright (C) 2003-2023 Free Software Foundation, Inc.

Is this year range really applicable to this new file?

> +   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 "dw2gencfi.h"
> +#include "subsegs.h"
> +#include "scfidw2gen.h"
> +
> +#if defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN)
> +
> +static int scfi_ignore_warn_once = 0;

Nit: bool please (and no real need for an initializer).

> +static void dot_scfi_sections (int);
> +static void dot_scfi_ignore (int);
> +static void dot_scfi (int);

May I suggest to avoid such forward declarations by moving ...

> +const pseudo_typeS scfi_pseudo_table[] =

... this table towards the bottom of the file?

> +  {
> +    { "cfi_sections", dot_scfi_sections, 0 }, /* No ignore.  */

Instead of three such individual comments, how about putting the three
relevant ones first, followed by a comment (serving as a separator) and
then all dot_scfi_ignore entries?

> +    { "cfi_startproc", dot_scfi_ignore, 0 },
> +    { "cfi_endproc", dot_scfi_ignore, 0 },
> +    { "cfi_fde_data", dot_scfi_ignore, 0 },
> +    { "cfi_def_cfa", dot_scfi_ignore, 0 },
> +    { "cfi_def_cfa_register", dot_scfi_ignore, 0 },
> +    { "cfi_def_cfa_offset", dot_scfi_ignore, 0 },
> +    { "cfi_adjust_cfa_offset", dot_scfi_ignore, 0 },
> +    { "cfi_offset", dot_scfi_ignore, 0 },
> +    { "cfi_rel_offset", dot_scfi_ignore, 0 },
> +    { "cfi_register", dot_scfi_ignore, 0 },
> +    { "cfi_return_column", dot_scfi_ignore, 0 },
> +    { "cfi_restore", dot_scfi_ignore, 0 },
> +    { "cfi_undefined", dot_scfi_ignore, 0 },
> +    { "cfi_same_value", dot_scfi_ignore, 0 },
> +    { "cfi_remember_state", dot_scfi_ignore, 0 },
> +    { "cfi_restore_state", dot_scfi_ignore, 0 },
> +    { "cfi_window_save", dot_scfi_ignore, 0 },
> +    { "cfi_negate_ra_state", dot_scfi_ignore, 0 },
> +    { "cfi_escape", dot_scfi_ignore, 0 },
> +    { "cfi_signal_frame", dot_scfi, CFI_signal_frame }, /* No ignore.  */
> +    { "cfi_personality", dot_scfi_ignore, 0 },
> +    { "cfi_personality_id", dot_scfi_ignore, 0 },
> +    { "cfi_lsda", dot_scfi_ignore, 0 },
> +    { "cfi_val_encoded_addr", dot_scfi_ignore, 0 },
> +    { "cfi_inline_lsda", dot_scfi_ignore, 0 },
> +    { "cfi_label", dot_scfi, CFI_label }, /* No ignore.  */
> +    { "cfi_val_offset", dot_scfi_ignore, 0 },
> +    { NULL, NULL, 0 }
> +  };
> +
> +static void
> +dot_scfi_ignore (int ignored ATTRIBUTE_UNUSED)
> +{
> +  gas_assert (flag_synth_cfi);
> +
> +  if (scfi_ignore_warn_once == 0)
> +    {
> +      as_warn (_("--scfi=all ignores some user-specified CFI directives"));

s/some/most/ ?

> +void
> +scfi_dot_cfi_startproc (symbolS *start_sym)

This and the following two functions presently have no caller, and I
also can't spot equivalents in dw2gencfi.c. How are they (going) to be
used? This ...

> +{
> +  if (frchain_now->frch_cfi_data != NULL)
> +    {
> +      as_bad (_("previous CFI entry not closed (missing .cfi_endproc)"));

... for example suggests that the function here might really be the
handler for .cfi_startproc, yet the table above says .cfi_startproc is
ignored.

> +#else
> +
> +static void
> +dot_scfi_dummy (int ignored ATTRIBUTE_UNUSED)
> +{
> +  as_bad (_("SCFI is not supported for this target"));
> +  ignore_rest_of_line ();
> +}
> +
> +const pseudo_typeS scfi_pseudo_table[] =
> +  {
> +    { "cfi_sections", dot_scfi_dummy, 0 },
> +    { "cfi_startproc", dot_scfi_dummy, 0 },
> +    { "cfi_endproc", dot_scfi_dummy, 0 },
> +    { "cfi_fde_data", dot_scfi_dummy, 0 },
> +    { "cfi_def_cfa", dot_scfi_dummy, 0 },
> +    { "cfi_def_cfa_register", dot_scfi_dummy, 0 },
> +    { "cfi_def_cfa_offset", dot_scfi_dummy, 0 },
> +    { "cfi_adjust_cfa_offset", dot_scfi_dummy, 0 },
> +    { "cfi_offset", dot_scfi_dummy, 0 },
> +    { "cfi_rel_offset", dot_scfi_dummy, 0 },
> +    { "cfi_register", dot_scfi_dummy, 0 },
> +    { "cfi_return_column", dot_scfi_dummy, 0 },
> +    { "cfi_restore", dot_scfi_dummy, 0 },
> +    { "cfi_undefined", dot_scfi_dummy, 0 },
> +    { "cfi_same_value", dot_scfi_dummy, 0 },
> +    { "cfi_remember_state", dot_scfi_dummy, 0 },
> +    { "cfi_restore_state", dot_scfi_dummy, 0 },
> +    { "cfi_window_save", dot_scfi_dummy, 0 },
> +    { "cfi_negate_ra_state", dot_scfi_dummy, 0 },
> +    { "cfi_escape", dot_scfi_dummy, 0 },
> +    { "cfi_signal_frame", dot_scfi_dummy, 0 },
> +    { "cfi_personality", dot_scfi_dummy, 0 },
> +    { "cfi_personality_id", dot_scfi_dummy, 0 },
> +    { "cfi_lsda", dot_scfi_dummy, 0 },
> +    { "cfi_val_encoded_addr", dot_scfi_dummy, 0 },
> +    { "cfi_inline_lsda", dot_scfi_dummy, 0 },
> +    { "cfi_label", dot_scfi_dummy, 0 },
> +    { "cfi_val_offset", dot_scfi_dummy, 0 },
> +    { NULL, NULL, 0 }
> +  };
> +
> +#endif

Is this really needed? Can't you simply error on use of the command
line option, without the need for the extra table and dummy handler?

> --- /dev/null
> +++ b/gas/scfidw2gen.h
> @@ -0,0 +1,37 @@
> +/* scfidw2gen.h - Support for emitting synthesized Dwarf2 CFI.
> +   Copyright (C) 2003-2023 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 SCFIDW2GEN_H
> +#define SCFIDW2GEN_H
> +
> +#include "as.h"
> +#include "dwarf2.h"
> +
> +extern int all_cfi_sections;

This needs to go into dw2gencfi.h, such that dw2gencfi.c will also see
the declaration (and the compiler be able to check that declaration and
definition are in sync).

Jan

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

* Re: [PATCH, V2 07/10] gas: synthesize CFI for hand-written asm
  2023-10-30 16:51 ` [PATCH, V2 07/10] gas: synthesize CFI for hand-written asm Indu Bhagat
@ 2023-10-31 14:10   ` Jan Beulich
  2023-11-02  8:15     ` Indu Bhagat
  2023-11-02 15:53   ` Jan Beulich
  1 sibling, 1 reply; 30+ messages in thread
From: Jan Beulich @ 2023-10-31 14:10 UTC (permalink / raw)
  To: Indu Bhagat; +Cc: binutils

On 30.10.2023 17:51, Indu Bhagat wrote:
> gas/
> 	* Makefile.am: Add new files.
> 	* Makefile.in: Regenerated.
> 	* as.c (defined): Handle documentation and listing option for
> 	ginsns and SCFI.
> 	* config/obj-elf.c (obj_elf_size): Invoke ginsn_data_end.
> 	(obj_elf_type): Invoke ginsn_data_begin.
> 	* config/tc-i386.c (ginsn_new): New functionality to generate
> 	ginsns.
> 	(x86_scfi_callee_saved_p): New function.
> 	(ginsn_dw2_regnum): Likewise.
> 	(ginsn_set_where): Likewise.
> 	(x86_ginsn_alu): Likewise.
> 	(x86_ginsn_move): Likewise.
> 	(x86_ginsn_lea): Likewise.
> 	(x86_ginsn_jump): Likewise.
> 	(x86_ginsn_jump_cond): Likewise.
> 	(md_assemble): Invoke ginsn_new.
> 	(s_insn): Likewise.
> 	(i386_target_format): Add hard error for usage of --scfi with non AMD64 ABIs.
> 	* config/tc-i386.h (TARGET_USE_GINSN): New definition.
> 	(TARGET_USE_SCFI): Likewise.
> 	(SCFI_NUM_REGS): Likewise.
> 	(REG_FP): Likewise.
> 	(REG_SP): Likewise.
> 	(SCFI_INIT_CFA_OFFSET): Likewise.
> 	(SCFI_CALLEE_SAVED_REG_P): Likewise.
> 	(x86_scfi_callee_saved_p): Likewise.

For this arch-specific code there's a fundamental question of maintenance
cost here: It doesn't look very reasonable to me to demand of people adding
support for new ISA extensions to also take into consideration all of this
new machinery. Yet if any such addition affects SCFI, things will go out-
of-sync without updating this code as well. It may not be very often that
such updating is necessary, but right now there's APX work in progress
which I expect will need dealing with here as well.

> --- a/gas/as.c
> +++ b/gas/as.c
> @@ -45,6 +45,7 @@
>  #include "codeview.h"
>  #include "bfdver.h"
>  #include "write.h"
> +#include "ginsn.h"
>  
>  #ifdef HAVE_ITBL_CPU
>  #include "itbl-ops.h"
> @@ -245,6 +246,7 @@ Options:\n\
>                        	  d      omit debugging directives\n\
>                        	  g      include general info\n\
>                        	  h      include high-level source\n\
> +			  i      include ginsn and synthesized CFI info\n\
>                        	  l      include assembly\n\
>                        	  m      include macro expansions\n\
>                        	  n      omit forms processing\n\

Please can you make indentation match neighboring code?

> --- a/gas/config/obj-elf.c
> +++ b/gas/config/obj-elf.c
> @@ -24,6 +24,7 @@
>  #include "subsegs.h"
>  #include "obstack.h"
>  #include "dwarf2dbg.h"
> +#include "ginsn.h"
>  
>  #ifndef ECOFF_DEBUGGING
>  #define ECOFF_DEBUGGING 0
> @@ -2302,6 +2303,13 @@ obj_elf_size (int ignore ATTRIBUTE_UNUSED)
>        symbol_get_obj (sym)->size = XNEW (expressionS);
>        *symbol_get_obj (sym)->size = exp;
>      }
> +
> +  /* If the symbol in the directive matches the current function being
> +     processed, indicate end of the current stream of ginsns.  */
> +  if (flag_synth_cfi
> +      && S_IS_FUNCTION (sym) && sym == ginsn_data_func_symbol ())
> +    ginsn_data_end (symbol_temp_new_now ());
> +
>    demand_empty_rest_of_line ();
>  }

Besides the restrictions mentioned, presence of this just in obj-elf.c
means non-ELF targets also won't be able to use this. You'll therefore
want to adjust the config/tc-i386.h such that e.g. COFF targets don't
needlessly have a large set of dead code compiled in.

> --- 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 "scfi.h"
>  #include "gen-sframe.h"
>  #include "sframe.h"
>  #include "elf/x86-64.h"
> @@ -193,8 +194,11 @@ static unsigned int x86_isa_1_used;
>  static unsigned int x86_feature_2_used;
>  /* Generate x86 used ISA and feature properties.  */
>  static unsigned int x86_used_note = DEFAULT_X86_USED_NOTE;
> +
>  #endif

Nit: Stray change?

> +static ginsnS *ginsn_new (symbolS *sym, enum ginsn_gen_mode gmode);

I don't see the need for this: The function definition lives ahead of any
caller afaics.

> @@ -5116,6 +5120,716 @@ static INLINE bool may_need_pass2 (const insn_template *t)
>  	       && t->base_opcode == 0x63);
>  }
>  
> +bool
> +x86_scfi_callee_saved_p (uint32_t dw2reg_num)
> +{
> +  if (dw2reg_num == 3 /* rbx.  */
> +      || dw2reg_num == REG_FP /* rbp.  */
> +      || dw2reg_num == REG_SP /* rsp.  */
> +      || (dw2reg_num >= 12 && dw2reg_num <= 15) /* r12 - r15.  */)
> +    return true;

Non-GPRs aren't of interest here?

What about the Windows ABI, which is also used in the EFI world?

> +  return false;
> +}
> +
> +static uint32_t
> +ginsn_dw2_regnum (const reg_entry *ireg)
> +{
> +  /* PS: Note the data type here as int32_t, because of Dw2Inval (-1).  */
> +  int32_t dwarf_reg = Dw2Inval;
> +  const reg_entry *temp;
> +
> +  if (ireg->dw2_regnum[0] == Dw2Inval && ireg->dw2_regnum[1] == Dw2Inval)
> +    return dwarf_reg;
> +
> +  dwarf_reg = ireg->dw2_regnum[flag_code >> 1];

flag_code == CODE_64BIT would likely be more robust.

Also are all calls here synchronous, i.e. happening right when a
particular insn is processed? Otherwise what about flag_code changing
in between?

Plus in how far does it make sense to handle other than CODE_64BIT when
you tie yourself to the SysV 64-bit ABI?

> +  if (dwarf_reg == Dw2Inval)
> +    {
> +      temp = ireg + 16;
> +      dwarf_reg = ginsn_dw2_regnum (temp);
> +    }

What is this about? This clearly needs a comment, and it may also need
a comment in opcodes/i386-reg.tbl to clarify that certain ordering
requirements have to be retained. It also wants to be clarified that
the recursion here won't be indefinite.

> +  if (dwarf_reg == Dw2Inval)
> +    gas_assert (1); /* Needs to be addressed.  */

This is dead code (you appear to mean 0 instead of 1). Yet even then
still gas_assert (dwarf_reg != Dw2Inval) please.

> +  return (uint32_t) dwarf_reg;
> +}
> +
> +static void
> +ginsn_set_where (ginsnS* ginsn)

Nit: Misplaced *.

> +{
> +  const char *file;
> +  unsigned int line;
> +  file = as_where (&line);
> +  ginsn_set_file_line (ginsn, file, line);
> +}

This function, also from its name, isn't x86-specific, is it? As such
it wants to live in ginsn.[ch].

> +static ginsnS *
> +x86_ginsn_alu (i386_insn insn, symbolS *insn_end_sym)
> +{
> +  offsetT src_imm;
> +  uint32_t dw2_regnum;
> +  enum ginsn_src_type src_type;
> +  enum ginsn_dst_type dst_type;
> +  ginsnS *ginsn = NULL;
> +
> +  /* FIXME - create ginsn for REG_SP target only ? */
> +  /* Map for insn.tm.extension_opcode
> +     000 ADD    100 AND
> +     001 OR     101 SUB
> +     010 ADC    110 XOR
> +     011 SBB    111 CMP  */
> +
> +  /* add/sub imm, %reg.
> +     and imm, %reg only at this time for SCFI. */
> +  if (!(insn.tm.extension_opcode == 0
> +	|| insn.tm.extension_opcode == 4
> +	|| insn.tm.extension_opcode == 5))
> +    return ginsn;

Why is AND permitted, but OR/XOR aren't?

Also this is about ALU insns with immediate operands only, yet that
fact isn't reflected in the function name.

> +  /* TBD_GINSN_REPRESENTATION_LIMIT: There is no representation for when a
> +     symbol is used as an operand, like so:
> +	  addq    $simd_cmp_op+8, %rdx
> +     Skip generating any ginsn for this.  */
> +  if (insn.imm_operands == 1
> +      && insn.op[0].imms->X_op == O_symbol)
> +    return ginsn;
> +
> +  gas_assert (insn.imm_operands == 1
> +	      && insn.op[0].imms->X_op == O_constant);

Perhaps less fragile if you use != O_constant in the preceding if()?
The remaining half could the move ahead of that if(), allowing half
of its condition to also be dropped.

> +  src_imm = insn.op[0].imms->X_add_number;
> +  /* The second operand may be a register or indirect access.  */
> +  if (insn.mem_operands == 1 && insn.base_reg)
> +    {
> +      dw2_regnum = ginsn_dw2_regnum (insn.base_reg);
> +      src_type = GINSN_SRC_INDIRECT;
> +      dst_type = GINSN_DST_INDIRECT;

The possibly in use index register isn't of interest in this case?
Nor the displacement in the memory operand, ...

> +    }
> +  else if (insn.mem_operands == 1 && insn.index_reg)
> +    {
> +      dw2_regnum = ginsn_dw2_regnum (insn.index_reg);
> +      src_type = GINSN_SRC_INDIRECT;
> +      dst_type = GINSN_DST_INDIRECT;

... similarly applicable here? Nor a segment override?

> +    }
> +  else
> +    {
> +      gas_assert (insn.reg_operands == 1);

Afaict this will trigger when the memory operand has neither base
nor index.

> +      dw2_regnum = ginsn_dw2_regnum (insn.op[1].regs);
> +      src_type = GINSN_SRC_REG;
> +      dst_type = GINSN_DST_REG;
> +    }
> +
> +  /* For ginsn, keep the imm as second src operand.  */
> +  if (insn.tm.extension_opcode == 5)
> +    ginsn = ginsn_new_sub (insn_end_sym, true,
> +			   src_type, dw2_regnum, 0,
> +			   GINSN_SRC_IMM, 0, src_imm,
> +			   dst_type, dw2_regnum, 0);
> +  else if (insn.tm.extension_opcode == 4)
> +    ginsn = ginsn_new_and (insn_end_sym, true,
> +			   src_type, dw2_regnum, 0,
> +			   GINSN_SRC_IMM, 0, src_imm,
> +			   dst_type, dw2_regnum, 0);
> +  else if (insn.tm.extension_opcode == 0)
> +    ginsn = ginsn_new_add (insn_end_sym, true,
> +			   src_type, dw2_regnum, 0,
> +			   GINSN_SRC_IMM, 0, src_imm,
> +			   dst_type, dw2_regnum, 0);

I think this would benefit from setting a function pointer near the
top of the function. Else can this please at least be put in switch()
form?

> +  ginsn_set_where (ginsn);
> +
> +  return ginsn;
> +}
> +
> +static ginsnS *
> +x86_ginsn_move (i386_insn insn, symbolS *insn_end_sym)
> +{
> +  ginsnS *ginsn;
> +  uint16_t opcode;
> +  uint32_t dst_reg;
> +  uint32_t src_reg;
> +  offsetT dst_disp;
> +  offsetT src_disp;
> +  const reg_entry *dst = NULL;
> +  const reg_entry *src = NULL;
> +  enum ginsn_dst_type dst_type;
> +  enum ginsn_src_type src_type;
> +
> +  opcode = insn.tm.base_opcode;
> +  src_type = GINSN_SRC_REG;
> +  src_disp = dst_disp = 0;
> +  dst_type = GINSN_DST_REG;

Please can such be initializers of their variables?

> +  if (opcode == 0x8b)
> +    {
> +      /* mov  disp(%reg), %reg.  */
> +      if (insn.mem_operands && insn.base_reg)
> +	{
> +	  src = insn.base_reg;
> +	  if (insn.disp_operands == 1)
> +	    src_disp = insn.op[0].disps->X_add_number;
> +	  src_type = GINSN_SRC_INDIRECT;
> +	}
> +      else
> +	src = insn.op[0].regs;

What about disp(%reg,%reg) or disp(,%reg) kinds of memory operands?

> +      dst = insn.op[1].regs;
> +    }
> +  else if (opcode == 0x89 || opcode == 0x88)

How come 0x88 is handled here, but 0x8a isn't handled above?

> +    {
> +      /* mov %reg, disp(%reg).  */
> +      src = insn.op[0].regs;
> +      if (insn.mem_operands && insn.base_reg)
> +	{
> +	  dst = insn.base_reg;
> +	  if (insn.disp_operands == 1)
> +	    dst_disp = insn.op[1].disps->X_add_number;
> +	  dst_type = GINSN_DST_INDIRECT;
> +	}
> +      else
> +	dst = insn.op[1].regs;
> +    }
> +
> +  src_reg = ginsn_dw2_regnum (src);
> +  dst_reg = ginsn_dw2_regnum (dst);
> +
> +  ginsn = ginsn_new_mov (insn_end_sym, true,
> +			 src_type, src_reg, src_disp,
> +			 dst_type, dst_reg, dst_disp);
> +  ginsn_set_where (ginsn);
> +
> +  return ginsn;
> +}
> +
> +static ginsnS *
> +x86_ginsn_lea (i386_insn insn, symbolS *insn_end_sym)
> +{
> +  offsetT src_disp = 0;
> +  ginsnS *ginsn = NULL;
> +  uint32_t base_reg;
> +  uint32_t index_reg;
> +  offsetT index_scale;
> +  uint32_t dst_reg;
> +
> +  if (!insn.index_reg && !insn.base_reg)
> +    {
> +      /* lea symbol, %rN.  */
> +      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
> +      /* FIXME - Skip encoding information about the symbol.
> +	 This is TBD_GINSN_INFO_LOSS, but it is fine if the mode is
> +	 GINSN_GEN_SCFI.  */
> +      ginsn = ginsn_new_mov (insn_end_sym, false,
> +			     GINSN_SRC_IMM, 0xf /* arbitrary const.  */, 0,
> +			     GINSN_DST_REG, dst_reg, 0);
> +    }
> +  else if (insn.base_reg && !insn.index_reg)
> +    {
> +      /* lea    -0x2(%base),%dst.  */
> +      base_reg = ginsn_dw2_regnum (insn.base_reg);
> +      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
> +
> +      if (insn.disp_operands)
> +	src_disp = insn.op[0].disps->X_add_number;

What if the displacement expression is other than O_constant?

> +      if (src_disp)
> +	/* Generate an ADD ginsn.  */
> +	ginsn = ginsn_new_add (insn_end_sym, true,
> +			       GINSN_SRC_REG, base_reg, 0,
> +			       GINSN_SRC_IMM, 0, src_disp,
> +			       GINSN_DST_REG, dst_reg, 0);
> +      else
> +	  /* Generate a MOV ginsn.  */
> +	  ginsn = ginsn_new_mov (insn_end_sym, true,
> +				 GINSN_SRC_REG, base_reg, 0,
> +				 GINSN_DST_REG, dst_reg, 0);
> +    }
> +  else if (!insn.base_reg && insn.index_reg)
> +    {
> +      /* lea (,%index,imm), %dst.  */
> +      /* FIXME - Skip encoding an explicit multiply operation, instead use
> +	 GINSN_TYPE_OTHER.  This is TBD_GINSN_INFO_LOSS, but it is fine if
> +	 the mode is GINSN_GEN_SCFI.  */
> +      index_scale = insn.log2_scale_factor;
> +      index_reg = ginsn_dw2_regnum (insn.index_reg);
> +      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
> +      ginsn = ginsn_new_other (insn_end_sym, true,
> +			       GINSN_SRC_REG, index_reg,
> +			       GINSN_SRC_IMM, index_scale,
> +			       GINSN_DST_REG, dst_reg);
> +    }
> +  else
> +    {
> +      /* lea disp(%base,%index,imm) %dst.  */
> +      /* FIXME - Skip encoding information about the disp and imm for index
> +	 reg.  This is TBD_GINSN_INFO_LOSS, but it is fine if the mode is
> +	 GINSN_GEN_SCFI.  */
> +      base_reg = ginsn_dw2_regnum (insn.base_reg);
> +      index_reg = ginsn_dw2_regnum (insn.index_reg);
> +      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
> +      /* Generate an ADD ginsn.  */
> +      ginsn = ginsn_new_add (insn_end_sym, true,
> +			     GINSN_SRC_REG, base_reg, 0,
> +			     GINSN_SRC_REG, index_reg, 0,
> +			     GINSN_DST_REG, dst_reg, 0);

The comment mentions the displacement, but it's not passed on? In the earlier
"else if" block you also pay attention to the scla factor, but here you don't?
That said, I think I'm confused anyway about the FIXME comments.

> +    }
> +
> +  ginsn_set_where (ginsn);
> +
> +  return ginsn;
> +}
> +
> +static ginsnS *
> +x86_ginsn_jump (i386_insn insn, symbolS *insn_end_sym)
> +{
> +  ginsnS *ginsn = NULL;
> +  symbolS *src_symbol;
> +
> +  gas_assert (insn.disp_operands == 1);
> +
> +  if (insn.op[0].disps->X_op == O_symbol)
> +    {
> +      src_symbol = insn.op[0].disps->X_add_symbol;
> +      /* The jump target is expected to be a symbol with 0 addend.
> +	 Assert for now to see if this assumption is true.  */
> +      gas_assert (insn.op[0].disps->X_add_number == 0);

If you assert this means elsewhere this is being checked and a call
here avoided in case the value is non-zero. I can't spot such a
check, though.

> +      ginsn = ginsn_new_jump (insn_end_sym, true,
> +			      GINSN_SRC_SYMBOL, 0, src_symbol);
> +
> +      ginsn_set_where (ginsn);
> +    }
> +
> +  return ginsn;
> +}

Further, what about XABORT transferring control to what XBEGIN has
supplied? (XBEGIN can, in a sense, also be considered a [conditional]
branch.)

> +static ginsnS *
> +x86_ginsn_jump_cond (i386_insn insn, symbolS *insn_end_sym)
> +{
> +  ginsnS *ginsn = NULL;
> +  symbolS *src_symbol;
> +
> +  /* TBD_GINSN_GEN_NOT_SCFI: Ignore move to or from xmm reg for mode.  */
> +  if (i.tm.opcode_space == SPACE_0F)
> +    return ginsn;

What is the comment about? And what about SPACE_0F-encoded conditional
jumps (Jcc <disp32>)? And where are LOOP and J{E,R}CXZ handled?

> +  gas_assert (insn.disp_operands == 1);
> +
> +  if (insn.op[0].disps->X_op == O_symbol)
> +    {
> +      src_symbol = insn.op[0].disps->X_add_symbol;
> +      /* The jump target is expected to be a symbol with 0 addend.
> +	 Assert for now to see if this assumption is true.  */
> +      gas_assert (insn.op[0].disps->X_add_number == 0);
> +      ginsn = ginsn_new_jump_cond (insn_end_sym, true,
> +				   GINSN_SRC_SYMBOL, 0, src_symbol);
> +      ginsn_set_where (ginsn);
> +    }
> +  else
> +    /* Catch them for now so we know what we are dealing with.  */
> +    gas_assert (0);

I'm going from the assumption that this (and alike) will be addressed
before this series is committed?

> +  return ginsn;
> +}
> +
> +/* Generate one or more GAS instructions for the current machine dependent
> +   instruction.
> +
> +   Returns the head of linked list of ginsn(s) added, if success;
> +   Returns NULL if failure.  */
> +
> +static ginsnS *
> +ginsn_new (symbolS *insn_end_sym, enum ginsn_gen_mode gmode)

x86_ginsn_new()?

> +{
> +  uint16_t opcode;
> +  uint32_t dw2_regnum;
> +  uint32_t src2_dw2_regnum;
> +  int32_t gdisp = 0;
> +  ginsnS *ginsn = NULL;
> +  ginsnS *ginsn_next = NULL;
> +  ginsnS *ginsn_last = NULL;
> +
> +  /* FIXME - Need a way to check whether the decoding is sane.  The specific
> +     checks around i.tm.opcode_space were added as issues were seen.  Likely
> +     insufficient.  */

Furthermore opcode encoding space (SPACE_...) need to be taken into
account in all cases.

> +  /* Currently supports generation of selected ginsns, sufficient for
> +     the use-case of SCFI only.  To remove this condition will require
> +     work on this target-specific process of creation of ginsns.  Some
> +     of such places are tagged with TBD_GINSN_GEN_NOT_SCFI to serve as
> +     examples.  */
> +  if (gmode != GINSN_GEN_SCFI)
> +    return ginsn;
> +
> +  opcode = i.tm.base_opcode;
> +
> +  switch (opcode)
> +    {
> +    case 0x1:
> +      /* add reg, reg.  */
> +      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);

You don't care about opcode 0 (byte operation). Then what about 16-bit
operand size? Or, since we're talking of a 64-bit-ABI-only feature,
even 32-bit operand size?

Also what about opcode 0x3?

> +      if (i.reg_operands == 2)
> +	{
> +	  src2_dw2_regnum = ginsn_dw2_regnum (i.op[1].regs);
> +	  ginsn = ginsn_new_add (insn_end_sym, true,
> +				 GINSN_SRC_REG, dw2_regnum, 0,
> +				 GINSN_SRC_REG, src2_dw2_regnum, 0,
> +				 GINSN_DST_REG, src2_dw2_regnum, 0);
> +	  ginsn_set_where (ginsn);
> +	}
> +      else if (i.mem_operands && i.base_reg)
> +	{
> +	  src2_dw2_regnum = ginsn_dw2_regnum (i.base_reg);
> +	  if (i.disp_operands == 1)
> +	    gdisp = i.op[1].disps->X_add_number;
> +
> +	  ginsn = ginsn_new_add (insn_end_sym, true,
> +				 GINSN_SRC_REG, dw2_regnum, 0,
> +				 GINSN_SRC_INDIRECT, src2_dw2_regnum, gdisp,
> +				 GINSN_DST_INDIRECT, src2_dw2_regnum, gdisp);
> +	  ginsn_set_where (ginsn);
> +	}
> +      else
> +	/* Catch them for now so we know what we are dealing with.  */
> +	gas_assert (0);
> +
> +      break;
> +    case 0x29:
> +      /* If opcode_space == SPACE_0F, this is a movaps insn.  Skip it
> +	 for GINSN_GEN_SCFI.  */
> +      if (i.tm.opcode_space == SPACE_0F)
> +	break;

Extending on the earlier related comment: Why would you exclude just SPACE_0F?

> +      /* sub reg, reg.  */
> +      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
> +
> +      if (i.reg_operands == 2)
> +	{
> +	  src2_dw2_regnum = ginsn_dw2_regnum (i.op[1].regs);
> +	  ginsn = ginsn_new_sub (insn_end_sym, true,
> +				 GINSN_SRC_REG, dw2_regnum, 0,
> +				 GINSN_SRC_REG, src2_dw2_regnum, 0,
> +				 GINSN_DST_REG, src2_dw2_regnum, 0);
> +	  ginsn_set_where (ginsn);
> +	}
> +      else if (i.mem_operands && i.base_reg)
> +	{
> +	  src2_dw2_regnum = ginsn_dw2_regnum (i.base_reg);
> +	  if (i.disp_operands == 1)
> +	    gdisp = i.op[1].disps->X_add_number;
> +
> +	  ginsn = ginsn_new_sub (insn_end_sym, true,
> +				 GINSN_SRC_REG, dw2_regnum, 0,
> +				 GINSN_SRC_INDIRECT, src2_dw2_regnum, gdisp,
> +				 GINSN_DST_INDIRECT, src2_dw2_regnum, gdisp);
> +	  ginsn_set_where (ginsn);
> +	}
> +      else
> +	/* Catch them for now so we know what we are dealing with.  */
> +	gas_assert (0);
> +
> +      break;
> +    case 0xa0:
> +    case 0xa8:

Since individual case blocks are non-trivial, can there please be blank
lines between any two non-fall-through case blocks?

> +      /* If opcode_space != SPACE_0F, this is a test insn.  Skip it
> +	 for GINSN_GEN_SCFI.  */
> +      if (i.tm.opcode_space != SPACE_0F)
> +	break;

While here you properly use !=, now the comment is wrong.

> +      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
> +      /* push fs / push gs.  */
> +      ginsn = ginsn_new_sub (insn_end_sym, false,
> +			     GINSN_SRC_REG, REG_SP, 0,
> +			     GINSN_SRC_IMM, 0, 8,

Note how the 8 here assumes flag_code == CODE_64BIT. (You further assume
no operand size override here.)

> +			     GINSN_DST_REG, REG_SP, 0);
> +      ginsn_set_where (ginsn);
> +
> +      ginsn_next = ginsn_new_store (insn_end_sym, false,
> +				    GINSN_SRC_REG, dw2_regnum,
> +				    GINSN_DST_INDIRECT, REG_SP, 0);
> +      ginsn_set_where (ginsn_next);
> +
> +      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
> +      break;
> +    case 0xa1:
> +    case 0xa9:
> +      /* If opcode_space != SPACE_0F, this is test insn.  Skip it
> +	 for GINSN_GEN_SCFI.  */
> +      if (i.tm.opcode_space != SPACE_0F)
> +	break;
> +
> +      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
> +      /* pop fs / pop gs.  */
> +      ginsn = ginsn_new_load (insn_end_sym, false,
> +			      GINSN_SRC_INDIRECT, REG_SP, 0,
> +			      GINSN_DST_REG, dw2_regnum);
> +      ginsn_set_where (ginsn);
> +
> +      ginsn_next = ginsn_new_add (insn_end_sym, false,
> +				  GINSN_SRC_REG, REG_SP, 0,
> +				  GINSN_SRC_IMM, 0, 8,
> +				  GINSN_DST_REG, REG_SP, 0);
> +      ginsn_set_where (ginsn_next);
> +
> +      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
> +      break;
> +    case 0x50 ... 0x57:
> +      /* push reg.  */
> +      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
> +      ginsn = ginsn_new_sub (insn_end_sym, false,
> +			     GINSN_SRC_REG, REG_SP, 0,
> +			     GINSN_SRC_IMM, 0, 8,
> +			     GINSN_DST_REG, REG_SP, 0);
> +      ginsn_set_where (ginsn);
> +
> +      ginsn_next = ginsn_new_store (insn_end_sym, false,
> +				    GINSN_SRC_REG, dw2_regnum,
> +				    GINSN_DST_INDIRECT, REG_SP, 0);
> +      ginsn_set_where (ginsn_next);
> +
> +      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
> +      break;
> +    case 0x58 ... 0x5f:
> +      if (i.tm.opcode_space != SPACE_BASE)
> +	break;
> +      /* pop reg.  */
> +      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
> +      ginsn = ginsn_new_load (insn_end_sym, false,
> +			      GINSN_SRC_INDIRECT, REG_SP, 0,
> +			      GINSN_DST_REG, dw2_regnum);
> +      ginsn_set_where (ginsn);
> +
> +      ginsn_next = ginsn_new_add (insn_end_sym, false,
> +				  GINSN_SRC_REG, REG_SP, 0,
> +				  GINSN_SRC_IMM, 0, 8,
> +				  GINSN_DST_REG, REG_SP, 0);
> +      ginsn_set_where (ginsn_next);
> +
> +      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
> +      break;
> +    case 0x68:
> +    case 0x6a:
> +      /* push imm. */
> +      /* Skip getting the value of imm from machine instruction
> +	 because for ginsn generation this is not important.  */

In (e.g.) kernel code model it is possible to do

	push	<address>
	pop	%rbp

(could even be %rsp). How is such a frame (or stack) pointer change
not relevant?

> +      ginsn = ginsn_new_sub (insn_end_sym, false,
> +			     GINSN_SRC_REG, REG_SP, 0,
> +			     GINSN_SRC_IMM, 0, 8,
> +			     GINSN_DST_REG, REG_SP, 0);
> +      ginsn_set_where (ginsn);
> +
> +      ginsn_next = ginsn_new_store (insn_end_sym, false,
> +				    GINSN_SRC_IMM, 0,
> +				    GINSN_DST_INDIRECT, REG_SP, 0);
> +      ginsn_set_where (ginsn_next);
> +
> +      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
> +      break;
> +    case 0x70 ... 0x7f:
> +      ginsn = x86_ginsn_jump_cond (i, insn_end_sym);
> +      break;
> +    case 0x81:
> +    case 0x83:
> +      ginsn = x86_ginsn_alu (i, insn_end_sym);
> +      break;
> +    case 0x8b:
> +      /* Move r/m64 to r64.  */
> +    case 0x88:
> +    case 0x89:
> +      /* mov reg, reg/mem.  */
> +      ginsn = x86_ginsn_move (i, insn_end_sym);
> +      break;
> +    case 0x8d:
> +      /* lea disp(%src), %dst */
> +      ginsn = x86_ginsn_lea (i, insn_end_sym);
> +      break;
> +    case 0x8f:
> +      /* pop to mem.  */
> +      gas_assert (i.base_reg);

This opcode can in principle also have a register operand. We
_currently_ have no way for a user to request to use this
alternative encoding, but that's still a latent issue. In any
event, like elsewhere, all memory operand forms are possible
here, not just (%reg).

> +      dw2_regnum = ginsn_dw2_regnum (i.base_reg);
> +      ginsn = ginsn_new_load (insn_end_sym, false,
> +			      GINSN_SRC_INDIRECT, REG_SP, 0,
> +			      GINSN_DST_INDIRECT, dw2_regnum);

When both operands are "indirect", what's the difference between
move, load, and store? IOW if the above is permitted, can't all
three be folded into a single ginsn kind?

> +      ginsn_set_where (ginsn);
> +
> +      ginsn_next = ginsn_new_add (insn_end_sym, false,
> +				  GINSN_SRC_REG, REG_SP, 0,
> +				  GINSN_SRC_IMM, 0, 8,
> +				  GINSN_DST_REG, REG_SP, 0);
> +      ginsn_set_where (ginsn_next);
> +
> +      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
> +      break;
> +    case 0x9c:
> +      /* pushf / pushfd / pushfq.
> +	 Tracking EFLAGS register by number is not necessary.  */
> +      ginsn = ginsn_new_sub (insn_end_sym, false,
> +			     GINSN_SRC_REG, REG_SP, 0,
> +			     GINSN_SRC_IMM, 0, 8,

Why does the comment mention PUSHFD when you assume PUSHFQ here?

> +			     GINSN_DST_REG, REG_SP, 0);
> +      ginsn_set_where (ginsn);
> +
> +      ginsn_next = ginsn_new_store (insn_end_sym, false,
> +				    GINSN_SRC_IMM, 0,
> +				    GINSN_DST_INDIRECT, REG_SP, 0);
> +      ginsn_set_where (ginsn_next);
> +
> +      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
> +
> +      break;

Where's POPFD?

> +    case 0xff:
> +      /* push from mem.  */
> +      if (i.tm.extension_opcode == 6)
> +	{
> +	  ginsn = ginsn_new_sub (insn_end_sym, false,
> +				 GINSN_SRC_REG, REG_SP, 0,
> +				 GINSN_SRC_IMM, 0, 8,
> +				 GINSN_DST_REG, REG_SP, 0);
> +	  ginsn_set_where (ginsn);
> +
> +	  /* These instructions have no imm, only indirect access.  */
> +	  gas_assert (i.base_reg);
> +	  dw2_regnum = ginsn_dw2_regnum (i.base_reg);
> +	  ginsn_next = ginsn_new_store (insn_end_sym, false,
> +					GINSN_SRC_INDIRECT, dw2_regnum,
> +					GINSN_DST_INDIRECT, REG_SP, 0);
> +	  ginsn_set_where (ginsn_next);
> +
> +	  gas_assert (!ginsn_link_next (ginsn, ginsn_next));
> +	}
> +      else if (i.tm.extension_opcode == 4)
> +	{
> +	  /* jmp r/m.  E.g., notrack jmp *%rax.  */
> +	  if (i.reg_operands)
> +	    {
> +	      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
> +	      ginsn = ginsn_new_jump (insn_end_sym, true,
> +				      GINSN_SRC_REG, dw2_regnum, NULL);
> +	      ginsn_set_where (ginsn);
> +	    }
> +	  else if (i.mem_operands && i.index_reg)
> +	    {
> +	      /* jmp    *0x0(,%rax,8).  */
> +	      dw2_regnum = ginsn_dw2_regnum (i.index_reg);
> +	      ginsn = ginsn_new_jump (insn_end_sym, true,
> +				      GINSN_SRC_REG, dw2_regnum, NULL);
> +	      ginsn_set_where (ginsn);
> +	    }
> +	  else if (i.mem_operands && i.base_reg)
> +	    {
> +	      dw2_regnum = ginsn_dw2_regnum (i.base_reg);
> +	      ginsn = ginsn_new_jump (insn_end_sym, true,
> +				      GINSN_SRC_REG, dw2_regnum, NULL);
> +	      ginsn_set_where (ginsn);
> +	    }
> +	  else
> +	    /* Catch them for now so we know what we are dealing with.  */
> +	    gas_assert (0);
> +	}
> +      else if (i.tm.extension_opcode == 2)
> +	{
> +	  /* 0xFF /2 (call).  */
> +	  if (i.reg_operands)
> +	    {
> +	      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
> +	      ginsn = ginsn_new_call (insn_end_sym, true,
> +				      GINSN_SRC_REG, dw2_regnum, NULL);
> +	      ginsn_set_where (ginsn);
> +	    }
> +	  else if (i.mem_operands && i.base_reg)
> +	    {
> +	      dw2_regnum = ginsn_dw2_regnum (i.base_reg);
> +	      ginsn = ginsn_new_call (insn_end_sym, true,
> +				      GINSN_SRC_REG, dw2_regnum, NULL);
> +	      ginsn_set_where (ginsn);
> +	    }
> +	  else
> +	    /* Catch them for now so we know what we are dealing with.  */
> +	    gas_assert (0);
> +	}
> +      else
> +	/* Catch them for now so we know what we are dealing with.  */
> +	gas_assert (0);
> +      break;
> +    case 0xc2:
> +    case 0xc3:
> +      /* Near ret.  */
> +      ginsn = ginsn_new_return (insn_end_sym, true);
> +      ginsn_set_where (ginsn);
> +      break;

Where did the immediate operand of 0xC2 go? And what about far return
(and more generally far branches)?

> +    case 0xc9:
> +      /* The 'leave' instruction copies the contents of the RBP register
> +	 into the RSP register to release all stack space allocated to the
> +	 procedure.  */
> +      ginsn = ginsn_new_mov (insn_end_sym, false,
> +			     GINSN_SRC_REG, REG_FP, 0,
> +			     GINSN_DST_REG, REG_SP, 0);
> +      ginsn_set_where (ginsn);
> +
> +      /* Then it restores the old value of the RBP register from the stack.  */
> +      ginsn_next = ginsn_new_load (insn_end_sym, false,
> +				   GINSN_SRC_INDIRECT, REG_SP, 0,
> +				   GINSN_DST_REG, REG_FP);
> +      ginsn_set_where (ginsn_next);
> +
> +      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
> +      ginsn_last = ginsn_new_add (insn_end_sym, false,
> +				  GINSN_SRC_REG, REG_SP, 0,
> +				  GINSN_SRC_IMM, 0, 8,
> +				  GINSN_DST_REG, REG_SP, 0);
> +      ginsn_set_where (ginsn_next);
> +
> +      gas_assert (!ginsn_link_next (ginsn_next, ginsn_last));
> +      break;

You handle LEAVE, but not ENTER?

> +    case 0xe8:
> +      /* PS: SCFI machinery does not care about which func is being
> +	 called.  OK to skip that info.  */
> +      ginsn = ginsn_new_call (insn_end_sym, true,
> +			      GINSN_SRC_SYMBOL, 0, NULL);
> +      ginsn_set_where (ginsn);
> +      break;
> +    case 0xe9:
> +    case 0xeb:
> +      /* If opcode_space == SPACE_0F, this is a psubw por insn.  Skip it
> +	 for GINSN_GEN_SCFI.  */
> +      if (i.tm.opcode_space == SPACE_0F)
> +	break;
> +
> +      /* Unconditional jmp.  */
> +      ginsn = x86_ginsn_jump (i, insn_end_sym);
> +      ginsn_set_where (ginsn);
> +      break;
> +      /* Fall Through.  */
> +    default:

There's noo fall-through here.

> +      /* TBD_GINSN_GEN_NOT_SCFI: Keep a warning, for now, to find out about
> +	 possibly missed instructions affecting REG_SP or REG_FP.  These
> +	 checks may not be completely exhaustive as they do not involve
> +	 index / base reg.  */

Cases with early "break" should probably come here as well.

> +      if (i.op[0].regs)
> +	{
> +	  dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);

This isn't valid without first having checked that operand 0 is a
register operand: "regs" is a member of a union, and you may not
pass expressionS * into this function.

> +	  if (dw2_regnum == REG_SP || dw2_regnum == REG_FP)
> +	    as_warn_where (last_insn.file, last_insn.line,
> +			   _("SCFI: unhandled op 0x%x may cause incorrect CFI"),
> +			   i.tm.base_opcode);
> +	}
> +      if (i.op[1].regs)
> +	{
> +	  dw2_regnum = ginsn_dw2_regnum (i.op[1].regs);
> +	  if (dw2_regnum == REG_SP || dw2_regnum == REG_FP)
> +	    as_warn_where (last_insn.file, last_insn.line,
> +			   _("SCFI: unhandled op 0x%x may cause incorrect CFI"),
> +			   i.tm.base_opcode);
> +	}

What about insns with 3 GPR operands?

Also, why do you use as_warn_where() here? You're still in the
context of synchonously processing the current input line.

> +      /* Keep an eye on other instructions affecting control flow.  */
> +      gas_assert (!i.tm.opcode_modifier.jump);

Again this cannot remain like this, even if here there's no FIXME
remark.

> +      /* TBD_GINSN_GEN_NOT_SCFI: Skip all other opcodes uninteresting for
> +	 GINSN_GEN_SCFI mode.  */

"uninteresting" is an interesting term when dealing with hand-written
assembly. How do you know what an assembly writer is going to do?
While the patch description mentions a few constraints, having come
here I'm under the impression that there are quite a few more of them.
Maybe I simply didn't spot where all caveats are fully spelled out?

> @@ -5128,6 +5842,7 @@ md_assemble (char *line)
>    const char *end, *pass1_mnem = NULL;
>    enum i386_error pass1_err = 0;
>    const insn_template *t;
> +  ginsnS *ginsn;
>  
>    /* Initialize globals.  */
>    current_templates = NULL;
> @@ -5659,6 +6374,13 @@ md_assemble (char *line)
>    /* We are ready to output the insn.  */
>    output_insn ();
>  
> +  /* At this time, SCFI is enabled only for AMD64 ABI.  */
> +  if (flag_synth_cfi && x86_elf_abi == X86_64_ABI)
> +    {
> +      ginsn = ginsn_new (symbol_temp_new_now (), frch_ginsn_gen_mode ());
> +      frch_ginsn_data_append (ginsn);
> +    }

Why would this not work for the x32 ABI?

> @@ -10904,6 +11626,7 @@ s_insn (int dummy ATTRIBUTE_UNUSED)
>    valueT val;
>    bool vex = false, xop = false, evex = false;
>    static const templates tt = { &i.tm, &i.tm + 1 };
> +  ginsnS *ginsn;
>  
>    init_globals ();
>  
> @@ -11658,7 +12381,14 @@ s_insn (int dummy ATTRIBUTE_UNUSED)
>  
>    output_insn ();
>  
> - done:
> +  /* At this time, SCFI is enabled only for AMD64 ABI.  */
> +  if (flag_synth_cfi && x86_elf_abi == X86_64_ABI)
> +    {
> +      ginsn = ginsn_new (symbol_temp_new_now (), frch_ginsn_gen_mode ());
> +      frch_ginsn_data_append (ginsn);
> +    }
> +
> +done:

Please can you leave label indentation alone?

> @@ -15293,6 +16023,9 @@ i386_target_format (void)
>    else
>      as_fatal (_("unknown architecture"));
>  
> +  if (flag_synth_cfi && x86_elf_abi != X86_64_ABI)
> +    as_fatal (_("Synthesizing CFI is not supported for this ABI"));

With this, what use are the x86_elf_abi checks in md_assemble() and
s_insn()?

> --- a/gas/config/tc-i386.h
> +++ b/gas/config/tc-i386.h
> @@ -373,6 +373,27 @@ extern int i386_elf_section_type (const char *, size_t);
>  extern void i386_solaris_fix_up_eh_frame (segT);
>  #endif
>  
> +#define TARGET_USE_GINSN 1
> +/* Allow GAS to synthesize DWARF CFI for hand-written asm.
> +   PS: TARGET_USE_CFIPOP is a pre-condition.  */
> +#define TARGET_USE_SCFI 1
> +/* Identify the maximum DWARF register number of all the registers being
> +   tracked for SCFI.  This is the last DWARF register number of the set
> +   of SP, BP, and all callee-saved registers.  For AMD64, this means
> +   R15 (15).  Use SCFI_CALLEE_SAVED_REG_P to identify which registers
> +   are callee-saved from this set.  */
> +#define SCFI_NUM_REGS 15

Is this "number of" (and then having a value of 16 to permit covering
register 15), or is this "maximum register number" (and then needing a
different name)?

> --- /dev/null
> +++ b/gas/ginsn.c

I'll see about replying on the non-x86 parts of this separately.

Jan

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

* Re: [PATCH, V2 09/10] gas: testsuite: add a x86_64 testsuite for SCFI
  2023-10-30 16:51 ` [PATCH, V2 09/10] gas: testsuite: add a x86_64 testsuite for SCFI Indu Bhagat
@ 2023-10-31 16:13   ` Jan Beulich
  2023-11-01  6:24     ` Indu Bhagat
  0 siblings, 1 reply; 30+ messages in thread
From: Jan Beulich @ 2023-10-31 16:13 UTC (permalink / raw)
  To: Indu Bhagat; +Cc: binutils

On 30.10.2023 17:51, Indu Bhagat wrote:
> --- /dev/null
> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-add-2.s
> @@ -0,0 +1,48 @@
> +        .section        .rodata
> +	.type	simd_cmp_op, @object
> +	.size	simd_cmp_op, 8
> +simd_cmp_op:
> +	.long	2
> +	.zero	4
> +
> +# Testcase for add instruction.
> +# add reg, reg instruction
> +	.text
> +	.globl	foo
> +	.type	foo, @function
> +foo:
> +	.cfi_startproc
> +	pushq	%rbp
> +	.cfi_def_cfa_offset 16
> +	.cfi_offset 6, -16
> +	movq    %rsp, %rbp
> +	.cfi_def_cfa_register 6
> +	pushq	%r12
> +	.cfi_offset 12, -24
> +	mov	%rsp, %r12

You copy %rsp to %r12 here, and ...

> +# Stack manipulation is permitted if the base register for
> +# tracking CFA has been changed to FP.
> +	addq    %rdx, %rsp
> +	addq	%rsp, %rax
> +# Some add instructions may access the stack indirectly.  Such
> +# accesses do not make REG_FP untraceable.
> +	addl    %eax, -84(%rbp)
> +# Other kind of add instructions should not error out in the
> +# x86_64 -> ginsn translator
> +	addq    $simd_cmp_op+8, %rdx
> +	addl    %edx, -32(%rsp)
> +	addl    $1, fb_low_counter(,%rbx,4)
> +	mov	%r12, %rsp

... you restore it here, but both without any .cfi_* annotation.
It is therefore unclear whether this is in any way related to ...

> +# Popping a callee-saved register.
> +# RSP must be traceable.
> +	pop     %r12
> +	.cfi_restore 12

... what the comment says about these.

> +	leave
> +	.cfi_def_cfa_register 7
> +	.cfi_restore 6

Using numbers here isn't very helpful, I'm afraid.

> --- /dev/null
> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.s
> @@ -0,0 +1,27 @@
> +# Testcase where a user may define hot and cold areas of function
> +# Note how the .type, and .size directives may be placed differently
> +# than a regular function.
> +
> +	.globl  foo
> +	.type   foo, @function
> +foo:
> +        .cfi_startproc
> +        testl   %edi, %edi
> +        je      .L3
> +        movl    b(%rip), %eax
> +        ret
> +        .cfi_endproc
> +        .section        .text.unlikely
> +        .cfi_startproc
> +        .type   foo.cold, @function
> +foo.cold:
> +.L3:
> +        pushq   %rax
> +        .cfi_def_cfa_offset 16
> +        call    abort
> +       .cfi_endproc
> +.LFE11:
> +        .text
> +        .size   foo, .-foo
> +        .section        .text.unlikely
> +        .size   foo.cold, .-foo.cold

Related to the comment: Is it "may" or is it rather "need to"?
(In the latter case it would be yet another constraint on when this
new machinery is actually usable.)

> --- /dev/null
> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.s
> @@ -0,0 +1,11 @@
> +# A programmer may not bother to set the size of the 
> +# function symbols via an explicit .size directive.
> +	.globl  foo
> +	.type   foo, @function
> +foo:
> +        .cfi_startproc
> +        testl   %edi, %edi
> +        je      .L3
> +        movl    b(%rip), %eax
> +        ret
> +       .cfi_endproc

Wasn't it said elsewhere that .size needs using after the function?

> --- /dev/null
> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.l
> @@ -0,0 +1,2 @@
> +.*Assembler messages:
> +.*9: Warning: --scfi=all ignores some user-specified CFI directives

Is repeating this for (about?) every test really necessary / useful?
If multiple passes for every test were to make sense to me, I'd expect
one pass with SCFI and one pass without, where the directives then
take effect. And then the same set of expectations should match.

> --- /dev/null
> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.s
> @@ -0,0 +1,49 @@
> +# Testcase for switching between sp/fp based CFA.
> +	.text
> +	.globl	foo
> +	.type	foo, @function
> +foo:
> +        .cfi_startproc
> +        pushq   %r14
> +        .cfi_def_cfa_offset 16
> +        .cfi_offset 14, -16
> +        pushq   %r13
> +        .cfi_def_cfa_offset 24
> +        .cfi_offset 13, -24
> +        pushq   %r12
> +        .cfi_def_cfa_offset 32
> +        .cfi_offset 12, -32
> +        pushq   %rbp
> +        .cfi_def_cfa_offset 40
> +        .cfi_offset 6, -40
> +        pushq   %rbx
> +        .cfi_def_cfa_offset 48
> +        .cfi_offset 3, -48
> +        movq    %rdi, %rbx
> +        subq    $32, %rsp
> +        .cfi_def_cfa_offset 80
> +# This mov does not switch CFA tracking to REG_FP, because there has already
> +# been stack usage between here and the push %rbp
> +        movq    %rsp, %rbp

Yet another constraint?

> +        xorl    %eax, %eax
> +        addq    $32, %rsp
> +        .cfi_def_cfa_offset 48
> +        popq    %rbx
> +	.cfi_restore 3

Nit: inconsistent indentation (also elsewhere).

> --- /dev/null
> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.s
> @@ -0,0 +1,26 @@
> +	.text
> +	.globl	foo
> +	.type	foo, @function
> +foo:
> +#	.cfi_startproc
> +	pushq	%rax
> +	.cfi_def_cfa_offset 16
> +	push	%rbx
> +	.cfi_def_cfa_offset 24
> +	.cfi_offset 3, -24
> +	pushq	%rbp
> +        .cfi_def_cfa_offset 32
> +        .cfi_offset 6, -32
> +	popq	%rbp
> +	.cfi_def_cfa_offset 24
> +	.cfi_restore 6
> +	popq	%rbx
> +	.cfi_def_cfa_offset 16
> +	.cfi_restore 3
> +	popq	%rax
> +        .cfi_def_cfa_offset 8
> +	ret
> +#	.cfi_endproc

Why are startproc/endproc commented out here?

> --- /dev/null
> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.s
> @@ -0,0 +1,42 @@
> +# Testcase for save reg ops for callee-saved registers
> +# These latter two pushq's of callee-saved regs must NOT generate
> +# .cfi_offset.
> +
> +	.text
> +	.globl	foo
> +	.type	foo, @function
> +foo:
> +	.cfi_startproc
> +	pushq	%r12
> +	.cfi_def_cfa_offset 16
> +	.cfi_offset 12, -16
> +	pushq	%r13
> +	.cfi_def_cfa_offset 24
> +	.cfi_offset 13, -24
> +# The function may use callee-saved registers for its use, and may even
> +# chose to spill them to stack if necessary.
> +	addq    %rax, %r13
> +	subq 	$8, %r13
> +# These two pushq's of callee-saved regs must NOT generate
> +# .cfi_offset.
> +	pushq	%r13
> +	.cfi_def_cfa_offset 32
> +	pushq	%rax
> +	.cfi_def_cfa_offset 40

Why "two" in the comment? %rax isn't callee-saved, is it? (Same in
variant 3 of this kind of test then.)

> --- /dev/null
> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.s
> @@ -0,0 +1,55 @@
> +	.type	byte_insert_op1, @function
> +byte_insert_op1:
> +.LFB10:
> +	.cfi_startproc
> +	endbr64
> +	pushq	%rbp
> +	.cfi_def_cfa_offset 16
> +	.cfi_offset 6, -16
> +	movq	%rsp, %rbp
> +	.cfi_def_cfa_register 6
> +	pushq	%r12
> +	.cfi_offset 12, -24
> +	pushq	%rbx
> +	.cfi_offset 3, -32
> +	subq	$24, %rsp
> +	movl	%edi, -20(%rbp)
> +	movq	%rsi, -32(%rbp)
> +	movl	%edx, -24(%rbp)
> +	movq	%rcx, -40(%rbp)
> +# The program may use callee-saved registers for its use, and may even
> +# chose to read them from stack if necessary.  The following use should
> +# not be treated as reg restore for SCFI purposes (because rbx has been
> +# saved to -16(%rbp).

But even if the value was read back from -16(%rbp) you wouldn't know for
sure that that's the ultimate restore. Yet another constraint?

> --- /dev/null
> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.d
> @@ -0,0 +1,36 @@
> +#as: --scfi -W
> +#objdump: -Wf
> +#name: Synthesize CFI in presence of control flow 1
> +#...
> +Contents of the .eh_frame section:
> +
> +00000000 0+0014 0+0000 CIE
> +  Version:               1
> +  Augmentation:          "zR"
> +  Code alignment factor: 1
> +  Data alignment factor: -8
> +  Return address column: 16
> +  Augmentation data:     [01][abc]
> +  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
> +  DW_CFA_offset: r16 \(rip\) at cfa-8
> +  DW_CFA_nop
> +  DW_CFA_nop
> +
> +0+0018 0+0024 0000001c FDE cie=00000000 pc=0+0000..0+003a
> +  DW_CFA_advance_loc: 1 to 0+0001
> +  DW_CFA_def_cfa_offset: 16
> +  DW_CFA_offset: r3 \(rbx\) at cfa-16
> +  DW_CFA_advance_loc: 37 to 0+0026
> +  DW_CFA_remember_state
> +  DW_CFA_advance_loc: 1 to 0+0027
> +  DW_CFA_restore: r3 \(rbx\)
> +  DW_CFA_def_cfa_offset: 8
> +  DW_CFA_advance_loc: 1 to 0+0028
> +  DW_CFA_restore_state
> +  DW_CFA_advance_loc: 9 to 0+0031
> +  DW_CFA_restore: r3 \(rbx\)
> +  DW_CFA_def_cfa_offset: 8
> +  DW_CFA_nop
> +#...
> +
> +#pass

Seeing this recurring pattern (the last three lines): Why not just #pass?

> --- /dev/null
> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.s
> @@ -0,0 +1,47 @@
> +# Testcase with one dominator bb and two exit bbs
> +# Something like for: return ferror (f) || fclose (f) != 0;
> +	.text
> +	.section	.rodata.str1.1,"aMS",@progbits,1
> +.LC0:
> +	.string	"w"
> +.LC1:
> +	.string	"conftest.out"
> +	.section	.text.startup,"ax",@progbits
> +	.p2align 4
> +	.globl	main
> +	.type	main, @function
> +main:
> +.LFB11:

Coming back to "hand-written assembly": The above looks very much like it
was compiler output (earlier tests did, too, but it's perhaps more
prominent here). That's not necessarily what a human might write, and
hence I wonder about the overall coverage that can be gained that way.

> --- /dev/null
> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.d
> @@ -0,0 +1,5 @@
> +#as: --scfi -W
> +#objdump: -Wf
> +#name: Synthesize CFI for add insn
> +
> +#pass
> --- /dev/null
> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.l
> @@ -0,0 +1,3 @@
> +.*Assembler messages:
> +.*12: Warning: --scfi=all ignores some user-specified CFI directives
> +.*22: Warning: Untraceable control flow for func 'foo'; Skipping SCFI
> --- /dev/null
> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.s
> @@ -0,0 +1,23 @@
> +# Testcase with a variety of "change of flow instructions"
> +#
> +# Must be run with -W so it remains warning free.
> +#
> +# This test does not have much going on wrt synthesis of CFI;
> +# it just aims to ensure x8_64 -> ginsn decoding must behave
> +# gracefully for these "change of flow instructions"
> +	.text
> +	.globl	foo
> +	.type	foo, @function
> +foo:
> +	.cfi_startproc
> +	addq    %rdx, %rax
> +	notrack jmp     *%rax
> +	call   *%r8
> +	jmp     *48(%rdi)
> +	jo      .L179
> +.L179:
> +	ret
> +	.cfi_endproc
> +.LFE0:
> +	.size	foo, .-foo

What exactly is being tested here? The .d file is effectively empty,
and the .l file expects a warning on the .size directive (which tells
about nothing regarding the reason for the failure to produce SCFI).

> --- /dev/null
> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-diag-1.s
> @@ -0,0 +1,23 @@
> +# Testcase for REG_FP based CFA
> +# and using REG_FP as scratch.
> +	.text
> +	.globl	foo
> +	.type	foo, @function
> +foo:
> +	.cfi_startproc
> +	pushq	%rbp
> +	.cfi_def_cfa_offset 16
> +	.cfi_offset 6, -16
> +	movq    %rsp, %rbp
> +	.cfi_def_cfa_register 6
> +# The following add causes REG_FP to become untraceable
> +	addq	%rax, %rbp

But this isn't a problem as long as FP isn't further used. Indeed ...

> +	.cfi_def_cfa_register 7
> +	pop	%rbp

... its original value is restored right afterwards. Yet another constraint?

> --- /dev/null
> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.s
> @@ -0,0 +1,55 @@
> +# Testcase for a diagnostic around assymetrical restore
> +# Testcase inspired by byte_insert_op1 in libiberty
> +# False positive for the diagnostic
> +.type	foo, @function
> +foo:
> +.LFB10:
> +	.cfi_startproc
> +	endbr64
> +	pushq	%rbp
> +	.cfi_def_cfa_offset 16
> +	.cfi_offset 6, -16
> +	movq	%rsp, %rbp
> +	.cfi_def_cfa_register 6
> +	pushq	%r12
> +	pushq	%rbx
> +	subq	$24, %rsp
> +	.cfi_offset 12, -24
> +	.cfi_offset 3, -32
> +	movl	%edi, -20(%rbp)
> +	movq	%rsi, -32(%rbp)
> +	movl	%edx, -24(%rbp)
> +	movq	%rcx, -40(%rbp)
> +# The assembler cannot differentiate that the following
> +# mov to %rbx is not a true restore operation, but simply
> +# %rbx register usage as a scratch reg of some sort.
> +# The assembler merely warns of a possible assymetric restore operation
> +# In this case, its noise for the user unfortunately.
> +	movq	-40(%rbp), %rbx
> +	movq	-40(%rbp), %rax
> +	leaq	3(%rax), %r12
> +	jmp	.L563
> +.L564:
> +	subq	$1, %rbx
> +	subq	$1, %r12
> +	movzbl	(%rbx), %eax
> +	movb	%al, (%r12)
> +.L563:
> +	cmpq	-32(%rbp), %rbx
> +	jne	.L564

But this is pretty common usage. Imo this (the false positive warning)
cannot remain like this.

> --- /dev/null
> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.s
> @@ -0,0 +1,13 @@
> +	.text
> +	.globl	foo
> +	.type	foo, @function
> +foo:
> +	.cfi_startproc
> +	pushq	%rbp
> +        .cfi_def_cfa_offset 16
> +        .cfi_offset 6, -16
> +	ret
> +	.cfi_endproc
> +.LFE0:
> +	.size	foo, .-foo

This, otoh, imo wants diagnosing. The framework ought to recognize that
the RET doesn't use the correct stack slot.

Additionally, isn't scfi-simple-1.s effectively testing the same? The
code at least looks extremely similar.

> --- /dev/null
> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.l
> @@ -0,0 +1,2 @@
> +Assembler messages:
> +Fatal error: Synthesizing CFI is not supported for this ABI
> diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.s
> new file mode 100644
> index 00000000000..87d2a4971a1
> --- /dev/null
> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.s
> @@ -0,0 +1,10 @@
> +# Testcase run with --m32 (Not supported).
> +	.text
> +	.globl	foo
> +	.type	foo, @function
> +foo:
> +	pushq	%rbp

This code, being built with --32, is bogus anyway. I wonder why this file
has any insns in the first place.

> --- /dev/null
> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-x86-64.exp
> @@ -0,0 +1,103 @@
> +# Copyright (C) 2022-2023 Free Software Foundation, Inc.
> +
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program; if not, write to the Free Software
> +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
> +
> +if { ![is_elf_format] } then {
> +    return
> +}
> +
> +# common tests
> +if  { ([istarget "x86_64-*-*"]) } then {
> +
> +    global ASFLAGS
> +    set old_ASFLAGS "$ASFLAGS"
> +
> +    run_dump_test "scfi-cfi-label-1"
> +    run_list_test "scfi-cfi-label-1" "--scfi --warn"
> +
> +    run_list_test "scfi-diag-1" "--scfi"
> +    run_list_test "scfi-fp-diag-2" "--scfi"
> +    run_list_test "scfi-diag-2" "--scfi"
> +
> +    run_list_test "scfi-unsupported-1" "--32 --scfi"

Perhaps a 2nd run with --x32, unless (see above) you choose to support
that ABI as well?

Jan

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

* Re: [PATCH, V2 06/10] gas: scfidw2gen: new functionality to prepapre for SCFI
  2023-10-31 11:28   ` Jan Beulich
@ 2023-10-31 22:06     ` Indu Bhagat
  2023-11-02 10:35       ` Jan Beulich
  0 siblings, 1 reply; 30+ messages in thread
From: Indu Bhagat @ 2023-10-31 22:06 UTC (permalink / raw)
  To: Jan Beulich; +Cc: binutils

Hi Jan,

Thanks for reviewing.

On 10/31/23 04:28, Jan Beulich wrote:
> On 30.10.2023 17:51, Indu Bhagat wrote:
>> --- /dev/null
>> +++ b/gas/scfidw2gen.c
>> @@ -0,0 +1,305 @@
>> +/* scfidw2gen.c - Support for emission of synthesized Dwarf2 CFI.
>> +   Copyright (C) 2003-2023 Free Software Foundation, Inc.
> 
> Is this year range really applicable to this new file?
> 

No. I will fix it here and other files.

>> +   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 "dw2gencfi.h"
>> +#include "subsegs.h"
>> +#include "scfidw2gen.h"
>> +
>> +#if defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN)
>> +
>> +static int scfi_ignore_warn_once = 0;
> 
> Nit: bool please (and no real need for an initializer).
> 

OK.

>> +static void dot_scfi_sections (int);
>> +static void dot_scfi_ignore (int);
>> +static void dot_scfi (int);
> 
> May I suggest to avoid such forward declarations by moving ...
> 
>> +const pseudo_typeS scfi_pseudo_table[] =
> 
> ... this table towards the bottom of the file?
> 

OK.

>> +  {
>> +    { "cfi_sections", dot_scfi_sections, 0 }, /* No ignore.  */
> 
> Instead of three such individual comments, how about putting the three
> relevant ones first, followed by a comment (serving as a separator) and
> then all dot_scfi_ignore entries?
> 

OK.

>> +    { "cfi_startproc", dot_scfi_ignore, 0 },
>> +    { "cfi_endproc", dot_scfi_ignore, 0 },
>> +    { "cfi_fde_data", dot_scfi_ignore, 0 },
>> +    { "cfi_def_cfa", dot_scfi_ignore, 0 },
>> +    { "cfi_def_cfa_register", dot_scfi_ignore, 0 },
>> +    { "cfi_def_cfa_offset", dot_scfi_ignore, 0 },
>> +    { "cfi_adjust_cfa_offset", dot_scfi_ignore, 0 },
>> +    { "cfi_offset", dot_scfi_ignore, 0 },
>> +    { "cfi_rel_offset", dot_scfi_ignore, 0 },
>> +    { "cfi_register", dot_scfi_ignore, 0 },
>> +    { "cfi_return_column", dot_scfi_ignore, 0 },
>> +    { "cfi_restore", dot_scfi_ignore, 0 },
>> +    { "cfi_undefined", dot_scfi_ignore, 0 },
>> +    { "cfi_same_value", dot_scfi_ignore, 0 },
>> +    { "cfi_remember_state", dot_scfi_ignore, 0 },
>> +    { "cfi_restore_state", dot_scfi_ignore, 0 },
>> +    { "cfi_window_save", dot_scfi_ignore, 0 },
>> +    { "cfi_negate_ra_state", dot_scfi_ignore, 0 },
>> +    { "cfi_escape", dot_scfi_ignore, 0 },
>> +    { "cfi_signal_frame", dot_scfi, CFI_signal_frame }, /* No ignore.  */
>> +    { "cfi_personality", dot_scfi_ignore, 0 },
>> +    { "cfi_personality_id", dot_scfi_ignore, 0 },
>> +    { "cfi_lsda", dot_scfi_ignore, 0 },
>> +    { "cfi_val_encoded_addr", dot_scfi_ignore, 0 },
>> +    { "cfi_inline_lsda", dot_scfi_ignore, 0 },
>> +    { "cfi_label", dot_scfi, CFI_label }, /* No ignore.  */
>> +    { "cfi_val_offset", dot_scfi_ignore, 0 },
>> +    { NULL, NULL, 0 }
>> +  };
>> +
>> +static void
>> +dot_scfi_ignore (int ignored ATTRIBUTE_UNUSED)
>> +{
>> +  gas_assert (flag_synth_cfi);
>> +
>> +  if (scfi_ignore_warn_once == 0)
>> +    {
>> +      as_warn (_("--scfi=all ignores some user-specified CFI directives"));
> 
> s/some/most/ ?
> 

Hmm. Technically, "most" is the correct choice. So, OK.

>> +void
>> +scfi_dot_cfi_startproc (symbolS *start_sym)
> 
> This and the following two functions presently have no caller, and I
> also can't spot equivalents in dw2gencfi.c. How are they (going) to be
> used? This ...
> 
>> +{
>> +  if (frchain_now->frch_cfi_data != NULL)
>> +    {
>> +      as_bad (_("previous CFI entry not closed (missing .cfi_endproc)"));
> 
> ... for example suggests that the function here might really be the
> handler for .cfi_startproc, yet the table above says .cfi_startproc is
> ignored.
> 

The callers of scfi_dot_cfi_startproc (), scfi_dot_cfi_endproc () and 
scfi_dot_cfi () are in scfi.c, when its time to emit DWARF CFI after 
SCFI machinery has generated the SCFI Ops (See scfi_dot_cfi () and 
scfi_emit_dw2cfi ()).  The callers are added in the next patch in the 
series, "[PATCH, V2 07/10] gas: synthesize CFI for hand-written asm".

>> +#else
>> +
>> +static void
>> +dot_scfi_dummy (int ignored ATTRIBUTE_UNUSED)
>> +{
>> +  as_bad (_("SCFI is not supported for this target"));
>> +  ignore_rest_of_line ();
>> +}
>> +
>> +const pseudo_typeS scfi_pseudo_table[] =
>> +  {
>> +    { "cfi_sections", dot_scfi_dummy, 0 },
>> +    { "cfi_startproc", dot_scfi_dummy, 0 },
>> +    { "cfi_endproc", dot_scfi_dummy, 0 },
>> +    { "cfi_fde_data", dot_scfi_dummy, 0 },
>> +    { "cfi_def_cfa", dot_scfi_dummy, 0 },
>> +    { "cfi_def_cfa_register", dot_scfi_dummy, 0 },
>> +    { "cfi_def_cfa_offset", dot_scfi_dummy, 0 },
>> +    { "cfi_adjust_cfa_offset", dot_scfi_dummy, 0 },
>> +    { "cfi_offset", dot_scfi_dummy, 0 },
>> +    { "cfi_rel_offset", dot_scfi_dummy, 0 },
>> +    { "cfi_register", dot_scfi_dummy, 0 },
>> +    { "cfi_return_column", dot_scfi_dummy, 0 },
>> +    { "cfi_restore", dot_scfi_dummy, 0 },
>> +    { "cfi_undefined", dot_scfi_dummy, 0 },
>> +    { "cfi_same_value", dot_scfi_dummy, 0 },
>> +    { "cfi_remember_state", dot_scfi_dummy, 0 },
>> +    { "cfi_restore_state", dot_scfi_dummy, 0 },
>> +    { "cfi_window_save", dot_scfi_dummy, 0 },
>> +    { "cfi_negate_ra_state", dot_scfi_dummy, 0 },
>> +    { "cfi_escape", dot_scfi_dummy, 0 },
>> +    { "cfi_signal_frame", dot_scfi_dummy, 0 },
>> +    { "cfi_personality", dot_scfi_dummy, 0 },
>> +    { "cfi_personality_id", dot_scfi_dummy, 0 },
>> +    { "cfi_lsda", dot_scfi_dummy, 0 },
>> +    { "cfi_val_encoded_addr", dot_scfi_dummy, 0 },
>> +    { "cfi_inline_lsda", dot_scfi_dummy, 0 },
>> +    { "cfi_label", dot_scfi_dummy, 0 },
>> +    { "cfi_val_offset", dot_scfi_dummy, 0 },
>> +    { NULL, NULL, 0 }
>> +  };
>> +
>> +#endif
> 
> Is this really needed? Can't you simply error on use of the command
> line option, without the need for the extra table and dummy handler?
> 

Actually, gas does not even present the option --scfi when the target 
does not have the two required defines TARGET_USE_SCFI  and 
TARGET_USE_GINSN.  So I could guard the code in pobegin () in read.c 
(and remove the dummy handlers in scfidw2genc.c):

   /* Now CFI ones.  */
#if defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN)
   if (flag_synth_cfi)
     {
       pop_table_name = "scfi";
       scfi_pop_insert ();
     }
   else
     {
       pop_table_name = "cfi";
       cfi_pop_insert ();
     }
#else
   pop_table_name = "cfi";
   cfi_pop_insert ();
#endif


>> --- /dev/null
>> +++ b/gas/scfidw2gen.h
>> @@ -0,0 +1,37 @@
>> +/* scfidw2gen.h - Support for emitting synthesized Dwarf2 CFI.
>> +   Copyright (C) 2003-2023 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 SCFIDW2GEN_H
>> +#define SCFIDW2GEN_H
>> +
>> +#include "as.h"
>> +#include "dwarf2.h"
>> +
>> +extern int all_cfi_sections;
> 
> This needs to go into dw2gencfi.h, such that dw2gencfi.c will also see
> the declaration (and the compiler be able to check that declaration and
> definition are in sync).
> 

OK.


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

* Re: [PATCH, V2 09/10] gas: testsuite: add a x86_64 testsuite for SCFI
  2023-10-31 16:13   ` Jan Beulich
@ 2023-11-01  6:24     ` Indu Bhagat
  2023-11-02 12:28       ` Jan Beulich
  0 siblings, 1 reply; 30+ messages in thread
From: Indu Bhagat @ 2023-11-01  6:24 UTC (permalink / raw)
  To: Jan Beulich; +Cc: binutils

Hi Jan,

Thanks for reviewing.

On 10/31/23 09:13, Jan Beulich wrote:
> On 30.10.2023 17:51, Indu Bhagat wrote:
>> --- /dev/null
>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-add-2.s
>> @@ -0,0 +1,48 @@
>> +        .section        .rodata
>> +	.type	simd_cmp_op, @object
>> +	.size	simd_cmp_op, 8
>> +simd_cmp_op:
>> +	.long	2
>> +	.zero	4
>> +
>> +# Testcase for add instruction.
>> +# add reg, reg instruction
>> +	.text
>> +	.globl	foo
>> +	.type	foo, @function
>> +foo:
>> +	.cfi_startproc
>> +	pushq	%rbp
>> +	.cfi_def_cfa_offset 16
>> +	.cfi_offset 6, -16
>> +	movq    %rsp, %rbp
>> +	.cfi_def_cfa_register 6
>> +	pushq	%r12
>> +	.cfi_offset 12, -24
>> +	mov	%rsp, %r12
> 
> You copy %rsp to %r12 here, and ...
> 
>> +# Stack manipulation is permitted if the base register for
>> +# tracking CFA has been changed to FP.
>> +	addq    %rdx, %rsp
>> +	addq	%rsp, %rax
>> +# Some add instructions may access the stack indirectly.  Such
>> +# accesses do not make REG_FP untraceable.
>> +	addl    %eax, -84(%rbp)
>> +# Other kind of add instructions should not error out in the
>> +# x86_64 -> ginsn translator
>> +	addq    $simd_cmp_op+8, %rdx
>> +	addl    %edx, -32(%rsp)
>> +	addl    $1, fb_low_counter(,%rbx,4)
>> +	mov	%r12, %rsp
> 
> ... you restore it here, but both without any .cfi_* annotation.
> It is therefore unclear whether this is in any way related to ...
> 

There are no .cfi_* annotations for the %rsp updates here because the 
CFA tracking has already been switched to REG_FP based tracking.  From 
the perspective of DWARF CFI, this usages of the stack do not need to 
tracked.  If unwinding happens from any instruction in the above 
sequence, we already have the correct and complete unwind information.

>> +# Popping a callee-saved register.
>> +# RSP must be traceable.
>> +	pop     %r12
>> +	.cfi_restore 12
> 
> ... what the comment says about these.
> 

The comment here means that hand-written programs may use stack for 
their local usage etc. but must ensure that before or in the epilogue, 
rsp is restored to the desired value (before register restores via pop 
instructions).

This is not a special handling in the SCFI machinery but it is what is 
needed to ensure correctness of the function.  For asm not adhering to 
the ABI/calling conventions, SCFI cannot be used.

>> +	leave
>> +	.cfi_def_cfa_register 7
>> +	.cfi_restore 6
> 
> Using numbers here isn't very helpful, I'm afraid.
> 

I am not sure I understand.  The DWARF register numbers and the offset 
values are required by the semantics of those CFI directives.

>> --- /dev/null
>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.s
>> @@ -0,0 +1,27 @@
>> +# Testcase where a user may define hot and cold areas of function
>> +# Note how the .type, and .size directives may be placed differently
>> +# than a regular function.
>> +
>> +	.globl  foo
>> +	.type   foo, @function
>> +foo:
>> +        .cfi_startproc
>> +        testl   %edi, %edi
>> +        je      .L3
>> +        movl    b(%rip), %eax
>> +        ret
>> +        .cfi_endproc
>> +        .section        .text.unlikely
>> +        .cfi_startproc
>> +        .type   foo.cold, @function
>> +foo.cold:
>> +.L3:
>> +        pushq   %rax
>> +        .cfi_def_cfa_offset 16
>> +        call    abort
>> +       .cfi_endproc
>> +.LFE11:
>> +        .text
>> +        .size   foo, .-foo
>> +        .section        .text.unlikely
>> +        .size   foo.cold, .-foo.cold
> 
> Related to the comment: Is it "may" or is it rather "need to"?
> (In the latter case it would be yet another constraint on when this
> new machinery is actually usable.)
> 

The .size directives are not necessary for SCFI.  They are recommended.
In this test, removing the code after .LFE11 should still work, I will 
check this.

>> --- /dev/null
>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.s
>> @@ -0,0 +1,11 @@
>> +# A programmer may not bother to set the size of the
>> +# function symbols via an explicit .size directive.
>> +	.globl  foo
>> +	.type   foo, @function
>> +foo:
>> +        .cfi_startproc
>> +        testl   %edi, %edi
>> +        je      .L3
>> +        movl    b(%rip), %eax
>> +        ret
>> +       .cfi_endproc
> 
> Wasn't it said elsewhere that .size needs using after the function?
> 

Only in the RFC series, the implementation of SCFI required that the 
blocks be closed by using a .size directive.  In the V1 onwards 
implementation, this restriction has been lifted.  This was based on the 
feedback that users may have hand-written asm without the .size directives.

I have tried to correct the code comments and git commit log to reflect 
that .size directive is recommended but not necessary, but perhaps some 
stale text/statement has slipped through. I will fix if I see it.

>> --- /dev/null
>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.l
>> @@ -0,0 +1,2 @@
>> +.*Assembler messages:
>> +.*9: Warning: --scfi=all ignores some user-specified CFI directives
> 
> Is repeating this for (about?) every test really necessary / useful?
> If multiple passes for every test were to make sense to me, I'd expect
> one pass with SCFI and one pass without, where the directives then
> take effect. And then the same set of expectations should match.
> 

re: is repeating this useful? Strictly speaking no. _But_, I think as 
the code evolves, we may add more diagnostics to the SCFI machinery. 
Explicitly checking for the set of warnings helps us ensure no new 
warnings show up where they are not expected. In a way, it is a stricter 
check and ensures no unintented slippage.

>> --- /dev/null
>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.s
>> @@ -0,0 +1,49 @@
>> +# Testcase for switching between sp/fp based CFA.
>> +	.text
>> +	.globl	foo
>> +	.type	foo, @function
>> +foo:
>> +        .cfi_startproc
>> +        pushq   %r14
>> +        .cfi_def_cfa_offset 16
>> +        .cfi_offset 14, -16
>> +        pushq   %r13
>> +        .cfi_def_cfa_offset 24
>> +        .cfi_offset 13, -24
>> +        pushq   %r12
>> +        .cfi_def_cfa_offset 32
>> +        .cfi_offset 12, -32
>> +        pushq   %rbp
>> +        .cfi_def_cfa_offset 40
>> +        .cfi_offset 6, -40
>> +        pushq   %rbx
>> +        .cfi_def_cfa_offset 48
>> +        .cfi_offset 3, -48
>> +        movq    %rdi, %rbx
>> +        subq    $32, %rsp
>> +        .cfi_def_cfa_offset 80
>> +# This mov does not switch CFA tracking to REG_FP, because there has already
>> +# been stack usage between here and the push %rbp
>> +        movq    %rsp, %rbp
> 
> Yet another constraint?
> 

Not sure I understand. This is not a constraint for SCFI.  A pattern 
where the user does:
     push %rbp
     .. more stack usage..
     movq %rsp, %rbp
is not using frame pointer for stack tracing, but simply as a scratch 
register.

So the SCFI machinery simply acknowledges this and does not switch to 
REG_FP based CFA tracking.


>> +        xorl    %eax, %eax
>> +        addq    $32, %rsp
>> +        .cfi_def_cfa_offset 48
>> +        popq    %rbx
>> +	.cfi_restore 3
> 
> Nit: inconsistent indentation (also elsewhere).
> 

I found lots of instances of this issue. Will fix it.

>> --- /dev/null
>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.s
>> @@ -0,0 +1,26 @@
>> +	.text
>> +	.globl	foo
>> +	.type	foo, @function
>> +foo:
>> +#	.cfi_startproc
>> +	pushq	%rax
>> +	.cfi_def_cfa_offset 16
>> +	push	%rbx
>> +	.cfi_def_cfa_offset 24
>> +	.cfi_offset 3, -24
>> +	pushq	%rbp
>> +        .cfi_def_cfa_offset 32
>> +        .cfi_offset 6, -32
>> +	popq	%rbp
>> +	.cfi_def_cfa_offset 24
>> +	.cfi_restore 6
>> +	popq	%rbx
>> +	.cfi_def_cfa_offset 16
>> +	.cfi_restore 3
>> +	popq	%rax
>> +        .cfi_def_cfa_offset 8
>> +	ret
>> +#	.cfi_endproc
> 
> Why are startproc/endproc commented out here?
> 

They are reminiscent of some previous state I had the tests in. I will 
add them back in here and other instances.

>> --- /dev/null
>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.s
>> @@ -0,0 +1,42 @@
>> +# Testcase for save reg ops for callee-saved registers
>> +# These latter two pushq's of callee-saved regs must NOT generate
>> +# .cfi_offset.
>> +
>> +	.text
>> +	.globl	foo
>> +	.type	foo, @function
>> +foo:
>> +	.cfi_startproc
>> +	pushq	%r12
>> +	.cfi_def_cfa_offset 16
>> +	.cfi_offset 12, -16
>> +	pushq	%r13
>> +	.cfi_def_cfa_offset 24
>> +	.cfi_offset 13, -24
>> +# The function may use callee-saved registers for its use, and may even
>> +# chose to spill them to stack if necessary.
>> +	addq    %rax, %r13
>> +	subq 	$8, %r13
>> +# These two pushq's of callee-saved regs must NOT generate
>> +# .cfi_offset.
>> +	pushq	%r13
>> +	.cfi_def_cfa_offset 32
>> +	pushq	%rax
>> +	.cfi_def_cfa_offset 40
> 
> Why "two" in the comment? %rax isn't callee-saved, is it? (Same in
> variant 3 of this kind of test then.)
> 

Correct, %rax is not calee-saved. I will fix the comment.

>> --- /dev/null
>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.s
>> @@ -0,0 +1,55 @@
>> +	.type	byte_insert_op1, @function
>> +byte_insert_op1:
>> +.LFB10:
>> +	.cfi_startproc
>> +	endbr64
>> +	pushq	%rbp
>> +	.cfi_def_cfa_offset 16
>> +	.cfi_offset 6, -16
>> +	movq	%rsp, %rbp
>> +	.cfi_def_cfa_register 6
>> +	pushq	%r12
>> +	.cfi_offset 12, -24
>> +	pushq	%rbx
>> +	.cfi_offset 3, -32
>> +	subq	$24, %rsp
>> +	movl	%edi, -20(%rbp)
>> +	movq	%rsi, -32(%rbp)
>> +	movl	%edx, -24(%rbp)
>> +	movq	%rcx, -40(%rbp)
>> +# The program may use callee-saved registers for its use, and may even
>> +# chose to read them from stack if necessary.  The following use should
>> +# not be treated as reg restore for SCFI purposes (because rbx has been
>> +# saved to -16(%rbp).
> 
> But even if the value was read back from -16(%rbp) you wouldn't know for
> sure that that's the ultimate restore. Yet another constraint?
> 

Correct, we cannot know in GAS that it's the ultimate restore before 
return.  I ask later as well, but the usage of "Yet another constraint" 
expression is not clear to me.

>> --- /dev/null
>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.d
>> @@ -0,0 +1,36 @@
>> +#as: --scfi -W
>> +#objdump: -Wf
>> +#name: Synthesize CFI in presence of control flow 1
>> +#...
>> +Contents of the .eh_frame section:
>> +
>> +00000000 0+0014 0+0000 CIE
>> +  Version:               1
>> +  Augmentation:          "zR"
>> +  Code alignment factor: 1
>> +  Data alignment factor: -8
>> +  Return address column: 16
>> +  Augmentation data:     [01][abc]
>> +  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
>> +  DW_CFA_offset: r16 \(rip\) at cfa-8
>> +  DW_CFA_nop
>> +  DW_CFA_nop
>> +
>> +0+0018 0+0024 0000001c FDE cie=00000000 pc=0+0000..0+003a
>> +  DW_CFA_advance_loc: 1 to 0+0001
>> +  DW_CFA_def_cfa_offset: 16
>> +  DW_CFA_offset: r3 \(rbx\) at cfa-16
>> +  DW_CFA_advance_loc: 37 to 0+0026
>> +  DW_CFA_remember_state
>> +  DW_CFA_advance_loc: 1 to 0+0027
>> +  DW_CFA_restore: r3 \(rbx\)
>> +  DW_CFA_def_cfa_offset: 8
>> +  DW_CFA_advance_loc: 1 to 0+0028
>> +  DW_CFA_restore_state
>> +  DW_CFA_advance_loc: 9 to 0+0031
>> +  DW_CFA_restore: r3 \(rbx\)
>> +  DW_CFA_def_cfa_offset: 8
>> +  DW_CFA_nop
>> +#...
>> +
>> +#pass
> 
> Seeing this recurring pattern (the last three lines): Why not just #pass?
> 

I thought adding #... is clearer as it indicates that the test knowingly 
skips lines thereafter.

>> --- /dev/null
>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.s
>> @@ -0,0 +1,47 @@
>> +# Testcase with one dominator bb and two exit bbs
>> +# Something like for: return ferror (f) || fclose (f) != 0;
>> +	.text
>> +	.section	.rodata.str1.1,"aMS",@progbits,1
>> +.LC0:
>> +	.string	"w"
>> +.LC1:
>> +	.string	"conftest.out"
>> +	.section	.text.startup,"ax",@progbits
>> +	.p2align 4
>> +	.globl	main
>> +	.type	main, @function
>> +main:
>> +.LFB11:
> 
> Coming back to "hand-written assembly": The above looks very much like it
> was compiler output (earlier tests did, too, but it's perhaps more
> prominent here). That's not necessarily what a human might write, and
> hence I wonder about the overall coverage that can be gained that way.
> 

Yes, its inspired by compiler generated output. I ran into this when 
running some tests. Its still a good basic cfg test for SCFI - a 
function with two return paths.

>> --- /dev/null
>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.d
>> @@ -0,0 +1,5 @@
>> +#as: --scfi -W
>> +#objdump: -Wf
>> +#name: Synthesize CFI for add insn
>> +
>> +#pass
>> --- /dev/null
>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.l
>> @@ -0,0 +1,3 @@
>> +.*Assembler messages:
>> +.*12: Warning: --scfi=all ignores some user-specified CFI directives
>> +.*22: Warning: Untraceable control flow for func 'foo'; Skipping SCFI
>> --- /dev/null
>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.s
>> @@ -0,0 +1,23 @@
>> +# Testcase with a variety of "change of flow instructions"
>> +#
>> +# Must be run with -W so it remains warning free.
>> +#
>> +# This test does not have much going on wrt synthesis of CFI;
>> +# it just aims to ensure x8_64 -> ginsn decoding must behave
>> +# gracefully for these "change of flow instructions"
>> +	.text
>> +	.globl	foo
>> +	.type	foo, @function
>> +foo:
>> +	.cfi_startproc
>> +	addq    %rdx, %rax
>> +	notrack jmp     *%rax
>> +	call   *%r8
>> +	jmp     *48(%rdi)
>> +	jo      .L179
>> +.L179:
>> +	ret
>> +	.cfi_endproc
>> +.LFE0:
>> +	.size	foo, .-foo
> 
> What exactly is being tested here? The .d file is effectively empty,
> and the .l file expects a warning on the .size directive (which tells
> about nothing regarding the reason for the failure to produce SCFI).
> 

Only that the x86 -> ginsn does not choke on these instructions.  You 
are right that it doesnt test much for SCFI per se.

>> --- /dev/null
>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-diag-1.s
>> @@ -0,0 +1,23 @@
>> +# Testcase for REG_FP based CFA
>> +# and using REG_FP as scratch.
>> +	.text
>> +	.globl	foo
>> +	.type	foo, @function
>> +foo:
>> +	.cfi_startproc
>> +	pushq	%rbp
>> +	.cfi_def_cfa_offset 16
>> +	.cfi_offset 6, -16
>> +	movq    %rsp, %rbp
>> +	.cfi_def_cfa_register 6
>> +# The following add causes REG_FP to become untraceable
>> +	addq	%rax, %rbp
> 
> But this isn't a problem as long as FP isn't further used. Indeed ...
> 
>> +	.cfi_def_cfa_register 7
>> +	pop	%rbp
> 
> ... its original value is restored right afterwards. Yet another constraint?
> 

Hmm. I need some clarification on the statement "Yet another constraint".

>> --- /dev/null
>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.s
>> @@ -0,0 +1,55 @@
>> +# Testcase for a diagnostic around assymetrical restore
>> +# Testcase inspired by byte_insert_op1 in libiberty
>> +# False positive for the diagnostic
>> +.type	foo, @function
>> +foo:
>> +.LFB10:
>> +	.cfi_startproc
>> +	endbr64
>> +	pushq	%rbp
>> +	.cfi_def_cfa_offset 16
>> +	.cfi_offset 6, -16
>> +	movq	%rsp, %rbp
>> +	.cfi_def_cfa_register 6
>> +	pushq	%r12
>> +	pushq	%rbx
>> +	subq	$24, %rsp
>> +	.cfi_offset 12, -24
>> +	.cfi_offset 3, -32
>> +	movl	%edi, -20(%rbp)
>> +	movq	%rsi, -32(%rbp)
>> +	movl	%edx, -24(%rbp)
>> +	movq	%rcx, -40(%rbp)
>> +# The assembler cannot differentiate that the following
>> +# mov to %rbx is not a true restore operation, but simply
>> +# %rbx register usage as a scratch reg of some sort.
>> +# The assembler merely warns of a possible assymetric restore operation
>> +# In this case, its noise for the user unfortunately.
>> +	movq	-40(%rbp), %rbx
>> +	movq	-40(%rbp), %rax
>> +	leaq	3(%rax), %r12
>> +	jmp	.L563
>> +.L564:
>> +	subq	$1, %rbx
>> +	subq	$1, %r12
>> +	movzbl	(%rbx), %eax
>> +	movb	%al, (%r12)
>> +.L563:
>> +	cmpq	-32(%rbp), %rbx
>> +	jne	.L564
> 
> But this is pretty common usage. Imo this (the false positive warning)
> cannot remain like this.
> 

The warning of "Warning: SCFI: asymetrical register restore" was added 
to alert user if they do something like
     pushq %rbx
     pushq %r12
     ...
     popq  %rbx
     popq  %r12
i.e., asymmetric save and restore.  Now the user may do a restore to any 
register and use what was saved on stack as scratch regiser; the only 
way to differentiate between a "true reg restore in epilogue" vs "reg 
restored for scratch purposes" is to know whether its the epilogue.  GAS 
will not be able to determine that easily.

We could remove the warning altogether.  I just thought it provides 
users with some protection / validation of hand-written asm.


>> --- /dev/null
>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.s
>> @@ -0,0 +1,13 @@
>> +	.text
>> +	.globl	foo
>> +	.type	foo, @function
>> +foo:
>> +	.cfi_startproc
>> +	pushq	%rbp
>> +        .cfi_def_cfa_offset 16
>> +        .cfi_offset 6, -16
>> +	ret
>> +	.cfi_endproc
>> +.LFE0:
>> +	.size	foo, .-foo
> 
> This, otoh, imo wants diagnosing. The framework ought to recognize that
> the RET doesn't use the correct stack slot.
> 

Yes, its on my TODO list. I plan to add this: if the stack is not 
balanced at the return from function, gas can warn.

> Additionally, isn't scfi-simple-1.s effectively testing the same? The
> code at least looks extremely similar.
> 

Yes, one can removed. The two tests achieve the same end result.

>> --- /dev/null
>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.l
>> @@ -0,0 +1,2 @@
>> +Assembler messages:
>> +Fatal error: Synthesizing CFI is not supported for this ABI
>> diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.s
>> new file mode 100644
>> index 00000000000..87d2a4971a1
>> --- /dev/null
>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.s
>> @@ -0,0 +1,10 @@
>> +# Testcase run with --m32 (Not supported).
>> +	.text
>> +	.globl	foo
>> +	.type	foo, @function
>> +foo:
>> +	pushq	%rbp
> 
> This code, being built with --32, is bogus anyway. I wonder why this file
> has any insns in the first place.
> 

I will update this.

>> --- /dev/null
>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-x86-64.exp
>> @@ -0,0 +1,103 @@
>> +# Copyright (C) 2022-2023 Free Software Foundation, Inc.
>> +
>> +# This program is free software; you can redistribute it and/or modify
>> +# it under the terms of the GNU General Public License as published by
>> +# the Free Software Foundation; either version 3 of the License, or
>> +# (at your option) any later version.
>> +#
>> +# This program is distributed in the hope that it will be useful,
>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +# GNU General Public License for more details.
>> +#
>> +# You should have received a copy of the GNU General Public License
>> +# along with this program; if not, write to the Free Software
>> +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
>> +
>> +if { ![is_elf_format] } then {
>> +    return
>> +}
>> +
>> +# common tests
>> +if  { ([istarget "x86_64-*-*"]) } then {
>> +
>> +    global ASFLAGS
>> +    set old_ASFLAGS "$ASFLAGS"
>> +
>> +    run_dump_test "scfi-cfi-label-1"
>> +    run_list_test "scfi-cfi-label-1" "--scfi --warn"
>> +
>> +    run_list_test "scfi-diag-1" "--scfi"
>> +    run_list_test "scfi-fp-diag-2" "--scfi"
>> +    run_list_test "scfi-diag-2" "--scfi"
>> +
>> +    run_list_test "scfi-unsupported-1" "--32 --scfi"
> 
> Perhaps a 2nd run with --x32, unless (see above) you choose to support
> that ABI as well?
> 

At the moment, the plan was to next work on --scfi=inline and add 
support for aarch64/aapcs64 ABIs.  No plans to add support for x32 
unless a need arises.

Thanks
Indu


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

* Re: [PATCH, V2 07/10] gas: synthesize CFI for hand-written asm
  2023-10-31 14:10   ` Jan Beulich
@ 2023-11-02  8:15     ` Indu Bhagat
  2023-11-02 11:39       ` Jan Beulich
  0 siblings, 1 reply; 30+ messages in thread
From: Indu Bhagat @ 2023-11-02  8:15 UTC (permalink / raw)
  To: Jan Beulich; +Cc: binutils

Hi Jan,

Thanks for reviewing.

On 10/31/23 07:10, Jan Beulich wrote:
> On 30.10.2023 17:51, Indu Bhagat wrote:
>> gas/
>> 	* Makefile.am: Add new files.
>> 	* Makefile.in: Regenerated.
>> 	* as.c (defined): Handle documentation and listing option for
>> 	ginsns and SCFI.
>> 	* config/obj-elf.c (obj_elf_size): Invoke ginsn_data_end.
>> 	(obj_elf_type): Invoke ginsn_data_begin.
>> 	* config/tc-i386.c (ginsn_new): New functionality to generate
>> 	ginsns.
>> 	(x86_scfi_callee_saved_p): New function.
>> 	(ginsn_dw2_regnum): Likewise.
>> 	(ginsn_set_where): Likewise.
>> 	(x86_ginsn_alu): Likewise.
>> 	(x86_ginsn_move): Likewise.
>> 	(x86_ginsn_lea): Likewise.
>> 	(x86_ginsn_jump): Likewise.
>> 	(x86_ginsn_jump_cond): Likewise.
>> 	(md_assemble): Invoke ginsn_new.
>> 	(s_insn): Likewise.
>> 	(i386_target_format): Add hard error for usage of --scfi with non AMD64 ABIs.
>> 	* config/tc-i386.h (TARGET_USE_GINSN): New definition.
>> 	(TARGET_USE_SCFI): Likewise.
>> 	(SCFI_NUM_REGS): Likewise.
>> 	(REG_FP): Likewise.
>> 	(REG_SP): Likewise.
>> 	(SCFI_INIT_CFA_OFFSET): Likewise.
>> 	(SCFI_CALLEE_SAVED_REG_P): Likewise.
>> 	(x86_scfi_callee_saved_p): Likewise.
> 
> For this arch-specific code there's a fundamental question of maintenance
> cost here: It doesn't look very reasonable to me to demand of people adding
> support for new ISA extensions to also take into consideration all of this
> new machinery. Yet if any such addition affects SCFI, things will go out-
> of-sync without updating this code as well. It may not be very often that
> such updating is necessary, but right now there's APX work in progress
> which I expect will need dealing with here as well.
> 

I understand your concerns.  FWIW, for SCFI, we will need to translate 
only a subset of instructions into ginsns (change of flow insns, 
save/restore and arith on REG_SP/REG_FP).  For APX, I see that there is 
are new instructions that fall in this set unfortunately.  Hopefully the 
set remains small. But in general, for future additions (and for APX 
currently), there will be time to act as SCFI is for hand-written asm 
for which synthesizing CFI is desired, not for all code that GAS 
necessarily has to deal with.

At the moment, our target is to see if the kernel can use SCFI for a 
subset of their hand-written asm for x86_64 and aarch64.

>> --- a/gas/as.c
>> +++ b/gas/as.c
>> @@ -45,6 +45,7 @@
>>   #include "codeview.h"
>>   #include "bfdver.h"
>>   #include "write.h"
>> +#include "ginsn.h"
>>   
>>   #ifdef HAVE_ITBL_CPU
>>   #include "itbl-ops.h"
>> @@ -245,6 +246,7 @@ Options:\n\
>>                         	  d      omit debugging directives\n\
>>                         	  g      include general info\n\
>>                         	  h      include high-level source\n\
>> +			  i      include ginsn and synthesized CFI info\n\
>>                         	  l      include assembly\n\
>>                         	  m      include macro expansions\n\
>>                         	  n      omit forms processing\n\
> 
> Please can you make indentation match neighboring code?
> 

OK.

>> --- a/gas/config/obj-elf.c
>> +++ b/gas/config/obj-elf.c
>> @@ -24,6 +24,7 @@
>>   #include "subsegs.h"
>>   #include "obstack.h"
>>   #include "dwarf2dbg.h"
>> +#include "ginsn.h"
>>   
>>   #ifndef ECOFF_DEBUGGING
>>   #define ECOFF_DEBUGGING 0
>> @@ -2302,6 +2303,13 @@ obj_elf_size (int ignore ATTRIBUTE_UNUSED)
>>         symbol_get_obj (sym)->size = XNEW (expressionS);
>>         *symbol_get_obj (sym)->size = exp;
>>       }
>> +
>> +  /* If the symbol in the directive matches the current function being
>> +     processed, indicate end of the current stream of ginsns.  */
>> +  if (flag_synth_cfi
>> +      && S_IS_FUNCTION (sym) && sym == ginsn_data_func_symbol ())
>> +    ginsn_data_end (symbol_temp_new_now ());
>> +
>>     demand_empty_rest_of_line ();
>>   }
> 
> Besides the restrictions mentioned, presence of this just in obj-elf.c
> means non-ELF targets also won't be able to use this. You'll therefore
> want to adjust the config/tc-i386.h such that e.g. COFF targets don't
> needlessly have a large set of dead code compiled in.
> 

OK.

>> --- 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 "scfi.h"
>>   #include "gen-sframe.h"
>>   #include "sframe.h"
>>   #include "elf/x86-64.h"
>> @@ -193,8 +194,11 @@ static unsigned int x86_isa_1_used;
>>   static unsigned int x86_feature_2_used;
>>   /* Generate x86 used ISA and feature properties.  */
>>   static unsigned int x86_used_note = DEFAULT_X86_USED_NOTE;
>> +
>>   #endif
> 
> Nit: Stray change?
> 
>> +static ginsnS *ginsn_new (symbolS *sym, enum ginsn_gen_mode gmode);
> 
> I don't see the need for this: The function definition lives ahead of any
> caller afaics.
> 

OK to both.

>> @@ -5116,6 +5120,716 @@ static INLINE bool may_need_pass2 (const insn_template *t)
>>   	       && t->base_opcode == 0x63);
>>   }
>>   
>> +bool
>> +x86_scfi_callee_saved_p (uint32_t dw2reg_num)
>> +{
>> +  if (dw2reg_num == 3 /* rbx.  */
>> +      || dw2reg_num == REG_FP /* rbp.  */
>> +      || dw2reg_num == REG_SP /* rsp.  */
>> +      || (dw2reg_num >= 12 && dw2reg_num <= 15) /* r12 - r15.  */)
>> +    return true;
> 
> Non-GPRs aren't of interest here?
> 
> What about the Windows ABI, which is also used in the EFI world?
> 

Windows ABI can be worked out later, if there is interest.  The current 
set of abstractions used in SCFI machinery are being worked out while 
ensuring that they work for at least two ABIs at the minimum - AMD64 and 
AAPCS64. So adding support for another ABI, hopefully gets some help.

>> +  return false;
>> +}
>> +
>> +static uint32_t
>> +ginsn_dw2_regnum (const reg_entry *ireg)
>> +{
>> +  /* PS: Note the data type here as int32_t, because of Dw2Inval (-1).  */
>> +  int32_t dwarf_reg = Dw2Inval;
>> +  const reg_entry *temp;
>> +
>> +  if (ireg->dw2_regnum[0] == Dw2Inval && ireg->dw2_regnum[1] == Dw2Inval)
>> +    return dwarf_reg;
>> +
>> +  dwarf_reg = ireg->dw2_regnum[flag_code >> 1];
> 
> flag_code == CODE_64BIT would likely be more robust.
> 
> Also are all calls here synchronous, i.e. happening right when a
> particular insn is processed? Otherwise what about flag_code changing
> in between?
> 

Looks like I havent factored that in. I think we will need to error out 
for now if flag_code changes in between.  I will need to take a look at 
the instructions and ABI differences to see what changes are necessary 
to support this.

> Plus in how far does it make sense to handle other than CODE_64BIT when
> you tie yourself to the SysV 64-bit ABI?
> 
>> +  if (dwarf_reg == Dw2Inval)
>> +    {
>> +      temp = ireg + 16;
>> +      dwarf_reg = ginsn_dw2_regnum (temp);
>> +    }
> 
> What is this about? This clearly needs a comment, and it may also need
> a comment in opcodes/i386-reg.tbl to clarify that certain ordering
> requirements have to be retained. It also wants to be clarified that
> the recursion here won't be indefinite.
> 

This code is indeed relying on the ordering of the the entries in 
i386_regtab [].  Its not a good way to achieve what it is doing, but I 
found no other way at that time. I will add code comments.

>> +  if (dwarf_reg == Dw2Inval)
>> +    gas_assert (1); /* Needs to be addressed.  */
> 
> This is dead code (you appear to mean 0 instead of 1). Yet even then
> still gas_assert (dwarf_reg != Dw2Inval) please.
> 

Will fix.

>> +  return (uint32_t) dwarf_reg;
>> +}
>> +
>> +static void
>> +ginsn_set_where (ginsnS* ginsn)
> 
> Nit: Misplaced *.
> 

OK.

>> +{
>> +  const char *file;
>> +  unsigned int line;
>> +  file = as_where (&line);
>> +  ginsn_set_file_line (ginsn, file, line);
>> +}
> 
> This function, also from its name, isn't x86-specific, is it? As such
> it wants to live in ginsn.[ch].
> 

OK.

>> +static ginsnS *
>> +x86_ginsn_alu (i386_insn insn, symbolS *insn_end_sym)
>> +{
>> +  offsetT src_imm;
>> +  uint32_t dw2_regnum;
>> +  enum ginsn_src_type src_type;
>> +  enum ginsn_dst_type dst_type;
>> +  ginsnS *ginsn = NULL;
>> +
>> +  /* FIXME - create ginsn for REG_SP target only ? */
>> +  /* Map for insn.tm.extension_opcode
>> +     000 ADD    100 AND
>> +     001 OR     101 SUB
>> +     010 ADC    110 XOR
>> +     011 SBB    111 CMP  */
>> +
>> +  /* add/sub imm, %reg.
>> +     and imm, %reg only at this time for SCFI. */
>> +  if (!(insn.tm.extension_opcode == 0
>> +	|| insn.tm.extension_opcode == 4
>> +	|| insn.tm.extension_opcode == 5))
>> +    return ginsn;
> 
> Why is AND permitted, but OR/XOR aren't?
> 
> Also this is about ALU insns with immediate operands only, yet that
> fact isn't reflected in the function name.
> 

OR/XOR should be handled. I will fix this.

This function is handling opcodes with imm only, true.  ADD/SUB with reg 
are handled elsewhere (with opcode == 0x1 and opcode == 0x29).

>> +  /* TBD_GINSN_REPRESENTATION_LIMIT: There is no representation for when a
>> +     symbol is used as an operand, like so:
>> +	  addq    $simd_cmp_op+8, %rdx
>> +     Skip generating any ginsn for this.  */
>> +  if (insn.imm_operands == 1
>> +      && insn.op[0].imms->X_op == O_symbol)
>> +    return ginsn;
>> +
>> +  gas_assert (insn.imm_operands == 1
>> +	      && insn.op[0].imms->X_op == O_constant);
> 
> Perhaps less fragile if you use != O_constant in the preceding if()?
> The remaining half could the move ahead of that if(), allowing half
> of its condition to also be dropped.
> 
>> +  src_imm = insn.op[0].imms->X_add_number;
>> +  /* The second operand may be a register or indirect access.  */
>> +  if (insn.mem_operands == 1 && insn.base_reg)
>> +    {
>> +      dw2_regnum = ginsn_dw2_regnum (insn.base_reg);
>> +      src_type = GINSN_SRC_INDIRECT;
>> +      dst_type = GINSN_DST_INDIRECT;
> 
> The possibly in use index register isn't of interest in this case?
> Nor the displacement in the memory operand, ...
> 

Not of interest, No.  When src_type and dst_type is INDIRECT.

>> +    }
>> +  else if (insn.mem_operands == 1 && insn.index_reg)
>> +    {
>> +      dw2_regnum = ginsn_dw2_regnum (insn.index_reg);
>> +      src_type = GINSN_SRC_INDIRECT;
>> +      dst_type = GINSN_DST_INDIRECT;
> 
> ... similarly applicable here? Nor a segment override?

Segment override probably is not of interest for SCFI.

> 
>> +    }
>> +  else
>> +    {
>> +      gas_assert (insn.reg_operands == 1);
> 
> Afaict this will trigger when the memory operand has neither base
> nor index.
> 

memory operand with neither base nor index ?  An asm instruction example 
will be helpful.

>> +      dw2_regnum = ginsn_dw2_regnum (insn.op[1].regs);
>> +      src_type = GINSN_SRC_REG;
>> +      dst_type = GINSN_DST_REG;
>> +    }
>> +
>> +  /* For ginsn, keep the imm as second src operand.  */
>> +  if (insn.tm.extension_opcode == 5)
>> +    ginsn = ginsn_new_sub (insn_end_sym, true,
>> +			   src_type, dw2_regnum, 0,
>> +			   GINSN_SRC_IMM, 0, src_imm,
>> +			   dst_type, dw2_regnum, 0);
>> +  else if (insn.tm.extension_opcode == 4)
>> +    ginsn = ginsn_new_and (insn_end_sym, true,
>> +			   src_type, dw2_regnum, 0,
>> +			   GINSN_SRC_IMM, 0, src_imm,
>> +			   dst_type, dw2_regnum, 0);
>> +  else if (insn.tm.extension_opcode == 0)
>> +    ginsn = ginsn_new_add (insn_end_sym, true,
>> +			   src_type, dw2_regnum, 0,
>> +			   GINSN_SRC_IMM, 0, src_imm,
>> +			   dst_type, dw2_regnum, 0);
> 
> I think this would benefit from setting a function pointer near the
> top of the function. Else can this please at least be put in switch()
> form?
> 

In this case, function pointer will also work.

>> +  ginsn_set_where (ginsn);
>> +
>> +  return ginsn;
>> +}
>> +
>> +static ginsnS *
>> +x86_ginsn_move (i386_insn insn, symbolS *insn_end_sym)
>> +{
>> +  ginsnS *ginsn;
>> +  uint16_t opcode;
>> +  uint32_t dst_reg;
>> +  uint32_t src_reg;
>> +  offsetT dst_disp;
>> +  offsetT src_disp;
>> +  const reg_entry *dst = NULL;
>> +  const reg_entry *src = NULL;
>> +  enum ginsn_dst_type dst_type;
>> +  enum ginsn_src_type src_type;
>> +
>> +  opcode = insn.tm.base_opcode;
>> +  src_type = GINSN_SRC_REG;
>> +  src_disp = dst_disp = 0;
>> +  dst_type = GINSN_DST_REG;
> 
> Please can such be initializers of their variables?
> 

OK.

>> +  if (opcode == 0x8b)
>> +    {
>> +      /* mov  disp(%reg), %reg.  */
>> +      if (insn.mem_operands && insn.base_reg)
>> +	{
>> +	  src = insn.base_reg;
>> +	  if (insn.disp_operands == 1)
>> +	    src_disp = insn.op[0].disps->X_add_number;
>> +	  src_type = GINSN_SRC_INDIRECT;
>> +	}
>> +      else
>> +	src = insn.op[0].regs;
> 
> What about disp(%reg,%reg) or disp(,%reg) kinds of memory operands?
> 

I will take a look.

>> +      dst = insn.op[1].regs;
>> +    }
>> +  else if (opcode == 0x89 || opcode == 0x88)
> 
> How come 0x88 is handled here, but 0x8a isn't handled above?
> 

It should have been. I will take a look.

>> +    {
>> +      /* mov %reg, disp(%reg).  */
>> +      src = insn.op[0].regs;
>> +      if (insn.mem_operands && insn.base_reg)
>> +	{
>> +	  dst = insn.base_reg;
>> +	  if (insn.disp_operands == 1)
>> +	    dst_disp = insn.op[1].disps->X_add_number;
>> +	  dst_type = GINSN_DST_INDIRECT;
>> +	}
>> +      else
>> +	dst = insn.op[1].regs;
>> +    }
>> +
>> +  src_reg = ginsn_dw2_regnum (src);
>> +  dst_reg = ginsn_dw2_regnum (dst);
>> +
>> +  ginsn = ginsn_new_mov (insn_end_sym, true,
>> +			 src_type, src_reg, src_disp,
>> +			 dst_type, dst_reg, dst_disp);
>> +  ginsn_set_where (ginsn);
>> +
>> +  return ginsn;
>> +}
>> +
>> +static ginsnS *
>> +x86_ginsn_lea (i386_insn insn, symbolS *insn_end_sym)
>> +{
>> +  offsetT src_disp = 0;
>> +  ginsnS *ginsn = NULL;
>> +  uint32_t base_reg;
>> +  uint32_t index_reg;
>> +  offsetT index_scale;
>> +  uint32_t dst_reg;
>> +
>> +  if (!insn.index_reg && !insn.base_reg)
>> +    {
>> +      /* lea symbol, %rN.  */
>> +      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
>> +      /* FIXME - Skip encoding information about the symbol.
>> +	 This is TBD_GINSN_INFO_LOSS, but it is fine if the mode is
>> +	 GINSN_GEN_SCFI.  */
>> +      ginsn = ginsn_new_mov (insn_end_sym, false,
>> +			     GINSN_SRC_IMM, 0xf /* arbitrary const.  */, 0,
>> +			     GINSN_DST_REG, dst_reg, 0);
>> +    }
>> +  else if (insn.base_reg && !insn.index_reg)
>> +    {
>> +      /* lea    -0x2(%base),%dst.  */
>> +      base_reg = ginsn_dw2_regnum (insn.base_reg);
>> +      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
>> +
>> +      if (insn.disp_operands)
>> +	src_disp = insn.op[0].disps->X_add_number;
> 
> What if the displacement expression is other than O_constant?
> 

For SCFI, a "complex" lea instruction will imply some untraceable change 
to the destination reg.  For SCFI, the extent of untraceable change is 
not of interest, hence, in general, some information loss in ginsn is 
tolerable.

I will have to take a look at such instructions to see what the options 
are.  If you have a example handy, it will be helpful.

>> +      if (src_disp)
>> +	/* Generate an ADD ginsn.  */
>> +	ginsn = ginsn_new_add (insn_end_sym, true,
>> +			       GINSN_SRC_REG, base_reg, 0,
>> +			       GINSN_SRC_IMM, 0, src_disp,
>> +			       GINSN_DST_REG, dst_reg, 0);
>> +      else
>> +	  /* Generate a MOV ginsn.  */
>> +	  ginsn = ginsn_new_mov (insn_end_sym, true,
>> +				 GINSN_SRC_REG, base_reg, 0,
>> +				 GINSN_DST_REG, dst_reg, 0);
>> +    }
>> +  else if (!insn.base_reg && insn.index_reg)
>> +    {
>> +      /* lea (,%index,imm), %dst.  */
>> +      /* FIXME - Skip encoding an explicit multiply operation, instead use
>> +	 GINSN_TYPE_OTHER.  This is TBD_GINSN_INFO_LOSS, but it is fine if
>> +	 the mode is GINSN_GEN_SCFI.  */
>> +      index_scale = insn.log2_scale_factor;
>> +      index_reg = ginsn_dw2_regnum (insn.index_reg);
>> +      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
>> +      ginsn = ginsn_new_other (insn_end_sym, true,
>> +			       GINSN_SRC_REG, index_reg,
>> +			       GINSN_SRC_IMM, index_scale,
>> +			       GINSN_DST_REG, dst_reg);
>> +    }
>> +  else
>> +    {
>> +      /* lea disp(%base,%index,imm) %dst.  */
>> +      /* FIXME - Skip encoding information about the disp and imm for index
>> +	 reg.  This is TBD_GINSN_INFO_LOSS, but it is fine if the mode is
>> +	 GINSN_GEN_SCFI.  */
>> +      base_reg = ginsn_dw2_regnum (insn.base_reg);
>> +      index_reg = ginsn_dw2_regnum (insn.index_reg);
>> +      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
>> +      /* Generate an ADD ginsn.  */
>> +      ginsn = ginsn_new_add (insn_end_sym, true,
>> +			     GINSN_SRC_REG, base_reg, 0,
>> +			     GINSN_SRC_REG, index_reg, 0,
>> +			     GINSN_DST_REG, dst_reg, 0);
> 
> The comment mentions the displacement, but it's not passed on? In the earlier
> "else if" block you also pay attention to the scla factor, but here you don't?
> That said, I think I'm confused anyway about the FIXME comments.
> 

Ah, This is what I was hinting at in the previous comment.

So whats going on here is the following -

First, An "add reg1, reg2, reg3" (reg3 being the destination) ginsn is 
only interesting for SCFI if the reg3 is either REG_FP or REG_SP. We 
still generate them all nevertheless for now.

Second, We say that "add reg1, reg2, reg3" makes reg3 "untraceable". 
This is based on the observation that static stack usage in a function 
will use imm based arithmetic insns. So, if reg3 is REG_SP, REG_FP, 
these instructions make REG_SP / REG_FP "untraceable".

Note that, We are not allowing all registers for CFA tracking. (Now 
_this_ is a constraint, yes. And which is why we dont support DRAP usage 
currently. But its a specific pattern that we can accommodate in future.)

Hence, tracking more information for such (untraceable) instructions is 
unnecessary for SCFI purposes.  If and when GAS uses ginsn (in the i386 
backend) for other purposes, such information loss will need to be 
revisited.

>> +    }
>> +
>> +  ginsn_set_where (ginsn);
>> +
>> +  return ginsn;
>> +}
>> +
>> +static ginsnS *
>> +x86_ginsn_jump (i386_insn insn, symbolS *insn_end_sym)
>> +{
>> +  ginsnS *ginsn = NULL;
>> +  symbolS *src_symbol;
>> +
>> +  gas_assert (insn.disp_operands == 1);
>> +
>> +  if (insn.op[0].disps->X_op == O_symbol)
>> +    {
>> +      src_symbol = insn.op[0].disps->X_add_symbol;
>> +      /* The jump target is expected to be a symbol with 0 addend.
>> +	 Assert for now to see if this assumption is true.  */
>> +      gas_assert (insn.op[0].disps->X_add_number == 0);
> 
> If you assert this means elsewhere this is being checked and a call
> here avoided in case the value is non-zero. I can't spot such a
> check, though.
> 

No, this assert was for me to take action if this assumption is not 
true.  Just to make sure I see what needs to be done if there are other 
encodings to take care of.  Have not hit this yet in my tests.

>> +      ginsn = ginsn_new_jump (insn_end_sym, true,
>> +			      GINSN_SRC_SYMBOL, 0, src_symbol);
>> +
>> +      ginsn_set_where (ginsn);
>> +    }
>> +
>> +  return ginsn;
>> +}
> 
> Further, what about XABORT transferring control to what XBEGIN has
> supplied? (XBEGIN can, in a sense, also be considered a [conditional]
> branch.)
> 

My plan after writing this x86 -> ginsn translator was:
There are and will remain many machine instructions that may need to be 
supported depending on hand-written assmebly programmers use.  This is 
the reason I had left some asserts towards the end in the ginsn_new () 
API: if any instructions which affect the control flow, or where the 
destination is REG_FP / REG_SP, or push* / pop*  are not handled, we 
will have an assert fail and GAS will need to handle it.

>> +static ginsnS *
>> +x86_ginsn_jump_cond (i386_insn insn, symbolS *insn_end_sym)
>> +{
>> +  ginsnS *ginsn = NULL;
>> +  symbolS *src_symbol;
>> +
>> +  /* TBD_GINSN_GEN_NOT_SCFI: Ignore move to or from xmm reg for mode.  */
>> +  if (i.tm.opcode_space == SPACE_0F)
>> +    return ginsn;
> 
> What is the comment about? And what about SPACE_0F-encoded conditional
> jumps (Jcc <disp32>)? And where are LOOP and J{E,R}CXZ handled?
> 

These need to be handled.  I will take a look.

>> +  gas_assert (insn.disp_operands == 1);
>> +
>> +  if (insn.op[0].disps->X_op == O_symbol)
>> +    {
>> +      src_symbol = insn.op[0].disps->X_add_symbol;
>> +      /* The jump target is expected to be a symbol with 0 addend.
>> +	 Assert for now to see if this assumption is true.  */
>> +      gas_assert (insn.op[0].disps->X_add_number == 0);
>> +      ginsn = ginsn_new_jump_cond (insn_end_sym, true,
>> +				   GINSN_SRC_SYMBOL, 0, src_symbol);
>> +      ginsn_set_where (ginsn);
>> +    }
>> +  else
>> +    /* Catch them for now so we know what we are dealing with.  */
>> +    gas_assert (0);
> 
> I'm going from the assumption that this (and alike) will be addressed
> before this series is committed?
> 

I will work on the ones you have raised above in the review. My plan was 
to exercise this code in different ways after commmitting and address 
the failures as I run into them.

>> +  return ginsn;
>> +}
>> +
>> +/* Generate one or more GAS instructions for the current machine dependent
>> +   instruction.
>> +
>> +   Returns the head of linked list of ginsn(s) added, if success;
>> +   Returns NULL if failure.  */
>> +
>> +static ginsnS *
>> +ginsn_new (symbolS *insn_end_sym, enum ginsn_gen_mode gmode)
> 
> x86_ginsn_new()?
> 

Yes, this looks better.

>> +{
>> +  uint16_t opcode;
>> +  uint32_t dw2_regnum;
>> +  uint32_t src2_dw2_regnum;
>> +  int32_t gdisp = 0;
>> +  ginsnS *ginsn = NULL;
>> +  ginsnS *ginsn_next = NULL;
>> +  ginsnS *ginsn_last = NULL;
>> +
>> +  /* FIXME - Need a way to check whether the decoding is sane.  The specific
>> +     checks around i.tm.opcode_space were added as issues were seen.  Likely
>> +     insufficient.  */
> 
> Furthermore opcode encoding space (SPACE_...) need to be taken into
> account in all cases.
> 
>> +  /* Currently supports generation of selected ginsns, sufficient for
>> +     the use-case of SCFI only.  To remove this condition will require
>> +     work on this target-specific process of creation of ginsns.  Some
>> +     of such places are tagged with TBD_GINSN_GEN_NOT_SCFI to serve as
>> +     examples.  */
>> +  if (gmode != GINSN_GEN_SCFI)
>> +    return ginsn;
>> +
>> +  opcode = i.tm.base_opcode;
>> +
>> +  switch (opcode)
>> +    {
>> +    case 0x1:
>> +      /* add reg, reg.  */
>> +      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
> 
> You don't care about opcode 0 (byte operation). Then what about 16-bit
> operand size? Or, since we're talking of a 64-bit-ABI-only feature,
> even 32-bit operand size?
> 

Operand size in some cases does not affect SCFI. So we dont keep 
information about operand sizes.  The case that I should handle operand 
size is when they are pushed to / popped from stack.

> Also what about opcode 0x3?
> 

LSL instruction ? It doesnt look like it can affect DWARF CFI of a 
function. Please correct me if this is wrong.

>> +      if (i.reg_operands == 2)
>> +	{
>> +	  src2_dw2_regnum = ginsn_dw2_regnum (i.op[1].regs);
>> +	  ginsn = ginsn_new_add (insn_end_sym, true,
>> +				 GINSN_SRC_REG, dw2_regnum, 0,
>> +				 GINSN_SRC_REG, src2_dw2_regnum, 0,
>> +				 GINSN_DST_REG, src2_dw2_regnum, 0);
>> +	  ginsn_set_where (ginsn);
>> +	}
>> +      else if (i.mem_operands && i.base_reg)
>> +	{
>> +	  src2_dw2_regnum = ginsn_dw2_regnum (i.base_reg);
>> +	  if (i.disp_operands == 1)
>> +	    gdisp = i.op[1].disps->X_add_number;
>> +
>> +	  ginsn = ginsn_new_add (insn_end_sym, true,
>> +				 GINSN_SRC_REG, dw2_regnum, 0,
>> +				 GINSN_SRC_INDIRECT, src2_dw2_regnum, gdisp,
>> +				 GINSN_DST_INDIRECT, src2_dw2_regnum, gdisp);
>> +	  ginsn_set_where (ginsn);
>> +	}
>> +      else
>> +	/* Catch them for now so we know what we are dealing with.  */
>> +	gas_assert (0);
>> +
>> +      break;
>> +    case 0x29:
>> +      /* If opcode_space == SPACE_0F, this is a movaps insn.  Skip it
>> +	 for GINSN_GEN_SCFI.  */
>> +      if (i.tm.opcode_space == SPACE_0F)
>> +	break;
> 
> Extending on the earlier related comment: Why would you exclude just SPACE_0F?
> 

Perhaps here I should check that space == SPACE_BASE and only allow 
that? And also for others.

>> +      /* sub reg, reg.  */
>> +      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
>> +
>> +      if (i.reg_operands == 2)
>> +	{
>> +	  src2_dw2_regnum = ginsn_dw2_regnum (i.op[1].regs);
>> +	  ginsn = ginsn_new_sub (insn_end_sym, true,
>> +				 GINSN_SRC_REG, dw2_regnum, 0,
>> +				 GINSN_SRC_REG, src2_dw2_regnum, 0,
>> +				 GINSN_DST_REG, src2_dw2_regnum, 0);
>> +	  ginsn_set_where (ginsn);
>> +	}
>> +      else if (i.mem_operands && i.base_reg)
>> +	{
>> +	  src2_dw2_regnum = ginsn_dw2_regnum (i.base_reg);
>> +	  if (i.disp_operands == 1)
>> +	    gdisp = i.op[1].disps->X_add_number;
>> +
>> +	  ginsn = ginsn_new_sub (insn_end_sym, true,
>> +				 GINSN_SRC_REG, dw2_regnum, 0,
>> +				 GINSN_SRC_INDIRECT, src2_dw2_regnum, gdisp,
>> +				 GINSN_DST_INDIRECT, src2_dw2_regnum, gdisp);
>> +	  ginsn_set_where (ginsn);
>> +	}
>> +      else
>> +	/* Catch them for now so we know what we are dealing with.  */
>> +	gas_assert (0);
>> +
>> +      break;
>> +    case 0xa0:
>> +    case 0xa8:
> 
> Since individual case blocks are non-trivial, can there please be blank
> lines between any two non-fall-through case blocks?
> 

OK.

>> +      /* If opcode_space != SPACE_0F, this is a test insn.  Skip it
>> +	 for GINSN_GEN_SCFI.  */
>> +      if (i.tm.opcode_space != SPACE_0F)
>> +	break;
> 
> While here you properly use !=, now the comment is wrong.
> 
>> +      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
>> +      /* push fs / push gs.  */
>> +      ginsn = ginsn_new_sub (insn_end_sym, false,
>> +			     GINSN_SRC_REG, REG_SP, 0,
>> +			     GINSN_SRC_IMM, 0, 8,
> 
> Note how the 8 here assumes flag_code == CODE_64BIT. (You further assume
> no operand size override here.)
> 

Hmm. Yes, I do assume no operand size override here. I will need to 
understand how to calculate the operand size here. Looks like I need to 
check for instruction prefixes 66H or REX.W.  Is there an API in the 
backend that be used for this ?

>> +			     GINSN_DST_REG, REG_SP, 0);
>> +      ginsn_set_where (ginsn);
>> +
>> +      ginsn_next = ginsn_new_store (insn_end_sym, false,
>> +				    GINSN_SRC_REG, dw2_regnum,
>> +				    GINSN_DST_INDIRECT, REG_SP, 0);
>> +      ginsn_set_where (ginsn_next);
>> +
>> +      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
>> +      break;
>> +    case 0xa1:
>> +    case 0xa9:
>> +      /* If opcode_space != SPACE_0F, this is test insn.  Skip it
>> +	 for GINSN_GEN_SCFI.  */
>> +      if (i.tm.opcode_space != SPACE_0F)
>> +	break;
>> +
>> +      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
>> +      /* pop fs / pop gs.  */
>> +      ginsn = ginsn_new_load (insn_end_sym, false,
>> +			      GINSN_SRC_INDIRECT, REG_SP, 0,
>> +			      GINSN_DST_REG, dw2_regnum);
>> +      ginsn_set_where (ginsn);
>> +
>> +      ginsn_next = ginsn_new_add (insn_end_sym, false,
>> +				  GINSN_SRC_REG, REG_SP, 0,
>> +				  GINSN_SRC_IMM, 0, 8,
>> +				  GINSN_DST_REG, REG_SP, 0);
>> +      ginsn_set_where (ginsn_next);
>> +
>> +      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
>> +      break;
>> +    case 0x50 ... 0x57:
>> +      /* push reg.  */
>> +      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
>> +      ginsn = ginsn_new_sub (insn_end_sym, false,
>> +			     GINSN_SRC_REG, REG_SP, 0,
>> +			     GINSN_SRC_IMM, 0, 8,
>> +			     GINSN_DST_REG, REG_SP, 0);
>> +      ginsn_set_where (ginsn);
>> +
>> +      ginsn_next = ginsn_new_store (insn_end_sym, false,
>> +				    GINSN_SRC_REG, dw2_regnum,
>> +				    GINSN_DST_INDIRECT, REG_SP, 0);
>> +      ginsn_set_where (ginsn_next);
>> +
>> +      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
>> +      break;
>> +    case 0x58 ... 0x5f:
>> +      if (i.tm.opcode_space != SPACE_BASE)
>> +	break;
>> +      /* pop reg.  */
>> +      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
>> +      ginsn = ginsn_new_load (insn_end_sym, false,
>> +			      GINSN_SRC_INDIRECT, REG_SP, 0,
>> +			      GINSN_DST_REG, dw2_regnum);
>> +      ginsn_set_where (ginsn);
>> +
>> +      ginsn_next = ginsn_new_add (insn_end_sym, false,
>> +				  GINSN_SRC_REG, REG_SP, 0,
>> +				  GINSN_SRC_IMM, 0, 8,
>> +				  GINSN_DST_REG, REG_SP, 0);
>> +      ginsn_set_where (ginsn_next);
>> +
>> +      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
>> +      break;
>> +    case 0x68:
>> +    case 0x6a:
>> +      /* push imm. */
>> +      /* Skip getting the value of imm from machine instruction
>> +	 because for ginsn generation this is not important.  */
> 
> In (e.g.) kernel code model it is possible to do
> 
> 	push	<address>
> 	pop	%rbp
> 
> (could even be %rsp). How is such a frame (or stack) pointer change
> not relevant?
> 

For SCFI, we are tracking _where_ the saves and restore are done. The 
values are not important.

In the above example, push <address> must tell the SCFI machinery that 
stack pointer has been updated.  What value was pushed to stack is not 
important.  Now rbp is a callee-saved register, so one must have saved 
to stack before reovering it (speaking of ABI/calling convention 
compliant asm).  So in the sequence above, the SCFI machinery must 
complain that this looks fishy.

>> +      ginsn = ginsn_new_sub (insn_end_sym, false,
>> +			     GINSN_SRC_REG, REG_SP, 0,
>> +			     GINSN_SRC_IMM, 0, 8,
>> +			     GINSN_DST_REG, REG_SP, 0);
>> +      ginsn_set_where (ginsn);
>> +
>> +      ginsn_next = ginsn_new_store (insn_end_sym, false,
>> +				    GINSN_SRC_IMM, 0,
>> +				    GINSN_DST_INDIRECT, REG_SP, 0);
>> +      ginsn_set_where (ginsn_next);
>> +
>> +      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
>> +      break;
>> +    case 0x70 ... 0x7f:
>> +      ginsn = x86_ginsn_jump_cond (i, insn_end_sym);
>> +      break;
>> +    case 0x81:
>> +    case 0x83:
>> +      ginsn = x86_ginsn_alu (i, insn_end_sym);
>> +      break;
>> +    case 0x8b:
>> +      /* Move r/m64 to r64.  */
>> +    case 0x88:
>> +    case 0x89:
>> +      /* mov reg, reg/mem.  */
>> +      ginsn = x86_ginsn_move (i, insn_end_sym);
>> +      break;
>> +    case 0x8d:
>> +      /* lea disp(%src), %dst */
>> +      ginsn = x86_ginsn_lea (i, insn_end_sym);
>> +      break;
>> +    case 0x8f:
>> +      /* pop to mem.  */
>> +      gas_assert (i.base_reg);
> 
> This opcode can in principle also have a register operand. We
> _currently_ have no way for a user to request to use this
> alternative encoding, but that's still a latent issue. In any
> event, like elsewhere, all memory operand forms are possible
> here, not just (%reg).
> 
>> +      dw2_regnum = ginsn_dw2_regnum (i.base_reg);
>> +      ginsn = ginsn_new_load (insn_end_sym, false,
>> +			      GINSN_SRC_INDIRECT, REG_SP, 0,
>> +			      GINSN_DST_INDIRECT, dw2_regnum);
> 
> When both operands are "indirect", what's the difference between
> move, load, and store? IOW if the above is permitted, can't all
> three be folded into a single ginsn kind?
> 

In theory, any of mov/load/store ginsn type may be used if both operands 
are indirect mem access.

>> +      ginsn_set_where (ginsn);
>> +
>> +      ginsn_next = ginsn_new_add (insn_end_sym, false,
>> +				  GINSN_SRC_REG, REG_SP, 0,
>> +				  GINSN_SRC_IMM, 0, 8,
>> +				  GINSN_DST_REG, REG_SP, 0);
>> +      ginsn_set_where (ginsn_next);
>> +
>> +      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
>> +      break;
>> +    case 0x9c:
>> +      /* pushf / pushfd / pushfq.
>> +	 Tracking EFLAGS register by number is not necessary.  */
>> +      ginsn = ginsn_new_sub (insn_end_sym, false,
>> +			     GINSN_SRC_REG, REG_SP, 0,
>> +			     GINSN_SRC_IMM, 0, 8,
> 
> Why does the comment mention PUSHFD when you assume PUSHFQ here?
> 

Pushfd should not be stated here. I see pushfd is not valid for 64-bit 
op mode. Will fix it.

>> +			     GINSN_DST_REG, REG_SP, 0);
>> +      ginsn_set_where (ginsn);
>> +
>> +      ginsn_next = ginsn_new_store (insn_end_sym, false,
>> +				    GINSN_SRC_IMM, 0,
>> +				    GINSN_DST_INDIRECT, REG_SP, 0);
>> +      ginsn_set_where (ginsn_next);
>> +
>> +      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
>> +
>> +      break;
> 
> Where's POPFD?
> 

Missed it! popf / popfq opcode == 0x9d will be handled.

>> +    case 0xff:
>> +      /* push from mem.  */
>> +      if (i.tm.extension_opcode == 6)
>> +	{
>> +	  ginsn = ginsn_new_sub (insn_end_sym, false,
>> +				 GINSN_SRC_REG, REG_SP, 0,
>> +				 GINSN_SRC_IMM, 0, 8,
>> +				 GINSN_DST_REG, REG_SP, 0);
>> +	  ginsn_set_where (ginsn);
>> +
>> +	  /* These instructions have no imm, only indirect access.  */
>> +	  gas_assert (i.base_reg);
>> +	  dw2_regnum = ginsn_dw2_regnum (i.base_reg);
>> +	  ginsn_next = ginsn_new_store (insn_end_sym, false,
>> +					GINSN_SRC_INDIRECT, dw2_regnum,
>> +					GINSN_DST_INDIRECT, REG_SP, 0);
>> +	  ginsn_set_where (ginsn_next);
>> +
>> +	  gas_assert (!ginsn_link_next (ginsn, ginsn_next));
>> +	}
>> +      else if (i.tm.extension_opcode == 4)
>> +	{
>> +	  /* jmp r/m.  E.g., notrack jmp *%rax.  */
>> +	  if (i.reg_operands)
>> +	    {
>> +	      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
>> +	      ginsn = ginsn_new_jump (insn_end_sym, true,
>> +				      GINSN_SRC_REG, dw2_regnum, NULL);
>> +	      ginsn_set_where (ginsn);
>> +	    }
>> +	  else if (i.mem_operands && i.index_reg)
>> +	    {
>> +	      /* jmp    *0x0(,%rax,8).  */
>> +	      dw2_regnum = ginsn_dw2_regnum (i.index_reg);
>> +	      ginsn = ginsn_new_jump (insn_end_sym, true,
>> +				      GINSN_SRC_REG, dw2_regnum, NULL);
>> +	      ginsn_set_where (ginsn);
>> +	    }
>> +	  else if (i.mem_operands && i.base_reg)
>> +	    {
>> +	      dw2_regnum = ginsn_dw2_regnum (i.base_reg);
>> +	      ginsn = ginsn_new_jump (insn_end_sym, true,
>> +				      GINSN_SRC_REG, dw2_regnum, NULL);
>> +	      ginsn_set_where (ginsn);
>> +	    }
>> +	  else
>> +	    /* Catch them for now so we know what we are dealing with.  */
>> +	    gas_assert (0);
>> +	}
>> +      else if (i.tm.extension_opcode == 2)
>> +	{
>> +	  /* 0xFF /2 (call).  */
>> +	  if (i.reg_operands)
>> +	    {
>> +	      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
>> +	      ginsn = ginsn_new_call (insn_end_sym, true,
>> +				      GINSN_SRC_REG, dw2_regnum, NULL);
>> +	      ginsn_set_where (ginsn);
>> +	    }
>> +	  else if (i.mem_operands && i.base_reg)
>> +	    {
>> +	      dw2_regnum = ginsn_dw2_regnum (i.base_reg);
>> +	      ginsn = ginsn_new_call (insn_end_sym, true,
>> +				      GINSN_SRC_REG, dw2_regnum, NULL);
>> +	      ginsn_set_where (ginsn);
>> +	    }
>> +	  else
>> +	    /* Catch them for now so we know what we are dealing with.  */
>> +	    gas_assert (0);
>> +	}
>> +      else
>> +	/* Catch them for now so we know what we are dealing with.  */
>> +	gas_assert (0);
>> +      break;
>> +    case 0xc2:
>> +    case 0xc3:
>> +      /* Near ret.  */
>> +      ginsn = ginsn_new_return (insn_end_sym, true);
>> +      ginsn_set_where (ginsn);
>> +      break;
> 
> Where did the immediate operand of 0xC2 go? And what about far return
> (and more generally far branches)?
> 

A return ginsn for SCFI can afford this information loss.

>> +    case 0xc9:
>> +      /* The 'leave' instruction copies the contents of the RBP register
>> +	 into the RSP register to release all stack space allocated to the
>> +	 procedure.  */
>> +      ginsn = ginsn_new_mov (insn_end_sym, false,
>> +			     GINSN_SRC_REG, REG_FP, 0,
>> +			     GINSN_DST_REG, REG_SP, 0);
>> +      ginsn_set_where (ginsn);
>> +
>> +      /* Then it restores the old value of the RBP register from the stack.  */
>> +      ginsn_next = ginsn_new_load (insn_end_sym, false,
>> +				   GINSN_SRC_INDIRECT, REG_SP, 0,
>> +				   GINSN_DST_REG, REG_FP);
>> +      ginsn_set_where (ginsn_next);
>> +
>> +      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
>> +      ginsn_last = ginsn_new_add (insn_end_sym, false,
>> +				  GINSN_SRC_REG, REG_SP, 0,
>> +				  GINSN_SRC_IMM, 0, 8,
>> +				  GINSN_DST_REG, REG_SP, 0);
>> +      ginsn_set_where (ginsn_next);
>> +
>> +      gas_assert (!ginsn_link_next (ginsn_next, ginsn_last));
>> +      break;
> 
> You handle LEAVE, but not ENTER?
> 

I should have. From the three variants ([Enter imm16,0], [ENTER imm16,1] 
and [ENTER imm16, imm8], the last one is not super clear currently. Is 
it possible for you to pass down some understanding to me on that ?

>> +    case 0xe8:
>> +      /* PS: SCFI machinery does not care about which func is being
>> +	 called.  OK to skip that info.  */
>> +      ginsn = ginsn_new_call (insn_end_sym, true,
>> +			      GINSN_SRC_SYMBOL, 0, NULL);
>> +      ginsn_set_where (ginsn);
>> +      break;
>> +    case 0xe9:
>> +    case 0xeb:
>> +      /* If opcode_space == SPACE_0F, this is a psubw por insn.  Skip it
>> +	 for GINSN_GEN_SCFI.  */
>> +      if (i.tm.opcode_space == SPACE_0F)
>> +	break;
>> +
>> +      /* Unconditional jmp.  */
>> +      ginsn = x86_ginsn_jump (i, insn_end_sym);
>> +      ginsn_set_where (ginsn);
>> +      break;
>> +      /* Fall Through.  */
>> +    default:
> 
> There's noo fall-through here.
> 
>> +      /* TBD_GINSN_GEN_NOT_SCFI: Keep a warning, for now, to find out about
>> +	 possibly missed instructions affecting REG_SP or REG_FP.  These
>> +	 checks may not be completely exhaustive as they do not involve
>> +	 index / base reg.  */
> 
> Cases with early "break" should probably come here as well.
> 
OK.

>> +      if (i.op[0].regs)
>> +	{
>> +	  dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
> 
> This isn't valid without first having checked that operand 0 is a
> register operand: "regs" is a member of a union, and you may not
> pass expressionS * into this function.
> 
>> +	  if (dw2_regnum == REG_SP || dw2_regnum == REG_FP)
>> +	    as_warn_where (last_insn.file, last_insn.line,
>> +			   _("SCFI: unhandled op 0x%x may cause incorrect CFI"),
>> +			   i.tm.base_opcode);
>> +	}
>> +      if (i.op[1].regs)
>> +	{
>> +	  dw2_regnum = ginsn_dw2_regnum (i.op[1].regs);
>> +	  if (dw2_regnum == REG_SP || dw2_regnum == REG_FP)
>> +	    as_warn_where (last_insn.file, last_insn.line,
>> +			   _("SCFI: unhandled op 0x%x may cause incorrect CFI"),
>> +			   i.tm.base_opcode);
>> +	}
> 
> What about insns with 3 GPR operands?
> 
> Also, why do you use as_warn_where() here? You're still in the
> context of synchonously processing the current input line.
> 
>> +      /* Keep an eye on other instructions affecting control flow.  */
>> +      gas_assert (!i.tm.opcode_modifier.jump);
> 
> Again this cannot remain like this, even if here there's no FIXME
> remark.
> 

My thinking was to exercise the code more soon and fix the cases as I 
run into them. And do this in iterations after the patches go in.

>> +      /* TBD_GINSN_GEN_NOT_SCFI: Skip all other opcodes uninteresting for
>> +	 GINSN_GEN_SCFI mode.  */
> 
> "uninteresting" is an interesting term when dealing with hand-written
> assembly. How do you know what an assembly writer is going to do?
> While the patch description mentions a few constraints, having come
> here I'm under the impression that there are quite a few more of them.
> Maybe I simply didn't spot where all caveats are fully spelled out?
> 

I have tried to clear up some things in my comments above. Perhaps 
scfi.c will make things clearer.

>> @@ -5128,6 +5842,7 @@ md_assemble (char *line)
>>     const char *end, *pass1_mnem = NULL;
>>     enum i386_error pass1_err = 0;
>>     const insn_template *t;
>> +  ginsnS *ginsn;
>>   
>>     /* Initialize globals.  */
>>     current_templates = NULL;
>> @@ -5659,6 +6374,13 @@ md_assemble (char *line)
>>     /* We are ready to output the insn.  */
>>     output_insn ();
>>   
>> +  /* At this time, SCFI is enabled only for AMD64 ABI.  */
>> +  if (flag_synth_cfi && x86_elf_abi == X86_64_ABI)
>> +    {
>> +      ginsn = ginsn_new (symbol_temp_new_now (), frch_ginsn_gen_mode ());
>> +      frch_ginsn_data_append (ginsn);
>> +    }
> 
> Why would this not work for the x32 ABI?
> 

As per my understanding, all of this should work for X86_64_X32_ABI. Its 
just that I would like to double-check and run tests.

>> @@ -10904,6 +11626,7 @@ s_insn (int dummy ATTRIBUTE_UNUSED)
>>     valueT val;
>>     bool vex = false, xop = false, evex = false;
>>     static const templates tt = { &i.tm, &i.tm + 1 };
>> +  ginsnS *ginsn;
>>   
>>     init_globals ();
>>   
>> @@ -11658,7 +12381,14 @@ s_insn (int dummy ATTRIBUTE_UNUSED)
>>   
>>     output_insn ();
>>   
>> - done:
>> +  /* At this time, SCFI is enabled only for AMD64 ABI.  */
>> +  if (flag_synth_cfi && x86_elf_abi == X86_64_ABI)
>> +    {
>> +      ginsn = ginsn_new (symbol_temp_new_now (), frch_ginsn_gen_mode ());
>> +      frch_ginsn_data_append (ginsn);
>> +    }
>> +
>> +done:
> 
> Please can you leave label indentation alone?
> 

Oops, will fix.

>> @@ -15293,6 +16023,9 @@ i386_target_format (void)
>>     else
>>       as_fatal (_("unknown architecture"));
>>   
>> +  if (flag_synth_cfi && x86_elf_abi != X86_64_ABI)
>> +    as_fatal (_("Synthesizing CFI is not supported for this ABI"));
> 
> With this, what use are the x86_elf_abi checks in md_assemble() and
> s_insn()?
> 

Yes, there is redundancy; I could remove from md_assemble () and s_insn ().

>> --- a/gas/config/tc-i386.h
>> +++ b/gas/config/tc-i386.h
>> @@ -373,6 +373,27 @@ extern int i386_elf_section_type (const char *, size_t);
>>   extern void i386_solaris_fix_up_eh_frame (segT);
>>   #endif
>>   
>> +#define TARGET_USE_GINSN 1
>> +/* Allow GAS to synthesize DWARF CFI for hand-written asm.
>> +   PS: TARGET_USE_CFIPOP is a pre-condition.  */
>> +#define TARGET_USE_SCFI 1
>> +/* Identify the maximum DWARF register number of all the registers being
>> +   tracked for SCFI.  This is the last DWARF register number of the set
>> +   of SP, BP, and all callee-saved registers.  For AMD64, this means
>> +   R15 (15).  Use SCFI_CALLEE_SAVED_REG_P to identify which registers
>> +   are callee-saved from this set.  */
>> +#define SCFI_NUM_REGS 15
> 
> Is this "number of" (and then having a value of 16 to permit covering
> register 15), or is this "maximum register number" (and then needing a
> different name)?
> 

This is "maximum register number". I will rename this to SCFI_MAX_REG_ID.

>> --- /dev/null
>> +++ b/gas/ginsn.c
> 
> I'll see about replying on the non-x86 parts of this separately.
> 

Thanks again for reviewing.  The reviews have been very helpful.


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

* Re: [PATCH, V2 06/10] gas: scfidw2gen: new functionality to prepapre for SCFI
  2023-10-31 22:06     ` Indu Bhagat
@ 2023-11-02 10:35       ` Jan Beulich
  2023-11-02 16:32         ` Indu Bhagat
  0 siblings, 1 reply; 30+ messages in thread
From: Jan Beulich @ 2023-11-02 10:35 UTC (permalink / raw)
  To: Indu Bhagat; +Cc: binutils

On 31.10.2023 23:06, Indu Bhagat wrote:
> On 10/31/23 04:28, Jan Beulich wrote:
>> On 30.10.2023 17:51, Indu Bhagat wrote:
>>> +void
>>> +scfi_dot_cfi_startproc (symbolS *start_sym)
>>
>> This and the following two functions presently have no caller, and I
>> also can't spot equivalents in dw2gencfi.c. How are they (going) to be
>> used? This ...
>>
>>> +{
>>> +  if (frchain_now->frch_cfi_data != NULL)
>>> +    {
>>> +      as_bad (_("previous CFI entry not closed (missing .cfi_endproc)"));
>>
>> ... for example suggests that the function here might really be the
>> handler for .cfi_startproc, yet the table above says .cfi_startproc is
>> ignored.
>>
> 
> The callers of scfi_dot_cfi_startproc (), scfi_dot_cfi_endproc () and 
> scfi_dot_cfi () are in scfi.c, when its time to emit DWARF CFI after 
> SCFI machinery has generated the SCFI Ops (See scfi_dot_cfi () and 
> scfi_emit_dw2cfi ()).  The callers are added in the next patch in the 
> series, "[PATCH, V2 07/10] gas: synthesize CFI for hand-written asm".

But that's not in the context of processing a .cfi_* directive. IOW the
message is properly misleading.

>>> +#else
>>> +
>>> +static void
>>> +dot_scfi_dummy (int ignored ATTRIBUTE_UNUSED)
>>> +{
>>> +  as_bad (_("SCFI is not supported for this target"));
>>> +  ignore_rest_of_line ();
>>> +}
>>> +
>>> +const pseudo_typeS scfi_pseudo_table[] =
>>> +  {
>>> +    { "cfi_sections", dot_scfi_dummy, 0 },
>>> +    { "cfi_startproc", dot_scfi_dummy, 0 },
>>> +    { "cfi_endproc", dot_scfi_dummy, 0 },
>>> +    { "cfi_fde_data", dot_scfi_dummy, 0 },
>>> +    { "cfi_def_cfa", dot_scfi_dummy, 0 },
>>> +    { "cfi_def_cfa_register", dot_scfi_dummy, 0 },
>>> +    { "cfi_def_cfa_offset", dot_scfi_dummy, 0 },
>>> +    { "cfi_adjust_cfa_offset", dot_scfi_dummy, 0 },
>>> +    { "cfi_offset", dot_scfi_dummy, 0 },
>>> +    { "cfi_rel_offset", dot_scfi_dummy, 0 },
>>> +    { "cfi_register", dot_scfi_dummy, 0 },
>>> +    { "cfi_return_column", dot_scfi_dummy, 0 },
>>> +    { "cfi_restore", dot_scfi_dummy, 0 },
>>> +    { "cfi_undefined", dot_scfi_dummy, 0 },
>>> +    { "cfi_same_value", dot_scfi_dummy, 0 },
>>> +    { "cfi_remember_state", dot_scfi_dummy, 0 },
>>> +    { "cfi_restore_state", dot_scfi_dummy, 0 },
>>> +    { "cfi_window_save", dot_scfi_dummy, 0 },
>>> +    { "cfi_negate_ra_state", dot_scfi_dummy, 0 },
>>> +    { "cfi_escape", dot_scfi_dummy, 0 },
>>> +    { "cfi_signal_frame", dot_scfi_dummy, 0 },
>>> +    { "cfi_personality", dot_scfi_dummy, 0 },
>>> +    { "cfi_personality_id", dot_scfi_dummy, 0 },
>>> +    { "cfi_lsda", dot_scfi_dummy, 0 },
>>> +    { "cfi_val_encoded_addr", dot_scfi_dummy, 0 },
>>> +    { "cfi_inline_lsda", dot_scfi_dummy, 0 },
>>> +    { "cfi_label", dot_scfi_dummy, 0 },
>>> +    { "cfi_val_offset", dot_scfi_dummy, 0 },
>>> +    { NULL, NULL, 0 }
>>> +  };
>>> +
>>> +#endif
>>
>> Is this really needed? Can't you simply error on use of the command
>> line option, without the need for the extra table and dummy handler?
>>
> 
> Actually, gas does not even present the option --scfi when the target 
> does not have the two required defines TARGET_USE_SCFI  and 
> TARGET_USE_GINSN.  So I could guard the code in pobegin () in read.c 
> (and remove the dummy handlers in scfidw2genc.c):
> 
>    /* Now CFI ones.  */
> #if defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN)
>    if (flag_synth_cfi)
>      {
>        pop_table_name = "scfi";
>        scfi_pop_insert ();
>      }
>    else
>      {
>        pop_table_name = "cfi";
>        cfi_pop_insert ();
>      }
> #else
>    pop_table_name = "cfi";
>    cfi_pop_insert ();
> #endif

Except imo preferably without redundancy:

   /* Now CFI ones.  */
#if defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN)
   if (flag_synth_cfi)
     {
       pop_table_name = "scfi";
       scfi_pop_insert ();
     }
   else
#endif
     {
       pop_table_name = "cfi";
       cfi_pop_insert ();
     }

Jan

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

* Re: [PATCH, V2 07/10] gas: synthesize CFI for hand-written asm
  2023-11-02  8:15     ` Indu Bhagat
@ 2023-11-02 11:39       ` Jan Beulich
  2023-12-10 10:22         ` Indu Bhagat
  0 siblings, 1 reply; 30+ messages in thread
From: Jan Beulich @ 2023-11-02 11:39 UTC (permalink / raw)
  To: Indu Bhagat; +Cc: binutils

On 02.11.2023 09:15, Indu Bhagat wrote:
> On 10/31/23 07:10, Jan Beulich wrote:
>> On 30.10.2023 17:51, Indu Bhagat wrote:
>>> gas/
>>> 	* Makefile.am: Add new files.
>>> 	* Makefile.in: Regenerated.
>>> 	* as.c (defined): Handle documentation and listing option for
>>> 	ginsns and SCFI.
>>> 	* config/obj-elf.c (obj_elf_size): Invoke ginsn_data_end.
>>> 	(obj_elf_type): Invoke ginsn_data_begin.
>>> 	* config/tc-i386.c (ginsn_new): New functionality to generate
>>> 	ginsns.
>>> 	(x86_scfi_callee_saved_p): New function.
>>> 	(ginsn_dw2_regnum): Likewise.
>>> 	(ginsn_set_where): Likewise.
>>> 	(x86_ginsn_alu): Likewise.
>>> 	(x86_ginsn_move): Likewise.
>>> 	(x86_ginsn_lea): Likewise.
>>> 	(x86_ginsn_jump): Likewise.
>>> 	(x86_ginsn_jump_cond): Likewise.
>>> 	(md_assemble): Invoke ginsn_new.
>>> 	(s_insn): Likewise.
>>> 	(i386_target_format): Add hard error for usage of --scfi with non AMD64 ABIs.
>>> 	* config/tc-i386.h (TARGET_USE_GINSN): New definition.
>>> 	(TARGET_USE_SCFI): Likewise.
>>> 	(SCFI_NUM_REGS): Likewise.
>>> 	(REG_FP): Likewise.
>>> 	(REG_SP): Likewise.
>>> 	(SCFI_INIT_CFA_OFFSET): Likewise.
>>> 	(SCFI_CALLEE_SAVED_REG_P): Likewise.
>>> 	(x86_scfi_callee_saved_p): Likewise.
>>
>> For this arch-specific code there's a fundamental question of maintenance
>> cost here: It doesn't look very reasonable to me to demand of people adding
>> support for new ISA extensions to also take into consideration all of this
>> new machinery. Yet if any such addition affects SCFI, things will go out-
>> of-sync without updating this code as well. It may not be very often that
>> such updating is necessary, but right now there's APX work in progress
>> which I expect will need dealing with here as well.
>>
> 
> I understand your concerns.  FWIW, for SCFI, we will need to translate 
> only a subset of instructions into ginsns (change of flow insns, 
> save/restore and arith on REG_SP/REG_FP).

Considering what you say further down regarding untraceability, I'm afraid
you will need to care about all insns which have SP/FP as their destination.

>  For APX, I see that there is 
> are new instructions that fall in this set unfortunately.  Hopefully the 
> set remains small. But in general, for future additions (and for APX 
> currently), there will be time to act as SCFI is for hand-written asm 
> for which synthesizing CFI is desired, not for all code that GAS 
> necessarily has to deal with.

I understand that, but if a new ISA extension is supported in a new gas
version, I'd expect it to be legitimate for people to expect that SCFI is
then also supported for them right away. Else detecting what may or may
not be used with particular versions of gas is going to become a nightmare.

>>> +static ginsnS *
>>> +x86_ginsn_alu (i386_insn insn, symbolS *insn_end_sym)
>>> +{
>>> +  offsetT src_imm;
>>> +  uint32_t dw2_regnum;
>>> +  enum ginsn_src_type src_type;
>>> +  enum ginsn_dst_type dst_type;
>>> +  ginsnS *ginsn = NULL;
>>> +
>>> +  /* FIXME - create ginsn for REG_SP target only ? */
>>> +  /* Map for insn.tm.extension_opcode
>>> +     000 ADD    100 AND
>>> +     001 OR     101 SUB
>>> +     010 ADC    110 XOR
>>> +     011 SBB    111 CMP  */
>>> +
>>> +  /* add/sub imm, %reg.
>>> +     and imm, %reg only at this time for SCFI. */
>>> +  if (!(insn.tm.extension_opcode == 0
>>> +	|| insn.tm.extension_opcode == 4
>>> +	|| insn.tm.extension_opcode == 5))
>>> +    return ginsn;
>>
>> Why is AND permitted, but OR/XOR aren't?
>>
>> Also this is about ALU insns with immediate operands only, yet that
>> fact isn't reflected in the function name.
>>
> 
> OR/XOR should be handled. I will fix this.
> 
> This function is handling opcodes with imm only, true.  ADD/SUB with reg 
> are handled elsewhere (with opcode == 0x1 and opcode == 0x29).

I saw that, but this doesn't eliminate my remark regarding the name of
this function. Also the two other opcodes you mention aren't quite
enough.

>>> +  /* TBD_GINSN_REPRESENTATION_LIMIT: There is no representation for when a
>>> +     symbol is used as an operand, like so:
>>> +	  addq    $simd_cmp_op+8, %rdx
>>> +     Skip generating any ginsn for this.  */
>>> +  if (insn.imm_operands == 1
>>> +      && insn.op[0].imms->X_op == O_symbol)
>>> +    return ginsn;
>>> +
>>> +  gas_assert (insn.imm_operands == 1
>>> +	      && insn.op[0].imms->X_op == O_constant);
>>
>> Perhaps less fragile if you use != O_constant in the preceding if()?
>> The remaining half could the move ahead of that if(), allowing half
>> of its condition to also be dropped.
>>
>>> +  src_imm = insn.op[0].imms->X_add_number;
>>> +  /* The second operand may be a register or indirect access.  */
>>> +  if (insn.mem_operands == 1 && insn.base_reg)
>>> +    {
>>> +      dw2_regnum = ginsn_dw2_regnum (insn.base_reg);
>>> +      src_type = GINSN_SRC_INDIRECT;
>>> +      dst_type = GINSN_DST_INDIRECT;
>>
>> The possibly in use index register isn't of interest in this case?
>> Nor the displacement in the memory operand, ...
>>
> 
> Not of interest, No.  When src_type and dst_type is INDIRECT.

It's not clear to me what you mean with your reply. If the addressing
form used isn't relevant, why do you handle base-only and ...

>>> +    }
>>> +  else if (insn.mem_operands == 1 && insn.index_reg)
>>> +    {
>>> +      dw2_regnum = ginsn_dw2_regnum (insn.index_reg);
>>> +      src_type = GINSN_SRC_INDIRECT;
>>> +      dst_type = GINSN_DST_INDIRECT;
>>
>> ... similarly applicable here?

... index-only separately in the first place?

>> Nor a segment override?
> 
> Segment override probably is not of interest for SCFI.
> 
>>
>>> +    }
>>> +  else
>>> +    {
>>> +      gas_assert (insn.reg_operands == 1);
>>
>> Afaict this will trigger when the memory operand has neither base
>> nor index.
>>
> 
> memory operand with neither base nor index ?  An asm instruction example 
> will be helpful.

	add	$1, symbol
	add	%eax, symbol

Maybe you really don't need to consider those (if only GPR destinations
are of interest), but then a comment should be saying so and you'd
probably want to bail from the function early on in such cases.

>>> +static ginsnS *
>>> +x86_ginsn_lea (i386_insn insn, symbolS *insn_end_sym)
>>> +{
>>> +  offsetT src_disp = 0;
>>> +  ginsnS *ginsn = NULL;
>>> +  uint32_t base_reg;
>>> +  uint32_t index_reg;
>>> +  offsetT index_scale;
>>> +  uint32_t dst_reg;
>>> +
>>> +  if (!insn.index_reg && !insn.base_reg)
>>> +    {
>>> +      /* lea symbol, %rN.  */
>>> +      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
>>> +      /* FIXME - Skip encoding information about the symbol.
>>> +	 This is TBD_GINSN_INFO_LOSS, but it is fine if the mode is
>>> +	 GINSN_GEN_SCFI.  */
>>> +      ginsn = ginsn_new_mov (insn_end_sym, false,
>>> +			     GINSN_SRC_IMM, 0xf /* arbitrary const.  */, 0,
>>> +			     GINSN_DST_REG, dst_reg, 0);
>>> +    }
>>> +  else if (insn.base_reg && !insn.index_reg)
>>> +    {
>>> +      /* lea    -0x2(%base),%dst.  */
>>> +      base_reg = ginsn_dw2_regnum (insn.base_reg);
>>> +      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
>>> +
>>> +      if (insn.disp_operands)
>>> +	src_disp = insn.op[0].disps->X_add_number;
>>
>> What if the displacement expression is other than O_constant?
>>
> 
> For SCFI, a "complex" lea instruction will imply some untraceable change 
> to the destination reg.  For SCFI, the extent of untraceable change is 
> not of interest, hence, in general, some information loss in ginsn is 
> tolerable.

As a fundamental request: For anything that's of no interest, can you
please try to cover this with as little (and thus clear) code as possible?
No taking apart of sub-cases when those are indifferent in the end anyway.

> I will have to take a look at such instructions to see what the options 
> are.  If you have a example handy, it will be helpful.

	lea	symbol(%rax), %rbp

>>> +      if (src_disp)
>>> +	/* Generate an ADD ginsn.  */
>>> +	ginsn = ginsn_new_add (insn_end_sym, true,
>>> +			       GINSN_SRC_REG, base_reg, 0,
>>> +			       GINSN_SRC_IMM, 0, src_disp,
>>> +			       GINSN_DST_REG, dst_reg, 0);
>>> +      else
>>> +	  /* Generate a MOV ginsn.  */
>>> +	  ginsn = ginsn_new_mov (insn_end_sym, true,
>>> +				 GINSN_SRC_REG, base_reg, 0,
>>> +				 GINSN_DST_REG, dst_reg, 0);
>>> +    }
>>> +  else if (!insn.base_reg && insn.index_reg)
>>> +    {
>>> +      /* lea (,%index,imm), %dst.  */
>>> +      /* FIXME - Skip encoding an explicit multiply operation, instead use
>>> +	 GINSN_TYPE_OTHER.  This is TBD_GINSN_INFO_LOSS, but it is fine if
>>> +	 the mode is GINSN_GEN_SCFI.  */
>>> +      index_scale = insn.log2_scale_factor;
>>> +      index_reg = ginsn_dw2_regnum (insn.index_reg);
>>> +      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
>>> +      ginsn = ginsn_new_other (insn_end_sym, true,
>>> +			       GINSN_SRC_REG, index_reg,
>>> +			       GINSN_SRC_IMM, index_scale,
>>> +			       GINSN_DST_REG, dst_reg);
>>> +    }
>>> +  else
>>> +    {
>>> +      /* lea disp(%base,%index,imm) %dst.  */
>>> +      /* FIXME - Skip encoding information about the disp and imm for index
>>> +	 reg.  This is TBD_GINSN_INFO_LOSS, but it is fine if the mode is
>>> +	 GINSN_GEN_SCFI.  */
>>> +      base_reg = ginsn_dw2_regnum (insn.base_reg);
>>> +      index_reg = ginsn_dw2_regnum (insn.index_reg);
>>> +      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
>>> +      /* Generate an ADD ginsn.  */
>>> +      ginsn = ginsn_new_add (insn_end_sym, true,
>>> +			     GINSN_SRC_REG, base_reg, 0,
>>> +			     GINSN_SRC_REG, index_reg, 0,
>>> +			     GINSN_DST_REG, dst_reg, 0);
>>
>> The comment mentions the displacement, but it's not passed on? In the earlier
>> "else if" block you also pay attention to the scla factor, but here you don't?
>> That said, I think I'm confused anyway about the FIXME comments.
>>
> 
> Ah, This is what I was hinting at in the previous comment.
> 
> So whats going on here is the following -
> 
> First, An "add reg1, reg2, reg3" (reg3 being the destination) ginsn is 
> only interesting for SCFI if the reg3 is either REG_FP or REG_SP. We 
> still generate them all nevertheless for now.
> 
> Second, We say that "add reg1, reg2, reg3" makes reg3 "untraceable". 
> This is based on the observation that static stack usage in a function 
> will use imm based arithmetic insns.

That is, you only intend to support that way of allocating stack space
(i.e. one of the apparently many constraints to using this machinery).

> So, if reg3 is REG_SP, REG_FP, 
> these instructions make REG_SP / REG_FP "untraceable".
> 
> Note that, We are not allowing all registers for CFA tracking. (Now 
> _this_ is a constraint, yes. And which is why we dont support DRAP usage 
> currently. But its a specific pattern that we can accommodate in future.)
> 
> Hence, tracking more information for such (untraceable) instructions is 
> unnecessary for SCFI purposes.  If and when GAS uses ginsn (in the i386 
> backend) for other purposes, such information loss will need to be 
> revisited.

I guess there wants to be a central comment (either in common code, or
once per arch) which other comments then can refer to. To the reader
of such code it needs to be clear what bits of information are relevant
and which ones can be ignored (for now).

>>> +      ginsn = ginsn_new_jump (insn_end_sym, true,
>>> +			      GINSN_SRC_SYMBOL, 0, src_symbol);
>>> +
>>> +      ginsn_set_where (ginsn);
>>> +    }
>>> +
>>> +  return ginsn;
>>> +}
>>
>> Further, what about XABORT transferring control to what XBEGIN has
>> supplied? (XBEGIN can, in a sense, also be considered a [conditional]
>> branch.)
>>
> 
> My plan after writing this x86 -> ginsn translator was:
> There are and will remain many machine instructions that may need to be 
> supported depending on hand-written assmebly programmers use.  This is 
> the reason I had left some asserts towards the end in the ginsn_new () 
> API: if any instructions which affect the control flow, or where the 
> destination is REG_FP / REG_SP, or push* / pop*  are not handled, we 
> will have an assert fail and GAS will need to handle it.

Elsewhere you mention you want to use this for some of the kernel's
assembly files. If this feature went in with incomplete coverage, the
kernel build system detecting its availability will likely be misled.

Furthermore I'm of the opinion that at the time this series goes in,
assertions should remain only to check that state is as expected. They
should not be used to check that user input is unexpected. Such may
only be communicated back by ordinary diagnostics; the assembler should
not crash (and even deliberately) on certain input. Besides such being
a bug, it also gets in the way of fuzzing.

>>> +static ginsnS *
>>> +x86_ginsn_jump_cond (i386_insn insn, symbolS *insn_end_sym)
>>> +{
>>> +  ginsnS *ginsn = NULL;
>>> +  symbolS *src_symbol;
>>> +
>>> +  /* TBD_GINSN_GEN_NOT_SCFI: Ignore move to or from xmm reg for mode.  */
>>> +  if (i.tm.opcode_space == SPACE_0F)
>>> +    return ginsn;
>>
>> What is the comment about? And what about SPACE_0F-encoded conditional
>> jumps (Jcc <disp32>)? And where are LOOP and J{E,R}CXZ handled?
>>
> 
> These need to be handled.  I will take a look.

Just to mention it: The Jcc <disp32> forms should appear only during
relaxation, while - aiui - all your code runs earlier than that. I was
misled by you (elsewhere) handling both 0xE9 and 0xEB as JMP opcodes,
despite the <disp32> form there - iirc - also only appearing during
relaxation.

>>> +  gas_assert (insn.disp_operands == 1);
>>> +
>>> +  if (insn.op[0].disps->X_op == O_symbol)
>>> +    {
>>> +      src_symbol = insn.op[0].disps->X_add_symbol;
>>> +      /* The jump target is expected to be a symbol with 0 addend.
>>> +	 Assert for now to see if this assumption is true.  */
>>> +      gas_assert (insn.op[0].disps->X_add_number == 0);
>>> +      ginsn = ginsn_new_jump_cond (insn_end_sym, true,
>>> +				   GINSN_SRC_SYMBOL, 0, src_symbol);
>>> +      ginsn_set_where (ginsn);
>>> +    }
>>> +  else
>>> +    /* Catch them for now so we know what we are dealing with.  */
>>> +    gas_assert (0);
>>
>> I'm going from the assumption that this (and alike) will be addressed
>> before this series is committed?
> 
> I will work on the ones you have raised above in the review. My plan was 
> to exercise this code in different ways after commmitting and address 
> the failures as I run into them.

Well, as said, I think full insn coverage is a prereq to committing, or
else there needs to be a way to easily detect an assumed fully functional
implementation in a later gas version, no matter that a partially
functional one may have been present in a number of earlier versions
already. But of course Nick in particular may have a different view on
this, and his view would trump mine.

>>> +{
>>> +  uint16_t opcode;
>>> +  uint32_t dw2_regnum;
>>> +  uint32_t src2_dw2_regnum;
>>> +  int32_t gdisp = 0;
>>> +  ginsnS *ginsn = NULL;
>>> +  ginsnS *ginsn_next = NULL;
>>> +  ginsnS *ginsn_last = NULL;
>>> +
>>> +  /* FIXME - Need a way to check whether the decoding is sane.  The specific
>>> +     checks around i.tm.opcode_space were added as issues were seen.  Likely
>>> +     insufficient.  */
>>
>> Furthermore opcode encoding space (SPACE_...) need to be taken into
>> account in all cases.
>>
>>> +  /* Currently supports generation of selected ginsns, sufficient for
>>> +     the use-case of SCFI only.  To remove this condition will require
>>> +     work on this target-specific process of creation of ginsns.  Some
>>> +     of such places are tagged with TBD_GINSN_GEN_NOT_SCFI to serve as
>>> +     examples.  */
>>> +  if (gmode != GINSN_GEN_SCFI)
>>> +    return ginsn;
>>> +
>>> +  opcode = i.tm.base_opcode;
>>> +
>>> +  switch (opcode)
>>> +    {
>>> +    case 0x1:
>>> +      /* add reg, reg.  */
>>> +      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
>>
>> You don't care about opcode 0 (byte operation). Then what about 16-bit
>> operand size? Or, since we're talking of a 64-bit-ABI-only feature,
>> even 32-bit operand size?
>>
> 
> Operand size in some cases does not affect SCFI. So we dont keep 
> information about operand sizes.  The case that I should handle operand 
> size is when they are pushed to / popped from stack.

So you care about recognizing when %rbp is overwritten by an insn. And
you also care about the same for %ebp and %bp. In that sense operand
size may indeed be unnecessary to determine. Except that then you also
want to deal with byte operations, i.e. %bpl being the destination.

>> Also what about opcode 0x3?
> 
> LSL instruction ? It doesnt look like it can affect DWARF CFI of a 
> function. Please correct me if this is wrong.

LSL is 0f03, i.e. opcode 0x3 in SPACE_0F. What my comment was about is
ADD with inversed operands (ModRM.rm soure, ModRM.reg destination). All
four of these flavors of ADD are covered by a single template, using the
D and W attributes to establish opcode bits 0 and 1 based on actual
operands (and possibly pseudo-prefixes).

>>> +      if (i.reg_operands == 2)
>>> +	{
>>> +	  src2_dw2_regnum = ginsn_dw2_regnum (i.op[1].regs);
>>> +	  ginsn = ginsn_new_add (insn_end_sym, true,
>>> +				 GINSN_SRC_REG, dw2_regnum, 0,
>>> +				 GINSN_SRC_REG, src2_dw2_regnum, 0,
>>> +				 GINSN_DST_REG, src2_dw2_regnum, 0);
>>> +	  ginsn_set_where (ginsn);
>>> +	}
>>> +      else if (i.mem_operands && i.base_reg)
>>> +	{
>>> +	  src2_dw2_regnum = ginsn_dw2_regnum (i.base_reg);
>>> +	  if (i.disp_operands == 1)
>>> +	    gdisp = i.op[1].disps->X_add_number;
>>> +
>>> +	  ginsn = ginsn_new_add (insn_end_sym, true,
>>> +				 GINSN_SRC_REG, dw2_regnum, 0,
>>> +				 GINSN_SRC_INDIRECT, src2_dw2_regnum, gdisp,
>>> +				 GINSN_DST_INDIRECT, src2_dw2_regnum, gdisp);
>>> +	  ginsn_set_where (ginsn);
>>> +	}
>>> +      else
>>> +	/* Catch them for now so we know what we are dealing with.  */
>>> +	gas_assert (0);
>>> +
>>> +      break;
>>> +    case 0x29:
>>> +      /* If opcode_space == SPACE_0F, this is a movaps insn.  Skip it
>>> +	 for GINSN_GEN_SCFI.  */
>>> +      if (i.tm.opcode_space == SPACE_0F)
>>> +	break;
>>
>> Extending on the earlier related comment: Why would you exclude just SPACE_0F?
>>
> 
> Perhaps here I should check that space == SPACE_BASE and only allow 
> that? And also for others.

Indeed.

>>> +      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
>>> +      /* push fs / push gs.  */
>>> +      ginsn = ginsn_new_sub (insn_end_sym, false,
>>> +			     GINSN_SRC_REG, REG_SP, 0,
>>> +			     GINSN_SRC_IMM, 0, 8,
>>
>> Note how the 8 here assumes flag_code == CODE_64BIT. (You further assume
>> no operand size override here.)
>>
> 
> Hmm. Yes, I do assume no operand size override here. I will need to 
> understand how to calculate the operand size here. Looks like I need to 
> check for instruction prefixes 66H or REX.W.  Is there an API in the 
> backend that be used for this ?

i.prefixes[] and (depending on the phase of assembly) i.rex should be
telling you.

>>> +      dw2_regnum = ginsn_dw2_regnum (i.base_reg);
>>> +      ginsn = ginsn_new_load (insn_end_sym, false,
>>> +			      GINSN_SRC_INDIRECT, REG_SP, 0,
>>> +			      GINSN_DST_INDIRECT, dw2_regnum);
>>
>> When both operands are "indirect", what's the difference between
>> move, load, and store? IOW if the above is permitted, can't all
>> three be folded into a single ginsn kind?
>>
> 
> In theory, any of mov/load/store ginsn type may be used if both operands 
> are indirect mem access.

Which suggests that things can be simplified then.

>>> +    case 0xc2:
>>> +    case 0xc3:
>>> +      /* Near ret.  */
>>> +      ginsn = ginsn_new_return (insn_end_sym, true);
>>> +      ginsn_set_where (ginsn);
>>> +      break;
>>
>> Where did the immediate operand of 0xC2 go? And what about far return
>> (and more generally far branches)?
>>
> 
> A return ginsn for SCFI can afford this information loss.

Hmm, right, I guess by how much the stack pointer is incremented at the
return point isn't of any interest anymore. Unless of course you intended
to support co-routines.

>>> +    case 0xc9:
>>> +      /* The 'leave' instruction copies the contents of the RBP register
>>> +	 into the RSP register to release all stack space allocated to the
>>> +	 procedure.  */
>>> +      ginsn = ginsn_new_mov (insn_end_sym, false,
>>> +			     GINSN_SRC_REG, REG_FP, 0,
>>> +			     GINSN_DST_REG, REG_SP, 0);
>>> +      ginsn_set_where (ginsn);
>>> +
>>> +      /* Then it restores the old value of the RBP register from the stack.  */
>>> +      ginsn_next = ginsn_new_load (insn_end_sym, false,
>>> +				   GINSN_SRC_INDIRECT, REG_SP, 0,
>>> +				   GINSN_DST_REG, REG_FP);
>>> +      ginsn_set_where (ginsn_next);
>>> +
>>> +      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
>>> +      ginsn_last = ginsn_new_add (insn_end_sym, false,
>>> +				  GINSN_SRC_REG, REG_SP, 0,
>>> +				  GINSN_SRC_IMM, 0, 8,
>>> +				  GINSN_DST_REG, REG_SP, 0);
>>> +      ginsn_set_where (ginsn_next);
>>> +
>>> +      gas_assert (!ginsn_link_next (ginsn_next, ginsn_last));
>>> +      break;
>>
>> You handle LEAVE, but not ENTER?
>>
> 
> I should have. From the three variants ([Enter imm16,0], [ENTER imm16,1] 
> and [ENTER imm16, imm8], the last one is not super clear currently. Is 
> it possible for you to pass down some understanding to me on that ?

I can't say more than what the CPU doc says: The second operand tells
how many earlier frame pointers are copied to the local frame. Perhaps
you want to simply treat non-zero values as untraceable SP/FP?

Jan

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

* Re: [PATCH, V2 09/10] gas: testsuite: add a x86_64 testsuite for SCFI
  2023-11-01  6:24     ` Indu Bhagat
@ 2023-11-02 12:28       ` Jan Beulich
  2023-11-03  5:45         ` Indu Bhagat
  0 siblings, 1 reply; 30+ messages in thread
From: Jan Beulich @ 2023-11-02 12:28 UTC (permalink / raw)
  To: Indu Bhagat; +Cc: binutils

On 01.11.2023 07:24, Indu Bhagat wrote:
> On 10/31/23 09:13, Jan Beulich wrote:
>> On 30.10.2023 17:51, Indu Bhagat wrote:
>>> --- /dev/null
>>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-add-2.s
>>> @@ -0,0 +1,48 @@
>>> +        .section        .rodata
>>> +	.type	simd_cmp_op, @object
>>> +	.size	simd_cmp_op, 8
>>> +simd_cmp_op:
>>> +	.long	2
>>> +	.zero	4
>>> +
>>> +# Testcase for add instruction.
>>> +# add reg, reg instruction
>>> +	.text
>>> +	.globl	foo
>>> +	.type	foo, @function
>>> +foo:
>>> +	.cfi_startproc
>>> +	pushq	%rbp
>>> +	.cfi_def_cfa_offset 16
>>> +	.cfi_offset 6, -16
>>> +	movq    %rsp, %rbp
>>> +	.cfi_def_cfa_register 6
>>> +	pushq	%r12
>>> +	.cfi_offset 12, -24
>>> +	mov	%rsp, %r12
>>
>> You copy %rsp to %r12 here, and ...
>>
>>> +# Stack manipulation is permitted if the base register for
>>> +# tracking CFA has been changed to FP.
>>> +	addq    %rdx, %rsp
>>> +	addq	%rsp, %rax
>>> +# Some add instructions may access the stack indirectly.  Such
>>> +# accesses do not make REG_FP untraceable.
>>> +	addl    %eax, -84(%rbp)
>>> +# Other kind of add instructions should not error out in the
>>> +# x86_64 -> ginsn translator
>>> +	addq    $simd_cmp_op+8, %rdx
>>> +	addl    %edx, -32(%rsp)
>>> +	addl    $1, fb_low_counter(,%rbx,4)
>>> +	mov	%r12, %rsp
>>
>> ... you restore it here, but both without any .cfi_* annotation.
>> It is therefore unclear whether this is in any way related to ...
>>
> 
> There are no .cfi_* annotations for the %rsp updates here because the 
> CFA tracking has already been switched to REG_FP based tracking.  From 
> the perspective of DWARF CFI, this usages of the stack do not need to 
> tracked.  If unwinding happens from any instruction in the above 
> sequence, we already have the correct and complete unwind information.
> 
>>> +# Popping a callee-saved register.
>>> +# RSP must be traceable.
>>> +	pop     %r12
>>> +	.cfi_restore 12
>>
>> ... what the comment says about these.
>>
> 
> The comment here means that hand-written programs may use stack for 
> their local usage etc. but must ensure that before or in the epilogue, 
> rsp is restored to the desired value (before register restores via pop 
> instructions).

And how exactly do you tell that the "mov %r12, %rsp" is a restore, not
the setting of an arbitrary new %rsp value?

> This is not a special handling in the SCFI machinery but it is what is 
> needed to ensure correctness of the function.  For asm not adhering to 
> the ABI/calling conventions, SCFI cannot be used.

But the calling convention doesn't go as far as dictating local frame
layout in a function, I don't think?

>>> +	leave
>>> +	.cfi_def_cfa_register 7
>>> +	.cfi_restore 6
>>
>> Using numbers here isn't very helpful, I'm afraid.
>>
> 
> I am not sure I understand.  The DWARF register numbers and the offset 
> values are required by the semantics of those CFI directives.

Offsets are (largely) unavoidable to be numerics, yes. But .cfi_*
directives support register names, and using them is (imo) far better
than Dwarf register numbers - you may have memorized them by now, but
others likely won't.

>>> --- /dev/null
>>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.l
>>> @@ -0,0 +1,2 @@
>>> +.*Assembler messages:
>>> +.*9: Warning: --scfi=all ignores some user-specified CFI directives
>>
>> Is repeating this for (about?) every test really necessary / useful?
>> If multiple passes for every test were to make sense to me, I'd expect
>> one pass with SCFI and one pass without, where the directives then
>> take effect. And then the same set of expectations should match.
>>
> 
> re: is repeating this useful? Strictly speaking no. _But_, I think as 
> the code evolves, we may add more diagnostics to the SCFI machinery. 
> Explicitly checking for the set of warnings helps us ensure no new 
> warnings show up where they are not expected. In a way, it is a stricter 
> check and ensures no unintented slippage.

Well, okay, that's fair. You didn't respond to the other part of my
comment, though.

>>> --- /dev/null
>>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.s
>>> @@ -0,0 +1,49 @@
>>> +# Testcase for switching between sp/fp based CFA.
>>> +	.text
>>> +	.globl	foo
>>> +	.type	foo, @function
>>> +foo:
>>> +        .cfi_startproc
>>> +        pushq   %r14
>>> +        .cfi_def_cfa_offset 16
>>> +        .cfi_offset 14, -16
>>> +        pushq   %r13
>>> +        .cfi_def_cfa_offset 24
>>> +        .cfi_offset 13, -24
>>> +        pushq   %r12
>>> +        .cfi_def_cfa_offset 32
>>> +        .cfi_offset 12, -32
>>> +        pushq   %rbp
>>> +        .cfi_def_cfa_offset 40
>>> +        .cfi_offset 6, -40
>>> +        pushq   %rbx
>>> +        .cfi_def_cfa_offset 48
>>> +        .cfi_offset 3, -48
>>> +        movq    %rdi, %rbx
>>> +        subq    $32, %rsp
>>> +        .cfi_def_cfa_offset 80
>>> +# This mov does not switch CFA tracking to REG_FP, because there has already
>>> +# been stack usage between here and the push %rbp
>>> +        movq    %rsp, %rbp
>>
>> Yet another constraint?
>>
> 
> Not sure I understand. This is not a constraint for SCFI.  A pattern 
> where the user does:
>      push %rbp
>      .. more stack usage..
>      movq %rsp, %rbp
> is not using frame pointer for stack tracing, but simply as a scratch 
> register.

Why might it not be? We're talking about hand-written assembly, and
assembly writers are free to e.g. first push all (necessary) callee-
saved register and then copy %rsp into %rbp. For a function with many
stack locals this has the benefit of allowing more of them to be
accessed via <disp8>(%rbp).

>>> --- /dev/null
>>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.d
>>> @@ -0,0 +1,36 @@
>>> +#as: --scfi -W
>>> +#objdump: -Wf
>>> +#name: Synthesize CFI in presence of control flow 1
>>> +#...
>>> +Contents of the .eh_frame section:
>>> +
>>> +00000000 0+0014 0+0000 CIE
>>> +  Version:               1
>>> +  Augmentation:          "zR"
>>> +  Code alignment factor: 1
>>> +  Data alignment factor: -8
>>> +  Return address column: 16
>>> +  Augmentation data:     [01][abc]
>>> +  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
>>> +  DW_CFA_offset: r16 \(rip\) at cfa-8
>>> +  DW_CFA_nop
>>> +  DW_CFA_nop
>>> +
>>> +0+0018 0+0024 0000001c FDE cie=00000000 pc=0+0000..0+003a
>>> +  DW_CFA_advance_loc: 1 to 0+0001
>>> +  DW_CFA_def_cfa_offset: 16
>>> +  DW_CFA_offset: r3 \(rbx\) at cfa-16
>>> +  DW_CFA_advance_loc: 37 to 0+0026
>>> +  DW_CFA_remember_state
>>> +  DW_CFA_advance_loc: 1 to 0+0027
>>> +  DW_CFA_restore: r3 \(rbx\)
>>> +  DW_CFA_def_cfa_offset: 8
>>> +  DW_CFA_advance_loc: 1 to 0+0028
>>> +  DW_CFA_restore_state
>>> +  DW_CFA_advance_loc: 9 to 0+0031
>>> +  DW_CFA_restore: r3 \(rbx\)
>>> +  DW_CFA_def_cfa_offset: 8
>>> +  DW_CFA_nop
>>> +#...
>>> +
>>> +#pass
>>
>> Seeing this recurring pattern (the last three lines): Why not just #pass?
>>
> 
> I thought adding #... is clearer as it indicates that the test knowingly 
> skips lines thereafter.

Well, #pass alone already says exactly this. If you didn't expect further
output lines, you'd _omit_ #pass.

>>> --- /dev/null
>>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.s
>>> @@ -0,0 +1,47 @@
>>> +# Testcase with one dominator bb and two exit bbs
>>> +# Something like for: return ferror (f) || fclose (f) != 0;
>>> +	.text
>>> +	.section	.rodata.str1.1,"aMS",@progbits,1
>>> +.LC0:
>>> +	.string	"w"
>>> +.LC1:
>>> +	.string	"conftest.out"
>>> +	.section	.text.startup,"ax",@progbits
>>> +	.p2align 4
>>> +	.globl	main
>>> +	.type	main, @function
>>> +main:
>>> +.LFB11:
>>
>> Coming back to "hand-written assembly": The above looks very much like it
>> was compiler output (earlier tests did, too, but it's perhaps more
>> prominent here). That's not necessarily what a human might write, and
>> hence I wonder about the overall coverage that can be gained that way.
>>
> 
> Yes, its inspired by compiler generated output. I ran into this when 
> running some tests. Its still a good basic cfg test for SCFI - a 
> function with two return paths.

You understand though that I used this only as (sutiable) example. My point
being that hand-written assembly frequently doesn't resemble compiler-
genertaed code. Otherwise, i.e. if the resulting code wasn't to be different,
why would one write assembly code in nthe first place?

>>> --- /dev/null
>>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-diag-1.s
>>> @@ -0,0 +1,23 @@
>>> +# Testcase for REG_FP based CFA
>>> +# and using REG_FP as scratch.
>>> +	.text
>>> +	.globl	foo
>>> +	.type	foo, @function
>>> +foo:
>>> +	.cfi_startproc
>>> +	pushq	%rbp
>>> +	.cfi_def_cfa_offset 16
>>> +	.cfi_offset 6, -16
>>> +	movq    %rsp, %rbp
>>> +	.cfi_def_cfa_register 6
>>> +# The following add causes REG_FP to become untraceable
>>> +	addq	%rax, %rbp
>>
>> But this isn't a problem as long as FP isn't further used. Indeed ...
>>
>>> +	.cfi_def_cfa_register 7
>>> +	pop	%rbp
>>
>> ... its original value is restored right afterwards. Yet another constraint?
>>
> 
> Hmm. I need some clarification on the statement "Yet another constraint".

By "constraint" I mean assumptions you make on the way assembly code is
written, for your machinery to be usable. The more such assumptions, the
smaller the set of code "eligible" to (future) use of your work. Hence
also why I think all such "constraints" need to be spelled out in a
single place, such that one can
- verify that everything meeting these constraints actually also works,
- check up front whether one's assembly code is actually suitable for
  enabling --scfi.

>>> --- /dev/null
>>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.s
>>> @@ -0,0 +1,55 @@
>>> +# Testcase for a diagnostic around assymetrical restore
>>> +# Testcase inspired by byte_insert_op1 in libiberty
>>> +# False positive for the diagnostic
>>> +.type	foo, @function
>>> +foo:
>>> +.LFB10:
>>> +	.cfi_startproc
>>> +	endbr64
>>> +	pushq	%rbp
>>> +	.cfi_def_cfa_offset 16
>>> +	.cfi_offset 6, -16
>>> +	movq	%rsp, %rbp
>>> +	.cfi_def_cfa_register 6
>>> +	pushq	%r12
>>> +	pushq	%rbx
>>> +	subq	$24, %rsp
>>> +	.cfi_offset 12, -24
>>> +	.cfi_offset 3, -32
>>> +	movl	%edi, -20(%rbp)
>>> +	movq	%rsi, -32(%rbp)
>>> +	movl	%edx, -24(%rbp)
>>> +	movq	%rcx, -40(%rbp)
>>> +# The assembler cannot differentiate that the following
>>> +# mov to %rbx is not a true restore operation, but simply
>>> +# %rbx register usage as a scratch reg of some sort.
>>> +# The assembler merely warns of a possible assymetric restore operation
>>> +# In this case, its noise for the user unfortunately.
>>> +	movq	-40(%rbp), %rbx
>>> +	movq	-40(%rbp), %rax
>>> +	leaq	3(%rax), %r12
>>> +	jmp	.L563
>>> +.L564:
>>> +	subq	$1, %rbx
>>> +	subq	$1, %r12
>>> +	movzbl	(%rbx), %eax
>>> +	movb	%al, (%r12)
>>> +.L563:
>>> +	cmpq	-32(%rbp), %rbx
>>> +	jne	.L564
>>
>> But this is pretty common usage. Imo this (the false positive warning)
>> cannot remain like this.
>>
> 
> The warning of "Warning: SCFI: asymetrical register restore" was added 
> to alert user if they do something like
>      pushq %rbx
>      pushq %r12
>      ...
>      popq  %rbx
>      popq  %r12
> i.e., asymmetric save and restore.  Now the user may do a restore to any 
> register and use what was saved on stack as scratch regiser; the only 
> way to differentiate between a "true reg restore in epilogue" vs "reg 
> restored for scratch purposes" is to know whether its the epilogue.  GAS 
> will not be able to determine that easily.
> 
> We could remove the warning altogether.  I just thought it provides 
> users with some protection / validation of hand-written asm.

I'm in favor of such assisting warnings, but they're useful only if there
are few false positives and few false negatives. Too many warnings makes
people ignore them all (or shut them off), while too few makes people
mistrust them being helpful.

>>> --- /dev/null
>>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-x86-64.exp
>>> @@ -0,0 +1,103 @@
>>> +# Copyright (C) 2022-2023 Free Software Foundation, Inc.
>>> +
>>> +# This program is free software; you can redistribute it and/or modify
>>> +# it under the terms of the GNU General Public License as published by
>>> +# the Free Software Foundation; either version 3 of the License, or
>>> +# (at your option) any later version.
>>> +#
>>> +# This program is distributed in the hope that it will be useful,
>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>> +# GNU General Public License for more details.
>>> +#
>>> +# You should have received a copy of the GNU General Public License
>>> +# along with this program; if not, write to the Free Software
>>> +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
>>> +
>>> +if { ![is_elf_format] } then {
>>> +    return
>>> +}
>>> +
>>> +# common tests
>>> +if  { ([istarget "x86_64-*-*"]) } then {
>>> +
>>> +    global ASFLAGS
>>> +    set old_ASFLAGS "$ASFLAGS"
>>> +
>>> +    run_dump_test "scfi-cfi-label-1"
>>> +    run_list_test "scfi-cfi-label-1" "--scfi --warn"
>>> +
>>> +    run_list_test "scfi-diag-1" "--scfi"
>>> +    run_list_test "scfi-fp-diag-2" "--scfi"
>>> +    run_list_test "scfi-diag-2" "--scfi"
>>> +
>>> +    run_list_test "scfi-unsupported-1" "--32 --scfi"
>>
>> Perhaps a 2nd run with --x32, unless (see above) you choose to support
>> that ABI as well?
>>
> 
> At the moment, the plan was to next work on --scfi=inline and add 
> support for aarch64/aapcs64 ABIs.  No plans to add support for x32 
> unless a need arises.

But that's precisely my point: As long as you don't support --x32, you
want to check that its use is properly diagnosed (just like use of
--32 is).

Jan

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

* Re: [PATCH, V2 07/10] gas: synthesize CFI for hand-written asm
  2023-10-30 16:51 ` [PATCH, V2 07/10] gas: synthesize CFI for hand-written asm Indu Bhagat
  2023-10-31 14:10   ` Jan Beulich
@ 2023-11-02 15:53   ` Jan Beulich
  2023-11-04  7:29     ` Indu Bhagat
  1 sibling, 1 reply; 30+ messages in thread
From: Jan Beulich @ 2023-11-02 15:53 UTC (permalink / raw)
  To: Indu Bhagat; +Cc: binutils

On 30.10.2023 17:51, Indu Bhagat wrote:
> --- /dev/null
> +++ b/gas/ginsn.c
> @@ -0,0 +1,1225 @@
> +/* ginsn.h - GAS instruction representation.
> +   Copyright (C) 2023 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 "ginsn.h"
> +#include "scfi.h"
> +
> +#ifdef TARGET_USE_GINSN
> +
> +const char *const ginsn_type_names[] =

This looks like it wants to be static.

> +{
> +#define _GINSN_TYPE_ITEM(NAME, STR) STR,
> +  _GINSN_TYPES
> +#undef _GINSN_TYPE_ITEM
> +};
> +
> +const char *const ginsn_src_type_names[] =

This and ...

> +{
> +#define _GINSN_SRC_TYPE_ITEM(NAME, STR) STR,
> +  _GINSN_SRC_TYPES
> +#undef _GINSN_SRC_TYPE_ITEM
> +};
> +const char *const ginsn_dst_type_names[] =

... this also look so, but then they're also unused inside this CU,
so when making them static the compiler would complain. If they're
needed by a later patch, perhaps they should be moved there? And
if entirely unused, perhaps they should both be deleted (or at least
commented / #ifdef-ed out)?

With the many symbols further down it's also hard to spot whether any
of them should perhaps also be static.

> +{
> +#define _GINSN_DST_TYPE_ITEM(NAME, STR) STR,
> +  _GINSN_DST_TYPES
> +#undef _GINSN_DST_TYPE_ITEM
> +};
> +
> +static
> +ginsnS *ginsn_alloc (void)
> +{
> +  ginsnS *ginsn = XCNEW (ginsnS);
> +  return ginsn;
> +}
> +
> +static ginsnS*
> +ginsn_init (enum ginsn_type type, symbolS *sym, bool real_p)
> +{
> +  ginsnS *ginsn = ginsn_alloc ();
> +  ginsn->type = type;
> +  ginsn->sym = sym;

Is a symbol hanging off of a ginsn ever intended to be altered? If
not, field and function argument would want to be pointer-to-const.

> +  if (real_p)
> +    ginsn->flags |= GINSN_F_INSN_REAL;
> +  return ginsn;
> +}
> +
> +static void
> +ginsn_cleanup (ginsnS **ginsnp)
> +{
> +  ginsnS *ginsn;
> +
> +  if (!ginsnp || !*ginsnp)
> +    return;
> +
> +  ginsn = *ginsnp;
> +  if (ginsn->scfi_ops)
> +    {
> +      scfi_ops_cleanup (ginsn->scfi_ops);
> +      ginsn->scfi_ops = NULL;
> +    }
> +
> +  free (ginsn);
> +  ginsn = NULL;

This doesn't do anything useful. Did you perhaps mean to set *ginsnp to NULL,
and then also ahead of calling free()?

> +}
> +
> +static void
> +ginsn_set_src (struct ginsn_src *src, enum ginsn_src_type type, uint32_t reg,
> +	       int32_t immdisp)

I find the use of fixed-width types suspicious here: For reg, likely it wants
to be unsigned int. For immdisp it's less clear: Immediates can be wider than
32 bits, and they may also be either signed ot unsigned. From an abstract
perspective, assuming the immediate value actually is used for anything, I'd
expect offsetT to be used, following struct expressionS' X_add_number.

> +{
> +  if (!src)
> +    return;
> +
> +  src->type = type;
> +  /* Even when the use-case is SCFI, the value of reg may be > SCFI_NUM_REGS.
> +     E.g., in AMD64, push fs etc.  */
> +  src->reg = reg;
> +
> +  if (type == GINSN_SRC_IMM || type == GINSN_SRC_INDIRECT)
> +    src->immdisp = immdisp;

What's the point of the conditional around the assignment?

> +ginsnS *
> +ginsn_new_add (symbolS *sym, bool real_p,
> +	       enum ginsn_src_type src1_type, uint32_t src1_val, int32_t src1_disp,
> +	       enum ginsn_src_type src2_type, uint32_t src2_val, int32_t src2_disp,
> +	       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp)
> +{
> +  ginsnS *ginsn = ginsn_init (GINSN_TYPE_ADD, sym, real_p);
> +  /* src info.  */
> +  ginsn_set_src (&ginsn->src[0], src1_type, src1_val, src1_disp);
> +  ginsn_set_src (&ginsn->src[1], src2_type, src2_val, src2_disp);
> +  /* dst info.  */
> +  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
> +
> +  return ginsn;
> +}
> +
> +ginsnS *
> +ginsn_new_and (symbolS *sym, bool real_p,
> +	       enum ginsn_src_type src1_type, uint32_t src1_val, int32_t src1_disp,
> +	       enum ginsn_src_type src2_type, uint32_t src2_val, int32_t src2_disp,
> +	       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp)
> +{
> +  ginsnS *ginsn = ginsn_init (GINSN_TYPE_AND, sym, real_p);
> +  /* src info.  */
> +  ginsn_set_src (&ginsn->src[0], src1_type, src1_val, src1_disp);
> +  ginsn_set_src (&ginsn->src[1], src2_type, src2_val, src2_disp);
> +  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
> +
> +  return ginsn;
> +}

The comments in the two functions don't look overly useful to me. But if
you have them, please have them be consistent across similar functions.

> +ginsnS *
> +ginsn_new_mov (symbolS *sym, bool real_p,
> +	       enum ginsn_src_type src_type, uint32_t src_reg, int32_t src_disp,
> +	       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp)
> +{
> +  ginsnS *ginsn = ginsn_init (GINSN_TYPE_MOV, sym, real_p);
> +  /* src info.  */
> +  ginsn_set_src (&ginsn->src[0], src_type, src_reg, src_disp);
> +  /* dst info.  */
> +  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
> +
> +  return ginsn;
> +}

As indicated before, if both src and dst can be indirect here, ...

> +ginsnS *
> +ginsn_new_store (symbolS *sym, bool real_p,
> +		 enum ginsn_src_type src_type, uint32_t src_reg,
> +		 enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp)
> +{
> +  ginsnS *ginsn = ginsn_init (GINSN_TYPE_STORE, sym, real_p);
> +  /* src info.  */
> +  ginsn_set_src (&ginsn->src[0], src_type, src_reg, 0);
> +  /* dst info.  */
> +  gas_assert (dst_type == GINSN_DST_INDIRECT);
> +  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
> +
> +  return ginsn;
> +}
> +
> +ginsnS *
> +ginsn_new_load (symbolS *sym, bool real_p,
> +		enum ginsn_src_type src_type, uint32_t src_reg, int32_t src_disp,
> +		enum ginsn_dst_type dst_type, uint32_t dst_reg)
> +{
> +  ginsnS *ginsn = ginsn_init (GINSN_TYPE_LOAD, sym, real_p);
> +  /* src info.  */
> +  gas_assert (src_type == GINSN_SRC_INDIRECT);
> +  ginsn_set_src (&ginsn->src[0], src_type, src_reg, src_disp);
> +  /* dst info.  */
> +  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, 0);
> +
> +  return ginsn;
> +}

... I can't see what these are needed for.

> +ginsnS *
> +ginsn_new_sub (symbolS *sym, bool real_p,
> +	       enum ginsn_src_type src1_type, uint32_t src1_val, int32_t src1_disp,
> +	       enum ginsn_src_type src2_type, uint32_t src2_val, int32_t src2_disp,
> +	       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp)
> +{
> +  ginsnS *ginsn = ginsn_init (GINSN_TYPE_SUB, sym, real_p);
> +  /* src info.  */
> +  ginsn_set_src (&ginsn->src[0], src1_type, src1_val, src1_disp);
> +  ginsn_set_src (&ginsn->src[1], src2_type, src2_val, src2_disp);
> +  /* dst info.  */
> +  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
> +
> +  return ginsn;
> +}
> +
> +/* PS: Note this API does not identify the displacement values of
> +   src1/src2/dst.  At this time, it is unnecessary for correctness to support
> +   the additional argument.  */

And why do the displacements need tracking for add, and, and sub?

> +static bool
> +ginsn_indirect_jump_p (ginsnS *ginsn)
> +{
> +  bool ret_p = false;
> +  if (!ginsn)
> +    return ret_p;
> +
> +  ret_p = (ginsn->type == GINSN_TYPE_JUMP
> +	   && ginsn->src[0].type == GINSN_SRC_REG);

On x86 an indirect jump can also have a memory source operand (i.e. what
you call "indirect").

> +static bool
> +ginsn_direct_local_jump_p (ginsnS *ginsn)
> +{
> +  bool ret_p = false;
> +  if (!ginsn)
> +    return ret_p;
> +
> +  ret_p |= (ginsn->type == GINSN_TYPE_JUMP
> +	    && ginsn->src[0].type == GINSN_SRC_SYMBOL
> +	    && S_IS_LOCAL (ginsn->src[0].sym));

This looks fragile: You may not yet have seen the .globl or .weak directive.

> +static char*
> +ginsn_src_print (struct ginsn_src *src)
> +{
> +  size_t len = 39;
> +  char *src_str = XNEWVEC (char, len);
> +
> +  memset (src_str, 0, len);
> +
> +  if (src->type == GINSN_SRC_REG)
> +    {
> +      char *buf = XNEWVEC (char, 32);
> +      sprintf (buf, "%%r%d, ", ginsn_get_src_reg (src));
> +      strcat (src_str, buf);
> +    }
> +  else if (src->type == GINSN_SRC_IMM)
> +    {
> +      char *buf = XNEWVEC (char, 32);
> +      sprintf (buf, "%d, ", ginsn_get_src_imm (src));

Here and below, if you stick to int32_t as the type, this wants to use
PRId32.

> +static const char*
> +ginsn_type_sym_begin_end_print (ginsnS *ginsn)
> +{
> +  int id = 0;
> +  const char *ginsn_sym_strs[]
> +    = { "", "FUNC_BEGIN", "FUNC_END" };

static and with a 2nd const?

> +static char*
> +ginsn_print (ginsnS *ginsn)
> +{
> +  struct ginsn_src *src;
> +  struct ginsn_dst *dst;
> +  size_t len = GINSN_LISTING_LEN;
> +  char *ginsn_str = XNEWVEC (char, len);
> +
> +  memset (ginsn_str, 0, len);
> +
> +  strcpy (ginsn_str, "ginsn: ");
> +
> +  strcat (ginsn_str, ginsn_type_names[ginsn->type]);
> +  strcat (ginsn_str, " ");
> +
> +  /* For some ginsn types, no further information is printed for now.  */
> +  if (ginsn->type == GINSN_TYPE_CALL
> +      || ginsn->type == GINSN_TYPE_RETURN
> +      || ginsn->type == GINSN_TYPE_OTHER)
> +    goto end;
> +  else if (ginsn->type == GINSN_TYPE_SYMBOL)
> +    {
> +      if (GINSN_F_USER_LABEL_P (ginsn))
> +	strncat (ginsn_str, S_GET_NAME (ginsn->sym), len - 10);
> +      else
> +	strcat (ginsn_str, ginsn_type_sym_begin_end_print (ginsn));
> +      goto end;
> +    }
> +
> +  /* src 1.  */
> +  src = ginsn_get_src1 (ginsn);
> +  strcat (ginsn_str, ginsn_src_print (src));
> +
> +  /* src 2.  */
> +  src = ginsn_get_src2 (ginsn);
> +  strcat (ginsn_str, ginsn_src_print (src));
> +
> +  /* dst.  */
> +  dst = ginsn_get_dst (ginsn);
> +  strcat (ginsn_str, ginsn_dst_print (dst));
> +
> +end:
> +  gas_assert (strlen (ginsn_str) < GINSN_LISTING_LEN);

This check comes too late - if it triggers, you've corrupted memory
already. I wonder anyway why you construct all this "by hand", rather
than using snprintf().

Jan

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

* Re: [PATCH, V2 06/10] gas: scfidw2gen: new functionality to prepapre for SCFI
  2023-11-02 10:35       ` Jan Beulich
@ 2023-11-02 16:32         ` Indu Bhagat
  0 siblings, 0 replies; 30+ messages in thread
From: Indu Bhagat @ 2023-11-02 16:32 UTC (permalink / raw)
  To: Jan Beulich; +Cc: binutils

On 11/2/23 03:35, Jan Beulich wrote:
> On 31.10.2023 23:06, Indu Bhagat wrote:
>> On 10/31/23 04:28, Jan Beulich wrote:
>>> On 30.10.2023 17:51, Indu Bhagat wrote:
>>>> +void
>>>> +scfi_dot_cfi_startproc (symbolS *start_sym)
>>>
>>> This and the following two functions presently have no caller, and I
>>> also can't spot equivalents in dw2gencfi.c. How are they (going) to be
>>> used? This ...
>>>
>>>> +{
>>>> +  if (frchain_now->frch_cfi_data != NULL)
>>>> +    {
>>>> +      as_bad (_("previous CFI entry not closed (missing .cfi_endproc)"));
>>>
>>> ... for example suggests that the function here might really be the
>>> handler for .cfi_startproc, yet the table above says .cfi_startproc is
>>> ignored.
>>>
>>
>> The callers of scfi_dot_cfi_startproc (), scfi_dot_cfi_endproc () and
>> scfi_dot_cfi () are in scfi.c, when its time to emit DWARF CFI after
>> SCFI machinery has generated the SCFI Ops (See scfi_dot_cfi () and
>> scfi_emit_dw2cfi ()).  The callers are added in the next patch in the
>> series, "[PATCH, V2 07/10] gas: synthesize CFI for hand-written asm".
> 
> But that's not in the context of processing a .cfi_* directive. IOW the
> message is properly misleading.
> 

Makes sense. This will usually arise when something goes amiss with the 
SCFI machinery.

I will reword the message.

Indu


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

* Re: [PATCH, V2 09/10] gas: testsuite: add a x86_64 testsuite for SCFI
  2023-11-02 12:28       ` Jan Beulich
@ 2023-11-03  5:45         ` Indu Bhagat
  0 siblings, 0 replies; 30+ messages in thread
From: Indu Bhagat @ 2023-11-03  5:45 UTC (permalink / raw)
  To: Jan Beulich; +Cc: binutils

On 11/2/23 05:28, Jan Beulich wrote:
> On 01.11.2023 07:24, Indu Bhagat wrote:
>> On 10/31/23 09:13, Jan Beulich wrote:
>>> On 30.10.2023 17:51, Indu Bhagat wrote:
>>>> --- /dev/null
>>>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-add-2.s
>>>> @@ -0,0 +1,48 @@
>>>> +        .section        .rodata
>>>> +	.type	simd_cmp_op, @object
>>>> +	.size	simd_cmp_op, 8
>>>> +simd_cmp_op:
>>>> +	.long	2
>>>> +	.zero	4
>>>> +
>>>> +# Testcase for add instruction.
>>>> +# add reg, reg instruction
>>>> +	.text
>>>> +	.globl	foo
>>>> +	.type	foo, @function
>>>> +foo:
>>>> +	.cfi_startproc
>>>> +	pushq	%rbp
>>>> +	.cfi_def_cfa_offset 16
>>>> +	.cfi_offset 6, -16
>>>> +	movq    %rsp, %rbp
>>>> +	.cfi_def_cfa_register 6
>>>> +	pushq	%r12
>>>> +	.cfi_offset 12, -24
>>>> +	mov	%rsp, %r12
>>>
>>> You copy %rsp to %r12 here, and ...
>>>
>>>> +# Stack manipulation is permitted if the base register for
>>>> +# tracking CFA has been changed to FP.
>>>> +	addq    %rdx, %rsp
>>>> +	addq	%rsp, %rax
>>>> +# Some add instructions may access the stack indirectly.  Such
>>>> +# accesses do not make REG_FP untraceable.
>>>> +	addl    %eax, -84(%rbp)
>>>> +# Other kind of add instructions should not error out in the
>>>> +# x86_64 -> ginsn translator
>>>> +	addq    $simd_cmp_op+8, %rdx
>>>> +	addl    %edx, -32(%rsp)
>>>> +	addl    $1, fb_low_counter(,%rbx,4)
>>>> +	mov	%r12, %rsp
>>>
>>> ... you restore it here, but both without any .cfi_* annotation.
>>> It is therefore unclear whether this is in any way related to ...
>>>
>>
>> There are no .cfi_* annotations for the %rsp updates here because the
>> CFA tracking has already been switched to REG_FP based tracking.  From
>> the perspective of DWARF CFI, this usages of the stack do not need to
>> tracked.  If unwinding happens from any instruction in the above
>> sequence, we already have the correct and complete unwind information.
>>
>>>> +# Popping a callee-saved register.
>>>> +# RSP must be traceable.
>>>> +	pop     %r12
>>>> +	.cfi_restore 12
>>>
>>> ... what the comment says about these.
>>>
>>
>> The comment here means that hand-written programs may use stack for
>> their local usage etc. but must ensure that before or in the epilogue,
>> rsp is restored to the desired value (before register restores via pop
>> instructions).
> 
> And how exactly do you tell that the "mov %r12, %rsp" is a restore, not
> the setting of an arbitrary new %rsp value?
> 

It checks that there must have been a "mov %rsp, %r12" seen earlier. 
The SCFI machinery caches such a save of stack pointer state.

>> This is not a special handling in the SCFI machinery but it is what is
>> needed to ensure correctness of the function.  For asm not adhering to
>> the ABI/calling conventions, SCFI cannot be used.
> 
> But the calling convention doesn't go as far as dictating local frame
> layout in a function, I don't think?
> 

No you are right on that one.

In this example, we only concern ourselves only with the callee-saved 
registers and their save/restores; the components that affect the CFI 
annotations.  The local stack usage is not of interest per se; the 
program is allowed to allocate and use as it pleases.

>>>> +	leave
>>>> +	.cfi_def_cfa_register 7
>>>> +	.cfi_restore 6
>>>
>>> Using numbers here isn't very helpful, I'm afraid.
>>>
>>
>> I am not sure I understand.  The DWARF register numbers and the offset
>> values are required by the semantics of those CFI directives.
> 
> Offsets are (largely) unavoidable to be numerics, yes. But .cfi_*
> directives support register names, and using them is (imo) far better
> than Dwarf register numbers - you may have memorized them by now, but
> others likely won't.
> 

Ah ok. Yeah I can use register numbers.

>>>> --- /dev/null
>>>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.l
>>>> @@ -0,0 +1,2 @@
>>>> +.*Assembler messages:
>>>> +.*9: Warning: --scfi=all ignores some user-specified CFI directives
>>>
>>> Is repeating this for (about?) every test really necessary / useful?
>>> If multiple passes for every test were to make sense to me, I'd expect
>>> one pass with SCFI and one pass without, where the directives then
>>> take effect. And then the same set of expectations should match.
>>>
>>
>> re: is repeating this useful? Strictly speaking no. _But_, I think as
>> the code evolves, we may add more diagnostics to the SCFI machinery.
>> Explicitly checking for the set of warnings helps us ensure no new
>> warnings show up where they are not expected. In a way, it is a stricter
>> check and ensures no unintented slippage.
> 
> Well, okay, that's fair. You didn't respond to the other part of my
> comment, though.
> 

Sorry I missed that.  Yes, we could do two runs for the meaningful 
tests, one with --scfi and another without. That should be useful.  I 
recall there was only one case (dont recollect which one) where the 
generated FDEs were reversed in order with --scfi, but it was not a 
functional difference.  Other than that test, rest all should match.

>>>> --- /dev/null
>>>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.s
>>>> @@ -0,0 +1,49 @@
>>>> +# Testcase for switching between sp/fp based CFA.
>>>> +	.text
>>>> +	.globl	foo
>>>> +	.type	foo, @function
>>>> +foo:
>>>> +        .cfi_startproc
>>>> +        pushq   %r14
>>>> +        .cfi_def_cfa_offset 16
>>>> +        .cfi_offset 14, -16
>>>> +        pushq   %r13
>>>> +        .cfi_def_cfa_offset 24
>>>> +        .cfi_offset 13, -24
>>>> +        pushq   %r12
>>>> +        .cfi_def_cfa_offset 32
>>>> +        .cfi_offset 12, -32
>>>> +        pushq   %rbp
>>>> +        .cfi_def_cfa_offset 40
>>>> +        .cfi_offset 6, -40
>>>> +        pushq   %rbx
>>>> +        .cfi_def_cfa_offset 48
>>>> +        .cfi_offset 3, -48
>>>> +        movq    %rdi, %rbx
>>>> +        subq    $32, %rsp
>>>> +        .cfi_def_cfa_offset 80
>>>> +# This mov does not switch CFA tracking to REG_FP, because there has already
>>>> +# been stack usage between here and the push %rbp
>>>> +        movq    %rsp, %rbp
>>>
>>> Yet another constraint?
>>>
>>
>> Not sure I understand. This is not a constraint for SCFI.  A pattern
>> where the user does:
>>       push %rbp
>>       .. more stack usage..
>>       movq %rsp, %rbp
>> is not using frame pointer for stack tracing, but simply as a scratch
>> register.
> 
> Why might it not be? We're talking about hand-written assembly, and
> assembly writers are free to e.g. first push all (necessary) callee-
> saved register and then copy %rsp into %rbp. For a function with many
> stack locals this has the benefit of allowing more of them to be
> accessed via <disp8>(%rbp).
> 

You are right on this one. It should be allowed. The mov %rsp, %rbp 
should switch to REG_FP based CFA in this example. I will fix it.

>>>> --- /dev/null
>>>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.d
>>>> @@ -0,0 +1,36 @@
>>>> +#as: --scfi -W
>>>> +#objdump: -Wf
>>>> +#name: Synthesize CFI in presence of control flow 1
>>>> +#...
>>>> +Contents of the .eh_frame section:
>>>> +
>>>> +00000000 0+0014 0+0000 CIE
>>>> +  Version:               1
>>>> +  Augmentation:          "zR"
>>>> +  Code alignment factor: 1
>>>> +  Data alignment factor: -8
>>>> +  Return address column: 16
>>>> +  Augmentation data:     [01][abc]
>>>> +  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
>>>> +  DW_CFA_offset: r16 \(rip\) at cfa-8
>>>> +  DW_CFA_nop
>>>> +  DW_CFA_nop
>>>> +
>>>> +0+0018 0+0024 0000001c FDE cie=00000000 pc=0+0000..0+003a
>>>> +  DW_CFA_advance_loc: 1 to 0+0001
>>>> +  DW_CFA_def_cfa_offset: 16
>>>> +  DW_CFA_offset: r3 \(rbx\) at cfa-16
>>>> +  DW_CFA_advance_loc: 37 to 0+0026
>>>> +  DW_CFA_remember_state
>>>> +  DW_CFA_advance_loc: 1 to 0+0027
>>>> +  DW_CFA_restore: r3 \(rbx\)
>>>> +  DW_CFA_def_cfa_offset: 8
>>>> +  DW_CFA_advance_loc: 1 to 0+0028
>>>> +  DW_CFA_restore_state
>>>> +  DW_CFA_advance_loc: 9 to 0+0031
>>>> +  DW_CFA_restore: r3 \(rbx\)
>>>> +  DW_CFA_def_cfa_offset: 8
>>>> +  DW_CFA_nop
>>>> +#...
>>>> +
>>>> +#pass
>>>
>>> Seeing this recurring pattern (the last three lines): Why not just #pass?
>>>
>>
>> I thought adding #... is clearer as it indicates that the test knowingly
>> skips lines thereafter.
> 
> Well, #pass alone already says exactly this. If you didn't expect further
> output lines, you'd _omit_ #pass.
> 

OK.

>>>> --- /dev/null
>>>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.s
>>>> @@ -0,0 +1,47 @@
>>>> +# Testcase with one dominator bb and two exit bbs
>>>> +# Something like for: return ferror (f) || fclose (f) != 0;
>>>> +	.text
>>>> +	.section	.rodata.str1.1,"aMS",@progbits,1
>>>> +.LC0:
>>>> +	.string	"w"
>>>> +.LC1:
>>>> +	.string	"conftest.out"
>>>> +	.section	.text.startup,"ax",@progbits
>>>> +	.p2align 4
>>>> +	.globl	main
>>>> +	.type	main, @function
>>>> +main:
>>>> +.LFB11:
>>>
>>> Coming back to "hand-written assembly": The above looks very much like it
>>> was compiler output (earlier tests did, too, but it's perhaps more
>>> prominent here). That's not necessarily what a human might write, and
>>> hence I wonder about the overall coverage that can be gained that way.
>>>
>>
>> Yes, its inspired by compiler generated output. I ran into this when
>> running some tests. Its still a good basic cfg test for SCFI - a
>> function with two return paths.
> 
> You understand though that I used this only as (sutiable) example. My point
> being that hand-written assembly frequently doesn't resemble compiler-
> genertaed code. Otherwise, i.e. if the resulting code wasn't to be different,
> why would one write assembly code in nthe first place?
> 

True.
Open to suggestions on improving the testsuite.

>>>> --- /dev/null
>>>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-diag-1.s
>>>> @@ -0,0 +1,23 @@
>>>> +# Testcase for REG_FP based CFA
>>>> +# and using REG_FP as scratch.
>>>> +	.text
>>>> +	.globl	foo
>>>> +	.type	foo, @function
>>>> +foo:
>>>> +	.cfi_startproc
>>>> +	pushq	%rbp
>>>> +	.cfi_def_cfa_offset 16
>>>> +	.cfi_offset 6, -16
>>>> +	movq    %rsp, %rbp
>>>> +	.cfi_def_cfa_register 6
>>>> +# The following add causes REG_FP to become untraceable
>>>> +	addq	%rax, %rbp
>>>
>>> But this isn't a problem as long as FP isn't further used. Indeed ...
>>>
>>>> +	.cfi_def_cfa_register 7
>>>> +	pop	%rbp
>>>
>>> ... its original value is restored right afterwards. Yet another constraint?
>>>
>>
>> Hmm. I need some clarification on the statement "Yet another constraint".
> 
> By "constraint" I mean assumptions you make on the way assembly code is
> written, for your machinery to be usable. The more such assumptions, the
> smaller the set of code "eligible" to (future) use of your work. Hence
> also why I think all such "constraints" need to be spelled out in a
> single place, such that one can
> - verify that everything meeting these constraints actually also works,
> - check up front whether one's assembly code is actually suitable for
>    enabling --scfi.
> 

OK, Thanks. We are on the same page wrt "Constraints" then. In the above 
example, though, the "constraint" is due to the desire to be able 
unwind/stack trace asynchronously. Which is why it confused me in the 
first place, I think I wouldn't call this a "constraint" here. But this 
is a subjective line.

In the above example, the 'addq    %rax, %rbp' does cause a problem for 
unwinding because the CFA is REG_FP based. And the SCFI machinery needs 
to be able to tell what offset from REG_FP is the CFA. I think its 
correct for the SCFI machinery to complain with a ".*14: Error: SCFI: 
usage of REG_FP as scratch not supported" right away, because the unwind 
information at the next instruction is already affected.

That said, I do agree that the constraints need to be spelled out in a 
single place. I already have done some of it in scfi.c, but there is 
room for improvement, I see.

>>>> --- /dev/null
>>>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.s
>>>> @@ -0,0 +1,55 @@
>>>> +# Testcase for a diagnostic around assymetrical restore
>>>> +# Testcase inspired by byte_insert_op1 in libiberty
>>>> +# False positive for the diagnostic
>>>> +.type	foo, @function
>>>> +foo:
>>>> +.LFB10:
>>>> +	.cfi_startproc
>>>> +	endbr64
>>>> +	pushq	%rbp
>>>> +	.cfi_def_cfa_offset 16
>>>> +	.cfi_offset 6, -16
>>>> +	movq	%rsp, %rbp
>>>> +	.cfi_def_cfa_register 6
>>>> +	pushq	%r12
>>>> +	pushq	%rbx
>>>> +	subq	$24, %rsp
>>>> +	.cfi_offset 12, -24
>>>> +	.cfi_offset 3, -32
>>>> +	movl	%edi, -20(%rbp)
>>>> +	movq	%rsi, -32(%rbp)
>>>> +	movl	%edx, -24(%rbp)
>>>> +	movq	%rcx, -40(%rbp)
>>>> +# The assembler cannot differentiate that the following
>>>> +# mov to %rbx is not a true restore operation, but simply
>>>> +# %rbx register usage as a scratch reg of some sort.
>>>> +# The assembler merely warns of a possible assymetric restore operation
>>>> +# In this case, its noise for the user unfortunately.
>>>> +	movq	-40(%rbp), %rbx
>>>> +	movq	-40(%rbp), %rax
>>>> +	leaq	3(%rax), %r12
>>>> +	jmp	.L563
>>>> +.L564:
>>>> +	subq	$1, %rbx
>>>> +	subq	$1, %r12
>>>> +	movzbl	(%rbx), %eax
>>>> +	movb	%al, (%r12)
>>>> +.L563:
>>>> +	cmpq	-32(%rbp), %rbx
>>>> +	jne	.L564
>>>
>>> But this is pretty common usage. Imo this (the false positive warning)
>>> cannot remain like this.
>>>
>>
>> The warning of "Warning: SCFI: asymetrical register restore" was added
>> to alert user if they do something like
>>       pushq %rbx
>>       pushq %r12
>>       ...
>>       popq  %rbx
>>       popq  %r12
>> i.e., asymmetric save and restore.  Now the user may do a restore to any
>> register and use what was saved on stack as scratch regiser; the only
>> way to differentiate between a "true reg restore in epilogue" vs "reg
>> restored for scratch purposes" is to know whether its the epilogue.  GAS
>> will not be able to determine that easily.
>>
>> We could remove the warning altogether.  I just thought it provides
>> users with some protection / validation of hand-written asm.
> 
> I'm in favor of such assisting warnings, but they're useful only if there
> are few false positives and few false negatives. Too many warnings makes
> people ignore them all (or shut them off), while too few makes people
> mistrust them being helpful.
> 

Noted. And I do agree. I just so happens that I dont have a solution to 
this problem of false positive warnings yet.

>>>> --- /dev/null
>>>> +++ b/gas/testsuite/gas/scfi/x86_64/scfi-x86-64.exp
>>>> @@ -0,0 +1,103 @@
>>>> +# Copyright (C) 2022-2023 Free Software Foundation, Inc.
>>>> +
>>>> +# This program is free software; you can redistribute it and/or modify
>>>> +# it under the terms of the GNU General Public License as published by
>>>> +# the Free Software Foundation; either version 3 of the License, or
>>>> +# (at your option) any later version.
>>>> +#
>>>> +# This program is distributed in the hope that it will be useful,
>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>>> +# GNU General Public License for more details.
>>>> +#
>>>> +# You should have received a copy of the GNU General Public License
>>>> +# along with this program; if not, write to the Free Software
>>>> +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
>>>> +
>>>> +if { ![is_elf_format] } then {
>>>> +    return
>>>> +}
>>>> +
>>>> +# common tests
>>>> +if  { ([istarget "x86_64-*-*"]) } then {
>>>> +
>>>> +    global ASFLAGS
>>>> +    set old_ASFLAGS "$ASFLAGS"
>>>> +
>>>> +    run_dump_test "scfi-cfi-label-1"
>>>> +    run_list_test "scfi-cfi-label-1" "--scfi --warn"
>>>> +
>>>> +    run_list_test "scfi-diag-1" "--scfi"
>>>> +    run_list_test "scfi-fp-diag-2" "--scfi"
>>>> +    run_list_test "scfi-diag-2" "--scfi"
>>>> +
>>>> +    run_list_test "scfi-unsupported-1" "--32 --scfi"
>>>
>>> Perhaps a 2nd run with --x32, unless (see above) you choose to support
>>> that ABI as well?
>>>
>>
>> At the moment, the plan was to next work on --scfi=inline and add
>> support for aarch64/aapcs64 ABIs.  No plans to add support for x32
>> unless a need arises.
> 
> But that's precisely my point: As long as you don't support --x32, you
> want to check that its use is properly diagnosed (just like use of
> --32 is).
> 

Ah, apologies I construed this comment differently earlier. Yes, I agree 
we can add another run with --x32.


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

* Re: [PATCH, V2 07/10] gas: synthesize CFI for hand-written asm
  2023-11-02 15:53   ` Jan Beulich
@ 2023-11-04  7:29     ` Indu Bhagat
  2023-11-06 11:03       ` Jan Beulich
  0 siblings, 1 reply; 30+ messages in thread
From: Indu Bhagat @ 2023-11-04  7:29 UTC (permalink / raw)
  To: Jan Beulich; +Cc: binutils

On 11/2/23 08:53, Jan Beulich wrote:
> On 30.10.2023 17:51, Indu Bhagat wrote:
>> --- /dev/null
>> +++ b/gas/ginsn.c
>> @@ -0,0 +1,1225 @@
>> +/* ginsn.h - GAS instruction representation.
>> +   Copyright (C) 2023 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 "ginsn.h"
>> +#include "scfi.h"
>> +
>> +#ifdef TARGET_USE_GINSN
>> +
>> +const char *const ginsn_type_names[] =
> 
> This looks like it wants to be static.
> 

OK.

>> +{
>> +#define _GINSN_TYPE_ITEM(NAME, STR) STR,
>> +  _GINSN_TYPES
>> +#undef _GINSN_TYPE_ITEM
>> +};
>> +
>> +const char *const ginsn_src_type_names[] =
> 
> This and ...
> 
>> +{
>> +#define _GINSN_SRC_TYPE_ITEM(NAME, STR) STR,
>> +  _GINSN_SRC_TYPES
>> +#undef _GINSN_SRC_TYPE_ITEM
>> +};
>> +const char *const ginsn_dst_type_names[] =
> 
> ... this also look so, but then they're also unused inside this CU,
> so when making them static the compiler would complain. If they're
> needed by a later patch, perhaps they should be moved there? And
> if entirely unused, perhaps they should both be deleted (or at least
> commented / #ifdef-ed out)?
> 

Remains of some previous implementation that I discarded. I will remove 
these.

> With the many symbols further down it's also hard to spot whether any
> of them should perhaps also be static.
> 
>> +{
>> +#define _GINSN_DST_TYPE_ITEM(NAME, STR) STR,
>> +  _GINSN_DST_TYPES
>> +#undef _GINSN_DST_TYPE_ITEM
>> +};
>> +
>> +static
>> +ginsnS *ginsn_alloc (void)
>> +{
>> +  ginsnS *ginsn = XCNEW (ginsnS);
>> +  return ginsn;
>> +}
>> +
>> +static ginsnS*
>> +ginsn_init (enum ginsn_type type, symbolS *sym, bool real_p)
>> +{
>> +  ginsnS *ginsn = ginsn_alloc ();
>> +  ginsn->type = type;
>> +  ginsn->sym = sym;
> 
> Is a symbol hanging off of a ginsn ever intended to be altered? If
> not, field and function argument would want to be pointer-to-const.
> 

No. This symbol is not intended to be altered.

However, using const symbolS* will cause complaints of "passing argument 
1 of ‘S_GET_NAME’ discards ‘const’ qualifier" etc.  Calling most of the 
S_XXX (symbolS *sym) will need some type casting etc, but that will 
somewhat defeat the purpose?

>> +  if (real_p)
>> +    ginsn->flags |= GINSN_F_INSN_REAL;
>> +  return ginsn;
>> +}
>> +
>> +static void
>> +ginsn_cleanup (ginsnS **ginsnp)
>> +{
>> +  ginsnS *ginsn;
>> +
>> +  if (!ginsnp || !*ginsnp)
>> +    return;
>> +
>> +  ginsn = *ginsnp;
>> +  if (ginsn->scfi_ops)
>> +    {
>> +      scfi_ops_cleanup (ginsn->scfi_ops);
>> +      ginsn->scfi_ops = NULL;
>> +    }
>> +
>> +  free (ginsn);
>> +  ginsn = NULL;
> 
> This doesn't do anything useful. Did you perhaps mean to set *ginsnp to NULL,
> and then also ahead of calling free()?
> 

Thanks. Yes I meant to do *ginsnp = NULL;

>> +}
>> +
>> +static void
>> +ginsn_set_src (struct ginsn_src *src, enum ginsn_src_type type, uint32_t reg,
>> +	       int32_t immdisp)
> 
> I find the use of fixed-width types suspicious here: For reg, likely it wants
> to be unsigned int. For immdisp it's less clear: Immediates can be wider than
> 32 bits, and they may also be either signed ot unsigned. From an abstract
> perspective, assuming the immediate value actually is used for anything, I'd
> expect offsetT to be used, following struct expressionS' X_add_number.
> 

Thanks, I will consider trying offsetT for immediate. It is a more 
appropriate data type than int32_t.  For reg, why is uint32_t less 
appropriate than unsigned int ?

>> +{
>> +  if (!src)
>> +    return;
>> +
>> +  src->type = type;
>> +  /* Even when the use-case is SCFI, the value of reg may be > SCFI_NUM_REGS.
>> +     E.g., in AMD64, push fs etc.  */
>> +  src->reg = reg;
>> +
>> +  if (type == GINSN_SRC_IMM || type == GINSN_SRC_INDIRECT)
>> +    src->immdisp = immdisp;
> 
> What's the point of the conditional around the assignment?
> 

Stale conditional. I earlier had some data structures with union etc. 
which I have since gotten rid of.

>> +ginsnS *
>> +ginsn_new_add (symbolS *sym, bool real_p,
>> +	       enum ginsn_src_type src1_type, uint32_t src1_val, int32_t src1_disp,
>> +	       enum ginsn_src_type src2_type, uint32_t src2_val, int32_t src2_disp,
>> +	       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp)
>> +{
>> +  ginsnS *ginsn = ginsn_init (GINSN_TYPE_ADD, sym, real_p);
>> +  /* src info.  */
>> +  ginsn_set_src (&ginsn->src[0], src1_type, src1_val, src1_disp);
>> +  ginsn_set_src (&ginsn->src[1], src2_type, src2_val, src2_disp);
>> +  /* dst info.  */
>> +  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
>> +
>> +  return ginsn;
>> +}
>> +
>> +ginsnS *
>> +ginsn_new_and (symbolS *sym, bool real_p,
>> +	       enum ginsn_src_type src1_type, uint32_t src1_val, int32_t src1_disp,
>> +	       enum ginsn_src_type src2_type, uint32_t src2_val, int32_t src2_disp,
>> +	       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp)
>> +{
>> +  ginsnS *ginsn = ginsn_init (GINSN_TYPE_AND, sym, real_p);
>> +  /* src info.  */
>> +  ginsn_set_src (&ginsn->src[0], src1_type, src1_val, src1_disp);
>> +  ginsn_set_src (&ginsn->src[1], src2_type, src2_val, src2_disp);
>> +  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
>> +
>> +  return ginsn;
>> +}
> 
> The comments in the two functions don't look overly useful to me. But if
> you have them, please have them be consistent across similar functions.
> 

OK.

>> +ginsnS *
>> +ginsn_new_mov (symbolS *sym, bool real_p,
>> +	       enum ginsn_src_type src_type, uint32_t src_reg, int32_t src_disp,
>> +	       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp)
>> +{
>> +  ginsnS *ginsn = ginsn_init (GINSN_TYPE_MOV, sym, real_p);
>> +  /* src info.  */
>> +  ginsn_set_src (&ginsn->src[0], src_type, src_reg, src_disp);
>> +  /* dst info.  */
>> +  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
>> +
>> +  return ginsn;
>> +}
> 
> As indicated before, if both src and dst can be indirect here, ...
> 
>> +ginsnS *
>> +ginsn_new_store (symbolS *sym, bool real_p,
>> +		 enum ginsn_src_type src_type, uint32_t src_reg,
>> +		 enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp)
>> +{
>> +  ginsnS *ginsn = ginsn_init (GINSN_TYPE_STORE, sym, real_p);
>> +  /* src info.  */
>> +  ginsn_set_src (&ginsn->src[0], src_type, src_reg, 0);
>> +  /* dst info.  */
>> +  gas_assert (dst_type == GINSN_DST_INDIRECT);
>> +  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
>> +
>> +  return ginsn;
>> +}
>> +
>> +ginsnS *
>> +ginsn_new_load (symbolS *sym, bool real_p,
>> +		enum ginsn_src_type src_type, uint32_t src_reg, int32_t src_disp,
>> +		enum ginsn_dst_type dst_type, uint32_t dst_reg)
>> +{
>> +  ginsnS *ginsn = ginsn_init (GINSN_TYPE_LOAD, sym, real_p);
>> +  /* src info.  */
>> +  gas_assert (src_type == GINSN_SRC_INDIRECT);
>> +  ginsn_set_src (&ginsn->src[0], src_type, src_reg, src_disp);
>> +  /* dst info.  */
>> +  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, 0);
>> +
>> +  return ginsn;
>> +}
> 
> ... I can't see what these are needed for.
> 

For x86, they may not seem necessary. But for other architectures, or 
say for future uses-cases, we may need them. I think it is more 
meaningful (and readable) to see a LOAD/STORE/MOV ginsn for a machine 
instruction of the same type.  For other RISC-like ISAs, it is clearer 
to have separate MOV/LOAD/STORE instructions.

ginsn is meant to provide an infrastructure for other uses cases that 
may crop up later.

>> +ginsnS *
>> +ginsn_new_sub (symbolS *sym, bool real_p,
>> +	       enum ginsn_src_type src1_type, uint32_t src1_val, int32_t src1_disp,
>> +	       enum ginsn_src_type src2_type, uint32_t src2_val, int32_t src2_disp,
>> +	       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp)
>> +{
>> +  ginsnS *ginsn = ginsn_init (GINSN_TYPE_SUB, sym, real_p);
>> +  /* src info.  */
>> +  ginsn_set_src (&ginsn->src[0], src1_type, src1_val, src1_disp);
>> +  ginsn_set_src (&ginsn->src[1], src2_type, src2_val, src2_disp);
>> +  /* dst info.  */
>> +  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
>> +
>> +  return ginsn;
>> +}
>> +
>> +/* PS: Note this API does not identify the displacement values of
>> +   src1/src2/dst.  At this time, it is unnecessary for correctness to support
>> +   the additional argument.  */
> 
> And why do the displacements need tracking for add, and, and sub?
> 

Hmm, I think its just a bad choice of arg name.  The value passed in 
src1_disp / src2_disp is actually the immediates in case add/sub/and. I 
will update it.

>> +static bool
>> +ginsn_indirect_jump_p (ginsnS *ginsn)
>> +{
>> +  bool ret_p = false;
>> +  if (!ginsn)
>> +    return ret_p;
>> +
>> +  ret_p = (ginsn->type == GINSN_TYPE_JUMP
>> +	   && ginsn->src[0].type == GINSN_SRC_REG);
> 
> On x86 an indirect jump can also have a memory source operand (i.e. what
> you call "indirect").
> 

IIRC, This is another case of acceptable loss of information in ginsns. 
I will double check this too when I get to fixing up the tc-i386.c to 
address those review comments for the ginsn creation process.

>> +static bool
>> +ginsn_direct_local_jump_p (ginsnS *ginsn)
>> +{
>> +  bool ret_p = false;
>> +  if (!ginsn)
>> +    return ret_p;
>> +
>> +  ret_p |= (ginsn->type == GINSN_TYPE_JUMP
>> +	    && ginsn->src[0].type == GINSN_SRC_SYMBOL
>> +	    && S_IS_LOCAL (ginsn->src[0].sym));
> 
> This looks fragile: You may not yet have seen the .globl or .weak directive.
> 

Hmm. I think the usage of S_IS_LOCAL is incorrect here. Further, I 
already have a defined label -> ginsn map per function block, and the 
checks with S_IS_LOCAL is not necessary really.  Will take a second look.

>> +static char*
>> +ginsn_src_print (struct ginsn_src *src)
>> +{
>> +  size_t len = 39;
>> +  char *src_str = XNEWVEC (char, len);
>> +
>> +  memset (src_str, 0, len);
>> +
>> +  if (src->type == GINSN_SRC_REG)
>> +    {
>> +      char *buf = XNEWVEC (char, 32);
>> +      sprintf (buf, "%%r%d, ", ginsn_get_src_reg (src));
>> +      strcat (src_str, buf);
>> +    }
>> +  else if (src->type == GINSN_SRC_IMM)
>> +    {
>> +      char *buf = XNEWVEC (char, 32);
>> +      sprintf (buf, "%d, ", ginsn_get_src_imm (src));
> 
> Here and below, if you stick to int32_t as the type, this wants to use
> PRId32.
> 

OK.

>> +static const char*
>> +ginsn_type_sym_begin_end_print (ginsnS *ginsn)
>> +{
>> +  int id = 0;
>> +  const char *ginsn_sym_strs[]
>> +    = { "", "FUNC_BEGIN", "FUNC_END" };
> 
> static and with a 2nd const?
> 

OK.

>> +static char*
>> +ginsn_print (ginsnS *ginsn)
>> +{
>> +  struct ginsn_src *src;
>> +  struct ginsn_dst *dst;
>> +  size_t len = GINSN_LISTING_LEN;
>> +  char *ginsn_str = XNEWVEC (char, len);
>> +
>> +  memset (ginsn_str, 0, len);
>> +
>> +  strcpy (ginsn_str, "ginsn: ");
>> +
>> +  strcat (ginsn_str, ginsn_type_names[ginsn->type]);
>> +  strcat (ginsn_str, " ");
>> +
>> +  /* For some ginsn types, no further information is printed for now.  */
>> +  if (ginsn->type == GINSN_TYPE_CALL
>> +      || ginsn->type == GINSN_TYPE_RETURN
>> +      || ginsn->type == GINSN_TYPE_OTHER)
>> +    goto end;
>> +  else if (ginsn->type == GINSN_TYPE_SYMBOL)
>> +    {
>> +      if (GINSN_F_USER_LABEL_P (ginsn))
>> +	strncat (ginsn_str, S_GET_NAME (ginsn->sym), len - 10);
>> +      else
>> +	strcat (ginsn_str, ginsn_type_sym_begin_end_print (ginsn));
>> +      goto end;
>> +    }
>> +
>> +  /* src 1.  */
>> +  src = ginsn_get_src1 (ginsn);
>> +  strcat (ginsn_str, ginsn_src_print (src));
>> +
>> +  /* src 2.  */
>> +  src = ginsn_get_src2 (ginsn);
>> +  strcat (ginsn_str, ginsn_src_print (src));
>> +
>> +  /* dst.  */
>> +  dst = ginsn_get_dst (ginsn);
>> +  strcat (ginsn_str, ginsn_dst_print (dst));
>> +
>> +end:
>> +  gas_assert (strlen (ginsn_str) < GINSN_LISTING_LEN);
> 
> This check comes too late - if it triggers, you've corrupted memory
> already. I wonder anyway why you construct all this "by hand", rather
> than using snprintf().
> 

I will update this to use snprintf instead.

Thanks for the review.
Indu


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

* Re: [PATCH, V2 07/10] gas: synthesize CFI for hand-written asm
  2023-11-04  7:29     ` Indu Bhagat
@ 2023-11-06 11:03       ` Jan Beulich
  2023-12-10 10:23         ` Indu Bhagat
  0 siblings, 1 reply; 30+ messages in thread
From: Jan Beulich @ 2023-11-06 11:03 UTC (permalink / raw)
  To: Indu Bhagat; +Cc: binutils

On 04.11.2023 08:29, Indu Bhagat wrote:
> On 11/2/23 08:53, Jan Beulich wrote:
>> On 30.10.2023 17:51, Indu Bhagat wrote:
>>> +static ginsnS*
>>> +ginsn_init (enum ginsn_type type, symbolS *sym, bool real_p)
>>> +{
>>> +  ginsnS *ginsn = ginsn_alloc ();
>>> +  ginsn->type = type;
>>> +  ginsn->sym = sym;
>>
>> Is a symbol hanging off of a ginsn ever intended to be altered? If
>> not, field and function argument would want to be pointer-to-const.
>>
> 
> No. This symbol is not intended to be altered.
> 
> However, using const symbolS* will cause complaints of "passing argument 
> 1 of ‘S_GET_NAME’ discards ‘const’ qualifier" etc.  Calling most of the 
> S_XXX (symbolS *sym) will need some type casting etc, but that will 
> somewhat defeat the purpose?

Well, yes, of course you shouldn't be casting away const-ness. I've made
a patch to adjust S_GET_NAME(), which I'll post after having run a full
set of tests on it.

>>> +static void
>>> +ginsn_set_src (struct ginsn_src *src, enum ginsn_src_type type, uint32_t reg,
>>> +	       int32_t immdisp)
>>
>> I find the use of fixed-width types suspicious here: For reg, likely it wants
>> to be unsigned int. For immdisp it's less clear: Immediates can be wider than
>> 32 bits, and they may also be either signed ot unsigned. From an abstract
>> perspective, assuming the immediate value actually is used for anything, I'd
>> expect offsetT to be used, following struct expressionS' X_add_number.
>>
> 
> Thanks, I will consider trying offsetT for immediate. It is a more 
> appropriate data type than int32_t.  For reg, why is uint32_t less 
> appropriate than unsigned int ?

Fixed-width types come with a price: In principle they may not even be
available, and they may also not be the most efficient types to deal with
for an architecture. Therefore my rule of thumb is that they're best
used only for "describing" interfaces. As long as (here) register numbers
will fit in an unsigned int, using that basic type looks more appropriate
to me.

>>> +ginsnS *
>>> +ginsn_new_mov (symbolS *sym, bool real_p,
>>> +	       enum ginsn_src_type src_type, uint32_t src_reg, int32_t src_disp,
>>> +	       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp)
>>> +{
>>> +  ginsnS *ginsn = ginsn_init (GINSN_TYPE_MOV, sym, real_p);
>>> +  /* src info.  */
>>> +  ginsn_set_src (&ginsn->src[0], src_type, src_reg, src_disp);
>>> +  /* dst info.  */
>>> +  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
>>> +
>>> +  return ginsn;
>>> +}
>>
>> As indicated before, if both src and dst can be indirect here, ...
>>
>>> +ginsnS *
>>> +ginsn_new_store (symbolS *sym, bool real_p,
>>> +		 enum ginsn_src_type src_type, uint32_t src_reg,
>>> +		 enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp)
>>> +{
>>> +  ginsnS *ginsn = ginsn_init (GINSN_TYPE_STORE, sym, real_p);
>>> +  /* src info.  */
>>> +  ginsn_set_src (&ginsn->src[0], src_type, src_reg, 0);
>>> +  /* dst info.  */
>>> +  gas_assert (dst_type == GINSN_DST_INDIRECT);
>>> +  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
>>> +
>>> +  return ginsn;
>>> +}
>>> +
>>> +ginsnS *
>>> +ginsn_new_load (symbolS *sym, bool real_p,
>>> +		enum ginsn_src_type src_type, uint32_t src_reg, int32_t src_disp,
>>> +		enum ginsn_dst_type dst_type, uint32_t dst_reg)
>>> +{
>>> +  ginsnS *ginsn = ginsn_init (GINSN_TYPE_LOAD, sym, real_p);
>>> +  /* src info.  */
>>> +  gas_assert (src_type == GINSN_SRC_INDIRECT);
>>> +  ginsn_set_src (&ginsn->src[0], src_type, src_reg, src_disp);
>>> +  /* dst info.  */
>>> +  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, 0);
>>> +
>>> +  return ginsn;
>>> +}
>>
>> ... I can't see what these are needed for.
>>
> 
> For x86, they may not seem necessary. But for other architectures, or 
> say for future uses-cases, we may need them. I think it is more 
> meaningful (and readable) to see a LOAD/STORE/MOV ginsn for a machine 
> instruction of the same type.  For other RISC-like ISAs, it is clearer 
> to have separate MOV/LOAD/STORE instructions.
> 
> ginsn is meant to provide an infrastructure for other uses cases that 
> may crop up later.

But then I consider it as odd that you munge loads/stores on x86 into
ginsn_new_mov(), by using "indirect" operands. Imo it would be better
to be consistent here, one way or the other.

Jan

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

* Re: [PATCH, V2 07/10] gas: synthesize CFI for hand-written asm
  2023-11-02 11:39       ` Jan Beulich
@ 2023-12-10 10:22         ` Indu Bhagat
  2023-12-11  7:59           ` Jan Beulich
  0 siblings, 1 reply; 30+ messages in thread
From: Indu Bhagat @ 2023-12-10 10:22 UTC (permalink / raw)
  To: Jan Beulich; +Cc: binutils

Hi Jan,

Apologies for the delay (was traveling). I finally got around to work on 
these review items (Will post a V3 soon).

On 11/2/23 04:39, Jan Beulich wrote:
> On 02.11.2023 09:15, Indu Bhagat wrote:
>> On 10/31/23 07:10, Jan Beulich wrote:
>>> On 30.10.2023 17:51, Indu Bhagat wrote:
>>>> gas/
>>>> 	* Makefile.am: Add new files.
>>>> 	* Makefile.in: Regenerated.
>>>> 	* as.c (defined): Handle documentation and listing option for
>>>> 	ginsns and SCFI.
>>>> 	* config/obj-elf.c (obj_elf_size): Invoke ginsn_data_end.
>>>> 	(obj_elf_type): Invoke ginsn_data_begin.
>>>> 	* config/tc-i386.c (ginsn_new): New functionality to generate
>>>> 	ginsns.
>>>> 	(x86_scfi_callee_saved_p): New function.
>>>> 	(ginsn_dw2_regnum): Likewise.
>>>> 	(ginsn_set_where): Likewise.
>>>> 	(x86_ginsn_alu): Likewise.
>>>> 	(x86_ginsn_move): Likewise.
>>>> 	(x86_ginsn_lea): Likewise.
>>>> 	(x86_ginsn_jump): Likewise.
>>>> 	(x86_ginsn_jump_cond): Likewise.
>>>> 	(md_assemble): Invoke ginsn_new.
>>>> 	(s_insn): Likewise.
>>>> 	(i386_target_format): Add hard error for usage of --scfi with non AMD64 ABIs.
>>>> 	* config/tc-i386.h (TARGET_USE_GINSN): New definition.
>>>> 	(TARGET_USE_SCFI): Likewise.
>>>> 	(SCFI_NUM_REGS): Likewise.
>>>> 	(REG_FP): Likewise.
>>>> 	(REG_SP): Likewise.
>>>> 	(SCFI_INIT_CFA_OFFSET): Likewise.
>>>> 	(SCFI_CALLEE_SAVED_REG_P): Likewise.
>>>> 	(x86_scfi_callee_saved_p): Likewise.
>>>
>>> For this arch-specific code there's a fundamental question of maintenance
>>> cost here: It doesn't look very reasonable to me to demand of people adding
>>> support for new ISA extensions to also take into consideration all of this
>>> new machinery. Yet if any such addition affects SCFI, things will go out-
>>> of-sync without updating this code as well. It may not be very often that
>>> such updating is necessary, but right now there's APX work in progress
>>> which I expect will need dealing with here as well.
>>>
>>
>> I understand your concerns.  FWIW, for SCFI, we will need to translate
>> only a subset of instructions into ginsns (change of flow insns,
>> save/restore and arith on REG_SP/REG_FP).
> 
> Considering what you say further down regarding untraceability, I'm afraid
> you will need to care about all insns which have SP/FP as their destination.
> 

Yeah, All insns which have SP/FP as their destination must be seen by 
the SCFI machinery. If any instruction which has SP/FP as destination is 
missed, and the user has specified a --scfi command line argument, my 
thinking is that GAS should spit out an error or a diagnostic.

That said, the current implementation handles the most commonly used 
instructions to manage stack pointer for dynamic and static stack 
allocation. For unhandled instructions which end up _possibly_ affecting 
REG_FP/REG_SP (I say _possibly_ because at this time I check both 
i.op[0].regs and i.op[1].regs for REG_SP/REG_FP), there is a warning:
"Warning: SCFI: unhandled op 0x63 may cause incorrect CFI"

I will continue to work through these identified opcodes and reduce 
these warnings further.

>>   For APX, I see that there is
>> are new instructions that fall in this set unfortunately.  Hopefully the
>> set remains small. But in general, for future additions (and for APX
>> currently), there will be time to act as SCFI is for hand-written asm
>> for which synthesizing CFI is desired, not for all code that GAS
>> necessarily has to deal with.
> 
> I understand that, but if a new ISA extension is supported in a new gas
> version, I'd expect it to be legitimate for people to expect that SCFI is
> then also supported for them right away. Else detecting what may or may
> not be used with particular versions of gas is going to become a nightmare.
> 
>>>> +static ginsnS *
>>>> +x86_ginsn_alu (i386_insn insn, symbolS *insn_end_sym)
>>>> +{
>>>> +  offsetT src_imm;
>>>> +  uint32_t dw2_regnum;
>>>> +  enum ginsn_src_type src_type;
>>>> +  enum ginsn_dst_type dst_type;
>>>> +  ginsnS *ginsn = NULL;
>>>> +
>>>> +  /* FIXME - create ginsn for REG_SP target only ? */
>>>> +  /* Map for insn.tm.extension_opcode
>>>> +     000 ADD    100 AND
>>>> +     001 OR     101 SUB
>>>> +     010 ADC    110 XOR
>>>> +     011 SBB    111 CMP  */
>>>> +
>>>> +  /* add/sub imm, %reg.
>>>> +     and imm, %reg only at this time for SCFI. */
>>>> +  if (!(insn.tm.extension_opcode == 0
>>>> +	|| insn.tm.extension_opcode == 4
>>>> +	|| insn.tm.extension_opcode == 5))
>>>> +    return ginsn;
>>>
>>> Why is AND permitted, but OR/XOR aren't?
>>>
>>> Also this is about ALU insns with immediate operands only, yet that
>>> fact isn't reflected in the function name.
>>>
>>
>> OR/XOR should be handled. I will fix this.
>>

Spoke too soon. I remembered later why OR/XOR is not handled but AND is 
(I have now added a code comment around this).

An OR/XOR on REG_SP/REG_FP as destination makes the destination 
untraceable.  So these operations are skipped here.  At a later point 
(towards the end of x86_ginsn_new): the ginsn generation machinery will 
complain about these OR/XOR as "unhandled opcode for SCFI" and bail out 
if destination was REG_SP/REG_FP.

On that note, AND operation also makes REG_SP/REG_FP untraceable, but 
they are being generated currently.  This is because supporting DRAP 
pattern is something we plan to look into. Currently these are 
generated, but the SCFI machinery later bails out with a message "Error: 
SCFI: unsupported stack manipulation pattern"

>> This function is handling opcodes with imm only, true.  ADD/SUB with reg
>> are handled elsewhere (with opcode == 0x1 and opcode == 0x29).
> 
> I saw that, but this doesn't eliminate my remark regarding the name of
> this function. Also the two other opcodes you mention aren't quite
> enough.
> 

ACK. I added handling for 0x3 and 0x2b.  I updated the name of the 
function to reflect that this is handling opcodes with imm only: 
x86_ginsn_alu_imm.

>>>> +  /* TBD_GINSN_REPRESENTATION_LIMIT: There is no representation for when a
>>>> +     symbol is used as an operand, like so:
>>>> +	  addq    $simd_cmp_op+8, %rdx
>>>> +     Skip generating any ginsn for this.  */
>>>> +  if (insn.imm_operands == 1
>>>> +      && insn.op[0].imms->X_op == O_symbol)
>>>> +    return ginsn;
>>>> +
>>>> +  gas_assert (insn.imm_operands == 1
>>>> +	      && insn.op[0].imms->X_op == O_constant);
>>>
>>> Perhaps less fragile if you use != O_constant in the preceding if()?
>>> The remaining half could the move ahead of that if(), allowing half
>>> of its condition to also be dropped.
>>>
>>>> +  src_imm = insn.op[0].imms->X_add_number;
>>>> +  /* The second operand may be a register or indirect access.  */
>>>> +  if (insn.mem_operands == 1 && insn.base_reg)
>>>> +    {
>>>> +      dw2_regnum = ginsn_dw2_regnum (insn.base_reg);
>>>> +      src_type = GINSN_SRC_INDIRECT;
>>>> +      dst_type = GINSN_DST_INDIRECT;
>>>
>>> The possibly in use index register isn't of interest in this case?
>>> Nor the displacement in the memory operand, ...
>>>
>>
>> Not of interest, No.  When src_type and dst_type is INDIRECT.
> 
> It's not clear to me what you mean with your reply. If the addressing
> form used isn't relevant, why do you handle base-only and ...
> 
>>>> +    }
>>>> +  else if (insn.mem_operands == 1 && insn.index_reg)
>>>> +    {
>>>> +      dw2_regnum = ginsn_dw2_regnum (insn.index_reg);
>>>> +      src_type = GINSN_SRC_INDIRECT;
>>>> +      dst_type = GINSN_DST_INDIRECT;
>>>
>>> ... similarly applicable here?
> 
> ... index-only separately in the first place?
> 

This specific case does fall in the category of redundant ginsn for SCFI 
purposes. These INDIRECT addressing mode arith instructions can neither 
be the save/restore ops, nor can be used to alter REG_SP/REG_FP.

I have removed the handling of indirect access cases from 
x86_ginsn_alu_imm and tagged with TBD_GINSN_GEN_NOT_SCFI.

>>> Nor a segment override?
>>
>> Segment override probably is not of interest for SCFI.
>>
>>>
>>>> +    }
>>>> +  else
>>>> +    {
>>>> +      gas_assert (insn.reg_operands == 1);
>>>
>>> Afaict this will trigger when the memory operand has neither base
>>> nor index.
>>>
>>
>> memory operand with neither base nor index ?  An asm instruction example
>> will be helpful.
> 
> 	add	$1, symbol
> 	add	%eax, symbol
> 
> Maybe you really don't need to consider those (if only GPR destinations
> are of interest), but then a comment should be saying so and you'd
> probably want to bail from the function early on in such cases.
> 

Right, I dont need them for SCFI. Now we will exit early in such cases 
from relevant functions.

>>>> +static ginsnS *
>>>> +x86_ginsn_lea (i386_insn insn, symbolS *insn_end_sym)
>>>> +{
>>>> +  offsetT src_disp = 0;
>>>> +  ginsnS *ginsn = NULL;
>>>> +  uint32_t base_reg;
>>>> +  uint32_t index_reg;
>>>> +  offsetT index_scale;
>>>> +  uint32_t dst_reg;
>>>> +
>>>> +  if (!insn.index_reg && !insn.base_reg)
>>>> +    {
>>>> +      /* lea symbol, %rN.  */
>>>> +      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
>>>> +      /* FIXME - Skip encoding information about the symbol.
>>>> +	 This is TBD_GINSN_INFO_LOSS, but it is fine if the mode is
>>>> +	 GINSN_GEN_SCFI.  */
>>>> +      ginsn = ginsn_new_mov (insn_end_sym, false,
>>>> +			     GINSN_SRC_IMM, 0xf /* arbitrary const.  */, 0,
>>>> +			     GINSN_DST_REG, dst_reg, 0);
>>>> +    }
>>>> +  else if (insn.base_reg && !insn.index_reg)
>>>> +    {
>>>> +      /* lea    -0x2(%base),%dst.  */
>>>> +      base_reg = ginsn_dw2_regnum (insn.base_reg);
>>>> +      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
>>>> +
>>>> +      if (insn.disp_operands)
>>>> +	src_disp = insn.op[0].disps->X_add_number;
>>>
>>> What if the displacement expression is other than O_constant?
>>>
>>
>> For SCFI, a "complex" lea instruction will imply some untraceable change
>> to the destination reg.  For SCFI, the extent of untraceable change is
>> not of interest, hence, in general, some information loss in ginsn is
>> tolerable.
> 
> As a fundamental request: For anything that's of no interest, can you
> please try to cover this with as little (and thus clear) code as possible?
> No taking apart of sub-cases when those are indifferent in the end anyway.
> 

In general, this is something I have battled with on and off. In the end 
I chose the principle "Encode as precise ginsn as possible given its 
current representation".  I have tried to follow this mostly 
(x86_ginsn_lea is the known outlier). For all cases known to be 
imprecise, it is my intention to have them marked with 
TBD_GINSN_INFO_LOSS etc. And to not have too many of those..

If I leave out more sub-cases (elsewhere) because they are indifferent 
in the end for SCFI at this time, two problems:
   - More of the currently generated ginsn will look imprecise.
   - More code will need to be revisited when there is other ginsn 
generation modes to be supported.

>> I will have to take a look at such instructions to see what the options
>> are.  If you have a example handy, it will be helpful.
> 
> 	lea	symbol(%rax), %rbp
> 

Thanks. I have handled this one now.

>>>> +      if (src_disp)
>>>> +	/* Generate an ADD ginsn.  */
>>>> +	ginsn = ginsn_new_add (insn_end_sym, true,
>>>> +			       GINSN_SRC_REG, base_reg, 0,
>>>> +			       GINSN_SRC_IMM, 0, src_disp,
>>>> +			       GINSN_DST_REG, dst_reg, 0);
>>>> +      else
>>>> +	  /* Generate a MOV ginsn.  */
>>>> +	  ginsn = ginsn_new_mov (insn_end_sym, true,
>>>> +				 GINSN_SRC_REG, base_reg, 0,
>>>> +				 GINSN_DST_REG, dst_reg, 0);
>>>> +    }
>>>> +  else if (!insn.base_reg && insn.index_reg)
>>>> +    {
>>>> +      /* lea (,%index,imm), %dst.  */
>>>> +      /* FIXME - Skip encoding an explicit multiply operation, instead use
>>>> +	 GINSN_TYPE_OTHER.  This is TBD_GINSN_INFO_LOSS, but it is fine if
>>>> +	 the mode is GINSN_GEN_SCFI.  */
>>>> +      index_scale = insn.log2_scale_factor;
>>>> +      index_reg = ginsn_dw2_regnum (insn.index_reg);
>>>> +      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
>>>> +      ginsn = ginsn_new_other (insn_end_sym, true,
>>>> +			       GINSN_SRC_REG, index_reg,
>>>> +			       GINSN_SRC_IMM, index_scale,
>>>> +			       GINSN_DST_REG, dst_reg);
>>>> +    }
>>>> +  else
>>>> +    {
>>>> +      /* lea disp(%base,%index,imm) %dst.  */
>>>> +      /* FIXME - Skip encoding information about the disp and imm for index
>>>> +	 reg.  This is TBD_GINSN_INFO_LOSS, but it is fine if the mode is
>>>> +	 GINSN_GEN_SCFI.  */
>>>> +      base_reg = ginsn_dw2_regnum (insn.base_reg);
>>>> +      index_reg = ginsn_dw2_regnum (insn.index_reg);
>>>> +      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
>>>> +      /* Generate an ADD ginsn.  */
>>>> +      ginsn = ginsn_new_add (insn_end_sym, true,
>>>> +			     GINSN_SRC_REG, base_reg, 0,
>>>> +			     GINSN_SRC_REG, index_reg, 0,
>>>> +			     GINSN_DST_REG, dst_reg, 0);
>>>
>>> The comment mentions the displacement, but it's not passed on? In the earlier
>>> "else if" block you also pay attention to the scla factor, but here you don't?
>>> That said, I think I'm confused anyway about the FIXME comments.
>>>
>>
>> Ah, This is what I was hinting at in the previous comment.
>>
>> So whats going on here is the following -
>>
>> First, An "add reg1, reg2, reg3" (reg3 being the destination) ginsn is
>> only interesting for SCFI if the reg3 is either REG_FP or REG_SP. We
>> still generate them all nevertheless for now.
>>
>> Second, We say that "add reg1, reg2, reg3" makes reg3 "untraceable".
>> This is based on the observation that static stack usage in a function
>> will use imm based arithmetic insns.
> 
> That is, you only intend to support that way of allocating stack space
> (i.e. one of the apparently many constraints to using this machinery).
> 
>> So, if reg3 is REG_SP, REG_FP,
>> these instructions make REG_SP / REG_FP "untraceable".
>>
>> Note that, We are not allowing all registers for CFA tracking. (Now
>> _this_ is a constraint, yes. And which is why we dont support DRAP usage
>> currently. But its a specific pattern that we can accommodate in future.)
>>
>> Hence, tracking more information for such (untraceable) instructions is
>> unnecessary for SCFI purposes.  If and when GAS uses ginsn (in the i386
>> backend) for other purposes, such information loss will need to be
>> revisited.
> 
> I guess there wants to be a central comment (either in common code, or
> once per arch) which other comments then can refer to. To the reader
> of such code it needs to be clear what bits of information are relevant
> and which ones can be ignored (for now).
> 

OK. I will add some comments.

>>>> +      ginsn = ginsn_new_jump (insn_end_sym, true,
>>>> +			      GINSN_SRC_SYMBOL, 0, src_symbol);
>>>> +
>>>> +      ginsn_set_where (ginsn);
>>>> +    }
>>>> +
>>>> +  return ginsn;
>>>> +}
>>>
>>> Further, what about XABORT transferring control to what XBEGIN has
>>> supplied? (XBEGIN can, in a sense, also be considered a [conditional]
>>> branch.)
>>>
>>
>> My plan after writing this x86 -> ginsn translator was:
>> There are and will remain many machine instructions that may need to be
>> supported depending on hand-written assmebly programmers use.  This is
>> the reason I had left some asserts towards the end in the ginsn_new ()
>> API: if any instructions which affect the control flow, or where the
>> destination is REG_FP / REG_SP, or push* / pop*  are not handled, we
>> will have an assert fail and GAS will need to handle it.
> 
> Elsewhere you mention you want to use this for some of the kernel's
> assembly files. If this feature went in with incomplete coverage, the
> kernel build system detecting its availability will likely be misled.
> 
> Furthermore I'm of the opinion that at the time this series goes in,
> assertions should remain only to check that state is as expected. They
> should not be used to check that user input is unexpected. Such may
> only be communicated back by ordinary diagnostics; the assembler should
> not crash (and even deliberately) on certain input. Besides such being
> a bug, it also gets in the way of fuzzing.
> 

I hope V3 has improved in this regard as compared to V2. Some asserts 
still remain, I will continue to work my way through them.

>>>> +static ginsnS *
>>>> +x86_ginsn_jump_cond (i386_insn insn, symbolS *insn_end_sym)
>>>> +{
>>>> +  ginsnS *ginsn = NULL;
>>>> +  symbolS *src_symbol;
>>>> +
>>>> +  /* TBD_GINSN_GEN_NOT_SCFI: Ignore move to or from xmm reg for mode.  */
>>>> +  if (i.tm.opcode_space == SPACE_0F)
>>>> +    return ginsn;
>>>
>>> What is the comment about? And what about SPACE_0F-encoded conditional
>>> jumps (Jcc <disp32>)? And where are LOOP and J{E,R}CXZ handled?
>>>
>>
>> These need to be handled.  I will take a look.
> 
> Just to mention it: The Jcc <disp32> forms should appear only during
> relaxation, while - aiui - all your code runs earlier than that. I was
> misled by you (elsewhere) handling both 0xE9 and 0xEB as JMP opcodes,
> despite the <disp32> form there - iirc - also only appearing during
> relaxation.
> 

I added LOOP and J{E,R}CXZ insns.

Regarding JCC <disp32> et all appearing after relaxation: right. E9 
handling was an oversight. I have removed it.

>>>> +  gas_assert (insn.disp_operands == 1);
>>>> +
>>>> +  if (insn.op[0].disps->X_op == O_symbol)
>>>> +    {
>>>> +      src_symbol = insn.op[0].disps->X_add_symbol;
>>>> +      /* The jump target is expected to be a symbol with 0 addend.
>>>> +	 Assert for now to see if this assumption is true.  */
>>>> +      gas_assert (insn.op[0].disps->X_add_number == 0);
>>>> +      ginsn = ginsn_new_jump_cond (insn_end_sym, true,
>>>> +				   GINSN_SRC_SYMBOL, 0, src_symbol);
>>>> +      ginsn_set_where (ginsn);
>>>> +    }
>>>> +  else
>>>> +    /* Catch them for now so we know what we are dealing with.  */
>>>> +    gas_assert (0);
>>>
>>> I'm going from the assumption that this (and alike) will be addressed
>>> before this series is committed?
>>
>> I will work on the ones you have raised above in the review. My plan was
>> to exercise this code in different ways after commmitting and address
>> the failures as I run into them.
> 
> Well, as said, I think full insn coverage is a prereq to committing, or
> else there needs to be a way to easily detect an assumed fully functional
> implementation in a later gas version, no matter that a partially
> functional one may have been present in a number of earlier versions
> already. But of course Nick in particular may have a different view on
> this, and his view would trump mine.
> 
>>>> +{
>>>> +  uint16_t opcode;
>>>> +  uint32_t dw2_regnum;
>>>> +  uint32_t src2_dw2_regnum;
>>>> +  int32_t gdisp = 0;
>>>> +  ginsnS *ginsn = NULL;
>>>> +  ginsnS *ginsn_next = NULL;
>>>> +  ginsnS *ginsn_last = NULL;
>>>> +
>>>> +  /* FIXME - Need a way to check whether the decoding is sane.  The specific
>>>> +     checks around i.tm.opcode_space were added as issues were seen.  Likely
>>>> +     insufficient.  */
>>>
>>> Furthermore opcode encoding space (SPACE_...) need to be taken into
>>> account in all cases.
>>>
>>>> +  /* Currently supports generation of selected ginsns, sufficient for
>>>> +     the use-case of SCFI only.  To remove this condition will require
>>>> +     work on this target-specific process of creation of ginsns.  Some
>>>> +     of such places are tagged with TBD_GINSN_GEN_NOT_SCFI to serve as
>>>> +     examples.  */
>>>> +  if (gmode != GINSN_GEN_SCFI)
>>>> +    return ginsn;
>>>> +
>>>> +  opcode = i.tm.base_opcode;
>>>> +
>>>> +  switch (opcode)
>>>> +    {
>>>> +    case 0x1:
>>>> +      /* add reg, reg.  */
>>>> +      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
>>>
>>> You don't care about opcode 0 (byte operation). Then what about 16-bit
>>> operand size? Or, since we're talking of a 64-bit-ABI-only feature,
>>> even 32-bit operand size?
>>>
>>
>> Operand size in some cases does not affect SCFI. So we dont keep
>> information about operand sizes.  The case that I should handle operand
>> size is when they are pushed to / popped from stack.
> 
> So you care about recognizing when %rbp is overwritten by an insn. And
> you also care about the same for %ebp and %bp. In that sense operand
> size may indeed be unnecessary to determine. Except that then you also
> want to deal with byte operations, i.e. %bpl being the destination.
> 

I have adjusted the ginsn_dw2_regnum to handle correctly 8-bit register 
usages so that any writes to REG_SP/REG_FP are detected.  I also added a 
new test "ginsn-dw2-regnum" to exercise this API.

>>> Also what about opcode 0x3?
>>
>> LSL instruction ? It doesnt look like it can affect DWARF CFI of a
>> function. Please correct me if this is wrong.
> 
> LSL is 0f03, i.e. opcode 0x3 in SPACE_0F. What my comment was about is
> ADD with inversed operands (ModRM.rm soure, ModRM.reg destination). All
> four of these flavors of ADD are covered by a single template, using the
> D and W attributes to establish opcode bits 0 and 1 based on actual
> operands (and possibly pseudo-prefixes).
> 

I added handling for 0x3 ADD.

Can you elaborate on the "using the D and W attributes to establish 
opcode bits 0 and 1 based on actual operands (and possibly 
pseudo-prefixes)." a bit ?  Sounds like there is a way to identify the 
destination operand without switch casing on the opcode...Is there? Not 
just for handling of opcode 0x1 and 0x3 with a single template, I have a 
requirement elsewhere to know the dest opnd given an instruction without 
switch casing on the opcode value explicitly.

>>>> +      if (i.reg_operands == 2)
>>>> +	{
>>>> +	  src2_dw2_regnum = ginsn_dw2_regnum (i.op[1].regs);
>>>> +	  ginsn = ginsn_new_add (insn_end_sym, true,
>>>> +				 GINSN_SRC_REG, dw2_regnum, 0,
>>>> +				 GINSN_SRC_REG, src2_dw2_regnum, 0,
>>>> +				 GINSN_DST_REG, src2_dw2_regnum, 0);
>>>> +	  ginsn_set_where (ginsn);
>>>> +	}
>>>> +      else if (i.mem_operands && i.base_reg)
>>>> +	{
>>>> +	  src2_dw2_regnum = ginsn_dw2_regnum (i.base_reg);
>>>> +	  if (i.disp_operands == 1)
>>>> +	    gdisp = i.op[1].disps->X_add_number;
>>>> +
>>>> +	  ginsn = ginsn_new_add (insn_end_sym, true,
>>>> +				 GINSN_SRC_REG, dw2_regnum, 0,
>>>> +				 GINSN_SRC_INDIRECT, src2_dw2_regnum, gdisp,
>>>> +				 GINSN_DST_INDIRECT, src2_dw2_regnum, gdisp);
>>>> +	  ginsn_set_where (ginsn);
>>>> +	}
>>>> +      else
>>>> +	/* Catch them for now so we know what we are dealing with.  */
>>>> +	gas_assert (0);
>>>> +
>>>> +      break;
>>>> +    case 0x29:
>>>> +      /* If opcode_space == SPACE_0F, this is a movaps insn.  Skip it
>>>> +	 for GINSN_GEN_SCFI.  */
>>>> +      if (i.tm.opcode_space == SPACE_0F)
>>>> +	break;
>>>
>>> Extending on the earlier related comment: Why would you exclude just SPACE_0F?
>>>
>>
>> Perhaps here I should check that space == SPACE_BASE and only allow
>> that? And also for others.
> 
> Indeed.
> 
>>>> +      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
>>>> +      /* push fs / push gs.  */
>>>> +      ginsn = ginsn_new_sub (insn_end_sym, false,
>>>> +			     GINSN_SRC_REG, REG_SP, 0,
>>>> +			     GINSN_SRC_IMM, 0, 8,
>>>
>>> Note how the 8 here assumes flag_code == CODE_64BIT. (You further assume
>>> no operand size override here.)
>>>
>>
>> Hmm. Yes, I do assume no operand size override here. I will need to
>> understand how to calculate the operand size here. Looks like I need to
>> check for instruction prefixes 66H or REX.W.  Is there an API in the
>> backend that be used for this ?
> 
> i.prefixes[] and (depending on the phase of assembly) i.rex should be
> telling you.
> 

I have added checks for prefix 66H to detect 16-bit ops now. IIUC, I 
dont need to detect REX.W prefix specifically.

>>>> +      dw2_regnum = ginsn_dw2_regnum (i.base_reg);
>>>> +      ginsn = ginsn_new_load (insn_end_sym, false,
>>>> +			      GINSN_SRC_INDIRECT, REG_SP, 0,
>>>> +			      GINSN_DST_INDIRECT, dw2_regnum);
>>>
>>> When both operands are "indirect", what's the difference between
>>> move, load, and store? IOW if the above is permitted, can't all
>>> three be folded into a single ginsn kind?
>>>
>>
>> In theory, any of mov/load/store ginsn type may be used if both operands
>> are indirect mem access.
> 
> Which suggests that things can be simplified then.
> 

I prefer to keep mov/load/store as I find ginsn more readable then. Also 
other architectures do use the load/store ginsn; In aarch64, a 
load/store machine instruction translates to a load/store opcode in ginsn.

>>>> +    case 0xc2:
>>>> +    case 0xc3:
>>>> +      /* Near ret.  */
>>>> +      ginsn = ginsn_new_return (insn_end_sym, true);
>>>> +      ginsn_set_where (ginsn);
>>>> +      break;
>>>
>>> Where did the immediate operand of 0xC2 go? And what about far return
>>> (and more generally far branches)?
>>>
>>
>> A return ginsn for SCFI can afford this information loss.
> 
> Hmm, right, I guess by how much the stack pointer is incremented at the
> return point isn't of any interest anymore. Unless of course you intended
> to support co-routines.
> 
>>>> +    case 0xc9:
>>>> +      /* The 'leave' instruction copies the contents of the RBP register
>>>> +	 into the RSP register to release all stack space allocated to the
>>>> +	 procedure.  */
>>>> +      ginsn = ginsn_new_mov (insn_end_sym, false,
>>>> +			     GINSN_SRC_REG, REG_FP, 0,
>>>> +			     GINSN_DST_REG, REG_SP, 0);
>>>> +      ginsn_set_where (ginsn);
>>>> +
>>>> +      /* Then it restores the old value of the RBP register from the stack.  */
>>>> +      ginsn_next = ginsn_new_load (insn_end_sym, false,
>>>> +				   GINSN_SRC_INDIRECT, REG_SP, 0,
>>>> +				   GINSN_DST_REG, REG_FP);
>>>> +      ginsn_set_where (ginsn_next);
>>>> +
>>>> +      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
>>>> +      ginsn_last = ginsn_new_add (insn_end_sym, false,
>>>> +				  GINSN_SRC_REG, REG_SP, 0,
>>>> +				  GINSN_SRC_IMM, 0, 8,
>>>> +				  GINSN_DST_REG, REG_SP, 0);
>>>> +      ginsn_set_where (ginsn_next);
>>>> +
>>>> +      gas_assert (!ginsn_link_next (ginsn_next, ginsn_last));
>>>> +      break;
>>>
>>> You handle LEAVE, but not ENTER?
>>>
>>
>> I should have. From the three variants ([Enter imm16,0], [ENTER imm16,1]
>> and [ENTER imm16, imm8], the last one is not super clear currently. Is
>> it possible for you to pass down some understanding to me on that ?
> 
> I can't say more than what the CPU doc says: The second operand tells
> how many earlier frame pointers are copied to the local frame. Perhaps
> you want to simply treat non-zero values as untraceable SP/FP?
> 

I have handled ENTER with zero in operand values.  Other values are 
treated as untraceable SP/FP.

Thanks for reviewing

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

* Re: [PATCH, V2 07/10] gas: synthesize CFI for hand-written asm
  2023-11-06 11:03       ` Jan Beulich
@ 2023-12-10 10:23         ` Indu Bhagat
  2023-12-11  8:02           ` Jan Beulich
  0 siblings, 1 reply; 30+ messages in thread
From: Indu Bhagat @ 2023-12-10 10:23 UTC (permalink / raw)
  To: Jan Beulich; +Cc: binutils

On 11/6/23 03:03, Jan Beulich wrote:
> On 04.11.2023 08:29, Indu Bhagat wrote:
>> On 11/2/23 08:53, Jan Beulich wrote:
>>> On 30.10.2023 17:51, Indu Bhagat wrote:
>>>> +static ginsnS*
>>>> +ginsn_init (enum ginsn_type type, symbolS *sym, bool real_p)
>>>> +{
>>>> +  ginsnS *ginsn = ginsn_alloc ();
>>>> +  ginsn->type = type;
>>>> +  ginsn->sym = sym;
>>>
>>> Is a symbol hanging off of a ginsn ever intended to be altered? If
>>> not, field and function argument would want to be pointer-to-const.
>>>
>>
>> No. This symbol is not intended to be altered.
>>
>> However, using const symbolS* will cause complaints of "passing argument
>> 1 of ‘S_GET_NAME’ discards ‘const’ qualifier" etc.  Calling most of the
>> S_XXX (symbolS *sym) will need some type casting etc, but that will
>> somewhat defeat the purpose?
> 
> Well, yes, of course you shouldn't be casting away const-ness. I've made
> a patch to adjust S_GET_NAME(), which I'll post after having run a full
> set of tests on it.
> 

Thanks. I had rebased the series after your commit.  I had initially 
thought of this to be a big patch,  but clearly I was overthinking.

>>>> +static void
>>>> +ginsn_set_src (struct ginsn_src *src, enum ginsn_src_type type, uint32_t reg,
>>>> +	       int32_t immdisp)
>>>
>>> I find the use of fixed-width types suspicious here: For reg, likely it wants
>>> to be unsigned int. For immdisp it's less clear: Immediates can be wider than
>>> 32 bits, and they may also be either signed ot unsigned. From an abstract
>>> perspective, assuming the immediate value actually is used for anything, I'd
>>> expect offsetT to be used, following struct expressionS' X_add_number.
>>>
>>
>> Thanks, I will consider trying offsetT for immediate. It is a more
>> appropriate data type than int32_t.  For reg, why is uint32_t less
>> appropriate than unsigned int ?
> 
> Fixed-width types come with a price: In principle they may not even be
> available, and they may also not be the most efficient types to deal with
> for an architecture. Therefore my rule of thumb is that they're best
> used only for "describing" interfaces. As long as (here) register numbers
> will fit in an unsigned int, using that basic type looks more appropriate
> to me.
> 

Changed the type to unsigned int for reg. I see that the components 
surrounding the ginsn/scfi like dw2gencfi uses unsigned int for register 
numbers.  So it makes sense to stay uniform.

>>>> +ginsnS *
>>>> +ginsn_new_mov (symbolS *sym, bool real_p,
>>>> +	       enum ginsn_src_type src_type, uint32_t src_reg, int32_t src_disp,
>>>> +	       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp)
>>>> +{
>>>> +  ginsnS *ginsn = ginsn_init (GINSN_TYPE_MOV, sym, real_p);
>>>> +  /* src info.  */
>>>> +  ginsn_set_src (&ginsn->src[0], src_type, src_reg, src_disp);
>>>> +  /* dst info.  */
>>>> +  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
>>>> +
>>>> +  return ginsn;
>>>> +}
>>>
>>> As indicated before, if both src and dst can be indirect here, ...
>>>
>>>> +ginsnS *
>>>> +ginsn_new_store (symbolS *sym, bool real_p,
>>>> +		 enum ginsn_src_type src_type, uint32_t src_reg,
>>>> +		 enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp)
>>>> +{
>>>> +  ginsnS *ginsn = ginsn_init (GINSN_TYPE_STORE, sym, real_p);
>>>> +  /* src info.  */
>>>> +  ginsn_set_src (&ginsn->src[0], src_type, src_reg, 0);
>>>> +  /* dst info.  */
>>>> +  gas_assert (dst_type == GINSN_DST_INDIRECT);
>>>> +  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
>>>> +
>>>> +  return ginsn;
>>>> +}
>>>> +
>>>> +ginsnS *
>>>> +ginsn_new_load (symbolS *sym, bool real_p,
>>>> +		enum ginsn_src_type src_type, uint32_t src_reg, int32_t src_disp,
>>>> +		enum ginsn_dst_type dst_type, uint32_t dst_reg)
>>>> +{
>>>> +  ginsnS *ginsn = ginsn_init (GINSN_TYPE_LOAD, sym, real_p);
>>>> +  /* src info.  */
>>>> +  gas_assert (src_type == GINSN_SRC_INDIRECT);
>>>> +  ginsn_set_src (&ginsn->src[0], src_type, src_reg, src_disp);
>>>> +  /* dst info.  */
>>>> +  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, 0);
>>>> +
>>>> +  return ginsn;
>>>> +}
>>>
>>> ... I can't see what these are needed for.
>>>
>>
>> For x86, they may not seem necessary. But for other architectures, or
>> say for future uses-cases, we may need them. I think it is more
>> meaningful (and readable) to see a LOAD/STORE/MOV ginsn for a machine
>> instruction of the same type.  For other RISC-like ISAs, it is clearer
>> to have separate MOV/LOAD/STORE instructions.
>>
>> ginsn is meant to provide an infrastructure for other uses cases that
>> may crop up later.
> 
> But then I consider it as odd that you munge loads/stores on x86 into
> ginsn_new_mov(), by using "indirect" operands. Imo it would be better
> to be consistent here, one way or the other.
> 

What I have not defined (deliberately) is any "temporary registers" in 
ginsn.  This means a CISC instruction cannot be split into multiple 
gisnns with data dependencies.  So to keep things simple, if machine 
instruction is a MOV, we pick MOV ginsn (with indirect access if this 
was a load/store).  If the machine instruction is an ADD, we pick ADD 
ginsn (with indirect access if applicable).

If I understand you correct, you suggest that we pick load/store ops 
like so:
   mov  disp(%reg), %reg --> load disp(%reg), %reg
   mov  %reg, disp(%reg) --> store %reg, disp(%reg)

Then what about arith ops with indirect access ? In theory those are 
load + arith + store. In other words, the above deliberation of picking 
load/store for mov seems inconsistent then.

For SCFI in aarch64 (WIP), load / store ginsns are used actively.


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

* Re: [PATCH, V2 07/10] gas: synthesize CFI for hand-written asm
  2023-12-10 10:22         ` Indu Bhagat
@ 2023-12-11  7:59           ` Jan Beulich
  2023-12-19  7:07             ` Indu Bhagat
  0 siblings, 1 reply; 30+ messages in thread
From: Jan Beulich @ 2023-12-11  7:59 UTC (permalink / raw)
  To: Indu Bhagat; +Cc: binutils

On 10.12.2023 11:22, Indu Bhagat wrote:
> On 11/2/23 04:39, Jan Beulich wrote:
>> On 02.11.2023 09:15, Indu Bhagat wrote:
>>> On 10/31/23 07:10, Jan Beulich wrote:
>>>> On 30.10.2023 17:51, Indu Bhagat wrote:
>>>>> gas/
>>>>> 	* Makefile.am: Add new files.
>>>>> 	* Makefile.in: Regenerated.
>>>>> 	* as.c (defined): Handle documentation and listing option for
>>>>> 	ginsns and SCFI.
>>>>> 	* config/obj-elf.c (obj_elf_size): Invoke ginsn_data_end.
>>>>> 	(obj_elf_type): Invoke ginsn_data_begin.
>>>>> 	* config/tc-i386.c (ginsn_new): New functionality to generate
>>>>> 	ginsns.
>>>>> 	(x86_scfi_callee_saved_p): New function.
>>>>> 	(ginsn_dw2_regnum): Likewise.
>>>>> 	(ginsn_set_where): Likewise.
>>>>> 	(x86_ginsn_alu): Likewise.
>>>>> 	(x86_ginsn_move): Likewise.
>>>>> 	(x86_ginsn_lea): Likewise.
>>>>> 	(x86_ginsn_jump): Likewise.
>>>>> 	(x86_ginsn_jump_cond): Likewise.
>>>>> 	(md_assemble): Invoke ginsn_new.
>>>>> 	(s_insn): Likewise.
>>>>> 	(i386_target_format): Add hard error for usage of --scfi with non AMD64 ABIs.
>>>>> 	* config/tc-i386.h (TARGET_USE_GINSN): New definition.
>>>>> 	(TARGET_USE_SCFI): Likewise.
>>>>> 	(SCFI_NUM_REGS): Likewise.
>>>>> 	(REG_FP): Likewise.
>>>>> 	(REG_SP): Likewise.
>>>>> 	(SCFI_INIT_CFA_OFFSET): Likewise.
>>>>> 	(SCFI_CALLEE_SAVED_REG_P): Likewise.
>>>>> 	(x86_scfi_callee_saved_p): Likewise.
>>>>
>>>> For this arch-specific code there's a fundamental question of maintenance
>>>> cost here: It doesn't look very reasonable to me to demand of people adding
>>>> support for new ISA extensions to also take into consideration all of this
>>>> new machinery. Yet if any such addition affects SCFI, things will go out-
>>>> of-sync without updating this code as well. It may not be very often that
>>>> such updating is necessary, but right now there's APX work in progress
>>>> which I expect will need dealing with here as well.
>>>>
>>>
>>> I understand your concerns.  FWIW, for SCFI, we will need to translate
>>> only a subset of instructions into ginsns (change of flow insns,
>>> save/restore and arith on REG_SP/REG_FP).
>>
>> Considering what you say further down regarding untraceability, I'm afraid
>> you will need to care about all insns which have SP/FP as their destination.
>>
> 
> Yeah, All insns which have SP/FP as their destination must be seen by 
> the SCFI machinery. If any instruction which has SP/FP as destination is 
> missed, and the user has specified a --scfi command line argument, my 
> thinking is that GAS should spit out an error or a diagnostic.
> 
> That said, the current implementation handles the most commonly used 
> instructions to manage stack pointer for dynamic and static stack 
> allocation. For unhandled instructions which end up _possibly_ affecting 
> REG_FP/REG_SP (I say _possibly_ because at this time I check both 
> i.op[0].regs and i.op[1].regs for REG_SP/REG_FP), there is a warning:
> "Warning: SCFI: unhandled op 0x63 may cause incorrect CFI"

Yet neither i.op[0] nor i.op[1] may represent the destination register of
an insn, when there are more than 2 operands.

>>>>> +static ginsnS *
>>>>> +x86_ginsn_alu (i386_insn insn, symbolS *insn_end_sym)
>>>>> +{
>>>>> +  offsetT src_imm;
>>>>> +  uint32_t dw2_regnum;
>>>>> +  enum ginsn_src_type src_type;
>>>>> +  enum ginsn_dst_type dst_type;
>>>>> +  ginsnS *ginsn = NULL;
>>>>> +
>>>>> +  /* FIXME - create ginsn for REG_SP target only ? */
>>>>> +  /* Map for insn.tm.extension_opcode
>>>>> +     000 ADD    100 AND
>>>>> +     001 OR     101 SUB
>>>>> +     010 ADC    110 XOR
>>>>> +     011 SBB    111 CMP  */
>>>>> +
>>>>> +  /* add/sub imm, %reg.
>>>>> +     and imm, %reg only at this time for SCFI. */
>>>>> +  if (!(insn.tm.extension_opcode == 0
>>>>> +	|| insn.tm.extension_opcode == 4
>>>>> +	|| insn.tm.extension_opcode == 5))
>>>>> +    return ginsn;
>>>>
>>>> Why is AND permitted, but OR/XOR aren't?
>>>>
>>>> Also this is about ALU insns with immediate operands only, yet that
>>>> fact isn't reflected in the function name.
>>>>
>>>
>>> OR/XOR should be handled. I will fix this.
>>>
> 
> Spoke too soon. I remembered later why OR/XOR is not handled but AND is 
> (I have now added a code comment around this).
> 
> An OR/XOR on REG_SP/REG_FP as destination makes the destination 
> untraceable.  So these operations are skipped here.  At a later point 
> (towards the end of x86_ginsn_new): the ginsn generation machinery will 
> complain about these OR/XOR as "unhandled opcode for SCFI" and bail out 
> if destination was REG_SP/REG_FP.
> 
> On that note, AND operation also makes REG_SP/REG_FP untraceable, but 
> they are being generated currently.  This is because supporting DRAP 
> pattern is something we plan to look into. Currently these are 
> generated, but the SCFI machinery later bails out with a message "Error: 
> SCFI: unsupported stack manipulation pattern"

But why would you need special handling just to mark a register untraceable?
Generic code merely looking at the destination register ought to be enough?

>>>>> +static ginsnS *
>>>>> +x86_ginsn_lea (i386_insn insn, symbolS *insn_end_sym)
>>>>> +{
>>>>> +  offsetT src_disp = 0;
>>>>> +  ginsnS *ginsn = NULL;
>>>>> +  uint32_t base_reg;
>>>>> +  uint32_t index_reg;
>>>>> +  offsetT index_scale;
>>>>> +  uint32_t dst_reg;
>>>>> +
>>>>> +  if (!insn.index_reg && !insn.base_reg)
>>>>> +    {
>>>>> +      /* lea symbol, %rN.  */
>>>>> +      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
>>>>> +      /* FIXME - Skip encoding information about the symbol.
>>>>> +	 This is TBD_GINSN_INFO_LOSS, but it is fine if the mode is
>>>>> +	 GINSN_GEN_SCFI.  */
>>>>> +      ginsn = ginsn_new_mov (insn_end_sym, false,
>>>>> +			     GINSN_SRC_IMM, 0xf /* arbitrary const.  */, 0,
>>>>> +			     GINSN_DST_REG, dst_reg, 0);
>>>>> +    }
>>>>> +  else if (insn.base_reg && !insn.index_reg)
>>>>> +    {
>>>>> +      /* lea    -0x2(%base),%dst.  */
>>>>> +      base_reg = ginsn_dw2_regnum (insn.base_reg);
>>>>> +      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
>>>>> +
>>>>> +      if (insn.disp_operands)
>>>>> +	src_disp = insn.op[0].disps->X_add_number;
>>>>
>>>> What if the displacement expression is other than O_constant?
>>>>
>>>
>>> For SCFI, a "complex" lea instruction will imply some untraceable change
>>> to the destination reg.  For SCFI, the extent of untraceable change is
>>> not of interest, hence, in general, some information loss in ginsn is
>>> tolerable.
>>
>> As a fundamental request: For anything that's of no interest, can you
>> please try to cover this with as little (and thus clear) code as possible?
>> No taking apart of sub-cases when those are indifferent in the end anyway.
>>
> 
> In general, this is something I have battled with on and off. In the end 
> I chose the principle "Encode as precise ginsn as possible given its 
> current representation".  I have tried to follow this mostly 
> (x86_ginsn_lea is the known outlier). For all cases known to be 
> imprecise, it is my intention to have them marked with 
> TBD_GINSN_INFO_LOSS etc. And to not have too many of those..
> 
> If I leave out more sub-cases (elsewhere) because they are indifferent 
> in the end for SCFI at this time, two problems:
>    - More of the currently generated ginsn will look imprecise.
>    - More code will need to be revisited when there is other ginsn 
> generation modes to be supported.

I can see your view. Still there's the other view of code not really
being needed right now potentially also net being in the exact shape it
may be needed down the road, at which point it would similarly need
touching again.

>>>>> +{
>>>>> +  uint16_t opcode;
>>>>> +  uint32_t dw2_regnum;
>>>>> +  uint32_t src2_dw2_regnum;
>>>>> +  int32_t gdisp = 0;
>>>>> +  ginsnS *ginsn = NULL;
>>>>> +  ginsnS *ginsn_next = NULL;
>>>>> +  ginsnS *ginsn_last = NULL;
>>>>> +
>>>>> +  /* FIXME - Need a way to check whether the decoding is sane.  The specific
>>>>> +     checks around i.tm.opcode_space were added as issues were seen.  Likely
>>>>> +     insufficient.  */
>>>>
>>>> Furthermore opcode encoding space (SPACE_...) need to be taken into
>>>> account in all cases.
>>>>
>>>>> +  /* Currently supports generation of selected ginsns, sufficient for
>>>>> +     the use-case of SCFI only.  To remove this condition will require
>>>>> +     work on this target-specific process of creation of ginsns.  Some
>>>>> +     of such places are tagged with TBD_GINSN_GEN_NOT_SCFI to serve as
>>>>> +     examples.  */
>>>>> +  if (gmode != GINSN_GEN_SCFI)
>>>>> +    return ginsn;
>>>>> +
>>>>> +  opcode = i.tm.base_opcode;
>>>>> +
>>>>> +  switch (opcode)
>>>>> +    {
>>>>> +    case 0x1:
>>>>> +      /* add reg, reg.  */
>>>>> +      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
>>>>
>>>> You don't care about opcode 0 (byte operation). Then what about 16-bit
>>>> operand size? Or, since we're talking of a 64-bit-ABI-only feature,
>>>> even 32-bit operand size?
>>>>
>>>
>>> Operand size in some cases does not affect SCFI. So we dont keep
>>> information about operand sizes.  The case that I should handle operand
>>> size is when they are pushed to / popped from stack.
>>
>> So you care about recognizing when %rbp is overwritten by an insn. And
>> you also care about the same for %ebp and %bp. In that sense operand
>> size may indeed be unnecessary to determine. Except that then you also
>> want to deal with byte operations, i.e. %bpl being the destination.
>>
> 
> I have adjusted the ginsn_dw2_regnum to handle correctly 8-bit register 
> usages so that any writes to REG_SP/REG_FP are detected.  I also added a 
> new test "ginsn-dw2-regnum" to exercise this API.
> 
>>>> Also what about opcode 0x3?
>>>
>>> LSL instruction ? It doesnt look like it can affect DWARF CFI of a
>>> function. Please correct me if this is wrong.
>>
>> LSL is 0f03, i.e. opcode 0x3 in SPACE_0F. What my comment was about is
>> ADD with inversed operands (ModRM.rm soure, ModRM.reg destination). All
>> four of these flavors of ADD are covered by a single template, using the
>> D and W attributes to establish opcode bits 0 and 1 based on actual
>> operands (and possibly pseudo-prefixes).
>>
> 
> I added handling for 0x3 ADD.
> 
> Can you elaborate on the "using the D and W attributes to establish 
> opcode bits 0 and 1 based on actual operands (and possibly 
> pseudo-prefixes)." a bit ?

Well, reg <-> reg/mem ADDs (taking the specific example) come in two
forms: ModR/M.reg reprsenting the source and the same representing the
destination. They're represented by a single opcode template, with D
indicating that both directions can be used.

Similarly there are multiple operand sizes supported by several such
templates, with W indicating that both byte nad word/dword/qword
operation is possible to encode by OR-ing in a single opcode bit.

For your purposes, W is likely of no direct interest. D may be, yet
then I take it that you work on actual operands, not on what templates
permit. In actual operands, destination (due to following to AT&T
syntax layout in the internal representation) is last at all stages of
assembly (for Intel syntax input after swapping operands, of course).

>>>>> +      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
>>>>> +      /* push fs / push gs.  */
>>>>> +      ginsn = ginsn_new_sub (insn_end_sym, false,
>>>>> +			     GINSN_SRC_REG, REG_SP, 0,
>>>>> +			     GINSN_SRC_IMM, 0, 8,
>>>>
>>>> Note how the 8 here assumes flag_code == CODE_64BIT. (You further assume
>>>> no operand size override here.)
>>>>
>>>
>>> Hmm. Yes, I do assume no operand size override here. I will need to
>>> understand how to calculate the operand size here. Looks like I need to
>>> check for instruction prefixes 66H or REX.W.  Is there an API in the
>>> backend that be used for this ?
>>
>> i.prefixes[] and (depending on the phase of assembly) i.rex should be
>> telling you.
>>
> 
> I have added checks for prefix 66H to detect 16-bit ops now. IIUC, I 
> dont need to detect REX.W prefix specifically.

I'm not sure. REX.W clear likely means the destination becomes untraceable?
Whereas REX.W set means to full register is updated (upper half not zeroed)
and hence it may remain traceable?

Jan

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

* Re: [PATCH, V2 07/10] gas: synthesize CFI for hand-written asm
  2023-12-10 10:23         ` Indu Bhagat
@ 2023-12-11  8:02           ` Jan Beulich
  0 siblings, 0 replies; 30+ messages in thread
From: Jan Beulich @ 2023-12-11  8:02 UTC (permalink / raw)
  To: Indu Bhagat; +Cc: binutils

On 10.12.2023 11:23, Indu Bhagat wrote:
> On 11/6/23 03:03, Jan Beulich wrote:
>> On 04.11.2023 08:29, Indu Bhagat wrote:
>>> On 11/2/23 08:53, Jan Beulich wrote:
>>>> On 30.10.2023 17:51, Indu Bhagat wrote:
>>>>> +ginsnS *
>>>>> +ginsn_new_mov (symbolS *sym, bool real_p,
>>>>> +	       enum ginsn_src_type src_type, uint32_t src_reg, int32_t src_disp,
>>>>> +	       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp)
>>>>> +{
>>>>> +  ginsnS *ginsn = ginsn_init (GINSN_TYPE_MOV, sym, real_p);
>>>>> +  /* src info.  */
>>>>> +  ginsn_set_src (&ginsn->src[0], src_type, src_reg, src_disp);
>>>>> +  /* dst info.  */
>>>>> +  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
>>>>> +
>>>>> +  return ginsn;
>>>>> +}
>>>>
>>>> As indicated before, if both src and dst can be indirect here, ...
>>>>
>>>>> +ginsnS *
>>>>> +ginsn_new_store (symbolS *sym, bool real_p,
>>>>> +		 enum ginsn_src_type src_type, uint32_t src_reg,
>>>>> +		 enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp)
>>>>> +{
>>>>> +  ginsnS *ginsn = ginsn_init (GINSN_TYPE_STORE, sym, real_p);
>>>>> +  /* src info.  */
>>>>> +  ginsn_set_src (&ginsn->src[0], src_type, src_reg, 0);
>>>>> +  /* dst info.  */
>>>>> +  gas_assert (dst_type == GINSN_DST_INDIRECT);
>>>>> +  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
>>>>> +
>>>>> +  return ginsn;
>>>>> +}
>>>>> +
>>>>> +ginsnS *
>>>>> +ginsn_new_load (symbolS *sym, bool real_p,
>>>>> +		enum ginsn_src_type src_type, uint32_t src_reg, int32_t src_disp,
>>>>> +		enum ginsn_dst_type dst_type, uint32_t dst_reg)
>>>>> +{
>>>>> +  ginsnS *ginsn = ginsn_init (GINSN_TYPE_LOAD, sym, real_p);
>>>>> +  /* src info.  */
>>>>> +  gas_assert (src_type == GINSN_SRC_INDIRECT);
>>>>> +  ginsn_set_src (&ginsn->src[0], src_type, src_reg, src_disp);
>>>>> +  /* dst info.  */
>>>>> +  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, 0);
>>>>> +
>>>>> +  return ginsn;
>>>>> +}
>>>>
>>>> ... I can't see what these are needed for.
>>>>
>>>
>>> For x86, they may not seem necessary. But for other architectures, or
>>> say for future uses-cases, we may need them. I think it is more
>>> meaningful (and readable) to see a LOAD/STORE/MOV ginsn for a machine
>>> instruction of the same type.  For other RISC-like ISAs, it is clearer
>>> to have separate MOV/LOAD/STORE instructions.
>>>
>>> ginsn is meant to provide an infrastructure for other uses cases that
>>> may crop up later.
>>
>> But then I consider it as odd that you munge loads/stores on x86 into
>> ginsn_new_mov(), by using "indirect" operands. Imo it would be better
>> to be consistent here, one way or the other.
>>
> 
> What I have not defined (deliberately) is any "temporary registers" in 
> ginsn.  This means a CISC instruction cannot be split into multiple 
> gisnns with data dependencies.  So to keep things simple, if machine 
> instruction is a MOV, we pick MOV ginsn (with indirect access if this 
> was a load/store).  If the machine instruction is an ADD, we pick ADD 
> ginsn (with indirect access if applicable).
> 
> If I understand you correct, you suggest that we pick load/store ops 
> like so:
>    mov  disp(%reg), %reg --> load disp(%reg), %reg
>    mov  %reg, disp(%reg) --> store %reg, disp(%reg)
> 
> Then what about arith ops with indirect access ? In theory those are 
> load + arith + store. In other words, the above deliberation of picking 
> load/store for mov seems inconsistent then.

If you don't decompose read-modify-write (memory destination) operations,
then some kind of inconsistency is always going to be there.

Jan

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

* Re: [PATCH, V2 07/10] gas: synthesize CFI for hand-written asm
  2023-12-11  7:59           ` Jan Beulich
@ 2023-12-19  7:07             ` Indu Bhagat
  0 siblings, 0 replies; 30+ messages in thread
From: Indu Bhagat @ 2023-12-19  7:07 UTC (permalink / raw)
  To: Jan Beulich; +Cc: binutils

On 12/10/23 23:59, Jan Beulich wrote:
> On 10.12.2023 11:22, Indu Bhagat wrote:
>> On 11/2/23 04:39, Jan Beulich wrote:
>>> On 02.11.2023 09:15, Indu Bhagat wrote:
>>>> On 10/31/23 07:10, Jan Beulich wrote:
>>>>> On 30.10.2023 17:51, Indu Bhagat wrote:
>>>>>> gas/
>>>>>> 	* Makefile.am: Add new files.
>>>>>> 	* Makefile.in: Regenerated.
>>>>>> 	* as.c (defined): Handle documentation and listing option for
>>>>>> 	ginsns and SCFI.
>>>>>> 	* config/obj-elf.c (obj_elf_size): Invoke ginsn_data_end.
>>>>>> 	(obj_elf_type): Invoke ginsn_data_begin.
>>>>>> 	* config/tc-i386.c (ginsn_new): New functionality to generate
>>>>>> 	ginsns.
>>>>>> 	(x86_scfi_callee_saved_p): New function.
>>>>>> 	(ginsn_dw2_regnum): Likewise.
>>>>>> 	(ginsn_set_where): Likewise.
>>>>>> 	(x86_ginsn_alu): Likewise.
>>>>>> 	(x86_ginsn_move): Likewise.
>>>>>> 	(x86_ginsn_lea): Likewise.
>>>>>> 	(x86_ginsn_jump): Likewise.
>>>>>> 	(x86_ginsn_jump_cond): Likewise.
>>>>>> 	(md_assemble): Invoke ginsn_new.
>>>>>> 	(s_insn): Likewise.
>>>>>> 	(i386_target_format): Add hard error for usage of --scfi with non AMD64 ABIs.
>>>>>> 	* config/tc-i386.h (TARGET_USE_GINSN): New definition.
>>>>>> 	(TARGET_USE_SCFI): Likewise.
>>>>>> 	(SCFI_NUM_REGS): Likewise.
>>>>>> 	(REG_FP): Likewise.
>>>>>> 	(REG_SP): Likewise.
>>>>>> 	(SCFI_INIT_CFA_OFFSET): Likewise.
>>>>>> 	(SCFI_CALLEE_SAVED_REG_P): Likewise.
>>>>>> 	(x86_scfi_callee_saved_p): Likewise.
>>>>>
>>>>> For this arch-specific code there's a fundamental question of maintenance
>>>>> cost here: It doesn't look very reasonable to me to demand of people adding
>>>>> support for new ISA extensions to also take into consideration all of this
>>>>> new machinery. Yet if any such addition affects SCFI, things will go out-
>>>>> of-sync without updating this code as well. It may not be very often that
>>>>> such updating is necessary, but right now there's APX work in progress
>>>>> which I expect will need dealing with here as well.
>>>>>
>>>>
>>>> I understand your concerns.  FWIW, for SCFI, we will need to translate
>>>> only a subset of instructions into ginsns (change of flow insns,
>>>> save/restore and arith on REG_SP/REG_FP).
>>>
>>> Considering what you say further down regarding untraceability, I'm afraid
>>> you will need to care about all insns which have SP/FP as their destination.
>>>
>>
>> Yeah, All insns which have SP/FP as their destination must be seen by
>> the SCFI machinery. If any instruction which has SP/FP as destination is
>> missed, and the user has specified a --scfi command line argument, my
>> thinking is that GAS should spit out an error or a diagnostic.
>>
>> That said, the current implementation handles the most commonly used
>> instructions to manage stack pointer for dynamic and static stack
>> allocation. For unhandled instructions which end up _possibly_ affecting
>> REG_FP/REG_SP (I say _possibly_ because at this time I check both
>> i.op[0].regs and i.op[1].regs for REG_SP/REG_FP), there is a warning:
>> "Warning: SCFI: unhandled op 0x63 may cause incorrect CFI"
> 
> Yet neither i.op[0] nor i.op[1] may represent the destination register of
> an insn, when there are more than 2 operands.
> 

I have now handled this case properly I think (will include this in V4). 
Thanks for your comment below.

>>>>>> +static ginsnS *
>>>>>> +x86_ginsn_alu (i386_insn insn, symbolS *insn_end_sym)
>>>>>> +{
>>>>>> +  offsetT src_imm;
>>>>>> +  uint32_t dw2_regnum;
>>>>>> +  enum ginsn_src_type src_type;
>>>>>> +  enum ginsn_dst_type dst_type;
>>>>>> +  ginsnS *ginsn = NULL;
>>>>>> +
>>>>>> +  /* FIXME - create ginsn for REG_SP target only ? */
>>>>>> +  /* Map for insn.tm.extension_opcode
>>>>>> +     000 ADD    100 AND
>>>>>> +     001 OR     101 SUB
>>>>>> +     010 ADC    110 XOR
>>>>>> +     011 SBB    111 CMP  */
>>>>>> +
>>>>>> +  /* add/sub imm, %reg.
>>>>>> +     and imm, %reg only at this time for SCFI. */
>>>>>> +  if (!(insn.tm.extension_opcode == 0
>>>>>> +	|| insn.tm.extension_opcode == 4
>>>>>> +	|| insn.tm.extension_opcode == 5))
>>>>>> +    return ginsn;
>>>>>
>>>>> Why is AND permitted, but OR/XOR aren't?
>>>>>
>>>>> Also this is about ALU insns with immediate operands only, yet that
>>>>> fact isn't reflected in the function name.
>>>>>
>>>>
>>>> OR/XOR should be handled. I will fix this.
>>>>
>>
>> Spoke too soon. I remembered later why OR/XOR is not handled but AND is
>> (I have now added a code comment around this).
>>
>> An OR/XOR on REG_SP/REG_FP as destination makes the destination
>> untraceable.  So these operations are skipped here.  At a later point
>> (towards the end of x86_ginsn_new): the ginsn generation machinery will
>> complain about these OR/XOR as "unhandled opcode for SCFI" and bail out
>> if destination was REG_SP/REG_FP.
>>
>> On that note, AND operation also makes REG_SP/REG_FP untraceable, but
>> they are being generated currently.  This is because supporting DRAP
>> pattern is something we plan to look into. Currently these are
>> generated, but the SCFI machinery later bails out with a message "Error:
>> SCFI: unsupported stack manipulation pattern"
> 
> But why would you need special handling just to mark a register untraceable?
> Generic code merely looking at the destination register ought to be enough?
> 

Yes, now a ginsn on type GINSN_TYPE_OTHER with destination REG_SP/REG_FP 
as applicable is generated.  The SCFI machinery then bails out if this 
ginsn makes REG_FP untraceable, when REG_FP based CFA tracking is 
active.  If CFA tracking is _not_ REG_FP based, it is fine to manipulate 
REG_FP in a untraceable way.

>>>>>> +static ginsnS *
>>>>>> +x86_ginsn_lea (i386_insn insn, symbolS *insn_end_sym)
>>>>>> +{
>>>>>> +  offsetT src_disp = 0;
>>>>>> +  ginsnS *ginsn = NULL;
>>>>>> +  uint32_t base_reg;
>>>>>> +  uint32_t index_reg;
>>>>>> +  offsetT index_scale;
>>>>>> +  uint32_t dst_reg;
>>>>>> +
>>>>>> +  if (!insn.index_reg && !insn.base_reg)
>>>>>> +    {
>>>>>> +      /* lea symbol, %rN.  */
>>>>>> +      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
>>>>>> +      /* FIXME - Skip encoding information about the symbol.
>>>>>> +	 This is TBD_GINSN_INFO_LOSS, but it is fine if the mode is
>>>>>> +	 GINSN_GEN_SCFI.  */
>>>>>> +      ginsn = ginsn_new_mov (insn_end_sym, false,
>>>>>> +			     GINSN_SRC_IMM, 0xf /* arbitrary const.  */, 0,
>>>>>> +			     GINSN_DST_REG, dst_reg, 0);
>>>>>> +    }
>>>>>> +  else if (insn.base_reg && !insn.index_reg)
>>>>>> +    {
>>>>>> +      /* lea    -0x2(%base),%dst.  */
>>>>>> +      base_reg = ginsn_dw2_regnum (insn.base_reg);
>>>>>> +      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
>>>>>> +
>>>>>> +      if (insn.disp_operands)
>>>>>> +	src_disp = insn.op[0].disps->X_add_number;
>>>>>
>>>>> What if the displacement expression is other than O_constant?
>>>>>
>>>>
>>>> For SCFI, a "complex" lea instruction will imply some untraceable change
>>>> to the destination reg.  For SCFI, the extent of untraceable change is
>>>> not of interest, hence, in general, some information loss in ginsn is
>>>> tolerable.
>>>
>>> As a fundamental request: For anything that's of no interest, can you
>>> please try to cover this with as little (and thus clear) code as possible?
>>> No taking apart of sub-cases when those are indifferent in the end anyway.
>>>
>>
>> In general, this is something I have battled with on and off. In the end
>> I chose the principle "Encode as precise ginsn as possible given its
>> current representation".  I have tried to follow this mostly
>> (x86_ginsn_lea is the known outlier). For all cases known to be
>> imprecise, it is my intention to have them marked with
>> TBD_GINSN_INFO_LOSS etc. And to not have too many of those..
>>
>> If I leave out more sub-cases (elsewhere) because they are indifferent
>> in the end for SCFI at this time, two problems:
>>     - More of the currently generated ginsn will look imprecise.
>>     - More code will need to be revisited when there is other ginsn
>> generation modes to be supported.
> 
> I can see your view. Still there's the other view of code not really
> being needed right now potentially also net being in the exact shape it
> may be needed down the road, at which point it would similarly need
> touching again.
> 
>>>>>> +{
>>>>>> +  uint16_t opcode;
>>>>>> +  uint32_t dw2_regnum;
>>>>>> +  uint32_t src2_dw2_regnum;
>>>>>> +  int32_t gdisp = 0;
>>>>>> +  ginsnS *ginsn = NULL;
>>>>>> +  ginsnS *ginsn_next = NULL;
>>>>>> +  ginsnS *ginsn_last = NULL;
>>>>>> +
>>>>>> +  /* FIXME - Need a way to check whether the decoding is sane.  The specific
>>>>>> +     checks around i.tm.opcode_space were added as issues were seen.  Likely
>>>>>> +     insufficient.  */
>>>>>
>>>>> Furthermore opcode encoding space (SPACE_...) need to be taken into
>>>>> account in all cases.
>>>>>
>>>>>> +  /* Currently supports generation of selected ginsns, sufficient for
>>>>>> +     the use-case of SCFI only.  To remove this condition will require
>>>>>> +     work on this target-specific process of creation of ginsns.  Some
>>>>>> +     of such places are tagged with TBD_GINSN_GEN_NOT_SCFI to serve as
>>>>>> +     examples.  */
>>>>>> +  if (gmode != GINSN_GEN_SCFI)
>>>>>> +    return ginsn;
>>>>>> +
>>>>>> +  opcode = i.tm.base_opcode;
>>>>>> +
>>>>>> +  switch (opcode)
>>>>>> +    {
>>>>>> +    case 0x1:
>>>>>> +      /* add reg, reg.  */
>>>>>> +      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
>>>>>
>>>>> You don't care about opcode 0 (byte operation). Then what about 16-bit
>>>>> operand size? Or, since we're talking of a 64-bit-ABI-only feature,
>>>>> even 32-bit operand size?
>>>>>
>>>>
>>>> Operand size in some cases does not affect SCFI. So we dont keep
>>>> information about operand sizes.  The case that I should handle operand
>>>> size is when they are pushed to / popped from stack.
>>>
>>> So you care about recognizing when %rbp is overwritten by an insn. And
>>> you also care about the same for %ebp and %bp. In that sense operand
>>> size may indeed be unnecessary to determine. Except that then you also
>>> want to deal with byte operations, i.e. %bpl being the destination.
>>>
>>
>> I have adjusted the ginsn_dw2_regnum to handle correctly 8-bit register
>> usages so that any writes to REG_SP/REG_FP are detected.  I also added a
>> new test "ginsn-dw2-regnum" to exercise this API.
>>
>>>>> Also what about opcode 0x3?
>>>>
>>>> LSL instruction ? It doesnt look like it can affect DWARF CFI of a
>>>> function. Please correct me if this is wrong.
>>>
>>> LSL is 0f03, i.e. opcode 0x3 in SPACE_0F. What my comment was about is
>>> ADD with inversed operands (ModRM.rm soure, ModRM.reg destination). All
>>> four of these flavors of ADD are covered by a single template, using the
>>> D and W attributes to establish opcode bits 0 and 1 based on actual
>>> operands (and possibly pseudo-prefixes).
>>>
>>
>> I added handling for 0x3 ADD.
>>
>> Can you elaborate on the "using the D and W attributes to establish
>> opcode bits 0 and 1 based on actual operands (and possibly
>> pseudo-prefixes)." a bit ?
> 
> Well, reg <-> reg/mem ADDs (taking the specific example) come in two
> forms: ModR/M.reg reprsenting the source and the same representing the
> destination. They're represented by a single opcode template, with D
> indicating that both directions can be used.
> 
> Similarly there are multiple operand sizes supported by several such
> templates, with W indicating that both byte nad word/dword/qword
> operation is possible to encode by OR-ing in a single opcode bit.
> 
> For your purposes, W is likely of no direct interest. D may be, yet
> then I take it that you work on actual operands, not on what templates
> permit. In actual operands, destination (due to following to AT&T
> syntax layout in the internal representation) is last at all stages of
> assembly (for Intel syntax input after swapping operands, of course).
> 

Thanks.  We will now take the last reg operand, i.op[i.operands - 
1].regs (after checks on i.operands and i.reg_operands) as destination 
and if the register is REG_SP/REG_FP, we generate GINSN_TYPE_OTHER with 
the appropriate destination register.

I will include this change in a V4 posting.

>>>>>> +      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
>>>>>> +      /* push fs / push gs.  */
>>>>>> +      ginsn = ginsn_new_sub (insn_end_sym, false,
>>>>>> +			     GINSN_SRC_REG, REG_SP, 0,
>>>>>> +			     GINSN_SRC_IMM, 0, 8,
>>>>>
>>>>> Note how the 8 here assumes flag_code == CODE_64BIT. (You further assume
>>>>> no operand size override here.)
>>>>>
>>>>
>>>> Hmm. Yes, I do assume no operand size override here. I will need to
>>>> understand how to calculate the operand size here. Looks like I need to
>>>> check for instruction prefixes 66H or REX.W.  Is there an API in the
>>>> backend that be used for this ?
>>>
>>> i.prefixes[] and (depending on the phase of assembly) i.rex should be
>>> telling you.
>>>
>>
>> I have added checks for prefix 66H to detect 16-bit ops now. IIUC, I
>> dont need to detect REX.W prefix specifically.
> 
> I'm not sure. REX.W clear likely means the destination becomes untraceable?
> Whereas REX.W set means to full register is updated (upper half not zeroed)
> and hence it may remain traceable?

I see that Push, Pop instructions do not need the REX prefix. Hence, 
checking for 66H to detect 16-bit operand size suffices.  Default 
operand size is 64-bit in 64-bit mode.


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

end of thread, other threads:[~2023-12-19  7:07 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-10-30 16:51 [PATCH, V2 00/10] Synthesize CFI for hand-written asm Indu Bhagat
2023-10-30 16:51 ` [PATCH, V2 01/10] gas: dw2gencfi: minor rejig for cfi_sections_set and all_cfi_sections Indu Bhagat
2023-10-30 16:51 ` [PATCH, V2 02/10] gas: dw2gencfi: use all_cfi_sections instead of cfi_sections Indu Bhagat
2023-10-30 16:51 ` [PATCH, V2 03/10] gas: dw2gencfi: expose a new cfi_set_last_fde API Indu Bhagat
2023-10-30 16:51 ` [PATCH, V2 04/10] gas: dw2gencfi: move some tc_* defines to the header file Indu Bhagat
2023-10-30 16:51 ` [PATCH, V2 05/10] gas: add new command line option --scfi[=all,none] Indu Bhagat
2023-10-30 16:51 ` [PATCH, V2 06/10] gas: scfidw2gen: new functionality to prepapre for SCFI Indu Bhagat
2023-10-31 11:28   ` Jan Beulich
2023-10-31 22:06     ` Indu Bhagat
2023-11-02 10:35       ` Jan Beulich
2023-11-02 16:32         ` Indu Bhagat
2023-10-30 16:51 ` [PATCH, V2 07/10] gas: synthesize CFI for hand-written asm Indu Bhagat
2023-10-31 14:10   ` Jan Beulich
2023-11-02  8:15     ` Indu Bhagat
2023-11-02 11:39       ` Jan Beulich
2023-12-10 10:22         ` Indu Bhagat
2023-12-11  7:59           ` Jan Beulich
2023-12-19  7:07             ` Indu Bhagat
2023-11-02 15:53   ` Jan Beulich
2023-11-04  7:29     ` Indu Bhagat
2023-11-06 11:03       ` Jan Beulich
2023-12-10 10:23         ` Indu Bhagat
2023-12-11  8:02           ` Jan Beulich
2023-10-30 16:51 ` [PATCH, V2 08/10] gas: doc: update documentation for the new listing option Indu Bhagat
2023-10-30 16:51 ` [PATCH, V2 09/10] gas: testsuite: add a x86_64 testsuite for SCFI Indu Bhagat
2023-10-31 16:13   ` Jan Beulich
2023-11-01  6:24     ` Indu Bhagat
2023-11-02 12:28       ` Jan Beulich
2023-11-03  5:45         ` Indu Bhagat
2023-10-30 16:51 ` [PATCH, V2 10/10] gas/NEWS: announce the new command line option 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).