[AArch64, 5/6] Enable BTI : Add new pass for BTI.

This patch is part of a series that enables ARMv8.5-A in GCC and
adds Branch Target Identification Mechanism.

This patch adds a new pass called "bti" which is triggered by the command
line argument -mbranch-protection whenever "bti" is turned on.

The pass iterates through the instructions and adds appropriated BTI
instructions based on the following:
  * Add a new "BTI C" at the beginning of a function, unless its already
    protected by a "PACIASP". 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".
(check patch 3/6).

*** gcc/ChangeLog ***

2018-01-09  Sudakshina Das  <sudi.das@arm.com>
	    Ramana Radhakrishnan  <ramana.radhakrishnan@arm.com>

	* config.gcc (aarch64*-*-*): Add aarch64-bti-insert.o.
	* gcc/config/aarch64/aarch64.h: Update comment for TRAMPOLINE_SIZE.
	* config/aarch64/aarch64.c (aarch64_asm_trampoline_template): Update
	if bti is enabled.
	* config/aarch64/aarch64-bti-insert.c: New file.
	* config/aarch64/aarch64-passes.def (INSERT_PASS_BEFORE): Insert bti
	pass.
	* config/aarch64/aarch64-protos.h (make_pass_insert_bti): Declare the
	new bti pass.
	* config/aarch64/aarch64.md (unspecv): Add UNSPECV_BTI_NOARG,
	UNSPECV_BTI_C, UNSPECV_BTI_J and UNSPECV_BTI_JC.
	(bti_noarg, bti_j, bti_c, bti_jc): New define_insns.
	* config/aarch64/t-aarch64: Add rule for aarch64-bti-insert.o.

*** gcc/testsuite/ChangeLog ***

2018-01-09  Sudakshina Das  <sudi.das@arm.com>

	* gcc.target/aarch64/bti-1.c: New test.
	* gcc.target/aarch64/bti-2.c: New test.
	* gcc.target/aarch64/bti-3.c: New test.
	* lib/target-supports.exp
	(check_effective_target_aarch64_bti_hw): Add new check for BTI hw.

Co-Authored-By: Ramana Radhakrishnan <ramana.radhakrishnan@arm.com>

From-SVN: r267769
This commit is contained in:
Sudakshina Das 2019-01-09 14:21:22 +00:00 committed by Sudakshina Das
parent 30afdf34a6
commit b5f794b47b
14 changed files with 490 additions and 7 deletions

View file

@ -1,3 +1,20 @@
2018-01-09 Sudakshina Das <sudi.das@arm.com>
Ramana Radhakrishnan <ramana.radhakrishnan@arm.com>
* config.gcc (aarch64*-*-*): Add aarch64-bti-insert.o.
* gcc/config/aarch64/aarch64.h: Update comment for TRAMPOLINE_SIZE.
* config/aarch64/aarch64.c (aarch64_asm_trampoline_template): Update
if bti is enabled.
* config/aarch64/aarch64-bti-insert.c: New file.
* config/aarch64/aarch64-passes.def (INSERT_PASS_BEFORE): Insert bti
pass.
* config/aarch64/aarch64-protos.h (make_pass_insert_bti): Declare the
new bti pass.
* config/aarch64/aarch64.md (unspecv): Add UNSPECV_BTI_NOARG,
UNSPECV_BTI_C, UNSPECV_BTI_J and UNSPECV_BTI_JC.
(bti_noarg, bti_j, bti_c, bti_jc): New define_insns.
* config/aarch64/t-aarch64: Add rule for aarch64-bti-insert.o.
2018-01-09 Sudakshina Das <sudi.das@arm.com>
* config/aarch64/aarch64-protos.h (aarch64_bti_enabled): Declare.

View file

