aboutsummaryrefslogtreecommitdiff
path: root/gcc/c-family/c-attribs.cc
diff options
context:
space:
mode:
authorAlexandre Oliva <oliva@adacore.com>2023-12-05 21:07:36 -0300
committerAlexandre Oliva <oliva@gnu.org>2023-12-05 21:07:36 -0300
commitf0a90c7d7333fc7f554b906245c84bdf04d716d7 (patch)
tree4396a55e7bf1d88895102499dbea7fdd7d3ac6d6 /gcc/c-family/c-attribs.cc
parent08448dc146b6dd32383d64ab491a594d41f62aaa (diff)
downloadgcc-f0a90c7d7333fc7f554b906245c84bdf04d716d7.zip
gcc-f0a90c7d7333fc7f554b906245c84bdf04d716d7.tar.gz
gcc-f0a90c7d7333fc7f554b906245c84bdf04d716d7.tar.bz2
Introduce strub: machine-independent stack scrubbing
This patch adds the strub attribute for function and variable types, command-line options, passes and adjustments to implement it, documentation, and tests. Stack scrubbing is implemented in a machine-independent way: functions with strub enabled are modified so that they take an extra stack watermark argument, that they update with their stack use, and the caller can then zero it out once it regains control, whether by return or exception. There are two ways to go about it: at-calls, that modifies the visible interface (signature) of the function, and internal, in which the body is moved to a clone, the clone undergoes the interface change, and the function becomes a wrapper, preserving its original interface, that calls the clone and then clears the stack used by it. Variables can also be annotated with the strub attribute, so that functions that read from them get stack scrubbing enabled implicitly, whether at-calls, for functions only usable within a translation unit, or internal, for functions whose interfaces must not be modified. There is a strict mode, in which functions that have their stack scrubbed can only call other functions with stack-scrubbing interfaces, or those explicitly marked as callable from strub contexts, so that an entire call chain gets scrubbing, at once or piecemeal depending on optimization levels. In the default mode, relaxed, this requirement is not enforced by the compiler. The implementation adds two IPA passes, one that assigns strub modes early on, another that modifies interfaces and adds calls to the builtins that jointly implement stack scrubbing. Another builtin, that obtains the stack pointer, is added for use in the implementation of the builtins, whether expanded inline or called in libgcc. There are new command-line options to change operation modes and to force the feature disabled; it is enabled by default, but it has no effect and is implicitly disabled if the strub attribute is never used. There are also options meant to use for testing the feature, enabling different strubbing modes for all (viable) functions. for gcc/ChangeLog * Makefile.in (OBJS): Add ipa-strub.o. (GTFILES): Add ipa-strub.cc. * builtins.def (BUILT_IN_STACK_ADDRESS): New. (BUILT_IN___STRUB_ENTER): New. (BUILT_IN___STRUB_UPDATE): New. (BUILT_IN___STRUB_LEAVE): New. * builtins.cc: Include ipa-strub.h. (STACK_STOPS, STACK_UNSIGNED): Define. (expand_builtin_stack_address): New. (expand_builtin_strub_enter): New. (expand_builtin_strub_update): New. (expand_builtin_strub_leave): New. (expand_builtin): Call them. * common.opt (fstrub=*): New options. * doc/extend.texi (strub): New type attribute. (__builtin_stack_address): New function. (Stack Scrubbing): New section. * doc/invoke.texi (-fstrub=*): New options. (-fdump-ipa-*): New passes. * gengtype-lex.l: Ignore multi-line pp-directives. * ipa-inline.cc: Include ipa-strub.h. (can_inline_edge_p): Test strub_inlinable_to_p. * ipa-split.cc: Include ipa-strub.h. (execute_split_functions): Test strub_splittable_p. * ipa-strub.cc, ipa-strub.h: New. * passes.def: Add strub_mode and strub passes. * tree-cfg.cc (gimple_verify_flow_info): Note on debug stmts. * tree-pass.h (make_pass_ipa_strub_mode): Declare. (make_pass_ipa_strub): Declare. (make_pass_ipa_function_and_variable_visibility): Fix formatting. * tree-ssa-ccp.cc (optimize_stack_restore): Keep restores before strub leave. * attribs.cc: Include ipa-strub.h. (decl_attributes): Support applying attributes to function type, rather than pointer type, at handler's request. (comp_type_attributes): Combine strub_comptypes and target comp_type results. * doc/tm.texi.in (TARGET_STRUB_USE_DYNAMIC_ARRAY): New. (TARGET_STRUB_MAY_USE_MEMSET): New. * doc/tm.texi: Rebuilt. * cgraph.h (symtab_node::reset): Add preserve_comdat_group param, with a default. * cgraphunit.cc (symtab_node::reset): Use it. for gcc/c-family/ChangeLog * c-attribs.cc: Include ipa-strub.h. (handle_strub_attribute): New. (c_common_attribute_table): Add strub. for gcc/ada/ChangeLog * gcc-interface/trans.cc: Include ipa-strub.h. (gigi): Make internal decls for targets of compiler-generated calls strub-callable too. (build_raise_check): Likewise. * gcc-interface/utils.cc: Include ipa-strub.h. (handle_strub_attribute): New. (gnat_internal_attribute_table): Add strub. for gcc/testsuite/ChangeLog * c-c++-common/strub-O0.c: New. * c-c++-common/strub-O1.c: New. * c-c++-common/strub-O2.c: New. * c-c++-common/strub-O2fni.c: New. * c-c++-common/strub-O3.c: New. * c-c++-common/strub-O3fni.c: New. * c-c++-common/strub-Og.c: New. * c-c++-common/strub-Os.c: New. * c-c++-common/strub-all1.c: New. * c-c++-common/strub-all2.c: New. * c-c++-common/strub-apply1.c: New. * c-c++-common/strub-apply2.c: New. * c-c++-common/strub-apply3.c: New. * c-c++-common/strub-apply4.c: New. * c-c++-common/strub-at-calls1.c: New. * c-c++-common/strub-at-calls2.c: New. * c-c++-common/strub-defer-O1.c: New. * c-c++-common/strub-defer-O2.c: New. * c-c++-common/strub-defer-O3.c: New. * c-c++-common/strub-defer-Os.c: New. * c-c++-common/strub-internal1.c: New. * c-c++-common/strub-internal2.c: New. * c-c++-common/strub-parms1.c: New. * c-c++-common/strub-parms2.c: New. * c-c++-common/strub-parms3.c: New. * c-c++-common/strub-relaxed1.c: New. * c-c++-common/strub-relaxed2.c: New. * c-c++-common/strub-short-O0-exc.c: New. * c-c++-common/strub-short-O0.c: New. * c-c++-common/strub-short-O1.c: New. * c-c++-common/strub-short-O2.c: New. * c-c++-common/strub-short-O3.c: New. * c-c++-common/strub-short-Os.c: New. * c-c++-common/strub-strict1.c: New. * c-c++-common/strub-strict2.c: New. * c-c++-common/strub-tail-O1.c: New. * c-c++-common/strub-tail-O2.c: New. * c-c++-common/torture/strub-callable1.c: New. * c-c++-common/torture/strub-callable2.c: New. * c-c++-common/torture/strub-const1.c: New. * c-c++-common/torture/strub-const2.c: New. * c-c++-common/torture/strub-const3.c: New. * c-c++-common/torture/strub-const4.c: New. * c-c++-common/torture/strub-data1.c: New. * c-c++-common/torture/strub-data2.c: New. * c-c++-common/torture/strub-data3.c: New. * c-c++-common/torture/strub-data4.c: New. * c-c++-common/torture/strub-data5.c: New. * c-c++-common/torture/strub-indcall1.c: New. * c-c++-common/torture/strub-indcall2.c: New. * c-c++-common/torture/strub-indcall3.c: New. * c-c++-common/torture/strub-inlinable1.c: New. * c-c++-common/torture/strub-inlinable2.c: New. * c-c++-common/torture/strub-ptrfn1.c: New. * c-c++-common/torture/strub-ptrfn2.c: New. * c-c++-common/torture/strub-ptrfn3.c: New. * c-c++-common/torture/strub-ptrfn4.c: New. * c-c++-common/torture/strub-pure1.c: New. * c-c++-common/torture/strub-pure2.c: New. * c-c++-common/torture/strub-pure3.c: New. * c-c++-common/torture/strub-pure4.c: New. * c-c++-common/torture/strub-run1.c: New. * c-c++-common/torture/strub-run2.c: New. * c-c++-common/torture/strub-run3.c: New. * c-c++-common/torture/strub-run4.c: New. * c-c++-common/torture/strub-run4c.c: New. * c-c++-common/torture/strub-run4d.c: New. * c-c++-common/torture/strub-run4i.c: New. * g++.dg/strub-run1.C: New. * g++.dg/torture/strub-init1.C: New. * g++.dg/torture/strub-init2.C: New. * g++.dg/torture/strub-init3.C: New. * gnat.dg/strub_attr.adb, gnat.dg/strub_attr.ads: New. * gnat.dg/strub_ind.adb, gnat.dg/strub_ind.ads: New. for libgcc/ChangeLog * Makefile.in (LIB2ADD): Add strub.c. * libgcc2.h (__strub_enter, __strub_update, __strub_leave): Declare. * strub.c: New. * libgcc-std.ver.in (__strub_enter): Add to GCC_14.0.0. (__strub_update, __strub_leave): Likewise.
Diffstat (limited to 'gcc/c-family/c-attribs.cc')
-rw-r--r--gcc/c-family/c-attribs.cc82
1 files changed, 82 insertions, 0 deletions
diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index 859dcf2..854e987 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -41,6 +41,7 @@ along with GCC; see the file COPYING3. If not see
#include "common/common-target.h"
#include "langhooks.h"
#include "tree-inline.h"
+#include "ipa-strub.h"
#include "toplev.h"
#include "tree-iterator.h"
#include "opts.h"
@@ -69,6 +70,7 @@ static tree handle_asan_odr_indicator_attribute (tree *, tree, tree, int,
static tree handle_stack_protect_attribute (tree *, tree, tree, int, bool *);
static tree handle_no_stack_protector_function_attribute (tree *, tree,
tree, int, bool *);
+static tree handle_strub_attribute (tree *, tree, tree, int, bool *);
static tree handle_noinline_attribute (tree *, tree, tree, int, bool *);
static tree handle_noclone_attribute (tree *, tree, tree, int, bool *);
static tree handle_nocf_check_attribute (tree *, tree, tree, int, bool *);
@@ -322,6 +324,8 @@ const struct attribute_spec c_common_gnu_attributes[] =
{ "no_stack_protector", 0, 0, true, false, false, false,
handle_no_stack_protector_function_attribute,
attr_stack_protect_exclusions },
+ { "strub", 0, 1, false, true, false, true,
+ handle_strub_attribute, NULL },
{ "noinline", 0, 0, true, false, false, false,
handle_noinline_attribute,
attr_noinline_exclusions },
@@ -1489,6 +1493,84 @@ handle_noipa_attribute (tree *node, tree name, tree, int, bool *no_add_attrs)
return NULL_TREE;
}
+/* Handle a "strub" attribute; arguments as in
+ struct attribute_spec.handler. */
+
+static tree
+handle_strub_attribute (tree *node, tree name,
+ tree args,
+ int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+ bool enable = true;
+
+ if (args && FUNCTION_POINTER_TYPE_P (*node))
+ *node = TREE_TYPE (*node);
+
+ if (args && FUNC_OR_METHOD_TYPE_P (*node))
+ {
+ switch (strub_validate_fn_attr_parm (TREE_VALUE (args)))
+ {
+ case 1:
+ case 2:
+ enable = true;
+ break;
+
+ case 0:
+ warning (OPT_Wattributes,
+ "%qE attribute ignored because of argument %qE",
+ name, TREE_VALUE (args));
+ *no_add_attrs = true;
+ enable = false;
+ break;
+
+ case -1:
+ case -2:
+ enable = false;
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ args = TREE_CHAIN (args);
+ }
+
+ if (args)
+ {
+ warning (OPT_Wattributes,
+ "ignoring attribute %qE because of excess arguments"
+ " starting at %qE",
+ name, TREE_VALUE (args));
+ *no_add_attrs = true;
+ enable = false;
+ }
+
+ /* Warn about unmet expectations that the strub attribute works like a
+ qualifier. ??? Could/should we extend it to the element/field types
+ here? */
+ if (TREE_CODE (*node) == ARRAY_TYPE
+ || VECTOR_TYPE_P (*node)
+ || TREE_CODE (*node) == COMPLEX_TYPE)
+ warning (OPT_Wattributes,
+ "attribute %qE does not apply to elements"
+ " of non-scalar type %qT",
+ name, *node);
+ else if (RECORD_OR_UNION_TYPE_P (*node))
+ warning (OPT_Wattributes,
+ "attribute %qE does not apply to fields"
+ " of aggregate type %qT",
+ name, *node);
+
+ /* If we see a strub-enabling attribute, and we're at the default setting,
+ implicitly or explicitly, note that the attribute was seen, so that we can
+ reduce the compile-time overhead to nearly zero when the strub feature is
+ not used. */
+ if (enable && flag_strub < -2)
+ flag_strub += 2;
+
+ return NULL_TREE;
+}
+
/* Handle a "noinline" attribute; arguments as in
struct attribute_spec.handler. */