diff mbox series

[2/4] rs6000: Emit ROP-protect instructions in prologue and epilogue

Message ID 19e943394d781d0d063ba1f61eb990a443383719.1619400506.git.wschmidt@linux.ibm.com
State New
Headers show
Series ROP support | expand

Commit Message

Bill Schmidt April 26, 2021, 1:50 a.m. UTC
Insert the hashst and hashchk instructions when -mrop-protect has been
selected.  The encrypted save slot for ROP mitigation is placed
between the parameter save area and the alloca space (if any;
otherwise the local variable space).

Note that ROP-mitigation instructions are currently only provided for
the ELFv2 ABI.

2021-03-25  Bill Schmidt  <wschmidt@linux.ibm.com>

gcc/
	* config/rs6000/rs6000-internal.h (rs6000_stack): Add
	rop_check_save_offset and rop_check_size.
	* config/rs6000/rs6000-logue.c (rs6000_stack_info): Compute
	rop_check_size and rop_check_save_offset.
	(debug_stack_info): Dump rop_save_offset and rop_check_size.
	(rs6000_emit_prologue): Assert if WORLD_SAVE used with
	-mrop-protect; emit hashst[p] in prologue; emit hashchk[p] in
	epilogue.
	* config/rs6000/rs6000.md (unspec): Add UNSPEC_HASHST[P] and
	UNSPEC_HASHCHK[P].
	(hashst): New define_insn.
	(hashstp): Likewise.
	(hashchk): Likewise.
	(hashchkp): Likewise.
---
 gcc/config/rs6000/rs6000-internal.h |  2 +
 gcc/config/rs6000/rs6000-logue.c    | 86 +++++++++++++++++++++++++----
 gcc/config/rs6000/rs6000.md         | 39 +++++++++++++
 3 files changed, 116 insertions(+), 11 deletions(-)

Comments

will schmidt April 26, 2021, 4:03 p.m. UTC | #1
On Sun, 2021-04-25 at 20:50 -0500, Bill Schmidt via Gcc-patches wrote:
> Insert the hashst and hashchk instructions when -mrop-protect has been
> selected.  The encrypted save slot for ROP mitigation is placed
> between the parameter save area and the alloca space (if any;
> otherwise the local variable space).
> 
> Note that ROP-mitigation instructions are currently only provided for
> the ELFv2 ABI.
> 
> 2021-03-25  Bill Schmidt  <wschmidt@linux.ibm.com>
> 
> gcc/
> 	* config/rs6000/rs6000-internal.h (rs6000_stack): Add
> 	rop_check_save_offset and rop_check_size.
> 	* config/rs6000/rs6000-logue.c (rs6000_stack_info): Compute
> 	rop_check_size and rop_check_save_offset.
> 	(debug_stack_info): Dump rop_save_offset and rop_check_size.
> 	(rs6000_emit_prologue): Assert if WORLD_SAVE used with
> 	-mrop-protect; emit hashst[p] in prologue; emit hashchk[p] in
> 	epilogue.
> 	* config/rs6000/rs6000.md (unspec): Add UNSPEC_HASHST[P] and
> 	UNSPEC_HASHCHK[P].
> 	(hashst): New define_insn.
> 	(hashstp): Likewise.
> 	(hashchk): Likewise.
> 	(hashchkp): Likewise.

ok

