diff mbox

[v4] add -fprolog-pad=N,M option

Message ID 20161221172333.GA6050@suse.de
State New
Headers show

Commit Message

Torsten Duwe Dec. 21, 2016, 5:23 p.m. UTC
On Tue, Dec 20, 2016 at 04:34:02PM +0300, Maxim Kuvyrkov wrote:
> 
> Hi Bernd, thanks for reviewing this!
> 
> Regarding the usefulness of this feature, it has been discussed here (2 years ago): http://gcc.gcc.gnu.narkive.com/JfWUDn8Y/rfc-kernel-livepatching-support-in-gcc .
> 
> Kernel live-patching is of interest to several major distros, and it already supported by GCC via architecture-specific implementations (x86, s390, sparc).  The -fprolog-pad= option is an architecture-neutral equivalent that can be used for porting kernel live-patching to new architectures.
> 
> Existing support for kernel live-patching in x86, s390 and sparc backends can be redirected to use -fprolog-pad= functionality and, thus, simplified.

Yes, indeed.

Hopefully I caught all the style problems, plus the functional change
that I no longer fiddle with the attributes by hand. I was really hoping
for more feedback on the functionality...

To be done: your default_print_prolog_pad really nicely handles >90% of
all cases, but what if a) the target platform does not allow arbitrary
section names or b) it has very distinct ideas about code sizes and offsets
around the prologue? (All target CPUs have an insn called "nop" which
does what we expect, AFAICS ;-) gcc should error out somehow meaningfully,
IMHO.

	Torsten

2016-12-21	Torsten Duwe	<duwe@suse.de> : 

	 * c-family/c-attribs.c : introduce prolog_pad attribute and create
	   a handler for it.

	 * lto/lto-lang.c : Likewise.

	 * common.opt : introduce -fprolog_pad command line option
	   and its variables prolog_nop_pad_size and prolog_nop_pad_entry.

	 * doc/extend.texi : document prolog_pad attribute.

	 * doc/invoke.texi : document -fprolog_pad command line option.

	 * opts.c (OPT_fprolog_pad_): add parser.

	 * doc/tm.texi.in (TARGET_ASM_PRINT_PROLOG_PAD): new target hook

	 * doc/tm.texi : Likewise.

	 * target.def (print_prolog_pad): Likewise.

	 * targhooks.h (default_print_prolog_pad): new function.

	 * targhooks.c (default_print_prolog_pad): Likewise.

	 * testsuite/c-c++-common/attribute-prolog_pad-1.c : New test.

	 * toplev.c (process_options): Switch off IPA-RA if
	   prolog pads are being generated.

	 * varasm.c (assemble_start_function): look at prolog-pad command
	   line switch and function attributes and maybe generate nop
	   pads by calling print_prolog_pad.

Comments

Sandra Loosemore Dec. 21, 2016, 6:52 p.m. UTC | #1
On 12/21/2016 10:23 AM, Torsten Duwe wrote:

> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index 1f303bc..a09851a 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -3076,6 +3076,17 @@ that affect more than one function.
>   This attribute should be used for debugging purposes only.  It is not
>   suitable in production code.
>
> +@item prolog_pad
> +@cindex @code{prolog_pad} function attribute
> +@cindex function entry padded with NOPs
> +In case the target's text segment can be made writable at run time
> +by any means, padding the function entry with a number of NOPs can
> +be used to provide a universal tool for instrumentation.  Usually,
> +prolog padding is enabled globally using the -fprolog-pad= command
> +line switch, and disabled by the attribute keyword for functions

@option{-fprolog-pad=} command-line switch

> +that are part of the actual instrumentation framework, to easily avoid
> +an endless recursion.

I'm confused.  Does the prolog_pad attribute *disable* the padding, 
then?  You should say that explicitly.

>   @item pure
>   @cindex @code{pure} function attribute
>   @cindex functions that have no side effects
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index b729964..21e5067 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -11309,6 +11309,21 @@ of the function name, it is considered to be a match.  For C99 and C++
>   extended identifiers, the function name must be given in UTF-8, not
>   using universal character names.
>
> +@item -fprolog-pad=N

@var{N}

