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 ESAN_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,35 @@ +# Build for the EfficiencySanitizer runtime support library. + +add_custom_target(esan) + +set(ESAN_RTL_CFLAGS ${SANITIZER_COMMON_CFLAGS}) +append_rtti_flag(OFF ESAN_RTL_CFLAGS) + +include_directories(..) + +set(ESAN_SOURCES + esan.cpp + esan_interface.cpp) + +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,44 @@ +//===-- 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_internal.h. +// - Platform-specific files should be used instead of ifdefs (*). +// - No system headers included in header files (*). +// - Platform specific headers 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_internal.h" + +namespace __esan { + +extern bool EsanIsInitialized; + +extern ToolType WhichTool; + +void initializeLibrary(ToolType Tool); +int finalizeLibrary(); +void processRangeAccess(uptr PC, uptr Addr, int Size, bool IsWrite); + +} // namespace __esan + +#endif // ESAN_H Index: lib/esan/esan.cpp =================================================================== --- /dev/null +++ lib/esan/esan.cpp @@ -0,0 +1,112 @@ +//===-- esan.cpp ----------------------------------------------------------===// +// +// 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_internal.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flag_parser.h" +#include "sanitizer_common/sanitizer_flags.h" + +// See comment below. +extern "C" { +extern void __cxa_atexit(void (*function)(void)); +} + +namespace __esan { + +bool EsanIsInitialized; +ToolType WhichTool; + +static const char EsanOptsEnv[] = "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_load_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. + +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.cpp 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(EsanOptsEnv)); + InitializeCommonFlags(); + if (Verbosity()) + ReportUnrecognizedFlags(); + if (common_flags()->help) + Parser.PrintFlagDescriptions(); + __sanitizer_set_report_path(common_flags()->log_path); +} + +void initializeLibrary(ToolType Tool) { + // We assume there is only one thread during init. + if (EsanIsInitialized) { + CHECK(Tool == WhichTool); + return; + } + WhichTool = Tool; + 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.cpp 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.cpp =================================================================== --- /dev/null +++ lib/esan/esan_interface.cpp @@ -0,0 +1,106 @@ +//===-- esan_interface.cpp ------------------------------------------------===// +// +// 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_internal.h" +#include "esan.h" +#include "sanitizer_common/sanitizer_internal_defs.h" + +using namespace __esan; // NOLINT + +// The instrumentation passes ToolType as a 32-bit integer. +// Here we guard against some arch with a non-32-bit int. +static_assert(sizeof(ToolType) == sizeof(u32), "enum type is not 32 bits"); + +void __esan_init(ToolType Tool) { + initializeLibrary(Tool); +} + +void __esan_aligned_load1(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 1, false); +} + +void __esan_aligned_load2(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 2, false); +} + +void __esan_aligned_load4(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 4, false); +} + +void __esan_aligned_load8(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 8, false); +} + +void __esan_aligned_load16(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 16, false); +} + +void __esan_aligned_store1(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 1, true); +} + +void __esan_aligned_store2(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 2, true); +} + +void __esan_aligned_store4(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 4, true); +} + +void __esan_aligned_store8(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 8, true); +} + +void __esan_aligned_store16(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 16, true); +} + +void __esan_unaligned_load2(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 2, false); +} + +void __esan_unaligned_load4(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 4, false); +} + +void __esan_unaligned_load8(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 8, false); +} + +void __esan_unaligned_load16(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 16, false); +} + +void __esan_unaligned_store2(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 2, true); +} + +void __esan_unaligned_store4(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 4, true); +} + +void __esan_unaligned_store8(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 8, true); +} + +void __esan_unaligned_store16(void *Addr) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 16, true); +} + +void __esan_unusual_load(void *Addr, uptr Size) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, Size, false); +} + +void __esan_unusual_store(void *Addr, uptr Size) { + processRangeAccess(GET_CALLER_PC(), (uptr)Addr, Size, true); +} Index: lib/esan/esan_interface_internal.h =================================================================== --- /dev/null +++ lib/esan/esan_interface_internal.h @@ -0,0 +1,73 @@ +//===-- esan_interface_internal.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. +// +// Calls to the functions declared in this header will be inserted by +// the instrumentation module. +//===----------------------------------------------------------------------===// + +#ifndef ESAN_INTERFACE_INTERNAL_H +#define ESAN_INTERFACE_INTERNAL_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_None = 0, + ESAN_CacheFrag, +} 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); + +// The instrumentation module will insert a call to one of these routines prior +// to each load and store instruction for which we do not have "fastpath" +// inlined instrumentation. These calls constitute the "slowpath" for our +// tools. We have separate routines for each type of memory access to enable +// targeted optimization. +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_load1(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_load2(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_load4(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_load8(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_load16(void *Addr); + +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_store1(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_store2(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_store4(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_store8(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_store16(void *Addr); + +SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_load2(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_load4(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_load8(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_load16(void *Addr); + +SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_store2(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_store4(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_store8(void *Addr); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_store16(void *Addr); + +// These cover unusually-sized accesses. +SANITIZER_INTERFACE_ATTRIBUTE void __esan_unusual_load(void *Addr, uptr Size); +SANITIZER_INTERFACE_ATTRIBUTE void __esan_unusual_store(void *Addr, uptr Size); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // ESAN_INTERFACE_INTERNAL_H 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" Index: test/CMakeLists.txt =================================================================== --- test/CMakeLists.txt +++ test/CMakeLists.txt @@ -70,6 +70,9 @@ if(COMPILER_RT_HAS_SAFESTACK) add_subdirectory(safestack) endif() + if(COMPILER_RT_HAS_ESAN) + add_subdirectory(esan) + endif() endif() if(COMPILER_RT_STANDALONE_BUILD) Index: test/esan/CMakeLists.txt =================================================================== --- /dev/null +++ test/esan/CMakeLists.txt @@ -0,0 +1,32 @@ +set(ESAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) +if(NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND ESAN_TEST_DEPS esan) +endif() + +set(ESAN_TESTSUITES) + +set(ESAN_TEST_ARCH ${ESAN_SUPPORTED_ARCH}) + +set(ESAN_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +foreach(arch ${ESAN_TEST_ARCH}) + set(ESAN_TEST_TARGET_ARCH ${arch}) + string(TOLOWER "-${arch}" ESAN_TEST_CONFIG_SUFFIX) + get_target_flags_for_arch(${arch} ESAN_TEST_TARGET_CFLAGS) + string(REPLACE ";" " " ESAN_TEST_TARGET_CFLAGS "${ESAN_TEST_TARGET_CFLAGS}") + + string(TOUPPER ${arch} ARCH_UPPER_CASE) + set(CONFIG_NAME ${ARCH_UPPER_CASE}Config) + + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/lit.site.cfg) + list(APPEND ESAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}) +endforeach() + +# TODO(bruening): add Unit/ tests as well + +add_lit_testsuite(check-esan "Running EfficiencySanitizer tests" + ${ESAN_TESTSUITES} + DEPENDS ${ESAN_TEST_DEPS}) +set_target_properties(check-esan PROPERTIES FOLDER "Esan tests") Index: test/esan/TestCases/verbose-simple.c =================================================================== --- /dev/null +++ test/esan/TestCases/verbose-simple.c @@ -0,0 +1,11 @@ +// RUN: %clang_esan_frag -O0 %s -o %t 2>&1 +// RUN: %env_esan_opts=verbosity=1 %run %t 2>&1 | FileCheck %s + +#include +#include +int main(int argc, char **argv) { + // CHECK: in esan::initializeLibrary + // CHECK-NEXT: in esan::finalizeLibrary + // CHECK-NEXT: {{.*}}EfficiencySanitizer is not finished: nothing yet to report + return 0; +} Index: test/esan/lit.cfg =================================================================== --- /dev/null +++ test/esan/lit.cfg @@ -0,0 +1,32 @@ +# -*- Python -*- + +import os + +# Setup config name. +config.name = 'EfficiencySanitizer' + config.name_suffix + +# Setup source root. +config.test_source_root = os.path.dirname(__file__) + +# Setup default compiler flags used with -fsanitize=efficiency option. +base_cflags = ([config.target_cflags] + config.debug_info_flags) +base_cxxflags = config.cxx_mode_flags + base_cflags + +frag_cflags = (["-fsanitize=efficiency-cache-frag"] + base_cflags) + +def build_invocation(compile_flags): + return " " + " ".join([config.clang] + compile_flags) + " " + +config.substitutions.append( ("%clang_esan_frag ", + build_invocation(frag_cflags)) ) + +default_esan_opts = '' +config.substitutions.append(('%env_esan_opts=', + 'env ESAN_OPTIONS=' + default_esan_opts)) + +# Default test suffixes. +config.suffixes = ['.c', '.cpp'] + +# EfficiencySanitizer tests are currently supported on Linux x86-64 only. +if config.host_os not in ['Linux'] or config.target_arch != 'x86_64': + config.unsupported = True Index: test/esan/lit.site.cfg.in =================================================================== --- /dev/null +++ test/esan/lit.site.cfg.in @@ -0,0 +1,14 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +# Tool-specific config options. +config.name_suffix = "@ESAN_TEST_CONFIG_SUFFIX@" +config.esan_lit_source_dir = "@ESAN_LIT_SOURCE_DIR@" +config.target_cflags = "@ESAN_TEST_TARGET_CFLAGS@" +config.target_arch = "@ESAN_TEST_TARGET_ARCH@" + +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured") + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@ESAN_LIT_SOURCE_DIR@/lit.cfg")