> ---
>  gcc/config/rs6000/rs6000-internal.h |  2 +
>  gcc/config/rs6000/rs6000-logue.c    | 86 +++++++++++++++++++++++++----
>  gcc/config/rs6000/rs6000.md         | 39 +++++++++++++
>  3 files changed, 116 insertions(+), 11 deletions(-)
> 
> diff --git a/gcc/config/rs6000/rs6000-internal.h b/gcc/config/rs6000/rs6000-internal.h
> index 428a7861a98..8fc77ba6138 100644
> --- a/gcc/config/rs6000/rs6000-internal.h
> +++ b/gcc/config/rs6000/rs6000-internal.h
> @@ -39,6 +39,7 @@ typedef struct rs6000_stack {
>    int gp_save_offset;		/* offset to save GP regs from initial SP */
>    int fp_save_offset;		/* offset to save FP regs from initial SP */
>    int altivec_save_offset;	/* offset to save AltiVec regs from initial SP */
> +  int rop_check_save_offset;	/* offset to save ROP check from initial SP */
>    int lr_save_offset;		/* offset to save LR from initial SP */
>    int cr_save_offset;		/* offset to save CR from initial SP */
>    int vrsave_save_offset;	/* offset to save VRSAVE from initial SP */
> @@ -53,6 +54,7 @@ typedef struct rs6000_stack {
>    int gp_size;			/* size of saved GP registers */
>    int fp_size;			/* size of saved FP registers */
>    int altivec_size;		/* size of saved AltiVec registers */
> +  int rop_check_size;		/* size of ROP check slot */
>    int cr_size;			/* size to hold CR if not in fixed area */
>    int vrsave_size;		/* size to hold VRSAVE */
>    int altivec_padding_size;	/* size of altivec alignment padding */

ok

> diff --git a/gcc/config/rs6000/rs6000-logue.c b/gcc/config/rs6000/rs6000-logue.c
> index b0ac183ceff..10cf7a2de93 100644
> --- a/gcc/config/rs6000/rs6000-logue.c
> +++ b/gcc/config/rs6000/rs6000-logue.c
> @@ -595,19 +595,21 @@ rs6000_savres_strategy (rs6000_stack_t *info,
>  		+---------------------------------------+
>  		| Parameter save area (+padding*) (P)	|  32
>  		+---------------------------------------+
> -		| Alloca space (A)			|  32+P
> +		| Optional ROP check slot (R)		|  32+P
>  		+---------------------------------------+
> -		| Local variable space (L)		|  32+P+A
> +		| Alloca space (A)			|  32+P+R
>  		+---------------------------------------+
> -		| Save area for AltiVec registers (W)	|  32+P+A+L
> +		| Local variable space (L)		|  32+P+R+A
>  		+---------------------------------------+
> -		| AltiVec alignment padding (Y)		|  32+P+A+L+W
> +		| Save area for AltiVec registers (W)	|  32+P+R+A+L
>  		+---------------------------------------+
> -		| Save area for GP registers (G)	|  32+P+A+L+W+Y
> +		| AltiVec alignment padding (Y)		|  32+P+R+A+L+W
>  		+---------------------------------------+
> -		| Save area for FP registers (F)	|  32+P+A+L+W+Y+G
> +		| Save area for GP registers (G)	|  32+P+R+A+L+W+Y
>  		+---------------------------------------+
> -	old SP->| back chain to caller's caller		|  32+P+A+L+W+Y+G+F
> +		| Save area for FP registers (F)	|  32+P+R+A+L+W+Y+G
> +		+---------------------------------------+
> +	old SP->| back chain to caller's caller		|  32+P+R+A+L+W+Y+G+F
>  		+---------------------------------------+
> 
>       * If the alloca area is present, the parameter save area is

ok

> @@ -717,6 +719,19 @@ rs6000_stack_info (void)
>    /* Does this function call anything (apart from sibling calls)?  */
>    info->calls_p = (!crtl->is_leaf || cfun->machine->ra_needs_full_frame);
> 
> +  if (TARGET_POWER10 && info->calls_p
> +      && DEFAULT_ABI == ABI_ELFv2 && rs6000_rop_protect)
> +    info->rop_check_size = 8;
> +  else if (rs6000_rop_protect && DEFAULT_ABI != ABI_ELFv2)
> +    {
> +      /* We can't check this in rs6000_option_override_internal since
> +	 DEFAULT_ABI isn't established yet.  */
> +      error ("%qs requires the ELFv2 ABI", "-mrop-protect");
> +      info->rop_check_size = 0;
ok
> +    }
> +  else
> +    info->rop_check_size = 0;
> +
ok

>    /* Determine if we need to save the condition code registers.  */
>    if (save_reg_p (CR2_REGNO)
>        || save_reg_p (CR3_REGNO)
> @@ -806,8 +821,14 @@ rs6000_stack_info (void)
>  	  gcc_assert (info->altivec_size == 0
>  		      || info->altivec_save_offset % 16 == 0);
> 
> -	  /* Adjust for AltiVec case.  */
> -	  info->ehrd_offset = info->altivec_save_offset - ehrd_size;
> +	  if (info->rop_check_size != 0)
> +	    {
> +	      info->rop_check_save_offset
> +		= info->altivec_save_offset - info->rop_check_size;
> +	      info->ehrd_offset = info->rop_check_save_offset - ehrd_size;
> +	    }
> +	  else
> +	    info->ehrd_offset = info->altivec_save_offset - ehrd_size;
ok

>  	}
>        else
>  	info->ehrd_offset = info->gp_save_offset - ehrd_size;
> @@ -849,6 +870,7 @@ rs6000_stack_info (void)
>  				  + info->gp_size
>  				  + info->altivec_size
>  				  + info->altivec_padding_size
> +				  + info->rop_check_size
>  				  + ehrd_size
>  				  + ehcr_size
>  				  + info->cr_size
> @@ -987,6 +1009,10 @@ debug_stack_info (rs6000_stack_t *info)
>      fprintf (stderr, "\tvrsave_save_offset  = %5d\n",
>  	     info->vrsave_save_offset);
> 
> +  if (info->rop_check_size)
> +    fprintf (stderr, "\trop_check_save_offset = %5d\n",
> +	     info->rop_check_save_offset);
> +

ok

>    if (info->lr_save_p)
>      fprintf (stderr, "\tlr_save_offset      = %5d\n", info->lr_save_offset);
> 
> @@ -1026,6 +1052,9 @@ debug_stack_info (rs6000_stack_t *info)
>      fprintf (stderr, "\taltivec_padding_size= %5d\n",
>  	     info->altivec_padding_size);
> 
> +  if (info->rop_check_size)
> +    fprintf (stderr, "\trop_check_size      = %5d\n", info->rop_check_size);
> +
>    if (info->cr_size)
>      fprintf (stderr, "\tcr_size             = %5d\n", info->cr_size);
> 
> @@ -3044,7 +3073,6 @@ rs6000_emit_prologue (void)
>  	cfun->machine->r2_setup_needed = true;
>      }
> 
> -
>    if (flag_stack_usage_info)
>      current_function_static_stack_size = info->total_size;
> 
> @@ -3105,7 +3133,8 @@ rs6000_emit_prologue (void)
>  		  && (!crtl->calls_eh_return
>  		      || info->ehrd_offset == -432)
>  		  && info->vrsave_save_offset == -224
> -		  && info->altivec_save_offset == -416);
> +		  && info->altivec_save_offset == -416
> +		  && !rs6000_rop_protect);
> 