> +@opindex fprolog-pad
> +Generate a pad of N nops right at the beginning

@var{N} again.

Can we be consistent about whether it's "NOP" or "nop"?  You used "NOP" 
in the doc snippet above, and that seems to be the preferred usage.

> +of each function, which can be used to patch in any desired

What if the target supports NOPs of different sizes?  (E.g., nios2 with 
-march=r2 -mcdx).

Where, exactly, is the padding inserted?  "At the beginning of each 
function" might correspond to the address of the first instruction of 
the function, or it might correspond to the address of some instruction 
after the prolog where GDB would set a breakpoint on function entry. 
This feature isn't usable without more explicit documentation about what 
it does.

> +instrumentation at run time, provided that the code segment
> +is writeable.  For run time identification, the starting addresses

s/writeable/writable/
s/run time identification/run-time identification/

> +of these pads, which correspond to their respective functions,
> +are additionally collected in the @code{__prolog_pads_loc} section
> +of the resulting binary.
> +
> +Note that value of @code{__attribute__ ((prolog_pad (N)))} takes

You didn't explain this syntax in the documentation of the attribute 
above...

> +precedence over command-line option -fprolog_pad=N.  This can be used

@option and @var markup on the option, please.  And if N here is 
different than N above, don't re-use the same metasyntactic variable for it.

> +to increase the pad size or to remove the pad completely on a single
> +function.
> +
>   @end table
>
>
> diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
> index cdf5f48..65c265c 100644
> --- a/gcc/doc/tm.texi
> +++ b/gcc/doc/tm.texi
> @@ -4564,6 +4564,10 @@ will select the smallest suitable mode.
>   This section describes the macros that output function entry
>   (@dfn{prologue}) and exit (@dfn{epilogue}) code.
>
> +@deftypefn {Target Hook} void TARGET_ASM_PRINT_PROLOG_PAD (FILE *@var{file}, unsigned HOST_WIDE_INT @var{pad_size}, bool @var{record_p})
> +Generate prologue pad

Needs more extensive documentation, and sentences should end in a period.

-Sandra
diff mbox

Patch

diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index f5adade..21d0386 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -139,6 +139,7 @@  static tree handle_bnd_variable_size_attribute (tree *, tree, tree, int, bool *)
 static tree handle_bnd_legacy (tree *, tree, tree, int, bool *);
 static tree handle_bnd_instrument (tree *, tree, tree, int, bool *);
 static tree handle_fallthrough_attribute (tree *, tree, tree, int, bool *);
