Skip to content

Commit

Permalink
Enable CLANG sanitizers for native debug builds
Browse files Browse the repository at this point in the history
  • Loading branch information
steveharter committed Nov 17, 2015
1 parent 48617ae commit 4e06e42
Show file tree
Hide file tree
Showing 14 changed files with 272 additions and 59 deletions.
55 changes: 40 additions & 15 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,6 @@ if (CLR_CMAKE_PLATFORM_UNIX)
else()
clr_unknown_arch()
endif()

endif(CLR_CMAKE_PLATFORM_LINUX)

if(CLR_CMAKE_PLATFORM_DARWIN)
Expand All @@ -253,23 +252,52 @@ if (CLR_CMAKE_PLATFORM_UNIX)
message("Detected FreeBSD amd64")
endif(CLR_CMAKE_PLATFORM_FREEBSD)

# Disable frame pointer optimizations so profilers can get better call stacks
add_compile_options(-fno-omit-frame-pointer)

# set the CLANG sanitizer flags for debug build
if(UPPERCASE_CMAKE_BUILD_TYPE STREQUAL DEBUG)
# obtain settings from running enablesanitizers.sh
string(FIND "$ENV{DEBUG_SANITIZERS}" "asan" __ASAN_POS)
string(FIND "$ENV{DEBUG_SANITIZERS}" "ubsan" __UBSAN_POS)
if ((${__ASAN_POS} GREATER -1) OR (${__UBSAN_POS} GREATER -1))
set(CLR_SANITIZE_CXX_FLAGS "${CLR_SANITIZE_CXX_FLAGS} -fsanitize=")
set(CLR_SANITIZE_LINK_FLAGS "${CLR_SANITIZE_LINK_FLAGS} -fsanitize=")
if (${__ASAN_POS} GREATER -1)
set(CLR_SANITIZE_CXX_FLAGS "${CLR_SANITIZE_CXX_FLAGS}address,")
set(CLR_SANITIZE_LINK_FLAGS "${CLR_SANITIZE_LINK_FLAGS}address,")
message("Address Sanitizer (asan) enabled")
endif ()
if (${__UBSAN_POS} GREATER -1)
set(CLR_SANITIZE_CXX_FLAGS "${CLR_SANITIZE_CXX_FLAGS}undefined")
set(CLR_SANITIZE_LINK_FLAGS "${CLR_SANITIZE_LINK_FLAGS}undefined")
message("Undefined Behavior Sanitizer (ubsan) enabled")
endif ()

# -fdata-sections -ffunction-sections: each function has own section instead of one per .o file (needed for --gc-sections)
# -fPIC: enable Position Independent Code normally just for shared libraries but required when linking with address sanitizer
# -O1: optimization level used instead of -O0 to avoid compile error "invalid operand for inline asm constraint"
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${CLR_SANITIZE_CXX_FLAGS} -fdata-sections -ffunction-sections -fPIC -O1")

set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${CLR_SANITIZE_LINK_FLAGS}")

# -Wl and --gc-sections: drop unused sections\functions (similar to Windows /Gy function-level-linking)
set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} ${CLR_SANITIZE_LINK_FLAGS} -Wl,--gc-sections")
endif ()
endif(UPPERCASE_CMAKE_BUILD_TYPE STREQUAL DEBUG)

add_subdirectory(src/ToolBox/SOS/lldbplugin)
add_subdirectory(src/pal)
add_subdirectory(src/corefx)
add_subdirectory(src/coreclr/hosts/unixcorerun)
add_subdirectory(src/coreclr/hosts/unixcoreconsole)
endif(CLR_CMAKE_PLATFORM_UNIX)

if(CLR_CMAKE_PLATFORM_UNIX)
add_subdirectory(src/ToolBox/SOS/lldbplugin)
add_subdirectory(src/pal)
add_subdirectory(src/corefx)
add_subdirectory(src/coreclr/hosts/unixcorerun)
add_subdirectory(src/coreclr/hosts/unixcoreconsole)
endif(CLR_CMAKE_PLATFORM_UNIX)


