Index: cmake/config-ix.cmake =================================================================== --- cmake/config-ix.cmake +++ cmake/config-ix.cmake @@ -263,6 +263,7 @@ ${MIPS32} ${MIPS64} ${PPC64}) set(ALL_SAFESTACK_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM64} ${MIPS32} ${MIPS64}) set(ALL_CFI_SUPPORTED_ARCH ${X86} ${X86_64}) +set(ALL_ESAN_SUPPORTED_ARCH ${X86_64}) if(APPLE) include(CompilerRTDarwinUtils) @@ -495,6 +496,9 @@ list_intersect(CFI_SUPPORTED_ARCH ALL_CFI_SUPPORTED_ARCH SANITIZER_COMMON_SUPPORTED_ARCH) + list_intersect(ESAN_SUPPORTED_ARCH + ALL_ESAN_SUPPORTED_ARCH + SANITIZER_COMMON_SUPPORTED_ARCH) else() # Architectures supported by compiler-rt libraries. filter_available_targets(BUILTIN_SUPPORTED_ARCH @@ -517,6 +521,7 @@ filter_available_targets(SAFESTACK_SUPPORTED_ARCH ${ALL_SAFESTACK_SUPPORTED_ARCH}) filter_available_targets(CFI_SUPPORTED_ARCH ${ALL_CFI_SUPPORTED_ARCH}) + filter_available_targets(ESAN_SUPPORTED_ARCH ${ALL_ESAN_SUPPORTED_ARCH}) endif() if (MSVC) @@ -624,3 +629,10 @@ else() set(COMPILER_RT_HAS_CFI FALSE) endif() + +if (COMPILER_RT_HAS_SANITIZER_COMMON AND CFI_SUPPORTED_ARCH AND + OS_NAME MATCHES "Linux") + set(COMPILER_RT_HAS_ESAN TRUE) +else() + set(COMPILER_RT_HAS_ESAN FALSE) +endif() Index: lib/CMakeLists.txt =================================================================== --- lib/CMakeLists.txt +++ lib/CMakeLists.txt @@ -48,4 +48,8 @@ if(COMPILER_RT_HAS_CFI) add_subdirectory(cfi) endif() + + if(COMPILER_RT_HAS_ESAN) + add_subdirectory(esan) + endif() endif() Index: lib/esan/CMakeLists.txt =================================================================== --- /dev/null +++ lib/esan/CMakeLists.txt @@ -0,0 +1,36 @@ +# Build for the EfficiencySanitizer runtime support library. + +add_custom_target(esan) + +set(ESAN_CFLAGS ${SANITIZER_COMMON_CFLAGS}) +append_rtti_flag(OFF ESAN_CFLAGS) +set(ESAN_RTL_CFLAGS ${ESAN_CFLAGS}) + +include_directories(..) + +set(ESAN_SOURCES + esan.cc + esan_interface.cc) + +foreach (arch ${ESAN_SUPPORTED_ARCH}) + add_compiler_rt_runtime(clang_rt.esan + STATIC + ARCHS ${arch} + SOURCES ${ESAN_SOURCES} + $ + $ + $ + CFLAGS ${ESAN_RTL_CFLAGS}) + add_sanitizer_rt_symbols(clang_rt.esan + ARCHS ${arch} + EXTRA esan.syms.extra) + add_dependencies(esan + clang_rt.esan-${arch} + clang_rt.esan-${arch}-symbols) +endforeach() + +add_dependencies(compiler-rt esan) + +if (COMPILER_RT_INCLUDE_TESTS) + # TODO(bruening): add tests via add_subdirectory(tests) +endif() Index: lib/esan/esan.h =================================================================== --- /dev/null +++ lib/esan/esan.h @@ -0,0 +1,53 @@ +//===-- esan.h --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of EfficiencySanitizer, a family of performance tuners. +// +// Main internal esan header file. +// +// Ground rules: +// - C++ run-time should not be used (static CTORs, RTTI, exceptions, static +// function-scope locals) +// - All functions/classes/etc reside in namespace __esan, except for those +// declared in esan_interface.h. +// - Platform-specific files should be used instead of ifdefs (*). +// - No system headers included in header files (*). +// - Platform specific headres included only into platform-specific files (*). +// +// (*) Except when inlining is critical for performance. +//===----------------------------------------------------------------------===// + +#ifndef ESAN_H +#define ESAN_H + +#include "sanitizer_common/sanitizer_common.h" +#include "esan_interface.h" + +#define CALLERPC ((uptr)__builtin_return_address(0)) + +namespace __esan { + +extern bool EsanIsInitialized; + +extern ToolType WhichTool; + +const int kSizeLog1 = 0; +const int kSizeLog2 = 1; +const int kSizeLog4 = 2; +const int kSizeLog8 = 3; + +void initializeLibrary(); +int finalizeLibrary(); +void processMemAccess(uptr PC, uptr Addr, int SizeLog, bool IsWrite); +void processUnalignedAccess(uptr PC, uptr Addr, int Size, bool IsWrite); +void processRangeAccess(uptr PC, uptr Addr, int Size, bool IsWrite); + +} // namespace __esan + +#endif // ESAN_H Index: lib/esan/esan.cc =================================================================== --- /dev/null +++ lib/esan/esan.cc @@ -0,0 +1,124 @@ +//===-- esan.cc ----------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of EfficiencySanitizer, a family of performance tuners. +// +// Main file (entry points) for the Esan run-time. +//===----------------------------------------------------------------------===// + +#include "esan.h" +#include "esan_interface.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" + +// See comment below. +extern "C" { + extern void __cxa_atexit(void (*function)(void)); +} + +namespace __esan { + +bool EsanIsInitialized; +ToolType WhichTool; + +static const char * const kEsanOpsEnv = "ESAN_OPTIONS"; + +// We are combining multiple performance tuning tools under the umbrella of +// one EfficiencySanitizer super-tool. Most of our tools have very similar +// memory access instrumentation, shadow memory mapping, libc interception, +// etc., and there is typically more shared code than distinct code. +// +// We are not willing to dispatch on tool dynamically in our fastpath +// instrumentation: thus, which tool to use is a static option selected +// at compile time and passed to __esan_init(). +// +// We are willing to pay the overhead of tool dispatch in the slowpath to more +// easily share code. We expect to only come here rarely. +// If this becomes a performance hit, we can add separate interface +// routines for each subtool (e.g., __esan_cache_frag_aligned_read_4). +// But for libc interceptors, we'll have to do one of the following: +// A) Add multiple-include support to sanitizer_common_interceptors.inc, +// instantiate it separately for each tool, and call the selected +// tool's intercept setup code. +// B) Build separate static runtime libraries, one for each tool. +// C) Completely split the tools into separate sanitizers. + +ALWAYS_INLINE USED +void processMemAccess(uptr PC, uptr Addr, int SizeLog, bool IsWrite) { + VPrintf(4, "in esan::%s %p: %c %p %d\n", __FUNCTION__, PC, + IsWrite ? 'w' : 'r', Addr, 1 << SizeLog); + // We expect common accesses to be inlined so we go for simplicity here: + processRangeAccess(PC, Addr, 1 << SizeLog, IsWrite); +} + +ALWAYS_INLINE USED +void processUnalignedAccess(uptr PC, uptr Addr, int Size, bool IsWrite) { + VPrintf(4, "in esan::%s %p: %c %p %d\n", __FUNCTION__, PC, + IsWrite ? 'w' : 'r', Addr, Size); + processRangeAccess(PC, Addr, Size, IsWrite); +} + +void processRangeAccess(uptr PC, uptr Addr, int Size, bool IsWrite) { + VPrintf(3, "in esan::%s %p: %c %p %d\n", __FUNCTION__, PC, + IsWrite ? 'w' : 'r', Addr, Size); + if (WhichTool == ESAN_CacheFrag) { + // TODO(bruening): add shadow mapping and update shadow bits here. + // We'll move this to cache_frag.cc once we have something. + } +} + +static void initializeFlags() { + // Once we add our own flags we'll parse them here. + // For now the common ones are sufficient. + FlagParser parser; + SetCommonFlagsDefaults(); + RegisterCommonFlags(&parser); + parser.ParseString(GetEnv(kEsanOpsEnv)); + SetVerbosity(common_flags()->verbosity); + if (Verbosity()) + ReportUnrecognizedFlags(); + if (common_flags()->help) + parser.PrintFlagDescriptions(); + __sanitizer_set_report_path(common_flags()->log_path); +} + +void initializeLibrary() { + // We assume there is only one thread during init. + if (EsanIsInitialized) + return; + SanitizerToolName = "EfficiencySanitizer"; + initializeFlags(); + + // Intercepting libc _exit or exit via COMMON_INTERCEPTOR_ON_EXIT only + // finalizes on an explicit exit call by the app. To handle a normal + // exit we register an atexit handler. + ::__cxa_atexit((void (*)())finalizeLibrary); + + VPrintf(1, "in esan::%s\n", __FUNCTION__); + if (WhichTool != ESAN_CacheFrag) { + Printf("ERROR: unknown tool %d requested\n", WhichTool); + Die(); + } + + EsanIsInitialized = true; +} + +int finalizeLibrary() { + VPrintf(1, "in esan::%s\n", __FUNCTION__); + if (WhichTool == ESAN_CacheFrag) { + // FIXME NYI: we need to add sampling + callstack gathering and have a + // strategy for how to generate a final report. + // We'll move this to cache_frag.cc once we have something. + Report("%s is not finished: nothing yet to report\n", SanitizerToolName); + } + return 0; +} + +} // namespace __esan Index: lib/esan/esan.syms.extra =================================================================== --- /dev/null +++ lib/esan/esan.syms.extra @@ -0,0 +1,3 @@ +__esan_init +__esan_aligned* +__esan_unaligned* Index: lib/esan/esan_interface.h =================================================================== --- /dev/null +++ lib/esan/esan_interface.h @@ -0,0 +1,63 @@ +//===-- esan_interface.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of EfficiencySanitizer, a family of performance tuners. +// +// The functions declared in this header will be inserted by the instrumentation +// module. +// This header can be included by the instrumented program or by esan tests. +//===----------------------------------------------------------------------===// +#ifndef ESAN_INTERFACE_H +#define ESAN_INTERFACE_H + +#include + +// This header should NOT include any other headers. +// All functions in this header are extern "C" and start with __esan_. + +#ifdef __cplusplus +extern "C" { +#endif + +// This should be kept consistent with LLVM's EfficiencySanitizerOptions. +typedef enum Type { + ESAN_CacheFrag = 0, +} ToolType; + +// This function should be called at the very beginning of the process, +// before any instrumented code is executed and before any call to malloc. +SANITIZER_INTERFACE_ATTRIBUTE void __esan_init(ToolType Tool); + +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_read1(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_read2(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_read4(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_read8(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_read16(void *Addr); + +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_write1(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_write2(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_write4(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_write8(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_write16(void *Addr); + +SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_read2(const void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_read4(const void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_read8(const void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_read16(const void *Addr); + +SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_write2(const void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_write4(const void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_write8(const void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_write16(const void *Addr); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // ESAN_INTERFACE_H Index: lib/esan/esan_interface.cc =================================================================== --- /dev/null +++ lib/esan/esan_interface.cc @@ -0,0 +1,101 @@ +//===-- esan_interface.cc -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of EfficiencySanitizer, a family of performance tuners. +// +//===----------------------------------------------------------------------===// + +#include "esan_interface.h" +#include "esan.h" +#include "sanitizer_common/sanitizer_internal_defs.h" + +using namespace __esan; // NOLINT + +void __esan_init(ToolType Tool) { + WhichTool = Tool; + initializeLibrary(); +} + +// TODO(bruening): put all of these in a header to ensure inlining -- though +// we expect calling these to be rare as our instrumentation-inlined fastpath +// should handle all of the common cases. + +void __esan_aligned_read1(void *Addr) { + processMemAccess(CALLERPC, (uptr)Addr, kSizeLog1, false); +} + +void __esan_aligned_read2(void *Addr) { + processMemAccess(CALLERPC, (uptr)Addr, kSizeLog2, false); +} + +void __esan_aligned_read4(void *Addr) { + processMemAccess(CALLERPC, (uptr)Addr, kSizeLog4, false); +} + +void __esan_aligned_read8(void *Addr) { + processMemAccess(CALLERPC, (uptr)Addr, kSizeLog8, false); +} + +void __esan_aligned_read16(void *Addr) { + processMemAccess(CALLERPC, (uptr)Addr, kSizeLog8, false); + processMemAccess(CALLERPC, (uptr)Addr + 8, kSizeLog8, false); +} + +void __esan_aligned_write1(void *Addr) { + processMemAccess(CALLERPC, (uptr)Addr, kSizeLog1, true); +} + +void __esan_aligned_write2(void *Addr) { + processMemAccess(CALLERPC, (uptr)Addr, kSizeLog2, true); +} + +void __esan_aligned_write4(void *Addr) { + processMemAccess(CALLERPC, (uptr)Addr, kSizeLog4, true); +} + +void __esan_aligned_write8(void *Addr) { + processMemAccess(CALLERPC, (uptr)Addr, kSizeLog8, true); +} + +void __esan_aligned_write16(void *Addr) { + processMemAccess(CALLERPC, (uptr)Addr, kSizeLog8, true); + processMemAccess(CALLERPC, (uptr)Addr + 8, kSizeLog8, true); +} + +void __esan_unaligned_read2(const void *Addr) { + processUnalignedAccess(CALLERPC, (uptr)Addr, 2, false); +} + +void __esan_unaligned_read4(const void *Addr) { + processUnalignedAccess(CALLERPC, (uptr)Addr, 4, false); +} + +void __esan_unaligned_read8(const void *Addr) { + processUnalignedAccess(CALLERPC, (uptr)Addr, 8, false); +} + +void __esan_unaligned_read16(const void *Addr) { + processUnalignedAccess(CALLERPC, (uptr)Addr, 16, false); +} + +void __esan_unaligned_write2(const void *Addr) { + processUnalignedAccess(CALLERPC, (uptr)Addr, 2, true); +} + +void __esan_unaligned_write4(const void *Addr) { + processUnalignedAccess(CALLERPC, (uptr)Addr, 4, true); +} + +void __esan_unaligned_write8(const void *Addr) { + processUnalignedAccess(CALLERPC, (uptr)Addr, 8, true); +} + +void __esan_unaligned_write16(const void *Addr) { + processUnalignedAccess(CALLERPC, (uptr)Addr, 16, true); +} Index: lib/sanitizer_common/sanitizer_common_interceptors.inc =================================================================== --- lib/sanitizer_common/sanitizer_common_interceptors.inc +++ lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -30,6 +30,9 @@ // COMMON_INTERCEPTOR_SET_PTHREAD_NAME // COMMON_INTERCEPTOR_HANDLE_RECVMSG // COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED +// +// COMMON_INTERCEPTOR_ON_EXIT is only called if the app explicitly calls exit(), +// not on a normal exit. //===----------------------------------------------------------------------===// #include "interception/interception.h"