diff options
author | Bernd Schmidt <bernds@redhat.com> | 2015-11-18 12:26:43 +0000 |
---|---|---|
committer | Bernd Schmidt <bernds@gcc.gnu.org> | 2015-11-18 12:26:43 +0000 |
commit | d085c468179245fdd31c1014d3029ddd9e116e01 (patch) | |
tree | cecf7a6e31ae97795e1eef1f83c8bb4b39867e0e /gcc/config | |
parent | 98f84050d6e2dd79bf85a1876f3f9ebae41bf926 (diff) | |
download | gcc-d085c468179245fdd31c1014d3029ddd9e116e01.zip gcc-d085c468179245fdd31c1014d3029ddd9e116e01.tar.gz gcc-d085c468179245fdd31c1014d3029ddd9e116e01.tar.bz2 |
regrename.h (struct du_head): Add target_data_1 and target_data_2 fields.
* regrename.h (struct du_head): Add target_data_1 and target_data_2
fields.
* regrename.c (create_new_chain): Clear entire struct after allocating.
* config/i386/i386.opt (mmitigate-rop): New option.
* doc/invoke.texi (mmitigate-rop): Document.
* config/i386/i386.c: Include "regrename.h".
(ix86_rop_should_change_byte_p, reg_encoded_number,
ix86_get_modrm_for_rop, set_rop_modrm_reg_bits, ix86_mitigate_rop): New
static functions.
(ix86_reorg): Call ix86_mitigate_rop if -fmitigate-rop.
* config/i386/i386.md (attr "modrm_class"): New.
(cmp<mode>_ccno_1, mov<mode>_xor, movstrict<mode>_xor,
x86_mov<mode>cc_0_m1. x86_mov<mode>cc_0_m1_se)
(x86_mov<mode>cc_0_m1_neg): Override modrm_class attribute.
From-SVN: r230543
Diffstat (limited to 'gcc/config')
-rw-r--r-- | gcc/config/i386/i386.c | 316 | ||||
-rw-r--r-- | gcc/config/i386/i386.md | 19 | ||||
-rw-r--r-- | gcc/config/i386/i386.opt | 4 |
3 files changed, 339 insertions, 0 deletions
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index f1a287b..83749d5 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -74,6 +74,7 @@ along with GCC; see the file COPYING3. If not see #include "rtl-chkp.h" #include "dbgcnt.h" #include "case-cfn-macros.h" +#include "regrename.h" /* This file should be included last. */ #include "target-def.h" @@ -3974,6 +3975,15 @@ ix86_debug_options (void) return; } +/* Return true if T is one of the bytes we should avoid with + -fmitigate-rop. */ + +static bool +ix86_rop_should_change_byte_p (int t) +{ + return t == 0xc2 || t == 0xc3 || t == 0xca || t == 0xcb; +} + static const char *stringop_alg_names[] = { #define DEF_ENUM #define DEF_ALG(alg, name) #name, @@ -27303,6 +27313,100 @@ ix86_instantiate_decls (void) instantiate_decl_rtl (s->rtl); } +/* Return the number used for encoding REG, in the range 0..7. */ + +static int +reg_encoded_number (rtx reg) +{ + unsigned regno = REGNO (reg); + switch (regno) + { + case AX_REG: + return 0; + case CX_REG: + return 1; + case DX_REG: + return 2; + case BX_REG: + return 3; + case SP_REG: + return 4; + case BP_REG: + return 5; + case SI_REG: + return 6; + case DI_REG: + return 7; + default: + break; + } + if (IN_RANGE (regno, FIRST_STACK_REG, LAST_STACK_REG)) + return regno - FIRST_STACK_REG; + if (IN_RANGE (regno, FIRST_SSE_REG, LAST_SSE_REG)) + return regno - FIRST_SSE_REG; + if (IN_RANGE (regno, FIRST_MMX_REG, LAST_MMX_REG)) + return regno - FIRST_MMX_REG; + if (IN_RANGE (regno, FIRST_REX_SSE_REG, LAST_REX_SSE_REG)) + return regno - FIRST_REX_SSE_REG; + if (IN_RANGE (regno, FIRST_REX_INT_REG, LAST_REX_INT_REG)) + return regno - FIRST_REX_INT_REG; + if (IN_RANGE (regno, FIRST_MASK_REG, LAST_MASK_REG)) + return regno - FIRST_MASK_REG; + if (IN_RANGE (regno, FIRST_BND_REG, LAST_BND_REG)) + return regno - FIRST_BND_REG; + return -1; +} + +/* Given an insn INSN with NOPERANDS OPERANDS, return the modr/m byte used + in its encoding if it could be relevant for ROP mitigation, otherwise + return -1. If POPNO0 and POPNO1 are nonnull, store the operand numbers + used for calculating it into them. */ + +static int +ix86_get_modrm_for_rop (rtx_insn *insn, rtx *operands, int noperands, + int *popno0 = 0, int *popno1 = 0) +{ + if (asm_noperands (PATTERN (insn)) >= 0) + return -1; + int has_modrm = get_attr_modrm (insn); + if (!has_modrm) + return -1; + enum attr_modrm_class cls = get_attr_modrm_class (insn); + rtx op0, op1; + switch (cls) + { + case MODRM_CLASS_OP02: + gcc_assert (noperands >= 3); + if (popno0) + { + *popno0 = 0; + *popno1 = 2; + } + op0 = operands[0]; + op1 = operands[2]; + break; + case MODRM_CLASS_OP01: + gcc_assert (noperands >= 2); + if (popno0) + { + *popno0 = 0; + *popno1 = 1; + } + op0 = operands[0]; + op1 = operands[1]; + break; + default: + return -1; + } + if (REG_P (op0) && REG_P (op1)) + { + int enc0 = reg_encoded_number (op0); + int enc1 = reg_encoded_number (op1); + return 0xc0 + (enc1 << 3) + enc0; + } + return -1; +} + /* Check whether x86 address PARTS is a pc-relative address. */ static bool @@ -45098,6 +45202,215 @@ ix86_seh_fixup_eh_fallthru (void) } } +/* Given a register number BASE, the lowest of a group of registers, update + regsets IN and OUT with the registers that should be avoided in input + and output operands respectively when trying to avoid generating a modr/m + byte for -fmitigate-rop. */ + +static void +set_rop_modrm_reg_bits (int base, HARD_REG_SET &in, HARD_REG_SET &out) +{ + SET_HARD_REG_BIT (out, base); + SET_HARD_REG_BIT (out, base + 1); + SET_HARD_REG_BIT (in, base + 2); + SET_HARD_REG_BIT (in, base + 3); +} + +/* Called if -fmitigate_rop is in effect. Try to rewrite instructions so + that certain encodings of modr/m bytes do not occur. */ +static void +ix86_mitigate_rop (void) +{ + HARD_REG_SET input_risky; + HARD_REG_SET output_risky; + HARD_REG_SET inout_risky; + + CLEAR_HARD_REG_SET (output_risky); + CLEAR_HARD_REG_SET (input_risky); + SET_HARD_REG_BIT (output_risky, AX_REG); + SET_HARD_REG_BIT (output_risky, CX_REG); + SET_HARD_REG_BIT (input_risky, BX_REG); + SET_HARD_REG_BIT (input_risky, DX_REG); + set_rop_modrm_reg_bits (FIRST_SSE_REG, input_risky, output_risky); + set_rop_modrm_reg_bits (FIRST_REX_INT_REG, input_risky, output_risky); + set_rop_modrm_reg_bits (FIRST_REX_SSE_REG, input_risky, output_risky); + set_rop_modrm_reg_bits (FIRST_EXT_REX_SSE_REG, input_risky, output_risky); + set_rop_modrm_reg_bits (FIRST_MASK_REG, input_risky, output_risky); + set_rop_modrm_reg_bits (FIRST_BND_REG, input_risky, output_risky); + COPY_HARD_REG_SET (inout_risky, input_risky); + IOR_HARD_REG_SET (inout_risky, output_risky); + + compute_bb_for_insn (); + df_note_add_problem (); + df_analyze (); + + regrename_init (true); + regrename_analyze (NULL); + + auto_vec<du_head_p> cands; + + for (rtx_insn *insn = get_insns (); insn; insn = NEXT_INSN (insn)) + { + if (!NONDEBUG_INSN_P (insn)) + continue; + + if (GET_CODE (PATTERN (insn)) == USE + || GET_CODE (PATTERN (insn)) == CLOBBER) + continue; + + extract_insn (insn); + + int opno0, opno1; + int modrm = ix86_get_modrm_for_rop (insn, recog_data.operand, + recog_data.n_operands, &opno0, + &opno1); + + if (!ix86_rop_should_change_byte_p (modrm)) + continue; + + insn_rr_info *info = &insn_rr[INSN_UID (insn)]; + + /* This happens when regrename has to fail a block. */ + if (!info->op_info) + continue; + + if (info->op_info[opno0].n_chains != 0) + { + gcc_assert (info->op_info[opno0].n_chains == 1); + du_head_p op0c; + op0c = regrename_chain_from_id (info->op_info[opno0].heads[0]->id); + if (op0c->target_data_1 + op0c->target_data_2 == 0 + && !op0c->cannot_rename) + cands.safe_push (op0c); + + op0c->target_data_1++; + } + if (info->op_info[opno1].n_chains != 0) + { + gcc_assert (info->op_info[opno1].n_chains == 1); + du_head_p op1c; + op1c = regrename_chain_from_id (info->op_info[opno1].heads[0]->id); + if (op1c->target_data_1 + op1c->target_data_2 == 0 + && !op1c->cannot_rename) + cands.safe_push (op1c); + + op1c->target_data_2++; + } + } + + int i; + du_head_p head; + FOR_EACH_VEC_ELT (cands, i, head) + { + int old_reg, best_reg; + HARD_REG_SET unavailable; + + CLEAR_HARD_REG_SET (unavailable); + if (head->target_data_1) + IOR_HARD_REG_SET (unavailable, output_risky); + if (head->target_data_2) + IOR_HARD_REG_SET (unavailable, input_risky); + + int n_uses; + reg_class superclass = regrename_find_superclass (head, &n_uses, + &unavailable); + old_reg = head->regno; + best_reg = find_rename_reg (head, superclass, &unavailable, + old_reg, false); + bool ok = regrename_do_replace (head, best_reg); + gcc_assert (ok); + if (dump_file) + fprintf (dump_file, "Chain %d renamed as %s in %s\n", head->id, + reg_names[best_reg], reg_class_names[superclass]); + + } + + regrename_finish (); + + df_analyze (); + + basic_block bb; + regset_head live; + + INIT_REG_SET (&live); + + FOR_EACH_BB_FN (bb, cfun) + { + rtx_insn *insn; + + COPY_REG_SET (&live, DF_LR_OUT (bb)); + df_simulate_initialize_backwards (bb, &live); + + FOR_BB_INSNS_REVERSE (bb, insn) + { + if (!NONDEBUG_INSN_P (insn)) + continue; + + df_simulate_one_insn_backwards (bb, insn, &live); + + if (GET_CODE (PATTERN (insn)) == USE + || GET_CODE (PATTERN (insn)) == CLOBBER) + continue; + + extract_insn (insn); + constrain_operands_cached (insn, reload_completed); + int opno0, opno1; + int modrm = ix86_get_modrm_for_rop (insn, recog_data.operand, + recog_data.n_operands, &opno0, + &opno1); + if (modrm < 0 + || !ix86_rop_should_change_byte_p (modrm) + || opno0 == opno1) + continue; + + rtx oldreg = recog_data.operand[opno1]; + preprocess_constraints (insn); + const operand_alternative *alt = which_op_alt (); + + int i; + for (i = 0; i < recog_data.n_operands; i++) + if (i != opno1 + && alt[i].earlyclobber + && reg_overlap_mentioned_p (recog_data.operand[i], + oldreg)) + break; + + if (i < recog_data.n_operands) + continue; + + if (dump_file) + fprintf (dump_file, + "attempting to fix modrm byte in insn %d:" + " reg %d class %s", INSN_UID (insn), REGNO (oldreg), + reg_class_names[alt[opno1].cl]); + + HARD_REG_SET unavailable; + REG_SET_TO_HARD_REG_SET (unavailable, &live); + SET_HARD_REG_BIT (unavailable, REGNO (oldreg)); + IOR_COMPL_HARD_REG_SET (unavailable, call_used_reg_set); + IOR_HARD_REG_SET (unavailable, fixed_reg_set); + IOR_HARD_REG_SET (unavailable, output_risky); + IOR_COMPL_HARD_REG_SET (unavailable, + reg_class_contents[alt[opno1].cl]); + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (!TEST_HARD_REG_BIT (unavailable, i)) + break; + if (i == FIRST_PSEUDO_REGISTER) + { + if (dump_file) + fprintf (dump_file, ", none available\n"); + continue; + } + if (dump_file) + fprintf (dump_file, " -> %d\n", i); + rtx newreg = gen_rtx_REG (recog_data.operand_mode[opno1], i); + validate_change (insn, recog_data.operand_loc[opno1], newreg, false); + insn = emit_insn_before (gen_move_insn (newreg, oldreg), insn); + } + } +} + /* Implement machine specific optimizations. We implement padding of returns for K8 CPUs and pass to avoid 4 jumps in the single 16 byte window. */ static void @@ -45107,6 +45420,9 @@ ix86_reorg (void) with old MDEP_REORGS that are not CFG based. Recompute it now. */ compute_bb_for_insn (); + if (flag_mitigate_rop) + ix86_mitigate_rop (); + if (TARGET_SEH && current_function_has_exception_handlers ()) ix86_seh_fixup_eh_fallthru (); diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index 34a6d3f..39b058f 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -637,6 +637,19 @@ ] (const_int 1))) +(define_attr "modrm_class" "none,incdec,op0,op01,op02,pushpop,unknown" + (cond [(eq_attr "modrm" "0") + (const_string "none") + (eq_attr "type" "alu,imul,ishift") + (const_string "op02") + (eq_attr "type" "imov,imovx,lea,alu1,icmp") + (const_string "op01") + (eq_attr "type" "incdec") + (const_string "incdec") + (eq_attr "type" "push,pop") + (const_string "pushpop")] + (const_string "unknown"))) + ;; The (bounding maximum) length of an instruction in bytes. ;; ??? fistp and frndint are in fact fldcw/{fistp,frndint}/fldcw sequences. ;; Later we may want to split them and compute proper length as for @@ -1235,6 +1248,7 @@ cmp{<imodesuffix>}\t{%1, %0|%0, %1}" [(set_attr "type" "test,icmp") (set_attr "length_immediate" "0,1") + (set_attr "modrm_class" "op0,unknown") (set_attr "mode" "<MODE>")]) (define_insn "*cmp<mode>_1" @@ -1942,6 +1956,7 @@ "reload_completed" "xor{l}\t%k0, %k0" [(set_attr "type" "alu1") + (set_attr "modrm_class" "op0") (set_attr "mode" "SI") (set_attr "length_immediate" "0")]) @@ -2713,6 +2728,7 @@ "reload_completed" "xor{<imodesuffix>}\t%0, %0" [(set_attr "type" "alu1") + (set_attr "modrm_class" "op0") (set_attr "mode" "<MODE>") (set_attr "length_immediate" "0")]) @@ -16847,6 +16863,7 @@ ; Since we don't have the proper number of operands for an alu insn, ; fill in all the blanks. [(set_attr "type" "alu") + (set_attr "modrm_class" "op0") (set_attr "use_carry" "1") (set_attr "pent_pair" "pu") (set_attr "memory" "none") @@ -16864,6 +16881,7 @@ "" "sbb{<imodesuffix>}\t%0, %0" [(set_attr "type" "alu") + (set_attr "modrm_class" "op0") (set_attr "use_carry" "1") (set_attr "pent_pair" "pu") (set_attr "memory" "none") @@ -16879,6 +16897,7 @@ "" "sbb{<imodesuffix>}\t%0, %0" [(set_attr "type" "alu") + (set_attr "modrm_class" "op0") (set_attr "use_carry" "1") (set_attr "pent_pair" "pu") (set_attr "memory" "none") diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt index 82de21f..2723c22 100644 --- a/gcc/config/i386/i386.opt +++ b/gcc/config/i386/i386.opt @@ -889,3 +889,7 @@ Enum(stack_protector_guard) String(tls) Value(SSP_TLS) EnumValue Enum(stack_protector_guard) String(global) Value(SSP_GLOBAL) + +mmitigate-rop +Target Var(flag_mitigate_rop) Init(0) +Attempt to avoid generating instruction sequences containing ret bytes. |