if(CLR_CMAKE_PLATFORM_DARWIN)
add_subdirectory(src/coreclr/hosts/osxbundlerun)
add_subdirectory(src/coreclr/hosts/osxbundlerun)
endif(CLR_CMAKE_PLATFORM_DARWIN)

# Add this subdir. We install the headers for the jit.

add_subdirectory(src/pal/prebuilt/inc)

# Set to 1 if you want to clear the CMAKE initial compiler flags and set all the flags explicitly
Expand Down Expand Up @@ -438,9 +466,6 @@ add_compile_options(-fms-extensions )
#-fms-compatibility Enable full Microsoft Visual C++ compatibility
#-fms-extensions Accept some non-standard constructs supported by the Microsoft compiler

# Disable frame pointer optimizations so profilers can get better call stacks
add_compile_options(-fno-omit-frame-pointer)

endif(CLR_CMAKE_PLATFORM_UNIX)

add_subdirectory(src/debug/debug-pal)
Expand Down
107 changes: 107 additions & 0 deletions enablesanitizers.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#!/usr/bin/env bash

if [ $# -eq 0 ]; then
echo "Script for enabling CLang sanitizers for debug builds."
echo "*Only tested on Ubuntu x64."
echo "*This script must be 'sourced' (via dot+space) so that changes to environment variables are preserved. Run like this:"
if [ "$(dirname $0)" = "." ]; then
echo " . enablesanitizers.sh [options]"
else
echo " cd $(dirname $0);. enablesanitizers.sh [options]; cd -"
fi
echo "Usage: [asan] [ubsan] [lsan] [all] [off] [clangx.y]"
echo "asan: optional argument to enable Address Sanitizer."
echo "ubsan: optional argument to enable Undefined Behavior Sanitizer."
echo "lsan - optional argument to enable memory Leak Sanitizer."
echo "all - optional argument to enable asan, ubsan and lsan."
echo "off - optional argument to turn off all sanitizers."
echo "clangx.y - optional argument to specify clang version x.y. which is used to resolve stack traces."
else
__ClangMajorVersion=3
__ClangMinorVersion=5
__EnableASan=0
__EnableUBSan=0
__EnableLSan=0
__TurnOff=0
__Options=

for i in "$@"
do
lowerI="$(echo $i | awk '{print tolower($0)}')"
case $lowerI in
asan)
__EnableASan=1
;;
ubsan)
__EnableUBSan=1
;;
lsan)
__EnableASan=1
__EnableLSan=1
;;
all)
__EnableASan=1
__EnableUBSan=1
__EnableLSan=1
;;
off)
__TurnOff=1
;;
clang3.5)
__ClangMajorVersion=3
__ClangMinorVersion=5
;;
clang3.6)
__ClangMajorVersion=3
__ClangMinorVersion=6
;;
clang3.7)
__ClangMajorVersion=3
__ClangMinorVersion=7
;;
*)
echo "Unknown arg: $i"
return 1
esac
done

if [ $__TurnOff == 1 ]; then
unset DEBUG_SANITIZERS
echo "Setting DEBUG_SANITIZERS="
else
ASAN_OPTIONS="symbolize=1"

if [ $__EnableASan == 1 ]; then
__Options="$__Options asan"
fi
if [ $__EnableUBSan == 1 ]; then
__Options="$__Options ubsan"
fi
if [ $__EnableLSan == 1 ]; then
ASAN_OPTIONS="$ASAN_OPTIONS detect_leaks"
fi

# passed to build.sh
DEBUG_SANITIZERS="$__Options"
export DEBUG_SANITIZERS
echo "Setting DEBUG_SANITIZERS=$DEBUG_SANITIZERS"

# used by ASan at run-time
ASAN_OPTIONS="\"$ASAN_OPTIONS\""
export ASAN_OPTIONS
echo "Setting ASAN_OPTIONS=$ASAN_OPTIONS"

# used by ASan at run-time
ASAN_SYMBOLIZER_PATH="/usr/bin/llvm-symbolizer-$__ClangMajorVersion.$__ClangMinorVersion"
export ASAN_SYMBOLIZER_PATH
echo "Setting ASAN_SYMBOLIZER_PATH=$ASAN_SYMBOLIZER_PATH"
fi

