| /* Branch Target Identification for AArch64 architecture. |
| Copyright (C) 2019-2020 Free Software Foundation, Inc. |
| Contributed by Arm Ltd. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it |
| under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| GCC 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 GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #define IN_TARGET_CODE 1 |
| |
| #include "config.h" |
| #define INCLUDE_STRING |
| #include "system.h" |
| #include "coretypes.h" |
| #include "backend.h" |
| #include "target.h" |
| #include "rtl.h" |
| #include "tree.h" |
| #include "memmodel.h" |
| #include "gimple.h" |
| #include "tm_p.h" |
| #include "stringpool.h" |
| #include "attribs.h" |
| #include "emit-rtl.h" |
| #include "gimplify.h" |
| #include "gimple-iterator.h" |
| #include "dumpfile.h" |
| #include "rtl-iter.h" |
| #include "cfgrtl.h" |
| #include "tree-pass.h" |
| #include "cgraph.h" |
| |
| /* This pass enables the support for Branch Target Identification Mechanism |
| for AArch64. This is a new security feature introduced in ARMv8.5-A |
| archtitecture. A BTI instruction is used to guard against the execution |
| of instructions which are not the intended target of an indirect branch. |
| |
| Outside of a guarded memory region, a BTI instruction executes as a NOP. |
| Within a guarded memory region any target of an indirect branch must be |
| a compatible BTI or BRK, HLT, PACIASP, PACIBASP instruction (even if the |
| branch is triggered in a non-guarded memory region). An incompatibility |
| generates a Branch Target Exception. |
| |
| The compatibility of the BTI instruction is as follows: |
| BTI j : Can be a target of any indirect jump (BR Xn). |
| BTI c : Can be a target of any indirect call (BLR Xn and BR X16/X17). |
| BTI jc: Can be a target of any indirect call or indirect jump. |
| BTI : Can not be a target of any indirect call or indirect jump. |
| |
| In order to enable this mechanism, this pass iterates through the |
| control flow of the code and adds appropriate BTI instructions : |
| * Add a new "BTI C" at the beginning of a function, unless its already |
| protected by a "PACIASP/PACIBSP". We exempt the functions that are only |
| called directly. |
| * Add a new "BTI J" for every target of an indirect jump, jump table targets, |
| non-local goto targets or labels that might be referenced by variables, |
| constant pools, etc (NOTE_INSN_DELETED_LABEL) |
| |
| Since we have already changed the use of indirect tail calls to only x16 |
| and x17, we do not have to use "BTI JC". |
| |
| This pass is triggered by the command line option -mbranch-protection=bti or |
| -mbranch-protection=standard. Since all the BTI instructions are in the HINT |
| space, this pass does not require any minimum architecture version. */ |
| |
| namespace { |
| |
| const pass_data pass_data_insert_bti = |
| { |
| RTL_PASS, /* type. */ |
| "bti", /* name. */ |
| OPTGROUP_NONE, /* optinfo_flags. */ |
| TV_MACH_DEP, /* tv_id. */ |
| 0, /* properties_required. */ |
| 0, /* properties_provided. */ |
| 0, /* properties_destroyed. */ |
| 0, /* todo_flags_start. */ |
| 0, /* todo_flags_finish. */ |
| }; |
| |
| /* Check if X (or any sub-rtx of X) is a PACIASP/PACIBSP instruction. */ |
| static bool |
| aarch64_pac_insn_p (rtx x) |
| { |
| if (!INSN_P (x)) |
| return false; |
| |
| subrtx_var_iterator::array_type array; |
| FOR_EACH_SUBRTX_VAR (iter, array, PATTERN (x), ALL) |
| { |
| rtx sub = *iter; |
| if (sub && GET_CODE (sub) == UNSPEC) |
| { |
| int unspec_val = XINT (sub, 1); |
| switch (unspec_val) |
| { |
| case UNSPEC_PACIASP: |
| /* fall-through. */ |
| case UNSPEC_PACIBSP: |
| return true; |
| |
| default: |
| return false; |
| } |
| iter.skip_subrtxes (); |
| } |
| } |
| return false; |
| } |
| |
| /* Check if INSN is a BTI J insn. */ |
| static bool |
| aarch64_bti_j_insn_p (rtx_insn *insn) |
| { |
| if (!insn || !INSN_P (insn)) |
| return false; |
| |
| rtx pat = PATTERN (insn); |
| return GET_CODE (pat) == UNSPEC_VOLATILE && XINT (pat, 1) == UNSPECV_BTI_J; |
| } |
| |
| /* Insert the BTI instruction. */ |
| /* This is implemented as a late RTL pass that runs before branch |
| shortening and does the following. */ |
| static unsigned int |
| rest_of_insert_bti (void) |
| { |
| timevar_push (TV_MACH_DEP); |
| |
| rtx bti_insn; |
| rtx_insn *insn; |
| basic_block bb; |
| |
| bb = 0; |
| FOR_EACH_BB_FN (bb, cfun) |
| { |
| for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb)); |
| insn = NEXT_INSN (insn)) |
| { |
| /* If a label is marked to be preserved or can be a non-local goto |
| target, it must be protected with a BTI J. */ |
| if (LABEL_P (insn) |
| && (LABEL_PRESERVE_P (insn) |
| || bb->flags & BB_NON_LOCAL_GOTO_TARGET)) |
| { |
| bti_insn = gen_bti_j (); |
| emit_insn_after (bti_insn, insn); |
| continue; |
| } |
| |
| /* There could still be more labels that are valid targets of a |
| BTI J instuction. To find them we start looking through the |
| JUMP_INSN. If it jumps to a jump table, then we find all labels |
| of the jump table to protect with a BTI J. */ |
| if (JUMP_P (insn)) |
| { |
| rtx_jump_table_data *table; |
| if (tablejump_p (insn, NULL, &table)) |
| { |
| rtvec vec = table->get_labels (); |
| int j; |
| rtx_insn *label; |
| |
| for (j = GET_NUM_ELEM (vec) - 1; j >= 0; --j) |
| { |
| label = as_a <rtx_insn *> (XEXP (RTVEC_ELT (vec, j), 0)); |
| rtx_insn *next = next_nonnote_nondebug_insn (label); |
| if (aarch64_bti_j_insn_p (next)) |
| continue; |
| |
| bti_insn = gen_bti_j (); |
| emit_insn_after (bti_insn, label); |
| } |
| } |
| } |
| |
| /* Also look for calls to setjmp () which would be marked with |
| REG_SETJMP note and put a BTI J after. This is where longjump () |
| will return. */ |
| if (CALL_P (insn) && (find_reg_note (insn, REG_SETJMP, NULL))) |
| { |
| bti_insn = gen_bti_j (); |
| emit_insn_after (bti_insn, insn); |
| continue; |
| } |
| } |
| } |
| |
| /* Since a Branch Target Exception can only be triggered by an indirect call, |
| we exempt function that are only called directly. We also exempt |
| functions that are already protected by Return Address Signing (PACIASP/ |
| PACIBSP). For all other cases insert a BTI C at the beginning of the |
| function. */ |
| if (!cgraph_node::get (cfun->decl)->only_called_directly_p ()) |
| { |
| bb = ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb; |
| insn = BB_HEAD (bb); |
| if (!aarch64_pac_insn_p (get_first_nonnote_insn ())) |
| { |
| bti_insn = gen_bti_c (); |
| emit_insn_before (bti_insn, insn); |
| } |
| } |
| |
| timevar_pop (TV_MACH_DEP); |
| return 0; |
| } |
| |
| |
| class pass_insert_bti : public rtl_opt_pass |
| { |
| public: |
| pass_insert_bti (gcc::context *ctxt) |
| : rtl_opt_pass (pass_data_insert_bti, ctxt) |
| {} |
| |
| /* opt_pass methods: */ |
| virtual bool gate (function *) |
| { |
| return aarch64_bti_enabled (); |
| } |
| |
| virtual unsigned int execute (function *) |
| { |
| return rest_of_insert_bti (); |
| } |
| |
| }; // class pass_insert_bti |
| |
| } // anon namespace |
| |
| rtl_opt_pass * |
| make_pass_insert_bti (gcc::context *ctxt) |
| { |
| return new pass_insert_bti (ctxt); |
| } |