new file mode 100644
@@ -0,0 +1,3489 @@
+/* strub (stack scrubbing) support.
+ Copyright (C) 2021-2022 Free Software Foundation, Inc.
+ Contributed by Alexandre Oliva <oliva@adacore.com>.
+
+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/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+#include "gimplify.h"
+#include "tree-pass.h"
+#include "ssa.h"
+#include "gimple-iterator.h"
+#include "gimplify-me.h"
+#include "tree-into-ssa.h"
+#include "tree-ssa.h"
+#include "tree-cfg.h"
+#include "cfghooks.h"
+#include "cfgloop.h"
+#include "cfgcleanup.h"
+#include "tree-eh.h"
+#include "except.h"
+#include "builtins.h"
+#include "attribs.h"
+#include "tree-inline.h"
+#include "cgraph.h"
+#include "alloc-pool.h"
+#include "symbol-summary.h"
+#include "ipa-prop.h"
+#include "ipa-fnsummary.h"
+#include "gimple-fold.h"
+#include "fold-const.h"
+#include "gimple-walk.h"
+#include "tree-dfa.h"
+#include "langhooks.h"
+#include "calls.h"
+#include "vec.h"
+#include "stor-layout.h"
+#include "varasm.h"
+#include "alias.h"
+#include "diagnostic.h"
+#include "intl.h"
+#include "ipa-strub.h"
+
+#if BUILDING_GCC_MAJOR >= 11
+# include "symtab-thunks.h"
+# include "attr-fnspec.h"
+# define HAVE_ATTR_FNSPEC 1
+# define FOR_GCC_11P 1
+#else
+# define HAVE_ATTR_FNSPEC 0
+# define FOR_GCC_11P 0
+#endif
+
+/* Const and pure functions that gain a watermark parameter for strub purposes
+ are still regarded as such, which may cause the inline expansions of the
+ __strub builtins to malfunction. Ideally, attribute "fn spec" would enable
+ us to inform the backend about requirements and side effects of the call, but
+ call_fusage building in calls.c:expand_call does not even look at
+ attr_fnspec, so we resort to asm loads and updates to attain an equivalent
+ effect. Once expand_call gains the ability to issue extra memory uses and
+ clobbers based on pure/const function's fnspec, we can define this to 1. */
+#define ATTR_FNSPEC_DECONST_WATERMARK 0
+
+enum strub_mode {
+ /* This mode denotes a regular function, that does not require stack
+ scrubbing (strubbing). It may call any other functions, but if
+ it calls AT_CALLS (or WRAPPED) ones, strubbing logic is
+ automatically introduced around those calls (the latter, by
+ inlining INTERNAL wrappers). */
+ STRUB_DISABLED = 0,
+
+ /* This denotes a function whose signature is (to be) modified to
+ take an extra parameter, for stack use annotation, and its
+ callers must initialize and pass that argument, and perform the
+ strubbing. Functions that are explicitly marked with attribute
+ strub must have the mark visible wherever the function is,
+ including aliases, and overriders and overriding methods.
+ Functions that are implicitly marked for strubbing, for accessing
+ variables explicitly marked as such, will only select this
+ strubbing method if they are internal to a translation unit. It
+ can only be inlined into other strubbing functions, i.e.,
+ STRUB_AT_CALLS or STRUB_WRAPPED. */
+ STRUB_AT_CALLS = 1,
+
+ /* This denotes a function that is to perform strubbing internally,
+ without any changes to its interface (the function is turned into
+ a strubbing wrapper, and its original body is moved to a separate
+ STRUB_WRAPPED function, with a modified interface). Functions
+ may be explicitly marked with attribute strub(2), and the
+ attribute must be visible at the point of definition. Functions
+ that are explicitly marked for strubbing, for accessing variables
+ explicitly marked as such, may select this strubbing mode if
+ their interface cannot change, e.g. because its interface is
+ visible to other translation units, directly, by indirection
+ (having its address taken), inheritance, etc. Functions that use
+ this method must not have the noclone attribute, nor the noipa
+ one. Functions marked as always_inline may select this mode, but
+ they are NOT wrapped, they remain unchanged, and are only inlined
+ into strubbed contexts. Once non-always_inline functions are
+ wrapped, the wrapper becomes STRUB_WRAPPER, and the wrapped becomes
+ STRUB_WRAPPED. */
+ STRUB_INTERNAL = 2,
+
+ /* This denotes a function whose stack is not strubbed, but that is
+ nevertheless explicitly or implicitly marked as callable from strubbing
+ functions. Normally, only STRUB_AT_CALLS (and STRUB_INTERNAL ->
+ STRUB_WRAPPED) functions can be called from strubbing contexts (bodies of
+ STRUB_AT_CALLS, STRUB_INTERNAL and STRUB_WRAPPED functions), but attribute
+ strub(3) enables other functions to be (indirectly) called from these
+ contexts. Some builtins and internal functions may be implicitly marked as
+ STRUB_CALLABLE. */
+ STRUB_CALLABLE = 3,
+
+ /* This denotes the function that took over the body of a
+ STRUB_INTERNAL function. At first, it's only called by its
+ wrapper, but the wrapper may be inlined. The wrapped function,
+ in turn, can only be inlined into other functions whose stack
+ frames are strubbed, i.e., that are STRUB_WRAPPED or
+ STRUB_AT_CALLS. */
+ STRUB_WRAPPED = -1,
+
+ /* This denotes the wrapper function that replaced the STRUB_INTERNAL
+ function. This mode overrides the STRUB_INTERNAL mode at the time the
+ internal to-be-wrapped function becomes a wrapper, so that inlining logic
+ can tell one from the other. */
+ STRUB_WRAPPER = -2,
+
+ /* This denotes an always_inline function that requires strubbing. It can
+ only be called from, and inlined into, other strubbing contexts. */
+ STRUB_INLINABLE = -3,
+
+ /* This denotes a function that accesses strub variables, so it would call for
+ internal strubbing (whether or not it's eligible for that), but since
+ at-calls strubbing is viable, that's selected as an optimization. This
+ mode addresses the inconvenience that such functions may have different
+ modes selected depending on optimization flags, and get a different
+ callable status depending on that choice: if we assigned them
+ STRUB_AT_CALLS mode, they would be callable when optimizing, whereas
+ STRUB_INTERNAL would not be callable. */
+ STRUB_AT_CALLS_OPT = -4,
+
+};
+
+/* Look up a strub attribute in TYPE, and return it. */
+
+static tree
+get_strub_attr_from_type (tree type)
+{
+ return lookup_attribute ("strub", TYPE_ATTRIBUTES (type));
+}
+
+/* Look up a strub attribute in DECL or in its type, and return it. */
+
+static tree
+get_strub_attr_from_decl (tree decl)
+{
+ tree ret = lookup_attribute ("strub", DECL_ATTRIBUTES (decl));
+ if (ret)
+ return ret;
+ return get_strub_attr_from_type (TREE_TYPE (decl));
+}
+
+/* Define a function to cache identifier ID, to be used as a strub attribute
+ parameter for a strub mode named after NAME. */
+#define DEF_STRUB_IDS(NAME, ID) \
+static inline tree get_strub_mode_id_ ## NAME () { \
+ static tree identifier = NULL_TREE; \
+ if (!identifier) \
+ identifier = get_identifier (ID); \
+ return identifier; \
+}
+/* Same as DEF_STRUB_IDS, but use the string expansion of NAME as ID. */
+#define DEF_STRUB_ID(NAME) \
+DEF_STRUB_IDS (NAME, #NAME)
+
+/* Define functions for each of the strub mode identifiers.
+ Expose dashes rather than underscores. */
+DEF_STRUB_ID (disabled)
+DEF_STRUB_IDS (at_calls, "at-calls")
+DEF_STRUB_ID (internal)
+DEF_STRUB_ID (callable)
+DEF_STRUB_ID (wrapped)
+DEF_STRUB_ID (wrapper)
+DEF_STRUB_ID (inlinable)
+DEF_STRUB_IDS (at_calls_opt, "at-calls-opt")
+
+/* Release the temporary macro names. */
+#undef DEF_STRUB_IDS
+#undef DEF_STRUB_ID
+
+/* Return the identifier corresponding to strub MODE. */
+
+static tree
+get_strub_mode_attr_parm (enum strub_mode mode)
+{
+ switch (mode)
+ {
+ case STRUB_DISABLED:
+ return get_strub_mode_id_disabled ();
+
+ case STRUB_AT_CALLS:
+ return get_strub_mode_id_at_calls ();
+
+ case STRUB_INTERNAL:
+ return get_strub_mode_id_internal ();
+
+ case STRUB_CALLABLE:
+ return get_strub_mode_id_callable ();
+
+ case STRUB_WRAPPED:
+ return get_strub_mode_id_wrapped ();
+
+ case STRUB_WRAPPER:
+ return get_strub_mode_id_wrapper ();
+
+ case STRUB_INLINABLE:
+ return get_strub_mode_id_inlinable ();
+
+ case STRUB_AT_CALLS_OPT:
+ return get_strub_mode_id_at_calls_opt ();
+
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* Return the parmeters (TREE_VALUE) for a strub attribute of MODE.
+ We know we use a single parameter, so we bypass the creation of a
+ tree list. */
+
+static tree
+get_strub_mode_attr_value (enum strub_mode mode)
+{
+ return get_strub_mode_attr_parm (mode);
+}
+
+/* Determine whether ID is a well-formed strub mode-specifying attribute
+ parameter for a function (type). Only user-visible modes are accepted, and
+ ID must be non-NULL.
+
+ For unacceptable parms, return 0, otherwise a nonzero value as below.
+
+ If the parm enables strub, return positive, otherwise negative.
+
+ If the affected type must be a distinct, incompatible type,return an integer
+ of absolute value 2, otherwise 1. */
+
+int
+strub_validate_fn_attr_parm (tree id)
+{
+ int ret;
+ const char *s = NULL;
+ size_t len = 0;
+
+ /* do NOT test for NULL. This is only to be called with non-NULL arguments.
+ We assume that the strub parameter applies to a function, because only
+ functions accept an explicit argument. If we accepted NULL, and we
+ happened to be called to verify the argument for a variable, our return
+ values would be wrong. */
+ if (TREE_CODE (id) == STRING_CST)
+ {
+ s = TREE_STRING_POINTER (id);
+ len = TREE_STRING_LENGTH (id) - 1;
+ }
+ else if (TREE_CODE (id) == IDENTIFIER_NODE)
+ {
+ s = IDENTIFIER_POINTER (id);
+ len = IDENTIFIER_LENGTH (id);
+ }
+ else
+ return 0;
+
+ enum strub_mode mode;
+
+ if (len != 8)
+ return 0;
+
+ switch (s[0])
+ {
+ case 'd':
+ mode = STRUB_DISABLED;
+ ret = -1;
+ break;
+
+ case 'a':
+ mode = STRUB_AT_CALLS;
+ ret = 2;
+ break;
+
+ case 'i':
+ mode = STRUB_INTERNAL;
+ ret = 1;
+ break;
+
+ case 'c':
+ mode = STRUB_CALLABLE;
+ ret = -2;
+ break;
+
+ default:
+ /* Other parms are for internal use only. */
+ return 0;
+ }
+
+ tree mode_id = get_strub_mode_attr_parm (mode);
+
+ if (TREE_CODE (id) == IDENTIFIER_NODE
+ ? id != mode_id
+ : strncmp (s, IDENTIFIER_POINTER (mode_id), len) != 0)
+ return 0;
+
+ return ret;
+}
+
+/* Return the strub mode from STRUB_ATTR. VAR_P should be TRUE if the attribute
+ is taken from a variable, rather than from a function, or a type thereof. */
+
+static enum strub_mode
+get_strub_mode_from_attr (tree strub_attr, bool var_p = false)
+{
+ enum strub_mode mode = STRUB_DISABLED;
+
+ if (strub_attr)
+ {
+ if (!TREE_VALUE (strub_attr))
+ mode = !var_p ? STRUB_AT_CALLS : STRUB_INTERNAL;
+ else
+ {
+ gcc_checking_assert (!var_p);
+ tree id = TREE_VALUE (strub_attr);
+ if (TREE_CODE (id) == TREE_LIST)
+ id = TREE_VALUE (id);
+ const char *s = (TREE_CODE (id) == STRING_CST
+ ? TREE_STRING_POINTER (id)
+ : IDENTIFIER_POINTER (id));
+ size_t len = (TREE_CODE (id) == STRING_CST
+ ? TREE_STRING_LENGTH (id) - 1
+ : IDENTIFIER_LENGTH (id));
+
+ switch (len)
+ {
+ case 7:
+ switch (s[6])
+ {
+ case 'r':
+ mode = STRUB_WRAPPER;
+ break;
+
+ case 'd':
+ mode = STRUB_WRAPPED;
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ break;
+
+ case 8:
+ switch (s[0])
+ {
+ case 'd':
+ mode = STRUB_DISABLED;
+ break;
+
+ case 'a':
+ mode = STRUB_AT_CALLS;
+ break;
+
+ case 'i':
+ mode = STRUB_INTERNAL;
+ break;
+
+ case 'c':
+ mode = STRUB_CALLABLE;
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ break;
+
+ case 9:
+ mode = STRUB_INLINABLE;
+ break;
+
+ case 12:
+ mode = STRUB_AT_CALLS_OPT;
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ gcc_checking_assert (TREE_CODE (id) == IDENTIFIER_NODE
+ ? id == get_strub_mode_attr_parm (mode)
+ : strncmp (IDENTIFIER_POINTER
+ (get_strub_mode_attr_parm (mode)),
+ s, len) == 0);
+ }
+ }
+
+ return mode;
+}
+
+/* Look up, decode and return the strub mode associated with FNDECL. */
+
+static enum strub_mode
+get_strub_mode_from_fndecl (tree fndecl)
+{
+ return get_strub_mode_from_attr (get_strub_attr_from_decl (fndecl));
+}
+
+/* Look up, decode and return the strub mode associated with NODE. */
+
+static enum strub_mode
+get_strub_mode (cgraph_node *node)
+{
+ return get_strub_mode_from_fndecl (node->decl);
+}
+
+/* Look up, decode and return the strub mode associated with TYPE. */
+
+static enum strub_mode
+get_strub_mode_from_type (tree type)
+{
+ bool var_p = !FUNC_OR_METHOD_TYPE_P (type);
+ tree attr = get_strub_attr_from_type (type);
+
+ if (attr)
+ return get_strub_mode_from_attr (attr, var_p);
+
+ if (flag_strub >= -1 && !var_p)
+ return STRUB_CALLABLE;
+
+ return STRUB_DISABLED;
+}
+
+
--
Alexandre Oliva, happy hacker https://FSFLA.org/blogs/lxo/
Free Software Activist GNU Toolchain Engineer
Disinformation flourishes because many people care deeply about injustice
but very few check the facts. Ask me about <https://stallmansupport.org>