ok


>        treg = gen_rtx_REG (SImode, 11);
>        emit_move_insn (treg, GEN_INT (-info->total_size));
> @@ -3252,6 +3281,24 @@ rs6000_emit_prologue (void)
>  	}
>      }
> 
> +  /* The ROP hash store must occur before a stack frame is created.  */
> +  /* NOTE: The hashst isn't needed if we're going to do a sibcall,
> +     but there's no way to know that here.  Harmless except for
> +     performance, of course.  */
> +  if (TARGET_POWER10 && rs6000_rop_protect && info->rop_check_size != 0)
> +    {
> +      gcc_assert (DEFAULT_ABI == ABI_ELFv2);
> +      rtx stack_ptr = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
> +      rtx addr = gen_rtx_PLUS (Pmode, stack_ptr,
> +			       GEN_INT (info->rop_check_save_offset));
> +      rtx mem = gen_rtx_MEM (Pmode, addr);
> +      rtx reg0 = gen_rtx_REG (Pmode, 0);
> +      if (rs6000_privileged)
> +	emit_insn (gen_hashstp (mem, reg0));
> +      else
> +	emit_insn (gen_hashst (mem, reg0));
> +    }
> +

ok


>    /* If we need to save CR, put it into r12 or r11.  Choose r12 except when
>       r12 will be needed by out-of-line gpr save.  */
>    cr_save_regno = ((DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2)
> @@ -4980,6 +5027,23 @@ rs6000_emit_epilogue (enum epilogue_type epilogue_type)
>        emit_insn (gen_add3_insn (sp_reg_rtx, sp_reg_rtx, sa));
>      }
> 
> +  /* The ROP hash check must occur after the stack pointer is restored,
> +     and is not performed for a sibcall.  */
> +  if (TARGET_POWER10 && rs6000_rop_protect && info->rop_check_size != 0
> +      && epilogue_type != EPILOGUE_TYPE_SIBCALL)
> +    {
> +      gcc_assert (DEFAULT_ABI == ABI_ELFv2);
> +      rtx stack_ptr = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
> +      rtx addr = gen_rtx_PLUS (Pmode, stack_ptr,
> +			       GEN_INT (info->rop_check_save_offset));
> +      rtx mem = gen_rtx_MEM (Pmode, addr);
> +      rtx reg0 = gen_rtx_REG (Pmode, 0);
> +      if (rs6000_privileged)
> +	emit_insn (gen_hashchkp (reg0, mem));
> +      else
> +	emit_insn (gen_hashchk (reg0, mem));
> +    }
> +

