Commit Graph

8 Commits

Author SHA1 Message Date
Indu Bhagat
648ad82c55 gas: ginsn: minor improvements in ginsn_dst_print and ginsn_src_print
Keep the two symmetrical looking.  Makes sense to perform the sanity
checks similarly too.

gas/
        * ginsn.c (ginsn_src_print): Buffer up result of snprintf and
	add sanity checks on the value.
        (ginsn_dst_print): Use switch case instead.
2024-08-01 11:50:10 -07:00
Indu Bhagat
67f39cdbc9 gas: ginsn: do not emit an unnecessary trailing comma in textual dump
For ginsns with less than 2 source operands or no destination operands,
the current textual dump contains a superfluous comma, like the relevant
testcases show.

Adjust the code a bit to not emit the lone trailing comma.  Also, adjust
the aarch64 and x86_64 testcases.

gas/
        * ginsn.c (ginsn_src_print): Do not use a trailing comma when
	printing the src of ginsn.
        (ginsn_print): Check the strlen and prefix a comma before the
	src string.

gas/testsuite/
	* gas/scfi/aarch64/ginsn-cofi-1.l: Adjust the expected textual
	dump of the ginsn.
	* gas/scfi/x86_64/ginsn-cofi-1.l: Likewise.
2024-08-01 11:34:08 -07:00
Indu Bhagat
002ccda0ef gas: ginsn: remove unnecessary buffer allocation and free
A previous commit 80ec235 fixed the memory leaks, but brought to light
that the code should ideally make consistent use of snprintf and not
allocate/free more buffers than necessary.

gas/
	* ginsn.c (ginsn_dst_print): Use snprintf consistently.
2024-05-21 12:59:55 -07:00
H.J. Lu
80ec23548f gas: Fix memory leaks in ginsn.c
Free buffer memory after use in ginsn.c.

	* ginsn.c (ginsn_dst_print): Free buffer after use.
	(ginsn_print): Likewise.
2024-04-11 04:10:28 -07:00
Indu Bhagat
a1c6a60cc5 gas: gcfg: add_bb_at_ginsn must return root_bb
A GCFG (ginsn control flow graph) is created for SCFI purposes in GAS.
The existing GCFG creation process was ignoring some paths.

add_bb_at_ginsn () is a recursive function which should return the root
of the added basic blocks.  This property was being violated in some
traversals, e.g., where a taken path involving a sequence of a few basic
blocks eventually culminated in a GINSN_TYPE_RETURN instruction.  This
patch fixes the issue by keeping an explicit variable root_bb to
memorize the bb to be returned.

Next, find_or_make_bb () must either create or find the bb with the
first ginsn as the provided ginsn.  Add a few assertions to ensure
health of the cfg creation process.

Note that the testcase, in its current shape, is not fit for catching
regressions for the issue at hand.  Although the testcase does exercise
the updated code path, the testcase passes even without the current fix,
because the added edge in this specific testcase does not alter the
synthesized CFI.  (The missing edge is the fallthrough edge of the
conditional branch "jne .L13" in the testcase.)

Using a manual gcfg_print (), one can see the missing edge without the
fix.  Lets keep the testcase for now, until there is a better way to
test the GCFG for this issue (e.g., either by dumping the GCFG in
textual format, or a case when the missing edge does cause wrong
synthesized CFI).

gas/
	* ginsn.c (bb_add_edge): Fix a code comment.
	(find_bb): Likewise.
	(find_or_make_bb): Add new assertions to ensure health of cfg
	creation process.
	(add_bb_at_ginsn): Keep reference to the root_bb and return it.

gas/testsuite/
	* gas/scfi/x86_64/scfi-x86-64.exp: Add new test.
	* gas/scfi/x86_64/scfi-cfg-4.d: New test.
	* gas/scfi/x86_64/scfi-cfg-4.l: New test.
	* gas/scfi/x86_64/scfi-cfg-4.s: New test.
2024-04-10 13:45:59 -07:00
Indu Bhagat
e67388a6a4 gas: gcfg: fix handling of non-local direct jmps in gcfg
The ginsn infrastructure in GAS includes the ability to create a GCFG
(ginsn CFG).  A GCFG is currently used for SCFI passes.

This patch fixes the following invalid assumptions / code blocks:
 - The function ginsn_direct_local_jump_p () was erroneously _not_
   checking whether the symbol is locally defined (i.e., within the
   scope of the code block for which GCFG is desired).  Fix the code
   to do so.
 - Similarly, the GCFG creation code, in gcfg_build () itself had an
   assumption that a GINSN_TYPE_JUMP to a non-local symbol will not be
   seen.  The latter can indeed be seen, and in fact, needs to be treated
   the same way as an exit from the function in terms of control-flow.

gas/
        * ginsn.c (ginsn_direct_local_jump_p): Check if the symbol
	is local to the code block or function being assembled.
        (add_bb_at_ginsn): Remove buggy assumption.
        (frch_ginsn_data_append): Direct jmps do not disqualify a stream
	of ginsns from GCFG creation.

gas/testsuite/
	* gas/scfi/x86_64/scfi-cfg-3.d: New test.
	* gas/scfi/x86_64/scfi-cfg-3.l: New test.
	* gas/scfi/x86_64/scfi-cfg-3.s: New test.
	* gas/scfi/x86_64/scfi-x86-64.exp: Add new test.
2024-03-28 11:57:23 -07:00
Indu Bhagat
91cdbed4d7 gas: scfi: untraceable control flow should be a hard error
PR gas/31284

Currently, if an indirect jump is seen, GCFG (a CFG of ginsns) cannot be
created, and the SCFI machinery bails out with a warning:
  "Warning: Untraceable control flow for func 'foo'; Skipping SCFI"

It is, however, better suited if this is a hard error.  Change it to a
hard error.  Also change the message to skip mentioning "SCFI", because
the error itself may also useful when ginsns are used for other passes
(distinct from SCFI) involving GCFG, like a pass to detect if there is
unreachable code.  Hence, simply say:
  "Error: untraceable control flow for func 'foo'"

gas/
PR gas/31284
	* ginsn.c (ginsn_data_end): Use as_bad instead of as_warn.

gas/testsuite/
PR gas/31284
	* gas/scfi/x86_64/ginsn-cofi-1.l: Adjust to the expected output
	in case of errors.
	* gas/scfi/x86_64/scfi-unsupported-cfg-1.l: Error not Warning.
2024-01-26 11:31:35 -08:00
Indu Bhagat
c7defc5386 gas: x86: synthesize CFI for hand-written asm
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
needs to generate 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.

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 System V AMD64 ABI for ELF at this time.  If
the user enables SCFI with --32, GAS issues an error:

  "Fatal error: SCFI 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 (x86_scfi_callee_saved_p): New function.
	(ginsn_prefix_66H_p): Likewise.
	(ginsn_dw2_regnum): Likewise.
	(x86_ginsn_addsub_reg_mem): Likewise.
	(x86_ginsn_addsub_mem_reg): Likewise.
	(x86_ginsn_alu_imm): Likewise.
	(x86_ginsn_move): Likewise.
	(x86_ginsn_lea): Likewise.
	(x86_ginsn_jump): Likewise.
	(x86_ginsn_jump_cond): Likewise.
	(x86_ginsn_enter): Likewise.
	(x86_ginsn_safe_to_skip): Likewise.
	(x86_ginsn_unhandled): Likewise.
	(x86_ginsn_new): New functionality to generate ginsns.
	(md_assemble): Invoke x86_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_MAX_REG_ID): 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.
2024-01-15 03:31:35 -08:00