[m-rev.] for review: Support building with AddressSanitizer and UndefinedBehaviorSanitizer.

Peter Wang novalazy at gmail.com
Tue Oct 11 16:15:50 AEDT 2016


configure.ac:
    Add configure option --enable-sanitizers.

Mmake.common.in:
scripts/Mercury.config.in:
    Add variables to be set when --enable-sanitizers is used.

scripts/mgnuc.in:
scripts/ml.in:
    Pass sanitizer options to the C compiler and the linker.

compiler/options.m:
    Add options --cflags-for-sanitizers and --linker-sanitizer-flags
    for receiving the configuration.

    Set --linker-trace-flags and --shlib-linker-trace-flags default
    values to empty instead of "-g" (likely copy error).

compiler/compile_target_code.m:
    Pass sanitizer options to the C compiler, and the linker when
    building an executable or shared library.

runtime/Mmakefile:
trace/Mmakefile:
    Pass sanitizer options to linker when building shared libraries.

README.sanitizers:
    Add instructions.
---
 Mmake.common.in                |  4 ++++
 README.sanitizers              | 27 +++++++++++++++++++++++++++
 compiler/compile_target_code.m |  7 +++++++
 compiler/options.m             | 14 +++++++++++---
 configure.ac                   | 27 +++++++++++++++++++++++++++
 runtime/Mmakefile              |  8 ++++----
 scripts/Mercury.config.in      |  2 ++
 scripts/mgnuc.in               |  5 +++++
 scripts/ml.in                  |  5 ++++-
 trace/Mmakefile                |  8 ++++----
 10 files changed, 95 insertions(+), 12 deletions(-)
 create mode 100644 README.sanitizers

diff --git a/Mmake.common.in b/Mmake.common.in
index 4a13ae9..1472d17 100644
--- a/Mmake.common.in
+++ b/Mmake.common.in
@@ -96,6 +96,10 @@ ifeq "$(findstring .par,$(GRADE))$(findstring .gc,$(GRADE))" ".par.gc"
 ERROR_UNDEFINED	= $(ALLOW_UNDEFINED)
 endif
 
+# Specify any options required when linking if AddressSanitizer and other
+# sanitizers are enabled.
+LDFLAGS_FOR_SANITIZERS = @LDFLAGS_FOR_SANITIZERS@
+
 # The following variables specify how to pass options to the $(CC) or
 # $(LINK_SHARED_OBJ) command to specify directories to be searched at
 # runtime for shared libraries.