ok

>    if (epilogue_type != EPILOGUE_TYPE_SIBCALL && restoring_FPRs_inline)
>      {
>        if (cfa_restores)
> diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md
> index c8cdc42533c..eec2e7532e1 100644
> --- a/gcc/config/rs6000/rs6000.md
> +++ b/gcc/config/rs6000/rs6000.md
> @@ -154,6 +154,10 @@ (define_c_enum "unspec"
>     UNSPEC_CNTTZDM
>     UNSPEC_PDEPD
>     UNSPEC_PEXTD
> +   UNSPEC_HASHST
> +   UNSPEC_HASHSTP
> +   UNSPEC_HASHCHK
> +   UNSPEC_HASHCHKP
>    ])
> 
>  ;;
> @@ -14948,6 +14952,41 @@ (define_insn "*cmpeqb_internal"
>    "TARGET_P9_MISC && TARGET_64BIT"
>    "cmpeqb %0,%1,%2"
>    [(set_attr "type" "logical")])
> +
> +
> +;; ROP mitigation instructions.
> +
> +(define_insn "hashst"
> +  [(set (match_operand:DI 0 "simple_offsettable_mem_operand" "=m")
> +        (unspec:DI [(match_operand:DI 1 "int_reg_operand" "r")]
> +	           UNSPEC_HASHST))]
> +  "TARGET_POWER10 && rs6000_rop_protect"
> +  "hashst %1,%0"
> +  [(set_attr "type" "store")])
> +
> +(define_insn "hashstp"
> +  [(set (match_operand:DI 0 "simple_offsettable_mem_operand" "=m")
> +        (unspec:DI [(match_operand:DI 1 "int_reg_operand" "r")]
> +	           UNSPEC_HASHSTP))]
> +  "TARGET_POWER10 && rs6000_rop_protect && rs6000_privileged"
> +  "hashstp %1,%0"
> +  [(set_attr "type" "store")])
> +
> +(define_insn "hashchk"
> +  [(unspec_volatile [(match_operand:DI 0 "int_reg_operand" "r")
> +		     (match_operand:DI 1 "simple_offsettable_mem_operand" "m")]
> +		    UNSPEC_HASHCHK)]
> +  "TARGET_POWER10 && rs6000_rop_protect"
> +  "hashchk %0,%1"
> +  [(set_attr "type" "load")])
> +
> +(define_insn "hashchkp"
> +  [(unspec_volatile [(match_operand:DI 0 "int_reg_operand" "r")
> +		     (match_operand:DI 1 "simple_offsettable_mem_operand" "m")]
> +		    UNSPEC_HASHCHKP)]
> +  "TARGET_POWER10 && rs6000_rop_protect && rs6000_privileged"
> +  "hashchkp %0,%1"
> +  [(set_attr "type" "load")])

ok


lgtm, 
thanks,
-Will

>  
> 
>  (include "sync.md")
Segher Boessenkool May 12, 2021, 11:09 p.m. UTC | #2
Hi!

