1/* Branch Target Identification for AArch64 architecture. 2 Copyright (C) 2019-2020 Free Software Foundation, Inc. 3 Contributed by Arm Ltd. 4 5 This file is part of GCC. 6 7 GCC is free software; you can redistribute it and/or modify it 8 under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3, or (at your option) 10 any later version. 11 12 GCC is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with GCC; see the file COPYING3. If not see 19 <http://www.gnu.org/licenses/>. */ 20 21#define IN_TARGET_CODE 1 22 23#include "config.h" 24#define INCLUDE_STRING 25#include "system.h" 26#include "coretypes.h" 27#include "backend.h" 28#include "target.h" 29#include "rtl.h" 30#include "tree.h" 31#include "memmodel.h" 32#include "gimple.h" 33#include "tm_p.h" 34#include "stringpool.h" 35#include "attribs.h" 36#include "emit-rtl.h" 37#include "gimplify.h" 38#include "gimple-iterator.h" 39#include "dumpfile.h" 40#include "rtl-iter.h" 41#include "cfgrtl.h" 42#include "tree-pass.h" 43#include "cgraph.h" 44 45/* This pass enables the support for Branch Target Identification Mechanism 46 for AArch64. This is a new security feature introduced in ARMv8.5-A 47 archtitecture. A BTI instruction is used to guard against the execution 48 of instructions which are not the intended target of an indirect branch. 49 50 Outside of a guarded memory region, a BTI instruction executes as a NOP. 51 Within a guarded memory region any target of an indirect branch must be 52 a compatible BTI or BRK, HLT, PACIASP, PACIBASP instruction (even if the 53 branch is triggered in a non-guarded memory region). An incompatibility 54 generates a Branch Target Exception. 55 56 The compatibility of the BTI instruction is as follows: 57 BTI j : Can be a target of any indirect jump (BR Xn). 58 BTI c : Can be a target of any indirect call (BLR Xn and BR X16/X17). 59 BTI jc: Can be a target of any indirect call or indirect jump. 60 BTI : Can not be a target of any indirect call or indirect jump. 61 62 In order to enable this mechanism, this pass iterates through the 63 control flow of the code and adds appropriate BTI instructions : 64 * Add a new "BTI C" at the beginning of a function, unless its already 65 protected by a "PACIASP/PACIBSP". We exempt the functions that are only 66 called directly. 67 * Add a new "BTI J" for every target of an indirect jump, jump table targets, 68 non-local goto targets or labels that might be referenced by variables, 69 constant pools, etc (NOTE_INSN_DELETED_LABEL) 70 71 Since we have already changed the use of indirect tail calls to only x16 72 and x17, we do not have to use "BTI JC". 73 74 This pass is triggered by the command line option -mbranch-protection=bti or 75 -mbranch-protection=standard. Since all the BTI instructions are in the HINT 76 space, this pass does not require any minimum architecture version. */ 77 78namespace { 79 80const pass_data pass_data_insert_bti = 81{ 82 RTL_PASS, /* type. */ 83 "bti", /* name. */ 84 OPTGROUP_NONE, /* optinfo_flags. */ 85 TV_MACH_DEP, /* tv_id. */ 86 0, /* properties_required. */ 87 0, /* properties_provided. */ 88 0, /* properties_destroyed. */ 89 0, /* todo_flags_start. */ 90 0, /* todo_flags_finish. */ 91}; 92 93/* Check if X (or any sub-rtx of X) is a PACIASP/PACIBSP instruction. */ 94static bool 95aarch64_pac_insn_p (rtx x) 96{ 97 if (!INSN_P (x)) 98 return false; 99 100 subrtx_var_iterator::array_type array; 101 FOR_EACH_SUBRTX_VAR (iter, array, PATTERN (x), ALL) 102 { 103 rtx sub = *iter; 104 if (sub && GET_CODE (sub) == UNSPEC) 105 { 106 int unspec_val = XINT (sub, 1); 107 switch (unspec_val) 108 { 109 case UNSPEC_PACIASP: 110 /* fall-through. */ 111 case UNSPEC_PACIBSP: 112 return true; 113 114 default: 115 return false; 116 } 117 iter.skip_subrtxes (); 118 } 119 } 120 return false; 121} 122 123/* Check if INSN is a BTI J insn. */ 124static bool 125aarch64_bti_j_insn_p (rtx_insn *insn) 126{ 127 if (!insn || !INSN_P (insn)) 128 return false; 129 130 rtx pat = PATTERN (insn); 131 return GET_CODE (pat) == UNSPEC_VOLATILE && XINT (pat, 1) == UNSPECV_BTI_J; 132} 133 134/* Insert the BTI instruction. */ 135/* This is implemented as a late RTL pass that runs before branch 136 shortening and does the following. */ 137static unsigned int 138rest_of_insert_bti (void) 139{ 140 timevar_push (TV_MACH_DEP); 141 142 rtx bti_insn; 143 rtx_insn *insn; 144 basic_block bb; 145 146 bb = 0; 147 FOR_EACH_BB_FN (bb, cfun) 148 { 149 for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb)); 150 insn = NEXT_INSN (insn)) 151 { 152 /* If a label is marked to be preserved or can be a non-local goto 153 target, it must be protected with a BTI J. */ 154 if (LABEL_P (insn) 155 && (LABEL_PRESERVE_P (insn) 156 || bb->flags & BB_NON_LOCAL_GOTO_TARGET)) 157 { 158 bti_insn = gen_bti_j (); 159 emit_insn_after (bti_insn, insn); 160 continue; 161 } 162 163 /* There could still be more labels that are valid targets of a 164 BTI J instuction. To find them we start looking through the 165 JUMP_INSN. If it jumps to a jump table, then we find all labels 166 of the jump table to protect with a BTI J. */ 167 if (JUMP_P (insn)) 168 { 169 rtx_jump_table_data *table; 170 if (tablejump_p (insn, NULL, &table)) 171 { 172 rtvec vec = table->get_labels (); 173 int j; 174 rtx_insn *label; 175 176 for (j = GET_NUM_ELEM (vec) - 1; j >= 0; --j) 177 { 178 label = as_a <rtx_insn *> (XEXP (RTVEC_ELT (vec, j), 0)); 179 rtx_insn *next = next_nonnote_nondebug_insn (label); 180 if (aarch64_bti_j_insn_p (next)) 181 continue; 182 183 bti_insn = gen_bti_j (); 184 emit_insn_after (bti_insn, label); 185 } 186 } 187 } 188 189 /* Also look for calls to setjmp () which would be marked with 190 REG_SETJMP note and put a BTI J after. This is where longjump () 191 will return. */ 192 if (CALL_P (insn) && (find_reg_note (insn, REG_SETJMP, NULL))) 193 { 194 bti_insn = gen_bti_j (); 195 emit_insn_after (bti_insn, insn); 196 continue; 197 } 198 } 199 } 200 201 /* Since a Branch Target Exception can only be triggered by an indirect call, 202 we exempt function that are only called directly. We also exempt 203 functions that are already protected by Return Address Signing (PACIASP/ 204 PACIBSP). For all other cases insert a BTI C at the beginning of the 205 function. */ 206 if (!cgraph_node::get (cfun->decl)->only_called_directly_p ()) 207 { 208 bb = ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb; 209 insn = BB_HEAD (bb); 210 if (!aarch64_pac_insn_p (get_first_nonnote_insn ())) 211 { 212 bti_insn = gen_bti_c (); 213 emit_insn_before (bti_insn, insn); 214 } 215 } 216 217 timevar_pop (TV_MACH_DEP); 218 return 0; 219} 220 221 222class pass_insert_bti : public rtl_opt_pass 223{ 224public: 225 pass_insert_bti (gcc::context *ctxt) 226 : rtl_opt_pass (pass_data_insert_bti, ctxt) 227 {} 228 229 /* opt_pass methods: */ 230 virtual bool gate (function *) 231 { 232 return aarch64_bti_enabled (); 233 } 234 235 virtual unsigned int execute (function *) 236 { 237 return rest_of_insert_bti (); 238 } 239 240}; // class pass_insert_bti 241 242} // anon namespace 243 244rtl_opt_pass * 245make_pass_insert_bti (gcc::context *ctxt) 246{ 247 return new pass_insert_bti (ctxt); 248} 249