Home | History | Annotate | Line # | Download | only in arm
      1 /* Branch Target Identification for AArch64 architecture.
      2    Copyright (C) 2019-2024 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 for
     46    Arm/AArch64.  This is a security feature introduced in ARMv8.5-A
     47    architecture and ARMv8.1-M.  A BTI instruction is used to guard against the
     48    execution of instructions which are not the intended target of an indirect
     49    branch.
     50 
     51    Outside of a guarded memory region, a BTI instruction executes as a NOP.
     52    Within a guarded memory region any target of an indirect branch must be
     53    a compatible BTI or BRK, HLT, PACIASP, PACIBASP instruction (even if the
     54    branch is triggered in a non-guarded memory region).  An incompatibility
     55    generates a Branch Target Exception.
     56 
     57    The compatibility of the BTI instruction is as follows (AArch64
     58    examples):
     59    BTI j : Can be a target of any indirect jump (BR Xn).
     60    BTI c : Can be a target of any indirect call (BLR Xn and BR X16/X17).
     61    BTI jc: Can be a target of any indirect call or indirect jump.
     62    BTI   : Can not be a target of any indirect call or indirect jump.
     63 
     64   In order to enable this mechanism, this pass iterates through the
     65   control flow of the code and adds appropriate BTI instructions :
     66   * Add a new "BTI C" at the beginning of a function, unless its already
     67     protected by a "PACIASP/PACIBSP".  We exempt the functions that are only
     68     called directly.
     69   * Add a new "BTI J" for every target of an indirect jump, jump table targets,
     70     non-local goto targets or labels that might be referenced by variables,
     71     constant pools, etc (NOTE_INSN_DELETED_LABEL)
     72 
     73   Since we have already changed the use of indirect tail calls to only x16
     74   and x17, we do not have to use "BTI JC".
     75 
     76   This pass is triggered by the command line option -mbranch-protection=bti or
     77   -mbranch-protection=standard.  Since all the BTI instructions are in the HINT
     78   space, this pass does not require any minimum architecture version.  */
     79 
     80 namespace {
     81 
     82 const pass_data pass_data_insert_bti =
     83 {
     84   RTL_PASS, /* type.  */
     85   "bti", /* name.  */
     86   OPTGROUP_NONE, /* optinfo_flags.  */
     87   TV_MACH_DEP, /* tv_id.  */
     88   0, /* properties_required.  */
     89   0, /* properties_provided.  */
     90   0, /* properties_destroyed.  */
     91   0, /* todo_flags_start.  */
     92   0, /* todo_flags_finish.  */
     93 };
     94 
     95 /* Insert the BTI instruction.  */
     96 /* This is implemented as a late RTL pass that runs before branch
     97    shortening and does the following.  */
     98 static unsigned int
     99 rest_of_insert_bti (void)
    100 {
    101   timevar_push (TV_MACH_DEP);
    102 
    103   rtx bti_insn;
    104   rtx_insn *insn;
    105   basic_block bb;
    106 
    107   bb = 0;
    108   FOR_EACH_BB_FN (bb, cfun)
    109     {
    110       for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb));
    111 	   insn = NEXT_INSN (insn))
    112 	{
    113 	  /* If a label is marked to be preserved or can be a non-local goto
    114 	     target, it must be protected with a BTI J.  */
    115 	  if (LABEL_P (insn)
    116 	       && (LABEL_PRESERVE_P (insn)
    117 		   || bb->flags & BB_NON_LOCAL_GOTO_TARGET))
    118 	    {
    119 	      bti_insn = aarch_gen_bti_j ();
    120 	      emit_insn_after (bti_insn, insn);
    121 	      continue;
    122 	    }
    123 
    124 	  /* There could still be more labels that are valid targets of a
    125 	     BTI J instuction.  To find them we start looking through the
    126 	     JUMP_INSN.  If it jumps to a jump table, then we find all labels
    127 	     of the jump table to protect with a BTI J.  */
    128 	  if (JUMP_P (insn))
    129 	    {
    130 	      rtx_jump_table_data *table;
    131 	      if (tablejump_p (insn, NULL, &table))
    132 		{
    133 		  rtvec vec = table->get_labels ();
    134 		  int j;
    135 		  rtx_insn *label;
    136 
    137 		  for (j = GET_NUM_ELEM (vec) - 1; j >= 0; --j)
    138 		    {
    139 		      label = as_a <rtx_insn *> (XEXP (RTVEC_ELT (vec, j), 0));
    140 		      rtx_insn *next = next_nonnote_nondebug_insn (label);
    141 		      if (aarch_bti_j_insn_p (next))
    142 			continue;
    143 
    144 		      bti_insn = aarch_gen_bti_j ();
    145 		      emit_insn_after (bti_insn, label);
    146 		    }
    147 		}
    148 	    }
    149 
    150 	  /* Also look for calls to setjmp () which would be marked with
    151 	     REG_SETJMP note and put a BTI J after.  This is where longjump ()
    152 	     will return.  */
    153 	  if (CALL_P (insn) && (find_reg_note (insn, REG_SETJMP, NULL)))
    154 	    {
    155 	      bti_insn = aarch_gen_bti_j ();
    156 	      emit_insn_after (bti_insn, insn);
    157 	      continue;
    158 	    }
    159 	}
    160     }
    161 
    162   /* Since a Branch Target Exception can only be triggered by an indirect call,
    163      we exempt function that are only called directly.  We also exempt
    164      functions that are already protected by Return Address Signing (PACIASP/
    165      PACIBSP).  For all other cases insert a BTI C at the beginning of the
    166      function.  */
    167   if (!cgraph_node::get (cfun->decl)->only_called_directly_p ())
    168     {
    169       bb = ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb;
    170       insn = BB_HEAD (bb);
    171       if (!aarch_pac_insn_p (get_first_nonnote_insn ()))
    172 	{
    173 	  bti_insn = aarch_gen_bti_c ();
    174 	  emit_insn_before (bti_insn, insn);
    175 	}
    176     }
    177 
    178   timevar_pop (TV_MACH_DEP);
    179   return 0;
    180 }
    181 
    182 
    183 class pass_insert_bti : public rtl_opt_pass
    184 {
    185 public:
    186   pass_insert_bti (gcc::context *ctxt)
    187     : rtl_opt_pass (pass_data_insert_bti, ctxt)
    188   {}
    189 
    190   /* opt_pass methods: */
    191   virtual bool gate (function *)
    192     {
    193       if (aarch_bti_enabled ())
    194         {
    195           aarch_bti_arch_check ();
    196           return true;
    197         }
    198       return false;
    199     }
    200 
    201   virtual unsigned int execute (function *)
    202     {
    203       return rest_of_insert_bti ();
    204     }
    205 
    206 }; // class pass_insert_bti
    207 
    208 } // anon namespace
    209 
    210 rtl_opt_pass *
    211 make_pass_insert_bti (gcc::context *ctxt)
    212 {
    213   return new pass_insert_bti (ctxt);
    214 }
    215