On Sun, Apr 25, 2021 at 08:50:16PM -0500, Bill Schmidt wrote:
> Insert the hashst and hashchk instructions when -mrop-protect has been
> selected.  The encrypted save slot for ROP mitigation is placed
> between the parameter save area and the alloca space (if any;
> otherwise the local variable space).
> 
> Note that ROP-mitigation instructions are currently only provided for
> the ELFv2 ABI.

The good thing about that is we will gain some experience with it before
we have to implement this for other ABIs, heh.

> 	(rs6000_emit_prologue): Assert if WORLD_SAVE used with
> 	-mrop-protect;

WORLD_SAVE is only used for Darwin, so this is pretty useless.  Use full
stops in changelogs please, not semicolons?

> 	* config/rs6000/rs6000.md (unspec): Add UNSPEC_HASHST[P] and
> 	UNSPEC_HASHCHK[P].
> 	(hashst): New define_insn.
> 	(hashstp): Likewise.
> 	(hashchk): Likewise.
> 	(hashchkp): Likewise.

> +  int rop_check_save_offset;	/* offset to save ROP check from initial SP */

Offset to store the hash, right?  It isn't a "Rop check" that is stored
there :-)

> +  if (TARGET_POWER10 && info->calls_p
> +      && DEFAULT_ABI == ABI_ELFv2 && rs6000_rop_protect)
> +    info->rop_check_size = 8;

One && per line please (if it doesn't fit on one line).  It of course
depends what is most readable, but in this case the four clauses are
all completely different, so each && should start a new line.

> +  else if (rs6000_rop_protect && DEFAULT_ABI != ABI_ELFv2)
> +    {
> +      /* We can't check this in rs6000_option_override_internal since
> +	 DEFAULT_ABI isn't established yet.  */
> +      error ("%qs requires the ELFv2 ABI", "-mrop-protect");
> +      info->rop_check_size = 0;
> +    }
> +  else
> +    info->rop_check_size = 0;

Hrm.  It would be nicer if you set rop_check_size to 0 before everything
else, and then overwrite it in the one case you need something else.

> @@ -806,8 +821,14 @@ rs6000_stack_info (void)
>  	  gcc_assert (info->altivec_size == 0
>  		      || info->altivec_save_offset % 16 == 0);
>  
> -	  /* Adjust for AltiVec case.  */
> -	  info->ehrd_offset = info->altivec_save_offset - ehrd_size;
> +	  if (info->rop_check_size != 0)
> +	    {
> +	      info->rop_check_save_offset
> +		= info->altivec_save_offset - info->rop_check_size;
> +	      info->ehrd_offset = info->rop_check_save_offset - ehrd_size;
> +	    }
> +	  else
> +	    info->ehrd_offset = info->altivec_save_offset - ehrd_size;
>  	}

Put the comment back here?  Also maybe easier if you do

	  /* Adjust for AltiVec case.  */
	  info->ehrd_offset = info->altivec_save_offset - ehrd_size;

	  /* Adjust for ROP protection.  */
	  if (info->rop_check_size != 0)
	    {
	      info->rop_check_save_offset
		= info->altivec_save_offset - info->rop_check_size;
	      info->ehrd_offset -= info->rop_check_size;
	    }

(or even make that unconditional, the "if" isn't needed anymore then).

> @@ -3044,7 +3073,6 @@ rs6000_emit_prologue (void)
>  	cfun->machine->r2_setup_needed = true;
>      }
>  
> -
>    if (flag_stack_usage_info)
>      current_function_static_stack_size = info->total_size;
>  

I don' think this improves the code, and it is an unrelated change, but
heh, *shrug*.

> +  /* The ROP hash store must occur before a stack frame is created.  */

Why?  (Explain in the code, not to me :-) )

> +  /* The ROP hash check must occur after the stack pointer is restored,
> +     and is not performed for a sibcall.  */

Same here.

> +   UNSPEC_HASHST
> +   UNSPEC_HASHSTP
> +   UNSPEC_HASHCHK
> +   UNSPEC_HASHCHKP

You don't need a separate unspec (or insns patterns) for privileged
mode: you can just sprintf the template, or maybe you can use
print_operand_punct_valid if you think this 'p' will happen more often
(there exist more insns like this already, we just never generate them).