unset __ClangMajorVersion
unset __ClangMinorVersion
unset __EnableASan
unset __EnableUBSan
unset __EnableLSan
unset __TurnOff
unset __Options
fi
2 changes: 2 additions & 0 deletions src/pal/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ elseif(PAL_CMAKE_PLATFORM_ARCH_ARM64)
add_definitions(-D_WIN64=1)
endif()

# turn off capability to remove unused functions (which was enabled in debug build with sanitizers)
set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} -Wl,--no-gc-sections")

add_compile_options(-fno-builtin)
add_compile_options(-fPIC)
Expand Down
37 changes: 35 additions & 2 deletions src/utilcode/clrhost_nodependencies.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@
#include "contract.h"
#include "tls.h"

#if defined __llvm__
# if defined(__has_feature) && __has_feature(address_sanitizer)
# define HAS_ADDRESS_SANITIZER
# endif
#endif

#ifdef _DEBUG_IMPL

//
Expand All @@ -37,6 +43,10 @@ void DisableThrowCheck()
dbg_fDisableThrowCheck = TRUE;
}

#ifdef HAS_ADDRESS_SANITIZER
// use the functionality from address santizier (which does not throw exceptions)
#else

#define CLRThrowsExceptionWorker() RealCLRThrowsExceptionWorker(__FUNCTION__, __FILE__, __LINE__)

static void RealCLRThrowsExceptionWorker(__in_z const char *szFunction,
Expand All @@ -53,6 +63,7 @@ static void RealCLRThrowsExceptionWorker(__in_z const char *szFunction,
CONTRACT_THROWSEX(szFunction, szFile, lineNum);
}

#endif // HAS_ADDRESS_SANITIZER
#endif //_DEBUG_IMPL

#if defined(_DEBUG_IMPL) && defined(ENABLE_CONTRACTS_IMPL)
Expand Down Expand Up @@ -383,6 +394,10 @@ FastFreeInProcessHeapFunc __ClrFreeInProcessHeap = (FastFreeInProcessHeapFunc) C

const NoThrow nothrow = { 0 };

#ifdef HAS_ADDRESS_SANITIZER
// use standard heap functions for address santizier
#else

#ifdef __llvm__
__attribute__((visibility("hidden")))
#endif
Expand Down Expand Up @@ -431,11 +446,17 @@ operator new[](size_t n)
return result;
};

#endif // HAS_ADDRESS_SANITIZER

#ifdef __llvm__
__attribute__((visibility("hidden")))
#endif
void * __cdecl operator new(size_t n, const NoThrow&)
{
#ifdef HAS_ADDRESS_SANITIZER
// use standard heap functions for address santizier (which doesn't provide for NoThrow)
void * result = operator new(n);
#else
STATIC_CONTRACT_NOTHROW;
STATIC_CONTRACT_GC_NOTRIGGER;
STATIC_CONTRACT_FAULT;
Expand All @@ -445,7 +466,8 @@ void * __cdecl operator new(size_t n, const NoThrow&)
INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN()));

void * result = ClrAllocInProcessHeap(0, S_SIZE_T(n));
TRASH_LASTERROR;
#endif // HAS_ADDRESS_SANITIZER
TRASH_LASTERROR;
return result;
}

Expand All @@ -454,6 +476,10 @@ __attribute__((visibility("hidden")))
#endif
void * __cdecl operator new[](size_t n, const NoThrow&)
{
#ifdef HAS_ADDRESS_SANITIZER
// use standard heap functions for address santizier (which doesn't provide for NoThrow)
void * result = operator new[](n);
#else
STATIC_CONTRACT_NOTHROW;
STATIC_CONTRACT_GC_NOTRIGGER;
STATIC_CONTRACT_FAULT;
Expand All @@ -463,10 +489,14 @@ void * __cdecl operator new[](size_t n, const NoThrow&)
INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN()));

void * result = ClrAllocInProcessHeap(0, S_SIZE_T(n));
TRASH_LASTERROR;
#endif // HAS_ADDRESS_SANITIZER
TRASH_LASTERROR;
return result;
}