+static tree handle_prolog_pad_attribute (tree *, tree, tree, int, bool *);
 
 /* Table of machine-independent attributes common to all C-like languages.
 
@@ -345,6 +346,8 @@  const struct attribute_spec c_common_attribute_table[] =
 			      handle_bnd_instrument, false },
   { "fallthrough",	      0, 0, false, false, false,
 			      handle_fallthrough_attribute, false },
+  { "prolog_pad",	      1, 2, true, false, false,
+			      handle_prolog_pad_attribute, false },
   { NULL,                     0, 0, false, false, false, NULL, false }
 };
 
@@ -3173,3 +3176,10 @@  handle_fallthrough_attribute (tree *, tree name, tree, int,
   *no_add_attrs = true;
   return NULL_TREE;
 }
+
+static tree
+handle_prolog_pad_attribute (tree *, tree, tree, int,
+			     bool *)
+{
+  return NULL_TREE;
+}
diff --git a/gcc/common.opt b/gcc/common.opt
index de06844..0317a86 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -163,6 +163,13 @@  bool flag_stack_usage_info = false
 Variable
 int flag_debug_asm
 
+; If we should generate NOP pads before each function prologue
+Variable
+HOST_WIDE_INT prolog_nop_pad_size
+
+; And how far the asm entry point is into this pad
+Variable
+HOST_WIDE_INT prolog_nop_pad_entry
 
 ; Balance between GNAT encodings and standard DWARF to emit.
 Variable
@@ -2019,6 +2026,10 @@  fprofile-reorder-functions
 Common Report Var(flag_profile_reorder_functions)
 Enable function reordering that improves code placement.
 
+fprolog-pad=
+Common Joined Optimization
+Pad NOPs before each function prolog
+
 frandom-seed
 Common Var(common_deferred_options) Defer
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 1f303bc..a09851a 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -3076,6 +3076,17 @@  that affect more than one function.
 This attribute should be used for debugging purposes only.  It is not
 suitable in production code.
 
+@item prolog_pad
+@cindex @code{prolog_pad} function attribute
+@cindex function entry padded with NOPs
+In case the target's text segment can be made writable at run time
+by any means, padding the function entry with a number of NOPs can
+be used to provide a universal tool for instrumentation.  Usually,
+prolog padding is enabled globally using the -fprolog-pad= command
+line switch, and disabled by the attribute keyword for functions
+that are part of the actual instrumentation framework, to easily avoid
+an endless recursion.
+
 @item pure
 @cindex @code{pure} function attribute
 @cindex functions that have no side effects
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index b729964..21e5067 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -11309,6 +11309,21 @@  of the function name, it is considered to be a match.  For C99 and C++
 extended identifiers, the function name must be given in UTF-8, not
 using universal character names.
 
+@item -fprolog-pad=N
+@opindex fprolog-pad
+Generate a pad of N nops right at the beginning
+of each function, which can be used to patch in any desired
+instrumentation at run time, provided that the code segment
+is writeable.  For run time identification, the starting addresses
+of these pads, which correspond to their respective functions,
+are additionally collected in the @code{__prolog_pads_loc} section
+of the resulting binary.
+
+Note that value of @code{__attribute__ ((prolog_pad (N)))} takes
+precedence over command-line option -fprolog_pad=N.  This can be used
+to increase the pad size or to remove the pad completely on a single
+function.
+
 @end table
 
 
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index cdf5f48..65c265c 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -4564,6 +4564,10 @@  will select the smallest suitable mode.
 This section describes the macros that output function entry
 (@dfn{prologue}) and exit (@dfn{epilogue}) code.
 
+@deftypefn {Target Hook} void TARGET_ASM_PRINT_PROLOG_PAD (FILE *@var{file}, unsigned HOST_WIDE_INT @var{pad_size}, bool @var{record_p})
+Generate prologue pad
+@end deftypefn
+
 @deftypefn {Target Hook} void TARGET_ASM_FUNCTION_PROLOGUE (FILE *@var{file}, HOST_WIDE_INT @var{size})
 If defined, a function that outputs the assembler code for entry to a
 function.  The prologue is responsible for setting up the stack frame,
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index bbf53c9..88424ae 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -3648,6 +3648,8 @@  will select the smallest suitable mode.
 This section describes the macros that output function entry
 (@dfn{prologue}) and exit (@dfn{epilogue}) code.
 
+@hook TARGET_ASM_PRINT_PROLOG_PAD
+
 @hook TARGET_ASM_FUNCTION_PROLOGUE
 
 @hook TARGET_ASM_FUNCTION_END_PROLOGUE
diff --git a/gcc/lto/lto-lang.c b/gcc/lto/lto-lang.c
index 58f6e0c..94599ea 100644
--- a/gcc/lto/lto-lang.c
+++ b/gcc/lto/lto-lang.c
@@ -50,6 +50,7 @@  static tree handle_sentinel_attribute (tree *, tree, tree, int, bool *);
 static tree handle_type_generic_attribute (tree *, tree, tree, int, bool *);
 static tree handle_transaction_pure_attribute (tree *, tree, tree, int, bool *);
 static tree handle_returns_twice_attribute (tree *, tree, tree, int, bool *);
+static tree handle_prolog_pad_attribute (tree *, tree, tree, int, bool *);
 static tree ignore_attribute (tree *, tree, tree, int, bool *);
 
 static tree handle_format_attribute (tree *, tree, tree, int, bool *);
@@ -78,6 +79,8 @@  const struct attribute_spec lto_attribute_table[] =
 			      handle_nonnull_attribute, false },
   { "nothrow",                0, 0, true,  false, false,
 			      handle_nothrow_attribute, false },
+  { "prolog_pad",	      1, 2, true, false, false,
+			      handle_prolog_pad_attribute, false },
   { "returns_twice",          0, 0, true,  false, false,
 			      handle_returns_twice_attribute, false },
   { "sentinel",               0, 1, false, true, true,
@@ -475,6 +478,14 @@  handle_returns_twice_attribute (tree *node, tree ARG_UNUSED (name),
   return NULL_TREE;
 }
 
+static tree
+handle_prolog_pad_attribute (tree *, tree, tree, int,
+			     bool *)
+{
+  /* Nothing to be done here.  */
+  return NULL_TREE;
+}
+
 /* Ignore the given attribute.  Used when this attribute may be usefully
    overridden by the target, but is not used generically.  */
 