> +;; ROP mitigation instructions.
> +
> +(define_insn "hashst"
> +  [(set (match_operand:DI 0 "simple_offsettable_mem_operand" "=m")
> +        (unspec:DI [(match_operand:DI 1 "int_reg_operand" "r")]
> +	           UNSPEC_HASHST))]

This needs unspec_volatile in principle.  There will be only one per
function, so it is kind of moot, but still.

Thanks,


Segher
diff mbox series

Patch

diff --git a/gcc/config/rs6000/rs6000-internal.h b/gcc/config/rs6000/rs6000-internal.h
index 428a7861a98..8fc77ba6138 100644
--- a/gcc/config/rs6000/rs6000-internal.h
+++ b/gcc/config/rs6000/rs6000-internal.h
@@ -39,6 +39,7 @@  typedef struct rs6000_stack {
   int gp_save_offset;		/* offset to save GP regs from initial SP */
   int fp_save_offset;		/* offset to save FP regs from initial SP */
   int altivec_save_offset;	/* offset to save AltiVec regs from initial SP */
+  int rop_check_save_offset;	/* offset to save ROP check from initial SP */
   int lr_save_offset;		/* offset to save LR from initial SP */
   int cr_save_offset;		/* offset to save CR from initial SP */
   int vrsave_save_offset;	/* offset to save VRSAVE from initial SP */
@@ -53,6 +54,7 @@  typedef struct rs6000_stack {
   int gp_size;			/* size of saved GP registers */
   int fp_size;			/* size of saved FP registers */
   int altivec_size;		/* size of saved AltiVec registers */
+  int rop_check_size;		/* size of ROP check slot */
   int cr_size;			/* size to hold CR if not in fixed area */
   int vrsave_size;		/* size to hold VRSAVE */
   int altivec_padding_size;	/* size of altivec alignment padding */
diff --git a/gcc/config/rs6000/rs6000-logue.c b/gcc/config/rs6000/rs6000-logue.c
index b0ac183ceff..10cf7a2de93 100644
--- a/gcc/config/rs6000/rs6000-logue.c
+++ b/gcc/config/rs6000/rs6000-logue.c
@@ -595,19 +595,21 @@  rs6000_savres_strategy (rs6000_stack_t *info,
 		+---------------------------------------+
 		| Parameter save area (+padding*) (P)	|  32
 		+---------------------------------------+
-		| Alloca space (A)			|  32+P
+		| Optional ROP check slot (R)		|  32+P
 		+---------------------------------------+
-		| Local variable space (L)		|  32+P+A
+		| Alloca space (A)			|  32+P+R
 		+---------------------------------------+
-		| Save area for AltiVec registers (W)	|  32+P+A+L
+		| Local variable space (L)		|  32+P+R+A
 		+---------------------------------------+
-		| AltiVec alignment padding (Y)		|  32+P+A+L+W
+		| Save area for AltiVec registers (W)	|  32+P+R+A+L
 		+---------------------------------------+
-		| Save area for GP registers (G)	|  32+P+A+L+W+Y
+		| AltiVec alignment padding (Y)		|  32+P+R+A+L+W
 		+---------------------------------------+
-		| Save area for FP registers (F)	|  32+P+A+L+W+Y+G
+		| Save area for GP registers (G)	|  32+P+R+A+L+W+Y
 		+---------------------------------------+
-	old SP->| back chain to caller's caller		|  32+P+A+L+W+Y+G+F
+		| Save area for FP registers (F)	|  32+P+R+A+L+W+Y+G
+		+---------------------------------------+
+	old SP->| back chain to caller's caller		|  32+P+R+A+L+W+Y+G+F
 		+---------------------------------------+
 
      * If the alloca area is present, the parameter save area is
@@ -717,6 +719,19 @@  rs6000_stack_info (void)
   /* Does this function call anything (apart from sibling calls)?  */
   info->calls_p = (!crtl->is_leaf || cfun->machine->ra_needs_full_frame);
 
+  if (TARGET_POWER10 && info->calls_p
+      && DEFAULT_ABI == ABI_ELFv2 && rs6000_rop_protect)
+    info->rop_check_size = 8;
+  else if (rs6000_rop_protect && DEFAULT_ABI != ABI_ELFv2)
+    {
+      /* We can't check this in rs6000_option_override_internal since
+	 DEFAULT_ABI isn't established yet.  */
+      error ("%qs requires the ELFv2 ABI", "-mrop-protect");
+      info->rop_check_size = 0;
+    }
+  else
+    info->rop_check_size = 0;
+
   /* Determine if we need to save the condition code registers.  */
   if (save_reg_p (CR2_REGNO)
       || save_reg_p (CR3_REGNO)
@@ -806,8 +821,14 @@  rs6000_stack_info (void)
 	  gcc_assert (info->altivec_size == 0
 		      || info->altivec_save_offset % 16 == 0);
 
-	  /* Adjust for AltiVec case.  */
-	  info->ehrd_offset = info->altivec_save_offset - ehrd_size;
+	  if (info->rop_check_size != 0)
+	    {
+	      info->rop_check_save_offset
+		= info->altivec_save_offset - info->rop_check_size;
+	      info->ehrd_offset = info->rop_check_save_offset - ehrd_size;
+	    }
+	  else
+	    info->ehrd_offset = info->altivec_save_offset - ehrd_size;
 	}
       else
 	info->ehrd_offset = info->gp_save_offset - ehrd_size;
@@ -849,6 +870,7 @@  rs6000_stack_info (void)
 				  + info->gp_size
 				  + info->altivec_size
 				  + info->altivec_padding_size
+				  + info->rop_check_size
 				  + ehrd_size
 				  + ehcr_size
 				  + info->cr_size
@@ -987,6 +1009,10 @@  debug_stack_info (rs6000_stack_t *info)
     fprintf (stderr, "\tvrsave_save_offset  = %5d\n",
 	     info->vrsave_save_offset);
 
+  if (info->rop_check_size)
+    fprintf (stderr, "\trop_check_save_offset = %5d\n",
+	     info->rop_check_save_offset);
+
   if (info->lr_save_p)
     fprintf (stderr, "\tlr_save_offset      = %5d\n", info->lr_save_offset);
 
@@ -1026,6 +1052,9 @@  debug_stack_info (rs6000_stack_t *info)
     fprintf (stderr, "\taltivec_padding_size= %5d\n",
 	     info->altivec_padding_size);
 
+  if (info->rop_check_size)
+    fprintf (stderr, "\trop_check_size      = %5d\n", info->rop_check_size);
+
   if (info->cr_size)
     fprintf (stderr, "\tcr_size             = %5d\n", info->cr_size);
 
@@ -3044,7 +3073,6 @@  rs6000_emit_prologue (void)
 	cfun->machine->r2_setup_needed = true;
     }
 
-
   if (flag_stack_usage_info)
     current_function_static_stack_size = info->total_size;
 
@@ -3105,7 +3133,8 @@  rs6000_emit_prologue (void)
 		  && (!crtl->calls_eh_return
 		      || info->ehrd_offset == -432)
 		  && info->vrsave_save_offset == -224
-		  && info->altivec_save_offset == -416);
+		  && info->altivec_save_offset == -416
+		  && !rs6000_rop_protect);
 
       treg = gen_rtx_REG (SImode, 11);
       emit_move_insn (treg, GEN_INT (-info->total_size));
@@ -3252,6 +3281,24 @@  rs6000_emit_prologue (void)
 	}
     }
 
