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