On Mon, Sep 22, 2025 at 5:21 PM Florian Weimer <fweimer@redhat.com> wrote:
>
> * H. J. Lu:
>
> > @@ -8106,6 +8109,34 @@ grokdeclarator (const struct c_declarator *declarator,
> > else
> > {
> > /* It's a variable. */
> > +
> > + const char *stack_protect_guard_name
> > + = targetm.stack_protect_guard_symbol;
> > + if (stack_protect_guard_name)
> > + {
> > + /* If the variable name is the same as the stack protect
> > + guard, use the stack protect guard decl. */
> > + tree guard_decl = targetm.stack_protect_guard ();
> > + if (guard_decl
> > + && VAR_P (guard_decl)
> > + && strcmp (stack_protect_guard_name,
> > + IDENTIFIER_POINTER (declarator->u.id.id)) == 0)
> > + {
> > + /* Allow different types with the same size. */
> > + tree guard_type = TREE_TYPE (guard_decl);
> > + if (TYPE_PRECISION (type)
> > + != TYPE_PRECISION (guard_type))
> > + {
> > + error ("conflicting types for %s; have %qT, "
> > + "should be %qT",
> > + stack_protect_guard_name, type,
> > + guard_type);
> > + return error_mark_node;
> > + }
> > + return guard_decl;
> > + }
> > + }
>
> It seems to me this goes in the wrong direction: guard_decl doesn't have
> proper location information, so any subsequent diagnostics against it
> will look wrong. I would expect this to be more like the re-declaration
It looks normal to me:
[hjl@gnu-zen4-1 pr121911]$ cat bad.c
extern const int __stack_chk_guard;
[hjl@gnu-zen4-1 pr121911]$ make bad.s
/export/build/gnu/tools-build/gcc-gitlab-debug/build-x86_64-linux/gcc/xgcc
-B/export/build/gnu/tools-build/gcc-gitlab-debug/build-x86_64-linux/gcc/
-O0 -g -fPIC -fstack-protector-all -mstack-protector-guard=global -S
bad.c
bad.c:1:1: error: conflicting types for __stack_chk_guard; have ‘int’,
should be ‘long unsigned int’
1 | extern const int __stack_chk_guard;
| ^~~~~~
make: *** [Makefile:43: bad.s] Error 1
[hjl@gnu-zen4-1 pr121911]$
> of builtins.
>
> But I'm not a GCC expert.
>
> Thanks,
> Florian
>
@@ -3652,6 +3652,11 @@ handle_visibility_attribute (tree *node, tree name, tree args,
DECL_VISIBILITY (decl) = vis;
DECL_VISIBILITY_SPECIFIED (decl) = 1;
+ /* Mark the symbol of DECL for non-default visibility as local if it
+ is the stack protect guard decl. */
+ if (vis != VISIBILITY_DEFAULT)
+ mark_stack_protect_guard_decl_local (decl);
+
/* Go ahead and attach the attribute to the node as well. This is needed
so we can determine whether we have VISIBILITY_DEFAULT because the
visibility was not specified, or because it was explicitly overridden
@@ -1191,6 +1191,13 @@ c_cpp_builtins (cpp_reader *pfile)
cpp_define (pfile, "__cpp_exceptions=199711L");
}
+ if (targetm.stack_protect_guard_symbol != nullptr)
+ {
+ /* Define this to indicate that the stack protection guard is
+ treated as an internal symbol. */
+ cpp_define (pfile, "__stack_protection_guard_is_internal_symbol__");
+ }
+
/* Represents the C++ ABI version, always defined so it can be used while
preprocessing C and assembler. */
if (flag_abi_version == 0)
@@ -3199,7 +3199,10 @@ duplicate_decls (tree newdecl, tree olddecl)
if (snode)
snode->remove ();
}
- ggc_free (newdecl);
+ /* Don't free the stack protect guard decl. */
+ if (targetm.stack_protect_guard_symbol == nullptr
+ || newdecl != targetm.stack_protect_guard ())
+ ggc_free (newdecl);
return true;
}
@@ -8106,6 +8109,34 @@ grokdeclarator (const struct c_declarator *declarator,
else
{
/* It's a variable. */
+
+ const char *stack_protect_guard_name
+ = targetm.stack_protect_guard_symbol;
+ if (stack_protect_guard_name)
+ {
+ /* If the variable name is the same as the stack protect
+ guard, use the stack protect guard decl. */
+ tree guard_decl = targetm.stack_protect_guard ();
+ if (guard_decl
+ && VAR_P (guard_decl)
+ && strcmp (stack_protect_guard_name,
+ IDENTIFIER_POINTER (declarator->u.id.id)) == 0)
+ {
+ /* Allow different types with the same size. */
+ tree guard_type = TREE_TYPE (guard_decl);
+ if (TYPE_PRECISION (type)
+ != TYPE_PRECISION (guard_type))
+ {
+ error ("conflicting types for %s; have %qT, "
+ "should be %qT",
+ stack_protect_guard_name, type,
+ guard_type);
+ return error_mark_node;
+ }
+ return guard_decl;
+ }
+ }
+
/* An uninitialized decl with `extern' is a reference. */
int extern_ref = !initialized && storage_class == csc_extern;
@@ -28152,6 +28152,9 @@ ix86_libgcc_floating_mode_supported_p
#undef TARGET_STACK_PROTECT_GUARD
#define TARGET_STACK_PROTECT_GUARD ix86_stack_protect_guard
+#undef TARGET_STACK_PROTECT_GUARD_SYMBOL
+#define TARGET_STACK_PROTECT_GUARD_SYMBOL "__stack_chk_guard"
+
#undef TARGET_STACK_PROTECT_RUNTIME_ENABLED_P
#define TARGET_STACK_PROTECT_RUNTIME_ENABLED_P \
ix86_stack_protect_runtime_enabled_p
@@ -3388,7 +3388,10 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
if (modules_p ())
remove_defining_module (newdecl);
- ggc_free (newdecl);
+ /* Don't free the stack protect guard decl. */
+ if (targetm.stack_protect_guard_symbol == nullptr
+ || newdecl != targetm.stack_protect_guard ())
+ ggc_free (newdecl);
return olddecl;
}
@@ -16233,6 +16236,33 @@ grokdeclarator (const cp_declarator *declarator,
{
/* It's a variable. */
+ const char *stack_protect_guard_name
+ = targetm.stack_protect_guard_symbol;
+ if (stack_protect_guard_name && dname && identifier_p (dname))
+ {
+ /* If the variable name is the same as the stack protect
+ guard, use the stack protect guard decl. */
+ tree guard_decl = targetm.stack_protect_guard ();
+ if (guard_decl
+ && VAR_P (guard_decl)
+ && strcmp (stack_protect_guard_name,
+ IDENTIFIER_POINTER (unqualified_id)) == 0)
+ {
+ tree guard_type = TREE_TYPE (guard_decl);
+ /* Allow different types with the same size. */
+ if (TYPE_PRECISION (type)
+ != TYPE_PRECISION (guard_type))
+ {
+ error ("conflicting types for %s; have %qT, "
+ "should be %qT",
+ stack_protect_guard_name, type,
+ guard_type);
+ return error_mark_node;
+ }
+ return guard_decl;
+ }
+ }
+
/* An uninitialized decl with `extern' is a reference. */
decl = grokvardecl (type, dname, unqualified_id,
declspecs,
@@ -5399,6 +5399,12 @@ The default version of this hook creates a variable called
@samp{__stack_chk_guard}, which is normally defined in @file{libgcc2.c}.
@end deftypefn
+@deftypevr {Target Hook} {const char *} TARGET_STACK_PROTECT_GUARD_SYMBOL
+Usually, the compiler uses an external variable @samp{__stack_chk_guard}
+defined in @file{libgcc2.c} as the stack protection guard symbol. Define
+this macro to a user provided string for the stack protection guard symbol.
+@end deftypevr
+
@deftypefn {Target Hook} tree TARGET_STACK_PROTECT_FAIL (void)
This hook returns a @code{CALL_EXPR} that alerts the runtime that the
stack protect guard variable has been modified. This expression should
@@ -3799,6 +3799,8 @@ generic code.
@hook TARGET_STACK_PROTECT_GUARD
+@hook TARGET_STACK_PROTECT_GUARD_SYMBOL
+
@hook TARGET_STACK_PROTECT_FAIL
@hook TARGET_STACK_PROTECT_RUNTIME_ENABLED_P
@@ -4595,6 +4595,15 @@ The default version of this hook creates a variable called\n\
tree, (void),
default_stack_protect_guard)
+/* Nonnull if the target supports a user defined symbol as the stack
+ protection guard. */
+DEFHOOKPOD
+(stack_protect_guard_symbol,
+ "Usually, the compiler uses an external variable @samp{__stack_chk_guard}\n\
+defined in @file{libgcc2.c} as the stack protection guard symbol. Define\n\
+this macro to a user provided string for the stack protection guard symbol.",
+ const char *, nullptr)
+
/* This target hook allows the operating system to override the CALL_EXPR
that is invoked when a check vs the guard variable fails. */
DEFHOOK
@@ -924,9 +924,23 @@ default_stack_protect_guard (void)
{
rtx x;
+ /* If not nullptr, targetm.stack_protect_guard_symbol is treated
+ as an internal C/C++ symbol defined by user. Use size_type_node
+ if it has the same size as ptr_type_node so that the guard symbol
+ can be initialized as an integer. */
+ const char *guard_symbol = targetm.stack_protect_guard_symbol;
+ t = ptr_type_node;
+ if (guard_symbol != nullptr)
+ {
+ if (TYPE_PRECISION (size_type_node)
+ == TYPE_PRECISION (ptr_type_node))
+ t = size_type_node;
+ }
+ else
+ guard_symbol = "__stack_chk_guard";
+ gcc_assert (guard_symbol != NULL);
t = build_decl (UNKNOWN_LOCATION,
- VAR_DECL, get_identifier ("__stack_chk_guard"),
- ptr_type_node);
+ VAR_DECL, get_identifier (guard_symbol), t);
TREE_STATIC (t) = 1;
TREE_PUBLIC (t) = 1;
DECL_EXTERNAL (t) = 1;
new file mode 100644
@@ -0,0 +1,35 @@
+/* { dg-do run { target fstack_protector } } */
+/* { dg-options "-O2 -fstack-protector-all -mstack-protector-guard=global" } */
+
+#include <stdlib.h>
+
+#ifdef __LP64__
+const unsigned long int __stack_chk_guard = 0x2d853605a4d9a09cUL;
+#else
+const unsigned long int __stack_chk_guard = 0xdd2cc927UL;
+#endif
+
+extern "C" void
+__stack_chk_fail (void)
+{
+ exit (0); /* pass */
+}
+
+__attribute__ ((noipa))
+void
+smash (char *p, int i)
+{
+ p[i] = 42;
+}
+
+int
+main (void)
+{
+ char foo[255];
+
+ /* smash stack */
+ for (int i = 0; i <= 400; i++)
+ smash (foo, i);
+
+ return 1;
+}
new file mode 100644
@@ -0,0 +1,10 @@
+/* { dg-do compile { target fstack_protector } } */
+/* { dg-options "-O2 -fstack-protector-all -mstack-protector-guard=global" } */
+
+extern char __stack_chk_guard; /* { dg-error "conflicting types for __stack_chk_guard;" } */
+
+void
+smash (char *p, int i)
+{
+ p[i] = 42;
+}
new file mode 100644
@@ -0,0 +1,49 @@
+/* { dg-do run { target { fstack_protector && fpic } } } */
+/* { dg-options "-O2 -fPIC -fstack-protector-all -mstack-protector-guard=global -save-temps" } */
+
+#ifndef __stack_protection_guard_is_internal_symbol__
+# error "__stack_protection_guard_is_internal_symbol__ isn't defined"
+#endif
+
+#include <stdlib.h>
+#include <stddef.h>
+
+__attribute__ ((visibility ("hidden")))
+#ifdef __LP64__
+const size_t __stack_chk_guard = 0x2d853605a4d9a09cUL;
+#else
+const size_t __stack_chk_guard = 0xdd2cc927UL;
+#endif
+
+extern "C" void
+__stack_chk_fail (void)
+{
+ exit (0); /* pass */
+}
+
+__attribute__ ((noipa))
+void
+smash (char *p, int i)
+{
+ p[i] = 42;
+}
+
+int
+main (void)
+{
+ char foo[255];
+
+ /* smash stack */
+ for (int i = 0; i <= 400; i++)
+ smash (foo, i);
+
+ return 1;
+}
+
+/* { dg-final { scan-hidden "__stack_chk_guard" } } */
+/* { dg-final { scan-assembler "__stack_chk_guard\\(%rip\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "__stack_chk_guard@GOTPCREL" { target { ! ia32 } } } } */
+ /* { dg-final { scan-assembler ".quad 3280087301477736604" { target { lp64 } } } } */
+/* { dg-final { scan-assembler "__stack_chk_guard@GOTOFF" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "__stack_chk_guard@GOT\\(" { target ia32 } } } */
+ /* { dg-final { scan-assembler ".long -584267481" { target { ! lp64 } } } } */
new file mode 100644
@@ -0,0 +1,20 @@
+/* { dg-do compile { target { fstack_protector && fpic } } } */
+/* { dg-options "-O2 -fPIC -fstack-protector-all -mstack-protector-guard=global" } */
+
+#include <stddef.h>
+
+extern const size_t __stack_chk_guard;
+__attribute__ ((visibility ("hidden")))
+extern const size_t __stack_chk_guard;
+
+void
+smash (char *p, int i)
+{
+ p[i] = 42;
+}
+
+/* { dg-final { scan-hidden "__stack_chk_guard" } } */
+/* { dg-final { scan-assembler "__stack_chk_guard\\(%rip\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "__stack_chk_guard@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "__stack_chk_guard@GOTOFF" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "__stack_chk_guard@GOT\\(" { target ia32 } } } */
new file mode 100644
@@ -0,0 +1,50 @@
+/* { dg-do run { target { fstack_protector && fpic } } } */
+/* { dg-options "-O2 -fPIC -mstack-protector-guard=global -save-temps" } */
+
+#ifndef __stack_protection_guard_is_internal_symbol__
+# error "__stack_protection_guard_is_internal_symbol__ isn't defined"
+#endif
+
+#include <stdlib.h>
+#include <stddef.h>
+
+__attribute__ ((visibility ("hidden")))
+#ifdef __LP64__
+const size_t __stack_chk_guard = 0x2d853605a4d9a09cUL;
+#else
+const size_t __stack_chk_guard = 0xdd2cc927UL;
+#endif
+
+extern "C" void
+__stack_chk_fail (void)
+{
+ exit (0); /* pass */
+}
+
+__attribute__ ((noipa))
+void
+smash (char *p, int i)
+{
+ p[i] = 42;
+}
+
+__attribute__ ((optimize ("stack-protector-all")))
+int
+main (void)
+{
+ char foo[255];
+
+ /* smash stack */
+ for (int i = 0; i <= 400; i++)
+ smash (foo, i);
+
+ return 1;
+}
+
+/* { dg-final { scan-hidden "__stack_chk_guard" } } */
+/* { dg-final { scan-assembler "__stack_chk_guard\\(%rip\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "__stack_chk_guard@GOTPCREL" { target { ! ia32 } } } } */
+ /* { dg-final { scan-assembler ".quad 3280087301477736604" { target { lp64 } } } } */
+/* { dg-final { scan-assembler "__stack_chk_guard@GOTOFF" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "__stack_chk_guard@GOT\\(" { target ia32 } } } */
+ /* { dg-final { scan-assembler ".long -584267481" { target { ! lp64 } } } } */
new file mode 100644
@@ -0,0 +1,10 @@
+/* { dg-do compile { target fstack_protector } } */
+/* { dg-options "-O2 -fstack-protector-all -mstack-protector-guard=global" } */
+
+extern char __stack_chk_guard; /* { dg-error "conflicting types for __stack_chk_guard;" } */
+
+void
+smash (char *p, int i)
+{
+ p[i] = 42;
+}
new file mode 100644
@@ -0,0 +1,49 @@
+/* { dg-do run { target { fstack_protector && fpic } } } */
+/* { dg-options "-O2 -fPIC -fstack-protector-all -mstack-protector-guard=global -save-temps" } */
+
+#ifndef __stack_protection_guard_is_internal_symbol__
+# error "__stack_protection_guard_is_internal_symbol__ isn't defined"
+#endif
+
+#include <stdlib.h>
+#include <stddef.h>
+
+__attribute__ ((visibility ("hidden")))
+#ifdef __LP64__
+const size_t __stack_chk_guard = 0x2d853605a4d9a09cUL;
+#else
+const size_t __stack_chk_guard = 0xdd2cc927UL;
+#endif
+
+void
+__stack_chk_fail (void)
+{
+ exit (0); /* pass */
+}
+
+__attribute__ ((noipa))
+void
+smash (char *p, int i)
+{
+ p[i] = 42;
+}
+
+int
+main (void)
+{
+ char foo[255];
+
+ /* smash stack */
+ for (int i = 0; i <= 400; i++)
+ smash (foo, i);
+
+ return 1;
+}
+
+/* { dg-final { scan-hidden "__stack_chk_guard" } } */
+/* { dg-final { scan-assembler "__stack_chk_guard\\(%rip\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "__stack_chk_guard@GOTPCREL" { target { ! ia32 } } } } */
+ /* { dg-final { scan-assembler ".quad 3280087301477736604" { target { lp64 } } } } */
+/* { dg-final { scan-assembler "__stack_chk_guard@GOTOFF" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "__stack_chk_guard@GOT\\(" { target ia32 } } } */
+ /* { dg-final { scan-assembler ".long -584267481" { target { ! lp64 } } } } */
new file mode 100644
@@ -0,0 +1,20 @@
+/* { dg-do compile { target { fstack_protector && fpic } } } */
+/* { dg-options "-O2 -fPIC -fstack-protector-all -mstack-protector-guard=global" } */
+
+#include <stddef.h>
+
+extern const size_t __stack_chk_guard;
+__attribute__ ((visibility ("hidden")))
+extern const size_t __stack_chk_guard;
+
+void
+smash (char *p, int i)
+{
+ p[i] = 42;
+}
+
+/* { dg-final { scan-hidden "__stack_chk_guard" } } */
+/* { dg-final { scan-assembler "__stack_chk_guard\\(%rip\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "__stack_chk_guard@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "__stack_chk_guard@GOTOFF" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "__stack_chk_guard@GOT\\(" { target ia32 } } } */
new file mode 100644
@@ -0,0 +1,50 @@
+/* { dg-do run { target { fstack_protector && fpic } } } */
+/* { dg-options "-O2 -fPIC -mstack-protector-guard=global -save-temps" } */
+
+#ifndef __stack_protection_guard_is_internal_symbol__
+# error "__stack_protection_guard_is_internal_symbol__ isn't defined"
+#endif
+
+#include <stdlib.h>
+#include <stddef.h>
+
+__attribute__ ((visibility ("hidden")))
+#ifdef __LP64__
+const size_t __stack_chk_guard = 0x2d853605a4d9a09cUL;
+#else
+const size_t __stack_chk_guard = 0xdd2cc927UL;
+#endif
+
+void
+__stack_chk_fail (void)
+{
+ exit (0); /* pass */
+}
+
+__attribute__ ((noipa))
+void
+smash (char *p, int i)
+{
+ p[i] = 42;
+}
+
+__attribute__ ((optimize ("stack-protector-all")))
+int
+main (void)
+{
+ char foo[255];
+
+ /* smash stack */
+ for (int i = 0; i <= 400; i++)
+ smash (foo, i);
+
+ return 1;
+}
+
+/* { dg-final { scan-hidden "__stack_chk_guard" } } */
+/* { dg-final { scan-assembler "__stack_chk_guard\\(%rip\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "__stack_chk_guard@GOTPCREL" { target { ! ia32 } } } } */
+ /* { dg-final { scan-assembler ".quad 3280087301477736604" { target { lp64 } } } } */
+/* { dg-final { scan-assembler "__stack_chk_guard@GOTOFF" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "__stack_chk_guard@GOT\\(" { target ia32 } } } */
+ /* { dg-final { scan-assembler ".long -584267481" { target { ! lp64 } } } } */
@@ -2920,6 +2920,19 @@ mark_decl_referenced (tree decl)
which do not need to be marked. */
}
+/* Mark the symbol of DECL as local if it is the stack protect guard
+ decl. */
+
+void
+mark_stack_protect_guard_decl_local (tree decl)
+{
+ if (targetm.stack_protect_guard_symbol == nullptr
+ || decl != targetm.stack_protect_guard ())
+ return;
+ rtx symbol = DECL_RTL (decl);
+ symbol = XEXP (symbol, 0);
+ SYMBOL_REF_FLAGS (symbol) |= SYMBOL_FLAG_LOCAL;
+}
/* Output to FILE (an assembly file) a reference to NAME. If NAME
starts with a *, the rest of NAME is output verbatim. Otherwise
@@ -41,6 +41,7 @@ extern void process_pending_assemble_externals (void);
extern bool decl_replaceable_p (tree, bool);
extern bool decl_binds_to_current_def_p (const_tree);
extern enum tls_model decl_default_tls_model (const_tree);
+extern void mark_stack_protect_guard_decl_local (tree);
/* Declare DECL to be a weak symbol. */
extern void declare_weak (tree);