+  /* The ROP hash store must occur before a stack frame is created.  */
+  /* NOTE: The hashst isn't needed if we're going to do a sibcall,
+     but there's no way to know that here.  Harmless except for
+     performance, of course.  */
+  if (TARGET_POWER10 && rs6000_rop_protect && info->rop_check_size != 0)
+    {
+      gcc_assert (DEFAULT_ABI == ABI_ELFv2);
+      rtx stack_ptr = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
+      rtx addr = gen_rtx_PLUS (Pmode, stack_ptr,
+			       GEN_INT (info->rop_check_save_offset));
+      rtx mem = gen_rtx_MEM (Pmode, addr);
+      rtx reg0 = gen_rtx_REG (Pmode, 0);
+      if (rs6000_privileged)
+	emit_insn (gen_hashstp (mem, reg0));
+      else
+	emit_insn (gen_hashst (mem, reg0));
+    }
+
   /* If we need to save CR, put it into r12 or r11.  Choose r12 except when
      r12 will be needed by out-of-line gpr save.  */
   cr_save_regno = ((DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2)
@@ -4980,6 +5027,23 @@  rs6000_emit_epilogue (enum epilogue_type epilogue_type)
       emit_insn (gen_add3_insn (sp_reg_rtx, sp_reg_rtx, sa));
     }
 
