From ef5a4fa59dbc786e04b7cf2908da2021a258193d Mon Sep 17 00:00:00 2001 From: Peter Wang Date: Thu, 13 Oct 2016 16:41:53 +1100 Subject: [PATCH] Support building with AddressSanitizer and UndefinedBehaviorSanitizer. 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 | 17 +++++++++++++++++ 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, 85 insertions(+), 12 deletions(-) create mode 100644 README.sanitizers diff --git a/Mmake.common.in b/Mmake.common.in index 4a13ae948..1472d17dd 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 000000000..da98f0733 --- /dev/null +++ b/README.sanitizers @@ -0,0 +1,17 @@ +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.3.0. + +Only high-level C grades work with the sanitizers enabled. + +For best results, use a `.ll_debug' grade or otherwise enable C debugging. + +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. + +See also: diff --git a/compiler/compile_target_code.m b/compiler/compile_target_code.m index 8c561085f..9c0895f4b 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 71911059e..5041ec5dd 100644 --- a/compiler/options.m +++ b/compiler/options.m @@ -828,6 +828,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 @@ -931,6 +932,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 @@ -1702,6 +1704,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(""), @@ -1814,8 +1817,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"), @@ -2679,6 +2683,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). @@ -2783,6 +2788,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). @@ -5500,7 +5506,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. @@ -5717,6 +5724,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 18568ad96..5b7916fa7 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 f10ed1093..87a0e1b29 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 011a5b5f1..a548856c2 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 fb555c167..2ee7f03a1 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 03e496aa9..9f8cd2673 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 81aad4540..8eb07bfa5 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 \