@@ -1506,6 +1506,7 @@ OBJS = \
gimple-ssa-warn-restrict.o \
gimple-streamer-in.o \
gimple-streamer-out.o \
+ gimple-valgrind-interop.o \
gimple-walk.o \
gimple-warn-recursion.o \
gimplify.o \
@@ -1194,6 +1194,9 @@ DEF_GCC_BUILTIN (BUILT_IN_LINE, "LINE", BT_FN_INT, ATTR_NOTHROW_LEAF_LIST)
/* Control Flow Redundancy hardening out-of-line checker. */
DEF_BUILTIN_STUB (BUILT_IN___HARDCFR_CHECK, "__builtin___hardcfr_check")
+/* Wrappers for Valgrind client requests. */
+DEF_EXT_LIB_BUILTIN (BUILT_IN_VALGRIND_MAKE_UNDEFINED, "__gcc_vgmc_make_mem_undefined", BT_FN_VOID_PTR_SIZE, ATTR_NOTHROW_LEAF_LIST)
+
/* Synchronization Primitives. */
#include "sync-builtins.def"
@@ -3372,6 +3372,10 @@ Enum(auto_init_type) String(pattern) Value(AUTO_INIT_PATTERN)
EnumValue
Enum(auto_init_type) String(zero) Value(AUTO_INIT_ZERO)
+fvalgrind-annotations
+Common Var(flag_valgrind_annotations) Optimization
+Annotate lifetime boundaries with Valgrind client requests.
+
; -fverbose-asm causes extra commentary information to be produced in
; the generated assembly code (to make it more readable). This option
; is generally only of use to those who actually need to read the
@@ -1567,6 +1567,11 @@ Disable TM clone registry in libgcc. It is enabled in libgcc by default.
This option helps to reduce code size for embedded targets which do
not use transactional memory.
+@item --enable-valgrind-interop
+Provide wrappers for Valgrind client requests in libgcc, which are used for
+@option{-fvalgrind-annotations}. Requires Valgrind header files for the
+target (in the build-time sysroot if building a cross-compiler).
+
@item --with-cpu=@var{cpu}
@itemx --with-cpu-32=@var{cpu}
@itemx --with-cpu-64=@var{cpu}
@@ -657,6 +657,7 @@ Objective-C and Objective-C++ Dialects}.
-fno-stack-limit -fsplit-stack
-fstrub=disable -fstrub=strict -fstrub=relaxed
-fstrub=all -fstrub=at-calls -fstrub=internal
+-fvalgrind-annotations
-fvtable-verify=@r{[}std@r{|}preinit@r{|}none@r{]}
-fvtv-counts -fvtv-debug
-finstrument-functions -finstrument-functions-once
@@ -12920,6 +12921,9 @@ can use @option{-flifetime-dse=1}. The default behavior can be
explicitly selected with @option{-flifetime-dse=2}.
@option{-flifetime-dse=0} is equivalent to @option{-fno-lifetime-dse}.
+See @option{-fvalgrind-annotations} for instrumentation that allows to
+detect violations of standard lifetime semantics using Valgrind.
+
@opindex flive-range-shrinkage
@item -flive-range-shrinkage
Attempt to decrease register pressure through register live range
@@ -17976,6 +17980,29 @@ function attributes that tell which mode was selected for each function.
The primary use of this option is for testing, to exercise thoroughly
the @code{strub} machinery.
+@opindex fvalgrind-annotations
+@item -fvalgrind-annotations
+Emit Valgrind client requests annotating object lifetime boundaries.
+This allows to detect attempts to access fields of a C++ object after
+its destructor has completed (but storage was not deallocated yet), or
+to initialize it in advance from @code{operator new} rather than the
+constructor.
+
+This instrumentation relies on presence of @code{__gcc_vgmc_make_mem_undefined}
+function that wraps the corresponding Valgrind client request. It is provided
+by libgcc when it is configured with @samp{--enable-valgrind-interop}.
+Otherwise, you can implement it like this:
+
+@smallexample
+#include <valgrind/memcheck.h>
+
+void
+__gcc_vgmc_make_mem_undefined (void *addr, size_t size)
+@{
+ VALGRIND_MAKE_MEM_UNDEFINED (addr, size);
+@}
+@end smallexample
+
@opindex fvtable-verify
@item -fvtable-verify=@r{[}std@r{|}preinit@r{|}none@r{]}
This option is only available when compiling C++ code.
new file mode 100644
@@ -0,0 +1,112 @@
+/* Annotate lifetime boundaries for Valgrind.
+ Copyright (C) 2023 Free Software Foundation, Inc.
+
+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 "tree.h"
+#include "function.h"
+#include "cfg.h"
+#include "basic-block.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "tree-pass.h"
+#include "gimplify-me.h"
+#include "fold-const.h"
+
+namespace {
+
+/* Emit VALGRIND_MAKE_MEM_UNDEFINED annotation for the object given by VAR. */
+
+bool
+annotate_undef (gimple_stmt_iterator *gsi, tree var)
+{
+ tree size = arg_size_in_bytes (TREE_TYPE (var));
+ if (size == size_zero_node)
+ return false;
+
+ tree addr = build_fold_addr_expr (var);
+ tree decl = builtin_decl_explicit (BUILT_IN_VALGRIND_MAKE_UNDEFINED);
+ tree call = build_call_expr (decl, 2, addr, size);
+
+ force_gimple_operand_gsi (gsi, call, false, NULL_TREE, false, GSI_NEW_STMT);
+ return true;
+}
+
+const pass_data pass_data_instrument_valgrind = {
+ GIMPLE_PASS, /* type */
+ "valgrind", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ TV_NONE, /* tv_id */
+ PROP_cfg, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0, /* todo_flags_finish */
+};
+
+}
+
+class pass_instrument_valgrind : public gimple_opt_pass
+{
+public:
+ pass_instrument_valgrind (gcc::context *ctxt)
+ : gimple_opt_pass (pass_data_instrument_valgrind, ctxt)
+ {
+ }
+
+ unsigned int execute (function *) final override;
+ bool gate (function *) final override;
+};
+
+bool
+pass_instrument_valgrind::gate (function *)
+{
+ return flag_valgrind_annotations;
+}
+
+/* Scan for GIMPLE clobbers that mark object lifetime boundaries and
+ make them known to Valgrind by emitting corresponding client requests. */
+
+unsigned int
+pass_instrument_valgrind::execute (function *fun)
+{
+ bool changed = false;
+ basic_block bb;
+ FOR_EACH_BB_FN (bb, fun)
+ {
+ for (gimple_stmt_iterator gsi = gsi_start_nondebug_bb (bb);
+ !gsi_end_p (gsi); gsi_next_nondebug (&gsi))
+ {
+ gimple *stmt = gsi_stmt (gsi);
+
+ /* Leave CLOBBER_EOLs for ASan. */
+ if (gimple_clobber_p (stmt, CLOBBER_UNDEF))
+ changed |= annotate_undef (&gsi, gimple_assign_lhs (stmt));
+ }
+ }
+
+ return changed ? TODO_update_ssa : 0;
+}
+
+gimple_opt_pass *
+make_pass_instrument_valgrind (gcc::context *ctxt)
+{
+ return new pass_instrument_valgrind (ctxt);
+}
@@ -57,6 +57,7 @@ along with GCC; see the file COPYING3. If not see
PUSH_INSERT_PASSES_WITHIN (pass_build_ssa_passes)
NEXT_PASS (pass_fixup_cfg);
NEXT_PASS (pass_build_ssa);
+ NEXT_PASS (pass_instrument_valgrind);
NEXT_PASS (pass_walloca, /*strict_mode_p=*/true);
NEXT_PASS (pass_warn_printf);
NEXT_PASS (pass_warn_nonnull_compare);
new file mode 100644
@@ -0,0 +1,22 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-options "-O2 -fvalgrind-annotations -fdump-tree-optimized" } */
+
+#include <new>
+
+struct Foo
+{
+ int val;
+};
+
+struct Bar : Foo
+{
+ Bar() {}
+};
+
+int main ()
+{
+ alignas(Bar) char buf [sizeof (Bar)];
+ Bar *obj = new(buf) Bar;
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin___gcc_vgmc_make_mem_undefined" 1 "optimized" } } */
new file mode 100644
@@ -0,0 +1,12 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-options "-O2 -fvalgrind-annotations -fdump-tree-optimized" } */
+
+struct Foo
+{
+ char buf [1024];
+ Foo () {}
+};
+
+Foo Obj;
+
+/* { dg-final { scan-tree-dump-times "__builtin___gcc_vgmc_make_mem_undefined" 1 "optimized" } } */
@@ -440,6 +440,7 @@ extern gimple_opt_pass *make_pass_omp_device_lower (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_object_sizes (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_early_object_sizes (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_warn_access (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_instrument_valgrind (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_warn_printf (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_warn_recursion (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_strlen (gcc::context *ctxt);
@@ -436,6 +436,9 @@ LIB2ADD += $(srcdir)/hardcfr.c
# Stack scrubbing infrastructure.
LIB2ADD += $(srcdir)/strub.c
+# Wrappers for Valgrind client requests.
+LIB2ADD += $(srcdir)/valgrind-interop.c
+
# While emutls.c has nothing to do with EH, it is in LIB2ADDEH*
# instead of LIB2ADD because that's the way to be sure on some targets
# (e.g. *-*-darwin*) only one copy of it is linked.
@@ -3,6 +3,9 @@
/* Define to the .hidden-like directive if it exists. */
#undef AS_HIDDEN_DIRECTIVE
+/* Build Valgrind request wrappers */
+#undef ENABLE_VALGRIND_INTEROP
+
/* Define to 1 if the assembler supports AVX. */
#undef HAVE_AS_AVX
@@ -61,6 +64,9 @@
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
+/* Define to 1 if you have the <valgrind/memcheck.h> header file. */
+#undef HAVE_VALGRIND_MEMCHECK_H
+
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
@@ -715,6 +715,7 @@ with_system_libunwind
enable_cet
enable_explicit_exception_frame_registration
enable_tm_clone_registry
+enable_valgrind_interop
with_glibc_version
enable_tls
with_gcc_major_version_only
@@ -1359,6 +1360,8 @@ Optional Features:
start, for use e.g. for compatibility with
installations without PT_GNU_EH_FRAME support
--disable-tm-clone-registry disable TM clone registry
+ --enable-valgrind-interop
+ build wrappers for Valgrind client requests
--enable-tls Use thread-local storage [default=yes]
Optional Packages:
@@ -4466,7 +4469,8 @@ as_fn_arith $ac_cv_sizeof_long_double \* 8 && long_double_type_size=$as_val
for ac_header in inttypes.h stdint.h stdlib.h ftw.h \
unistd.h sys/stat.h sys/types.h \
- string.h strings.h memory.h sys/auxv.h sys/mman.h
+ string.h strings.h memory.h sys/auxv.h sys/mman.h \
+ valgrind/memcheck.h
do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_preproc "$LINENO" "$ac_header" "$as_ac_Header"
@@ -5024,6 +5028,22 @@ fi
+# Check whether --enable-valgrind-interop was given.
+if test "${enable_valgrind_interop+set}" = set; then :
+ enableval=$enable_valgrind_interop;
+else
+ enable_valgrind_interop=no
+fi
+
+if test $enable_valgrind_interop != no; then
+ if test $ac_cv_header_valgrind_memcheck_h = no; then
+ as_fn_error $? "--enable-valgrind-interop: valgrind/memcheck.h not found" "$LINENO" 5
+ fi
+
+$as_echo "#define ENABLE_VALGRIND_INTEROP 1" >>confdefs.h
+
+fi
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5
$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; }
if ${acl_cv_prog_gnu_ld+:} false; then :
@@ -231,7 +231,8 @@ AC_SUBST(long_double_type_size)
AC_CHECK_HEADERS(inttypes.h stdint.h stdlib.h ftw.h \
unistd.h sys/stat.h sys/types.h \
- string.h strings.h memory.h sys/auxv.h sys/mman.h)
+ string.h strings.h memory.h sys/auxv.h sys/mman.h \
+ valgrind/memcheck.h)
AC_HEADER_STDC
# Check for decimal float support.
@@ -303,6 +304,18 @@ esac
])
AC_SUBST([use_tm_clone_registry])
+AC_ARG_ENABLE([valgrind-interop],
+ [AC_HELP_STRING([--enable-valgrind-interop],
+ [build wrappers for Valgrind client requests])],
+ [],
+ [enable_valgrind_interop=no])
+if test $enable_valgrind_interop != no; then
+ if test $ac_cv_header_valgrind_memcheck_h = no; then
+ AC_MSG_ERROR([--enable-valgrind-interop: valgrind/memcheck.h not found])
+ fi
+ AC_DEFINE(ENABLE_VALGRIND_INTEROP, 1, [Build Valgrind request wrappers])
+fi
+
AC_LIB_PROG_LD_GNU
AC_MSG_CHECKING([for thread model used by GCC])
@@ -558,6 +558,8 @@ extern void __strub_enter (void **);
extern void __strub_update (void**);
extern void __strub_leave (void **);
+extern void __gcc_vgmc_make_mem_undefined (void *, size_t);
+
#ifndef HIDE_EXPORTS
#pragma GCC visibility pop
#endif
new file mode 100644
@@ -0,0 +1,40 @@
+/* Wrappers for Valgrind client requests.
+ Copyright (C) 2023 Free Software Foundation, Inc.
+
+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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+#include "tsystem.h"
+#include "auto-target.h"
+
+#ifdef ENABLE_VALGRIND_INTEROP
+
+#include <valgrind/memcheck.h>
+
+/* Externally callable wrapper for Valgrind Memcheck client request:
+ declare SIZE bytes of memory at address ADDR as uninitialized. */
+
+void
+__gcc_vgmc_make_mem_undefined (void *addr, size_t size)
+{
+ VALGRIND_MAKE_MEM_UNDEFINED (addr, size);
+}
+#endif