#ifdef HAS_ADDRESS_SANITIZER
// use standard heap functions for address santizier
#else
#ifdef __llvm__
__attribute__((visibility("hidden")))
#endif
Expand Down Expand Up @@ -499,6 +529,9 @@ operator delete[](void *p) NOEXCEPT
TRASH_LASTERROR;
}

#endif // HAS_ADDRESS_SANITIZER


/* ------------------------------------------------------------------------ *
* New operator overloading for the executable heap
* ------------------------------------------------------------------------ */
Expand Down
2 changes: 1 addition & 1 deletion src/vm/arm/stubs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2270,6 +2270,7 @@ void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD)

LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK TransitionFrame::UpdateRegDisplay(rip:%p, rsp:%p)\n", pRD->ControlPC, pRD->SP));
}
#endif // !CROSSGEN_COMPILE

void TailCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
{
Expand Down Expand Up @@ -2311,7 +2312,6 @@ void TailCallFrame::InitFromContext(T_CONTEXT * pContext)
}

#endif // !DACCESS_COMPILE
#endif // !CROSSGEN_COMPILE

void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
{
Expand Down
2 changes: 1 addition & 1 deletion src/vm/ceeload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14846,7 +14846,7 @@ void ReflectionModule::ReleaseILData()

Module::ReleaseILData();
}
#endif // CROSSGEN_COMPILE
#endif // !CROSSGEN_COMPILE

#endif // !DACCESS_COMPILE

Expand Down
20 changes: 10 additions & 10 deletions src/vm/ceeload.h
Original file line number Diff line number Diff line change
Expand Up @@ -3686,6 +3686,7 @@ class ReflectionModule : public Module
// If true, then only other transient modules can depend on this module.
bool m_fIsTransient;

#if !defined DACCESS_COMPILE && !defined CROSSGEN_COMPILE
// Returns true iff metadata capturing is suppressed
bool IsMetadataCaptureSuppressed();

Expand All @@ -3705,8 +3706,8 @@ class ReflectionModule : public Module
pModule->ResumeMetadataCapture();
}


ReflectionModule(Assembly *pAssembly, mdFile token, PEFile *pFile);
#endif // !DACCESS_COMPILE && !CROSSGEN_COMPILE

public:

Expand All @@ -3715,14 +3716,13 @@ class ReflectionModule : public Module
PTR_SBuffer GetDynamicMetadataBuffer() const;
#endif

#if !defined DACCESS_COMPILE && !defined CROSSGEN_COMPILE
static ReflectionModule *Create(Assembly *pAssembly, PEFile *pFile, AllocMemTracker *pamTracker, LPCWSTR szName, BOOL fIsTransient);

void Initialize(AllocMemTracker *pamTracker, LPCWSTR szName);

void Destruct();
#ifndef DACCESS_COMPILE

void ReleaseILData();
#endif
#endif // !DACCESS_COMPILE && !CROSSGEN_COMPILE

// Overides functions to access sections
virtual TADDR GetIL(RVA target);
Expand Down Expand Up @@ -3786,17 +3786,14 @@ class ReflectionModule : public Module
}

#ifndef DACCESS_COMPILE
#ifndef CROSSGEN_COMPILE

typedef Wrapper<
ReflectionModule*,
ReflectionModule::SuppressCaptureWrapper,
ReflectionModule::ResumeCaptureWrapper> SuppressMetadataCaptureHolder;
#endif // !CROSSGEN_COMPILE



// Eagerly serialize the metadata to a buffer that the debugger can retrieve.
void CaptureModuleMetaDataToMemory();

HRESULT SetISymUnmanagedWriter(ISymUnmanagedWriter *pWriter)
{
CONTRACTL
Expand Down Expand Up @@ -3825,6 +3822,9 @@ class ReflectionModule : public Module
return S_OK;
}
#endif // !DACCESS_COMPILE

// Eagerly serialize the metadata to a buffer that the debugger can retrieve.
void CaptureModuleMetaDataToMemory();
};

// Module holders
Expand Down
Loading

0 comments on commit 4e06e42

Please sign in to comment.