mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-11-16 12:34:43 +00:00
Compare commits
8 Commits
gdb-16-bra
...
users/ibha
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eeadfb158b | ||
|
|
6ea6536f01 | ||
|
|
977da52111 | ||
|
|
0d12d72fc6 | ||
|
|
d412bdce32 | ||
|
|
f7a2f944bb | ||
|
|
4e97c9dcfc | ||
|
|
ad35d7451d |
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
1012
gas/doc/scfi/scfi-paper.tex
Normal file
File diff suppressed because it is too large
Load Diff
93
gas/ginsn.c
93
gas/ginsn.c
@@ -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));
|
||||
}
|
||||
|
||||
if (ginsn->type == GINSN_TYPE_JUMP_COND)
|
||||
{
|
||||
/* Add the bb for the fall through path. */
|
||||
find_or_make_bb (func, gcfg, ginsn->next, prev_bb, errp);
|
||||
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
|
||||
|
||||
20
gas/scfi.c
20
gas/scfi.c
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
30
gas/testsuite/gas/scfi/aarch64/ginsn-cofi-1.l
Normal file
30
gas/testsuite/gas/scfi/aarch64/ginsn-cofi-1.l
Normal 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
|
||||
16
gas/testsuite/gas/scfi/aarch64/ginsn-cofi-1.s
Normal file
16
gas/testsuite/gas/scfi/aarch64/ginsn-cofi-1.s
Normal 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
|
||||
40
gas/testsuite/gas/scfi/aarch64/ginsn-ldst-1.l
Normal file
40
gas/testsuite/gas/scfi/aarch64/ginsn-ldst-1.l
Normal 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
|
||||
21
gas/testsuite/gas/scfi/aarch64/ginsn-ldst-1.s
Normal file
21
gas/testsuite/gas/scfi/aarch64/ginsn-ldst-1.s
Normal 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
|
||||
32
gas/testsuite/gas/scfi/aarch64/ginsn-misc-1.l
Normal file
32
gas/testsuite/gas/scfi/aarch64/ginsn-misc-1.l
Normal 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
|
||||
15
gas/testsuite/gas/scfi/aarch64/ginsn-misc-1.s
Normal file
15
gas/testsuite/gas/scfi/aarch64/ginsn-misc-1.s
Normal 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
|
||||
59
gas/testsuite/gas/scfi/aarch64/scfi-aarch64.exp
Normal file
59
gas/testsuite/gas/scfi/aarch64/scfi-aarch64.exp
Normal 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"
|
||||
|
||||
}
|
||||
20
gas/testsuite/gas/scfi/aarch64/scfi-cb-1.d
Normal file
20
gas/testsuite/gas/scfi/aarch64/scfi-cb-1.d
Normal 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
|
||||
2
gas/testsuite/gas/scfi/aarch64/scfi-cb-1.l
Normal file
2
gas/testsuite/gas/scfi/aarch64/scfi-cb-1.l
Normal file
@@ -0,0 +1,2 @@
|
||||
.*Assembler messages:
|
||||
.*5: Warning: SCFI ignores most user-specified CFI directives
|
||||
14
gas/testsuite/gas/scfi/aarch64/scfi-cb-1.s
Normal file
14
gas/testsuite/gas/scfi/aarch64/scfi-cb-1.s
Normal 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
|
||||
31
gas/testsuite/gas/scfi/aarch64/scfi-cfg-1.d
Normal file
31
gas/testsuite/gas/scfi/aarch64/scfi-cfg-1.d
Normal 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
|
||||
2
gas/testsuite/gas/scfi/aarch64/scfi-cfg-1.l
Normal file
2
gas/testsuite/gas/scfi/aarch64/scfi-cfg-1.l
Normal file
@@ -0,0 +1,2 @@
|
||||
.*Assembler messages:
|
||||
.*6: Warning: SCFI ignores most user-specified CFI directives
|
||||
46
gas/testsuite/gas/scfi/aarch64/scfi-cfg-1.s
Normal file
46
gas/testsuite/gas/scfi/aarch64/scfi-cfg-1.s
Normal 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
|
||||
40
gas/testsuite/gas/scfi/aarch64/scfi-cfg-2.d
Normal file
40
gas/testsuite/gas/scfi/aarch64/scfi-cfg-2.d
Normal 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
|
||||
2
gas/testsuite/gas/scfi/aarch64/scfi-cfg-2.l
Normal file
2
gas/testsuite/gas/scfi/aarch64/scfi-cfg-2.l
Normal file
@@ -0,0 +1,2 @@
|
||||
.*Assembler messages:
|
||||
.*13: Warning: SCFI ignores most user-specified CFI directives
|
||||
42
gas/testsuite/gas/scfi/aarch64/scfi-cfg-2.s
Normal file
42
gas/testsuite/gas/scfi/aarch64/scfi-cfg-2.s
Normal 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
|
||||
32
gas/testsuite/gas/scfi/aarch64/scfi-cfg-3.d
Normal file
32
gas/testsuite/gas/scfi/aarch64/scfi-cfg-3.d
Normal 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
|
||||
2
gas/testsuite/gas/scfi/aarch64/scfi-cfg-3.l
Normal file
2
gas/testsuite/gas/scfi/aarch64/scfi-cfg-3.l
Normal file
@@ -0,0 +1,2 @@
|
||||
.*Assembler messages:
|
||||
.*9: Warning: SCFI ignores most user-specified CFI directives
|
||||
34
gas/testsuite/gas/scfi/aarch64/scfi-cfg-3.s
Normal file
34
gas/testsuite/gas/scfi/aarch64/scfi-cfg-3.s
Normal 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
|
||||
20
gas/testsuite/gas/scfi/aarch64/scfi-cond-br-1.d
Normal file
20
gas/testsuite/gas/scfi/aarch64/scfi-cond-br-1.d
Normal 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
|
||||
2
gas/testsuite/gas/scfi/aarch64/scfi-cond-br-1.l
Normal file
2
gas/testsuite/gas/scfi/aarch64/scfi-cond-br-1.l
Normal file
@@ -0,0 +1,2 @@
|
||||
.*Assembler messages:
|
||||
.*6: Warning: SCFI ignores most user-specified CFI directives
|
||||
13
gas/testsuite/gas/scfi/aarch64/scfi-cond-br-1.s
Normal file
13
gas/testsuite/gas/scfi/aarch64/scfi-cond-br-1.s
Normal 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
|
||||
2
gas/testsuite/gas/scfi/aarch64/scfi-diag-1.l
Normal file
2
gas/testsuite/gas/scfi/aarch64/scfi-diag-1.l
Normal file
@@ -0,0 +1,2 @@
|
||||
.*Assembler messages:
|
||||
.*5: Warning: SCFI: ignored probable save/restore op with reg offset
|
||||
6
gas/testsuite/gas/scfi/aarch64/scfi-diag-1.s
Normal file
6
gas/testsuite/gas/scfi/aarch64/scfi-diag-1.s
Normal file
@@ -0,0 +1,6 @@
|
||||
.text
|
||||
.globl foo
|
||||
.type foo, @function
|
||||
foo:
|
||||
str x19, [sp, x1]
|
||||
.size foo, .-foo
|
||||
59
gas/testsuite/gas/scfi/aarch64/scfi-ldrp-1.d
Normal file
59
gas/testsuite/gas/scfi/aarch64/scfi-ldrp-1.d
Normal 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
|
||||
2
gas/testsuite/gas/scfi/aarch64/scfi-ldrp-1.l
Normal file
2
gas/testsuite/gas/scfi/aarch64/scfi-ldrp-1.l
Normal file
@@ -0,0 +1,2 @@
|
||||
.*Assembler messages:
|
||||
.*8: Warning: SCFI ignores most user-specified CFI directives
|
||||
52
gas/testsuite/gas/scfi/aarch64/scfi-ldrp-1.s
Normal file
52
gas/testsuite/gas/scfi/aarch64/scfi-ldrp-1.s
Normal 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
|
||||
33
gas/testsuite/gas/scfi/aarch64/scfi-ldrp-2.d
Normal file
33
gas/testsuite/gas/scfi/aarch64/scfi-ldrp-2.d
Normal 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
|
||||
2
gas/testsuite/gas/scfi/aarch64/scfi-ldrp-2.l
Normal file
2
gas/testsuite/gas/scfi/aarch64/scfi-ldrp-2.l
Normal file
@@ -0,0 +1,2 @@
|
||||
.*Assembler messages:
|
||||
.*7: Warning: SCFI ignores most user-specified CFI directives
|
||||
26
gas/testsuite/gas/scfi/aarch64/scfi-ldrp-2.s
Normal file
26
gas/testsuite/gas/scfi/aarch64/scfi-ldrp-2.s
Normal 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
|
||||
39
gas/testsuite/gas/scfi/aarch64/scfi-strp-1.d
Normal file
39
gas/testsuite/gas/scfi/aarch64/scfi-strp-1.d
Normal 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
|
||||
2
gas/testsuite/gas/scfi/aarch64/scfi-strp-1.l
Normal file
2
gas/testsuite/gas/scfi/aarch64/scfi-strp-1.l
Normal file
@@ -0,0 +1,2 @@
|
||||
.*Assembler messages:
|
||||
.*6: Warning: SCFI ignores most user-specified CFI directives
|
||||
37
gas/testsuite/gas/scfi/aarch64/scfi-strp-1.s
Normal file
37
gas/testsuite/gas/scfi/aarch64/scfi-strp-1.s
Normal 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
|
||||
35
gas/testsuite/gas/scfi/aarch64/scfi-strp-2.d
Normal file
35
gas/testsuite/gas/scfi/aarch64/scfi-strp-2.d
Normal 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
|
||||
2
gas/testsuite/gas/scfi/aarch64/scfi-strp-2.l
Normal file
2
gas/testsuite/gas/scfi/aarch64/scfi-strp-2.l
Normal file
@@ -0,0 +1,2 @@
|
||||
.*Assembler messages:
|
||||
.*7: Warning: SCFI ignores most user-specified CFI directives
|
||||
30
gas/testsuite/gas/scfi/aarch64/scfi-strp-2.s
Normal file
30
gas/testsuite/gas/scfi/aarch64/scfi-strp-2.s
Normal 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
|
||||
4
gas/testsuite/gas/scfi/aarch64/scfi-unsupported-1.l
Normal file
4
gas/testsuite/gas/scfi/aarch64/scfi-unsupported-1.l
Normal 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'
|
||||
31
gas/testsuite/gas/scfi/aarch64/scfi-unsupported-1.s
Normal file
31
gas/testsuite/gas/scfi/aarch64/scfi-unsupported-1.s
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
43
gas/testsuite/gas/scfi/x86_64/scfi-cfg-4.d
Normal file
43
gas/testsuite/gas/scfi/x86_64/scfi-cfg-4.d
Normal 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
|
||||
2
gas/testsuite/gas/scfi/x86_64/scfi-cfg-4.l
Normal file
2
gas/testsuite/gas/scfi/x86_64/scfi-cfg-4.l
Normal file
@@ -0,0 +1,2 @@
|
||||
.*Assembler messages:
|
||||
.*5: Warning: SCFI ignores most user-specified CFI directives
|
||||
42
gas/testsuite/gas/scfi/x86_64/scfi-cfg-4.s
Normal file
42
gas/testsuite/gas/scfi/x86_64/scfi-cfg-4.s
Normal 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
|
||||
39
gas/testsuite/gas/scfi/x86_64/scfi-cfg-5.d
Normal file
39
gas/testsuite/gas/scfi/x86_64/scfi-cfg-5.d
Normal 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
|
||||
2
gas/testsuite/gas/scfi/x86_64/scfi-cfg-5.l
Normal file
2
gas/testsuite/gas/scfi/x86_64/scfi-cfg-5.l
Normal file
@@ -0,0 +1,2 @@
|
||||
.*Assembler messages:
|
||||
.*5: Warning: SCFI ignores most user-specified CFI directives
|
||||
32
gas/testsuite/gas/scfi/x86_64/scfi-cfg-5.s
Normal file
32
gas/testsuite/gas/scfi/x86_64/scfi-cfg-5.s
Normal 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
|
||||
@@ -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'
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user