+  /* The ROP hash check must occur after the stack pointer is restored,
+     and is not performed for a sibcall.  */
+  if (TARGET_POWER10 && rs6000_rop_protect && info->rop_check_size != 0
+      && epilogue_type != EPILOGUE_TYPE_SIBCALL)
+    {
+      gcc_assert (DEFAULT_ABI == ABI_ELFv2);
+      rtx stack_ptr = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
+      rtx addr = gen_rtx_PLUS (Pmode, stack_ptr,
+			       GEN_INT (info->rop_check_save_offset));
+      rtx mem = gen_rtx_MEM (Pmode, addr);
+      rtx reg0 = gen_rtx_REG (Pmode, 0);
+      if (rs6000_privileged)
+	emit_insn (gen_hashchkp (reg0, mem));
+      else
+	emit_insn (gen_hashchk (reg0, mem));
+    }
+
   if (epilogue_type != EPILOGUE_TYPE_SIBCALL && restoring_FPRs_inline)
     {
       if (cfa_restores)
diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md
index c8cdc42533c..eec2e7532e1 100644
--- a/gcc/config/rs6000/rs6000.md
+++ b/gcc/config/rs6000/rs6000.md
@@ -154,6 +154,10 @@  (define_c_enum "unspec"
    UNSPEC_CNTTZDM
    UNSPEC_PDEPD
    UNSPEC_PEXTD
+   UNSPEC_HASHST
+   UNSPEC_HASHSTP
+   UNSPEC_HASHCHK
+   UNSPEC_HASHCHKP
   ])
 
 ;;
@@ -14948,6 +14952,41 @@  (define_insn "*cmpeqb_internal"
   "TARGET_P9_MISC && TARGET_64BIT"
   "cmpeqb %0,%1,%2"
   [(set_attr "type" "logical")])
+
+
+;; ROP mitigation instructions.
+
+(define_insn "hashst"
+  [(set (match_operand:DI 0 "simple_offsettable_mem_operand" "=m")
+        (unspec:DI [(match_operand:DI 1 "int_reg_operand" "r")]
+	           UNSPEC_HASHST))]
+  "TARGET_POWER10 && rs6000_rop_protect"
+  "hashst %1,%0"
+  [(set_attr "type" "store")])
+
+(define_insn "hashstp"
+  [(set (match_operand:DI 0 "simple_offsettable_mem_operand" "=m")
+        (unspec:DI [(match_operand:DI 1 "int_reg_operand" "r")]
+	           UNSPEC_HASHSTP))]
+  "TARGET_POWER10 && rs6000_rop_protect && rs6000_privileged"
+  "hashstp %1,%0"
+  [(set_attr "type" "store")])
+
+(define_insn "hashchk"
+  [(unspec_volatile [(match_operand:DI 0 "int_reg_operand" "r")
+		     (match_operand:DI 1 "simple_offsettable_mem_operand" "m")]
+		    UNSPEC_HASHCHK)]
+  "TARGET_POWER10 && rs6000_rop_protect"
+  "hashchk %0,%1"
+  [(set_attr "type" "load")])
+
+(define_insn "hashchkp"
+  [(unspec_volatile [(match_operand:DI 0 "int_reg_operand" "r")
+		     (match_operand:DI 1 "simple_offsettable_mem_operand" "m")]
+		    UNSPEC_HASHCHKP)]
+  "TARGET_POWER10 && rs6000_rop_protect && rs6000_privileged"
+  "hashchkp %0,%1"
+  [(set_attr "type" "load")])
 
 
 (include "sync.md")