diff --git a/README.sanitizers b/README.sanitizers
new file mode 100644
index 0000000..003c133
--- /dev/null
+++ b/README.sanitizers
@@ -0,0 +1,27 @@
+The configure option `--enable-sanitizers' will enable AddressSanitizer
+and UndefinedBehaviorSanitizer when building with gcc. The built Mercury
+compiler will also enable the same sanitizers when building user programs.
+
+AddressSanitizer and UndefinedBehaviorSanitizer are included in gcc 4.9
+and above. We have tested with gcc 5.8.0.
+
+Only high-level C grades work with the sanitizers enabled.
+
+For best results, add these options to Mmake.params before building Mercury:
+
+    EXTRA_MCFLAGS += --c-debug
+    EXTRA_MGNUCFLAGS += --c-debug
+    EXTRA_MLFLAGS += --c-debug
+
+Likewise, pass --c-debug when building user programs.
+
+clang does not work yet (tested version 3.8.0). It seemingly does not
+terminate when compiling certain files with UndefinedBehaviorSanitizer,
+e.g. runtime/mercury_deconstruct.c. There are also linking issues to be
+sorted out.
+
+Resources:
+
+    <http://clang.llvm.org/docs/AddressSanitizer.html>
+    <http://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html>
+    <https://github.com/google/sanitizers/wiki/AddressSanitizer>
diff --git a/compiler/compile_target_code.m b/compiler/compile_target_code.m
index 8c56108..9c0895f 100644
--- a/compiler/compile_target_code.m
+++ b/compiler/compile_target_code.m
@@ -447,6 +447,8 @@ gather_c_compiler_flags(Globals, PIC, AllCFlags) :-
         Target_Debug = no,
         Target_DebugOpt = ""
     ),
+    globals.lookup_string_option(Globals, cflags_for_sanitizers,
+        SanitizerOpts),
     globals.lookup_bool_option(Globals, use_trail, UseTrail),
     (
         UseTrail = yes,
@@ -573,6 +575,7 @@ gather_c_compiler_flags(Globals, PIC, AllCFlags) :-
         CFLAGS_FOR_REGS, " ", CFLAGS_FOR_GOTOS, " ",
         CFLAGS_FOR_THREADS, " ", CFLAGS_FOR_PIC, " ",
         Target_DebugOpt,
+        SanitizerOpts, " ",
         TypeLayoutOpt,
         InlineAllocOpt,
         AnsiOpt, " ",
@@ -1882,6 +1885,9 @@ link_exe_or_shared_lib(Globals, ErrorStream, LinkTargetType, ModuleName,
         DebugOpts = ""
     ),
 
+    globals.lookup_string_option(Globals, linker_sanitizer_flags,
+        SanitizerOpts),
+
     % Should the executable be statically linked?
     globals.lookup_string_option(Globals, linkage, Linkage),
     ( if
@@ -2046,6 +2052,7 @@ link_exe_or_shared_lib(Globals, ErrorStream, LinkTargetType, ModuleName,
                     FrameworkDirectories, " ",
                     InstallNameOpt, " ",
                     DebugOpts, " ",
+                    SanitizerOpts, " ",
                     Frameworks, " ",
                     ResCmdLinkOpts, " ",
                     LDFlags, " ",
diff --git a/compiler/options.m b/compiler/options.m
index f197f78..11512e0 100644
--- a/compiler/options.m
+++ b/compiler/options.m
@@ -827,6 +827,7 @@
     ;       cflags_for_gotos
     ;       cflags_for_threads
     ;       cflags_for_debug
+    ;       cflags_for_sanitizers
     ;       cflags_for_pic
     ;       c_flag_to_name_object_file
     ;       object_file_extension
@@ -930,6 +931,7 @@
     ;       shlib_linker_link_lib_suffix
     ;       linker_debug_flags
     ;       shlib_linker_debug_flags
+    ;       linker_sanitizer_flags
     ;       linker_trace_flags
     ;       shlib_linker_trace_flags
     ;       linker_path_flag
@@ -1700,6 +1702,7 @@ option_defaults_2(target_code_compilation_option, [
                                         % The `mmc' script will override the
                                         % default with values determined at
                                         % configuration time.
+    cflags_for_sanitizers               -   string(""),
     cflags_for_optimization             -   string("-O"),
     cflags_for_ansi                     -   string(""),
     cflags_for_regs                     -   string(""),