diff --git a/gcc/opts.c b/gcc/opts.c
index 890da03..af3a5b5 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -2080,6 +2080,26 @@  common_handle_option (struct gcc_options *opts,
         opts->x_flag_ipa_reference = false;
       break;
 
+    case OPT_fprolog_pad_:
+      {
+	const char *comma = strchr (arg, ',');
+	if (comma)
+	  {
+	    prolog_nop_pad_size = atoi (arg);
+	    prolog_nop_pad_entry = atoi (comma + 1);
+	  }
+	else
+	  {
+	    prolog_nop_pad_size = atoi (arg);
+	    prolog_nop_pad_entry = 0;
+	  }
+	if (prolog_nop_pad_size < 0
+	    || prolog_nop_pad_entry < 0
+	    || prolog_nop_pad_size < prolog_nop_pad_entry)
+	  error ("invalid arguments for %<-fprolog_pad%>");
+      }
+      break;
+
     case OPT_ftree_vectorize:
       if (!opts_set->x_flag_tree_loop_vectorize)
         opts->x_flag_tree_loop_vectorize = value;
diff --git a/gcc/target.def b/gcc/target.def
index ac3470e..487b900 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -288,6 +288,12 @@  hidden, protected or internal visibility as specified by @var{visibility}.",
  void, (tree decl, int visibility),
  default_assemble_visibility)
 
+DEFHOOK
+(print_prolog_pad,
+ "Generate prologue pad",
+ void, (FILE *file, unsigned HOST_WIDE_INT pad_size, bool record_p),
+ default_print_prolog_pad)
+
 /* Output the assembler code for entry to a function.  */
 DEFHOOK
 (function_prologue,
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 5d3e91e..b6de992 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -1617,6 +1617,31 @@  default_compare_by_pieces_branch_ratio (machine_mode)
   return 1;
 }
 
+/* Write pad_size NOPs into the asm outfile before a function
+   prologue.  If record_p is true, the location of the pad will be
+   recorded in a special object section called "__prolog_pads_loc".
+   This routine may be called twice per function to put NOPs before
+   and after the function entry.  */
+
+void
+default_print_prolog_pad (FILE *file, unsigned HOST_WIDE_INT pad_size,
+			  bool record_p)
+{
+  if (record_p)
+    fprintf (file, "1:");
+
+  unsigned i;
+  for (i = 0; i < pad_size; ++i)
+    fprintf (file, "\tnop\n");
+
+  if (record_p)
+    {
+      fprintf (file, "\t.section __prolog_pads_loc, \"a\",@progbits\n");
+      fprintf (file, "\t.quad 1b\n");
+      fprintf (file, "\t.previous\n");
+    }
+}
+
 bool
 default_profile_before_prologue (void)
 {
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index 3a9271f..13c33de 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -203,6 +203,7 @@  extern bool default_use_by_pieces_infrastructure_p (unsigned HOST_WIDE_INT,
 						    bool);
 extern int default_compare_by_pieces_branch_ratio (machine_mode);
 
+extern void default_print_prolog_pad (FILE *, unsigned HOST_WIDE_INT , bool);
 extern bool default_profile_before_prologue (void);
 extern reg_class_t default_preferred_reload_class (rtx, reg_class_t);
 extern reg_class_t default_preferred_output_reload_class (rtx, reg_class_t);
diff --git a/gcc/testsuite/c-c++-common/attribute-prolog_pad-1.c b/gcc/testsuite/c-c++-common/attribute-prolog_pad-1.c
new file mode 100644
index 0000000..179b492
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/attribute-prolog_pad-1.c
@@ -0,0 +1,34 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fprolog-pad=3,1" } */
+
+void f1 (void) __attribute__((prolog_pad(2,1)));
+void f2 (void) __attribute__((prolog_pad(3)));
+int f3 (void);
+
+void
+f1 (void)
+{
+  f2 ();
+}
+
+void
+f2 (void)
+{
+  f1 ();
+}
+
+/* F3 should never have a nop pad.  */
+int
+__attribute__((prolog_pad(0)))
+__attribute__((noinline))
+f3 (void)
+{
+  return 5;
+}
+
+/* F4 should receive the command line default setting.  */
+int
+f4 (void)
+{
+  return 3*f3 ()+1;
+}
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 79d7a6f..ab4578e 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1595,8 +1595,10 @@  process_options (void)
     }
 
  /* Do not use IPA optimizations for register allocation if profiler is active
+    or prolog pads are inserted for run-time instrumentation
     or port does not emit prologue and epilogue as RTL.  */
