Compare commits

...

8 Commits

Author SHA1 Message Date
Indu Bhagat
eeadfb158b gas: x86: fix code comments and other nits
ChangeLog:
	* gas/config/tc-i386.c
2024-03-28 16:55:50 -07:00
Indu Bhagat
6ea6536f01 gas: x86: ginsn: adapt the APX insn handling
A review comment on the SCFI V4 series was to bail out on APX
instructions differently:

Currently, GAS bails out at the mere sight of an APX instruction when
--scfi=experimental is in effect; this is implemented in form of an
early exit from x86_ginsn_new ().  It is preferable to not have to
handle APX instructions explicitly like that with an early exit,
but rather to use the existing infrastructure in the x86_ginsn_new ()
function to bail out only if the APX insn affects SCFI correctness.

FIXME -
1. There remains an outstanding issue with adcx etc ops
2. Add tests once above is fixed

gas/
	* config/tc-i386.c (ginsn_opsize_prefix_p): Rename and adjust to
	be ginsn_implicitstackop_opsize16_p instead.
	(x86_ginsn_enter): Use the new function.
	(x86_ginsn_leave): Likewise.
	(x86_ginsn_new): Likewise.  Also remove the stub for early exit
	for APX instructions.

gas/testsuite/
        * gas/scfi/x86_64/scfi-unsupported-insn-1.l:
2024-03-28 16:55:50 -07:00
Indu Bhagat
977da52111 gas: x86: ginsn: handle previously missed indirect call and jmp ops
Some flavors of indirect call and jmp instructions were not being
handled earlier, leading to a GAS error (#1):
  (#1) "Error: SCFI: unhandled op 0xff may cause incorrect CFI"

Not handling jmp/call (direct or indirect) ops is an error (as shown
above) because SCFI needs an accurate CFG to synthesize CFI correctly.
Recall that the presence of indirect jmp/call, however, does make the
CFG ineligible for SCFI. In other words, generating the ginsns for them
now, will eventually cause SCFI to bail out later with an error (#2)
anyway:
  (#2) "Error: untraceable control flow for func 'XXX'"

The first error (#1) gives the impression of missing functionality in
GAS.  So, it seems cleaner to synthesize a GINSN_TYPE_JUMP /
GINSN_TYPE_CALL accurately in the backend, and let SCFI machinery
complain with the error as expected.

The handling for these indirect jmp/call instructions is similar, so
reuse the code by carving out a function for the same.

Adjust the testcase to include the now handled jmp/call instructions as
well.

gas/
	* config/tc-i386.c (x86_ginsn_indirect_jump_call): New
	function.
	(x86_ginsn_new): Refactor out functionality to above.

gas/testsuite/
	* gas/scfi/x86_64/ginsn-cofi-1.l: Adjust the output.
	* gas/scfi/x86_64/ginsn-cofi-1.s: Add further varieties of
	jmp/call opcodes.
2024-03-28 16:55:50 -07:00
Indu Bhagat
0d12d72fc6 gas: scfi: bugfixes for SCFI state propagation
There are two state propagation functions in SCFI machinery - forward
and backward flow.  The patch addresses two issues:
  - In forward_flow_scfi_state (), the state being compared in forward flow
    must be that at the exit of a prev bb and that at the entry of the
    next bb.  The variable holding the state to be compared was
    previously (erroneously) stale.
  - In cmp_scfi_state (), the assumption that two different control
    flows, leading to the same basic block, cannot have a mismatched
    notion of CFA base register, is not true.  Remove the assertion and
    instead return err if mismatch.

Fixing these issues helps correctly synthesize CFI, when previously
SCFI was erroring out for an otherwise valid input asm.

gas/
	* scfi.c (cmp_scfi_state): Remove assertion and return mismatch
	in return value as applicable.
	(forward_flow_scfi_state): Update state object to be the same as
	the exit state of the prev bb before comparing.
gas/testsuite/
	* gas/scfi/x86_64/scfi-x86-64.exp: Add new test.
	* gas/scfi/x86_64/scfi-cfg-5.d: New test.
	* gas/scfi/x86_64/scfi-cfg-5.l: New test.
	* gas/scfi/x86_64/scfi-cfg-5.s: New test.
2024-03-28 16:55:50 -07:00
Indu Bhagat
d412bdce32 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-03-28 16:55:50 -07:00
Indu Bhagat
f7a2f944bb gas: aarch64: testsuite: add new tests for SCFI
Similar to the x86_64 testcases, some .s files contain the corresponding
CFI directives.  This helps in validating the synthesized CFI by running
those tests with and without the --scfi=experimental command line
option.

Apart from the existing scfi-diag-*.s tests in gas/scfi/x86_64
directory, the newly added scfi-diag-1.s in this patch highlights an
aarch64-specific diagnostic:

  "Warning: SCFI: ignored probable save/restore op with reg offset"

Additionally, some testcases are added to showcase the (currently) unsupported
patterns, e.g., scfi-unsupported-1.s
        mov     x16, 4384
        sub     sp, sp, x16

gas/testsuite/:
	* gas/scfi/README: Update comment to include aarch64.
	* gas/scfi/aarch64/scfi-aarch64.exp: New file.
	* gas/scfi/aarch64/ginsn-cofi-1.l: New test.
	* gas/scfi/aarch64/ginsn-cofi-1.s: New test.
	* gas/scfi/aarch64/ginsn-ldst-1.l: New test.
	* gas/scfi/aarch64/ginsn-ldst-1.s: New test.
	* gas/scfi/aarch64/scfi-cb-1.d: New test.
	* gas/scfi/aarch64/scfi-cb-1.l: New test.
	* gas/scfi/aarch64/scfi-cb-1.s: New test.
	* gas/scfi/aarch64/scfi-cfg-1.d: New test.
	* gas/scfi/aarch64/scfi-cfg-1.l: New test.
	* gas/scfi/aarch64/scfi-cfg-1.s: New test.
	* gas/scfi/aarch64/scfi-cfg-2.d: New test.
	* gas/scfi/aarch64/scfi-cfg-2.l: New test.
	* gas/scfi/aarch64/scfi-cfg-2.s: New test.
	* gas/scfi/aarch64/scfi-cfg-3.d: New test.
	* gas/scfi/aarch64/scfi-cfg-3.l: New test.
	* gas/scfi/aarch64/scfi-cfg-3.s: New test.
	* gas/scfi/aarch64/scfi-cond-br-1.d: New test.
	* gas/scfi/aarch64/scfi-cond-br-1.l: New test.
	* gas/scfi/aarch64/scfi-cond-br-1.s: New test.
	* gas/scfi/aarch64/scfi-diag-1.l: New test.
	* gas/scfi/aarch64/scfi-diag-1.s: New test.
	* gas/scfi/aarch64/scfi-ldrp-1.d: New test.
	* gas/scfi/aarch64/scfi-ldrp-1.l: New test.
	* gas/scfi/aarch64/scfi-ldrp-1.s: New test.
	* gas/scfi/aarch64/scfi-ldrp-2.d: New test.
	* gas/scfi/aarch64/scfi-ldrp-2.l: New test.
	* gas/scfi/aarch64/scfi-ldrp-2.s: New test.
	* gas/scfi/aarch64/scfi-strp-1.d: New test.
	* gas/scfi/aarch64/scfi-strp-1.l: New test.
	* gas/scfi/aarch64/scfi-strp-1.s: New test.
	* gas/scfi/aarch64/scfi-strp-2.d: New test.
	* gas/scfi/aarch64/scfi-strp-2.l: New test.
	* gas/scfi/aarch64/scfi-strp-2.s: New test.
2024-03-28 16:55:49 -07:00
Indu Bhagat
4e97c9dcfc gas: aarch64: add experimental support for SCFI
For synthesizing CFI (SCFI) for hand-written asm, the SCFI machinery in
GAS works on the generic GAS insns (ginsns).  This patch adds support in
the aarch64 backend to create ginsns for the following set of machine
instructions, which are necessary to process to ensure SCFI correctness:

  - Any potential register saves and unsaves.  This implies the need to
    process many iclasses involving str, ldr, stp, ldp.
  - Any change of flow instructions, including all conditional and
    unconditional branches, call (bl, blr) and return.
  - Any instruction that could affect the two registers of
    interest: REG_SP, REG_FP.  This set includes all pre-indexed and
    post-indexed memory operations, with writeback, on the stack.

SCFI is enabled for ELF targets only.

Apart from creating ginsn, we also add functionality in the backend to
detect dynamically if an instruction of interest may have been skipped.
Such a check is added with an intent to also capture changes in the ISA
which may otherwise render incorrect SCFI results.

The current SCFI machinery does not currently synthesize the PAC-related
aarch64-specific CFI directives: .cfi_b_key_frame.  The support for this
is planned for near future.

gas/
        * config/tc-aarch64.c (GINSN_DW2_REGNUM_R1_DUMMY):
        (aarch64_scfi_callee_saved_p):
        (ginsn_dw2_regnum):
        (aarch64_ginsn_addsub):
        (aarch64_ginsn_ldstp):
        (aarch64_ginsn_ldstr):
        (aarch64_ginsn_jump):
        (aarch64_ginsn_jump_cond):
        (aarch64_ginsn_mov):
        (aarch64_ginsn_safe_to_skip_p):
        (AARCH64_GINSN_UNHANDLED_NONE):
        (AARCH64_GINSN_UNHANDLED_DEST_REG):
        (AARCH64_GINSN_UNHANDLED_CFG):
        (AARCH64_GINSN_UNHANDLED_STACKOP):
        (AARCH64_GINSN_UNHANDLED_UNEXPECTED):
        (aarch64_ginsn_unhandled):
        (aarch64_ginsn_new):
        (md_assemble):
        * config/tc-aarch64.h (TARGET_USE_GINSN):
        (TARGET_USE_SCFI):
        (SCFI_MAX_REG_ID):
        (REG_FP):
        (REG_LR):
        (REG_SP):
        (SCFI_INIT_CFA_OFFSET):
        (SCFI_CALLEE_SAVED_REG_P):
        (aarch64_scfi_callee_saved_p):
2024-03-28 16:55:49 -07:00
Indu Bhagat
ad35d7451d Add the tex file for the SCFI paper 2024-03-28 16:55:49 -07:00
55 changed files with 3004 additions and 131 deletions

View File

@@ -37,6 +37,7 @@
#include "dw2gencfi.h"
#include "dwarf2dbg.h"
#include "scfi.h"
#define streq(a, b) (strcmp (a, b) == 0)
@@ -6062,6 +6063,725 @@ get_aarch64_insn (char *buf)
return result;
}
#ifdef OBJ_ELF
/* DWARF register number for R1. Used as dummy value when wzr. */
#define GINSN_DW2_REGNUM_R1_DUMMY 1
bool
aarch64_scfi_callee_saved_p (uint32_t dw2reg_num)
{
if (dw2reg_num == REG_SP /* x31. */
|| dw2reg_num == REG_FP /* x29. */
|| dw2reg_num == REG_LR /* x30. */
|| (dw2reg_num >= 19 && dw2reg_num <= 28) /* x19 - x28. */)
return true;
return false;
}
/* Get the DWARF register number for the given OPND.
Whether 31 is used to encode WZR or SP is specified via SP_ALLOWED_P.
The caller must decide the value of SP_ALLOWED_P based on the instruction
encoding. */
static uint32_t
ginsn_dw2_regnum (aarch64_opnd_info *opnd, bool sp_allowed_p)
{
/* For registers of our interest (callee-saved regs, SP, FP, LR),
DWARF register number is the same as AArch64 register number. */
if (!sp_allowed_p && opnd->reg.regno == REG_SP)
return GINSN_DW2_REGNUM_R1_DUMMY;
return opnd->reg.regno;
}
static ginsnS *
aarch64_ginsn_addsub_imm (const symbolS *insn_end_sym)
{
ginsnS *ginsn = NULL;
bool add_p, sub_p;
int32_t src_imm = 0;
uint32_t dst_reg, opnd_reg;
aarch64_opnd_info *dst, *opnd;
aarch64_inst *base = &inst.base;
const aarch64_opcode *opcode = base->opcode;
add_p = (opcode->opcode == 0x11000000);
sub_p = (opcode->opcode == 0x51000000);
gas_assert (add_p || sub_p);
gas_assert (aarch64_num_of_operands (opcode) == 3);
dst = &base->operands[0];
opnd = &base->operands[1];
dst_reg = ginsn_dw2_regnum (dst, true);
if (aarch64_gas_internal_fixup_p () && inst.reloc.exp.X_op == O_constant)
src_imm = inst.reloc.exp.X_add_number;
/* For any other relocation type, e.g., in add reg, reg, symbol, skip now and
handle via aarch64_ginsn_unhandled () code path. */
else if (inst.reloc.type != BFD_RELOC_UNUSED)
return ginsn;
/* FIXME - verify the understanding and remove assert. */
else
gas_assert (0);
opnd_reg = ginsn_dw2_regnum (opnd, true);
if (sub_p)
ginsn = ginsn_new_sub (insn_end_sym, true,
GINSN_SRC_REG, opnd_reg, 0,
GINSN_SRC_IMM, 0, src_imm,
GINSN_DST_REG, dst_reg, 0);
else
ginsn = ginsn_new_add (insn_end_sym, true,
GINSN_SRC_REG, opnd_reg, 0,
GINSN_SRC_IMM, 0, src_imm,
GINSN_DST_REG, dst_reg, 0);
ginsn_set_where (ginsn);
return ginsn;
}
static ginsnS *
aarch64_ginsn_addsub (const symbolS *insn_end_sym)
{
ginsnS *ginsn = NULL;
bool add_p, sub_p;
uint32_t dst_reg, src1_reg, src2_reg;
aarch64_opnd_info *dst, *src1, *src2;
ginsnS *(*ginsn_func) (const symbolS *, bool,
enum ginsn_src_type, unsigned int, offsetT,
enum ginsn_src_type, unsigned int, offsetT,
enum ginsn_dst_type, unsigned int, offsetT);
aarch64_inst *base = &inst.base;
const aarch64_opcode *opcode = base->opcode;
add_p = (opcode->opcode == 0x0b200000);
sub_p = (opcode->opcode == 0x4b200000);
gas_assert (add_p || sub_p);
ginsn_func = add_p ? ginsn_new_add : ginsn_new_sub;
gas_assert (aarch64_num_of_operands (opcode) == 3);
dst = &base->operands[0];
src1 = &base->operands[1];
src2 = &base->operands[2];
dst_reg = ginsn_dw2_regnum (dst, true);
src1_reg = ginsn_dw2_regnum (src1, true);
src2_reg = ginsn_dw2_regnum (src2, false);
ginsn = ginsn_func (insn_end_sym, true,
GINSN_SRC_REG, src1_reg, 0,
GINSN_SRC_REG, src2_reg, 0,
GINSN_DST_REG, dst_reg, 0);
ginsn_set_where (ginsn);
return ginsn;
}
/* Handle the load pair and store pair instructions. */
static ginsnS *
aarch64_ginsn_ldstp (const symbolS *insn_end_sym, bool store_p)
{
ginsnS *ginsn = NULL;
ginsnS *ginsn_ind = NULL;
ginsnS *ginsn_mem1 = NULL;
ginsnS *ginsn_mem2 = NULL;
uint32_t opnd_reg, addr_reg;
int32_t offset, mem_offset;
unsigned int width = 8;
aarch64_opnd_info *opnd1, *opnd2, *addr;
aarch64_inst *base = &inst.base;
const aarch64_opcode *opcode = base->opcode;
/* This function is for handling ldp / stp ops only. */
gas_assert (opcode->iclass == ldstpair_indexed
|| opcode->iclass == ldstpair_off);
gas_assert (aarch64_num_of_operands (opcode) == 3);
opnd1 = &base->operands[0];
opnd2 = &base->operands[1];
addr = &base->operands[2];
addr_reg = ginsn_dw2_regnum (addr, true);
gas_assert (!addr->addr.offset.is_reg);
mem_offset = addr->addr.offset.imm;
if (opnd1->qualifier == AARCH64_OPND_QLF_W)
width = 4;
/* Handle address calculation. */
if ((addr->addr.preind || addr->addr.postind) && addr->addr.writeback)
{
/* Pre-indexed store, e.g., stp x29, x30, [sp, -128]!
Pre-indexed addressing is like offset addressing, except that
the base pointer is updated as a result of the instruction.
Post-indexed store, e.g., stp x29, x30, [sp],128
Post-index addressing is useful for popping off the stack. The
instruction loads the value from the location pointed at by the stack
pointer, and then moves the stack pointer on to the next full location
in the stack. */
ginsn_ind = ginsn_new_add (insn_end_sym, false,
GINSN_SRC_REG, addr_reg, 0,
GINSN_SRC_IMM, 0, mem_offset,
GINSN_DST_REG, addr_reg, 0);
ginsn_set_where (ginsn_ind);
}
/* Save / restore of floating point registers is not of interest for SCFI.
However, the address processing component may have updated the stack
pointer. At least, emit that ginsn and return.
TBD_GINSN_GEN_NOT_SCFI. */
if (aarch64_get_operand_class (opnd1->type) != AARCH64_OPND_CLASS_INT_REG)
return ginsn_ind;
/* With post-index addressing, the value is loaded from the
address in the base pointer, and then the pointer is updated.
With pre-index addressing, the addr computation has already
been explicitly done. */
offset = mem_offset;
if ((addr->addr.postind || addr->addr.preind) && addr->addr.writeback)
offset = 0;
opnd_reg = ginsn_dw2_regnum (opnd1, false);
if (store_p)
{
ginsn_mem1 = ginsn_new_store (insn_end_sym, false,
GINSN_SRC_REG, opnd_reg,
GINSN_DST_INDIRECT, addr_reg, offset);
ginsn_set_where (ginsn_mem1);
opnd_reg = ginsn_dw2_regnum (opnd2, false);
ginsn_mem2 = ginsn_new_store (insn_end_sym, false,
GINSN_SRC_REG, opnd_reg,
GINSN_DST_INDIRECT, addr_reg, offset + width);
ginsn_set_where (ginsn_mem2);
}
else
{
opnd_reg = ginsn_dw2_regnum (opnd1, false);
ginsn_mem1 = ginsn_new_load (insn_end_sym, false,
GINSN_SRC_INDIRECT, addr_reg, offset,
GINSN_DST_REG, opnd_reg);
ginsn_set_where (ginsn_mem1);
opnd_reg = ginsn_dw2_regnum (opnd2, false);
ginsn_mem2 = ginsn_new_load (insn_end_sym, false,
GINSN_SRC_INDIRECT, addr_reg, offset + width,
GINSN_DST_REG, opnd_reg);
ginsn_set_where (ginsn_mem2);
}
/* Link the list of ginsns created. */
if (addr->addr.preind && addr->addr.writeback)
gas_assert (!ginsn_link_next (ginsn_ind, ginsn_mem1));
gas_assert (!ginsn_link_next (ginsn_mem1, ginsn_mem2));
if (addr->addr.postind && addr->addr.writeback)
gas_assert (!ginsn_link_next (ginsn_mem2, ginsn_ind));
/* Make note of the first instruction in the list. */
ginsn = (addr->addr.preind && addr->addr.writeback) ? ginsn_ind : ginsn_mem1;
return ginsn;
}
static ginsnS *
aarch64_ginsn_ldstr (const symbolS *insn_end_sym, bool store_p)
{
ginsnS *ginsn = NULL;
ginsnS *ginsn_ind = NULL;
ginsnS *ginsn_mem = NULL;
uint32_t opnd_reg, addr_reg;
int32_t offset = 0;
int32_t mem_offset = 0;
aarch64_opnd_info *opnd1, *addr;
aarch64_inst *base = &inst.base;
const aarch64_opcode *opcode = base->opcode;
/* This function is for handling ldr, str ops only. */
gas_assert (opcode->iclass == ldst_imm9 || opcode->iclass == ldst_pos);
gas_assert (aarch64_num_of_operands (opcode) == 2);
opnd1 = &base->operands[0];
addr = &base->operands[1];
addr_reg = ginsn_dw2_regnum (addr, true);
/* STR <Xt>, [<Xn|SP>, (<Wm>|<Xm>){, <extend> {<amount>}}].
LDR <Xt>, [<Xn|SP>], #<simm>. */
opnd_reg = ginsn_dw2_regnum (opnd1, false);
/* If opnd_reg is WZR, ignore this (OK to do for SCFI). Note, this is a
potential case of TBD_GINSN_GEN_NOT_SCFI. */
if (opnd_reg == REG_SP)
return ginsn;
if (aarch64_gas_internal_fixup_p () && inst.reloc.exp.X_op == O_constant)
mem_offset = inst.reloc.exp.X_add_number;
else
{
gas_assert (!addr->addr.offset.is_reg);
mem_offset = addr->addr.offset.imm;
}
/* Handle address calculation. */
if ((addr->addr.preind || addr->addr.postind) && addr->addr.writeback)
{
ginsn_ind = ginsn_new_add (insn_end_sym, false,
GINSN_SRC_REG, addr_reg, 0,
GINSN_SRC_IMM, 0, mem_offset,
GINSN_DST_REG, addr_reg, 0);
ginsn_set_where (ginsn_ind);
}
/* Save / restore of floating point registers is not of interest for SCFI.
However, the address processing component may have updated the stack
pointer. At least, emit that ginsn and return.
TBD_GINSN_GEN_NOT_SCFI. */
if (aarch64_get_operand_class (opnd1->type) != AARCH64_OPND_CLASS_INT_REG)
return ginsn_ind;
/* With post-index addressing, the value is loaded from the
address in the base pointer, and then the pointer is updated.
With pre-index addressing, the addr computation has already
been explicitly done. */
offset = mem_offset;
if ((addr->addr.postind || addr->addr.preind) && addr->addr.writeback)
offset = 0;
if (store_p)
ginsn_mem = ginsn_new_store (insn_end_sym, false,
GINSN_SRC_REG, opnd_reg,
GINSN_DST_INDIRECT, addr_reg, offset);
else
ginsn_mem = ginsn_new_load (insn_end_sym, false,
GINSN_SRC_INDIRECT, addr_reg, offset,
GINSN_DST_REG, opnd_reg);
ginsn_set_where (ginsn_mem);
if (addr->addr.preind && addr->addr.writeback)
gas_assert (!ginsn_link_next (ginsn_ind, ginsn_mem));
else if (addr->addr.postind && addr->addr.writeback)
gas_assert (!ginsn_link_next (ginsn_mem, ginsn_ind));
/* Make note of the first instruction in the list. */
ginsn = (addr->addr.preind && addr->addr.writeback) ? ginsn_ind : ginsn_mem;
return ginsn;
}
static ginsnS *
aarch64_ginsn_jump (const symbolS *insn_end_sym)
{
ginsnS *ginsn = NULL;
const symbolS *src_symbol = NULL;
enum ginsn_src_type src_type = GINSN_SRC_UNKNOWN;
uint32_t src_value = 0;
bool call_p = false;
aarch64_inst *base = &inst.base;
const aarch64_opcode *opcode = base->opcode;
if (opcode->iclass == branch_imm)
{
/* opcode 0x14000000 or 0x94000000. */
gas_assert (inst.reloc.type == BFD_RELOC_AARCH64_CALL26
|| inst.reloc.type == BFD_RELOC_AARCH64_JUMP26);
src_symbol = inst.reloc.exp.X_add_symbol;
src_type = GINSN_SRC_SYMBOL;
}
else if (opcode->opcode == 0xd61f0000 || opcode->opcode == 0xd63f0000)
{
src_type = GINSN_SRC_REG;
src_value = ginsn_dw2_regnum (&base->operands[0], false);
}
if (opcode->opcode == 0x94000000 || opcode->opcode == 0xd63f0000)
call_p = true;
gas_assert (src_type != GINSN_SRC_UNKNOWN);
if (call_p)
ginsn = ginsn_new_call (insn_end_sym, true,
src_type, src_value, src_symbol);
else
ginsn = ginsn_new_jump (insn_end_sym, true,
src_type, src_value, src_symbol);
ginsn_set_where (ginsn);
return ginsn;
}
static ginsnS *
aarch64_ginsn_jump_cond (const symbolS *insn_end_sym)
{
ginsnS *ginsn = NULL;
const symbolS *src_symbol;
enum ginsn_src_type src_type = GINSN_SRC_SYMBOL;
aarch64_inst *base = &inst.base;
const aarch64_opcode *opcode = base->opcode;
aarch64_insn opc = opcode->opcode;
gas_assert ((opc == 0x34000000 || opc == 0x35000000
|| opc == 0x36000000 || opc == 0x37000000)
|| (opc >= 0x54000000 && opc <= 0x5400000d));
gas_assert (inst.reloc.type == BFD_RELOC_AARCH64_BRANCH19
|| inst.reloc.type == BFD_RELOC_AARCH64_TSTBR14);
src_symbol = inst.reloc.exp.X_add_symbol;
ginsn = ginsn_new_jump_cond (insn_end_sym, true,
src_type, 0, src_symbol);
ginsn_set_where (ginsn);
return ginsn;
}
static ginsnS *
aarch64_ginsn_mov (const symbolS *insn_end_sym)
{
ginsnS *ginsn = NULL;
uint32_t src_reg = 0, dst_reg;
aarch64_opnd_info *src, *dst;
offsetT src_imm = 0;
enum ginsn_src_type src_type;
aarch64_inst *base = &inst.base;
const aarch64_opcode *opcode = base->opcode;
gas_assert (aarch64_num_of_operands (opcode) == 2);
dst = &base->operands[0];
src = &base->operands[1];
dst_reg = ginsn_dw2_regnum (dst, true);
if (src->type == AARCH64_OPND_IMM_MOV
&& aarch64_gas_internal_fixup_p () && inst.reloc.exp.X_op == O_constant)
{
src_imm = inst.reloc.exp.X_add_number;
src_type = GINSN_SRC_IMM;
}
else
{
/* mov x27, sp. */
src_reg = ginsn_dw2_regnum (src, true);
src_type = GINSN_SRC_REG;
}
ginsn = ginsn_new_mov (insn_end_sym, false,
src_type, src_reg, src_imm,
GINSN_DST_REG, dst_reg, 0);
ginsn_set_where (ginsn);
return ginsn;
}
/* Check if an instruction is whitelisted.
An instruction is a candidate for whitelisting if not generating ginsn
for it, does not affect SCFI correctness. */
static bool
aarch64_ginsn_safe_to_skip_p (void)
{
bool skip_p = false;
aarch64_opnd_info *opnd = NULL;
uint32_t opnd_reg;
aarch64_inst *base = &inst.base;
const aarch64_opcode *opcode = base->opcode;
int num_opnds = aarch64_num_of_operands (opcode);
aarch64_opnd_info *addr = &base->operands[num_opnds - 1];
/* It is not expected to have reg offset based ld/st ops to be used
for reg save and restore operations. Warn the user though. */
if (opcode->iclass == ldst_regoff)
{
opnd = &base->operands[0];
opnd_reg = ginsn_dw2_regnum (opnd, false);
if (aarch64_scfi_callee_saved_p (opnd_reg))
{
skip_p = true;
as_warn ("SCFI: ignored probable save/restore op with reg offset");
}
}
switch (opcode->opcode)
{
/* Load/store FP register pair (offset) are safe to skip even if the base
register is SP-based because there cannot be writeback with pre- or
post- indexing. */
case 0x2d000000: /* ldstpair_off. Store FP register pair (offset). */
case 0x2d400000: /* ldstpair_off. Load FP register pair (offset). */
case 0x3d000000: /* ldst_pos. Store FP register (unsigned immediate). */
case 0x3d400000: /* ldst_pos. Load FP register (unsigned immediate). */
/* IIUC, there cannot be a writeback here. Confirm? FIXME.
if not true, need to generate addr gen opcodes at the least. */
gas_assert (!addr->addr.writeback);
skip_p = true;
break;
default:
break;
}
return skip_p;
}
#define AARCH64_GINSN_UNHANDLED_NONE 0
#define AARCH64_GINSN_UNHANDLED_DEST_REG 1
#define AARCH64_GINSN_UNHANDLED_CFG 2
#define AARCH64_GINSN_UNHANDLED_STACKOP 3
#define AARCH64_GINSN_UNHANDLED_UNEXPECTED 4
/* Check the input insn for its impact on the correctness of the synthesized
CFI. Returns an error code to the caller. */
static int
aarch64_ginsn_unhandled (void)
{
int err = AARCH64_GINSN_UNHANDLED_NONE;
aarch64_inst *base = &inst.base;
const aarch64_opcode *opcode = base->opcode;
aarch64_opnd_info *dest = &base->operands[0];
int num_opnds = aarch64_num_of_operands (opcode);
aarch64_opnd_info *addr;
unsigned int dw2_regnum;
uint32_t addr_reg;
bool sp_allowed_p = false;
/* All change of flow instructions are important for SCFI. */
if (opcode->iclass == condbranch
|| opcode->iclass == compbranch
|| opcode->iclass == testbranch
|| opcode->iclass == branch_imm
|| opcode->iclass == branch_reg)
err = AARCH64_GINSN_UNHANDLED_CFG;
/* Also, any memory instructions that may involve an update to the stack
pointer. Some classes can be skipped altogether though, as they cannot be
used to push or pop stack because of disallowed writeback:
- ldst_unscaled, ldst_regoff, ldst_unpriv, ldstexcl, loadlit,
ldstnapair_offs. FIXME double-check. */
else if (opcode->iclass == ldstpair_off
|| opcode->iclass == ldstpair_indexed
|| opcode->iclass == ldst_imm9
|| opcode->iclass == ldst_imm10
|| opcode->iclass == ldst_pos)
{
addr = &base->operands[num_opnds - 1];
addr_reg = ginsn_dw2_regnum (addr, true);
/* For all skipped memory operations, check if an update to REG_SP or
REG_FP is involved. */
if ((addr_reg == REG_SP || addr_reg == REG_FP)
&& (addr->addr.postind || addr->addr.preind) && addr->addr.writeback)
err = AARCH64_GINSN_UNHANDLED_STACKOP;
}
/* Finally, also check if the missed instructions are affecting REG_SP or
REG_FP. */
else if (dest && (dest->type == AARCH64_OPND_Rd
|| dest->type == AARCH64_OPND_Rd_SP))
{
sp_allowed_p = (dest->type == AARCH64_OPND_Rd_SP) ? true : false;
dw2_regnum = ginsn_dw2_regnum (dest, sp_allowed_p);
if (dw2_regnum == REG_SP || dw2_regnum == REG_FP)
err = AARCH64_GINSN_UNHANDLED_DEST_REG;
}
return err;
}
/* Generate one or more generic GAS instructions, a.k.a, ginsns for the current
machine instruction.
Returns the head of linked list of ginsn(s) added, if success; Returns NULL
if failure.
The input ginsn_gen_mode GMODE determines the set of minimal necessary
ginsns necessary for correctness of any passes applicable for that mode.
For supporting the GINSN_GEN_SCFI generation mode, following is the list of
machine instructions that must be translated into the corresponding ginsns
to ensure correctness of SCFI:
- 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.
- All change of flow instructions: conditional and unconditional branches,
call and return from functions.
- All instructions that can potentially be a register save / restore
operations.
- All instructions that may update the stack pointer: pre-indexed and
post-indexed stack operations with writeback.
The function currently supports GINSN_GEN_SCFI ginsn generation mode only.
To support other generation modes 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
possible starting points.
- Also note that ginsn representation may need enhancements. Specifically,
note some TBD_GINSN_INFO_LOSS and TBD_GINSN_REPRESENTATION_LIMIT markers.
*/
static ginsnS *
aarch64_ginsn_new (const symbolS *insn_end_sym, enum ginsn_gen_mode gmode)
{
int err = 0;
ginsnS *ginsn = NULL;
unsigned int dw2_regnum;
aarch64_opnd_info *dest = NULL;
bool sp_allowed_p = false;
aarch64_inst *base = &inst.base;
const aarch64_opcode *opcode = base->opcode;
/* 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;
switch (opcode->opcode)
{
case 0x0b200000: /* addsub_ext. add (extended register). */
case 0x4b200000: /* addsub_ext. sub (extended register). */
ginsn = aarch64_ginsn_addsub (insn_end_sym);
break;
case 0x2c800000: /* ldstpair_indexed. Store fp register pair. */
case 0x28800000: /* ldstpair_indexed. Store register pair. */
/* pre-index: stp x29, x30, [sp, -128]!
post-index: stp x29, x30, [sp] -128. */
case 0x29000000: /* ldstpair_off. Store register pair (offset). */
/* stp x19, x20, [sp, 16]. */
ginsn = aarch64_ginsn_ldstp (insn_end_sym, true);
break;
case 0x2cc00000: /* ldstpair_indexed. Load fp register pair. */
case 0x28c00000: /* ldstpair_indexed. Load register pair. */
/* pre-index: ldp x29, x30, [sp, -128]!
post-index: ldp x29, x30, [sp], -128. */
case 0x29400000: /* ldstpair_off. Load register pair (offset). */
/* ldp x19, x20, [sp, 16]. */
ginsn = aarch64_ginsn_ldstp (insn_end_sym, false);
break;
case 0x38000400: /* ldst_imm9. Store register (immediate indexed). */
/* strb w7, [sp, 255]! */
case 0x3c000400: /* ldst_imm9. Store FP register (immediate indexed). */
/* str q29, [sp, 44]! */
case 0xb8000400: /* ldst_imm9. Store register (immediate indexed). */
/* str x19, [sp], 32 */
case 0x78000400: /* ldst_imm9. strh. */
case 0xb9000000: /* ldst_pos. Store register (unsigned immediate). */
/* str x19, [sp] */
ginsn = aarch64_ginsn_ldstr (insn_end_sym, true);
break;
case 0x38400400: /* ldst_imm9. Load register (immediate indexed). */
/* ldrb w7, [sp, 255]! */
case 0x3c400400: /* ldst_imm9. Load FP register (immediate indexed). */
/* ldr q29, [sp, 44]! */
case 0xb8400400: /* ldst_imm9. Load register (immediate indexed). */
/* ldr x19, [sp], 32 */
/* Following sub-word loads can affect stack-pointer due to indexed
addressing mode. */
case 0x38800400: /* ldst_imm9. ldrsb. */
case 0x78400400: /* ldst_imm9. ldrh. */
case 0x78800400: /* ldst_imm9. ldrsh. */
case 0xb8800400: /* ldst_imm9. ldrsw. */
case 0xb9400000: /* ldst_pos. Load register (unsigned immediate). */
/* ldr x19, [sp] */
ginsn = aarch64_ginsn_ldstr (insn_end_sym, false);
break;
case 0x51000000:
/* sub. */
ginsn = aarch64_ginsn_addsub_imm (insn_end_sym);
break;
case 0x52800000:
ginsn = aarch64_ginsn_mov (insn_end_sym);
break;
case 0x11000000:
if (opcode->mask == 0x7ffffc00)
ginsn = aarch64_ginsn_mov (insn_end_sym);
else if (opcode->mask == 0x7f000000)
ginsn = aarch64_ginsn_addsub_imm (insn_end_sym);
break;
case 0xd65f0000:
ginsn = ginsn_new_return (insn_end_sym, true);
ginsn_set_where (ginsn);
break;
case 0x14000000: /* b. */
case 0xd61f0000: /* br. */
case 0x94000000: /* bl. */
case 0xd63f0000: /* blr. */
ginsn = aarch64_ginsn_jump (insn_end_sym);
break;
case 0x34000000: /* cbz. */
case 0x35000000: /* cbnz. */
/* Although cbz/cbnz has an additional operand and are functionally
distinct from conditional branches, it is fine to use the same ginsn
type for both from the perspective of SCFI. */
case 0x36000000: /* tbz. */
case 0x37000000: /* tbnz. */
case 0x54000000 ... 0x5400000d:
ginsn = aarch64_ginsn_jump_cond (insn_end_sym);
break;
default:
/* TBD_GINSN_GEN_NOT_SCFI: Skip all other opcodes uninteresting for
GINSN_GEN_SCFI mode. */
break;
}
if (!ginsn && !aarch64_ginsn_safe_to_skip_p ())
{
/* For all unhandled insns, check that they no not impact SCFI
correctness. */
err = aarch64_ginsn_unhandled ();
switch (err)
{
case AARCH64_GINSN_UNHANDLED_NONE:
break;
case AARCH64_GINSN_UNHANDLED_DEST_REG:
/* Not all writes to REG_FP are harmful in context of SCFI. Simply
generate a GINSN_TYPE_OTHER with destination set to the
appropriate register. The SCFI machinery will bail out if this
ginsn affects SCFI correctness. */
dest = &base->operands[0];
sp_allowed_p = (dest->type == AARCH64_OPND_Rd_SP) ? true : false;
dw2_regnum = ginsn_dw2_regnum (dest, sp_allowed_p);
ginsn = ginsn_new_other (insn_end_sym, true,
GINSN_SRC_IMM, 0,
GINSN_SRC_IMM, 0,
GINSN_DST_REG, dw2_regnum);
ginsn_set_where (ginsn);
break;
case AARCH64_GINSN_UNHANDLED_CFG:
case AARCH64_GINSN_UNHANDLED_STACKOP:
as_bad (_("SCFI: unhandled op %#x may cause incorrect CFI"),
opcode->opcode);
break;
case AARCH64_GINSN_UNHANDLED_UNEXPECTED:
as_bad (_("SCFI: unexpected op %#x may cause incorrect CFI"),
opcode->opcode);
break;
default:
abort ();
break;
}
}
return ginsn;
}
#endif /* OBJ_ELF */
static void
output_inst (struct aarch64_inst *new_inst)
{
@@ -8520,6 +9240,16 @@ md_assemble (char *str)
output_inst (copy);
}
#ifdef OBJ_ELF
if (flag_synth_cfi)
{
ginsnS *ginsn;
ginsn = aarch64_ginsn_new (symbol_temp_new_now (),
frch_ginsn_gen_mode ());
frch_ginsn_data_append (ginsn);
}
#endif
/* Issue non-fatal messages if any. */
output_operand_error_report (str, true);
return;

View File

@@ -263,6 +263,26 @@ extern void aarch64_after_parse_args (void);
#ifdef OBJ_ELF
#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, FP, and all callee-saved registers. For Aarch64, this means 31. */
# define SCFI_MAX_REG_ID 31
/* Identify the DWARF register number of the frame-pointer register. */
# define REG_FP 29
/* Identify the DWARF register number of the link register. */
# define REG_LR 30
/* Identify the DWARF register number of the stack-pointer register. */
# define REG_SP 31
#define SCFI_INIT_CFA_OFFSET 0
#define SCFI_CALLEE_SAVED_REG_P(dw2reg) aarch64_scfi_callee_saved_p (dw2reg)
extern bool aarch64_scfi_callee_saved_p (uint32_t dw2reg_num);
/* Whether SFrame stack trace info is supported. */
extern bool aarch64_support_sframe_p (void);
#define support_sframe_p aarch64_support_sframe_p

View File

@@ -5387,9 +5387,8 @@ x86_scfi_callee_saved_p (unsigned int dw2reg_num)
return false;
}
/* Check whether an instruction prefix which affects operation size
accompanies. For insns in the legacy space, setting REX.W takes precedence
over the operand-size prefix (66H) when both are used.
/* Check whether the instruction affecting stack implicitly has a 16-bit
operation size.
The current users of this API are in the handlers for PUSH, POP or other
instructions which affect the stack pointer implicitly: the operation size
@@ -5397,9 +5396,20 @@ x86_scfi_callee_saved_p (unsigned int dw2reg_num)
incremented / decremented (2, 4 or 8). */
static bool
ginsn_opsize_prefix_p (void)
ginsn_implicitstackop_opsize16_p (void)
{
return (!(i.prefix[REX_PREFIX] & REX_W) && i.prefix[DATA_PREFIX]);
bool opsize_prefix_p = false;
if (i.tm.opcode_modifier.operandconstraint != IMPLICIT_STACK_OP)
return opsize_prefix_p;
/* For insns in the legacy space, setting REX.W takes precedence
over the operand-size prefix (66H) when both are used. */
opsize_prefix_p |= !(i.prefix[REX_PREFIX] & REX_W) && i.prefix[DATA_PREFIX];
opsize_prefix_p |= (i.tm.opcode_space == SPACE_EVEXMAP4
&& i.tm.opcode_modifier.opcodeprefix == PREFIX_0X66);
return opsize_prefix_p;
}
/* Get the DWARF register number for the given register entry.
@@ -5542,7 +5552,7 @@ x86_ginsn_addsub_mem_reg (const symbolS *insn_end_sym)
}
else if (i.mem_operands)
{
mem_reg = (i.base_reg) ? i.base_reg : i.index_reg;
mem_reg = i.base_reg ? i.base_reg : i.index_reg;
src1_dw2_regnum = ginsn_dw2_regnum (mem_reg);
if (i.disp_operands == 1)
gdisp = i.op[0].disps->X_add_number;
@@ -5671,7 +5681,7 @@ x86_ginsn_move (const symbolS *insn_end_sym)
/* mov disp(%reg), %reg. */
if (i.mem_operands)
{
src = (i.base_reg) ? i.base_reg : i.index_reg;
src = i.base_reg ? i.base_reg : i.index_reg;
if (i.disp_operands == 1)
src_disp = i.op[0].disps->X_add_number;
src_type = GINSN_SRC_INDIRECT;
@@ -5687,7 +5697,7 @@ x86_ginsn_move (const symbolS *insn_end_sym)
src = i.op[0].regs;
if (i.mem_operands)
{
dst = (i.base_reg) ? i.base_reg : i.index_reg;
dst = i.base_reg ? i.base_reg : i.index_reg;
if (i.disp_operands == 1)
dst_disp = i.op[1].disps->X_add_number;
dst_type = GINSN_DST_INDIRECT;
@@ -5815,6 +5825,54 @@ x86_ginsn_jump (const symbolS *insn_end_sym, bool cond_p)
return ginsn;
}
static ginsnS *
x86_ginsn_indirect_jump_call (const symbolS *insn_end_sym)
{
ginsnS *ginsn = NULL;
const reg_entry *mem_reg;
unsigned int dw2_regnum;
ginsnS * (*ginsn_func) (const symbolS *sym, bool real_p,
enum ginsn_src_type src_type, unsigned int src_reg,
const symbolS *src_ginsn_sym);
if (i.tm.extension_opcode == 4)
/* 0xFF /4 (jmp r/m). */
ginsn_func = ginsn_new_jump;
else if (i.tm.extension_opcode == 2)
/* 0xFF /2 (call). */
ginsn_func = ginsn_new_call;
else
/* Other cases are not expected. Caller must screen. */
return ginsn;
if (i.reg_operands)
{
dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
ginsn = ginsn_func (insn_end_sym, true,
GINSN_SRC_REG, dw2_regnum, NULL);
ginsn_set_where (ginsn);
}
else if (i.mem_operands)
{
mem_reg = i.base_reg ? i.base_reg : i.index_reg;
/* Use dummy register if no base or index. Unlike other opcodes,
where we simply return NULL, we must try to generate a ginsn here.
Otherwise, the user gets the impression of missing functionality:
- a call insn is an IMPLICIT_STACK_OP.
- jmp insns are necessary for accurate control flow. */
dw2_regnum = (mem_reg
? ginsn_dw2_regnum (mem_reg)
: GINSN_DW2_REGNUM_RSI_DUMMY);
/* jmp/call *sym(,%rN,imm) or jmp/call *sym(%rN). */
ginsn = ginsn_func (insn_end_sym, true,
GINSN_SRC_REG, dw2_regnum, NULL);
ginsn_set_where (ginsn);
}
return ginsn;
}
static ginsnS *
x86_ginsn_enter (const symbolS *insn_end_sym)
{
@@ -5835,7 +5893,7 @@ x86_ginsn_enter (const symbolS *insn_end_sym)
}
/* Check if this is a 16-bit op. */
if (ginsn_opsize_prefix_p ())
if (ginsn_implicitstackop_opsize16_p ())
stack_opnd_size = 2;
/* If the nesting level is 0, the processor pushes the frame pointer from
@@ -5872,7 +5930,7 @@ x86_ginsn_leave (const symbolS *insn_end_sym)
int stack_opnd_size = 8;
/* Check if this is a 16-bit op. */
if (ginsn_opsize_prefix_p ())
if (ginsn_implicitstackop_opsize16_p ())
stack_opnd_size = 2;
/* The 'leave' instruction copies the contents of the RBP register
@@ -6049,16 +6107,6 @@ x86_ginsn_new (const symbolS *insn_end_sym, enum ginsn_gen_mode gmode)
opcode = i.tm.base_opcode;
/* Until it is clear how to handle APX NDD and other new opcodes, disallow
them from SCFI. */
if (is_apx_rex2_encoding ()
|| (i.tm.opcode_modifier.evex && is_apx_evex_encoding ()))
{
as_bad (_("SCFI: unsupported APX op %#x may cause incorrect CFI"),
opcode);
return ginsn;
}
switch (opcode)
{
@@ -6088,7 +6136,7 @@ x86_ginsn_new (const symbolS *insn_end_sym, enum ginsn_gen_mode gmode)
break;
dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
/* Check if operation size is 16-bit. */
if (ginsn_opsize_prefix_p ())
if (ginsn_implicitstackop_opsize16_p ())
stack_opnd_size = 2;
ginsn = ginsn_new_sub (insn_end_sym, false,
GINSN_SRC_REG, REG_SP, 0,
@@ -6109,7 +6157,7 @@ x86_ginsn_new (const symbolS *insn_end_sym, enum ginsn_gen_mode gmode)
break;
dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
/* Check if operation size is 16-bit. */
if (ginsn_opsize_prefix_p ())
if (ginsn_implicitstackop_opsize16_p ())
stack_opnd_size = 2;
ginsn = ginsn_new_load (insn_end_sym, false,
GINSN_SRC_INDIRECT, REG_SP, 0,
@@ -6129,7 +6177,7 @@ x86_ginsn_new (const symbolS *insn_end_sym, enum ginsn_gen_mode gmode)
/* push reg. */
dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
/* Check if operation size is 16-bit. */
if (ginsn_opsize_prefix_p ())
if (ginsn_implicitstackop_opsize16_p ())
stack_opnd_size = 2;
ginsn = ginsn_new_sub (insn_end_sym, false,
GINSN_SRC_REG, REG_SP, 0,
@@ -6153,7 +6201,7 @@ x86_ginsn_new (const symbolS *insn_end_sym, enum ginsn_gen_mode gmode)
GINSN_DST_REG, dw2_regnum);
ginsn_set_where (ginsn);
/* Check if operation size is 16-bit. */
if (ginsn_opsize_prefix_p ())
if (ginsn_implicitstackop_opsize16_p ())
stack_opnd_size = 2;
ginsn_next = ginsn_new_add (insn_end_sym, false,
GINSN_SRC_REG, REG_SP, 0,
@@ -6168,7 +6216,7 @@ x86_ginsn_new (const symbolS *insn_end_sym, enum ginsn_gen_mode gmode)
if (i.tm.opcode_space != SPACE_BASE)
break;
/* Check if operation size is 16-bit. */
if (ginsn_opsize_prefix_p ())
if (ginsn_implicitstackop_opsize16_p ())
stack_opnd_size = 2;
/* Skip getting the value of imm from machine instruction
because this is not important for SCFI. */
@@ -6184,7 +6232,7 @@ x86_ginsn_new (const symbolS *insn_end_sym, enum ginsn_gen_mode gmode)
gas_assert (!ginsn_link_next (ginsn, ginsn_next));
break;
/* PS: Opcodes 0x80 ... 0x8f with opcode_space SPACE_0F are present
/* PS: JCC Opcodes 0x80 ... 0x8f (opcode_space SPACE_0F) are present
only after relaxation. They do not need to be handled for ginsn
creation. */
case 0x70 ... 0x7f:
@@ -6223,7 +6271,7 @@ x86_ginsn_new (const symbolS *insn_end_sym, enum ginsn_gen_mode gmode)
/* pop to reg/mem. */
if (i.mem_operands)
{
mem_reg = (i.base_reg) ? i.base_reg : i.index_reg;
mem_reg = i.base_reg ? i.base_reg : i.index_reg;
/* Use dummy register if no base or index. Unlike other opcodes,
ginsns must be generated as this affect stack pointer. */
dw2_regnum = (mem_reg
@@ -6237,7 +6285,7 @@ x86_ginsn_new (const symbolS *insn_end_sym, enum ginsn_gen_mode gmode)
GINSN_DST_INDIRECT, dw2_regnum);
ginsn_set_where (ginsn);
/* Check if operation size is 16-bit. */
if (ginsn_opsize_prefix_p ())
if (ginsn_implicitstackop_opsize16_p ())
stack_opnd_size = 2;
ginsn_next = ginsn_new_add (insn_end_sym, false,
GINSN_SRC_REG, REG_SP, 0,
@@ -6252,7 +6300,7 @@ x86_ginsn_new (const symbolS *insn_end_sym, enum ginsn_gen_mode gmode)
break;
/* pushf / pushfq. */
/* Check if operation size is 16-bit. */
if (ginsn_opsize_prefix_p ())
if (ginsn_implicitstackop_opsize16_p ())
stack_opnd_size = 2;
ginsn = ginsn_new_sub (insn_end_sym, false,
GINSN_SRC_REG, REG_SP, 0,
@@ -6275,7 +6323,7 @@ x86_ginsn_new (const symbolS *insn_end_sym, enum ginsn_gen_mode gmode)
break;
/* popf / popfq. */
/* Check if operation size is 16-bit. */
if (ginsn_opsize_prefix_p ())
if (ginsn_implicitstackop_opsize16_p ())
stack_opnd_size = 2;
/* FIXME - hardcode the actual DWARF reg number value. As for SCFI
correctness, although this behaves simply a placeholder value; its
@@ -6300,7 +6348,7 @@ x86_ginsn_new (const symbolS *insn_end_sym, enum ginsn_gen_mode gmode)
if (i.tm.extension_opcode == 6)
{
/* Check if operation size is 16-bit. */
if (ginsn_opsize_prefix_p ())
if (ginsn_implicitstackop_opsize16_p ())
stack_opnd_size = 2;
ginsn = ginsn_new_sub (insn_end_sym, false,
GINSN_SRC_REG, REG_SP, 0,
@@ -6309,7 +6357,7 @@ x86_ginsn_new (const symbolS *insn_end_sym, enum ginsn_gen_mode gmode)
ginsn_set_where (ginsn);
if (i.mem_operands)
{
mem_reg = (i.base_reg) ? i.base_reg : i.index_reg;
mem_reg = i.base_reg ? i.base_reg : i.index_reg;
/* Use dummy register if no base or index. Unlike other opcodes,
ginsns must be generated as this affect stack pointer. */
dw2_regnum = (mem_reg
@@ -6324,50 +6372,9 @@ x86_ginsn_new (const symbolS *insn_end_sym, enum ginsn_gen_mode gmode)
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 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 if (i.tm.extension_opcode == 4 || i.tm.extension_opcode == 2)
ginsn = x86_ginsn_indirect_jump_call (insn_end_sym);
break;
case 0xc2: /* ret imm16. */

1012
gas/doc/scfi/scfi-paper.tex Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -614,6 +614,8 @@ gbb_cleanup (gbbS **bbp)
*bbp = NULL;
}
/* Add an edge from the source bb FROM_BB to the sink bb TO_BB. */
static void
bb_add_edge (gbbS* from_bb, gbbS *to_bb)
{
@@ -638,7 +640,7 @@ bb_add_edge (gbbS* from_bb, gbbS *to_bb)
}
else
{
/* Get the tail of the list. */
/* Get the head of the list. */
tmpedge = from_bb->out_gedges;
while (tmpedge)
{
@@ -689,6 +691,9 @@ static gbbS *
add_bb_at_ginsn (const symbolS *func, gcfgS *gcfg, ginsnS *ginsn, gbbS *prev_bb,
int *errp);
/* Return the already existing basic block (if present), which begins with
GINSN, in the given GCFG. Return NULL otherwise. */
static gbbS *
find_bb (gcfgS *gcfg, ginsnS *ginsn)
{
@@ -708,13 +713,18 @@ find_bb (gcfgS *gcfg, ginsnS *ginsn)
break;
}
}
/* Must be found if ginsn is visited. */
/* Must be found because ginsn is visited. */
gas_assert (found_bb);
}
return found_bb;
}
/* Get the basic block starting at GINSN in the GCFG.
If not already present, the function will make one, while adding an edge
from the PREV_BB to it. */
static gbbS *
find_or_make_bb (const symbolS *func, gcfgS *gcfg, ginsnS *ginsn, gbbS *prev_bb,
int *errp)
@@ -722,26 +732,40 @@ find_or_make_bb (const symbolS *func, gcfgS *gcfg, ginsnS *ginsn, gbbS *prev_bb,
gbbS *found_bb = NULL;
found_bb = find_bb (gcfg, ginsn);
if (found_bb)
return found_bb;
if (!found_bb)
found_bb = add_bb_at_ginsn (func, gcfg, ginsn, prev_bb, errp);
return add_bb_at_ginsn (func, gcfg, ginsn, prev_bb, errp);
gas_assert (found_bb);
gas_assert (found_bb->first_ginsn == ginsn);
return found_bb;
}
/* 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.
/* Add basic block(s) for all reachable, unvisited ginsns, starting from GINSN,
to the given GCFG. Also add an edge from the PREV_BB to the root of the
newly added basic block(s).
This is a recursive function which returns the root of the added
basic blocks. */
This is a recursive function which returns the root of the added basic
blocks. */
static gbbS *
add_bb_at_ginsn (const symbolS *func, gcfgS *gcfg, ginsnS *ginsn, gbbS *prev_bb,
int *errp)
{
gbbS *root_bb = NULL;
gbbS *current_bb = NULL;
ginsnS *target_ginsn = NULL;
const symbolS *taken_label;
/* Create a new bb. N.B. The caller must ensure bb with this ginsn does not
already exist. */
gas_assert (!find_bb (gcfg, ginsn));
root_bb = XCNEW (gbbS);
cfg_add_bb (gcfg, root_bb);
root_bb->first_ginsn = ginsn;
current_bb = root_bb;
while (ginsn)
{
/* Skip these as they may be right after a GINSN_TYPE_RETURN.
@@ -749,6 +773,8 @@ add_bb_at_ginsn (const symbolS *func, gcfgS *gcfg, ginsnS *ginsn, gbbS *prev_bb,
end of bb, and a logical exit from function. */
if (GINSN_F_FUNC_END_P (ginsn))
{
/* Dont mark them visited yet though, leaving the option of these
being visited via other control flows as applicable. */
ginsn = ginsn->next;
continue;
}
@@ -765,27 +791,27 @@ add_bb_at_ginsn (const symbolS *func, gcfgS *gcfg, ginsnS *ginsn, gbbS *prev_bb,
bb_add_edge (prev_bb, current_bb);
break;
}
else if (current_bb && GINSN_F_USER_LABEL_P (ginsn))
else if (current_bb && current_bb->first_ginsn != ginsn
&& GINSN_F_USER_LABEL_P (ginsn))
{
/* Create new bb starting at this label ginsn. */
/* Create new bb starting at ginsn for (user-defined) label. This is
likely going to be a destination of a some control flow. */
prev_bb = current_bb;
find_or_make_bb (func, gcfg, ginsn, prev_bb, errp);
current_bb = find_or_make_bb (func, gcfg, ginsn, prev_bb, errp);
bb_add_edge (prev_bb, current_bb);
break;
}
if (current_bb == NULL)
{
/* Create a new bb. */
current_bb = XCNEW (gbbS);
cfg_add_bb (gcfg, current_bb);
current_bb->first_ginsn = ginsn;
/* 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;
@@ -809,16 +835,21 @@ add_bb_at_ginsn (const symbolS *func, gcfgS *gcfg, ginsnS *ginsn, gbbS *prev_bb,
taken_label = ginsn->src[0].sym;
gas_assert (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. */
/* Preserve the prev_bb to be the source 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);
{
current_bb = find_or_make_bb (func, gcfg, target_ginsn,
prev_bb, errp);
gas_assert (prev_bb);
bb_add_edge (prev_bb, current_bb);
current_bb = NULL;
}
else
{
*errp = GCFG_JLABEL_NOT_PRESENT;
@@ -826,27 +857,39 @@ add_bb_at_ginsn (const symbolS *func, gcfgS *gcfg, ginsnS *ginsn, gbbS *prev_bb,
_("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);
if (ginsn->type == GINSN_TYPE_JUMP_COND)
{
/* Add the bb for the fall through path. */
current_bb = find_or_make_bb (func, gcfg, ginsn->next,
prev_bb, errp);
gas_assert (prev_bb);
bb_add_edge (prev_bb, current_bb);
current_bb = NULL;
}
else
/* Unconditional jump. Current BB has been processed. */
current_bb = NULL;
}
else
{
gas_assert (ginsn->type == GINSN_TYPE_RETURN
|| (ginsn->type == GINSN_TYPE_JUMP
&& !ginsn_direct_local_jump_p (ginsn)));
/* Current BB has been processed. */
current_bb = NULL;
/* We'll come back to the ginsns following GINSN_TYPE_RETURN or
other (non-local) unconditional jmps from another path if they
are indeed reachable code. */
break;
}
/* Current BB has been processed. */
current_bb = NULL;
}
ginsn = ginsn->next;
}
return current_bb;
return root_bb;
}
static int

View File

@@ -155,7 +155,7 @@ cmp_scfi_state (scfi_stateS *state1, scfi_stateS *state2)
int ret;
if (!state1 || !state2)
ret = 1;
return 1;
/* Skip comparing the scratch[] value of registers. The user visible
unwind information is derived from the regs[] from the SCFI state. */
@@ -164,10 +164,12 @@ cmp_scfi_state (scfi_stateS *state1, scfi_stateS *state2)
/* For user functions which perform dynamic stack allocation, after switching
t REG_FP based CFA tracking, it is perfectly possible to have stack usage
in some control flows. However, double-checking that all control flows
have the same idea of CFA tracking before this wont hurt. */
gas_assert (state1->regs[REG_CFA].base == state2->regs[REG_CFA].base);
if (state1->regs[REG_CFA].base == REG_SP)
in some control flows. Further, the different control flows may even not
have the same idea of CFA tracking (likely later necessitating generation
of .cfi_remember_state / .cfi_restore_state pair). */
ret |= state1->regs[REG_CFA].base != state2->regs[REG_CFA].base;
if (!ret && state1->regs[REG_CFA].base == REG_SP)
ret |= state1->stack_size != state2->stack_size;
ret |= state1->traceable_p != state2->traceable_p;
@@ -911,11 +913,11 @@ gen_scfi_ops (ginsnS *ginsn, scfi_stateS *state)
return ret;
}
/* Recursively perform forward flow of the (unwind information) SCFI state
/* 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.
The core of forward flow process takes the SCFI state at the entry of a bb
and updates it incrementally as per the semantics of each ginsn in the bb.
Returns error code, if any. */
@@ -961,6 +963,8 @@ forward_flow_scfi_state (gcfgS *gcfg, gbbS *gbb, scfi_stateS *state)
bb_for_each_edge(gbb, gedge)
{
gbb = gedge->dst_bb;
/* Ensure that the state is the one from the exit of the prev bb. */
memcpy (state, prev_bb->exit_state, sizeof (scfi_stateS));
if (gbb->visited)
{
ret = cmp_scfi_state (gbb->entry_state, state);

View File

@@ -1,6 +1,6 @@
Notes on the SCFI testsuite in GAS:
* At this time, SCFI machinery is only supported for x86_64.
* At this time, SCFI machinery is only supported for x86_64 and aarch64.
* When adding more tests, please keep CFI annotations updated in the .s files.
Ideally the test should be run with and without --scfi (as is done currently

View File

@@ -0,0 +1,30 @@
.*: Assembler messages:
.*:16: Error: untraceable control flow for func 'foo'
AARCH64 GAS .*
1 # Testcase for a variety of change of flow instructions
2 # Because some of these are indirect branches, SCFI will bail out
3 # with an error. This test merely checks that the ginsn creation
4 # process can handle these insns gracefully.
5 .text
6 .align 2
7 .global foo
8 .type foo, %function
8 ginsn: SYM FUNC_BEGIN
9 foo:
9 ginsn: SYM foo
10 \?\?\?\? 00000094 bl dump_bt
10 ginsn: CALL
11 \?\?\?\? 02000014 b .L3
11 ginsn: JMP
12 \?\?\?\? 20021FD6 br x17
12 ginsn: JMP %r17,
13 .L3:
13 ginsn: SYM .L3
14 \?\?\?\? 60003FD6 blr x3
14 ginsn: CALL
15 \?\?\?\? C0035FD6 ret
15 ginsn: RET
16 .size foo, .-foo
16 ginsn: SYM FUNC_END

View File

@@ -0,0 +1,16 @@
# Testcase for a variety of change of flow instructions
# Because some of these are indirect branches, SCFI will bail out
# with an error. This test merely checks that the ginsn creation
# process can handle these insns gracefully.
.text
.align 2
.global foo
.type foo, %function
foo:
bl dump_bt
b .L3
br x17
.L3:
blr x3
ret
.size foo, .-foo

View File

@@ -0,0 +1,40 @@
AARCH64 GAS .*
1 # Testcase for a variety of ld st instructions
2 .text
3 .align 2
4 .global foo
5 .type foo, %function
5 ginsn: SYM FUNC_BEGIN
6 foo:
6 ginsn: SYM foo
7 # ldstpair_indexed
8 0000 FFFF8629 stp wzr, wzr, \[sp, 52\]!
8 ginsn: ADD %r31, 52, %r31
8 ginsn: STORE %r1, \[%r31\+0\]
8 ginsn: STORE %r1, \[%r31\+4\]
9 0004 E00782A9 stp x0, x1, \[sp, 32\]!
9 ginsn: ADD %r31, 32, %r31
9 ginsn: STORE %r0, \[%r31\+0\]
9 ginsn: STORE %r1, \[%r31\+8\]
10 0008 E827BC6D stp d8, d9, \[sp, -64\]!
10 ginsn: ADD %r31, -64, %r31
11 # Following is skipped from ginsn generation
12 000c E00702AD stp q0, q1, \[sp, 64\]
13 # ldstpair_off
14 0010 FFFF0629 stp wzr, wzr, \[sp, 52\]
14 ginsn: STORE %r1, \[%r31\+52\]
14 ginsn: STORE %r1, \[%r31\+56\]
15 0014 1F7840AD ldp q31, q30, \[x0\]
16 0018 E827C46C ldp d8, d9, \[sp\], 64
16 ginsn: ADD %r31, 64, %r31
17 # ldst_imm9
18 001c E7FF4F38 ldrb w7, \[sp, 255\]!
18 ginsn: ADD %r31, 255, %r31
18 ginsn: LOAD \[%r31\+0\], %r7
19 0020 FDCFC23C ldr q29, \[sp, 44\]!
19 ginsn: ADD %r31, 44, %r31
20 0024 C0035FD6 ret
20 ginsn: RET
21 .size foo, .-foo
21 ginsn: SYM FUNC_END

View File

@@ -0,0 +1,21 @@
# Testcase for a variety of ld st instructions
.text
.align 2
.global foo
.type foo, %function
foo:
# ldstpair_indexed
stp wzr, wzr, [sp, 52]!
stp x0, x1, [sp, 32]!
stp d8, d9, [sp, -64]!
# Following is skipped from ginsn generation
stp q0, q1, [sp, 64]
# ldstpair_off
stp wzr, wzr, [sp, 52]
ldp q31, q30, [x0]
ldp d8, d9, [sp], 64
# ldst_imm9
ldrb w7, [sp, 255]!
ldr q29, [sp, 44]!
ret
.size foo, .-foo

View File

@@ -0,0 +1,32 @@
.*: Assembler messages:
.*:9: Error: SCFI: unsupported stack manipulation pattern
.*:15: Error: SCFI: forward pass failed for func 'foo'
AARCH64 GAS .*
1 # Testcase for a variety of misc instructions
2 # Ensure graceful handling, irrespective of ginsn generation
3 symbol:
4 \?\?\?\? 7700 .string "w"
5
6 .type foo, %function
6 ginsn: SYM FUNC_BEGIN
7 foo:
7 ginsn: SYM foo
8 \?\?\?\? 00001024 mov x16, 4384
8 82D2
8 ginsn: MOV 4384, %r16
9 \?\?\?\? FF63308B add sp, sp, x16
9 ginsn: ADD %r31, %r16, %r31
10 \?\?\?\? 00000090 adrp x0, symbol
11 \?\?\?\? 1F000091 add sp, x0, :lo12:symbol
11 ginsn: OTH 0, 0, %r31
12 \?\?\?\? FF0F00D1 sub sp, sp, 3
12 ginsn: SUB %r31, 3, %r31
13 \?\?\?\? FF0F0091 add sp, sp, 3
13 ginsn: ADD %r31, 3, %r31
14 \?\?\?\? C0035FD6 ret
14 ginsn: RET
15 .size foo,.-foo
15 ginsn: SYM FUNC_END

View File

@@ -0,0 +1,15 @@
# Testcase for a variety of misc instructions
# Ensure graceful handling, irrespective of ginsn generation
symbol:
.string "w"
.type foo, %function
foo:
mov x16, 4384
add sp, sp, x16
adrp x0, symbol
add sp, x0, :lo12:symbol
sub sp, sp, 3
add sp, sp, 3
ret
.size foo,.-foo

View File

@@ -0,0 +1,59 @@
# 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 "aarch64-*-*"]) } then {
global ASFLAGS
set old_ASFLAGS "$ASFLAGS"
run_list_test "ginsn-cofi-1" "--scfi=experimental -ali --warn"
run_list_test "ginsn-ldst-1" "--scfi=experimental -ali --warn"
run_list_test "ginsn-misc-1" "--scfi=experimental -ali --warn"
run_list_test "scfi-diag-1" "--scfi=experimental"
run_list_test "scfi-unsupported-1" "--scfi=experimental"
run_dump_test "scfi-ldrp-1"
run_list_test "scfi-ldrp-1" "--scfi=experimental --warn"
run_dump_test "scfi-ldrp-2"
run_list_test "scfi-ldrp-2" "--scfi=experimental --warn"
run_dump_test "scfi-strp-1"
run_list_test "scfi-strp-1" "--scfi=experimental --warn"
run_dump_test "scfi-strp-2"
run_list_test "scfi-strp-2" "--scfi=experimental --warn"
run_dump_test "scfi-cb-1"
run_list_test "scfi-cb-1" "--scfi=experimental --warn"
run_dump_test "scfi-cond-br-1"
run_list_test "scfi-cond-br-1" "--scfi=experimental --warn"
run_dump_test "scfi-cfg-1"
run_list_test "scfi-cfg-1" "--scfi=experimental --warn"
run_dump_test "scfi-cfg-2"
run_list_test "scfi-cfg-2" "--scfi=experimental --warn"
run_dump_test "scfi-cfg-3"
run_list_test "scfi-cfg-3" "--scfi=experimental --warn"
}

View File

@@ -0,0 +1,20 @@
#as: --scfi=experimental -W
#objdump: -Wf
#name: Synthesize CFI for comp branch 1
#...
Contents of the .eh_frame section:
0+0000 0+0010 0+0000 CIE
Version: 1
Augmentation: "zR"
Code alignment factor: 4
Data alignment factor: -8
Return address column: 30
Augmentation data: 1b
DW_CFA_def_cfa: r31 \(sp\) ofs 0
0+0014 0+0010 00000018 FDE cie=00000000 pc=0+0000..0+0014
DW_CFA_nop
#pass

View File

@@ -0,0 +1,2 @@
.*Assembler messages:
.*5: Warning: SCFI ignores most user-specified CFI directives

View File

@@ -0,0 +1,14 @@
.text
.global foo
.type foo, %function
foo:
.cfi_startproc
.L7:
add w4, w3, w1
cbnz w4, .L7
cbz w4, .L10
tbnz w0, #31, .L7
.L10:
ret
.cfi_endproc
.size foo, .-foo

View File

@@ -0,0 +1,31 @@
#as: --scfi=experimental -W
#objdump: -Wf
#name: Synthesize CFI for cfg 1
#...
Contents of the .eh_frame section:
0+0000 0+0010 0+0000 CIE
Version: 1
Augmentation: "zR"
Code alignment factor: 4
Data alignment factor: -8
Return address column: 30
Augmentation data: 1b
DW_CFA_def_cfa: r31 \(sp\) ofs 0
0+0014 0+0020 0+0018 FDE cie=00000000 pc=0+0000..0+0068
DW_CFA_advance_loc: 4 to 0+0004
DW_CFA_def_cfa_offset: 48
DW_CFA_offset: r29 \(x29\) at cfa-48
DW_CFA_offset: r30 \(x30\) at cfa-40
DW_CFA_advance_loc: 4 to 0+0008
DW_CFA_def_cfa_register: r29 \(x29\)
DW_CFA_advance_loc: 92 to 0+0064
DW_CFA_def_cfa_register: r31 \(sp\)
DW_CFA_restore: r29 \(x29\)
DW_CFA_restore: r30 \(x30\)
DW_CFA_def_cfa_offset: 0
DW_CFA_nop
#pass

View File

@@ -0,0 +1,2 @@
.*Assembler messages:
.*6: Warning: SCFI ignores most user-specified CFI directives

View File

@@ -0,0 +1,46 @@
# Testcase for forward flow of SCFI information
# and CFG creation as well. This testcase has two backward edges
# (one of which is a loop) and one exit path.
.type foo, %function
foo:
.cfi_startproc
stp x29, x30, [sp, -48]!
.cfi_def_cfa_offset 48
.cfi_offset 29, -48
.cfi_offset 30, -40
mov x29, sp
.cfi_def_cfa_register 29
str x0, [sp, 24]
adrp x0, :got:xyz
str x0, [sp, 40]
b .L7
.L10:
ldr x0, [sp, 40]
ldr x0, [x0]
mov x1, x0
ldr x0, [sp, 24]
bl strcmp
cmp w0, 0
bne .L8
ldr x0, [sp, 40]
ldr w0, [x0, 8]
b .L9
.L8:
ldr x0, [sp, 40]
add x0, x0, 24
str x0, [sp, 40]
.L7:
ldr x0, [sp, 40]
ldr w0, [x0, 8]
cmp w0, 0
bne .L10
mov w0, 0
.L9:
ldp x29, x30, [sp], 48
.cfi_def_cfa_register 31
.cfi_restore 30
.cfi_restore 29
.cfi_def_cfa_offset 0
ret
.cfi_endproc
.size foo, .-foo

View File

@@ -0,0 +1,40 @@
#as: --scfi=experimental -W
#objdump: -Wf
#name: Synthesize CFI for cfg 2
#...
Contents of the .eh_frame section:
0+0000 0+0010 0+0000 CIE
Version: 1
Augmentation: "zR"
Code alignment factor: 4
Data alignment factor: -8
Return address column: 30
Augmentation data: 1b
DW_CFA_def_cfa: r31 \(sp\) ofs 0
0+0014 0+[0-9a-f]+ 0+0018 FDE cie=00000000 pc=0+0000..0+0028
DW_CFA_advance_loc: 4 to 0+0004
DW_CFA_def_cfa_offset: 48
DW_CFA_offset: r29 \(x29\) at cfa-48
DW_CFA_offset: r30 \(x30\) at cfa-40
DW_CFA_advance_loc: 4 to 0+0008
DW_CFA_def_cfa_register: r29 \(x29\)
DW_CFA_advance_loc: 8 to 0+0010
DW_CFA_remember_state
DW_CFA_advance_loc: 8 to 0+0018
DW_CFA_def_cfa_register: r31 \(sp\)
DW_CFA_restore: r29 \(x29\)
DW_CFA_restore: r30 \(x30\)
DW_CFA_def_cfa_offset: 0
DW_CFA_advance_loc: 4 to 0+001c
DW_CFA_restore_state
DW_CFA_advance_loc: 8 to 0+0024
DW_CFA_def_cfa_register: r31 \(sp\)
DW_CFA_restore: r29 \(x29\)
DW_CFA_restore: r30 \(x30\)
DW_CFA_def_cfa_offset: 0
DW_CFA_nop
#pass

View File

@@ -0,0 +1,2 @@
.*Assembler messages:
.*13: Warning: SCFI ignores most user-specified CFI directives

View File

@@ -0,0 +1,42 @@
# Testcase for backward flow of SCFI state.
# The cfg has two exit paths, with epilogue duplicated in
# the two.
#
# SCFI must synthesize the remember_state / restore_state pair.
# Note how SCFI does not necessary generate the least number of
# CFI directives (.cfi_remember_state can possibly be clubbed
# together with other immediately following CFI directives).
# This is not a correctness issue, however.
.global foo
.type foo, %function
foo:
.cfi_startproc
stp x29, x30, [sp, -48]!
.cfi_def_cfa_offset 48
.cfi_offset 29, -48
.cfi_offset 30, -40
mov x29, sp
.cfi_def_cfa_register 29
cmp w4, w19
bge .L1
.L2:
.cfi_remember_state
bl bar
ldp x29, x30, [sp], 48
.cfi_def_cfa_register 31
.cfi_restore 29
.cfi_restore 30
.cfi_def_cfa_offset 0
ret
.L1:
.cfi_restore_state
cbz w3, .L2
ldp x29, x30, [sp], 48
.cfi_def_cfa_register 31
.cfi_restore 29
.cfi_restore 30
.cfi_def_cfa_offset 0
ret
.cfi_endproc
.size foo, .-foo

View File

@@ -0,0 +1,32 @@
#as: --scfi=experimental -W
#objdump: -Wf
#name: Synthesize CFI for cfg 3
#...
Contents of the .eh_frame section:
0+0000 0+0010 0+0000 CIE
Version: 1
Augmentation: "zR"
Code alignment factor: 4
Data alignment factor: -8
Return address column: 30
Augmentation data: 1b
DW_CFA_def_cfa: r31 \(sp\) ofs 0
0+0014 0+0020 0+0018 FDE cie=00000000 pc=0+0000..0+002c
DW_CFA_advance_loc: 4 to 0+0004
DW_CFA_def_cfa_offset: 32
DW_CFA_offset: r29 \(x29\) at cfa-32
DW_CFA_offset: r30 \(x30\) at cfa-24
DW_CFA_advance_loc: 4 to 0+0008
DW_CFA_def_cfa_register: r29 \(x29\)
DW_CFA_advance_loc: 32 to 0+0028
DW_CFA_def_cfa_register: r31 \(sp\)
DW_CFA_restore: r29 \(x29\)
DW_CFA_restore: r30 \(x30\)
DW_CFA_def_cfa_offset: 0
DW_CFA_nop
DW_CFA_nop
#pass

View File

@@ -0,0 +1,2 @@
.*Assembler messages:
.*9: Warning: SCFI ignores most user-specified CFI directives

View File

@@ -0,0 +1,34 @@
# Testcase for cfg creation.
# There is at least one bb here with a single GINSN_TYPE_SYMBOL instruction
# for a user-defined label. This ginsn is visited in the fallthrough path of
# another bb.
.text
.global main
.type main, %function
main:
.cfi_startproc
stp x29, x30, [sp, -32]!
.cfi_def_cfa_offset 32
.cfi_offset 29, -32
.cfi_offset 30, -24
mov x29, sp
.cfi_def_cfa_register 29
cmp w0, 0
bne .L2
# ldr x0, [sp, 24]
# bl fclose
cmp w0, 0
beq .L3
.L2:
mov w0, 1
b .L5
.L3:
mov w0, 0
.L5:
ldp x29, x30, [sp], 32
.cfi_def_cfa_register 31
.cfi_restore 29
.cfi_restore 30
.cfi_def_cfa_offset 0
ret
.cfi_endproc

View File

@@ -0,0 +1,20 @@
#as: --scfi=experimental -W
#objdump: -Wf
#name: Synthesize CFI for conditional br 1
#...
Contents of the .eh_frame section:
0+0000 0+0010 0+0000 CIE
Version: 1
Augmentation: "zR"
Code alignment factor: 4
Data alignment factor: -8
Return address column: 30
Augmentation data: 1b
DW_CFA_def_cfa: r31 \(sp\) ofs 0
0+0014 0+0010 00000018 FDE cie=00000000 pc=0+0000..0+0010
DW_CFA_nop
#pass

View File

@@ -0,0 +1,2 @@
.*Assembler messages:
.*6: Warning: SCFI ignores most user-specified CFI directives

View File

@@ -0,0 +1,13 @@
.text
.align 2
.global foo
.type foo, %function
foo:
.cfi_startproc
bge .L10
ble .L10
bne .L10
.L10:
ret
.cfi_endproc
.size foo, .-foo

View File

@@ -0,0 +1,2 @@
.*Assembler messages:
.*5: Warning: SCFI: ignored probable save/restore op with reg offset

View File

@@ -0,0 +1,6 @@
.text
.globl foo
.type foo, @function
foo:
str x19, [sp, x1]
.size foo, .-foo

View File

@@ -0,0 +1,59 @@
#as: --scfi=experimental -W
#objdump: -Wf
#name: Synthesize CFI for ldp ldr instructions
#...
Contents of the .eh_frame section:
00000000 0+0010 00000000 CIE
Version: 1
Augmentation: "zR"
Code alignment factor: 4
Data alignment factor: -8
Return address column: 30
Augmentation data: 1b
DW_CFA_def_cfa: r31 \(sp\) ofs 0
00000014 0+0048 00000018 FDE cie=00000000 pc=0+0000..0+0040
DW_CFA_advance_loc: 4 to 0+0004
DW_CFA_def_cfa_offset: 128
DW_CFA_offset: r29 \(x29\) at cfa-128
DW_CFA_offset: r30 \(x30\) at cfa-120
DW_CFA_advance_loc: 4 to 0+0008
DW_CFA_def_cfa_register: r29 \(x29\)
DW_CFA_advance_loc: 4 to 0+000c
DW_CFA_offset: r19 \(x19\) at cfa-112
DW_CFA_offset: r20 \(x20\) at cfa-104
DW_CFA_advance_loc: 4 to 0+0010
DW_CFA_offset: r21 \(x21\) at cfa-96
DW_CFA_offset: r22 \(x22\) at cfa-88
DW_CFA_advance_loc: 4 to 0+0014
DW_CFA_offset: r23 \(x23\) at cfa-80
DW_CFA_offset: r24 \(x24\) at cfa-72
DW_CFA_advance_loc: 4 to 0+0018
DW_CFA_offset: r25 \(x25\) at cfa-64
DW_CFA_offset: r26 \(x26\) at cfa-56
DW_CFA_advance_loc: 4 to 0+001c
DW_CFA_offset: r27 \(x27\) at cfa-48
DW_CFA_advance_loc: 8 to 0+0024
DW_CFA_def_cfa_register: r31 \(sp\)
DW_CFA_advance_loc: 4 to 0+0028
DW_CFA_restore: r19 \(x19\)
DW_CFA_restore: r20 \(x20\)
DW_CFA_advance_loc: 4 to 0+002c
DW_CFA_restore: r21 \(x21\)
DW_CFA_restore: r22 \(x22\)
DW_CFA_advance_loc: 4 to 0+0030
DW_CFA_restore: r23 \(x23\)
DW_CFA_restore: r24 \(x24\)
DW_CFA_advance_loc: 4 to 0+0034
DW_CFA_restore: r25 \(x25\)
DW_CFA_restore: r26 \(x26\)
DW_CFA_advance_loc: 4 to 0+0038
DW_CFA_restore: r27 \(x27\)
DW_CFA_advance_loc: 4 to 0+003c
DW_CFA_restore: r29 \(x29\)
DW_CFA_restore: r30 \(x30\)
DW_CFA_def_cfa_offset: 0
DW_CFA_nop
#pass

View File

@@ -0,0 +1,2 @@
.*Assembler messages:
.*8: Warning: SCFI ignores most user-specified CFI directives

View File

@@ -0,0 +1,52 @@
# Testcase for various ldp / ldr instructions.
# This test also serves for checking callee-saved regs.
.text
.align 2
.global foo
.type foo, %function
foo:
.cfi_startproc
stp x29, x30, [sp, -128]!
.cfi_def_cfa_offset 128
.cfi_offset 29, -128
.cfi_offset 30, -120
mov x29, sp
.cfi_def_cfa_register 29
stp x19, x20, [sp, 16]
.cfi_offset 19, -112
.cfi_offset 20, -104
stp x21, x22, [sp, 32]
.cfi_offset 21, -96
.cfi_offset 22, -88
stp x23, x24, [sp, 48]
.cfi_offset 23, -80
.cfi_offset 24, -72
stp x25, x26, [sp, 64]
.cfi_offset 25, -64
.cfi_offset 26, -56
str x27, [sp, 80]
.cfi_offset 27, -48
mov w0, 0
mov sp, x29
.cfi_def_cfa_register 31
ldp x19, x20, [sp, 16]
.cfi_restore 19
.cfi_restore 20
ldp x21, x22, [sp, 32]
.cfi_restore 21
.cfi_restore 22
ldp x23, x24, [sp, 48]
.cfi_restore 23
.cfi_restore 24
ldp x25, x26, [sp, 64]
.cfi_restore 25
.cfi_restore 26
ldr x27, [sp, 80]
.cfi_restore 27
ldp x29, x30, [sp], 128
.cfi_restore 29
.cfi_restore 30
.cfi_def_cfa_offset 0
ret
.cfi_endproc
.size foo, .-foo

View File

@@ -0,0 +1,33 @@
#as: --scfi=experimental -W
#objdump: -Wf
#name: Synthesize CFI for ldr insns 2
#...
Contents of the .eh_frame section:
00000000 0+0010 00000000 CIE
Version: 1
Augmentation: "zR"
Code alignment factor: 4
Data alignment factor: -8
Return address column: 30
Augmentation data: 1b
DW_CFA_def_cfa: r31 \(sp\) ofs 0
00000014 0+[0-9a-f]+ 00000018 FDE cie=00000000 pc=0+0000..0+0018
DW_CFA_advance_loc: 4 to 0+0004
DW_CFA_def_cfa_offset: 128
DW_CFA_offset: r29 \(x29\) at cfa-128
DW_CFA_offset: r30 \(x30\) at cfa-120
DW_CFA_advance_loc: 4 to 0+0008
DW_CFA_def_cfa_register: r29 \(x29\)
DW_CFA_advance_loc: 4 to 0+000c
DW_CFA_def_cfa_register: r31 \(sp\)
DW_CFA_advance_loc: 4 to 0+0010
DW_CFA_restore: r29 \(x29\)
DW_CFA_def_cfa_offset: 120
DW_CFA_advance_loc: 4 to 0+0014
DW_CFA_restore: r30 \(x30\)
DW_CFA_def_cfa_offset: 0
DW_CFA_nop
#pass

View File

@@ -0,0 +1,2 @@
.*Assembler messages:
.*7: Warning: SCFI ignores most user-specified CFI directives

View File

@@ -0,0 +1,26 @@
# Testcase for various ldp / ldr instructions
.text
.align 2
.global foo
.type foo, %function
foo:
.cfi_startproc
stp x29, x30, [sp, -128]!
.cfi_def_cfa_offset 128
.cfi_offset 29, -128
.cfi_offset 30, -120
mov x29, sp
.cfi_def_cfa_register 29
mov sp, x29
.cfi_def_cfa_register 31
# Post-indexed ldr
ldr x29, [sp], 8
.cfi_restore 29
.cfi_def_cfa_offset 120
# Post-indexed ldr
ldr x30, [sp], 120
.cfi_restore 30
.cfi_def_cfa_offset 0
ret
.cfi_endproc
.size foo, .-foo

View File

@@ -0,0 +1,39 @@
#as: --scfi=experimental -W
#objdump: -Wf
#name: Synthesize CFI for str stp insns 1
#...
Contents of the .eh_frame section:
00000000 0+0010 00000000 CIE
Version: 1
Augmentation: "zR"
Code alignment factor: 4
Data alignment factor: -8
Return address column: 30
Augmentation data: 1b
DW_CFA_def_cfa: r31 \(sp\) ofs 0
00000014 0+0030 00000018 FDE cie=00000000 pc=0+0000..0+002c
DW_CFA_advance_loc: 4 to 0+0004
DW_CFA_def_cfa_offset: 128
DW_CFA_offset: r29 \(x29\) at cfa-128
DW_CFA_offset: r30 \(x30\) at cfa-120
DW_CFA_advance_loc: 4 to 0+0008
DW_CFA_def_cfa_register: r29 \(x29\)
DW_CFA_advance_loc: 4 to 0+000c
DW_CFA_offset: r19 \(x19\) at cfa-112
DW_CFA_offset: r20 \(x20\) at cfa-104
DW_CFA_advance_loc: 4 to 0+0010
DW_CFA_offset: r21 \(x21\) at cfa-96
DW_CFA_offset: r22 \(x22\) at cfa-88
DW_CFA_advance_loc: 4 to 0+0014
DW_CFA_offset: r23 \(x23\) at cfa-80
DW_CFA_offset: r24 \(x24\) at cfa-72
DW_CFA_advance_loc: 4 to 0+0018
DW_CFA_offset: r25 \(x25\) at cfa-64
DW_CFA_offset: r26 \(x26\) at cfa-56
DW_CFA_advance_loc: 4 to 0+001c
DW_CFA_offset: r27 \(x27\) at cfa-48
DW_CFA_nop
#pass

View File

@@ -0,0 +1,2 @@
.*Assembler messages:
.*6: Warning: SCFI ignores most user-specified CFI directives

View File

@@ -0,0 +1,37 @@
## Testcase with a variety of str/stp instructions
.text
.globl foo
.type foo, @function
foo:
.cfi_startproc
# Pre-indexed addressing is like offset addressing, except that
# the base pointer is updated as a result of the instruction.
stp x29, x30, [sp, -128]!
.cfi_def_cfa_offset 128
.cfi_offset 29, -128
.cfi_offset 30, -120
mov x29, sp
.cfi_def_cfa_register 29
# Offset addressing mode is when ann offset can be applied optionally to the
# base address.
stp x19, x20, [sp, 16]
.cfi_offset 19, -112
.cfi_offset 20, -104
stp x21, x22, [sp, 32]
.cfi_offset 21, -96
.cfi_offset 22, -88
stp x23, x24, [sp, 48]
.cfi_offset 23, -80
.cfi_offset 24, -72
stp x25, x26, [sp, 64]
.cfi_offset 25, -64
.cfi_offset 26, -56
str x27, [sp, 80]
.cfi_offset 27, -48
# Stores non callee-saved register on stack.
str w0, [x29, 124]
str wzr, [x29, 120]
str w0, [x29, 120]
ret
.cfi_endproc
.size foo, .-foo

View File

@@ -0,0 +1,35 @@
#as: --scfi=experimental -W
#objdump: -Wf
#name: Synthesize CFI for str insns 2
#...
Contents of the .eh_frame section:
00000000 0+0010 00000000 CIE
Version: 1
Augmentation: "zR"
Code alignment factor: 4
Data alignment factor: -8
Return address column: 30
Augmentation data: 1b
DW_CFA_def_cfa: r31 \(sp\) ofs 0
00000014 0+0028 00000018 FDE cie=00000000 pc=0+0000..0+001c
DW_CFA_advance_loc: 4 to 0+0004
DW_CFA_def_cfa_offset: 128
DW_CFA_offset: r29 \(x29\) at cfa-128
DW_CFA_offset: r30 \(x30\) at cfa-120
DW_CFA_advance_loc: 4 to 0+0008
DW_CFA_def_cfa_register: r29 \(x29\)
DW_CFA_advance_loc: 4 to 0+000c
DW_CFA_offset: r27 \(x27\) at cfa-128
DW_CFA_advance_loc: 4 to 0+0010
DW_CFA_def_cfa_register: r31 \(sp\)
DW_CFA_advance_loc: 4 to 0+0014
DW_CFA_restore: r29 \(x29\)
DW_CFA_def_cfa_offset: 120
DW_CFA_advance_loc: 4 to 0+0018
DW_CFA_restore: r30 \(x30\)
DW_CFA_def_cfa_offset: 0
DW_CFA_nop
#pass

View File

@@ -0,0 +1,2 @@
.*Assembler messages:
.*7: Warning: SCFI ignores most user-specified CFI directives

View File

@@ -0,0 +1,30 @@
# Testcase for a variety of stp/str including a post-indexed store
.text
.align 2
.global foo
.type foo, %function
foo:
.cfi_startproc
stp x29, x30, [sp, -128]!
.cfi_def_cfa_offset 128
.cfi_offset 29, -128
.cfi_offset 30, -120
mov x29, sp
.cfi_def_cfa_register 29
# post-indexed store, a stack corrupting one which over-writes
# x29! Only for testing purposes for now
# This does not generate a .cfi_def_cfa_offset 208 because
# CFA is REG_FP based
str x27, [sp], 80
.cfi_offset 27, -128
mov sp, x29
.cfi_def_cfa_register 31
ldr x29, [sp], 8
.cfi_restore 29
.cfi_def_cfa_offset 120
ldr x30, [sp], 120
.cfi_restore 30
.cfi_def_cfa_offset 0
ret
.cfi_endproc
.size foo, .-foo

View File

@@ -0,0 +1,4 @@
.*Assembler messages:
.*7: Warning: SCFI ignores most user-specified CFI directives
.*9: Error: SCFI: unsupported stack manipulation pattern
.*31: Error: SCFI: forward pass failed for func 'foo'

View File

@@ -0,0 +1,31 @@
# Testcase where immediate used for stack allocation is a wide
# one. Since SCFI does not currently have any data-flow
# capabilities, this is currently not supported.
.global foo
.type foo, %function
foo:
.cfi_startproc
mov x16, 4384
sub sp, sp, x16
.cfi_def_cfa_offset 4384
stp x29, x30, [sp]
.cfi_offset 29, -4384
.cfi_offset 30, -4376
mov x29, sp
str x0, [sp, 24]
str x1, [sp, 16]
add x0, sp, 4096
add x0, x0, 112
bl bar
.L1:
str xzr, [sp, 4376]
.L2:
ldp x29, x30, [sp]
mov x16, 4384
add sp, sp, x16
.cfi_restore 29
.cfi_restore 30
.cfi_def_cfa_offset 0
ret
.cfi_endproc
.size foo, .-foo

View File

@@ -1,8 +1,7 @@
.*: Assembler messages:
.*:20: Error: untraceable control flow for func 'foo'
.*:24: Error: untraceable control flow for func 'foo'
GAS LISTING .*
1 # Testcase with a variety of "change of flow instructions"
2 #
3 # This test does not have much going on wrt synthesis of CFI;
@@ -22,17 +21,29 @@ GAS LISTING .*
12 ginsn: JMP %r0,
13 \?\?\?\? 41FFD0 call \*%r8
13 ginsn: CALL
14 \?\?\?\? 67E305 jecxz .L179
14 ginsn: JCC
15 \?\?\?\? FF6730 jmp \*48\(%rdi\)
15 ginsn: JMP %r5,
16 \?\?\?\? 7000 jo .L179
14 \?\?\?\? FF14C500 call \*cost_arr\(,%rax,8\)
14 000000
14 ginsn: CALL
15 \?\?\?\? FF142500 call \*symbol\+1
15 000000
15 ginsn: CALL
16 \?\?\?\? 67E313 jecxz .L179
16 ginsn: JCC
17 .L179:
17 ginsn: SYM .L179
18 \?\?\?\? C3 ret
18 ginsn: RET
19 .LFE0:
19 ginsn: SYM .LFE0
20 .size foo, .-foo
20 ginsn: SYM FUNC_END
17 \?\?\?\? FF6730 jmp \*48\(%rdi\)
17 ginsn: JMP %r5,
18 \?\?\?\? FF24C500 jmp \*cost_arr\(,%rax,8\)
18 000000
18 ginsn: JMP %r0,
19 \?\?\?\? FF242500 jmp \*symbol\+1
19 000000
19 ginsn: JMP %r4,
20 \?\?\?\? 7000 jo .L179
20 ginsn: JCC
21 .L179:
21 ginsn: SYM .L179
22 \?\?\?\? C3 ret
22 ginsn: RET
23 .LFE0:
23 ginsn: SYM .LFE0
24 .size foo, .-foo
24 ginsn: SYM FUNC_END

View File

@@ -11,8 +11,12 @@ foo:
loop foo
notrack jmp *%rax
call *%r8
call *cost_arr(,%rax,8)
call *symbol+1
jecxz .L179
jmp *48(%rdi)
jmp *cost_arr(,%rax,8)
jmp *symbol+1
jo .L179
.L179:
ret

View File

@@ -0,0 +1,43 @@
#as: --scfi=experimental -W
#as:
#objdump: -Wf
#name: Synthesize CFI in presence of control flow 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: 1b
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=0000000000000000..0000000000000045
DW_CFA_advance_loc: 1 to 0000000000000001
DW_CFA_def_cfa_offset: 16
DW_CFA_offset: r3 \(rbx\) at cfa-16
DW_CFA_advance_loc: 6 to 0000000000000007
DW_CFA_def_cfa_offset: 32
DW_CFA_advance_loc: 15 to 0000000000000016
DW_CFA_remember_state
DW_CFA_advance_loc: 4 to 000000000000001a
DW_CFA_def_cfa_offset: 16
DW_CFA_advance_loc: 1 to 000000000000001b
DW_CFA_restore: r3 \(rbx\)
DW_CFA_def_cfa_offset: 8
DW_CFA_advance_loc: 1 to 000000000000001c
DW_CFA_restore_state
DW_CFA_advance_loc: 35 to 000000000000003f
DW_CFA_def_cfa_offset: 16
DW_CFA_advance_loc: 1 to 0000000000000040
DW_CFA_restore: r3 \(rbx\)
DW_CFA_def_cfa_offset: 8
DW_CFA_nop
#...
#pass

View File

@@ -0,0 +1,2 @@
.*Assembler messages:
.*5: Warning: SCFI ignores most user-specified CFI directives

View File

@@ -0,0 +1,42 @@
.text
.globl foo_handler
.type foo_handler, @function
foo_handler:
.cfi_startproc
pushq %rbx
.cfi_def_cfa_offset 16
.cfi_offset %rbx, -16
movl %esi, %ebx
subq $16, %rsp
.cfi_def_cfa_offset 32
movl current_style(%rip), %eax
cmpl $-1, %eax
je .L12
testb $4, %al
jne .L13
.L1:
.cfi_remember_state
addq $16, %rsp
.cfi_def_cfa_offset 16
popq %rbx
.cfi_restore %rbx
.cfi_def_cfa_offset 8
ret
.L13:
.cfi_restore_state
movq %rdi, 8(%rsp)
call foo_handler_v2
testq %rax, %rax
jne .L1
movl current_style(%rip), %eax
movq 8(%rsp), %rdi
jmp .L3
.L12:
addq $16, %rsp
.cfi_def_cfa_offset 16
popq %rbx
.cfi_restore %rbx
.cfi_def_cfa_offset 8
jmp xstrdup
.cfi_endproc
.size foo_handler, .-foo_handler

View File

@@ -0,0 +1,39 @@
#as: --scfi=experimental -W
#as:
#objdump: -Wf
#name: Synthesize CFI in presence of control flow 5
#...
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: 1b
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 0000001c FDE cie=00000000 pc=0+0000..0+0017
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: 5 to 0+0009
DW_CFA_remember_state
DW_CFA_advance_loc: 6 to 0+000f
DW_CFA_def_cfa_register: r7 \(rsp\)
DW_CFA_restore: r6 \(rbp\)
DW_CFA_def_cfa_offset: 8
DW_CFA_advance_loc: 1 to 0+0010
DW_CFA_restore_state
DW_CFA_advance_loc: 6 to 0+0016
DW_CFA_def_cfa_register: r7 \(rsp\)
DW_CFA_restore: r6 \(rbp\)
DW_CFA_def_cfa_offset: 8
#pass

View File

@@ -0,0 +1,2 @@
.*Assembler messages:
.*5: Warning: SCFI ignores most user-specified CFI directives

View File

@@ -0,0 +1,32 @@
.text
.globl foo
.type foo, @function
foo:
.cfi_startproc
push %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
mov %rsp, %rbp
.cfi_def_cfa_register %rbp
cmpl $-1, %eax
jne .L1
.L2:
.cfi_remember_state
call bar
pop %rbp
.cfi_def_cfa_register %rsp
.cfi_restore %rbp
.cfi_def_cfa_offset 8
ret
.L1:
.cfi_restore_state
testq %rax, %rax
je .L2
pop %rbp
.cfi_def_cfa_register %rsp
.cfi_restore %rbp
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.size foo,.-foo

View File

@@ -1,7 +1,8 @@
.*Assembler messages:
.*7: Error: SCFI: unsupported APX op 0x8f may cause incorrect CFI
.*8: Error: SCFI: unsupported APX op 0x8f may cause incorrect CFI
.*9: Error: SCFI: unsupported APX op 0xff may cause incorrect CFI
.*10: Error: SCFI: unsupported APX op 0xff may cause incorrect CFI
.*11: Error: SCFI: unsupported APX op 0x11 may cause incorrect CFI
.*7: Error: SCFI: unhandled op 0x8f may cause incorrect CFI
.*8: Error: SCFI: unhandled op 0x8f may cause incorrect CFI
.*9: Error: SCFI: unhandled op 0xff may cause incorrect CFI
.*10: Error: SCFI: unhandled op 0xff may cause incorrect CFI
.*13: Error: SCFI: hand-crafting instructions not supported
.*11: Error: SCFI: unsupported stack manipulation pattern
.*16: Error: SCFI: forward pass failed for func 'foo'

View File

@@ -80,6 +80,10 @@ if { ([istarget "x86_64-*-*"] && ![istarget "x86_64-*-linux*-gnux32"]) } then {
run_list_test "scfi-cfg-2" "--scfi=experimental --warn"
run_dump_test "scfi-cfg-3"
run_list_test "scfi-cfg-3" "--scfi=experimental --warn"
run_dump_test "scfi-cfg-4"
run_list_test "scfi-cfg-4" "--scfi=experimental --warn"
run_dump_test "scfi-cfg-5"
run_list_test "scfi-cfg-5" "--scfi=experimental --warn"
run_dump_test "scfi-asm-marker-1"
run_list_test "scfi-asm-marker-1" "--scfi=experimental --warn"
run_dump_test "scfi-asm-marker-2"