@@ -1812,8 +1815,9 @@ option_defaults_2(link_option, [
     linker_opt_separator                -   string(""),
     linker_debug_flags                  -   string("-g"),
     shlib_linker_debug_flags            -   string("-g"),
-    linker_trace_flags                  -   string("-g"),
-    shlib_linker_trace_flags            -   string("-g"),
+    linker_sanitizer_flags              -   string(""),
+    linker_trace_flags                  -   string(""),
+    shlib_linker_trace_flags            -   string(""),
     linker_thread_flags                 -   string(""),
     shlib_linker_thread_flags           -   string(""),
     linker_static_flags                 -   string("-static"),
@@ -2676,6 +2680,7 @@ long_option("cflags-for-regs",      cflags_for_regs).
 long_option("cflags-for-gotos",     cflags_for_gotos).
 long_option("cflags-for-threads",   cflags_for_threads).
 long_option("cflags-for-debug",     cflags_for_debug).
+long_option("cflags-for-sanitizers", cflags_for_sanitizers).
 long_option("cflags-for-pic",       cflags_for_pic).
 long_option("c-flag-to-name-object-file", c_flag_to_name_object_file).
 long_option("object-file-extension",    object_file_extension).
@@ -2780,6 +2785,7 @@ long_option("readline-libs",        readline_libs).
 long_option("linker-opt-separator", linker_opt_separator).
 long_option("linker-debug-flags",   linker_debug_flags).
 long_option("shlib-linker-debug-flags", shlib_linker_debug_flags).
+long_option("linker-sanitizer-flags", linker_sanitizer_flags).
 long_option("linker-trace-flags",   linker_trace_flags).
 long_option("shlib-linker-trace-flags", shlib_linker_trace_flags).
 long_option("linker-thread-flags",  linker_thread_flags).
@@ -5494,7 +5500,8 @@ options_help_target_code_compilation -->
         % The --cflags-for-regs, --cflags-for-gotos,
         % --cflags-for-threads, --cflags-for-pic,
         % --cflags-for-warnings, --cflags-for-ansi,
-        % --cflags-for-optimization, --c-flag-to-name-object-file,
+        % --cflags-for-optimization, --cflags-for-sanitizers,
+        % --c-flag-to-name-object-file,
         % --object-file-extension and --pic-object-file-extension
         % options are reserved for use by the `mmc' script;
         % they are deliberately not documented.
@@ -5711,6 +5718,7 @@ options_help_link -->
         % --hwloc-libs, --hwloc-static-libs,
         % --linker-opt-separator,
         % --linker-debug-flags, --shlib-linker-debug-flags,
+        % --linker-sanitizer-flags,
         % --linker-trace-flags, --shlib-linker-trace-flags,
         % --linker-thread-flags, --shlib-linker-thread-flags,
         % --linker-static-flags, --linker-strip-flag,
diff --git a/configure.ac b/configure.ac
index 18568ad..5b7916f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -4758,6 +4758,33 @@ if test "$USE_DLLS" = "yes"; then
     AC_DEFINE(MR_USE_DLLS)
 fi
 
+#-----------------------------------------------------------------------------#
+#
+# Sanitizers
+#
+
+AC_ARG_ENABLE([sanitizers],
+    AC_HELP_STRING([--enable-sanitizers],
+        [enable AddressSanitizer and UndefinedBehaviorSanitizer]),
+    [ac_sanitizers="$enableval"], [ac_sanitizers=no])
+
+case "$C_COMPILER_TYPE" in
+    gcc*)
+        if test "$ac_sanitizers" = yes
+        then
+            CFLAGS_FOR_SANITIZERS="-fsanitize=address,undefined"
+            LDFLAGS_FOR_SANITIZERS="-fsanitize=address,undefined -pthread"
+        fi
+        ;;
+    *)
+        CFLAGS_FOR_SANITIZERS=
+        LDFLAGS_FOR_SANITIZERS=
+        ;;
+esac
+
+AC_SUBST(CFLAGS_FOR_SANITIZERS)
+AC_SUBST(LDFLAGS_FOR_SANITIZERS)
+
 #-----------------------------------------------------------------------------#
 
 if test "$BOOTSTRAP_MC" = ""; then
diff --git a/runtime/Mmakefile b/runtime/Mmakefile
index f10ed10..87a0e1b 100644
--- a/runtime/Mmakefile
+++ b/runtime/Mmakefile
@@ -366,19 +366,19 @@ lib$(RT_LIB_NAME)$(DLL_DEF_LIB).$A: $(OBJS)
 	$(RANLIB) $(RANLIBFLAGS) lib$(RT_LIB_NAME)$(DLL_DEF_LIB).$A
 
 lib$(RT_LIB_NAME).so: $(PIC_OBJS)
-	$(LINK_SHARED_OBJ) $(ERROR_UNDEFINED)				\
+	$(LINK_SHARED_OBJ) $(ERROR_UNDEFINED) $(LDFLAGS_FOR_SANITIZERS)	\
 		-o lib$(RT_LIB_NAME).so $(PIC_OBJS)			\
 		$(SHLIB_RPATH_OPT)$(FINAL_INSTALL_MERC_GC_LIB_DIR)	\
-		$(ALL_LD_LIBFLAGS) $(LDLIBS) $(THREADLIBS)			\
+		$(ALL_LD_LIBFLAGS) $(LDLIBS) $(THREADLIBS)		\
 		$(SHARED_LIBS)
 
 # For Darwin we should pass the -install_name option.
 lib$(RT_LIB_NAME).dylib: $(PIC_OBJS)
-	$(LINK_SHARED_OBJ) $(ERROR_UNDEFINED)				\
+	$(LINK_SHARED_OBJ) $(ERROR_UNDEFINED) $(LDFLAGS_FOR_SANITIZERS) \
 		-o lib$(RT_LIB_NAME).dylib $(PIC_OBJS)			\
 		-install_name						\
 			$(FINAL_INSTALL_MERC_LIB_DIR)/lib$(RT_LIB_NAME).dylib \
-		$(ALL_LD_LIBFLAGS) $(LDLIBS) $(THREADLIBS)			\
+		$(ALL_LD_LIBFLAGS) $(LDLIBS) $(THREADLIBS)		\
 		$(SHARED_LIBS)
 
 endif
diff --git a/scripts/Mercury.config.in b/scripts/Mercury.config.in
index 011a5b5..a548856 100644
--- a/scripts/Mercury.config.in
+++ b/scripts/Mercury.config.in
@@ -68,6 +68,7 @@ DEFAULT_MCFLAGS=\
 		--cflags-for-warnings "@CFLAGS_FOR_WARNINGS@" \
 		--cflags-for-threads "@CFLAGS_FOR_THREADS@" \
 		--cflags-for-debug "@CFLAGS_FOR_DEBUG@" \
+		--cflags-for-sanitizers "@CFLAGS_FOR_SANITIZERS@" \
 		--cflags-for-regs "@CFLAGS_FOR_REGS@" \
 		--cflags-for-gotos "@CFLAGS_FOR_GOTOS@" \
 		--cflags-for-pic "@CFLAGS_FOR_PIC@" \
@@ -104,6 +105,7 @@ DEFAULT_MCFLAGS=\
 		--linker-strip-flag "@LD_STRIP_FLAG@" \
 		--linker-debug-flags "@LDFLAGS_FOR_DEBUG@" \
 		--shlib-linker-debug-flags "@LD_LIBFLAGS_FOR_DEBUG@" \
+		--linker-sanitizer-flags "@LDFLAGS_FOR_SANITIZERS@" \
 		--linker-link-lib-flag "@LINK_LIB@" \
 		--linker-link-lib-suffix "@LINK_LIB_SUFFIX@" \
 		--shlib-linker-link-lib-flag "@LINK_LIB@" \
diff --git a/scripts/mgnuc.in b/scripts/mgnuc.in
index fb555c1..2ee7f03 100644
--- a/scripts/mgnuc.in
+++ b/scripts/mgnuc.in
@@ -27,6 +27,7 @@ CFLAGS_FOR_GOTOS="@CFLAGS_FOR_GOTOS@"
 CFLAGS_FOR_THREADS="@CFLAGS_FOR_THREADS@"
 CFLAGS_FOR_NO_STRICT_ALIASING="@CFLAGS_FOR_NO_STRICT_ALIASING@"
 CFLAGS_FOR_ANSI="@CFLAGS_FOR_ANSI@"
+CFLAGS_FOR_SANITIZERS="@CFLAGS_FOR_SANITIZERS@"
 AS="@AS@"
 BYTES_PER_WORD="@BYTES_PER_WORD@"
 MKTEMP=@MKTEMP@
@@ -501,6 +502,9 @@ case $non_local_gotos in
     false)      ;;
 esac
 
+# if sanitizers were enabled at configure time, add CFLAGS_FOR_SANITIZERS
+SANITIZER_OPTS="$CFLAGS_FOR_SANITIZERS"
+
 #
 # Special case hacks for particular architectures
 # Any code here needs to be duplicated in ../configure.in.
@@ -646,6 +650,7 @@ ALL_CC_OPTS="$MERC_ALL_C_INCL_DIRS\
     $RECORD_TERM_SIZE_OPTS\
     $MINIMAL_MODEL_OPTS\
     $PREGEN_SPF_OPTS\
+    $SANITIZER_OPTS\
     $SPLIT_OPTS\
     $THREAD_OPTS\
     $THREADSCOPE_OPTS\
diff --git a/scripts/ml.in b/scripts/ml.in
index 03e496a..9f8cd26 100644
--- a/scripts/ml.in
+++ b/scripts/ml.in
@@ -53,6 +53,7 @@ THREAD_LIBS="@THREAD_LIBS@"
 HWLOC_LIBS="@HWLOC_LIBS@"
 HWLOC_STATIC_LIBS="@HWLOC_STATIC_LIBS@"
 TRACE_BASE_LIBS_SYSTEM="@TRACE_BASE_LIBS_SYSTEM@"
+LDFLAGS_FOR_SANITIZERS="@LDFLAGS_FOR_SANITIZERS@"
 
 TMPDIR=${TMPDIR=/tmp}
 MATH_LIB=${MERCURY_MATH_LIB="@MATH_LIB@"}
@@ -426,6 +427,7 @@ case $make_shared_lib in
 				ARCH_OPTS="$ARCH_OPTS $LD_LIBFLAGS_FOR_TRACE"
 				;;
 		esac
+		SANITIZER_OPTS="$LDFLAGS_FOR_SANITIZERS"
 		;;
 	false)
 		LINKER="$CC"
@@ -438,6 +440,7 @@ case $make_shared_lib in
 				ARCH_OPTS="$ARCH_OPTS $LDFLAGS_FOR_TRACE"
 				;;
 		esac
+		SANITIZER_OPTS="$LDFLAGS_FOR_SANITIZERS"
 		;;
 esac
 
@@ -556,7 +559,7 @@ else
 	NOLOGO_OPTS=""
 fi
 
-LINKER_PRE_FLAGS="$NOLOGO_OPTS $MSVCRT_OPTS $PRINT_MAP_OPT $UNDEF_OPT $STRIP_OPTS $MAYBE_STATIC_OPT $ARCH_OPTS"
+LINKER_PRE_FLAGS="$NOLOGO_OPTS $MSVCRT_OPTS $PRINT_MAP_OPT $UNDEF_OPT $STRIP_OPTS $MAYBE_STATIC_OPT $ARCH_OPTS $SANITIZER_OPTS"
 LINKER_POST_FLAGS="@LINK_OPT_SEP@ $NODEFAULTLIB_FLAG $DEBUG_FLAG $LIBDIR_OPTS $RPATH_OPT_LIST $LIBS"
 
 case $verbose in
diff --git a/trace/Mmakefile b/trace/Mmakefile
index 81aad45..8eb07bf 100644
--- a/trace/Mmakefile
+++ b/trace/Mmakefile
@@ -272,7 +272,7 @@ lib$(EVENTSPEC_LIB_NAME)$(DLL_DEF_LIB).$A: $(EVENTSPEC_OBJS)
 	$(RANLIB) lib$(EVENTSPEC_LIB_NAME)$(DLL_DEF_LIB).$A
 
 lib$(TRACE_LIB_NAME).so: $(TRACE_PIC_OBJS) lib$(EVENTSPEC_LIB_NAME).so
-	$(LINK_SHARED_OBJ) $(ERROR_UNDEFINED)				\
+	$(LINK_SHARED_OBJ) $(ERROR_UNDEFINED) $(LDFLAGS_FOR_SANITIZERS) \
 		-o lib$(TRACE_LIB_NAME).so $(TRACE_PIC_OBJS)		\
 		$(RPATH_1)$(RPATH_2)					\
 		$(ALL_LD_LIBFLAGS) $(TRACE_LDFLAGS)			\
@@ -280,7 +280,7 @@ lib$(TRACE_LIB_NAME).so: $(TRACE_PIC_OBJS) lib$(EVENTSPEC_LIB_NAME).so
 		$(SHARED_LIBS)
 
 lib$(EVENTSPEC_LIB_NAME).so: $(EVENTSPEC_PIC_OBJS)
-	$(LINK_SHARED_OBJ) $(ERROR_UNDEFINED)				\
+	$(LINK_SHARED_OBJ) $(ERROR_UNDEFINED) $(LDFLAGS_FOR_SANITIZERS) \
 		-o lib$(EVENTSPEC_LIB_NAME).so $(EVENTSPEC_PIC_OBJS)	\
 		$(RPATH_1)$(RPATH_2)					\
 		$(ALL_LD_LIBFLAGS) $(EVENTSPEC_LDFLAGS)			\
@@ -289,7 +289,7 @@ lib$(EVENTSPEC_LIB_NAME).so: $(EVENTSPEC_PIC_OBJS)
 
 # For Darwin:
 lib$(TRACE_LIB_NAME).dylib: $(TRACE_PIC_OBJS) lib$(EVENTSPEC_LIB_NAME).dylib
-	$(LINK_SHARED_OBJ) $(ERROR_UNDEFINED)				\
+	$(LINK_SHARED_OBJ) $(ERROR_UNDEFINED) $(LDFLAGS_FOR_SANITIZERS) \
 		-o lib$(TRACE_LIB_NAME).dylib $(TRACE_PIC_OBJS)		\
 		-install_name 						\
 		$(FINAL_INSTALL_MERC_LIB_DIR)/lib$(TRACE_LIB_NAME).dylib \
@@ -298,7 +298,7 @@ lib$(TRACE_LIB_NAME).dylib: $(TRACE_PIC_OBJS) lib$(EVENTSPEC_LIB_NAME).dylib
 		$(SHARED_LIBS)
 
 lib$(EVENTSPEC_LIB_NAME).dylib: $(EVENTSPEC_PIC_OBJS)
-	$(LINK_SHARED_OBJ) $(ERROR_UNDEFINED)				\
+	$(LINK_SHARED_OBJ) $(ERROR_UNDEFINED) $(LDFLAGS_FOR_SANITIZERS) \
 		-o lib$(EVENTSPEC_LIB_NAME).dylib $(EVENTSPEC_PIC_OBJS)	\
 		-install_name 						\
 		$(FINAL_INSTALL_MERC_LIB_DIR)/lib$(EVENTSPEC_LIB_NAME).dylib \
-- 
2.9.0



More information about the reviews mailing list