-  if (profile_flag || !targetm.have_prologue () || !targetm.have_epilogue ())
+  if (profile_flag || prolog_nop_pad_size
+      || !targetm.have_prologue () || !targetm.have_epilogue ())
     flag_ipa_ra = 0;
 
   /* Enable -Werror=coverage-mismatch when -Werror and -Wno-error
diff --git a/gcc/varasm.c b/gcc/varasm.c
index 5b15847..c1308f2 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -1830,6 +1830,44 @@  assemble_start_function (tree decl, const char *fnname)
   if (DECL_PRESERVE_P (decl))
     targetm.asm_out.mark_decl_preserved (fnname);
 
+  unsigned HOST_WIDE_INT pad_size = prolog_nop_pad_size;
+  unsigned HOST_WIDE_INT pad_entry = prolog_nop_pad_entry;
+
+  tree prolog_pad_attr
+    = lookup_attribute ("prolog_pad", DECL_ATTRIBUTES (decl));
+  if (prolog_pad_attr)
+    {
+      tree pp_val = TREE_VALUE (prolog_pad_attr);
+      tree prolog_pad_value1 = TREE_VALUE (pp_val);
+
+      if (tree_fits_uhwi_p (prolog_pad_value1))
+	pad_size = tree_to_uhwi (prolog_pad_value1);
+      else
+	gcc_unreachable ();
+
+      pad_entry = 0;
+      if (list_length (pp_val) > 1)
+	{
+	  tree prolog_pad_value2 = TREE_VALUE (TREE_CHAIN (pp_val));
+
+	  if (tree_fits_uhwi_p (prolog_pad_value2))
+	    pad_entry = tree_to_uhwi (prolog_pad_value2);
+	  else
+	    gcc_unreachable ();
+	}
+    }
+
+  if (pad_entry > pad_size)
+    {
+      if (pad_size > 0)
+	warning (OPT_Wattributes, "Prolog nop pad entry > size");
+      pad_entry = 0;
+    }
+
+  /* Emit the prolog padding before the entry label, if any.  */
+  if (pad_entry > 0)
+    targetm.asm_out.print_prolog_pad (asm_out_file, pad_entry, true);
+
   /* Do any machine/system dependent processing of the function name.  */
 #ifdef ASM_DECLARE_FUNCTION_NAME
   ASM_DECLARE_FUNCTION_NAME (asm_out_file, fnname, current_function_decl);
@@ -1838,6 +1876,11 @@  assemble_start_function (tree decl, const char *fnname)
   ASM_OUTPUT_FUNCTION_LABEL (asm_out_file, fnname, current_function_decl);
 #endif /* ASM_DECLARE_FUNCTION_NAME */
 
+  /* And the padding after the label.  Record it if we haven't done so yet.  */
+  if (pad_size > pad_entry)
+    targetm.asm_out.print_prolog_pad (asm_out_file, pad_size-pad_entry,
+				      (pad_entry == 0));
+
   if (lookup_attribute ("no_split_stack", DECL_ATTRIBUTES (decl)))
     saw_no_split_stack = true;
 }