@ -318,7 +318,7 @@ aarch64*-*-*)
c_target_objs="aarch64-c.o"
cxx_target_objs="aarch64-c.o"
d_target_objs="aarch64-d.o"
extra_objs="aarch64-builtins.o aarch-common.o cortex-a57-fma-steering.o aarch64-speculation.o falkor-tag-collision-avoidance.o"
extra_objs="aarch64-builtins.o aarch-common.o cortex-a57-fma-steering.o aarch64-speculation.o falkor-tag-collision-avoidance.o aarch64-bti-insert.o"
target_gtfiles="\$(srcdir)/config/aarch64/aarch64-builtins.c"
target_has_targetm_common=yes
;;

View file

@ -0,0 +1,235 @@
/* Branch Target Identification for AArch64 architecture.
Copyright (C) 2019 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 x;
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_PACISP:
return true;
default:
return false;
}
iter.skip_subrtxes ();
}
}
return false;
}
/* 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;
/* 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);
}
}
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. The same applies to
NOTE_INSN_DELETED_LABEL since they are basically labels that might
be referenced via variables or constant pool. */
if ((LABEL_P (insn)
&& (LABEL_PRESERVE_P (insn)
|| bb->flags & BB_NON_LOCAL_GOTO_TARGET))
|| (NOTE_P (insn)
&& NOTE_KIND (insn) == NOTE_INSN_DELETED_LABEL))
{
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));
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;
}
}
}
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);
}

View file

@ -21,3 +21,4 @@
INSERT_PASS_AFTER (pass_regrename, 1, pass_fma_steering);
INSERT_PASS_BEFORE (pass_reorder_blocks, 1, pass_track_speculation);
INSERT_PASS_AFTER (pass_machine_reorg, 1, pass_tag_collision_avoidance);
INSERT_PASS_BEFORE (pass_shorten_branches, 1, pass_insert_bti);

View file

@ -633,6 +633,7 @@ extern void aarch64_d_target_versions (void);
rtl_opt_pass *make_pass_fma_steering (gcc::context *);
rtl_opt_pass *make_pass_track_speculation (gcc::context *);
rtl_opt_pass *make_pass_tag_collision_avoidance (gcc::context *);
rtl_opt_pass *make_pass_insert_bti (gcc::context *ctxt);
poly_uint64 aarch64_regmode_natural_size (machine_mode);

View file

@ -8250,18 +8250,36 @@ aarch64_return_addr (int count, rtx frame ATTRIBUTE_UNUSED)
static void
aarch64_asm_trampoline_template (FILE *f)
{
int offset1 = 16;
int offset2 = 20;
if (aarch64_bti_enabled ())
{
asm_fprintf (f, "\thint\t34 // bti c\n");
offset1 -= 4;
offset2 -= 4;
}
if (TARGET_ILP32)
{
asm_fprintf (f, "\tldr\tw%d, .+16\n", IP1_REGNUM - R0_REGNUM);
asm_fprintf (f, "\tldr\tw%d, .+16\n", STATIC_CHAIN_REGNUM - R0_REGNUM);
asm_fprintf (f, "\tldr\tw%d, .+%d\n", IP1_REGNUM - R0_REGNUM, offset1);
asm_fprintf (f, "\tldr\tw%d, .+%d\n", STATIC_CHAIN_REGNUM - R0_REGNUM,
offset1);
}
else
{
asm_fprintf (f, "\tldr\t%s, .+16\n", reg_names [IP1_REGNUM]);
asm_fprintf (f, "\tldr\t%s, .+20\n", reg_names [STATIC_CHAIN_REGNUM]);
asm_fprintf (f, "\tldr\t%s, .+%d\n", reg_names [IP1_REGNUM], offset1);
asm_fprintf (f, "\tldr\t%s, .+%d\n", reg_names [STATIC_CHAIN_REGNUM],
offset2);
}
asm_fprintf (f, "\tbr\t%s\n", reg_names [IP1_REGNUM]);
assemble_aligned_integer (4, const0_rtx);
/* The trampoline needs an extra padding instruction. In case if BTI is
enabled the padding instruction is replaced by the BTI instruction at
the beginning. */
if (!aarch64_bti_enabled ())
assemble_aligned_integer (4, const0_rtx);
assemble_aligned_integer (POINTER_BYTES, const0_rtx);
assemble_aligned_integer (POINTER_BYTES, const0_rtx);
}

View file

@ -918,7 +918,7 @@ typedef struct
#define RETURN_ADDR_RTX aarch64_return_addr
/* 3 insns + padding + 2 pointer-sized entries. */
/* BTI c + 3 insns + 2 pointer-sized entries. */
#define TRAMPOLINE_SIZE (TARGET_ILP32 ? 24 : 32)
/* Trampolines contain dwords, so must be dword aligned. */

View file

@ -237,6 +237,10 @@
UNSPECV_BLOCKAGE ; Represent a blockage
UNSPECV_PROBE_STACK_RANGE ; Represent stack range probing.
UNSPECV_SPECULATION_BARRIER ; Represent speculation barrier.
UNSPECV_BTI_NOARG ; Represent BTI.
UNSPECV_BTI_C ; Represent BTI c.
UNSPECV_BTI_J ; Represent BTI j.
UNSPECV_BTI_JC ; Represent BTI jc.
]
)
@ -6939,6 +6943,35 @@
[(set_attr "type" "csel")]
)
;; BTI <target> instructions
(define_insn "bti_noarg"
[(unspec_volatile [(const_int 0)] UNSPECV_BTI_NOARG)]
""
"hint\t32 // bti"
[(set_attr "type" "no_insn")]
)
(define_insn "bti_c"
[(unspec_volatile [(const_int 0)] UNSPECV_BTI_C)]
""
"hint\t34 // bti c"
[(set_attr "type" "no_insn")]
)
(define_insn "bti_j"
[(unspec_volatile [(const_int 0)] UNSPECV_BTI_J)]
""
"hint\t36 // bti j"
[(set_attr "type" "no_insn")]
)
(define_insn "bti_jc"
[(unspec_volatile [(const_int 0)] UNSPECV_BTI_JC)]
""
"hint\t38 // bti jc"
[(set_attr "type" "no_insn")]
)
;; Helper for aarch64.c code.
(define_expand "set_clobber_cc"
[(parallel [(set (match_operand 0)

View file

@ -91,6 +91,15 @@ falkor-tag-collision-avoidance.o: \
$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
$(srcdir)/config/aarch64/falkor-tag-collision-avoidance.c
aarch64-bti-insert.o: $(srcdir)/config/aarch64/aarch64-bti-insert.c \
$(CONFIG_H) $(SYSTEM_H) $(TM_H) $(REGS_H) insn-config.h $(RTL_BASE_H) \
dominance.h cfg.h cfganal.h $(BASIC_BLOCK_H) $(INSN_ATTR_H) $(RECOG_H) \
output.h hash-map.h $(DF_H) $(OBSTACK_H) $(TARGET_H) $(RTL_H) \
$(CONTEXT_H) $(TREE_PASS_H) regrename.h \
$(srcdir)/config/aarch64/aarch64-protos.h
$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
$(srcdir)/config/aarch64/aarch64-bti-insert.c
comma=,
MULTILIB_OPTIONS = $(subst $(comma),/, $(patsubst %, mabi=%, $(subst $(comma),$(comma)mabi=,$(TM_MULTILIB_CONFIG))))
MULTILIB_DIRNAMES = $(subst $(comma), ,$(TM_MULTILIB_CONFIG))

View file

@ -1,3 +1,11 @@
2018-01-09 Sudakshina Das <sudi.das@arm.com>
* gcc.target/aarch64/bti-1.c: New test.
* gcc.target/aarch64/bti-2.c: New test.
* gcc.target/aarch64/bti-3.c: New test.
* lib/target-supports.exp
(check_effective_target_aarch64_bti_hw): Add new check for BTI hw.
2018-01-09 Sudakshina Das <sudi.das@arm.com>
* gcc.target/aarch64/test_frame_17.c: Update to check for EP0_REGNUM

View file

@ -0,0 +1,59 @@
/* { dg-do compile } */
/* -Os to create jump table. */
/* { dg-options "-Os -mbranch-protection=standard" } */
extern int f1 (void);
extern int f2 (void);
extern int f3 (void);
extern int f4 (void);
extern int f5 (void);
extern int f6 (void);
extern int f7 (void);
extern int f8 (void);
extern int f9 (void);
extern int f10 (void);
int (*ptr) (void);
int
f_jump_table (int y, int n)
{
int i;
for (i = 0; i < n ;i ++)
{
switch (y)
{
case 0 : ptr = f1; break;
case 1 : ptr = f2; break;
case 2 : ptr = f3; break;
case 3 : ptr = f4; break;
case 4 : ptr = f5; break;
case 5 : ptr = f6; break;
case 6 : ptr = f7; break;
case 7 : ptr = f8; break;
case 8 : ptr = f9; break;
case 9 : ptr = f10; break;
default: break;
}
y += ptr ();
}
return (y == 0)? y+1:4;
}
/* f_jump_table should have PACIASP and AUTIASP. */
/* { dg-final { scan-assembler-times "hint\t25" 1 } } */
/* { dg-final { scan-assembler-times "hint\t29" 1 } } */
int
f_label_address ()
{
static void * addr = &&lab1;
goto *addr;
lab1:
addr = &&lab2;
return 1;
lab2:
addr = &&lab1;
return 2;
}
/* { dg-final { scan-assembler-times "hint\t34" 1 } } */
/* { dg-final { scan-assembler-times "hint\t36" 12 } } */

View file

@ -0,0 +1,34 @@
/* { dg-do run } */
/* { dg-require-effective-target aarch64_bti_hw } */
/* { dg-options "-mbranch-protection=standard" } */
#include<stdio.h>
typedef int FP (int);
int
f1 (FP fp, int n)
{
return (fp) (n);
}
int
f2 (int n, FP fp)
{
return (fp) (n);
}
int __attribute__ ((noinline))
func (int x)
{
return x+1;
}
int main ()
{
int s = 0;
s += f1 (func, 10);
s += f2 (s, func);
printf ("S: %d\n", s);
return !(s == 23);
}

View file

@ -0,0 +1,52 @@
/* This is a copy of gcc/testsuite/gcc.c-torture/execute/pr56982.c to test the
setjmp case of the bti pass. */
/* { dg-do run } */
/* { dg-require-effective-target aarch64_bti_hw } */
/* { dg-options "--save-temps -mbranch-protection=standard" } */
#include <setjmp.h>
extern void abort (void);
extern void exit (int);
static jmp_buf env;
void baz (void)
{
__asm__ volatile ("" : : : "memory");
}
static inline int g(int x)
{
if (x)
{
baz();
return 0;
}
else
{
baz();
return 1;
}
}
int f(int *e)
{
if (*e)
return 1;
int x = setjmp(env);
int n = g(x);
if (n == 0)
exit(0);
if (x)
abort();
longjmp(env, 42);
}
/* { dg-final { scan-assembler "hint\t36" } } */
int main(int argc, char** argv)
{
int v = 0;
return f(&v);
}

View file

@ -4313,6 +4313,22 @@ proc check_effective_target_arm_neonv2_hw { } {
} [add_options_for_arm_neonv2 ""]]
}
# ID_AA64PFR1_EL1.BT using bits[3:0] == 1 implies BTI implimented.
proc check_effective_target_aarch64_bti_hw { } {
if { ![istarget aarch64*-*-*] } {
return 0
}
return [check_runtime aarch64_bti_hw_available {
int
main (void)
{
int a;
asm volatile ("mrs %0, id_aa64pfr1_el1" : "=r" (a));
return !((a & 0xf) == 1);
}
} "-O2" ]
}
# Return 1 if the target supports the ARMv8.1 Adv.SIMD extension, 0
# otherwise. The test is valid for AArch64 and ARM. Record the command
# line options needed.